skeleton & stubs

This commit is contained in:
2026-04-19 02:13:56 +03:00
parent e3f641d646
commit 4f14980610
13 changed files with 468 additions and 609 deletions

4
crates/params/Cargo.toml Normal file
View File

@@ -0,0 +1,4 @@
[package]
name = "params"
version.workspace = true
edition.workspace = true

278
crates/params/src/lib.rs Normal file
View File

@@ -0,0 +1,278 @@
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(usize)]
#[allow(dead_code)]
pub enum ParamId {
// Master
MasterVolume = 0,
MasterPan,
// Oscillator 1
Osc1Volume,
Osc1Pan,
Osc1Pitch,
Osc1Fine,
Osc1WavePos,
Osc1UnisonVoices,
Osc1UnisonDetune,
Osc1UnisonSpread,
// Oscillator 2
Osc2Volume,
Osc2Pan,
Osc2Pitch,
Osc2Fine,
Osc2WavePos,
Osc2UnisonVoices,
Osc2UnisonDetune,
Osc2UnisonSpread,
// Oscillator 3
Osc3Volume,
Osc3Pan,
Osc3Pitch,
Osc3Fine,
Osc3WavePos,
Osc3UnisonVoices,
Osc3UnisonDetune,
Osc3UnisonSpread,
// Envelope 1 (amplitude)
Env1Delay,
Env1Attack,
Env1Hold,
Env1Decay,
Env1Sustain,
Env1Release,
// Envelope 2
Env2Delay,
Env2Attack,
Env2Hold,
Env2Decay,
Env2Sustain,
Env2Release,
// Envelope 3
Env3Delay,
Env3Attack,
Env3Hold,
Env3Decay,
Env3Sustain,
Env3Release,
// LFO 14
Lfo1Rate,
Lfo1Depth,
Lfo1Phase,
Lfo2Rate,
Lfo2Depth,
Lfo2Phase,
Lfo3Rate,
Lfo3Depth,
Lfo3Phase,
Lfo4Rate,
Lfo4Depth,
Lfo4Phase,
// Filter 1
Filter1Cutoff,
Filter1Resonance,
Filter1Drive,
Filter1Keytrack,
// Filter 2
Filter2Cutoff,
Filter2Resonance,
Filter2Drive,
Filter2Keytrack,
// FX — Chorus
ChorusRate,
ChorusDepth,
ChorusMix,
// FX — Reverb
ReverbSize,
ReverbDamping,
ReverbMix,
// FX — Delay
DelayTime,
DelayFeedback,
DelayMix,
// FX — Distortion
DistortionDrive,
DistortionMix,
// FX — EQ
EqLowGain,
EqMidFreq,
EqMidGain,
EqHighGain,
// Macros
Macro1,
Macro2,
Macro3,
Macro4,
Macro5,
Macro6,
Macro7,
Macro8,
Count,
}
pub const PARAM_COUNT: usize = ParamId::Count as usize;
pub fn default_value(id: ParamId) -> f32 {
use ParamId::*;
match id {
MasterVolume => 0.8,
MasterPan => 0.5,
Osc1Volume => 0.8,
Osc2Volume | Osc3Volume => 0.0, // off by default
Osc1Pan | Osc2Pan | Osc3Pan => 0.5,
Osc1Pitch | Osc2Pitch | Osc3Pitch => 0.5,
Osc1Fine | Osc2Fine | Osc3Fine => 0.5,
Env1Attack | Env2Attack | Env3Attack => 0.02,
Env1Decay | Env2Decay | Env3Decay => 0.30,
Env1Sustain | Env2Sustain | Env3Sustain => 0.70,
Env1Release | Env2Release | Env3Release => 0.35,
Filter1Cutoff | Filter2Cutoff => 1.0,
Filter1Keytrack | Filter2Keytrack => 0.0,
Lfo1Rate | Lfo2Rate | Lfo3Rate | Lfo4Rate => 0.3,
_ => 0.0,
}
}
#[derive(Clone)]
pub struct ParamStore {
data: Arc<[AtomicU32]>,
}
impl ParamStore {
pub fn new() -> Self {
let data: Arc<[AtomicU32]> = (0..PARAM_COUNT)
.map(|i| {
let id: ParamId = unsafe { std::mem::transmute(i) };
AtomicU32::new(default_value(id).to_bits())
})
.collect::<Vec<_>>()
.into();
Self { data }
}
#[inline(always)]
pub fn get(&self, id: ParamId) -> f32 {
f32::from_bits(self.data[id as usize].load(Ordering::Relaxed))
}
#[inline(always)]
pub fn set(&self, id: ParamId, value: f32) {
debug_assert!((0.0..=1.0).contains(&value), "param out of range: {value}");
self.data[id as usize].store(value.to_bits(), Ordering::Relaxed);
}
pub fn reset_to_defaults(&self) {
for i in 0..PARAM_COUNT {
let id: ParamId = unsafe { std::mem::transmute(i) };
self.data[i].store(default_value(id).to_bits(), Ordering::Relaxed);
}
}
pub fn snapshot(&self) -> Vec<f32> {
(0..PARAM_COUNT)
.map(|i| f32::from_bits(self.data[i].load(Ordering::Relaxed)))
.collect()
}
pub fn restore(&self, values: &[f32]) {
assert_eq!(values.len(), PARAM_COUNT, "snapshot length mismatch");
for (i, &v) in values.iter().enumerate() {
self.data[i].store(v.to_bits(), Ordering::Relaxed);
}
}
}
impl Default for ParamStore {
fn default() -> Self {
Self::new()
}
}
pub struct ParamMeta {
pub name: &'static str,
pub label: &'static str,
pub steps: Option<u32>,
}
pub fn param_meta(id: ParamId) -> ParamMeta {
use ParamId::*;
match id {
MasterVolume => ParamMeta {
name: "Master Volume",
label: "%",
steps: None,
},
MasterPan => ParamMeta {
name: "Master Pan",
label: "",
steps: None,
},
Osc1Pitch => ParamMeta {
name: "Osc1 Pitch",
label: "st",
steps: Some(96),
},
Filter1Cutoff => ParamMeta {
name: "Filter1 Cutoff",
label: "Hz",
steps: None,
},
Filter1Resonance => ParamMeta {
name: "Filter1 Res",
label: "",
steps: None,
},
Env1Attack => ParamMeta {
name: "Env1 Attack",
label: "s",
steps: None,
},
Env1Decay => ParamMeta {
name: "Env1 Decay",
label: "s",
steps: None,
},
Env1Sustain => ParamMeta {
name: "Env1 Sustain",
label: "%",
steps: None,
},
Env1Release => ParamMeta {
name: "Env1 Release",
label: "s",
steps: None,
},
Macro1 => ParamMeta {
name: "Macro 1",
label: "",
steps: None,
},
_ => ParamMeta {
name: "?",
label: "",
steps: None,
},
}
}