Skip to content

Commit b856d87

Browse files
authored
Merge 57316e6 into dcc8d95
2 parents dcc8d95 + 57316e6 commit b856d87

File tree

15 files changed

+90
-32
lines changed

15 files changed

+90
-32
lines changed

README.md

-114 Bytes

Gate

A Gate is a filter that determines whether or not an event should be emitted based on a pulse value. The gate can be used to filter out pulse events or to add randomness to the rhythm. A gate can be a predefined gate from the library or a dynamic filter - a function.

The default gate in a rhythm is a probability gate, which passes 1 and 0 events as they are, and values in between (0 - 1) with a probability.

The default gate in a rhythm is a threshold gate, which passes all pulse values > 0.

Emitter

An Emitter is an iterator that generates events for each pulse value. It can be made up of a fixed list of events, tidal cycles, or it can be a dynamic generator - a function.

examples/assets/hihat.lua

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ return rhythm {
1414
end
1515
end
1616
end,
17+
gate = function (context)
18+
return context.pulse_value > math.random()
19+
end,
1720
emit = function(context)
1821
local rand = math.randomstate(0x8879)
1922
return function(context)

examples/assets/snare.lua

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ math.randomseed(0x13ee127)
33
return rhythm {
44
unit = "1/16",
55
pattern = pattern.from { 0, 0, 0, 0, 1, 0, 0.075, 0 } * 7 + { 0, 0, 0, 1, 0, 0, 0.5, 0 },
6+
gate = function (context)
7+
return context.pulse_value > math.random()
8+
end,
69
emit = function(context)
710
return { key = "C5", volume = (context.pulse_value == 1) and 0.8 or 0.5 }
811
end,

examples/play-script.rs

-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
132132
Rc::new(RefCell::new(BeatTimeRhythm::new(
133133
beat_time,
134134
BeatTimeStep::Beats(1.0),
135-
None
136135
)))
137136
})
138137
};

examples/play.rs

