1use alloc::collections::BTreeSet;
4use alloc::vec::Vec;
5
6use super::curve::CurveType;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct AutomationPoint {
12 pub time: f64,
14 pub sample_position: Option<u64>,
16 pub value: f32,
17 pub curve: CurveType,
19}
20
21impl AutomationPoint {
22 pub const fn new(time: f64, value: f32) -> Self {
23 Self {
24 time,
25 sample_position: None,
26 value,
27 curve: CurveType::Linear,
28 }
29 }
30
31 pub const fn with_curve(time: f64, value: f32, curve: CurveType) -> Self {
32 Self {
33 time,
34 sample_position: None,
35 value,
36 curve,
37 }
38 }
39
40 pub const fn with_samples(
41 time: f64,
42 sample_position: u64,
43 value: f32,
44 curve: CurveType,
45 ) -> Self {
46 Self {
47 time,
48 sample_position: Some(sample_position),
49 value,
50 curve,
51 }
52 }
53
54 pub fn set_sample_position(&mut self, sample: u64) {
55 self.sample_position = Some(sample);
56 }
57}
58
59impl PartialEq for AutomationPoint {
62 fn eq(&self, other: &Self) -> bool {
63 (self.time - other.time).abs() < f64::EPSILON
64 && (self.value - other.value).abs() < f32::EPSILON
65 }
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct AutomationEnvelope<T> {
74 pub target: T,
75 pub points: Vec<AutomationPoint>,
77 pub enabled: bool,
78 pub min_value: Option<f32>,
79 pub max_value: Option<f32>,
80 pub step_size: Option<f32>,
81}
82
83impl<T> AutomationEnvelope<T> {
84 pub fn new(target: T) -> Self {
85 Self {
86 target,
87 points: Vec::new(),
88 enabled: true,
89 min_value: None,
90 max_value: None,
91 step_size: None,
92 }
93 }
94
95 pub fn with_min(mut self, min: f32) -> Self {
96 self.min_value = Some(min);
97 self
98 }
99
100 pub fn with_max(mut self, max: f32) -> Self {
101 self.max_value = Some(max);
102 self
103 }
104
105 pub fn with_range(mut self, min: f32, max: f32) -> Self {
106 self.min_value = Some(min);
107 self.max_value = Some(max);
108 self
109 }
110
111 pub fn with_step(mut self, step: f32) -> Self {
112 self.step_size = Some(step);
113 self
114 }
115
116 pub fn with_point(mut self, point: AutomationPoint) -> Self {
118 self.add_point(point);
119 self
120 }
121
122 pub fn with_points(mut self, points: impl IntoIterator<Item = AutomationPoint>) -> Self {
123 for point in points {
124 self.add_point(point);
125 }
126 self
127 }
128
129 pub fn add_point(&mut self, point: AutomationPoint) -> &mut Self {
131 let pos = self
132 .points
133 .binary_search_by(|p| p.time.total_cmp(&point.time));
134
135 match pos {
136 Ok(idx) => {
137 self.points[idx] = point;
138 }
139 Err(idx) => {
140 self.points.insert(idx, point);
141 }
142 }
143 self
144 }
145
146 pub fn remove_point_at(&mut self, time: f64) -> &mut Self {
147 if let Some(pos) = self
148 .points
149 .iter()
150 .position(|p| (p.time - time).abs() < 0.001)
151 {
152 self.points.remove(pos);
153 }
154 self
155 }
156
157 pub fn remove_point(&mut self, index: usize) -> &mut Self {
158 if index < self.points.len() {
159 self.points.remove(index);
160 }
161 self
162 }
163
164 #[must_use]
165 pub fn get_point(&self, index: usize) -> Option<&AutomationPoint> {
166 self.points.get(index)
167 }
168
169 pub fn get_point_mut(&mut self, index: usize) -> Option<&mut AutomationPoint> {
170 self.points.get_mut(index)
171 }
172
173 pub fn clear(&mut self) -> &mut Self {
174 self.points.clear();
175 self
176 }
177
178 #[must_use]
179 pub fn len(&self) -> usize {
180 self.points.len()
181 }
182
183 #[must_use]
184 pub fn is_empty(&self) -> bool {
185 self.points.is_empty()
186 }
187
188 #[must_use]
189 #[inline]
190 pub fn get_value_at(&self, time: f64) -> Option<f32> {
191 if !self.enabled || self.points.is_empty() {
192 return None;
193 }
194
195 if self.points.len() == 1 {
196 return Some(self.apply_constraints(self.points[0].value));
197 }
198
199 if time <= self.points[0].time {
200 return Some(self.apply_constraints(self.points[0].value));
201 }
202
203 let last_idx = self.points.len() - 1;
204 if time >= self.points[last_idx].time {
205 return Some(self.apply_constraints(self.points[last_idx].value));
206 }
207
208 let (prev_idx, next_idx) = self.find_surrounding_indices(time)?;
209 let prev = &self.points[prev_idx];
210 let next = &self.points[next_idx];
211
212 let time_span = next.time - prev.time;
213 let t = if time_span > 0.0 {
214 ((time - prev.time) / time_span) as f32
215 } else {
216 0.0
217 };
218
219 let value = prev.curve.interpolate(prev.value, next.value, t);
220 Some(self.apply_constraints(value))
221 }
222
223 #[must_use]
224 #[inline]
226 pub fn get_value_at_sample(&self, sample: u64) -> Option<f32> {
227 if !self.enabled || self.points.is_empty() {
228 return None;
229 }
230
231 if self.points.len() == 1 {
232 return Some(self.apply_constraints(self.points[0].value));
233 }
234
235 let first_sample = self.points[0].sample_position.unwrap_or(0);
237
238 if sample <= first_sample {
239 return Some(self.apply_constraints(self.points[0].value));
240 }
241
242 let last_idx = self.points.len() - 1;
243 let last_sample = self.points[last_idx]
245 .sample_position
246 .unwrap_or((self.points[last_idx].time * 48000.0) as u64);
247
248 if sample >= last_sample {
249 return Some(self.apply_constraints(self.points[last_idx].value));
250 }
251
252 let (prev_idx, next_idx) = self.find_surrounding_samples(sample)?;
253 let prev = &self.points[prev_idx];
254 let next = &self.points[next_idx];
255
256 let prev_sample = prev.sample_position.unwrap_or((prev.time * 48000.0) as u64);
257 let next_sample = next.sample_position.unwrap_or((next.time * 48000.0) as u64);
258
259 let sample_span = next_sample - prev_sample;
260 let t = if sample_span > 0 {
261 (sample - prev_sample) as f32 / sample_span as f32
262 } else {
263 0.0
264 };
265
266 let value = prev.curve.interpolate(prev.value, next.value, t);
267 Some(self.apply_constraints(value))
268 }
269
270 fn apply_constraints(&self, mut value: f32) -> f32 {
271 if let Some(min) = self.min_value {
272 value = value.max(min);
273 }
274 if let Some(max) = self.max_value {
275 value = value.min(max);
276 }
277
278 if let Some(step) = self.step_size {
279 if step > 0.0 {
280 value = libm::roundf(value / step) * step;
281 }
282 }
283
284 value
285 }
286
287 fn find_surrounding_indices(&self, time: f64) -> Option<(usize, usize)> {
288 let pos = self.points.binary_search_by(|p| p.time.total_cmp(&time));
289
290 match pos {
291 Ok(exact) => Some((exact, exact)),
292 Err(insert_pos) => {
293 if insert_pos == 0 || insert_pos >= self.points.len() {
294 None
295 } else {
296 Some((insert_pos - 1, insert_pos))
297 }
298 }
299 }
300 }
301
302 fn find_surrounding_samples(&self, sample: u64) -> Option<(usize, usize)> {
303 let pos = self.points.binary_search_by_key(&sample, |p| {
304 p.sample_position.unwrap_or((p.time * 48000.0) as u64)
305 });
306
307 match pos {
308 Ok(exact) => Some((exact, exact)),
309 Err(insert_pos) => {
310 if insert_pos == 0 || insert_pos >= self.points.len() {
311 None
312 } else {
313 Some((insert_pos - 1, insert_pos))
314 }
315 }
316 }
317 }
318
319 pub fn sort_points(&mut self) -> &mut Self {
321 self.points.sort_by(|a, b| a.time.total_cmp(&b.time));
322 self
323 }
324
325 pub fn validate(&mut self) {
326 for i in 1..self.points.len() {
327 if self.points[i].time < self.points[i - 1].time {
328 self.sort_points();
329 break;
330 }
331 }
332
333 let mut seen_times = BTreeSet::new();
334 self.points.retain(|p| seen_times.insert(p.time.to_bits()));
335 }
336
337 #[must_use]
338 pub fn get_range_samples(&self, start_sample: u64, end_sample: u64) -> Option<(f32, f32)> {
339 if self.points.is_empty() {
340 return None;
341 }
342
343 let mut min = f32::INFINITY;
344 let mut max = f32::NEG_INFINITY;
345
346 let sample_step = ((end_sample - start_sample) / 100).max(1);
347 let mut current = start_sample;
348
349 while current <= end_sample {
350 if let Some(value) = self.get_value_at_sample(current) {
351 min = min.min(value);
352 max = max.max(value);
353 }
354 current += sample_step;
355 }
356
357 if min.is_finite() {
358 Some((min, max))
359 } else {
360 None
361 }
362 }
363
364 #[must_use]
365 pub fn get_range(&self, start_time: f64, end_time: f64) -> Option<(f32, f32)> {
366 if self.points.is_empty() {
367 return None;
368 }
369
370 let mut min = f32::INFINITY;
371 let mut max = f32::NEG_INFINITY;
372
373 let sample_count = 100;
374 let step = (end_time - start_time) / sample_count as f64;
375
376 for i in 0..=sample_count {
377 let time = start_time + step * i as f64;
378 if let Some(value) = self.get_value_at(time) {
379 min = min.min(value);
380 max = max.max(value);
381 }
382 }
383
384 for point in &self.points {
385 if point.time >= start_time && point.time <= end_time {
386 min = min.min(point.value);
387 max = max.max(point.value);
388 }
389 }
390
391 if min.is_finite() {
392 Some((min, max))
393 } else {
394 None
395 }
396 }
397
398 pub fn shift_points(&mut self, offset: f64) -> &mut Self {
399 for point in &mut self.points {
400 point.time += offset;
401 }
402 self
403 }
404
405 pub fn scale_time(&mut self, factor: f64) -> &mut Self {
406 if factor > 0.0 {
407 for point in &mut self.points {
408 point.time *= factor;
409 }
410 }
411 self
412 }
413
414 pub fn trim(&mut self, start_time: f64, end_time: f64) -> &mut Self {
415 self.points
416 .retain(|p| p.time >= start_time && p.time <= end_time);
417 self
418 }
419
420 pub fn reverse(&mut self) -> &mut Self {
421 if self.points.is_empty() {
422 return self;
423 }
424
425 let max_time = self.points.last().unwrap().time;
426
427 for point in &mut self.points {
428 point.time = max_time - point.time;
429 }
430
431 self.points.reverse();
432 self
433 }
434
435 pub fn invert_values(&mut self, min: f32, max: f32) -> &mut Self {
436 for point in &mut self.points {
437 point.value = max - (point.value - min);
438 }
439 self
440 }
441
442 pub fn quantize_time(&mut self, grid: f64) -> &mut Self {
443 if grid <= 0.0 {
444 return self;
445 }
446
447 for point in &mut self.points {
448 point.time = libm::round(point.time / grid) * grid;
449 }
450
451 self.validate();
452 self
453 }
454
455 pub fn simplify(&mut self, tolerance: f32) -> &mut Self {
457 if self.points.len() <= 2 {
458 return self;
459 }
460
461 let mut simplified = Vec::new();
462 simplified.push(self.points[0].clone());
463
464 for i in 1..self.points.len() - 1 {
465 let prev = &self.points[i - 1];
466 let curr = &self.points[i];
467 let next = &self.points[i + 1];
468
469 let time_span = next.time - prev.time;
470 let t = ((curr.time - prev.time) / time_span) as f32;
471 let interpolated = prev.curve.interpolate(prev.value, next.value, t);
472
473 if (curr.value - interpolated).abs() > tolerance {
474 simplified.push(curr.clone());
475 }
476 }
477
478 simplified.push(self.points.last().unwrap().clone());
479 self.points = simplified;
480 self
481 }
482
483 #[must_use]
484 pub fn to_buffer(&self, sample_rate: f64, duration: f64) -> Vec<f32> {
485 let num_samples = (duration * sample_rate) as usize;
486 (0..num_samples)
487 .map(|i| self.get_value_at(i as f64 / sample_rate).unwrap_or(0.0))
488 .collect()
489 }
490
491 #[must_use]
492 pub fn iter_samples(&self, sample_rate: f64, duration: f64) -> SampleIterator<'_, T> {
493 SampleIterator {
494 envelope: self,
495 sample_rate,
496 current_sample: 0,
497 total_samples: (duration * sample_rate) as usize,
498 }
499 }
500
501 #[must_use]
502 pub fn get_slope_at(&self, time: f64) -> Option<f32> {
503 if self.points.len() < 2 {
504 return Some(0.0);
505 }
506
507 let delta = 0.001;
508 let v1 = self.get_value_at(time - delta)?;
509 let v2 = self.get_value_at(time + delta)?;
510
511 Some((v2 - v1) / (2.0 * delta) as f32)
512 }
513
514 #[must_use]
515 pub fn find_peaks(&self) -> Vec<(f64, f32)> {
516 self.points
517 .windows(3)
518 .filter(|w| w[1].value > w[0].value && w[1].value > w[2].value)
519 .map(|w| (w[1].time, w[1].value))
520 .collect()
521 }
522
523 #[must_use]
524 pub fn find_valleys(&self) -> Vec<(f64, f32)> {
525 self.points
526 .windows(3)
527 .filter(|w| w[1].value < w[0].value && w[1].value < w[2].value)
528 .map(|w| (w[1].time, w[1].value))
529 .collect()
530 }
531}
532
533impl<T: Clone> AutomationEnvelope<T> {
534 pub fn fade_in(target: T, duration: f64, curve: CurveType) -> Self {
536 let mut env = Self::new(target);
537 env.add_point(AutomationPoint::new(0.0, 0.0));
538 env.add_point(AutomationPoint::with_curve(duration, 1.0, curve));
539 env
540 }
541
542 pub fn fade_out(target: T, duration: f64, curve: CurveType) -> Self {
544 let mut env = Self::new(target);
545 env.add_point(AutomationPoint::new(0.0, 1.0));
546 env.add_point(AutomationPoint::with_curve(duration, 0.0, curve));
547 env
548 }
549
550 pub fn pulse(target: T, fade_in: f64, sustain: f64, fade_out: f64, curve: CurveType) -> Self {
552 let mut env = Self::new(target);
553 env.add_point(AutomationPoint::new(0.0, 0.0));
554 env.add_point(AutomationPoint::with_curve(fade_in, 1.0, curve));
555 env.add_point(AutomationPoint::new(fade_in + sustain, 1.0));
556 env.add_point(AutomationPoint::with_curve(
557 fade_in + sustain + fade_out,
558 0.0,
559 curve,
560 ));
561 env
562 }
563
564 pub fn ramp(target: T, duration: f64, start: f32, end_value: f32, curve: CurveType) -> Self {
565 let mut envelope = Self::new(target);
566 envelope.add_point(AutomationPoint::new(0.0, start));
567 envelope.add_point(AutomationPoint::with_curve(duration, end_value, curve));
568 envelope
569 }
570
571 pub fn lfo(target: T, frequency: f64, duration: f64, min: f32, max: f32) -> Self {
573 let period = 1.0 / frequency;
574 let num_cycles = libm::ceil(duration / period) as usize;
575
576 (0..=num_cycles * 4)
577 .map(|i| {
578 let t = i as f64 * period / 4.0;
579 let phase = (i % 4) as f32 / 4.0;
580 let value = min
581 + (max - min) * (libm::sinf(phase * core::f32::consts::PI * 2.0) * 0.5 + 0.5);
582 (t, value)
583 })
584 .take_while(|&(t, _)| t <= duration)
585 .fold(Self::new(target), |mut env, (t, value)| {
586 env.add_point(AutomationPoint::new(t, value));
587 env
588 })
589 }
590}
591
592impl<T: Clone> AutomationEnvelope<T> {
593 #[must_use]
595 pub fn blend(&self, other: &Self, factor: f32) -> Self {
596 let factor = factor.clamp(0.0, 1.0);
597 self.combine(other, |a, b| a * (1.0 - factor) + b * factor)
598 }
599
600 pub fn merge(&mut self, other: &Self, offset: f64) -> &mut Self {
602 for point in &other.points {
603 self.add_point(AutomationPoint {
604 time: point.time + offset,
605 ..point.clone()
606 });
607 }
608 self
609 }
610
611 #[must_use]
613 pub fn add(&self, other: &Self) -> Self {
614 self.combine(other, |a, b| a + b)
615 }
616
617 #[must_use]
619 pub fn multiply(&self, other: &Self) -> Self {
620 self.combine(other, |a, b| a * b)
621 }
622
623 #[must_use]
625 pub fn min(&self, other: &Self) -> Self {
626 self.combine(other, |a, b| a.min(b))
627 }
628
629 #[must_use]
631 pub fn max(&self, other: &Self) -> Self {
632 self.combine(other, |a, b| a.max(b))
633 }
634
635 #[must_use]
636 pub fn subtract(&self, other: &Self) -> Self {
637 self.combine(other, |a, b| a - b)
638 }
639
640 fn combine<F>(&self, other: &Self, op: F) -> Self
641 where
642 F: Fn(f32, f32) -> f32,
643 {
644 let times: BTreeSet<u64> = self
645 .points
646 .iter()
647 .chain(other.points.iter())
648 .map(|p| p.time.to_bits())
649 .collect();
650
651 let mut result = Self::new(self.target.clone());
652 for time in times.into_iter().map(f64::from_bits) {
653 let v1 = self.get_value_at(time).unwrap_or(0.0);
654 let v2 = other.get_value_at(time).unwrap_or(0.0);
655 result.add_point(AutomationPoint::new(time, op(v1, v2)));
656 }
657 result
658 }
659
660 pub fn normalize(&mut self, new_min: f32, new_max: f32) -> &mut Self {
662 let (current_min, current_max) = self
663 .points
664 .iter()
665 .fold((f32::INFINITY, f32::NEG_INFINITY), |(lo, hi), p| {
666 (lo.min(p.value), hi.max(p.value))
667 });
668
669 let range = current_max - current_min;
670 if range > 0.0 {
671 let new_range = new_max - new_min;
672 for point in &mut self.points {
673 point.value = new_min + (point.value - current_min) / range * new_range;
674 }
675 }
676
677 self
678 }
679
680 pub fn scale(&mut self, factor: f32) -> &mut Self {
681 for point in &mut self.points {
682 point.value *= factor;
683 }
684 self
685 }
686
687 pub fn offset(&mut self, amount: f32) -> &mut Self {
688 for point in &mut self.points {
689 point.value += amount;
690 }
691 self
692 }
693
694 pub fn clamp_values(&mut self, min: f32, max: f32) -> &mut Self {
695 for point in &mut self.points {
696 point.value = point.value.clamp(min, max);
697 }
698 self
699 }
700
701 pub fn apply_fade_in(&mut self, duration: f64, curve: CurveType) -> &mut Self {
702 if duration <= 0.0 {
703 return self;
704 }
705 if let Some(start_time) = self.points.first().map(|p| p.time) {
706 let end_time = start_time + duration;
707 for point in &mut self.points {
708 if point.time <= end_time {
709 let t = ((point.time - start_time) / duration).clamp(0.0, 1.0) as f32;
710 point.value *= curve.interpolate(0.0, 1.0, t);
711 }
712 }
713 }
714 self
715 }
716
717 pub fn apply_fade_out(&mut self, duration: f64, curve: CurveType) -> &mut Self {
718 if duration <= 0.0 {
719 return self;
720 }
721 if let Some(end_time) = self.points.last().map(|p| p.time) {
722 let start_time = end_time - duration;
723 for point in &mut self.points {
724 if point.time >= start_time {
725 let t = ((point.time - start_time) / duration).clamp(0.0, 1.0) as f32;
726 point.value *= curve.interpolate(1.0, 0.0, t);
727 }
728 }
729 }
730 self
731 }
732
733 pub fn apply_fades(
734 &mut self,
735 fade_in_duration: f64,
736 fade_out_duration: f64,
737 curve: CurveType,
738 ) -> &mut Self {
739 self.apply_fade_in(fade_in_duration, curve)
740 .apply_fade_out(fade_out_duration, curve)
741 }
742
743 pub fn apply_gate(&mut self, threshold: f32) -> &mut Self {
744 for point in &mut self.points {
745 point.value = if point.value < threshold {
746 0.0
747 } else {
748 point.value
749 };
750 }
751 self
752 }
753
754 pub fn apply_compression(&mut self, threshold: f32, ratio: f32) -> &mut Self {
756 for point in &mut self.points {
757 if point.value > threshold {
758 let excess = point.value - threshold;
759 point.value = threshold + excess / ratio;
760 }
761 }
762 self
763 }
764}
765
766pub struct SampleIterator<'a, T> {
768 envelope: &'a AutomationEnvelope<T>,
769 sample_rate: f64,
770 current_sample: usize,
771 total_samples: usize,
772}
773
774impl<'a, T> Iterator for SampleIterator<'a, T> {
775 type Item = f32;
776
777 fn next(&mut self) -> Option<Self::Item> {
778 if self.current_sample >= self.total_samples {
779 return None;
780 }
781
782 let time = self.current_sample as f64 / self.sample_rate;
783 self.current_sample += 1;
784
785 self.envelope.get_value_at(time)
786 }
787}
788
789impl<'a, T> ExactSizeIterator for SampleIterator<'a, T> {
790 fn len(&self) -> usize {
791 self.total_samples - self.current_sample
792 }
793}
794
795impl<T> core::ops::Index<usize> for AutomationEnvelope<T> {
796 type Output = AutomationPoint;
797 fn index(&self, index: usize) -> &Self::Output {
798 &self.points[index]
799 }
800}
801
802impl<T> core::ops::IndexMut<usize> for AutomationEnvelope<T> {
803 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
804 &mut self.points[index]
805 }
806}
807
808impl<'a, T> IntoIterator for &'a AutomationEnvelope<T> {
809 type Item = &'a AutomationPoint;
810 type IntoIter = core::slice::Iter<'a, AutomationPoint>;
811 fn into_iter(self) -> Self::IntoIter {
812 self.points.iter()
813 }
814}
815
816impl<'a, T> IntoIterator for &'a mut AutomationEnvelope<T> {
817 type Item = &'a mut AutomationPoint;
818 type IntoIter = core::slice::IterMut<'a, AutomationPoint>;
819 fn into_iter(self) -> Self::IntoIter {
820 self.points.iter_mut()
821 }
822}
823
824impl<T: Default> FromIterator<AutomationPoint> for AutomationEnvelope<T> {
825 fn from_iter<I: IntoIterator<Item = AutomationPoint>>(iter: I) -> Self {
826 let mut env = Self::new(T::default());
827 for point in iter {
828 env.add_point(point);
829 }
830 env
831 }
832}
833
834#[cfg(test)]
835mod tests {
836 use super::*;
837
838 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
840 enum TestTarget {
841 Volume,
842 Pan,
843 }
844
845 #[test]
846 fn test_add_point_maintains_order() {
847 let mut env = AutomationEnvelope::new(TestTarget::Volume);
848 env.add_point(AutomationPoint::new(2.0, 0.5));
849 env.add_point(AutomationPoint::new(1.0, 0.3));
850 env.add_point(AutomationPoint::new(3.0, 0.7));
851
852 assert_eq!(env.points.len(), 3);
853 assert_eq!(env.points[0].time, 1.0);
854 assert_eq!(env.points[1].time, 2.0);
855 assert_eq!(env.points[2].time, 3.0);
856 }
857
858 #[test]
859 fn test_get_value_at() {
860 let mut env = AutomationEnvelope::new(TestTarget::Volume);
861 env.add_point(AutomationPoint::new(0.0, 0.0));
862 env.add_point(AutomationPoint::new(4.0, 1.0));
863
864 let mid_value = env.get_value_at(2.0).unwrap();
866 assert!((mid_value - 0.5).abs() < 0.01);
867
868 assert_eq!(env.get_value_at(-1.0).unwrap(), 0.0);
870
871 assert_eq!(env.get_value_at(5.0).unwrap(), 1.0);
873 }
874
875 #[test]
876 fn test_remove_point() {
877 let mut env = AutomationEnvelope::new(TestTarget::Volume);
878 env.add_point(AutomationPoint::new(1.0, 0.1));
879 env.add_point(AutomationPoint::new(2.0, 0.2));
880 env.add_point(AutomationPoint::new(3.0, 0.3));
881
882 env.remove_point_at(2.0);
883 assert_eq!(env.points.len(), 2);
884 assert_eq!(env.points[1].time, 3.0);
885 }
886
887 #[test]
888 fn test_value_constraints() {
889 let mut env = AutomationEnvelope::new(TestTarget::Volume).with_range(0.0, 1.0);
890 env.add_point(AutomationPoint::new(0.0, -0.5));
891 env.add_point(AutomationPoint::new(4.0, 1.5));
892
893 assert_eq!(env.get_value_at(0.0).unwrap(), 0.0);
895 assert_eq!(env.get_value_at(4.0).unwrap(), 1.0);
896 }
897
898 #[test]
899 fn test_step_quantization() {
900 let mut env = AutomationEnvelope::new(TestTarget::Volume).with_step(0.25);
901 env.add_point(AutomationPoint::new(0.0, 0.0));
902 env.add_point(AutomationPoint::new(4.0, 1.0));
903
904 let value = env.get_value_at(2.0).unwrap();
905 assert_eq!(value, 0.5);
907 }
908
909 #[test]
910 fn test_shift_points() {
911 let mut env = AutomationEnvelope::new(TestTarget::Volume);
912 env.add_point(AutomationPoint::new(0.0, 0.0));
913 env.add_point(AutomationPoint::new(4.0, 1.0));
914
915 env.shift_points(2.0);
916 assert_eq!(env.points[0].time, 2.0);
917 assert_eq!(env.points[1].time, 6.0);
918 }
919
920 #[test]
921 fn test_scale_time() {
922 let mut env = AutomationEnvelope::new(TestTarget::Volume);
923 env.add_point(AutomationPoint::new(0.0, 0.0));
924 env.add_point(AutomationPoint::new(4.0, 1.0));
925
926 env.scale_time(2.0);
927 assert_eq!(env.points[0].time, 0.0);
928 assert_eq!(env.points[1].time, 8.0);
929 }
930
931 #[test]
932 fn test_reverse() {
933 let mut env = AutomationEnvelope::new(TestTarget::Volume);
934 env.add_point(AutomationPoint::new(0.0, 0.0));
935 env.add_point(AutomationPoint::new(2.0, 0.5));
936 env.add_point(AutomationPoint::new(4.0, 1.0));
937
938 env.reverse();
939 assert_eq!(env.points[0].time, 0.0);
940 assert_eq!(env.points[0].value, 1.0);
941 assert_eq!(env.points[2].time, 4.0);
942 assert_eq!(env.points[2].value, 0.0);
943 }
944
945 #[test]
946 fn test_invert_values() {
947 let mut env = AutomationEnvelope::new(TestTarget::Volume);
948 env.add_point(AutomationPoint::new(0.0, 0.0));
949 env.add_point(AutomationPoint::new(4.0, 1.0));
950
951 env.invert_values(0.0, 1.0);
952 assert_eq!(env.points[0].value, 1.0);
953 assert_eq!(env.points[1].value, 0.0);
954 }
955
956 #[test]
957 fn test_preset_fade_in() {
958 let env = AutomationEnvelope::fade_in(TestTarget::Volume, 4.0, CurveType::Linear);
959 assert_eq!(env.points.len(), 2);
960 assert_eq!(env.get_value_at(0.0).unwrap(), 0.0);
961 assert_eq!(env.get_value_at(4.0).unwrap(), 1.0);
962 }
963
964 #[test]
965 fn test_preset_pulse() {
966 let env = AutomationEnvelope::pulse(TestTarget::Volume, 1.0, 2.0, 1.0, CurveType::Linear);
967 assert_eq!(env.points.len(), 4);
968 assert_eq!(env.get_value_at(0.0).unwrap(), 0.0);
969 assert_eq!(env.get_value_at(1.0).unwrap(), 1.0);
970 assert_eq!(env.get_value_at(3.0).unwrap(), 1.0);
971 assert_eq!(env.get_value_at(4.0).unwrap(), 0.0);
972 }
973
974 #[test]
975 fn test_to_buffer() {
976 let mut env = AutomationEnvelope::new(TestTarget::Volume);
977 env.add_point(AutomationPoint::new(0.0, 0.0));
978 env.add_point(AutomationPoint::new(1.0, 1.0));
979
980 let buffer = env.to_buffer(10.0, 1.0);
981 assert_eq!(buffer.len(), 10);
982 assert_eq!(buffer[0], 0.0);
983 assert!((buffer[9] - 0.9).abs() < 0.1);
985 }
986
987 #[test]
988 fn test_iter_samples() {
989 let mut env = AutomationEnvelope::new(TestTarget::Volume);
990 env.add_point(AutomationPoint::new(0.0, 0.0));
991 env.add_point(AutomationPoint::new(1.0, 1.0));
992
993 let samples: Vec<f32> = env.iter_samples(10.0, 1.0).collect();
994 assert_eq!(samples.len(), 10);
995 assert_eq!(samples[0], 0.0);
996 assert!((samples[9] - 0.9).abs() < 0.1);
998 }
999
1000 #[test]
1001 fn test_find_peaks() {
1002 let mut env = AutomationEnvelope::new(TestTarget::Volume);
1003 env.add_point(AutomationPoint::new(0.0, 0.0));
1004 env.add_point(AutomationPoint::new(2.0, 1.0)); env.add_point(AutomationPoint::new(4.0, 0.5));
1006
1007 let peaks = env.find_peaks();
1008 assert_eq!(peaks.len(), 1);
1009 assert_eq!(peaks[0].0, 2.0);
1010 assert_eq!(peaks[0].1, 1.0);
1011 }
1012
1013 #[test]
1014 fn test_blend() {
1015 let mut env1 = AutomationEnvelope::new(TestTarget::Volume);
1016 env1.add_point(AutomationPoint::new(0.0, 0.0));
1017 env1.add_point(AutomationPoint::new(4.0, 0.0));
1018
1019 let mut env2 = AutomationEnvelope::new(TestTarget::Volume);
1020 env2.add_point(AutomationPoint::new(0.0, 1.0));
1021 env2.add_point(AutomationPoint::new(4.0, 1.0));
1022
1023 let blended = env1.blend(&env2, 0.5);
1024 assert!((blended.get_value_at(2.0).unwrap() - 0.5).abs() < 0.01);
1025 }
1026
1027 #[test]
1030 fn test_add() {
1031 let mut env1 = AutomationEnvelope::new(TestTarget::Volume);
1032 env1.add_point(AutomationPoint::new(0.0, 0.5));
1033 env1.add_point(AutomationPoint::new(4.0, 0.5));
1034
1035 let mut env2 = AutomationEnvelope::new(TestTarget::Volume);
1036 env2.add_point(AutomationPoint::new(0.0, 0.3));
1037 env2.add_point(AutomationPoint::new(4.0, 0.3));
1038
1039 let result = env1.add(&env2);
1040 assert!((result.get_value_at(2.0).unwrap() - 0.8).abs() < 0.01);
1041 }
1042
1043 #[test]
1044 fn test_multiply() {
1045 let mut env1 = AutomationEnvelope::new(TestTarget::Volume);
1046 env1.add_point(AutomationPoint::new(0.0, 2.0));
1047 env1.add_point(AutomationPoint::new(4.0, 2.0));
1048
1049 let mut env2 = AutomationEnvelope::new(TestTarget::Volume);
1050 env2.add_point(AutomationPoint::new(0.0, 0.5));
1051 env2.add_point(AutomationPoint::new(4.0, 0.5));
1052
1053 let result = env1.multiply(&env2);
1054 assert!((result.get_value_at(2.0).unwrap() - 1.0).abs() < 0.01);
1055 }
1056
1057 #[test]
1058 fn test_min_max() {
1059 let mut env1 = AutomationEnvelope::new(TestTarget::Volume);
1060 env1.add_point(AutomationPoint::new(0.0, 0.3));
1061 env1.add_point(AutomationPoint::new(4.0, 0.3));
1062
1063 let mut env2 = AutomationEnvelope::new(TestTarget::Volume);
1064 env2.add_point(AutomationPoint::new(0.0, 0.7));
1065 env2.add_point(AutomationPoint::new(4.0, 0.7));
1066
1067 let min_result = env1.min(&env2);
1068 assert!((min_result.get_value_at(2.0).unwrap() - 0.3).abs() < 0.01);
1069
1070 let max_result = env1.max(&env2);
1071 assert!((max_result.get_value_at(2.0).unwrap() - 0.7).abs() < 0.01);
1072 }
1073
1074 #[test]
1077 fn test_normalize() {
1078 let mut env = AutomationEnvelope::new(TestTarget::Volume);
1079 env.add_point(AutomationPoint::new(0.0, 10.0));
1080 env.add_point(AutomationPoint::new(4.0, 20.0));
1081
1082 env.normalize(0.0, 1.0);
1083
1084 assert!((env.get_value_at(0.0).unwrap() - 0.0).abs() < 0.01);
1085 assert!((env.get_value_at(4.0).unwrap() - 1.0).abs() < 0.01);
1086 }
1087
1088 #[test]
1089 fn test_scale_offset() {
1090 let mut env = AutomationEnvelope::new(TestTarget::Volume);
1091 env.add_point(AutomationPoint::new(0.0, 1.0));
1092 env.add_point(AutomationPoint::new(4.0, 1.0));
1093
1094 env.scale(2.0).offset(0.5);
1095
1096 assert!((env.get_value_at(2.0).unwrap() - 2.5).abs() < 0.01);
1097 }
1098
1099 #[test]
1100 fn test_clamp_values() {
1101 let mut env = AutomationEnvelope::new(TestTarget::Volume);
1102 env.add_point(AutomationPoint::new(0.0, -1.0));
1103 env.add_point(AutomationPoint::new(2.0, 0.5));
1104 env.add_point(AutomationPoint::new(4.0, 2.0));
1105
1106 env.clamp_values(0.0, 1.0);
1107
1108 assert_eq!(env.points[0].value, 0.0);
1109 assert_eq!(env.points[1].value, 0.5);
1110 assert_eq!(env.points[2].value, 1.0);
1111 }
1112
1113 #[test]
1116 fn test_apply_fade_in() {
1117 let mut env = AutomationEnvelope::new(TestTarget::Volume);
1118 env.add_point(AutomationPoint::new(0.0, 1.0));
1119 env.add_point(AutomationPoint::new(4.0, 1.0));
1120
1121 env.apply_fade_in(2.0, CurveType::Linear);
1122
1123 assert!((env.get_value_at(0.0).unwrap() - 0.0).abs() < 0.01);
1125 assert!((env.get_value_at(4.0).unwrap() - 1.0).abs() < 0.01);
1127 }
1128
1129 #[test]
1130 fn test_apply_fade_out() {
1131 let mut env = AutomationEnvelope::new(TestTarget::Volume);
1132 env.add_point(AutomationPoint::new(0.0, 1.0));
1133 env.add_point(AutomationPoint::new(4.0, 1.0));
1134
1135 env.apply_fade_out(2.0, CurveType::Linear);
1136
1137 assert!((env.get_value_at(0.0).unwrap() - 1.0).abs() < 0.01);
1139 assert!((env.get_value_at(4.0).unwrap() - 0.0).abs() < 0.01);
1141 }
1142
1143 #[test]
1144 fn test_apply_fades_chaining() {
1145 let mut env = AutomationEnvelope::new(TestTarget::Volume);
1146 env.add_point(AutomationPoint::new(0.0, 1.0));
1147 env.add_point(AutomationPoint::new(2.0, 1.0));
1148 env.add_point(AutomationPoint::new(4.0, 1.0));
1149 env.add_point(AutomationPoint::new(6.0, 1.0));
1150 env.add_point(AutomationPoint::new(8.0, 1.0));
1151
1152 env.apply_fades(2.0, 2.0, CurveType::Linear);
1153
1154 assert!((env.get_value_at(0.0).unwrap() - 0.0).abs() < 0.01);
1156 assert!((env.get_value_at(4.0).unwrap() - 1.0).abs() < 0.1);
1158 assert!((env.get_value_at(8.0).unwrap() - 0.0).abs() < 0.01);
1159 }
1160
1161 #[test]
1162 fn test_apply_gate() {
1163 let mut env = AutomationEnvelope::new(TestTarget::Volume);
1164 env.add_point(AutomationPoint::new(0.0, 0.1));
1165 env.add_point(AutomationPoint::new(2.0, 0.5));
1166 env.add_point(AutomationPoint::new(4.0, 0.2));
1167
1168 env.apply_gate(0.3);
1169
1170 assert_eq!(env.points[0].value, 0.0); assert_eq!(env.points[1].value, 0.5); assert_eq!(env.points[2].value, 0.0); }
1174
1175 #[test]
1176 fn test_apply_compression() {
1177 let mut env = AutomationEnvelope::new(TestTarget::Volume);
1178 env.add_point(AutomationPoint::new(0.0, 0.5));
1179 env.add_point(AutomationPoint::new(2.0, 1.0));
1180
1181 env.apply_compression(0.7, 2.0);
1182
1183 assert_eq!(env.points[0].value, 0.5);
1185 assert!((env.points[1].value - 0.85).abs() < 0.01);
1187 }
1188}