Rework presets + add Flanger/Tremolo/Overdrive, per-preset gate, vocoder carrier Hz
- New effects: Overdrive (soft atan saturation), Tremolo (LFO AM), Flanger (modulated delay + feedback) - Vocoder carrier is now parametrizable (carrier_hz) and CarrierKind::Input is available again; internal makeup raised so no giant external gains - Per-preset gate_db: Demon/Ghost/etc. gate room noise; user gate wins - Preset rework per review: Girl gets chorus+overdrive shimmer, Boy neutral formant + compressor, Demon less dirty (overdrive, gate, tremolo), Robot/Cyborg differentiated, Alien ring-mod 140Hz + flanger, Vader gets overdrive growl, Ghost tremolo, Walkie-Talkie bitcrush moved last, Growl compressor, Chipmunk drops bitcrush - Gains normalized to moderate values (no more 6x clip factories)
This commit is contained in:
parent
13f85b4220
commit
72769f4615
6 changed files with 361 additions and 101 deletions
56
README.md
56
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`.
|
||||
|
||||
### Зависимости
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<f32>,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ pub struct Preset {
|
|||
pub effects: Vec<EffectSpec>,
|
||||
/// 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<f32>,
|
||||
pub formant: Option<f32>,
|
||||
pub effects: Option<Vec<EffectSpec>>,
|
||||
pub gate_db: Option<f32>,
|
||||
}
|
||||
|
||||
static PRESETS: OnceLock<Vec<Preset>> = OnceLock::new();
|
||||
|
|
@ -81,49 +99,79 @@ fn builtin_presets() -> Vec<Preset> {
|
|||
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<Preset> {
|
|||
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<Preset> {
|
|||
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<Preset> {
|
|||
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<Preset> {
|
|||
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<Preset> {
|
|||
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<Preset> {
|
|||
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<Preset> {
|
|||
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<Preset> {
|
|||
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<Preset> {
|
|||
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<Preset> {
|
|||
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<Preset> {
|
|||
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<Preset>) {
|
||||
let _ = PRESETS.get_or_init(|| {
|
||||
let mut all = builtin_presets();
|
||||
|
|
@ -386,6 +471,9 @@ fn build_effect(spec: &EffectSpec, sample_rate: u32) -> Box<dyn Effect> {
|
|||
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<dyn Effect> {
|
|||
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<dyn Effect> {
|
|||
EffectSpec::Vocoder {
|
||||
bands,
|
||||
carrier,
|
||||
carrier_hz,
|
||||
wet,
|
||||
} => Box::new(effects::ChannelVocoder::new(
|
||||
sample_rate,
|
||||
*bands,
|
||||
*carrier,
|
||||
*carrier_hz,
|
||||
*wet,
|
||||
)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue