Skip to main content

mlua_pulse/
instruments.rs

1use crate::error::{PulseError, PulseResult};
2use tunes::instruments::Instrument;
3
4type InstrumentConstructor = fn() -> Instrument;
5
6#[derive(Clone, Copy)]
7struct InstrumentPreset {
8    name: &'static str,
9    constructor: InstrumentConstructor,
10}
11
12macro_rules! instrument_presets {
13    ($($name:ident),+ $(,)?) => {
14        const INSTRUMENT_PRESETS: &[InstrumentPreset] = &[
15            $(InstrumentPreset {
16                name: stringify!($name),
17                constructor: Instrument::$name,
18            },)+
19        ];
20
21        const INSTRUMENT_NAMES: &[&str] = &[
22            $(stringify!($name),)+
23        ];
24    };
25}
26
27instrument_presets!(
28    accordion,
29    acid_bass,
30    acid_synth,
31    acoustic_guitar,
32    acoustic_piano,
33    alto_sax,
34    ambient_pad,
35    analog_brass,
36    arp_lead,
37    bagpipes,
38    balalaika,
39    banjo,
40    baritone_sax,
41    bass_808,
42    bass_percussion,
43    bassoon,
44    bells,
45    bitcrush_noise,
46    brass,
47    brass_section,
48    bright_lead,
49    celesta,
50    cello,
51    charango,
52    chiptune,
53    choir_aahs,
54    choir_oohs,
55    chorus_bass,
56    church_organ,
57    circuit_bent,
58    clarinet,
59    clavinet,
60    cosmic_rays,
61    cowbell,
62    cp70,
63    dark_bass,
64    dark_pad,
65    data_stream,
66    deep_bass,
67    detuned_lead,
68    didgeridoo,
69    djembe,
70    double_bass,
71    drone_machine,
72    duduk,
73    dulcimer,
74    dx7_electric_piano,
75    electric_guitar_clean,
76    electric_guitar_distorted,
77    electric_piano,
78    english_horn,
79    erhu,
80    euphonium,
81    fingerstyle_bass,
82    flugelhorn,
83    flute,
84    fm_bass,
85    fm_bells,
86    formant_synth,
87    french_horn,
88    fretless_bass,
89    funk_bass,
90    gaita,
91    glass_harmonica,
92    glitch,
93    glockenspiel,
94    granular_pad,
95    growl_bass,
96    guitar_12_string,
97    guitar_harmonics,
98    guitar_palm_muted,
99    guzheng,
100    hammond_organ,
101    harp,
102    harpsichord,
103    honky_tonk_piano,
104    hoover,
105    impact,
106    juno_pad,
107    kalimba,
108    koto,
109    laser,
110    laser_lead,
111    mallet,
112    marimba,
113    mark_i_rhodes,
114    mbira,
115    metallic_perc,
116    minimoog_bass,
117    music_box,
118    muted_trombone,
119    muted_trumpet,
120    oboe,
121    obx_strings,
122    organ,
123    oud,
124    pan_flute,
125    pianet,
126    piccolo,
127    picked_bass,
128    pizzicato_strings,
129    pluck,
130    pluck_bass,
131    prophet_brass,
132    pwm_bass,
133    reed_organ,
134    reese_bass,
135    ring_mod_bells,
136    riser,
137    saw_lead,
138    scream_lead,
139    sh101_lead,
140    shakuhachi,
141    shamisen,
142    shimmer_pad,
143    sitar,
144    slap_bass,
145    slow_strings,
146    solo_trumpet,
147    soprano_sax,
148    square_lead,
149    stab,
150    stage_73,
151    steel_drums,
152    string_pad,
153    strings,
154    sub_bass,
155    supersaw,
156    synth_bass,
157    synth_lead,
158    synth_voice,
159    tabla,
160    taiko,
161    taiko_drum,
162    tenor_sax,
163    theremin,
164    timpani,
165    toy_piano,
166    trance_synth,
167    tremolo_strings,
168    trombone,
169    tuba,
170    tubular_bells,
171    uilleann_pipes,
172    ukulele,
173    upright_bass,
174    vibraphone,
175    viola,
176    violin,
177    vocal_pad,
178    vocoder,
179    warm_pad,
180    wind_chimes,
181    wobble_bass,
182    wurlitzer,
183    xylophone,
184);
185
186/// Returns the stable Lua-facing instrument aliases.
187pub fn instrument_names() -> &'static [&'static str] {
188    INSTRUMENT_NAMES
189}
190
191/// Resolves stable Lua-facing aliases to `tunes` instrument presets.
192pub fn instrument_by_name(name: &str) -> PulseResult<Instrument> {
193    let normalized = normalize_name(name);
194    INSTRUMENT_PRESETS
195        .iter()
196        .find(|preset| normalize_name(preset.name) == normalized)
197        .map(|preset| (preset.constructor)())
198        .ok_or_else(|| PulseError::InvalidInstrument {
199            value: name.to_string(),
200        })
201}
202
203fn normalize_name(value: &str) -> String {
204    value
205        .trim()
206        .to_ascii_lowercase()
207        .replace(['_', '-', ' '], "")
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213
214    #[test]
215    fn exposes_all_tunes_instrument_presets() {
216        let names = instrument_names();
217        assert!(names.len() >= 150);
218        assert!(names.contains(&"electric_piano"));
219        assert!(names.contains(&"supersaw"));
220        assert!(names.contains(&"bass_808"));
221
222        for name in names {
223            let instrument = instrument_by_name(name).expect("catalog entry should resolve");
224            assert!(!instrument.name.trim().is_empty());
225        }
226    }
227
228    #[test]
229    fn still_accepts_existing_normalized_aliases() {
230        assert!(instrument_by_name("electric piano").is_ok());
231        assert!(instrument_by_name("Stage-73").is_ok());
232        assert!(instrument_by_name("synth_lead").is_ok());
233    }
234
235    #[test]
236    fn rejects_unknown_instrument_alias() {
237        let error = instrument_by_name("laser_harp").expect_err("unknown alias should fail");
238        assert_eq!(error.to_string(), "invalid instrument: laser_harp");
239    }
240}