diff --git a/README.md b/README.md index bd0436d..26b1be9 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ vois.rs turns your microphone into a voice changer in real time: shift pitch and | Real-time pitch shift | Phase-vocoder, −12…+12 semitones, formants preserved | | Formant shift | Change the timbre (girl / deep / bright) independently of pitch | | 12 presets | Clean, Girl/Anime, Boy, Manly/Deep, Demon, Robot, 8-bit, Alien, Radio, Megaphone, Ghost, Cyborg | -| Effects | Vocoder, bitcrush, distortion, reverb, chorus, ring-mod, bandpass, noise, compressor, noise gate | +| Effects | Vocoder, bitcrush, distortion, overdrive, reverb, chorus, flanger, tremolo, ring-mod, bandpass, noise, compressor, noise gate | | Virtual microphone | Built-in `vois.rs` mic (PipeWire/PulseAudio) — no VB-CABLE needed on Linux | | Auto-Tune | Optional pitch snap to the nearest semitone (smooth, no wobble) | | Config file | Defaults + custom presets in `~/.config/vois/config.toml` | @@ -47,23 +47,23 @@ vois.rs turns your microphone into a voice changer in real time: shift pitch and | Preset | Pitch | Formant | Effects | |--------|-------|---------|---------| | Clean | 0 st | 1.00× | passthrough (A/B compare) | -| Girl / Anime | +5 st | 1.25× | — | -| Boy | +3 st | 1.12× | — | +| Girl / Anime | +5 st | 1.25× | chorus + overdrive | +| Boy | +2 st | 1.08× | compressor | | Manly / Deep | −4 st | 0.78× | compressor | -| Demon | −9 st | 0.85× | distortion + reverb | +| Demon | −9 st | 0.85× | overdrive + reverb + tremolo | | Robot | 0 st | 1.00× | channel vocoder + bitcrush | | 8-bit | 0 st | 1.00× | bitcrush (5-bit, decimate ×4, auto-level) | -| Alien | −2 st | 1.15× | ring-mod + chorus | +| Alien | −2 st | 1.15× | ring-mod + flanger | | Radio | 0 st | 1.00× | bandpass + noise + compressor | | Megaphone | 0 st | 1.00× | distortion + narrow EQ + compressor | -| Ghost | −3 st | 1.05× | big reverb + chorus | -| Cyborg | +2 st | 1.00× | vocoder + bitcrush | -| Chipmunk | +7 st | 1.40× | bitcrush | +| Ghost | −3 st | 1.05× | big reverb + chorus + tremolo | +| Cyborg | +2 st | 1.00× | saw vocoder + overdrive + bitcrush | +| Chipmunk | +7 st | 1.40× | chorus | | Helium | +10 st | 1.50× | chorus | -| Vader | −10 st | 0.60× | reverb | +| Vader | −10 st | 0.60× | overdrive + reverb + tremolo | | Telephone | 0 st | 1.00× | bandpass + noise + compressor | -| Growl | −5 st | 0.70× | distortion + reverb | -| Walkie-Talkie | 0 st | 1.05× | narrow bandpass + bitcrush + squelch noise + compressor | +| Growl | −5 st | 0.70× | distortion + compressor + reverb | +| Walkie-Talkie | 0 st | 1.05× | narrow bandpass + squelch noise + compressor + bitcrush | ### Usage @@ -107,9 +107,9 @@ virtual_mic = true ] } ``` -Any CLI option overrides the file. Effects: `Distortion`, `Bitcrush`, `Reverb`, -`Chorus`, `RingMod`, `Bandpass`, `Noise`, `Compressor`, `Vocoder` (carrier: -`Noise` / `Saw`). +Any CLI option overrides the file. Effects: `Distortion`, `Overdrive`, `Bitcrush`, `Reverb`, `Chorus`, `Flanger`, +`Tremolo`, `RingMod`, `Bandpass`, `Noise`, `Compressor`, `Vocoder` (carrier: +`Noise` / `Saw` / `Input`). Custom presets also accept `gate_db`. ### Dependencies @@ -154,7 +154,7 @@ vois.rs превращает твой микрофон в войс-чендже | Сдвиг высоты | Фазовый вокодер, −12…+12 полутонов, форманты сохраняются | | Сдвиг формант | Меняет тембр (девушка / глубокий / звонкий) независимо от высоты | | 12 пресетов | Clean, Girl/Anime, Boy, Manly/Deep, Demon, Robot, 8-bit, Alien, Radio, Megaphone, Ghost, Cyborg | -| Эффекты | Вокодер, биткраш, дисторшн, реверб, хорус, кольцевая модуляция, полосовой фильтр, шум, компрессор, шумоподавитель | +| Эффекты | Вокодер, биткраш, дисторшн, овердрайв, реверб, хорус, флэнджер, тремоло, кольцевая модуляция, полосовой фильтр, шум, компрессор, шумоподавитель | | Виртуальный микрофон | Встроенный `vois.rs` (PipeWire/PulseAudio) — на Linux не нужен VB-CABLE | | Автолюн (Auto-Tune) | Снап высоты к ближайшему полутону (плавно, без «плавания») | | Конфиг-файл | Дефолты и свои пресеты в `~/.config/vois/config.toml` | @@ -168,23 +168,23 @@ vois.rs превращает твой микрофон в войс-чендже | Пресет | Высота | Форманты | Эффекты | |--------|--------|----------|---------| | Clean | 0 пт | 1.00× | passthrough (сравнение A/B) | -| Girl / Anime | +5 пт | 1.25× | — | -| Boy | +3 пт | 1.12× | — | +| Girl / Anime | +5 пт | 1.25× | хорус + овердрайв | +| Boy | +2 пт | 1.08× | компрессор | | Manly / Deep | −4 пт | 0.78× | компрессор | -| Demon | −9 пт | 0.85× | дисторшн + реверб | +| Demon | −9 пт | 0.85× | овердрайв + реверб + тремоло | | Robot | 0 пт | 1.00× | вокодер + биткраш | | 8-bit | 0 пт | 1.00× | биткраш (5 бит, децимация ×4, автолевел) | -| Alien | −2 пт | 1.15× | кольцевая модуляция + хорус | +| Alien | −2 пт | 1.15× | кольцевая модуляция + флэнджер | | Radio | 0 пт | 1.00× | полосовой фильтр + шум + компрессор | | Megaphone | 0 пт | 1.00× | дисторшн + узкий EQ + компрессор | -| Ghost | −3 пт | 1.05× | большой реверб + хорус | -| Cyborg | +2 пт | 1.00× | вокодер + биткраш | -| Chipmunk | +7 пт | 1.40× | биткраш | +| Ghost | −3 пт | 1.05× | большой реверб + хорус + тремоло | +| Cyborg | +2 пт | 1.00× | saw-вокодер + овердрайв + биткраш | +| Chipmunk | +7 пт | 1.40× | хорус | | Helium | +10 пт | 1.50× | хорус | -| Vader | −10 пт | 0.60× | реверб | +| Vader | −10 пт | 0.60× | овердрайв + реверб + тремоло | | Telephone | 0 пт | 1.00× | полосовой фильтр + шум + компрессор | -| Growl | −5 пт | 0.70× | дисторшн + реверб | -| Walkie-Talkie | 0 пт | 1.05× | узкий bandpass + биткраш + сквилч-шум + компрессор | +| Growl | −5 пт | 0.70× | дисторшн + компрессор + реверб | +| Walkie-Talkie | 0 пт | 1.05× | узкий bandpass + сквилч-шум + компрессор + биткраш | ### Запуск @@ -228,9 +228,9 @@ virtual_mic = true ] } ``` -Любой CLI-флаг перекрывает файл. Эффекты: `Distortion`, `Bitcrush`, `Reverb`, -`Chorus`, `RingMod`, `Bandpass`, `Noise`, `Compressor`, `Vocoder` (carrier: -`Noise` / `Saw`). +Любой CLI-флаг перекрывает файл. Эффекты: `Distortion`, `Overdrive`, `Bitcrush`, `Reverb`, `Chorus`, `Flanger`, +`Tremolo`, `RingMod`, `Bandpass`, `Noise`, `Compressor`, `Vocoder` (carrier: +`Noise` / `Saw` / `Input`). Свои пресеты также принимают `gate_db`. ### Зависимости diff --git a/src/config.rs b/src/config.rs index 122d031..2c838bc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -55,6 +55,7 @@ impl FileConfig { formant_ratio: c.formant.unwrap_or(1.0), effects: c.effects.clone().unwrap_or_default(), gain: 1.0, + gate_db: c.gate_db.unwrap_or(-100.0), }) .collect() }) diff --git a/src/dsp/chain.rs b/src/dsp/chain.rs index d731905..3829693 100644 --- a/src/dsp/chain.rs +++ b/src/dsp/chain.rs @@ -148,7 +148,12 @@ impl Processor { ctrl.preset_idx.min(presets().len() - 1), preset.pitch_semitones + ctrl.pitch_delta, preset.formant_ratio * ratio_from_semitones(ctrl.formant_delta), - ctrl.gate_threshold_db, + // User-set gate wins; otherwise fall back to the preset's gate. + if ctrl.gate_threshold_db > -90.0 { + ctrl.gate_threshold_db + } else { + preset.gate_db + }, ctrl.gain, preset.gain, ctrl.mute, diff --git a/src/dsp/effects.rs b/src/dsp/effects.rs index 9a93fff..1e4788b 100644 --- a/src/dsp/effects.rs +++ b/src/dsp/effects.rs @@ -496,6 +496,138 @@ impl Effect for Compressor { } } +// --------------------------------------------------------------------------- +// Overdrive (soft tube-style saturation, unity gain at low levels) +// --------------------------------------------------------------------------- + +pub struct Overdrive { + drive: f32, + output: f32, +} + +impl Overdrive { + pub fn new(drive: f32, output: f32) -> Self { + Self { + drive: drive.max(0.01), + output, + } + } +} + +impl Effect for Overdrive { + fn process(&mut self, mono: &mut [f32]) { + let d = self.drive; + let o = self.output; + for s in mono.iter_mut() { + *s = ((*s * d).atan() / d) * o; + } + } +} + +// --------------------------------------------------------------------------- +// Tremolo (LFO amplitude modulation) +// --------------------------------------------------------------------------- + +pub struct Tremolo { + depth: f32, + rate: f32, + phase: f32, + fs: f32, +} + +impl Tremolo { + pub fn new(sample_rate: u32, depth: f32, rate_hz: f32) -> Self { + Self { + depth: depth.clamp(0.0, 1.0), + rate: rate_hz, + phase: 0.0, + fs: sample_rate as f32, + } + } +} + +impl Effect for Tremolo { + fn process(&mut self, mono: &mut [f32]) { + for s in mono.iter_mut() { + let lfo = (2.0 * PI * self.phase).sin(); + let amp = 1.0 - self.depth * (0.5 - 0.5 * lfo); + *s *= amp; + self.phase += self.rate / self.fs; + if self.phase >= 1.0 { + self.phase -= 1.0; + } + } + } +} + +// --------------------------------------------------------------------------- +// Flanger (short modulated delay with feedback) +// --------------------------------------------------------------------------- + +pub struct Flanger { + delay: Vec, + idx: usize, + cap: usize, + base: f32, + depth: f32, + rate: f32, + feedback: f32, + wet: f32, + phase: f32, + fs: f32, +} + +impl Flanger { + pub fn new(sample_rate: u32, depth_ms: f32, rate_hz: f32, feedback: f32, wet: f32) -> Self { + let depth = (depth_ms / 1000.0 * sample_rate as f32).max(1.0); + let cap = (sample_rate as f32 * 0.1) as usize + (depth * 2.0) as usize + 2; + Self { + delay: vec![0.0; cap], + idx: 0, + cap, + base: depth * 0.5, + depth: depth * 0.5, + rate: rate_hz, + feedback: feedback.clamp(0.0, 0.9), + wet: wet.clamp(0.0, 1.0), + phase: 0.0, + fs: sample_rate as f32, + } + } + + #[inline] + fn read(&self, offset: f32) -> f32 { + let mut pos = self.idx as f32 - offset; + if pos < 0.0 { + pos += self.cap as f32; + } + let i = pos.floor() as usize % self.cap; + let j = (i + 1) % self.cap; + let t = pos - pos.floor(); + self.delay[i] * (1.0 - t) + self.delay[j] * t + } +} + +impl Effect for Flanger { + fn process(&mut self, mono: &mut [f32]) { + let wet = self.wet; + let dry = 1.0 - wet; + for s in mono.iter_mut() { + let lfo = (2.0 * PI * self.phase).sin(); + let offset = self.base + self.depth * lfo; + let delayed = self.read(offset); + let y = *s * dry + delayed * wet; + self.delay[self.idx] = *s + delayed * self.feedback; + *s = y; + self.idx = (self.idx + 1) % self.cap; + self.phase += self.rate / self.fs; + if self.phase >= 1.0 { + self.phase -= 1.0; + } + } + } +} + // --------------------------------------------------------------------------- // Channel vocoder (classic robot voice) // --------------------------------------------------------------------------- @@ -504,6 +636,7 @@ impl Effect for Compressor { pub enum CarrierKind { Noise, Saw, + Input, } pub struct ChannelVocoder { @@ -511,6 +644,9 @@ pub struct ChannelVocoder { carrier: CarrierKind, bands_count: usize, wet: f32, + /// Output makeup so vocoder presets stay loud without giant external gain. + makeup: f32, + carrier_hz: f32, phase: f32, noise_state: u32, fs: f32, @@ -523,7 +659,13 @@ struct VocoderBand { } impl ChannelVocoder { - pub fn new(sample_rate: u32, bands_count: usize, carrier: CarrierKind, wet: f32) -> Self { + pub fn new( + sample_rate: u32, + bands_count: usize, + carrier: CarrierKind, + carrier_hz: f32, + wet: f32, + ) -> Self { let fs = sample_rate as f32; let low: f32 = 150.0; let high: f32 = 4500.0; @@ -546,6 +688,8 @@ impl ChannelVocoder { carrier, bands_count: n, wet: wet.clamp(0.0, 1.0), + makeup: 5.0, + carrier_hz: carrier_hz.max(20.0), phase: 0.0, noise_state: 0x2545F491, fs, @@ -553,7 +697,7 @@ impl ChannelVocoder { } #[inline] - fn carrier_sample(&mut self) -> f32 { + fn carrier_sample(&mut self, input: f32) -> f32 { match self.carrier { CarrierKind::Noise => { let mut x = self.noise_state; @@ -565,12 +709,13 @@ impl ChannelVocoder { } CarrierKind::Saw => { let s = self.phase * 2.0 - 1.0; - self.phase += 1.0 / self.fs * 120.0; + self.phase += self.carrier_hz / self.fs; if self.phase >= 1.0 { self.phase -= 1.0; } s } + CarrierKind::Input => input, } } } @@ -585,7 +730,7 @@ impl Effect for ChannelVocoder { for s in mono.iter_mut() { let input = *s; - let carrier = self.carrier_sample(); + let carrier = self.carrier_sample(input); let mut acc = 0.0; for band in &mut self.bands { @@ -599,7 +744,7 @@ impl Effect for ChannelVocoder { acc += band.synthesis.tick(carrier) * band.env; } - *s = input * dry + acc * wet * inv_n * 4.0; + *s = input * dry + acc * wet * inv_n * self.makeup; } } } diff --git a/src/dsp/presets.rs b/src/dsp/presets.rs index 1d55b7b..2818008 100644 --- a/src/dsp/presets.rs +++ b/src/dsp/presets.rs @@ -16,6 +16,8 @@ pub struct Preset { pub effects: Vec, /// Output makeup gain so every preset sounds equally loud. pub gain: f32, + /// Noise gate threshold for this preset (-100 = off). + pub gate_db: f32, } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] @@ -24,6 +26,10 @@ pub enum EffectSpec { drive: f32, output: f32, }, + Overdrive { + drive: f32, + output: f32, + }, Bitcrush { bits: u8, sample_rate_div: u32, @@ -40,6 +46,16 @@ pub enum EffectSpec { rate_hz: f32, wet: f32, }, + Flanger { + depth_ms: f32, + rate_hz: f32, + feedback: f32, + wet: f32, + }, + Tremolo { + depth: f32, + rate_hz: f32, + }, RingMod { freq_hz: f32, depth: f32, @@ -60,6 +76,7 @@ pub enum EffectSpec { Vocoder { bands: usize, carrier: CarrierKind, + carrier_hz: f32, wet: f32, }, } @@ -70,6 +87,7 @@ pub struct CustomPresetCfg { pub pitch: Option, pub formant: Option, pub effects: Option>, + pub gate_db: Option, } static PRESETS: OnceLock> = OnceLock::new(); @@ -81,49 +99,79 @@ fn builtin_presets() -> Vec { pitch_semitones: 0.0, formant_ratio: 1.0, effects: vec![], - gain: 1.10, + gain: 1.1, + gate_db: -100.0, }, Preset { name: "Girl / Anime".into(), pitch_semitones: 5.0, formant_ratio: 1.25, - effects: vec![], - gain: 1.50, + effects: vec![ + EffectSpec::Chorus { + depth_ms: 4.0, + rate_hz: 0.6, + wet: 0.25, + }, + EffectSpec::Overdrive { + drive: 1.4, + output: 1.0, + }, + ], + gain: 1.3, + gate_db: -45.0, }, Preset { name: "Boy".into(), - pitch_semitones: 3.0, - formant_ratio: 1.12, - effects: vec![], - gain: 1.00, + pitch_semitones: 2.0, + formant_ratio: 1.08, + effects: vec![EffectSpec::Compressor { + threshold_db: -20.0, + ratio: 3.0, + makeup_db: 4.0, + }], + gain: 1.1, + gate_db: -45.0, }, Preset { name: "Manly / Deep".into(), pitch_semitones: -4.0, formant_ratio: 0.78, - effects: vec![EffectSpec::Compressor { - threshold_db: -20.0, - ratio: 3.0, - makeup_db: 2.0, - }], - gain: 1.50, + effects: vec![ + EffectSpec::Compressor { + threshold_db: -20.0, + ratio: 3.0, + makeup_db: 2.0, + }, + EffectSpec::Reverb { + room: 0.25, + damp: 0.5, + wet: 0.15, + }, + ], + gain: 1.3, + gate_db: -40.0, }, Preset { name: "Demon".into(), pitch_semitones: -9.0, formant_ratio: 0.85, effects: vec![ - EffectSpec::Distortion { - drive: 6.0, - output: 0.7, + EffectSpec::Overdrive { + drive: 2.5, + output: 0.8, }, EffectSpec::Reverb { - room: 0.6, - damp: 0.4, - wet: 0.4, + room: 0.5, + damp: 0.45, + wet: 0.3, + }, + EffectSpec::Tremolo { + depth: 0.15, + rate_hz: 4.0, }, ], - gain: 1.00, + gain: 1.0, + gate_db: -38.0, }, Preset { name: "Robot".into(), @@ -133,16 +181,18 @@ fn builtin_presets() -> Vec { EffectSpec::Vocoder { bands: 16, carrier: CarrierKind::Noise, + carrier_hz: 110.0, wet: 1.0, }, EffectSpec::Bitcrush { bits: 8, sample_rate_div: 2, - max_gain: 8.0, + max_gain: 4.0, target: 0.6, }, ], - gain: 6.00, + gain: 1.8, + gate_db: -40.0, }, Preset { name: "8-bit".into(), @@ -154,7 +204,8 @@ fn builtin_presets() -> Vec { max_gain: 32.0, target: 0.6, }], - gain: 1.00, + gain: 1.0, + gate_db: -45.0, }, Preset { name: "Alien".into(), @@ -162,16 +213,18 @@ fn builtin_presets() -> Vec { formant_ratio: 1.15, effects: vec![ EffectSpec::RingMod { - freq_hz: 55.0, + freq_hz: 140.0, depth: 0.5, }, - EffectSpec::Chorus { - depth_ms: 6.0, - rate_hz: 0.5, - wet: 0.4, + EffectSpec::Flanger { + depth_ms: 2.5, + rate_hz: 0.4, + feedback: 0.5, + wet: 0.5, }, ], - gain: 5.50, + gain: 2.0, + gate_db: -40.0, }, Preset { name: "Radio".into(), @@ -190,7 +243,8 @@ fn builtin_presets() -> Vec { makeup_db: 6.0, }, ], - gain: 1.00, + gain: 1.0, + gate_db: -40.0, }, Preset { name: "Megaphone".into(), @@ -212,7 +266,8 @@ fn builtin_presets() -> Vec { makeup_db: 4.0, }, ], - gain: 1.60, + gain: 1.2, + gate_db: -38.0, }, Preset { name: "Ghost".into(), @@ -220,17 +275,22 @@ fn builtin_presets() -> Vec { formant_ratio: 1.05, effects: vec![ EffectSpec::Reverb { - room: 0.95, - damp: 0.2, - wet: 0.8, + room: 0.85, + damp: 0.3, + wet: 0.6, }, EffectSpec::Chorus { - depth_ms: 8.0, - rate_hz: 0.4, - wet: 0.5, + depth_ms: 5.0, + rate_hz: 0.5, + wet: 0.3, + }, + EffectSpec::Tremolo { + depth: 0.2, + rate_hz: 1.5, }, ], - gain: 1.00, + gain: 0.80, + gate_db: -40.0, }, Preset { name: "Cyborg".into(), @@ -240,28 +300,34 @@ fn builtin_presets() -> Vec { EffectSpec::Vocoder { bands: 16, carrier: CarrierKind::Saw, + carrier_hz: 220.0, wet: 0.9, }, + EffectSpec::Overdrive { + drive: 1.5, + output: 1.0, + }, EffectSpec::Bitcrush { bits: 8, sample_rate_div: 2, - max_gain: 8.0, + max_gain: 4.0, target: 0.6, }, ], - gain: 1.20, + gain: 1.8, + gate_db: -40.0, }, Preset { name: "Chipmunk".into(), pitch_semitones: 7.0, formant_ratio: 1.4, - effects: vec![EffectSpec::Bitcrush { - bits: 8, - sample_rate_div: 2, - max_gain: 8.0, - target: 0.5, + effects: vec![EffectSpec::Chorus { + depth_ms: 3.0, + rate_hz: 0.8, + wet: 0.2, }], - gain: 1.00, + gain: 1.0, + gate_db: -45.0, }, Preset { name: "Helium".into(), @@ -272,18 +338,30 @@ fn builtin_presets() -> Vec { rate_hz: 0.6, wet: 0.3, }], - gain: 1.20, + gain: 1.0, + gate_db: -45.0, }, Preset { name: "Vader".into(), pitch_semitones: -10.0, formant_ratio: 0.6, - effects: vec![EffectSpec::Reverb { - room: 0.7, - damp: 0.3, - wet: 0.35, - }], - gain: 2.80, + effects: vec![ + EffectSpec::Overdrive { + drive: 2.0, + output: 0.8, + }, + EffectSpec::Reverb { + room: 0.6, + damp: 0.4, + wet: 0.3, + }, + EffectSpec::Tremolo { + depth: 0.1, + rate_hz: 2.0, + }, + ], + gain: 1.4, + gate_db: -38.0, }, Preset { name: "Telephone".into(), @@ -302,7 +380,8 @@ fn builtin_presets() -> Vec { makeup_db: 8.0, }, ], - gain: 1.00, + gain: 1.0, + gate_db: -42.0, }, Preset { name: "Walkie-Talkie".into(), @@ -314,20 +393,21 @@ fn builtin_presets() -> Vec { high_hz: 2400.0, mix: 1.0, }, - EffectSpec::Bitcrush { - bits: 8, - sample_rate_div: 2, - max_gain: 4.0, - target: 0.6, - }, EffectSpec::Noise { gain: 0.05 }, EffectSpec::Compressor { threshold_db: -20.0, ratio: 4.0, makeup_db: 4.0, }, + EffectSpec::Bitcrush { + bits: 8, + sample_rate_div: 2, + max_gain: 4.0, + target: 0.6, + }, ], - gain: 1.30, + gain: 1.00, + gate_db: -40.0, }, Preset { name: "Growl".into(), @@ -338,18 +418,23 @@ fn builtin_presets() -> Vec { drive: 4.0, output: 0.6, }, + EffectSpec::Compressor { + threshold_db: -18.0, + ratio: 4.0, + makeup_db: 4.0, + }, EffectSpec::Reverb { - room: 0.5, - damp: 0.4, - wet: 0.3, + room: 0.3, + damp: 0.5, + wet: 0.2, }, ], - gain: 1.00, + gain: 1.0, + gate_db: -38.0, }, ] } -/// Register custom presets (from the config file). Call once at startup. pub fn init_custom(customs: Vec) { let _ = PRESETS.get_or_init(|| { let mut all = builtin_presets(); @@ -386,6 +471,9 @@ fn build_effect(spec: &EffectSpec, sample_rate: u32) -> Box { EffectSpec::Distortion { drive, output } => { Box::new(effects::Distortion::new(*drive, *output)) } + EffectSpec::Overdrive { drive, output } => { + Box::new(effects::Overdrive::new(*drive, *output)) + } EffectSpec::Bitcrush { bits, sample_rate_div, @@ -405,6 +493,21 @@ fn build_effect(spec: &EffectSpec, sample_rate: u32) -> Box { rate_hz, wet, } => Box::new(effects::Chorus::new(sample_rate, *depth_ms, *rate_hz, *wet)), + EffectSpec::Flanger { + depth_ms, + rate_hz, + feedback, + wet, + } => Box::new(effects::Flanger::new( + sample_rate, + *depth_ms, + *rate_hz, + *feedback, + *wet, + )), + EffectSpec::Tremolo { depth, rate_hz } => { + Box::new(effects::Tremolo::new(sample_rate, *depth, *rate_hz)) + } EffectSpec::RingMod { freq_hz, depth } => { Box::new(effects::RingMod::new(sample_rate, *freq_hz, *depth)) } @@ -427,11 +530,13 @@ fn build_effect(spec: &EffectSpec, sample_rate: u32) -> Box { EffectSpec::Vocoder { bands, carrier, + carrier_hz, wet, } => Box::new(effects::ChannelVocoder::new( sample_rate, *bands, *carrier, + *carrier_hz, *wet, )), } diff --git a/src/ui/tui.rs b/src/ui/tui.rs index 3b60cb4..26b7b7d 100644 --- a/src/ui/tui.rs +++ b/src/ui/tui.rs @@ -128,7 +128,11 @@ impl App { preset.name.to_string(), preset.pitch_semitones + c.pitch_delta, preset.formant_ratio * 2f32.powf(c.formant_delta / 12.0), - c.gate_threshold_db, + if c.gate_threshold_db > -90.0 { + c.gate_threshold_db + } else { + preset.gate_db + }, c.gain, c.mute, c.record,