+2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
111111
.every_nth_sixteenth(2.0)
112112
.with_offset(BeatTimeStep::Sixteenth(1.0))
113113
.with_instrument(HIHAT)
114+
.with_pattern([1.0, 0.5].to_pattern())
115+
.with_gate(ProbabilityGate::new(None))
114116
.trigger(new_note_event("C_5").mutate({
115117
let mut vel_step = 0;
116118
let mut note_step = 0;

src/bindings.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -282,17 +282,11 @@ fn register_global_bindings(
282282
Ok(unit) => matches!(unit.as_str(), "seconds" | "ms"),
283283
Err(_) => false,
284284
};
285-
// NB: don't keep borrowing app_data_ref here: Rhythm constructors may use random functions
286-
let rand_seed = {
287-
lua.app_data_ref::<LuaAppData>()
288-
.expect("Failed to access Lua app data")
289-
.rand_seed
290-
};
291285
if second_time_unit {
292-
SecondTimeRhythm::from_table(lua, &timeout_hook, &time_base, &table, rand_seed)?
286+
SecondTimeRhythm::from_table(lua, &timeout_hook, &time_base, &table)?
293287
.into_lua(lua)
294288
} else {
295-
BeatTimeRhythm::from_table(lua, &timeout_hook, &time_base, &table, rand_seed)?
289+
BeatTimeRhythm::from_table(lua, &timeout_hook, &time_base, &table)?
296290
.into_lua(lua)
297291
}
298292
}

src/bindings/rhythm/beat_time.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ impl BeatTimeRhythm {
2323
timeout_hook: &LuaTimeoutHook,
2424
time_base: &BeatTimeBase,
2525
table: &LuaTable,
26-
rand_seed: Option<u64>,
2726
) -> LuaResult<BeatTimeRhythm> {
2827
// resolution
2928
let mut resolution = 1.0;
@@ -56,7 +55,7 @@ impl BeatTimeRhythm {
5655
}
5756
}
5857
// create a new BeatTimeRhythm with the given time base and step
59-
let mut rhythm = BeatTimeRhythm::new(*time_base, step, rand_seed);
58+
let mut rhythm = BeatTimeRhythm::new(*time_base, step);
6059
// offset
6160
if table.contains_key("offset")? {
6261
let offset = table.get::<_, f32>("offset")?;

src/bindings/rhythm/second_time.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ impl SecondTimeRhythm {
2323
timeout_hook: &LuaTimeoutHook,
2424
time_base: &BeatTimeBase,
2525
table: &LuaTable,
26-
rand_seed: Option<u64>,
2726
) -> LuaResult<SecondTimeRhythm> {
2827
// resolution
2928
let mut resolution = 1.0;
@@ -49,7 +48,7 @@ impl SecondTimeRhythm {
4948
}
5049
}
5150
// create a new SecondTimeRhythm with the given time base and step
52-
let mut rhythm = SecondTimeRhythm::new(*time_base, resolution, rand_seed);
51+
let mut rhythm = SecondTimeRhythm::new(*time_base, resolution);
5352
// offset
5453
if table.contains_key("offset")? {
5554
let offset = table.get::<_, f32>("offset")? as SecondTimeStep;

src/gate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::{BeatTimeBase, InputParameterSet, PulseIterItem};
99
pub mod probability;
1010
#[cfg(feature = "scripting")]
1111
pub mod scripted;
12+
pub mod threshold;
1213

1314
// -------------------------------------------------------------------------------------------------
1415

src/gate/threshold.rs

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use std::borrow::Cow;
2+
3+
use crate::{BeatTimeBase, Gate, InputParameterSet, PulseIterItem};
4+
5+
// -------------------------------------------------------------------------------------------------
6+
7+
/// Gate implementation which passes all pulse values > a specified threshold value (by default 0).
8+
#[derive(Debug, Clone)]
9+
pub struct ThresholdGate {
10+
threshold: f32,
11+
}
12+
13+
impl ThresholdGate {
14+
pub fn new() -> Self {
15+
Self::with_threshold(0.0)
16+
}
17+
18+
pub fn with_threshold(threshold: f32) -> Self {
19+
Self { threshold }
20+
}
21+
}
22+
23+
impl Default for ThresholdGate {
24+
fn default() -> Self {
25+
Self::new()
26+
}
27+
}
28+
29+
impl Gate for ThresholdGate {
30+
fn set_time_base(&mut self, _time_base: &BeatTimeBase) {
31+
// nothing to do
32+
}
33+
34+
fn set_external_context(&mut self, _data: &[(Cow<str>, f64)]) {
35+
// nothing to do
36+
}
37+
38+
fn set_input_parameters(&mut self, _parameters: InputParameterSet) {
39+
// nothing to do
40+
}
41+
42+
fn run(&mut self, pulse: &PulseIterItem) -> bool {
43+
pulse.value > self.threshold
44+
}
45+
46+
fn duplicate(&self) -> Box<dyn Gate> {
47+
Box::new(self.clone())
48+
}
49+
50+
fn reset(&mut self) {
51+
// nothing to do
52+
}
53+
}

src/prelude.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub use super::{
1818
new_parameter_change_event, new_polyphonic_note_event, new_polyphonic_note_sequence_event,
1919
unique_instrument_id, InstrumentId, NoteEvent, ParameterChangeEvent, ParameterId,
2020
},
21-
gate::probability::ProbabilityGate,
21+
gate::{probability::ProbabilityGate, threshold::ThresholdGate},
2222
pattern::{euclidean, fixed::ToFixedPattern},
2323
rhythm::{beat_time::BeatTimeRhythm, second_time::SecondTimeRhythm},
2424
time::{BeatTimeStep, SecondTimeStep},

src/rhythm/beat_time.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ macro_rules! generate_step_funcs {
4848
/// Shortcuts for creating beat-time based patterns.
4949
impl BeatTimeBase {
5050
pub fn every_nth_step(&self, step: BeatTimeStep) -> BeatTimeRhythm {
51-
BeatTimeRhythm::new(*self, step, None)
51+
BeatTimeRhythm::new(*self, step)
5252
}
5353
generate_step_funcs!(sixteenth, BeatTimeStep::Sixteenth);
5454
generate_step_funcs!(eighth, BeatTimeStep::Eighth);

src/rhythm/generic.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::borrow::BorrowMut;
1515

1616
use crate::{
1717
event::{fixed::FixedEventIter, Event, EventIter, EventIterItem, InstrumentId},
18-
gate::probability::ProbabilityGate,
18+
gate::threshold::ThresholdGate,
1919
pattern::{fixed::FixedPattern, Pattern},
2020
time::{BeatTimeBase, SampleTimeDisplay},
2121
Gate, InputParameter, InputParameterSet, PulseIterItem, Rhythm, RhythmIter, RhythmIterItem,
@@ -63,14 +63,14 @@ pub struct GenericRhythm<Step: GenericRhythmTimeStep, Offset: GenericRhythmTimeS
6363
impl<Step: GenericRhythmTimeStep, Offset: GenericRhythmTimeStep> GenericRhythm<Step, Offset> {
6464
/// Create a new pattern based rhythm which emits `value` every `beat_time` `step`,
6565
/// and an optional seed for the random number generator.
66-
pub fn new(time_base: BeatTimeBase, step: Step, seed: Option<u64>) -> Self {
66+
pub fn new(time_base: BeatTimeBase, step: Step) -> Self {
6767
let offset = Offset::default_offset();
6868
let instrument = None;
6969
let input_parameters = InputParameterSet::new();
7070
let pattern = Box::<FixedPattern>::default();
7171
let pattern_repeat_count = None;
7272
let pattern_playback_finished = false;
73-
let gate = Box::new(ProbabilityGate::new(seed));
73+
let gate = Box::new(ThresholdGate::new());
7474
let event_iter = Box::<FixedEventIter>::default();
7575
let event_iter_sample_time = 0;
7676
let event_iter_next_sample_time = offset.to_samples(&time_base);
@@ -181,15 +181,13 @@ impl<Step: GenericRhythmTimeStep, Offset: GenericRhythmTimeStep> GenericRhythm<S
181181
new
182182
}
183183

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

191-
/// Return a new rhythm instance which uses the given dyn [`Gate`] instead of the default
192-
/// probability gate.
190+
/// Return a new rhythm instance which uses the given dyn [`Gate`] instead of the default gate.
193191
#[must_use]
194192
pub fn with_gate_dyn(self, gate: Box<dyn Gate>) -> Self {
195193
let time_base = self.time_base;

src/rhythm/second_time.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ pub type SecondTimeRhythm = GenericRhythm<SecondTimeStep, SecondTimeStep>;
3333

3434
// -------------------------------------------------------------------------------------------------
3535

36-
/// Shortcuts for creating sample-time based rhythms.
36+
/// Shortcuts for creating second-time based rhythms.
3737
impl BeatTimeBase {
3838
pub fn every_nth_seconds(&self, step: SecondTimeStep) -> SecondTimeRhythm {
39-
SecondTimeRhythm::new(*self, step, None)
39+
SecondTimeRhythm::new(*self, step)
4040
}
4141
}

types/nerdo/library/rhythm.lua

+15-8
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ error("Do not try to execute this file. It's just a type definition file.")
1919
---@field trigger_offset integer?
2020
---
2121
---Current input parameter values, using parameter ids as keys
22-
---and the actual parameter value as value.
22+
---and the actual parameter value as value.
2323
---@see InputParameter
2424
---@field inputs table<string, number|integer|boolean|string>
2525

@@ -120,13 +120,13 @@ error("Do not try to execute this file. It's just a type definition file.")
120120
---```
121121
---@field offset number?
122122
---
123-
---Define optional input parameters for the rhythm. Input parameters can dynamically
124-
---change a rhythms behavior everywhere where `context`s are passed, e.g. in pattern,
123+
---Define optional input parameters for the rhythm. Input parameters can dynamically
124+
---change a rhythms behavior everywhere where `context`s are passed, e.g. in pattern,
125125
---gate, emitter or cycle map generator functions.
126126
---
127127
---## examples:
128128
---```lua
129-
----- trigger a single note as specified by input parameter 'note'
129+
----- trigger a single note as specified by input parameter 'note'
130130
----- when input parameter 'enabled' is true, else triggers nothing.
131131
--- inputs = {
132132
--- parameter.boolean("enabled", true),
@@ -206,12 +206,19 @@ error("Do not try to execute this file. It's just a type definition file.")
206206
---```
207207
---@field repeats (integer|boolean)?
208208
---
209-
---Set optional pulse train filter between pattern and emitter. By default a probability
210-
---gate is used, which passes 1s directly, skips 0s, and applies values in range (0 - 1) using
211-
---the pulse value as probability, like:
209+
---Optional pulse train filter function or generator function which filters events between
210+
---the pattern and emitter. By default a threshold gate, which passes all pulse values
211+
---greater than zero.
212+
---
213+
---Custom function should returns true when a pattern pulse value should be passed,
214+
---and false when the emitter should be skipped.
215+
---
216+
---### examples:
212217
---```lua
218+
----- probability gate: skips all 0s, passes all 1s. pulse alues in range (0, 1) are
219+
----- maybe passed, using the pulse value as probablility.
213220
---gate = function(context)
214-
--- return context.pulse_value >= 1 or context.pulse_value > math.random()
221+
--- return context.pulse_value > math.random()
215222
---end
216223
---```
217224
---@field gate Pulse[]|(fun(context: GateContext):boolean)|(fun(context: GateContext):fun(context: GateContext):boolean)?

0 commit comments

Comments
 (0)