currawong_core/
loopers.rs1use crate::signal::*;
2use std::cell::RefCell;
3
4pub struct ClockedTriggerLooper {
5 pub clock: Trigger,
6 pub add: Gate,
7 pub remove: Gate,
8 pub length: usize,
9}
10
11impl ClockedTriggerLooper {
12 pub fn signal(self) -> Sbool {
13 let Self {
14 clock,
15 add,
16 remove,
17 length,
18 } = self;
19 struct State {
20 sequence: Vec<bool>,
21 samples_since_last_clock_pulse: usize,
22 samples_since_last_add: usize,
23 next_index: usize,
24 }
25 let state = RefCell::new(State {
26 sequence: (0..length).map(|_| false).collect::<Vec<_>>(),
27 samples_since_last_clock_pulse: 0,
28 samples_since_last_add: 0,
29 next_index: 0,
30 });
31 let add_trigger = add.to_trigger_rising_edge();
32 Signal::from_fn(move |ctx| {
33 let mut state = state.borrow_mut();
34 let add_this_sample = add_trigger.sample(ctx);
35 let mut output = add_this_sample;
36 if add_this_sample {
37 state.samples_since_last_add = 0;
38 } else {
39 state.samples_since_last_add += 1;
40 }
41 if clock.sample(ctx) {
42 let next_index = state.next_index;
43 if remove.sample(ctx) {
44 state.sequence[next_index] = false;
45 }
46 output = state.sequence[next_index];
50 if state.samples_since_last_add < state.samples_since_last_clock_pulse / 2 {
51 state.sequence[next_index] = true;
52 } else {
53 if state.samples_since_last_add < state.samples_since_last_clock_pulse {
54 state.sequence[(next_index + length - 1) % length] = true;
55 }
56 if add.sample(ctx) {
57 state.sequence[next_index] = true;
58 output = true;
61 }
62 }
63 state.samples_since_last_clock_pulse = 0;
64 state.next_index = (state.next_index + 1) % length;
65 } else {
66 state.samples_since_last_clock_pulse += 1;
67 }
68 output
69 })
70 }
71
72 pub fn trigger(self) -> Trigger {
73 self.signal().to_trigger_raw()
74 }
75}
76
77pub struct ClockedMidiNoteMonophonicLooper {
78 pub clock: Trigger,
79 pub input_gate: Gate,
80 pub input_midi_index: Su8,
81 pub clear: Gate,
82 pub length: usize,
83}
84
85impl ClockedMidiNoteMonophonicLooper {
86 pub fn signal(self) -> (Gate, Su8) {
87 let Self {
88 clock,
89 input_gate,
90 input_midi_index,
91 clear,
92 length,
93 } = self;
94 #[derive(Clone, Copy)]
95 struct Entry {
96 midi_index: u8,
97 key_down: bool,
98 key_up: bool,
99 }
100 struct State {
101 sequence: Vec<Entry>,
102 samples_since_last_clock_pulse: usize,
103 next_index: usize,
104 gate_state: bool,
105 tap: bool,
106 output: (bool, u8),
107 last_period_samples: usize,
108 }
109 let state = RefCell::new(State {
110 sequence: (0..length)
111 .map(|_| Entry {
112 midi_index: 0,
113 key_down: false,
114 key_up: false,
115 })
116 .collect::<Vec<_>>(),
117 samples_since_last_clock_pulse: 0,
118 next_index: 0,
119 gate_state: false,
120 tap: false,
121 output: (false, 0),
122 last_period_samples: 0,
123 });
124 let combined_signal = Signal::from_fn(move |ctx| {
125 let mut state = state.borrow_mut();
126 if state.tap {
127 state.tap = false;
128 state.output.0 = false;
129 }
130 state.samples_since_last_clock_pulse += 1;
131 if clock.sample(ctx) {
132 let next_index = state.next_index;
133 if clear.sample(ctx) {
134 let entry = &mut state.sequence[next_index];
135 entry.key_down = false;
136 entry.key_up = true;
137 }
138 if input_gate.sample(ctx) {
139 let entry = &mut state.sequence[next_index];
140 entry.midi_index = input_midi_index.sample(ctx);
141 entry.key_down = true;
142 entry.key_up = false;
143 }
144 let entry = state.sequence[next_index];
145 if entry.key_down {
146 state.output = (true, entry.midi_index);
147 if entry.key_up {
148 state.tap = true;
149 }
150 } else {
151 state.output.0 = false;
152 }
153 state.next_index = (next_index + 1) % length;
154 state.last_period_samples = state.samples_since_last_clock_pulse;
155 state.samples_since_last_clock_pulse = 0;
156 }
157 let next_gate_state = input_gate.sample(ctx);
158 if next_gate_state {
159 if !state.gate_state {
160 let index_to_update =
162 if state.samples_since_last_clock_pulse < state.last_period_samples / 2 {
163 (state.next_index + length - 1) % length
164 } else {
165 state.next_index
166 };
167 let entry = &mut state.sequence[index_to_update];
168 entry.midi_index = input_midi_index.sample(ctx);
169 entry.key_down = true;
170 entry.key_up = false;
171 }
172 state.output = (true, input_midi_index.sample(ctx));
173 } else {
174 if state.gate_state {
175 state.output = (false, input_midi_index.sample(ctx));
177 let index_to_update =
178 if state.samples_since_last_clock_pulse < state.last_period_samples / 2 {
179 (state.next_index + length - 1) % length
180 } else {
181 state.next_index
182 };
183 let entry = &mut state.sequence[index_to_update];
184 entry.key_up = true;
185 }
186 }
187 state.gate_state = next_gate_state;
188 state.output
189 });
190 let gate = combined_signal.map(|s| s.0).to_gate();
191 let midi_index = combined_signal.map(|s| s.1);
192 (gate, midi_index)
193 }
194}