Skip to content

Commit f26ef12

Browse files
committed
Boilerplate for sequence looper
1 parent c640128 commit f26ef12

File tree

7 files changed

+178
-17
lines changed

7 files changed

+178
-17
lines changed

caw/examples/live_example.rs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ fn main() {
1010
volume.set(knob("volume").initial_value_01(0.2).build());
1111
let tempo_s = sv(knob("tempo s").build() * 2.);
1212
let clock = sv(periodic_trig_s(tempo_s.clone()).build());
13-
let env = sv({
13+
let env_ = sv({
1414
let mut scope = blink::Server::new("env", Default::default()).unwrap();
1515
adsr(clock.clone().trig_to_gate(tempo_s.clone() / 2.))
1616
.a(tempo_s.clone() / 8.)
@@ -21,21 +21,47 @@ fn main() {
2121
let _ = scope.send_samples(buf);
2222
})
2323
});
24-
let keys = computer_keyboard("keys").build().shared();
25-
let space_button = keys.clone().controllers().get_01(0).is_positive();
24+
let keys = computer_keyboard("keys")
25+
.start_note(note::B_1)
26+
.build()
27+
.shared();
28+
let space_button =
29+
keys.clone().controllers().get_01(0).is_positive().shared();
2630
let key_events = keys.clone().key_events().shared();
27-
let env2 = adsr(space_button).build().shared();
2831
out.set(|| {
2932
let voice = key_events.clone().mono_voice();
30-
super_saw(voice.note.freq_hz())
33+
let (note, gate) = sequence_looper(
34+
voice.gated_note(),
35+
clock.clone(),
36+
space_button.clone(),
37+
8,
38+
)
39+
.ungated();
40+
let env = adsr(gate)
41+
.a(tempo_s.clone() / 24.)
42+
.r(tempo_s.clone() / 2.)
43+
.s(0.75)
44+
.d(tempo_s.clone() / 24.)
45+
.build()
46+
.exp_01(1.);
47+
super_saw(note.freq_hz())
3148
.build()
3249
.filter(
33-
low_pass::default(
34-
100. + env.clone() * 4000. + (env2.clone() * 5000.),
35-
)
36-
.q(0.5),
50+
low_pass::default(10. + env_.clone() * 0. + (env * 15000.))
51+
.q(0.2),
3752
)
38-
.filter(reverb())
53+
.filter(
54+
chorus()
55+
.lfo_rate_hz(0.1)
56+
.delay_s(0.001)
57+
.depth_s(0.002)
58+
.mix_01(0.5)
59+
.feedback_ratio(0.5),
60+
)
61+
.filter(reverb::default().room_size(0.9).damping(0.1))
62+
63+
// .filter(reverb())
64+
// .filter(chorus())
3965
});
4066
thread::park();
4167
}

core/src/sig.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,18 @@ where
641641
})
642642
}
643643

644+
/// A signal whose items are None if the gate is down and the input signal if the gate is up.
645+
pub fn gated<G>(self, gate: G) -> Sig<impl SigT<Item = Option<S::Item>>>
646+
where
647+
G: SigT<Item = bool>,
648+
{
649+
self.zip(gate).map(
650+
|(self_, gate)| {
651+
if gate { Some(self_) } else { None }
652+
},
653+
)
654+
}
655+
644656
pub fn shared(self) -> Sig<SigShared<S>> {
645657
sig_shared(self.0)
646658
}
@@ -1024,6 +1036,30 @@ where
10241036
}
10251037
}
10261038

1039+
impl<T, S> Sig<S>
1040+
where
1041+
T: Clone + Default,
1042+
S: SigT<Item = Option<T>>,
1043+
{
1044+
/// Reverses the `gated` function. Given a sequence of optional values, produces a sequence of
1045+
/// values and a gate sequence. When the input signal is `None`, the output value sequence uses
1046+
/// the previous `Some` value of the input, or `Default::default()` if no `Some` value has been
1047+
/// produced yet.
1048+
pub fn ungated(
1049+
self,
1050+
) -> (Sig<impl SigT<Item = T>>, Sig<impl SigT<Item = bool>>) {
1051+
let mut prev_value = T::default();
1052+
self.map_mut(move |value_opt| match value_opt {
1053+
Some(value) => {
1054+
prev_value = value.clone();
1055+
(value, true)
1056+
}
1057+
None => (prev_value.clone(), false),
1058+
})
1059+
.unzip()
1060+
}
1061+
}
1062+
10271063
pub struct WithBuf<S, F>
10281064
where
10291065
S: SigT,

keyboard/src/mono_voice.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,20 @@ where
3434
}
3535
}
3636

37+
impl<K> MonoVoice<K>
38+
where
39+
K: SigT<Item = KeyEvents>,
40+
{
41+
pub fn gated_note(self) -> Sig<impl SigT<Item = Option<crate::Note>>> {
42+
let Self {
43+
note,
44+
key_down_gate,
45+
..
46+
} = self;
47+
note.gated(key_down_gate)
48+
}
49+
}
50+
3751
/// Extracts a sequence of notes from a sequence of key events.
3852
pub struct Note<K>
3953
where

midi/src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ impl MidiEvents {
4949
Self(smallvec![])
5050
}
5151

52+
pub fn is_empty(&self) -> bool {
53+
self.0.is_empty()
54+
}
55+
5256
pub fn push(&mut self, midi_event: MidiEvent) {
5357
self.0.push(midi_event);
5458
}
@@ -80,6 +84,14 @@ impl MidiMessages {
8084
Self(smallvec![])
8185
}
8286

