1use crate::automaton::Range;
7use crate::control::Transform;
8
9#[derive(Debug, Clone)]
15pub struct ValueConverter {
16 input_range: Range,
17 output_range: Range,
18 transform: Transform,
19}
20
21impl ValueConverter {
22 pub fn new(input_range: Range, output_range: Range, transform: Transform) -> Self {
24 Self {
25 input_range,
26 output_range,
27 transform,
28 }
29 }
30
31 pub fn convert(&self, value: f64) -> f64 {
33 let norm = self.input_range.normalize(value);
34
35 let transformed = match self.transform {
36 Transform::Linear => norm,
37 Transform::Exponential => norm * norm,
38 Transform::Logarithmic => (1.0 + norm * 9.0).log10(),
39 Transform::Inverted => 1.0 - norm,
40 Transform::Custom(ref f) => f(norm as f32) as f64,
41 };
42
43 self.output_range.denormalize(transformed)
44 }
45
46 pub fn convert_inverse(&self, value: f64) -> f64 {
48 let norm = self.output_range.normalize(value);
49 self.input_range.denormalize(norm)
50 }
51}
52
53pub fn midi_to_normalized(midi: u8) -> f64 {
55 midi as f64 / 127.0
56}
57
58pub fn normalized_to_midi(norm: f64) -> u8 {
60 (norm.clamp(0.0, 1.0) * 127.0).round() as u8
61}
62
63pub fn freq_to_midi_note(freq: f64) -> f64 {
65 69.0 + 12.0 * (freq / 440.0).log2()
66}
67
68pub fn midi_note_to_freq(note: f64) -> f64 {
70 440.0 * 2.0_f64.powf((note - 69.0) / 12.0)
71}
72
73#[derive(Debug, Clone)]
79pub struct Metronome {
80 bpm: f64,
81 last_tick: f64,
82 next_tick: f64,
83 quarter_duration: f64,
84}
85
86impl Metronome {
87 pub fn new(bpm: f64) -> Self {
89 let quarter_duration = 60.0 / bpm;
90 Self {
91 bpm,
92 last_tick: 0.0,
93 next_tick: quarter_duration,
94 quarter_duration,
95 }
96 }
97
98 pub fn update(&mut self, time: f64) -> bool {
100 if time >= self.next_tick {
101 self.last_tick = self.next_tick;
102 self.next_tick += self.quarter_duration;
103 true
104 } else {
105 false
106 }
107 }
108
109 pub fn phase(&self, time: f64) -> f64 {
111 ((time - self.last_tick) / self.quarter_duration).clamp(0.0, 1.0)
112 }
113
114 pub fn set_bpm(&mut self, bpm: f64) {
116 self.bpm = bpm;
117 self.quarter_duration = 60.0 / bpm;
118 self.next_tick = self.last_tick + self.quarter_duration;
119 }
120
121 pub fn reset(&mut self) {
123 self.last_tick = 0.0;
124 self.next_tick = self.quarter_duration;
125 }
126}
127
128pub fn note_duration_to_seconds(note_type: NoteType, bpm: f64) -> f64 {
130 let quarter = 60.0 / bpm;
131 match note_type {
132 NoteType::Whole => quarter * 4.0,
133 NoteType::Half => quarter * 2.0,
134 NoteType::Quarter => quarter,
135 NoteType::Eighth => quarter / 2.0,
136 NoteType::Sixteenth => quarter / 4.0,
137 NoteType::ThirtySecond => quarter / 8.0,
138 NoteType::Dotted(n) => note_duration_to_seconds(*n, bpm) * 1.5,
139 NoteType::Triplet(n) => note_duration_to_seconds(*n, bpm) * 2.0 / 3.0,
140 }
141}
142
143#[derive(Debug, Clone)]
145pub enum NoteType {
146 Whole,
148 Half,
150 Quarter,
152 Eighth,
154 Sixteenth,
156 ThirtySecond,
158 Dotted(Box<NoteType>),
160 Triplet(Box<NoteType>),
162}
163
164#[derive(Debug, Default)]
170pub struct EventRecorder {
171 events: Vec<RecordedEvent>,
172}
173
174#[derive(Debug, Clone)]
176pub struct RecordedEvent {
177 pub time: f64,
179 pub event_type: String,
181 pub value: f64,
183 pub data: String,
185}
186
187impl EventRecorder {
188 pub fn new() -> Self {
190 Self { events: Vec::new() }
191 }
192
193 pub fn record(&mut self, time: f64, event_type: &str, value: f64, data: &str) {
195 self.events.push(RecordedEvent {
196 time,
197 event_type: event_type.to_string(),
198 value,
199 data: data.to_string(),
200 });
201 }
202
203 pub fn events(&self) -> &[RecordedEvent] {
205 &self.events
206 }
207
208 pub fn clear(&mut self) {
210 self.events.clear();
211 }
212
213 pub fn find_by_type(&self, event_type: &str) -> Vec<&RecordedEvent> {
215 self.events
216 .iter()
217 .filter(|e| e.event_type == event_type)
218 .collect()
219 }
220}
221
222pub struct TestSignalGenerator {
228 signal_type: TestSignalType,
229 params: TestSignalParams,
230}
231
232#[derive(Debug, Clone)]
234pub enum TestSignalType {
235 Sine,
237 Square,
239 Saw,
241 Noise,
243 Envelope,
245}
246
247#[derive(Debug, Clone)]
249pub struct TestSignalParams {
250 pub frequency: f64,
252 pub amplitude: f64,
254 pub offset: f64,
256 pub duration: f64,
258}
259
260impl TestSignalGenerator {
261 pub fn new(signal_type: TestSignalType, params: TestSignalParams) -> Self {
263 Self {
264 signal_type,
265 params,
266 }
267 }
268
269 pub fn generate(&self, time: f64) -> f64 {
271 if time > self.params.duration {
272 return 0.0;
273 }
274
275 match self.signal_type {
276 TestSignalType::Sine => {
277 let phase = 2.0 * std::f64::consts::PI * self.params.frequency * time;
278 self.params.offset + self.params.amplitude * phase.sin()
279 }
280
281 TestSignalType::Square => {
282 let phase = (self.params.frequency * time) % 1.0;
283 let value = if phase < 0.5 { 1.0 } else { -1.0 };
284 self.params.offset + self.params.amplitude * value
285 }
286
287 TestSignalType::Saw => {
288 let phase = (self.params.frequency * time) % 1.0;
289 let value = 2.0 * phase - 1.0;
290 self.params.offset + self.params.amplitude * value
291 }
292
293 TestSignalType::Noise => {
294 use rand::Rng;
295 let mut rng = rand::thread_rng();
296 self.params.offset + self.params.amplitude * (rng.gen::<f64>() * 2.0 - 1.0)
297 }
298
299 TestSignalType::Envelope => {
300 let attack = 0.1;
301 let decay = 0.2;
302 let sustain = 0.7;
303 let release = 0.3;
304
305 if time < attack {
306 (time / attack) * self.params.amplitude
307 } else if time < attack + decay {
308 (1.0 - (1.0 - sustain) * ((time - attack) / decay)) * self.params.amplitude
309 } else if time < self.params.duration - release {
310 sustain * self.params.amplitude
311 } else {
312 let rel_time = time - (self.params.duration - release);
313 (sustain * (1.0 - rel_time / release)) * self.params.amplitude
314 }
315 }
316 }
317 }
318}
319
320#[cfg(test)]
325mod tests {
326 use super::*;
327
328 #[test]
329 fn test_value_converter() {
330 let converter = ValueConverter::new(
331 Range::new(0.0, 127.0),
332 Range::new(0.0, 1.0),
333 Transform::Linear,
334 );
335
336 let result = converter.convert(64.0);
337 assert!((result - 0.5).abs() < 0.01);
338 }
339
340 #[test]
341 fn test_metronome() {
342 let mut metro = Metronome::new(120.0);
343
344 assert!(!metro.update(0.2));
345 assert!(metro.update(0.6));
346 assert!((metro.phase(0.6) - 0.2).abs() < 0.01);
347 }
348
349 #[test]
350 fn test_test_signal() {
351 let params = TestSignalParams {
352 frequency: 1.0,
353 amplitude: 1.0,
354 offset: 0.0,
355 duration: 2.0,
356 };
357
358 let gen = TestSignalGenerator::new(TestSignalType::Sine, params);
359 let val = gen.generate(0.25);
360 assert!((val - 1.0).abs() < 0.01);
361 }
362}