1use super::AudioData;
48use std::collections::HashMap;
49use voirs_sdk::{Result, VoirsError};
50
51#[derive(Debug, Clone)]
53pub struct EffectConfig {
54 pub effect_type: String,
56 pub parameters: HashMap<String, f32>,
58 pub enabled: bool,
60 pub priority: i32,
62}
63
64impl EffectConfig {
65 pub fn new(effect_type: &str) -> Self {
67 Self {
68 effect_type: effect_type.to_string(),
69 parameters: HashMap::new(),
70 enabled: true,
71 priority: 0,
72 }
73 }
74
75 pub fn with_parameter(mut self, name: &str, value: f32) -> Self {
77 self.parameters.insert(name.to_string(), value);
78 self
79 }
80
81 pub fn with_enabled(mut self, enabled: bool) -> Self {
83 self.enabled = enabled;
84 self
85 }
86
87 pub fn with_priority(mut self, priority: i32) -> Self {
89 self.priority = priority;
90 self
91 }
92
93 pub fn get_parameter(&self, name: &str) -> Option<f32> {
95 self.parameters.get(name).copied()
96 }
97}
98
99pub trait AudioEffect: Send + Sync {
101 fn name(&self) -> &str;
103
104 fn process(&mut self, samples: &mut [f32], sample_rate: u32) -> Result<()>;
106
107 fn reset(&mut self) -> Result<()>;
109
110 fn is_enabled(&self) -> bool;
112
113 fn set_enabled(&mut self, enabled: bool);
115
116 fn get_parameters(&self) -> HashMap<String, f32>;
118
119 fn set_parameter(&mut self, name: &str, value: f32) -> Result<()>;
121}
122
123pub struct VolumeEffect {
125 gain: f32,
126 enabled: bool,
127}
128
129impl VolumeEffect {
130 pub fn new(gain: f32) -> Self {
132 Self {
133 gain: gain.clamp(0.0, 2.0), enabled: true,
135 }
136 }
137}
138
139impl AudioEffect for VolumeEffect {
140 fn name(&self) -> &str {
141 "volume"
142 }
143
144 fn process(&mut self, samples: &mut [f32], _sample_rate: u32) -> Result<()> {
145 if !self.enabled {
146 return Ok(());
147 }
148
149 for sample in samples.iter_mut() {
150 *sample *= self.gain;
151 }
152
153 Ok(())
154 }
155
156 fn reset(&mut self) -> Result<()> {
157 Ok(())
159 }
160
161 fn is_enabled(&self) -> bool {
162 self.enabled
163 }
164
165 fn set_enabled(&mut self, enabled: bool) {
166 self.enabled = enabled;
167 }
168
169 fn get_parameters(&self) -> HashMap<String, f32> {
170 let mut params = HashMap::new();
171 params.insert("gain".to_string(), self.gain);
172 params
173 }
174
175 fn set_parameter(&mut self, name: &str, value: f32) -> Result<()> {
176 match name {
177 "gain" => {
178 self.gain = value.clamp(0.0, 2.0);
179 Ok(())
180 }
181 _ => Err(VoirsError::config_error(format!(
182 "Unknown parameter: {name}"
183 ))),
184 }
185 }
186}
187
188pub struct LowPassFilter {
190 cutoff_freq: f32,
191 resonance: f32,
192 enabled: bool,
193 prev_input: f32,
195 prev_output: f32,
196}
197
198impl LowPassFilter {
199 pub fn new(cutoff_freq: f32, resonance: f32) -> Self {
201 Self {
202 cutoff_freq: cutoff_freq.clamp(20.0, 20000.0),
203 resonance: resonance.clamp(0.1, 10.0),
204 enabled: true,
205 prev_input: 0.0,
206 prev_output: 0.0,
207 }
208 }
209}
210
211impl AudioEffect for LowPassFilter {
212 fn name(&self) -> &str {
213 "lowpass"
214 }
215
216 fn process(&mut self, samples: &mut [f32], sample_rate: u32) -> Result<()> {
217 if !self.enabled {
218 return Ok(());
219 }
220
221 let dt = 1.0 / sample_rate as f32;
223 let rc = 1.0 / (2.0 * std::f32::consts::PI * self.cutoff_freq);
224 let alpha = dt / (rc + dt);
225
226 for sample in samples.iter_mut() {
227 let output = alpha * *sample + (1.0 - alpha) * self.prev_output;
228 self.prev_output = output;
229 *sample = output;
230 }
231
232 Ok(())
233 }
234
235 fn reset(&mut self) -> Result<()> {
236 self.prev_input = 0.0;
237 self.prev_output = 0.0;
238 Ok(())
239 }
240
241 fn is_enabled(&self) -> bool {
242 self.enabled
243 }
244
245 fn set_enabled(&mut self, enabled: bool) {
246 self.enabled = enabled;
247 }
248
249 fn get_parameters(&self) -> HashMap<String, f32> {
250 let mut params = HashMap::new();
251 params.insert("cutoff_freq".to_string(), self.cutoff_freq);
252 params.insert("resonance".to_string(), self.resonance);
253 params
254 }
255
256 fn set_parameter(&mut self, name: &str, value: f32) -> Result<()> {
257 match name {
258 "cutoff_freq" => {
259 self.cutoff_freq = value.clamp(20.0, 20000.0);
260 Ok(())
261 }
262 "resonance" => {
263 self.resonance = value.clamp(0.1, 10.0);
264 Ok(())
265 }
266 _ => Err(VoirsError::config_error(format!(
267 "Unknown parameter: {name}"
268 ))),
269 }
270 }
271}
272
273pub struct ReverbEffect {
275 room_size: f32,
276 damping: f32,
277 wet_level: f32,
278 dry_level: f32,
279 enabled: bool,
280 delay_buffer: Vec<f32>,
282 delay_index: usize,
283}
284
285impl ReverbEffect {
286 pub fn new(room_size: f32, damping: f32, wet_level: f32) -> Self {
288 let delay_samples = (room_size * 22050.0) as usize; let clamped_wet = wet_level.clamp(0.0, 1.0);
290
291 Self {
292 room_size: room_size.clamp(0.0, 1.0),
293 damping: damping.clamp(0.0, 1.0),
294 wet_level: clamped_wet,
295 dry_level: 1.0 - clamped_wet,
296 enabled: true,
297 delay_buffer: vec![0.0; delay_samples.max(1024)],
298 delay_index: 0,
299 }
300 }
301}
302
303impl AudioEffect for ReverbEffect {
304 fn name(&self) -> &str {
305 "reverb"
306 }
307
308 fn process(&mut self, samples: &mut [f32], _sample_rate: u32) -> Result<()> {
309 if !self.enabled {
310 return Ok(());
311 }
312
313 for sample in samples.iter_mut() {
314 let delayed = self.delay_buffer[self.delay_index];
316
317 let reverb_sample = delayed * (1.0 - self.damping);
319
320 let output = (*sample * self.dry_level) + (reverb_sample * self.wet_level);
322
323 self.delay_buffer[self.delay_index] = *sample + (reverb_sample * self.room_size * 0.5);
325
326 self.delay_index = (self.delay_index + 1) % self.delay_buffer.len();
328
329 *sample = output;
330 }
331
332 Ok(())
333 }
334
335 fn reset(&mut self) -> Result<()> {
336 self.delay_buffer.fill(0.0);
337 self.delay_index = 0;
338 Ok(())
339 }
340
341 fn is_enabled(&self) -> bool {
342 self.enabled
343 }
344
345 fn set_enabled(&mut self, enabled: bool) {
346 self.enabled = enabled;
347 }
348
349 fn get_parameters(&self) -> HashMap<String, f32> {
350 let mut params = HashMap::new();
351 params.insert("room_size".to_string(), self.room_size);
352 params.insert("damping".to_string(), self.damping);
353 params.insert("wet_level".to_string(), self.wet_level);
354 params.insert("dry_level".to_string(), self.dry_level);
355 params
356 }
357
358 fn set_parameter(&mut self, name: &str, value: f32) -> Result<()> {
359 match name {
360 "room_size" => {
361 self.room_size = value.clamp(0.0, 1.0);
362 Ok(())
363 }
364 "damping" => {
365 self.damping = value.clamp(0.0, 1.0);
366 Ok(())
367 }
368 "wet_level" => {
369 self.wet_level = value.clamp(0.0, 1.0);
370 self.dry_level = 1.0 - self.wet_level;
371 Ok(())
372 }
373 _ => Err(VoirsError::config_error(format!(
374 "Unknown parameter: {name}"
375 ))),
376 }
377 }
378}
379
380pub struct CompressorEffect {
382 threshold: f32, ratio: f32, attack_time: f32, release_time: f32, makeup_gain: f32, enabled: bool,
388 envelope: f32, }
390
391impl CompressorEffect {
392 pub fn new(threshold: f32, ratio: f32, attack_ms: f32, release_ms: f32) -> Self {
394 Self {
395 threshold: threshold.clamp(-60.0, 0.0),
396 ratio: ratio.clamp(1.0, 20.0),
397 attack_time: attack_ms.clamp(0.1, 100.0),
398 release_time: release_ms.clamp(10.0, 1000.0),
399 makeup_gain: 0.0,
400 enabled: true,
401 envelope: 0.0,
402 }
403 }
404
405 pub fn with_makeup_gain(mut self, gain_db: f32) -> Self {
407 self.makeup_gain = gain_db.clamp(0.0, 20.0);
408 self
409 }
410
411 fn db_to_linear(db: f32) -> f32 {
413 10.0_f32.powf(db / 20.0)
414 }
415
416 fn linear_to_db(linear: f32) -> f32 {
418 20.0 * linear.abs().max(1e-10).log10()
419 }
420}
421
422impl AudioEffect for CompressorEffect {
423 fn name(&self) -> &str {
424 "compressor"
425 }
426
427 fn process(&mut self, samples: &mut [f32], sample_rate: u32) -> Result<()> {
428 if !self.enabled {
429 return Ok(());
430 }
431
432 let attack_coef = 1.0 - (-1.0 / (sample_rate as f32 * self.attack_time / 1000.0)).exp();
433 let release_coef = 1.0 - (-1.0 / (sample_rate as f32 * self.release_time / 1000.0)).exp();
434
435 for sample in samples.iter_mut() {
436 let input_level_db = Self::linear_to_db(*sample);
437
438 let coef = if input_level_db > Self::linear_to_db(self.envelope) {
440 attack_coef
441 } else {
442 release_coef
443 };
444 self.envelope = self.envelope + coef * (sample.abs() - self.envelope);
445
446 let envelope_db = Self::linear_to_db(self.envelope);
448 let gain_reduction = if envelope_db > self.threshold {
449 (envelope_db - self.threshold) * (1.0 - 1.0 / self.ratio)
450 } else {
451 0.0
452 };
453
454 let total_gain_db = -gain_reduction + self.makeup_gain;
456 let gain = Self::db_to_linear(total_gain_db);
457
458 *sample *= gain;
459 }
460
461 Ok(())
462 }
463
464 fn reset(&mut self) -> Result<()> {
465 self.envelope = 0.0;
466 Ok(())
467 }
468
469 fn is_enabled(&self) -> bool {
470 self.enabled
471 }
472
473 fn set_enabled(&mut self, enabled: bool) {
474 self.enabled = enabled;
475 }
476
477 fn get_parameters(&self) -> HashMap<String, f32> {
478 let mut params = HashMap::new();
479 params.insert("threshold".to_string(), self.threshold);
480 params.insert("ratio".to_string(), self.ratio);
481 params.insert("attack_time".to_string(), self.attack_time);
482 params.insert("release_time".to_string(), self.release_time);
483 params.insert("makeup_gain".to_string(), self.makeup_gain);
484 params
485 }
486
487 fn set_parameter(&mut self, name: &str, value: f32) -> Result<()> {
488 match name {
489 "threshold" => {
490 self.threshold = value.clamp(-60.0, 0.0);
491 Ok(())
492 }
493 "ratio" => {
494 self.ratio = value.clamp(1.0, 20.0);
495 Ok(())
496 }
497 "attack_time" => {
498 self.attack_time = value.clamp(0.1, 100.0);
499 Ok(())
500 }
501 "release_time" => {
502 self.release_time = value.clamp(10.0, 1000.0);
503 Ok(())
504 }
505 "makeup_gain" => {
506 self.makeup_gain = value.clamp(0.0, 20.0);
507 Ok(())
508 }
509 _ => Err(VoirsError::config_error(format!(
510 "Unknown parameter: {name}"
511 ))),
512 }
513 }
514}
515
516pub struct NoiseGateEffect {
518 threshold: f32, attack_time: f32, release_time: f32, hold_time: f32, enabled: bool,
523 envelope: f32, hold_counter: usize, }
526
527impl NoiseGateEffect {
528 pub fn new(threshold: f32, attack_ms: f32, hold_ms: f32, release_ms: f32) -> Self {
530 Self {
531 threshold: threshold.clamp(-80.0, -10.0),
532 attack_time: attack_ms.clamp(0.1, 50.0),
533 hold_time: hold_ms.clamp(0.0, 500.0),
534 release_time: release_ms.clamp(10.0, 1000.0),
535 enabled: true,
536 envelope: 0.0,
537 hold_counter: 0,
538 }
539 }
540
541 fn db_to_linear(db: f32) -> f32 {
543 10.0_f32.powf(db / 20.0)
544 }
545
546 fn linear_to_db(linear: f32) -> f32 {
548 20.0 * linear.abs().max(1e-10).log10()
549 }
550}
551
552impl AudioEffect for NoiseGateEffect {
553 fn name(&self) -> &str {
554 "noise_gate"
555 }
556
557 fn process(&mut self, samples: &mut [f32], sample_rate: u32) -> Result<()> {
558 if !self.enabled {
559 return Ok(());
560 }
561
562 let threshold_linear = Self::db_to_linear(self.threshold);
563 let attack_coef = 1.0 - (-1.0 / (sample_rate as f32 * self.attack_time / 1000.0)).exp();
564 let release_coef = 1.0 - (-1.0 / (sample_rate as f32 * self.release_time / 1000.0)).exp();
565 let hold_samples = (sample_rate as f32 * self.hold_time / 1000.0) as usize;
566
567 for sample in samples.iter_mut() {
568 let input_level = sample.abs();
569
570 if input_level > threshold_linear {
572 self.envelope = self.envelope + attack_coef * (1.0 - self.envelope);
574 self.hold_counter = hold_samples;
575 } else if self.hold_counter > 0 {
576 self.hold_counter -= 1;
578 self.envelope = 1.0;
579 } else {
580 self.envelope = self.envelope + release_coef * (0.0 - self.envelope);
582 }
583
584 *sample *= self.envelope;
586 }
587
588 Ok(())
589 }
590
591 fn reset(&mut self) -> Result<()> {
592 self.envelope = 0.0;
593 self.hold_counter = 0;
594 Ok(())
595 }
596
597 fn is_enabled(&self) -> bool {
598 self.enabled
599 }
600
601 fn set_enabled(&mut self, enabled: bool) {
602 self.enabled = enabled;
603 }
604
605 fn get_parameters(&self) -> HashMap<String, f32> {
606 let mut params = HashMap::new();
607 params.insert("threshold".to_string(), self.threshold);
608 params.insert("attack_time".to_string(), self.attack_time);
609 params.insert("hold_time".to_string(), self.hold_time);
610 params.insert("release_time".to_string(), self.release_time);
611 params
612 }
613
614 fn set_parameter(&mut self, name: &str, value: f32) -> Result<()> {
615 match name {
616 "threshold" => {
617 self.threshold = value.clamp(-80.0, -10.0);
618 Ok(())
619 }
620 "attack_time" => {
621 self.attack_time = value.clamp(0.1, 50.0);
622 Ok(())
623 }
624 "hold_time" => {
625 self.hold_time = value.clamp(0.0, 500.0);
626 Ok(())
627 }
628 "release_time" => {
629 self.release_time = value.clamp(10.0, 1000.0);
630 Ok(())
631 }
632 _ => Err(VoirsError::config_error(format!(
633 "Unknown parameter: {name}"
634 ))),
635 }
636 }
637}
638
639pub struct EqualizerEffect {
641 low_gain: f32, mid_gain: f32, high_gain: f32, low_freq: f32, high_freq: f32, enabled: bool,
647 low_filter_state: [f32; 2],
649 high_filter_state: [f32; 2],
650}
651
652impl EqualizerEffect {
653 pub fn new(low_gain_db: f32, mid_gain_db: f32, high_gain_db: f32) -> Self {
655 Self {
656 low_gain: low_gain_db.clamp(-12.0, 12.0),
657 mid_gain: mid_gain_db.clamp(-12.0, 12.0),
658 high_gain: high_gain_db.clamp(-12.0, 12.0),
659 low_freq: 250.0, high_freq: 4000.0, enabled: true,
662 low_filter_state: [0.0; 2],
663 high_filter_state: [0.0; 2],
664 }
665 }
666
667 fn db_to_linear(db: f32) -> f32 {
669 10.0_f32.powf(db / 20.0)
670 }
671}
672
673impl AudioEffect for EqualizerEffect {
674 fn name(&self) -> &str {
675 "equalizer"
676 }
677
678 fn process(&mut self, samples: &mut [f32], sample_rate: u32) -> Result<()> {
679 if !self.enabled {
680 return Ok(());
681 }
682
683 let low_gain_linear = Self::db_to_linear(self.low_gain);
684 let mid_gain_linear = Self::db_to_linear(self.mid_gain);
685 let high_gain_linear = Self::db_to_linear(self.high_gain);
686
687 let low_omega = 2.0 * std::f32::consts::PI * self.low_freq / sample_rate as f32;
689 let high_omega = 2.0 * std::f32::consts::PI * self.high_freq / sample_rate as f32;
690
691 let low_alpha = low_omega / (1.0 + low_omega);
692 let high_alpha = high_omega / (1.0 + high_omega);
693
694 for sample in samples.iter_mut() {
695 let input = *sample;
696
697 self.low_filter_state[1] =
699 self.low_filter_state[1] + low_alpha * (input - self.low_filter_state[1]);
700 let low_band = self.low_filter_state[1];
701
702 self.high_filter_state[0] =
704 low_alpha * (self.high_filter_state[0] + input - self.high_filter_state[1]);
705 self.high_filter_state[1] = input;
706 let high_band = self.high_filter_state[0];
707
708 let mid_band = input - low_band - high_band;
710
711 *sample = (low_band * low_gain_linear)
713 + (mid_band * mid_gain_linear)
714 + (high_band * high_gain_linear);
715 }
716
717 Ok(())
718 }
719
720 fn reset(&mut self) -> Result<()> {
721 self.low_filter_state = [0.0; 2];
722 self.high_filter_state = [0.0; 2];
723 Ok(())
724 }
725
726 fn is_enabled(&self) -> bool {
727 self.enabled
728 }
729
730 fn set_enabled(&mut self, enabled: bool) {
731 self.enabled = enabled;
732 }
733
734 fn get_parameters(&self) -> HashMap<String, f32> {
735 let mut params = HashMap::new();
736 params.insert("low_gain".to_string(), self.low_gain);
737 params.insert("mid_gain".to_string(), self.mid_gain);
738 params.insert("high_gain".to_string(), self.high_gain);
739 params.insert("low_freq".to_string(), self.low_freq);
740 params.insert("high_freq".to_string(), self.high_freq);
741 params
742 }
743
744 fn set_parameter(&mut self, name: &str, value: f32) -> Result<()> {
745 match name {
746 "low_gain" => {
747 self.low_gain = value.clamp(-12.0, 12.0);
748 Ok(())
749 }
750 "mid_gain" => {
751 self.mid_gain = value.clamp(-12.0, 12.0);
752 Ok(())
753 }
754 "high_gain" => {
755 self.high_gain = value.clamp(-12.0, 12.0);
756 Ok(())
757 }
758 "low_freq" => {
759 self.low_freq = value.clamp(50.0, 500.0);
760 Ok(())
761 }
762 "high_freq" => {
763 self.high_freq = value.clamp(2000.0, 10000.0);
764 Ok(())
765 }
766 _ => Err(VoirsError::config_error(format!(
767 "Unknown parameter: {name}"
768 ))),
769 }
770 }
771}
772
773pub struct EffectChain {
775 effects: Vec<Box<dyn AudioEffect>>,
776 enabled: bool,
777}
778
779impl EffectChain {
780 pub fn new() -> Self {
782 Self {
783 effects: Vec::new(),
784 enabled: true,
785 }
786 }
787
788 pub fn add_effect(&mut self, effect: Box<dyn AudioEffect>) {
790 self.effects.push(effect);
791 }
792
793 pub fn remove_effect(&mut self, name: &str) -> Result<()> {
795 let initial_len = self.effects.len();
796 self.effects.retain(|effect| effect.name() != name);
797
798 if self.effects.len() == initial_len {
799 return Err(VoirsError::config_error(format!(
800 "Effect '{}' not found",
801 name
802 )));
803 }
804
805 Ok(())
806 }
807
808 pub fn get_effect_mut(&mut self, name: &str) -> Option<&mut Box<dyn AudioEffect>> {
810 self.effects.iter_mut().find(|effect| effect.name() == name)
811 }
812
813 pub fn process(&mut self, audio_data: &mut AudioData) -> Result<()> {
815 if !self.enabled {
816 return Ok(());
817 }
818
819 let mut samples: Vec<f32> = audio_data
820 .samples
821 .iter()
822 .map(|&s| s as f32 / i16::MAX as f32)
823 .collect();
824
825 for effect in &mut self.effects {
827 if effect.is_enabled() {
828 effect.process(&mut samples, audio_data.sample_rate)?;
829 }
830 }
831
832 audio_data.samples = samples
834 .iter()
835 .map(|&s| (s * i16::MAX as f32) as i16)
836 .collect();
837
838 Ok(())
839 }
840
841 pub fn process_samples(&mut self, samples: &mut [f32], sample_rate: u32) -> Result<()> {
843 if !self.enabled {
844 return Ok(());
845 }
846
847 for effect in &mut self.effects {
848 if effect.is_enabled() {
849 effect.process(samples, sample_rate)?;
850 }
851 }
852
853 Ok(())
854 }
855
856 pub fn reset(&mut self) -> Result<()> {
858 for effect in &mut self.effects {
859 effect.reset()?;
860 }
861 Ok(())
862 }
863
864 pub fn set_enabled(&mut self, enabled: bool) {
866 self.enabled = enabled;
867 }
868
869 pub fn is_enabled(&self) -> bool {
871 self.enabled
872 }
873
874 pub fn get_effect_names(&self) -> Vec<String> {
876 self.effects
877 .iter()
878 .map(|effect| effect.name().to_string())
879 .collect()
880 }
881
882 pub fn len(&self) -> usize {
884 self.effects.len()
885 }
886
887 pub fn is_empty(&self) -> bool {
889 self.effects.is_empty()
890 }
891}
892
893impl Default for EffectChain {
894 fn default() -> Self {
895 Self::new()
896 }
897}
898
899pub fn create_preset_effect_chain(preset_name: &str) -> Result<EffectChain> {
901 let mut chain = EffectChain::new();
902
903 match preset_name {
904 "vocal_enhancement" => {
905 chain.add_effect(Box::new(NoiseGateEffect::new(-45.0, 2.0, 50.0, 100.0)));
907 chain.add_effect(Box::new(EqualizerEffect::new(0.0, 2.0, 3.0))); chain.add_effect(Box::new(
909 CompressorEffect::new(-18.0, 3.0, 5.0, 50.0).with_makeup_gain(4.0),
910 ));
911 chain.add_effect(Box::new(LowPassFilter::new(8000.0, 0.7)));
912 chain.add_effect(Box::new(ReverbEffect::new(0.3, 0.4, 0.2)));
913 }
914 "podcast" => {
915 chain.add_effect(Box::new(NoiseGateEffect::new(-50.0, 3.0, 100.0, 200.0)));
917 chain.add_effect(Box::new(EqualizerEffect::new(-1.0, 3.0, 1.0))); chain.add_effect(Box::new(
919 CompressorEffect::new(-20.0, 4.0, 10.0, 100.0).with_makeup_gain(6.0),
920 ));
921 chain.add_effect(Box::new(LowPassFilter::new(7000.0, 0.8)));
922 }
923 "radio" => {
924 chain.add_effect(Box::new(NoiseGateEffect::new(-40.0, 1.0, 20.0, 80.0)));
926 chain.add_effect(Box::new(EqualizerEffect::new(3.0, 4.0, 2.0))); chain.add_effect(Box::new(
928 CompressorEffect::new(-15.0, 6.0, 3.0, 30.0).with_makeup_gain(8.0),
929 ));
930 chain.add_effect(Box::new(LowPassFilter::new(6000.0, 0.9)));
931 chain.add_effect(Box::new(ReverbEffect::new(0.1, 0.8, 0.1)));
932 }
933 "warm" => {
934 chain.add_effect(Box::new(NoiseGateEffect::new(-55.0, 5.0, 150.0, 300.0)));
936 chain.add_effect(Box::new(EqualizerEffect::new(2.0, 0.0, -2.0))); chain.add_effect(Box::new(
938 CompressorEffect::new(-24.0, 2.5, 15.0, 150.0).with_makeup_gain(3.0),
939 ));
940 chain.add_effect(Box::new(LowPassFilter::new(5000.0, 0.6)));
941 chain.add_effect(Box::new(ReverbEffect::new(0.4, 0.3, 0.3)));
942 }
943 "clean" => {
944 chain.add_effect(Box::new(NoiseGateEffect::new(-60.0, 5.0, 100.0, 250.0)));
946 chain.add_effect(Box::new(VolumeEffect::new(1.0)));
947 }
948 "broadcast" => {
949 chain.add_effect(Box::new(NoiseGateEffect::new(-42.0, 2.0, 75.0, 150.0)));
951 chain.add_effect(Box::new(EqualizerEffect::new(0.0, 4.0, 2.0))); chain.add_effect(Box::new(
953 CompressorEffect::new(-16.0, 5.0, 5.0, 50.0).with_makeup_gain(7.0),
954 ));
955 chain.add_effect(Box::new(LowPassFilter::new(7500.0, 0.75)));
956 }
957 "audiobook" => {
958 chain.add_effect(Box::new(NoiseGateEffect::new(-48.0, 4.0, 120.0, 250.0)));
960 chain.add_effect(Box::new(EqualizerEffect::new(1.0, 2.0, 0.0))); chain.add_effect(Box::new(
962 CompressorEffect::new(-22.0, 3.5, 12.0, 120.0).with_makeup_gain(5.0),
963 ));
964 chain.add_effect(Box::new(LowPassFilter::new(6500.0, 0.7)));
965 }
966 "telephone" => {
967 chain.add_effect(Box::new(EqualizerEffect::new(-6.0, 8.0, -10.0))); chain.add_effect(Box::new(
970 CompressorEffect::new(-12.0, 8.0, 2.0, 20.0).with_makeup_gain(10.0),
971 ));
972 chain.add_effect(Box::new(LowPassFilter::new(3400.0, 1.2)));
973 }
974 _ => {
975 return Err(VoirsError::config_error(format!(
976 "Unknown preset: {}. Available presets: vocal_enhancement, podcast, radio, warm, clean, broadcast, audiobook, telephone",
977 preset_name
978 )));
979 }
980 }
981
982 Ok(chain)
983}
984
985#[cfg(test)]
986mod tests {
987 use super::AudioData;
988 use super::*;
989
990 #[test]
991 fn test_effect_config() {
992 let config = EffectConfig::new("volume")
993 .with_parameter("gain", 1.5)
994 .with_enabled(true)
995 .with_priority(10);
996
997 assert_eq!(config.effect_type, "volume");
998 assert_eq!(config.get_parameter("gain"), Some(1.5));
999 assert!(config.enabled);
1000 assert_eq!(config.priority, 10);
1001 }
1002
1003 #[test]
1004 fn test_volume_effect() {
1005 let mut effect = VolumeEffect::new(2.0);
1006 assert_eq!(effect.name(), "volume");
1007 assert!(effect.is_enabled());
1008
1009 let mut samples = vec![0.1, 0.2, 0.3, 0.4];
1010 effect.process(&mut samples, 22050).unwrap();
1011
1012 assert_eq!(samples, vec![0.2, 0.4, 0.6, 0.8]);
1014
1015 effect.set_parameter("gain", 0.5).unwrap();
1017 let params = effect.get_parameters();
1018 assert_eq!(params.get("gain"), Some(&0.5));
1019 }
1020
1021 #[test]
1022 fn test_lowpass_filter() {
1023 let mut filter = LowPassFilter::new(1000.0, 0.7);
1024 assert_eq!(filter.name(), "lowpass");
1025
1026 let mut samples = vec![1.0, -1.0, 1.0, -1.0]; filter.process(&mut samples, 22050).unwrap();
1028
1029 assert!(samples.iter().all(|&s| s.abs() < 1.0));
1031
1032 filter.reset().unwrap();
1034 }
1035
1036 #[test]
1037 fn test_reverb_effect() {
1038 let mut reverb = ReverbEffect::new(0.1, 0.2, 0.6); assert_eq!(reverb.name(), "reverb");
1040
1041 let mut samples = vec![1.0; 10]; samples.extend(vec![0.0; 100]); let original_samples = samples.clone();
1046 reverb.process(&mut samples, 22050).unwrap();
1047
1048 assert_ne!(samples, original_samples);
1050
1051 assert_ne!(samples[0], 1.0);
1053 }
1054
1055 #[test]
1056 fn test_effect_chain() {
1057 let mut chain = EffectChain::new();
1058 assert!(chain.is_empty());
1059
1060 chain.add_effect(Box::new(VolumeEffect::new(2.0)));
1062 chain.add_effect(Box::new(LowPassFilter::new(5000.0, 0.7)));
1063
1064 assert_eq!(chain.len(), 2);
1065 assert!(!chain.is_empty());
1066
1067 let effect_names = chain.get_effect_names();
1068 assert!(effect_names.contains(&"volume".to_string()));
1069 assert!(effect_names.contains(&"lowpass".to_string()));
1070
1071 let mut audio_data = AudioData {
1073 samples: vec![1000, 2000, 3000, 4000],
1074 sample_rate: 22050,
1075 channels: 1,
1076 };
1077
1078 chain.process(&mut audio_data).unwrap();
1079
1080 assert_ne!(audio_data.samples, vec![1000, 2000, 3000, 4000]);
1082
1083 chain.remove_effect("volume").unwrap();
1085 assert_eq!(chain.len(), 1);
1086 }
1087
1088 #[test]
1089 fn test_preset_effect_chains() {
1090 let presets = vec!["vocal_enhancement", "podcast", "radio", "warm", "clean"];
1091
1092 for preset in presets {
1093 let chain = create_preset_effect_chain(preset).unwrap();
1094 assert!(!chain.is_empty());
1095 }
1096
1097 assert!(create_preset_effect_chain("unknown").is_err());
1099 }
1100
1101 #[test]
1102 fn test_effect_chain_enable_disable() {
1103 let mut chain = EffectChain::new();
1104 chain.add_effect(Box::new(VolumeEffect::new(2.0)));
1105
1106 let mut samples = vec![0.1, 0.2, 0.3, 0.4];
1107 let original_samples = samples.clone();
1108
1109 chain.process_samples(&mut samples, 22050).unwrap();
1111 assert_ne!(samples, original_samples);
1112
1113 samples = original_samples.clone();
1115 chain.set_enabled(false);
1116 chain.process_samples(&mut samples, 22050).unwrap();
1117 assert_eq!(samples, original_samples); }
1119}