87+
pub fn is_empty(&self) -> bool {
88+
self.0.is_empty()
89+
}
90+
91+
pub fn clear(&mut self) {
92+
self.0.clear()
93+
}
94+
8395
pub fn push(&mut self, midi_event: MidiMessage) {
8496
self.0.push(midi_event);
8597
}
@@ -299,6 +311,7 @@ where
299311
{
300312
// Only copy messages from the first sample worth of events.
301313
let midi_messages = &mut self.buf[0];
314+
midi_messages.clear();
302315
if let Some(midi_events) = midi_events.iter().next() {
303316
for midi_event in midi_events {
304317
if midi_event.channel == self.channel {

utils/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ pub use bitwise_pattern_trig::bitwise_pattern_trigs_8;
33
pub mod noise;
44
pub mod sequencer;
55
pub use sequencer::value_sequencer;
6+
pub mod looper;
7+
pub use looper::sequence_looper;

utils/src/looper.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use caw_core::{Buf, Sig, SigCtx, SigT};
2+
use itertools::izip;
3+
4+
struct SequenceLooper<X, S, T, R, N>
5+
where
6+
X: Clone,
7+
S: SigT<Item = Option<X>>,
8+
T: SigT<Item = bool>,
9+
R: SigT<Item = bool>,
10+
N: SigT<Item = u32>,
11+
{
12+
sig: S,
13+
tick: T,
14+
recording: R,
15+
length: N,
16+
sequence: Vec<S::Item>,
17+
buf: Vec<S::Item>,
18+
}
19+
20+
impl<X, S, T, R, N> SigT for SequenceLooper<X, S, T, R, N>
21+
where
22+
X: Clone,
23+
S: SigT<Item = Option<X>>,
24+
T: SigT<Item = bool>,
25+
R: SigT<Item = bool>,
26+
N: SigT<Item = u32>,
27+
{
28+
type Item = S::Item;
29+
30+
fn sample(&mut self, ctx: &SigCtx) -> impl Buf<Self::Item> {
31+
self.buf.resize_with(ctx.num_samples, || None);
32+
let sig = self.sig.sample(ctx);
33+
let tick = self.tick.sample(ctx);
34+
let recording = self.recording.sample(ctx);
35+
let length = self.length.sample(ctx);
36+
for (out, sample, tick, recording, length) in izip! {
37+
self.buf.iter_mut(),
38+
sig.iter(),
39+
tick.iter(),
40+
recording.iter(),
41+
length.iter(),
42+
} {
43+
*out = sample;
44+
}
45+
&self.buf
46+
}
47+
}
48+
49+
pub fn sequence_looper<X: Clone>(
50+
sig: impl SigT<Item = Option<X>>,
51+
tick: impl SigT<Item = bool>,
52+
recording: impl SigT<Item = bool>,
53+
length: impl SigT<Item = u32>,
54+
) -> Sig<impl SigT<Item = Option<X>>> {
55+
Sig(SequenceLooper {
56+
sig,
57+
tick,
58+
recording,
59+
length,
60+
sequence: Vec::new(),
61+
buf: Vec::new(),
62+
})
63+
}

widgets/src/computer_keyboard.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use caw_computer_keyboard::Key;
33
use caw_keyboard::Note;
44
use midly::{MidiMessage, num::u7};
55
use sdl2::{keyboard::Scancode, pixels::Color};
6-
use std::{collections::HashMap, time::Instant};
6+
use std::{
7+
collections::{HashMap, HashSet},
8+
time::Instant,
9+
};
710

811
const WIDTH_PX: u32 = 128;
912
const HEIGHT_PX: u32 = 128;
@@ -20,6 +23,7 @@ impl KeyMappings {
2023
pub struct ComputerKeyboard {
2124
window: Window,
2225
key_mappings: KeyMappings,
26+
pressed_keys: HashSet<Note>,
2327
}
2428

2529
const SPACEBAR_CONTROLLER: u7 = u7::from_int_lossy(0);
@@ -35,6 +39,7 @@ impl ComputerKeyboard {
3539
Ok(Self {
3640
window,
3741
key_mappings,
42+
pressed_keys: HashSet::new(),
3843
})
3944
}
4045

@@ -57,18 +62,19 @@ impl ComputerKeyboard {
5762
controller: SPACEBAR_CONTROLLER,
5863
value: 0.into(),
5964
}),
60-
6165
Event::KeyDown {
6266
scancode: Some(scancode),
6367
..
6468
} => {
6569
if let Some(note) =
6670
self.key_mappings.note_from_scancode(scancode)
6771
{
68-
buf.push(MidiMessage::NoteOn {
69-
key: note.to_midi_index().into(),
70-
vel: u7::max_value(),
71-
});
72+
if self.pressed_keys.insert(note) {
73+
buf.push(MidiMessage::NoteOn {
74+
key: note.to_midi_index().into(),
75+
vel: u7::max_value(),
76+
});
77+
}
7278
}
7379
}
7480
Event::KeyUp {
@@ -78,9 +84,10 @@ impl ComputerKeyboard {
7884
if let Some(note) =
7985
self.key_mappings.note_from_scancode(scancode)
8086
{
87+
self.pressed_keys.remove(&note);
8188
buf.push(MidiMessage::NoteOff {
8289
key: note.to_midi_index().into(),
83-
vel: u7::max_value(),
90+
vel: 0.into(),
8491
});
8592
}
8693
}

0 commit comments

Comments
 (0)