Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add new ThresholdGate and make it the default gate #35

Merged
merged 1 commit into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified README.md
Binary file not shown.
3 changes: 3 additions & 0 deletions examples/assets/hihat.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ return rhythm {
end
end
end,
gate = function (context)
return context.pulse_value > math.random()
end,
emit = function(context)
local rand = math.randomstate(0x8879)
return function(context)
Expand Down
3 changes: 3 additions & 0 deletions examples/assets/snare.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ math.randomseed(0x13ee127)
return rhythm {
unit = "1/16",
pattern = pattern.from { 0, 0, 0, 0, 1, 0, 0.075, 0 } * 7 + { 0, 0, 0, 1, 0, 0, 0.5, 0 },
gate = function (context)
return context.pulse_value > math.random()
end,
emit = function(context)
return { key = "C5", volume = (context.pulse_value == 1) and 0.8 or 0.5 }
end,
Expand Down
1 change: 0 additions & 1 deletion examples/play-script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Rc::new(RefCell::new(BeatTimeRhythm::new(
beat_time,
BeatTimeStep::Beats(1.0),
None
)))
})
};
Expand Down
2 changes: 2 additions & 0 deletions examples/play.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.every_nth_sixteenth(2.0)
.with_offset(BeatTimeStep::Sixteenth(1.0))
.with_instrument(HIHAT)
.with_pattern([1.0, 0.5].to_pattern())
.with_gate(ProbabilityGate::new(None))
.trigger(new_note_event("C_5").mutate({
let mut vel_step = 0;
let mut note_step = 0;
Expand Down
10 changes: 2 additions & 8 deletions src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,17 +282,11 @@ fn register_global_bindings(
Ok(unit) => matches!(unit.as_str(), "seconds" | "ms"),
Err(_) => false,
};
// NB: don't keep borrowing app_data_ref here: Rhythm constructors may use random functions
let rand_seed = {
lua.app_data_ref::<LuaAppData>()
.expect("Failed to access Lua app data")
.rand_seed
};
if second_time_unit {
SecondTimeRhythm::from_table(lua, &timeout_hook, &time_base, &table, rand_seed)?
SecondTimeRhythm::from_table(lua, &timeout_hook, &time_base, &table)?
.into_lua(lua)
} else {
BeatTimeRhythm::from_table(lua, &timeout_hook, &time_base, &table, rand_seed)?
BeatTimeRhythm::from_table(lua, &timeout_hook, &time_base, &table)?
.into_lua(lua)
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/bindings/rhythm/beat_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ impl BeatTimeRhythm {
timeout_hook: &LuaTimeoutHook,
time_base: &BeatTimeBase,
table: &LuaTable,
rand_seed: Option<u64>,
) -> LuaResult<BeatTimeRhythm> {
// resolution
let mut resolution = 1.0;
Expand Down Expand Up @@ -56,7 +55,7 @@ impl BeatTimeRhythm {
}
}
// create a new BeatTimeRhythm with the given time base and step
let mut rhythm = BeatTimeRhythm::new(*time_base, step, rand_seed);
let mut rhythm = BeatTimeRhythm::new(*time_base, step);
// offset
if table.contains_key("offset")? {
let offset = table.get::<_, f32>("offset")?;
Expand Down
3 changes: 1 addition & 2 deletions src/bindings/rhythm/second_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ impl SecondTimeRhythm {
timeout_hook: &LuaTimeoutHook,
time_base: &BeatTimeBase,
table: &LuaTable,
rand_seed: Option<u64>,
) -> LuaResult<SecondTimeRhythm> {
// resolution
let mut resolution = 1.0;
Expand All @@ -49,7 +48,7 @@ impl SecondTimeRhythm {
}
}
// create a new SecondTimeRhythm with the given time base and step
let mut rhythm = SecondTimeRhythm::new(*time_base, resolution, rand_seed);
let mut rhythm = SecondTimeRhythm::new(*time_base, resolution);
// offset
if table.contains_key("offset")? {
let offset = table.get::<_, f32>("offset")? as SecondTimeStep;
Expand Down
1 change: 1 addition & 0 deletions src/gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::{BeatTimeBase, InputParameterSet, PulseIterItem};
pub mod probability;
#[cfg(feature = "scripting")]
pub mod scripted;
pub mod threshold;

// -------------------------------------------------------------------------------------------------

Expand Down
53 changes: 53 additions & 0 deletions src/gate/threshold.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use std::borrow::Cow;

use crate::{BeatTimeBase, Gate, InputParameterSet, PulseIterItem};

// -------------------------------------------------------------------------------------------------

/// Gate implementation which passes all pulse values > a specified threshold value (by default 0).
#[derive(Debug, Clone)]
pub struct ThresholdGate {
threshold: f32,
}

impl ThresholdGate {
pub fn new() -> Self {
Self::with_threshold(0.0)
}

pub fn with_threshold(threshold: f32) -> Self {
Self { threshold }
}
}

impl Default for ThresholdGate {
fn default() -> Self {
Self::new()
}
}

impl Gate for ThresholdGate {
fn set_time_base(&mut self, _time_base: &BeatTimeBase) {
// nothing to do
}

fn set_external_context(&mut self, _data: &[(Cow<str>, f64)]) {
// nothing to do
}

fn set_input_parameters(&mut self, _parameters: InputParameterSet) {
// nothing to do
}

fn run(&mut self, pulse: &PulseIterItem) -> bool {
pulse.value > self.threshold
}

fn duplicate(&self) -> Box<dyn Gate> {
Box::new(self.clone())
}

fn reset(&mut self) {
// nothing to do
}
}
2 changes: 1 addition & 1 deletion src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub use super::{
new_parameter_change_event, new_polyphonic_note_event, new_polyphonic_note_sequence_event,
unique_instrument_id, InstrumentId, NoteEvent, ParameterChangeEvent, ParameterId,
},
gate::probability::ProbabilityGate,
gate::{probability::ProbabilityGate, threshold::ThresholdGate},
pattern::{euclidean, fixed::ToFixedPattern},
rhythm::{beat_time::BeatTimeRhythm, second_time::SecondTimeRhythm},
time::{BeatTimeStep, SecondTimeStep},
Expand Down
2 changes: 1 addition & 1 deletion src/rhythm/beat_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ macro_rules! generate_step_funcs {
/// Shortcuts for creating beat-time based patterns.
impl BeatTimeBase {
pub fn every_nth_step(&self, step: BeatTimeStep) -> BeatTimeRhythm {
BeatTimeRhythm::new(*self, step, None)
BeatTimeRhythm::new(*self, step)
}
generate_step_funcs!(sixteenth, BeatTimeStep::Sixteenth);
generate_step_funcs!(eighth, BeatTimeStep::Eighth);
Expand Down
12 changes: 5 additions & 7 deletions src/rhythm/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::borrow::BorrowMut;

use crate::{
event::{fixed::FixedEventIter, Event, EventIter, EventIterItem, InstrumentId},
gate::probability::ProbabilityGate,
gate::threshold::ThresholdGate,
pattern::{fixed::FixedPattern, Pattern},
time::{BeatTimeBase, SampleTimeDisplay},
Gate, InputParameter, InputParameterSet, PulseIterItem, Rhythm, RhythmIter, RhythmIterItem,
Expand Down Expand Up @@ -63,14 +63,14 @@ pub struct GenericRhythm<Step: GenericRhythmTimeStep, Offset: GenericRhythmTimeS
impl<Step: GenericRhythmTimeStep, Offset: GenericRhythmTimeStep> GenericRhythm<Step, Offset> {
/// Create a new pattern based rhythm which emits `value` every `beat_time` `step`,
/// and an optional seed for the random number generator.
pub fn new(time_base: BeatTimeBase, step: Step, seed: Option<u64>) -> Self {
pub fn new(time_base: BeatTimeBase, step: Step) -> Self {
let offset = Offset::default_offset();
let instrument = None;
let input_parameters = InputParameterSet::new();
let pattern = Box::<FixedPattern>::default();
let pattern_repeat_count = None;
let pattern_playback_finished = false;
let gate = Box::new(ProbabilityGate::new(seed));
let gate = Box::new(ThresholdGate::new());
let event_iter = Box::<FixedEventIter>::default();
let event_iter_sample_time = 0;
let event_iter_next_sample_time = offset.to_samples(&time_base);
Expand Down Expand Up @@ -181,15 +181,13 @@ impl<Step: GenericRhythmTimeStep, Offset: GenericRhythmTimeStep> GenericRhythm<S
new
}

/// Return a new rhythm instance which uses the given [`Gate`] instead of the default
/// probability gate.
/// Return a new rhythm instance which uses the given [`Gate`] instead of the default gate.
#[must_use]
pub fn with_gate<T: Gate + Sized + 'static>(self, gate: T) -> Self {
self.with_gate_dyn(Box::new(gate))
}

/// Return a new rhythm instance which uses the given dyn [`Gate`] instead of the default
/// probability gate.
/// Return a new rhythm instance which uses the given dyn [`Gate`] instead of the default gate.
#[must_use]
pub fn with_gate_dyn(self, gate: Box<dyn Gate>) -> Self {
let time_base = self.time_base;
Expand Down
4 changes: 2 additions & 2 deletions src/rhythm/second_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ pub type SecondTimeRhythm = GenericRhythm<SecondTimeStep, SecondTimeStep>;

// -------------------------------------------------------------------------------------------------

/// Shortcuts for creating sample-time based rhythms.
/// Shortcuts for creating second-time based rhythms.
impl BeatTimeBase {
pub fn every_nth_seconds(&self, step: SecondTimeStep) -> SecondTimeRhythm {
SecondTimeRhythm::new(*self, step, None)
SecondTimeRhythm::new(*self, step)
}
}
23 changes: 15 additions & 8 deletions types/nerdo/library/rhythm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ error("Do not try to execute this file. It's just a type definition file.")
---@field trigger_offset integer?
---
---Current input parameter values, using parameter ids as keys
---and the actual parameter value as value.
---and the actual parameter value as value.
---@see InputParameter
---@field inputs table<string, number|integer|boolean|string>

Expand Down Expand Up @@ -120,13 +120,13 @@ error("Do not try to execute this file. It's just a type definition file.")
---```
---@field offset number?
---
---Define optional input parameters for the rhythm. Input parameters can dynamically
---change a rhythms behavior everywhere where `context`s are passed, e.g. in pattern,
---Define optional input parameters for the rhythm. Input parameters can dynamically
---change a rhythms behavior everywhere where `context`s are passed, e.g. in pattern,
---gate, emitter or cycle map generator functions.
---
---## examples:
---```lua
----- trigger a single note as specified by input parameter 'note'
----- trigger a single note as specified by input parameter 'note'
----- when input parameter 'enabled' is true, else triggers nothing.
--- inputs = {
--- parameter.boolean("enabled", true),
Expand Down Expand Up @@ -206,12 +206,19 @@ error("Do not try to execute this file. It's just a type definition file.")
---```
---@field repeats (integer|boolean)?
---
---Set optional pulse train filter between pattern and emitter. By default a probability
---gate is used, which passes 1s directly, skips 0s, and applies values in range (0 - 1) using
---the pulse value as probability, like:
---Optional pulse train filter function or generator function which filters events between
---the pattern and emitter. By default a threshold gate, which passes all pulse values
---greater than zero.
---
---Custom function should returns true when a pattern pulse value should be passed,
---and false when the emitter should be skipped.
---
---### examples:
---```lua
----- probability gate: skips all 0s, passes all 1s. pulse alues in range (0, 1) are
----- maybe passed, using the pulse value as probablility.
---gate = function(context)
--- return context.pulse_value >= 1 or context.pulse_value > math.random()
--- return context.pulse_value > math.random()
---end
---```
---@field gate Pulse[]|(fun(context: GateContext):boolean)|(fun(context: GateContext):fun(context: GateContext):boolean)?
Expand Down
Loading