From 245a69ee4dfbe41dd61080259c7fa8fde6516057 Mon Sep 17 00:00:00 2001 From: lobo Date: Thu, 12 Mar 2026 12:44:49 +0100 Subject: [PATCH] feat: expand synth presets + bump version to 1.2.4 Add 22 new synth presets (44 total), doubling the preset library: - Bass: Growl Bass, Rubber Bass, FM Bass (dual-LFO showcases) - Lead: Trance Lead, Portamento Lead (LFO1+LFO2 combos) - Pad: Shimmer Pad, Evolving Pad (cross-modulated PWM/detune) - Pluck: Kalimba, Harp - Stab: Detroit Stab, Brass Stab - Keys: Wurlitzer, Toy Piano - Drums (NEW category): Synth Kick, 808 Kick, Synth Snare, Noise Snare, Synth Tom, Zap, Clap - FX: Siren, Riser, Robot New helper functions: with_lfo2(), with_lfos(), with_sends() for convenient dual-LFO and effect preset construction. Co-Authored-By: Claude Opus 4.6 --- Cargo.toml | 2 +- src/presets/synth_presets.rs | 325 ++++++++++++++++++++++++++++++++++- src/ui/splash.rs | 2 +- 3 files changed, 326 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7b302db..8befc89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "textstep" -version = "1.2.3" +version = "1.2.4" edition = "2024" [dependencies] diff --git a/src/presets/synth_presets.rs b/src/presets/synth_presets.rs index d31fdef..3ff0231 100644 --- a/src/presets/synth_presets.rs +++ b/src/presets/synth_presets.rs @@ -30,7 +30,7 @@ const fn sp( } } -/// Override LFO settings on a preset +/// Override LFO1 settings on a preset const fn with_lfo(mut p: SynthParams, wave: u8, div: f32, depth: f32, dest: u8) -> SynthParams { p.lfo_waveform = wave; p.lfo_division = div; @@ -39,6 +39,31 @@ const fn with_lfo(mut p: SynthParams, wave: u8, div: f32, depth: f32, dest: u8) p } +/// Override LFO2 settings on a preset +const fn with_lfo2(mut p: SynthParams, wave: u8, div: f32, depth: f32, dest: u8) -> SynthParams { + p.lfo2_waveform = wave; + p.lfo2_division = div; + p.lfo2_depth = depth; + p.lfo2_dest = dest; + p +} + +/// Override both LFO1 and LFO2 settings +const fn with_lfos( + p: SynthParams, + w1: u8, d1: f32, dp1: f32, dst1: u8, + w2: u8, d2: f32, dp2: f32, dst2: u8, +) -> SynthParams { + with_lfo2(with_lfo(p, w1, d1, dp1, dst1), w2, d2, dp2, dst2) +} + +/// Override send effects on a preset +const fn with_sends(mut p: SynthParams, reverb: f32, delay: f32) -> SynthParams { + p.send_reverb = reverb; + p.send_delay = delay; + p +} + pub static SYNTH_PRESETS: &[SynthSoundPreset] = &[ // ── Bass ───────────────────────────────────────────────────────────── SynthSoundPreset { @@ -102,6 +127,46 @@ pub static SYNTH_PRESETS: &[SynthSoundPreset] = &[ 0.80), 1, 0.47, 0.3, 3), // LFO: Tri, 1/4, moderate, → Osc1 PWM }, + SynthSoundPreset { + name: "Growl Bass", category: "Bass", + // Saw + sub, LFO1 wobbles filter, LFO2 pumps osc2 level rhythmically + params: with_lfos(sp(1, 0.5, 0.0, 0.9, + 1, 0.5, 0.0, 0.5, 0.54, + 0.6, + 0.01, 0.2, 0.9, 0.1, + 0.01, 0.2, 0.9, 0.1, + 0, 0.35, 0.45, 0.15, + 0.0, 0.25, 0.2, 0.1, + 0.80), + 1, 0.22, 0.40, 0, // LFO1: Tri, 1/8, strong → Filter + 4, 0.35, 0.30, 7), // LFO2: Square, 1/4T, moderate → Osc2 Level + }, + SynthSoundPreset { + name: "Rubber Bass", category: "Bass", + // Sine + detuned saw, LFO1 slow pitch drift, LFO2 on filter + params: with_lfos(sp(2, 0.5, 0.0, 0.8, + 1, 0.5, 0.0, 0.6, 0.53, + 0.5, + 0.01, 0.15, 0.85, 0.12, + 0.01, 0.15, 0.85, 0.12, + 0, 0.38, 0.25, 0.10, + 0.0, 0.3, 0.2, 0.15, + 0.78), + 0, 0.67, 0.06, 2, // LFO1: Sine, 1 bar, subtle → Osc1 Pitch + 1, 0.47, 0.20, 0), // LFO2: Tri, 1/4, moderate → Filter + }, + SynthSoundPreset { + name: "FM Bass", category: "Bass", + // Square + sine octave up, snappy filter env + params: sp(0, 0.5, 0.5, 0.7, + 2, 0.75, 0.0, 0.5, 0.5, // Sine +12 + 0.4, + 0.00, 0.12, 0.6, 0.08, + 0.00, 0.12, 0.6, 0.08, + 0, 0.30, 0.35, 0.50, // LP, low cutoff, reso, strong env + 0.0, 0.10, 0.0, 0.06, + 0.82), + }, // ── Lead ───────────────────────────────────────────────────────────── SynthSoundPreset { @@ -141,6 +206,34 @@ pub static SYNTH_PRESETS: &[SynthSoundPreset] = &[ 0.70), 0, 0.35, 0.12, 2), // LFO: Sine, moderate rate, vibrato, → Osc1 Tune }, + SynthSoundPreset { + name: "Trance Lead", category: "Lead", + // Bright saw, LFO1 on PWM, LFO2 slow filter sweep for movement + params: with_lfos(sp(1, 0.5, 0.0, 0.9, + 0, 0.5, 0.4, 0.5, 0.52, + 0.0, + 0.03, 0.2, 0.85, 0.35, + 0.03, 0.2, 0.85, 0.35, + 0, 0.60, 0.30, 0.20, + 0.01, 0.3, 0.4, 0.3, + 0.72), + 1, 0.47, 0.25, 3, // LFO1: Tri, 1/4, moderate → Osc1 PWM + 0, 0.78, 0.15, 0), // LFO2: Sine, 2 bars, gentle → Filter + }, + SynthSoundPreset { + name: "Portamento Lead", category: "Lead", + // Single saw, no osc2, vibrato + filter motion + params: with_lfos(sp(1, 0.5, 0.0, 1.0, + 2, 0.5, 0.0, 0.0, 0.5, // Osc2 off + 0.2, + 0.04, 0.2, 0.9, 0.4, + 0.04, 0.2, 0.9, 0.4, + 0, 0.55, 0.20, 0.18, + 0.02, 0.3, 0.3, 0.3, + 0.78), + 0, 0.35, 0.10, 2, // LFO1: Sine, 1/4T, vibrato → Pitch + 1, 0.67, 0.12, 0), // LFO2: Tri, 1 bar, subtle → Filter + }, // ── Pad ────────────────────────────────────────────────────────────── SynthSoundPreset { @@ -195,6 +288,34 @@ pub static SYNTH_PRESETS: &[SynthSoundPreset] = &[ 0.60), 0, 0.88, 0.18, 0), // LFO: Sine, 4 bars, gentle, → Filter }, + SynthSoundPreset { + name: "Shimmer Pad", category: "Pad", + // Sines detuned, LFO1 slow filter, LFO2 slow detune drift + params: with_lfos(with_sends(sp(2, 0.5, 0.0, 0.6, + 2, 0.5, 0.0, 0.5, 0.53, + 0.2, + 0.50, 0.2, 0.85, 0.60, + 0.50, 0.2, 0.85, 0.60, + 0, 0.55, 0.20, 0.10, + 0.4, 0.6, 0.3, 0.5, + 0.62), 0.35, 0.15), + 0, 0.88, 0.12, 0, // LFO1: Sine, 4 bars, subtle → Filter + 1, 0.78, 0.08, 8), // LFO2: Tri, 2 bars, gentle → Osc2 Detune + }, + SynthSoundPreset { + name: "Evolving Pad", category: "Pad", + // Squares with PWM, LFO1 on PWM1, LFO2 on PWM2 at different rates + params: with_lfos(with_sends(sp(0, 0.5, 0.4, 0.6, + 0, 0.5, 0.6, 0.5, 0.52, + 0.3, + 0.45, 0.3, 0.75, 0.55, + 0.45, 0.3, 0.75, 0.55, + 0, 0.42, 0.15, 0.08, + 0.3, 0.5, 0.3, 0.4, + 0.65), 0.30, 0.10), + 1, 0.67, 0.30, 3, // LFO1: Tri, 1 bar, strong → Osc1 PWM + 1, 0.78, 0.25, 6), // LFO2: Tri, 2 bars, moderate → Osc2 PWM + }, // ── Pluck ──────────────────────────────────────────────────────────── SynthSoundPreset { @@ -231,6 +352,31 @@ pub static SYNTH_PRESETS: &[SynthSoundPreset] = &[ 0.80), }, + SynthSoundPreset { + name: "Kalimba", category: "Pluck", + // Sine + sine octave, very short decay, gentle filter env + params: sp(2, 0.5, 0.0, 0.8, + 2, 0.75, 0.0, 0.4, 0.5, // Sine +12 + 0.0, + 0.00, 0.18, 0.0, 0.08, + 0.00, 0.10, 0.0, 0.06, + 0, 0.65, 0.08, 0.35, + 0.0, 0.12, 0.0, 0.08, + 0.78), + }, + SynthSoundPreset { + name: "Harp", category: "Pluck", + // Saw + sine, moderate decay, open filter + params: with_sends(sp(1, 0.5, 0.0, 0.6, + 2, 0.5, 0.0, 0.5, 0.51, + 0.0, + 0.00, 0.35, 0.0, 0.25, + 0.00, 0.25, 0.0, 0.20, + 0, 0.60, 0.12, 0.40, + 0.0, 0.25, 0.0, 0.2, + 0.72), 0.30, 0.15), + }, + // ── Stab ───────────────────────────────────────────────────────────── SynthSoundPreset { name: "Chord Stab", category: "Stab", @@ -255,6 +401,31 @@ pub static SYNTH_PRESETS: &[SynthSoundPreset] = &[ 0.75), }, + SynthSoundPreset { + name: "Detroit Stab", category: "Stab", + // Single saw, fast filter env snap + params: sp(1, 0.5, 0.0, 0.9, + 1, 0.5, 0.0, 0.0, 0.5, + 0.2, + 0.00, 0.08, 0.0, 0.06, + 0.00, 0.08, 0.0, 0.06, + 0, 0.30, 0.20, 0.70, // LP, low cut, strong env + 0.0, 0.06, 0.0, 0.04, + 0.82), + }, + SynthSoundPreset { + name: "Brass Stab", category: "Stab", + // Two saws, medium attack for brass-like onset + params: sp(1, 0.5, 0.0, 0.8, + 1, 0.5, 0.0, 0.7, 0.53, + 0.3, + 0.06, 0.12, 0.5, 0.12, + 0.06, 0.12, 0.5, 0.12, + 0, 0.45, 0.15, 0.35, + 0.04, 0.10, 0.2, 0.1, + 0.78), + }, + // ── Keys ───────────────────────────────────────────────────────────── SynthSoundPreset { name: "Electric Piano", category: "Keys", @@ -292,6 +463,118 @@ pub static SYNTH_PRESETS: &[SynthSoundPreset] = &[ 0.80), }, + SynthSoundPreset { + name: "Wurlitzer", category: "Keys", + // Sine with tremolo (LFO1→Volume) and gentle filter env + params: with_lfo(sp(2, 0.5, 0.0, 0.8, + 2, 0.75, 0.0, 0.2, 0.5, // Sine +12, subtle + 0.0, + 0.01, 0.35, 0.55, 0.25, + 0.01, 0.20, 0.3, 0.15, + 0, 0.50, 0.12, 0.20, + 0.0, 0.25, 0.15, 0.2, + 0.72), + 0, 0.47, 0.10, 10), // LFO: Sine, 1/4, tremolo → Volume + }, + SynthSoundPreset { + name: "Toy Piano", category: "Keys", + // Square, short decay, bright, no filter env + params: sp(0, 0.5, 0.5, 0.8, + 2, 0.75, 0.0, 0.3, 0.5, + 0.0, + 0.00, 0.20, 0.0, 0.10, + 0.00, 0.12, 0.0, 0.08, + 0, 0.70, 0.05, 0.20, + 0.0, 0.10, 0.0, 0.08, + 0.75), + }, + + // ── Drums (synth-based percussion) ───────────────────────────────── + SynthSoundPreset { + name: "Synth Kick", category: "Drums", + // Sine with fast pitch drop (sweep), no sustain, punchy + params: sp(2, 0.25, 0.0, 1.0, // Sine, low tune + 2, 0.5, 0.0, 0.0, 0.5, // Osc2 off + 0.7, // Heavy sub + 0.00, 0.15, 0.0, 0.02, // Very short envelope + 0.00, 0.08, 0.0, 0.02, + 0, 0.45, 0.0, 0.30, // LP, moderate, some env + 0.0, 0.06, 0.0, 0.02, + 0.90), + }, + SynthSoundPreset { + name: "808 Kick", category: "Drums", + // Long sine sub with pitch sweep + params: sp(2, 0.20, 0.0, 1.0, + 2, 0.5, 0.0, 0.0, 0.5, + 0.8, + 0.00, 0.40, 0.0, 0.05, // Medium-long decay + 0.00, 0.25, 0.0, 0.05, + 0, 0.35, 0.0, 0.15, + 0.0, 0.10, 0.0, 0.03, + 0.88), + }, + SynthSoundPreset { + name: "Synth Snare", category: "Drums", + // Noise + sine body, short decay + params: sp(2, 0.45, 0.0, 0.6, // Sine body + 3, 0.5, 0.5, 0.7, 0.5, // Noise layer + 0.0, + 0.00, 0.12, 0.0, 0.04, + 0.00, 0.08, 0.0, 0.03, + 0, 0.55, 0.15, 0.45, // LP, moderate env + 0.0, 0.08, 0.0, 0.03, + 0.82), + }, + SynthSoundPreset { + name: "Noise Snare", category: "Drums", + // Pure noise, tight filter env for snap + params: sp(3, 0.5, 0.5, 0.9, + 3, 0.5, 0.3, 0.3, 0.5, + 0.0, + 0.00, 0.10, 0.0, 0.03, + 0.00, 0.06, 0.0, 0.02, + 0, 0.40, 0.30, 0.60, + 0.0, 0.06, 0.0, 0.02, + 0.80), + }, + SynthSoundPreset { + name: "Synth Tom", category: "Drums", + // Sine with pitch sweep, medium decay + params: sp(2, 0.40, 0.0, 0.9, + 2, 0.5, 0.0, 0.0, 0.5, + 0.3, + 0.00, 0.22, 0.0, 0.05, + 0.00, 0.15, 0.0, 0.04, + 0, 0.50, 0.10, 0.25, + 0.0, 0.10, 0.0, 0.04, + 0.80), + }, + SynthSoundPreset { + name: "Zap", category: "Drums", + // Fast pitch sweep down, very short — like a 909 zap + params: sp(2, 0.55, 0.0, 1.0, + 2, 0.5, 0.0, 0.0, 0.5, + 0.0, + 0.00, 0.06, 0.0, 0.02, + 0.00, 0.04, 0.0, 0.01, + 0, 0.70, 0.0, 0.80, // Strong filter env for sweep + 0.0, 0.04, 0.0, 0.01, + 0.78), + }, + SynthSoundPreset { + name: "Clap", category: "Drums", + // Noise burst, medium decay + params: sp(3, 0.5, 0.5, 0.8, + 3, 0.5, 0.3, 0.4, 0.5, + 0.0, + 0.00, 0.15, 0.0, 0.05, + 0.00, 0.10, 0.0, 0.04, + 0, 0.50, 0.25, 0.40, + 0.0, 0.08, 0.0, 0.04, + 0.75), + }, + // ── FX ─────────────────────────────────────────────────────────────── SynthSoundPreset { name: "Noise Sweep", category: "FX", @@ -328,6 +611,46 @@ pub static SYNTH_PRESETS: &[SynthSoundPreset] = &[ 0.55), 0, 0.78, 0.25, 0), // LFO: Sine, 2 bars, moderate, → Filter }, + SynthSoundPreset { + name: "Siren", category: "FX", + // Sine, fast LFO1 on pitch, LFO2 on filter for texture + params: with_lfos(sp(2, 0.5, 0.0, 0.9, + 2, 0.5, 0.0, 0.0, 0.5, + 0.0, + 0.01, 0.1, 1.0, 0.3, + 0.01, 0.1, 1.0, 0.3, + 0, 0.60, 0.10, 0.0, + 0.0, 0.1, 0.0, 0.1, + 0.70), + 0, 0.10, 0.35, 2, // LFO1: Sine, 1/16, strong → Pitch + 1, 0.47, 0.20, 0), // LFO2: Tri, 1/4, moderate → Filter + }, + SynthSoundPreset { + name: "Riser", category: "FX", + // Noise, long attack envelope, building tension + params: sp(3, 0.5, 0.5, 0.7, + 3, 0.5, 0.3, 0.4, 0.5, + 0.0, + 0.90, 0.1, 0.8, 0.30, // Very long attack + 0.90, 0.1, 0.8, 0.30, + 0, 0.25, 0.35, 0.50, // LP, low cut, opens with env + 0.80, 0.2, 0.6, 0.3, // Filter env also long attack + 0.65), + }, + SynthSoundPreset { + name: "Robot", category: "FX", + // Square, LFO1 fast on pitch (ring mod effect), LFO2 on volume (tremolo) + params: with_lfos(sp(0, 0.5, 0.5, 0.8, + 0, 0.75, 0.5, 0.4, 0.5, + 0.0, + 0.01, 0.1, 0.8, 0.1, + 0.01, 0.1, 0.8, 0.1, + 0, 0.50, 0.15, 0.0, + 0.0, 0.1, 0.0, 0.05, + 0.70), + 0, 0.05, 0.20, 2, // LFO1: Sine, 1/16D, fast pitch → ring mod + 4, 0.22, 0.40, 10), // LFO2: Square, 1/8, strong tremolo → Volume + }, ]; pub fn categories() -> Vec<&'static str> { diff --git a/src/ui/splash.rs b/src/ui/splash.rs index c0b745e..374279b 100644 --- a/src/ui/splash.rs +++ b/src/ui/splash.rs @@ -28,7 +28,7 @@ const LOGO_WIDTH: u16 = (LETTER_WIDTH * 8 + LETTER_GAP * 7) as u16; // 54 const LOGO_HEIGHT: u16 = LOGO_ROWS as u16; const SUBTITLE: &str = "step sequencer + synthesizer"; -const VERSION: &str = "v1.2.3"; +const VERSION: &str = "v1.2.4"; // Step pattern displayed below the logo (16 steps) const STEP_PATTERN: [bool; 16] = [