1use crate::traits::{ParamSpec, Processor, ProcessorParams, Transport};
4
5pub struct Bypassed<P> {
7 pub processor: P,
8 source_bypassed: bool,
9 target_bypassed: bool,
10 transition_phase: BypassTransitionPhase,
11 transition_samples: u32,
12}
13
14#[derive(Debug, Clone, Copy)]
15enum BypassTransitionPhase {
16 Stable,
17 FadeOut { remaining: u32 },
18 FadeIn { remaining: u32 },
19}
20
21const DEFAULT_BYPASS_TRANSITION_SAMPLES: u32 = 64;
22const MIN_BYPASS_TRANSITION_SAMPLES: u32 = 16;
23const MAX_BYPASS_TRANSITION_SAMPLES: u32 = 256;
24const BYPASS_TRANSITION_SECONDS: f32 = 0.002;
25
26impl<P> Bypassed<P> {
27 pub fn new(processor: P) -> Self {
29 Self {
30 processor,
31 source_bypassed: false,
32 target_bypassed: false,
33 transition_phase: BypassTransitionPhase::Stable,
34 transition_samples: DEFAULT_BYPASS_TRANSITION_SAMPLES,
35 }
36 }
37
38 #[inline]
39 fn transition_samples_for_rate(sample_rate: f32) -> u32 {
40 if sample_rate <= 0.0 {
41 return DEFAULT_BYPASS_TRANSITION_SAMPLES;
42 }
43
44 ((sample_rate * BYPASS_TRANSITION_SECONDS).round() as u32)
45 .clamp(MIN_BYPASS_TRANSITION_SAMPLES, MAX_BYPASS_TRANSITION_SAMPLES)
46 }
47
48 #[inline]
49 fn apply_gain_ramp(buffer: &mut [&mut [f32]], start_gain: f32, end_gain: f32) {
50 let samples = buffer
51 .iter()
52 .map(|channel| channel.len())
53 .min()
54 .unwrap_or(0);
55 if samples == 0 {
56 return;
57 }
58
59 let denominator = samples.saturating_sub(1) as f32;
60
61 for channel in buffer.iter_mut() {
62 for (idx, sample) in channel.iter_mut().take(samples).enumerate() {
63 let t = if denominator <= 0.0 {
64 1.0
65 } else {
66 idx as f32 / denominator
67 };
68 let gain = start_gain + (end_gain - start_gain) * t;
69 *sample *= gain;
70 }
71 }
72 }
73}
74
75impl<P> Default for Bypassed<P>
76where
77 P: Default,
78{
79 fn default() -> Self {
80 Self::new(P::default())
81 }
82}
83
84impl<P> From<P> for Bypassed<P> {
85 fn from(processor: P) -> Self {
86 Self::new(processor)
87 }
88}
89
90pub struct BypassedParams<PP> {
94 pub inner: PP,
95 pub bypassed: bool,
96}
97
98impl<PP> Default for BypassedParams<PP>
99where
100 PP: Default,
101{
102 fn default() -> Self {
103 Self {
104 inner: PP::default(),
105 bypassed: false,
106 }
107 }
108}
109
110impl<PP> ProcessorParams for BypassedParams<PP>
111where
112 PP: ProcessorParams,
113{
114 fn param_specs() -> &'static [ParamSpec] {
115 fn extend_specs(target: &mut Vec<ParamSpec>, source: &[ParamSpec]) {
116 for spec in source {
117 target.push(ParamSpec {
118 name: spec.name,
119 id_suffix: spec.id_suffix,
120 range: spec.range.clone(),
121 default: spec.default,
122 unit: spec.unit,
123 group: spec.group,
124 });
125 }
126 }
127
128 let inner_specs = PP::param_specs();
129 let mut merged = Vec::with_capacity(inner_specs.len() + 1);
130
131 extend_specs(&mut merged, inner_specs);
132 merged.push(ParamSpec {
133 name: "Bypass",
134 id_suffix: "bypass",
135 range: crate::ParamRange::Stepped { min: 0, max: 1 },
136 default: 0.0,
137 unit: "",
138 group: None,
139 });
140
141 Box::leak(merged.into_boxed_slice())
143 }
144
145 fn from_param_defaults() -> Self {
146 Self {
147 inner: PP::from_param_defaults(),
148 bypassed: false,
149 }
150 }
151
152 fn plain_value_count() -> usize {
153 PP::plain_value_count() + 1
154 }
155
156 fn apply_plain_values(&mut self, values: &[f32]) {
157 let inner_count = PP::plain_value_count();
158 let split_at = inner_count.min(values.len());
159 let (inner_values, bypass_values) = values.split_at(split_at);
160
161 self.inner.apply_plain_values(inner_values);
162
163 if let Some(bypass_value) = bypass_values.first() {
164 self.bypassed = *bypass_value >= 0.5;
165 }
166 }
167}
168
169impl<P> Processor for Bypassed<P>
170where
171 P: Processor,
172{
173 type Params = BypassedParams<P::Params>;
174
175 fn process(&mut self, buffer: &mut [&mut [f32]], transport: &Transport, params: &Self::Params) {
176 if params.bypassed != self.target_bypassed {
177 self.target_bypassed = params.bypassed;
178 if self.source_bypassed != self.target_bypassed {
179 self.transition_phase = BypassTransitionPhase::FadeOut {
180 remaining: self.transition_samples,
181 };
182 }
183 }
184
185 if !self.source_bypassed {
186 self.processor.process(buffer, transport, ¶ms.inner);
187 }
188
189 let samples = buffer
190 .iter()
191 .map(|channel| channel.len())
192 .min()
193 .unwrap_or(0) as u32;
194 if samples == 0 {
195 return;
196 }
197
198 match self.transition_phase {
199 BypassTransitionPhase::Stable => {}
200 BypassTransitionPhase::FadeOut { remaining } => {
201 let used = remaining.min(samples);
202 let total = self.transition_samples.max(1) as f32;
203 let start_gain = remaining as f32 / total;
204 let end_gain = remaining.saturating_sub(used) as f32 / total;
205
206 Self::apply_gain_ramp(buffer, start_gain, end_gain);
207
208 let new_remaining = remaining.saturating_sub(used);
209 if new_remaining == 0 {
210 self.source_bypassed = self.target_bypassed;
211 self.transition_phase = BypassTransitionPhase::FadeIn {
212 remaining: self.transition_samples,
213 };
214 } else {
215 self.transition_phase = BypassTransitionPhase::FadeOut {
216 remaining: new_remaining,
217 };
218 }
219 }
220 BypassTransitionPhase::FadeIn { remaining } => {
221 let used = remaining.min(samples);
222 let total = self.transition_samples.max(1) as f32;
223 let start_gain = 1.0 - (remaining as f32 / total);
224 let end_gain = 1.0 - (remaining.saturating_sub(used) as f32 / total);
225
226 Self::apply_gain_ramp(buffer, start_gain, end_gain);
227
228 let new_remaining = remaining.saturating_sub(used);
229 if new_remaining == 0 {
230 self.transition_phase = BypassTransitionPhase::Stable;
231 } else {
232 self.transition_phase = BypassTransitionPhase::FadeIn {
233 remaining: new_remaining,
234 };
235 }
236 }
237 }
238 }
239
240 fn set_sample_rate(&mut self, sample_rate: f32) {
241 self.transition_samples = Self::transition_samples_for_rate(sample_rate);
242 self.processor.set_sample_rate(sample_rate);
243 }
244
245 fn reset(&mut self) {
246 self.source_bypassed = self.target_bypassed;
247 self.transition_phase = BypassTransitionPhase::Stable;
248 self.processor.reset();
249 }
250}
251
252pub struct Chain<A, B> {
257 pub first: A,
258 pub second: B,
259}
260
261impl<A, B> Default for Chain<A, B>
262where
263 A: Default,
264 B: Default,
265{
266 fn default() -> Self {
267 Self {
268 first: A::default(),
269 second: B::default(),
270 }
271 }
272}
273
274pub struct ChainParams<PA, PB> {
278 pub first: PA,
279 pub second: PB,
280}
281
282impl<PA, PB> Default for ChainParams<PA, PB>
283where
284 PA: Default,
285 PB: Default,
286{
287 fn default() -> Self {
288 Self {
289 first: PA::default(),
290 second: PB::default(),
291 }
292 }
293}
294
295impl<PA, PB> ProcessorParams for ChainParams<PA, PB>
296where
297 PA: ProcessorParams,
298 PB: ProcessorParams,
299{
300 fn param_specs() -> &'static [ParamSpec] {
301 fn extend_specs(target: &mut Vec<ParamSpec>, source: &[ParamSpec]) {
302 for spec in source {
303 target.push(ParamSpec {
304 name: spec.name,
305 id_suffix: spec.id_suffix,
306 range: spec.range.clone(),
307 default: spec.default,
308 unit: spec.unit,
309 group: spec.group,
310 });
311 }
312 }
313
314 let first_specs = PA::param_specs();
331 let second_specs = PB::param_specs();
332
333 let mut merged = Vec::with_capacity(first_specs.len() + second_specs.len());
334
335 extend_specs(&mut merged, first_specs);
336 extend_specs(&mut merged, second_specs);
337
338 Box::leak(merged.into_boxed_slice())
340 }
341
342 fn from_param_defaults() -> Self {
343 Self {
344 first: PA::from_param_defaults(),
345 second: PB::from_param_defaults(),
346 }
347 }
348
349 fn plain_value_count() -> usize {
350 PA::plain_value_count() + PB::plain_value_count()
351 }
352
353 fn apply_plain_values(&mut self, values: &[f32]) {
354 let first_count = PA::plain_value_count();
355 let split_at = first_count.min(values.len());
356 let (first_values, second_values) = values.split_at(split_at);
357
358 self.first.apply_plain_values(first_values);
359 self.second.apply_plain_values(second_values);
360 }
361}
362
363impl<A, B> Processor for Chain<A, B>
364where
365 A: Processor,
366 B: Processor,
367{
368 type Params = ChainParams<A::Params, B::Params>;
369
370 fn process(&mut self, buffer: &mut [&mut [f32]], transport: &Transport, params: &Self::Params) {
371 self.first.process(buffer, transport, ¶ms.first);
373 self.second.process(buffer, transport, ¶ms.second);
374 }
375
376 fn set_sample_rate(&mut self, sample_rate: f32) {
377 self.first.set_sample_rate(sample_rate);
378 self.second.set_sample_rate(sample_rate);
379 }
380
381 fn reset(&mut self) {
382 self.first.reset();
383 self.second.reset();
384 }
385}
386
387#[cfg(test)]
388mod tests {
389 use super::*;
390 use std::sync::{
391 Arc,
392 atomic::{AtomicBool, AtomicU32, Ordering},
393 };
394
395 #[derive(Clone)]
396 struct TestGainParams {
397 level: f32,
398 }
399
400 impl Default for TestGainParams {
401 fn default() -> Self {
402 Self { level: 1.0 }
403 }
404 }
405
406 impl ProcessorParams for TestGainParams {
407 fn param_specs() -> &'static [ParamSpec] {
408 static SPECS: [ParamSpec; 1] = [ParamSpec {
409 name: "Level",
410 id_suffix: "level",
411 range: crate::ParamRange::Linear { min: 0.0, max: 2.0 },
412 default: 1.0,
413 unit: "x",
414 group: None,
415 }];
416 &SPECS
417 }
418
419 fn from_param_defaults() -> Self {
420 Self { level: 1.0 }
421 }
422
423 fn apply_plain_values(&mut self, values: &[f32]) {
424 if let Some(level) = values.first() {
425 self.level = *level;
426 }
427 }
428 }
429
430 #[derive(Default)]
431 struct TestGainDsp;
432
433 impl Processor for TestGainDsp {
434 type Params = TestGainParams;
435
436 fn process(
437 &mut self,
438 buffer: &mut [&mut [f32]],
439 _transport: &Transport,
440 params: &Self::Params,
441 ) {
442 for channel in buffer.iter_mut() {
443 for sample in channel.iter_mut() {
444 *sample *= params.level;
445 }
446 }
447 }
448 }
449
450 #[derive(Clone, Default)]
451 struct TestPassthroughParams;
452
453 impl ProcessorParams for TestPassthroughParams {
454 fn param_specs() -> &'static [ParamSpec] {
455 &[]
456 }
457 }
458
459 #[derive(Default)]
460 struct TestPassthroughDsp;
461
462 impl Processor for TestPassthroughDsp {
463 type Params = TestPassthroughParams;
464
465 fn process(
466 &mut self,
467 _buffer: &mut [&mut [f32]],
468 _transport: &Transport,
469 _params: &Self::Params,
470 ) {
471 }
472 }
473
474 #[derive(Clone)]
475 struct TestParams;
476
477 impl Default for TestParams {
478 fn default() -> Self {
479 Self
480 }
481 }
482
483 impl ProcessorParams for TestParams {
484 fn param_specs() -> &'static [ParamSpec] {
485 &[]
486 }
487 }
488
489 #[derive(Clone, Default)]
490 struct PanicParamSpecsParams {
491 value: f32,
492 }
493
494 impl ProcessorParams for PanicParamSpecsParams {
495 fn param_specs() -> &'static [ParamSpec] {
496 panic!("param_specs must not be called from runtime split path")
497 }
498
499 fn plain_value_count() -> usize {
500 1
501 }
502
503 fn apply_plain_values(&mut self, values: &[f32]) {
504 if let Some(value) = values.first() {
505 self.value = *value;
506 }
507 }
508 }
509
510 struct LifecycleProbe {
511 set_sample_rate_calls: Arc<AtomicU32>,
512 reset_calls: Arc<AtomicU32>,
513 last_sample_rate_bits: Arc<AtomicU32>,
514 touched_process: Arc<AtomicBool>,
515 }
516
517 impl LifecycleProbe {
518 fn new() -> Self {
519 Self {
520 set_sample_rate_calls: Arc::new(AtomicU32::new(0)),
521 reset_calls: Arc::new(AtomicU32::new(0)),
522 last_sample_rate_bits: Arc::new(AtomicU32::new(0)),
523 touched_process: Arc::new(AtomicBool::new(false)),
524 }
525 }
526 }
527
528 impl Processor for LifecycleProbe {
529 type Params = TestParams;
530
531 fn process(
532 &mut self,
533 _buffer: &mut [&mut [f32]],
534 _transport: &Transport,
535 _params: &Self::Params,
536 ) {
537 self.touched_process.store(true, Ordering::SeqCst);
538 }
539
540 fn set_sample_rate(&mut self, sample_rate: f32) {
541 self.set_sample_rate_calls.fetch_add(1, Ordering::SeqCst);
542 self.last_sample_rate_bits
543 .store(sample_rate.to_bits(), Ordering::SeqCst);
544 }
545
546 fn reset(&mut self) {
547 self.reset_calls.fetch_add(1, Ordering::SeqCst);
548 }
549 }
550
551 #[test]
552 fn test_chain_processes_in_order() {
553 let mut chain = Chain {
554 first: TestGainDsp,
555 second: TestGainDsp,
556 };
557
558 let mut left = [1.0_f32, 1.0_f32];
559 let mut right = [1.0_f32, 1.0_f32];
560 let mut buffer = [&mut left[..], &mut right[..]];
561
562 let transport = Transport::default();
563 let params = ChainParams {
564 first: TestGainParams { level: 0.5 },
565 second: TestGainParams { level: 2.0 },
566 };
567
568 chain.process(&mut buffer, &transport, ¶ms);
569
570 assert!((buffer[0][0] - 1.0_f32).abs() < 1e-6);
572 assert!((buffer[1][0] - 1.0_f32).abs() < 1e-6);
573 }
574
575 #[test]
576 fn test_chain_with_passthrough() {
577 let mut chain = Chain {
578 first: TestPassthroughDsp,
579 second: TestGainDsp,
580 };
581
582 let mut left = [2.0_f32, 2.0_f32];
583 let mut right = [2.0_f32, 2.0_f32];
584 let mut buffer = [&mut left[..], &mut right[..]];
585
586 let transport = Transport::default();
587 let params = ChainParams {
588 first: TestPassthroughParams,
589 second: TestGainParams { level: 0.5 },
590 };
591
592 chain.process(&mut buffer, &transport, ¶ms);
593
594 assert!((buffer[0][0] - 1.0_f32).abs() < 1e-6);
596 }
597
598 #[test]
599 fn test_chain_params_merge() {
600 let specs = <ChainParams<TestGainParams, TestGainParams>>::param_specs();
601 assert_eq!(specs.len(), 2); assert_eq!(specs[0].name, "Level");
605 assert_eq!(specs[1].name, "Level");
606 }
607
608 #[test]
609 fn test_chain_default() {
610 let _chain: Chain<TestGainDsp, TestPassthroughDsp> = Chain::default();
611 let _params: ChainParams<TestGainParams, TestPassthroughParams> = ChainParams::default();
612 }
613
614 #[test]
615 fn test_chain_from_param_defaults_uses_children_spec_defaults() {
616 let defaults = <ChainParams<TestGainParams, TestGainParams>>::from_param_defaults();
617 assert!((defaults.first.level - 1.0).abs() < 1e-6);
618 assert!((defaults.second.level - 1.0).abs() < 1e-6);
619 }
620
621 #[test]
622 fn test_chain_apply_plain_values_splits_by_child_param_count() {
623 let mut params = <ChainParams<TestGainParams, TestGainParams>>::from_param_defaults();
624 params.apply_plain_values(&[0.25, 1.75]);
625
626 assert!((params.first.level - 0.25).abs() < 1e-6);
627 assert!((params.second.level - 1.75).abs() < 1e-6);
628 }
629
630 #[test]
631 fn test_bypassed_apply_plain_values_uses_plain_value_count_without_param_specs() {
632 let mut params = <BypassedParams<PanicParamSpecsParams>>::from_param_defaults();
633 params.apply_plain_values(&[0.5, 1.0]);
634
635 assert!((params.inner.value - 0.5).abs() < 1e-6);
636 assert!(params.bypassed);
637 }
638
639 #[test]
640 fn test_chain_apply_plain_values_uses_plain_value_count_without_param_specs() {
641 let mut params =
642 <ChainParams<PanicParamSpecsParams, PanicParamSpecsParams>>::from_param_defaults();
643 params.apply_plain_values(&[0.25, 0.75]);
644
645 assert!((params.first.value - 0.25).abs() < 1e-6);
646 assert!((params.second.value - 0.75).abs() < 1e-6);
647 }
648
649 #[test]
650 fn test_chain_propagates_set_sample_rate_to_both_processors() {
651 let first = LifecycleProbe::new();
652 let second = LifecycleProbe::new();
653
654 let first_calls = Arc::clone(&first.set_sample_rate_calls);
655 let second_calls = Arc::clone(&second.set_sample_rate_calls);
656 let first_sr = Arc::clone(&first.last_sample_rate_bits);
657 let second_sr = Arc::clone(&second.last_sample_rate_bits);
658
659 let mut chain = Chain { first, second };
660 chain.set_sample_rate(48_000.0);
661
662 assert_eq!(first_calls.load(Ordering::SeqCst), 1);
663 assert_eq!(second_calls.load(Ordering::SeqCst), 1);
664 assert_eq!(f32::from_bits(first_sr.load(Ordering::SeqCst)), 48_000.0);
665 assert_eq!(f32::from_bits(second_sr.load(Ordering::SeqCst)), 48_000.0);
666 }
667
668 #[test]
669 fn test_chain_propagates_reset_to_both_processors() {
670 let first = LifecycleProbe::new();
671 let second = LifecycleProbe::new();
672
673 let first_resets = Arc::clone(&first.reset_calls);
674 let second_resets = Arc::clone(&second.reset_calls);
675
676 let mut chain = Chain { first, second };
677 chain.reset();
678
679 assert_eq!(first_resets.load(Ordering::SeqCst), 1);
680 assert_eq!(second_resets.load(Ordering::SeqCst), 1);
681 }
682
683 #[test]
684 fn test_bypassed_param_specs_include_bypass_flag() {
685 let specs = <BypassedParams<TestGainParams>>::param_specs();
686 assert_eq!(specs.len(), 2);
687 assert_eq!(specs[0].id_suffix, "level");
688 assert_eq!(specs[1].id_suffix, "bypass");
689 }
690
691 #[test]
692 fn test_bypassed_process_skips_child_when_bypassed_after_transition() {
693 let touched = Arc::new(AtomicBool::new(false));
694
695 struct TouchProbe {
696 touched: Arc<AtomicBool>,
697 }
698
699 impl Processor for TouchProbe {
700 type Params = TestParams;
701
702 fn process(
703 &mut self,
704 _buffer: &mut [&mut [f32]],
705 _transport: &Transport,
706 _params: &Self::Params,
707 ) {
708 self.touched.store(true, Ordering::SeqCst);
709 }
710 }
711
712 let mut wrapped = Bypassed::new(TouchProbe {
713 touched: Arc::clone(&touched),
714 });
715
716 let mut left = [1.0_f32, 2.0_f32];
717 let mut right = [3.0_f32, 4.0_f32];
718 let mut buffer = [&mut left[..], &mut right[..]];
719
720 for _ in 0..140 {
722 wrapped.process(
723 &mut buffer,
724 &Transport::default(),
725 &BypassedParams {
726 inner: TestParams,
727 bypassed: true,
728 },
729 );
730 }
731
732 touched.store(false, Ordering::SeqCst);
733
734 wrapped.process(
735 &mut buffer,
736 &Transport::default(),
737 &BypassedParams {
738 inner: TestParams,
739 bypassed: true,
740 },
741 );
742
743 let mut verify_left = [1.0_f32, 2.0_f32];
744 let mut verify_right = [3.0_f32, 4.0_f32];
745 let original_left = verify_left;
746 let original_right = verify_right;
747 let mut verify_buffer = [&mut verify_left[..], &mut verify_right[..]];
748
749 wrapped.process(
750 &mut verify_buffer,
751 &Transport::default(),
752 &BypassedParams {
753 inner: TestParams,
754 bypassed: true,
755 },
756 );
757
758 assert!(!touched.load(Ordering::SeqCst));
759 assert_eq!(verify_left, original_left);
760 assert_eq!(verify_right, original_right);
761 }
762
763 #[test]
764 fn test_bypassed_process_runs_child_when_active() {
765 let mut wrapped = Bypassed::new(TestGainDsp);
766
767 let mut left = [1.0_f32, 1.0_f32];
768 let mut right = [1.0_f32, 1.0_f32];
769 let mut buffer = [&mut left[..], &mut right[..]];
770
771 wrapped.process(
772 &mut buffer,
773 &Transport::default(),
774 &BypassedParams {
775 inner: TestGainParams { level: 0.5 },
776 bypassed: false,
777 },
778 );
779
780 assert!((left[0] - 0.5_f32).abs() < 1e-6);
781 assert!((right[0] - 0.5_f32).abs() < 1e-6);
782 }
783
784 #[derive(Default)]
785 struct PolarityFlip;
786
787 impl Processor for PolarityFlip {
788 type Params = TestPassthroughParams;
789
790 fn process(
791 &mut self,
792 buffer: &mut [&mut [f32]],
793 _transport: &Transport,
794 _params: &Self::Params,
795 ) {
796 for channel in buffer.iter_mut() {
797 for sample in channel.iter_mut() {
798 *sample = -*sample;
799 }
800 }
801 }
802 }
803
804 #[test]
805 fn test_bypassed_transition_smooths_toggle_edges() {
806 let mut wrapped = Bypassed::new(PolarityFlip);
807
808 let active = BypassedParams {
809 inner: TestPassthroughParams,
810 bypassed: false,
811 };
812 let bypassed = BypassedParams {
813 inner: TestPassthroughParams,
814 bypassed: true,
815 };
816
817 let mut previous = -1.0_f32;
819 for _ in 0..4 {
820 let mut sample = [1.0_f32];
821 let mut buffer = [&mut sample[..]];
822 wrapped.process(&mut buffer, &Transport::default(), &active);
823 previous = sample[0];
824 }
825 assert!(previous < -0.95);
826
827 let mut max_step = 0.0_f32;
829 for _ in 0..160 {
830 let mut sample = [1.0_f32];
831 let mut buffer = [&mut sample[..]];
832 wrapped.process(&mut buffer, &Transport::default(), &bypassed);
833
834 let step = (sample[0] - previous).abs();
835 max_step = max_step.max(step);
836 previous = sample[0];
837 }
838
839 assert!(max_step < 1.0);
841 assert!(previous > 0.95);
843 }
844
845 #[test]
846 fn test_bypassed_transition_is_bidirectional() {
847 let mut wrapped = Bypassed::new(PolarityFlip);
848
849 let active = BypassedParams {
850 inner: TestPassthroughParams,
851 bypassed: false,
852 };
853 let bypassed = BypassedParams {
854 inner: TestPassthroughParams,
855 bypassed: true,
856 };
857
858 let mut sample_out = 0.0_f32;
860 for _ in 0..160 {
861 let mut sample = [1.0_f32];
862 let mut buffer = [&mut sample[..]];
863 wrapped.process(&mut buffer, &Transport::default(), &bypassed);
864 sample_out = sample[0];
865 }
866 assert!(sample_out > 0.95);
867
868 let mut max_step = 0.0_f32;
870 let mut previous = sample_out;
871 for _ in 0..160 {
872 let mut sample = [1.0_f32];
873 let mut buffer = [&mut sample[..]];
874 wrapped.process(&mut buffer, &Transport::default(), &active);
875
876 let step = (sample[0] - previous).abs();
877 max_step = max_step.max(step);
878 previous = sample[0];
879 }
880
881 assert!(max_step < 1.0);
882 assert!(previous < -0.95);
883 }
884}