1use crate::automaton::Range;
9use crate::control::Transform;
10
11#[derive(Debug, Clone)]
17pub struct ValueConverter {
18 input_range: Range,
20 output_range: Range,
22 transform: Transform,
24}
25
26impl ValueConverter {
27 pub fn new(input_range: Range, output_range: Range, transform: Transform) -> Self {
29 Self {
30 input_range,
31 output_range,
32 transform,
33 }
34 }
35
36 pub fn convert(&self, value: f64) -> f64 {
38 let norm = self.input_range.normalize(value);
40
41 let transformed = match self.transform {
43 Transform::Linear => norm,
44 Transform::Exponential => norm * norm,
45 Transform::Logarithmic => (1.0 + norm * 9.0).log10(),
46 Transform::Inverted => 1.0 - norm,
47 Transform::Custom(ref f) => f(norm as f32) as f64,
48 };
49
50 self.output_range.denormalize(transformed)
52 }
53
54 pub fn convert_inverse(&self, value: f64) -> f64 {
56 let norm = self.output_range.normalize(value);
58 self.input_range.denormalize(norm)
59 }
60}
61
62pub fn midi_to_normalized(midi: u8) -> f64 {
64 midi as f64 / 127.0
65}
66
67pub fn normalized_to_midi(norm: f64) -> u8 {
69 (norm.clamp(0.0, 1.0) * 127.0).round() as u8
70}
71
72pub fn freq_to_midi_note(freq: f64) -> f64 {
74 69.0 + 12.0 * (freq / 440.0).log2()
75}
76
77pub fn midi_note_to_freq(note: f64) -> f64 {
79 440.0 * 2.0_f64.powf((note - 69.0) / 12.0)
80}
81
82#[derive(Debug, Clone)]
88pub struct Metronome {
89 bpm: f64,
91 last_tick: f64,
93 next_tick: f64,
95 quarter_duration: f64,
97}
98
99impl Metronome {
100 pub fn new(bpm: f64) -> Self {
102 let quarter_duration = 60.0 / bpm;
103 Self {
104 bpm,
105 last_tick: 0.0,
106 next_tick: quarter_duration,
107 quarter_duration,
108 }
109 }
110
111 pub fn update(&mut self, time: f64) -> bool {
113 if time >= self.next_tick {
114 self.last_tick = self.next_tick;
115 self.next_tick += self.quarter_duration;
116 true
117 } else {
118 false
119 }
120 }
121
122 pub fn phase(&self, time: f64) -> f64 {
124 ((time - self.last_tick) / self.quarter_duration).clamp(0.0, 1.0)
125 }
126
127 pub fn set_bpm(&mut self, bpm: f64) {
129 self.bpm = bpm;
130 self.quarter_duration = 60.0 / bpm;
131 self.next_tick = self.last_tick + self.quarter_duration;
132 }
133
134 pub fn reset(&mut self) {
136 self.last_tick = 0.0;
137 self.next_tick = self.quarter_duration;
138 }
139}
140
141pub fn note_duration_to_seconds(note_type: NoteType, bpm: f64) -> f64 {
143 let quarter = 60.0 / bpm;
144 match note_type {
145 NoteType::Whole => quarter * 4.0,
146 NoteType::Half => quarter * 2.0,
147 NoteType::Quarter => quarter,
148 NoteType::Eighth => quarter / 2.0,
149 NoteType::Sixteenth => quarter / 4.0,
150 NoteType::ThirtySecond => quarter / 8.0,
151 NoteType::Dotted(n) => note_duration_to_seconds(*n, bpm) * 1.5,
152 NoteType::Triplet(n) => note_duration_to_seconds(*n, bpm) * 2.0 / 3.0,
153 }
154}
155
156#[derive(Debug, Clone)]
158pub enum NoteType {
159 Whole,
160 Half,
161 Quarter,
162 Eighth,
163 Sixteenth,
164 ThirtySecond,
165 Dotted(Box<NoteType>),
166 Triplet(Box<NoteType>),
167}
168
169#[derive(Debug, Default)]
175pub struct EventRecorder {
176 events: Vec<RecordedEvent>,
178}
179
180#[derive(Debug, Clone)]
182pub struct RecordedEvent {
183 pub time: f64,
185 pub event_type: String,
187 pub value: f64,
189 pub data: String,
191}
192
193impl EventRecorder {
194 pub fn new() -> Self {
196 Self { events: Vec::new() }
197 }
198
199 pub fn record(&mut self, time: f64, event_type: &str, value: f64, data: &str) {
201 self.events.push(RecordedEvent {
202 time,
203 event_type: event_type.to_string(),
204 value,
205 data: data.to_string(),
206 });
207 }
208
209 pub fn events(&self) -> &[RecordedEvent] {
211 &self.events
212 }
213
214 pub fn clear(&mut self) {
216 self.events.clear();
217 }
218
219 pub fn find_by_type(&self, event_type: &str) -> Vec<&RecordedEvent> {
221 self.events
222 .iter()
223 .filter(|e| e.event_type == event_type)
224 .collect()
225 }
226}
227
228pub struct TestSignalGenerator {
234 signal_type: TestSignalType,
236 params: TestSignalParams,
238}
239
240#[derive(Debug, Clone)]
242pub enum TestSignalType {
243 Sine,
245 Square,
247 Saw,
249 Noise,
251 Envelope,
253}
254
255#[derive(Debug, Clone)]
257pub struct TestSignalParams {
258 pub frequency: f64,
260 pub amplitude: f64,
262 pub offset: f64,
264 pub duration: f64,
266}
267
268impl TestSignalGenerator {
269 pub fn new(signal_type: TestSignalType, params: TestSignalParams) -> Self {
271 Self {
272 signal_type,
273 params,
274 }
275 }
276
277 pub fn generate(&self, time: f64) -> f64 {
279 if time > self.params.duration {
280 return 0.0;
281 }
282
283 match self.signal_type {
284 TestSignalType::Sine => {
285 let phase = 2.0 * std::f64::consts::PI * self.params.frequency * time;
286 self.params.offset + self.params.amplitude * phase.sin()
287 }
288
289 TestSignalType::Square => {
290 let phase = (self.params.frequency * time) % 1.0;
291 let value = if phase < 0.5 { 1.0 } else { -1.0 };
292 self.params.offset + self.params.amplitude * value
293 }
294
295 TestSignalType::Saw => {
296 let phase = (self.params.frequency * time) % 1.0;
297 let value = 2.0 * phase - 1.0;
298 self.params.offset + self.params.amplitude * value
299 }
300
301 TestSignalType::Noise => {
302 use rand::Rng;
303 let mut rng = rand::thread_rng();
304 self.params.offset + self.params.amplitude * (rng.gen::<f64>() * 2.0 - 1.0)
305 }
306
307 TestSignalType::Envelope => {
308 let attack = 0.1;
310 let decay = 0.2;
311 let sustain = 0.7;
312 let release = 0.3;
313
314 if time < attack {
315 (time / attack) * self.params.amplitude
316 } else if time < attack + decay {
317 (1.0 - (1.0 - sustain) * ((time - attack) / decay)) * self.params.amplitude
318 } else if time < self.params.duration - release {
319 sustain * self.params.amplitude
320 } else {
321 let rel_time = time - (self.params.duration - release);
322 (sustain * (1.0 - rel_time / release)) * self.params.amplitude
323 }
324 }
325 }
326 }
327}
328
329#[cfg(test)]
334mod tests {
335 use super::*;
336
337 #[test]
338 fn test_value_converter() {
339 let converter = ValueConverter::new(
340 Range::new(0.0, 127.0),
341 Range::new(0.0, 1.0),
342 Transform::Linear,
343 );
344
345 let result = converter.convert(64.0);
346 assert!((result - 0.5).abs() < 0.01);
347 }
348
349 #[test]
350 fn test_metronome() {
351 let mut metro = Metronome::new(120.0); assert!(!metro.update(0.2));
354 assert!(metro.update(0.6));
355 assert!((metro.phase(0.6) - 0.2).abs() < 0.01);
356 }
357
358 #[test]
359 fn test_test_signal() {
360 let params = TestSignalParams {
361 frequency: 1.0,
362 amplitude: 1.0,
363 offset: 0.0,
364 duration: 2.0,
365 };
366
367 let gen = TestSignalGenerator::new(TestSignalType::Sine, params);
368 let val = gen.generate(0.25);
369 assert!((val - 1.0).abs() < 0.01);
370 }
371}