const SONG_ID_TABLE: &[&str] = &[
"pad.level",
"pad.attack_time",
"pad.release_time",
"pad.type",
"pad.chord_bars",
"pad.chord_count",
"pad.progression",
"pad.reverb_mix",
"pad.stereo_width",
"pad.detune",
"pad.octave_mix",
"pad.chord1_degree",
"pad.chord1_accidental",
"pad.chord1_quality",
"pad.chord1_extension",
"pad.chord1_inversion",
"pad.chord2_degree",
"pad.chord2_accidental",
"pad.chord2_quality",
"pad.chord2_extension",
"pad.chord2_inversion",
"pad.chord3_degree",
"pad.chord3_accidental",
"pad.chord3_quality",
"pad.chord3_extension",
"pad.chord3_inversion",
"pad.chord4_degree",
"pad.chord4_accidental",
"pad.chord4_quality",
"pad.chord4_extension",
"pad.chord4_inversion",
"pad.chord5_degree",
"pad.chord5_accidental",
"pad.chord5_quality",
"pad.chord5_extension",
"pad.chord5_inversion",
"pad.chord6_degree",
"pad.chord6_accidental",
"pad.chord6_quality",
"pad.chord6_extension",
"pad.chord6_inversion",
"pad.chord7_degree",
"pad.chord7_accidental",
"pad.chord7_quality",
"pad.chord7_extension",
"pad.chord7_inversion",
"pad.chord8_degree",
"pad.chord8_accidental",
"pad.chord8_quality",
"pad.chord8_extension",
"pad.chord8_inversion",
"perc.level",
"perc.filter",
"perc.decay_ms",
"perc.interval_beats",
"perc.offset_beats",
"perc.swing",
"bass.level",
"bass.cutoff",
"bass.attack_time",
"bass.decay_time",
"bass.type",
"bass.interval_beats",
"bass.offset_beats",
"bass.rhythm",
"bass.octave",
"bass.drive",
"kick.level",
"kick.filter",
"kick.pitch_decay_ms",
"kick.amp_decay_ms",
"kick.type",
"kick.interval_beats",
"kick.offset_beats",
"kick.start_freq",
"kick.click",
"kick.drive",
"tonal.level",
"tonal.attack",
"tonal.decay",
"tonal.synth_type",
"tonal.octave",
"tonal.phrase",
"tonal.rate_beats",
"tonal.step_interval_beats",
"tonal.offset_beats",
"tonal.swing",
"tonal.randomness",
"tonal.evolve_rate",
"tonal.reverb_mix",
"clap.level",
"clap.filter",
"clap.decay_ms",
"clap.interval_beats",
"clap.offset_beats",
"clap.slap_count",
"clap.slap_spread_ms",
"clap.room",
"clap.body",
"arp.gain",
"arp.attack",
"arp.decay",
"arp.type",
"arp.rate_beats",
"arp.offset_beats",
"arp.swing",
"arp.pattern",
"arp.octaves",
"arp.reverb_mix",
"macro.1",
"macro.2",
"macro.3",
"macro.4",
"master.bpm",
"master.level",
"master.drive",
"master.comp_amount",
"master.comp_release_ms",
"master.tone",
"master.tune",
"master.comp_threshold",
"master.comp_ratio",
"master.comp_makeup",
"pad.slot1.kind",
"pad.slot1.amount",
"pad.slot1.time",
"pad.slot2.kind",
"pad.slot2.amount",
"pad.slot2.time",
"pad.slot3.kind",
"pad.slot3.amount",
"pad.slot3.time",
"pad.slot4.kind",
"pad.slot4.amount",
"pad.slot4.time",
"pad.slot5.kind",
"pad.slot5.amount",
"pad.slot5.time",
"pad.slot6.kind",
"pad.slot6.amount",
"pad.slot6.time",
"pad.slot7.kind",
"pad.slot7.amount",
"pad.slot7.time",
"pad.slot8.kind",
"pad.slot8.amount",
"pad.slot8.time",
"perc.slot1.kind",
"perc.slot1.amount",
"perc.slot1.time",
"perc.slot2.kind",
"perc.slot2.amount",
"perc.slot2.time",
"perc.slot3.kind",
"perc.slot3.amount",
"perc.slot3.time",
"perc.slot4.kind",
"perc.slot4.amount",
"perc.slot4.time",
"perc.slot5.kind",
"perc.slot5.amount",
"perc.slot5.time",
"perc.slot6.kind",
"perc.slot6.amount",
"perc.slot6.time",
"perc.slot7.kind",
"perc.slot7.amount",
"perc.slot7.time",
"perc.slot8.kind",
"perc.slot8.amount",
"perc.slot8.time",
"bass.slot1.kind",
"bass.slot1.amount",
"bass.slot1.time",
"bass.slot2.kind",
"bass.slot2.amount",
"bass.slot2.time",
"bass.slot3.kind",
"bass.slot3.amount",
"bass.slot3.time",
"bass.slot4.kind",
"bass.slot4.amount",
"bass.slot4.time",
"bass.slot5.kind",
"bass.slot5.amount",
"bass.slot5.time",
"bass.slot6.kind",
"bass.slot6.amount",
"bass.slot6.time",
"bass.slot7.kind",
"bass.slot7.amount",
"bass.slot7.time",
"bass.slot8.kind",
"bass.slot8.amount",
"bass.slot8.time",
"kick.slot1.kind",
"kick.slot1.amount",
"kick.slot1.time",
"kick.slot2.kind",
"kick.slot2.amount",
"kick.slot2.time",
"kick.slot3.kind",
"kick.slot3.amount",
"kick.slot3.time",
"kick.slot4.kind",
"kick.slot4.amount",
"kick.slot4.time",
"kick.slot5.kind",
"kick.slot5.amount",
"kick.slot5.time",
"kick.slot6.kind",
"kick.slot6.amount",
"kick.slot6.time",
"kick.slot7.kind",
"kick.slot7.amount",
"kick.slot7.time",
"kick.slot8.kind",
"kick.slot8.amount",
"kick.slot8.time",
"tonal.slot1.kind",
"tonal.slot1.amount",
"tonal.slot1.time",
"tonal.slot2.kind",
"tonal.slot2.amount",
"tonal.slot2.time",
"tonal.slot3.kind",
"tonal.slot3.amount",
"tonal.slot3.time",
"tonal.slot4.kind",
"tonal.slot4.amount",
"tonal.slot4.time",
"tonal.slot5.kind",
"tonal.slot5.amount",
"tonal.slot5.time",
"tonal.slot6.kind",
"tonal.slot6.amount",
"tonal.slot6.time",
"tonal.slot7.kind",
"tonal.slot7.amount",
"tonal.slot7.time",
"tonal.slot8.kind",
"tonal.slot8.amount",
"tonal.slot8.time",
"clap.slot1.kind",
"clap.slot1.amount",
"clap.slot1.time",
"clap.slot2.kind",
"clap.slot2.amount",
"clap.slot2.time",
"clap.slot3.kind",
"clap.slot3.amount",
"clap.slot3.time",
"clap.slot4.kind",
"clap.slot4.amount",
"clap.slot4.time",
"clap.slot5.kind",
"clap.slot5.amount",
"clap.slot5.time",
"clap.slot6.kind",
"clap.slot6.amount",
"clap.slot6.time",
"clap.slot7.kind",
"clap.slot7.amount",
"clap.slot7.time",
"clap.slot8.kind",
"clap.slot8.amount",
"clap.slot8.time",
"arp.slot1.kind",
"arp.slot1.amount",
"arp.slot1.time",
"arp.slot2.kind",
"arp.slot2.amount",
"arp.slot2.time",
"arp.slot3.kind",
"arp.slot3.amount",
"arp.slot3.time",
"arp.slot4.kind",
"arp.slot4.amount",
"arp.slot4.time",
"arp.slot5.kind",
"arp.slot5.amount",
"arp.slot5.time",
"arp.slot6.kind",
"arp.slot6.amount",
"arp.slot6.time",
"arp.slot7.kind",
"arp.slot7.amount",
"arp.slot7.time",
"arp.slot8.kind",
"arp.slot8.amount",
"arp.slot8.time",
"pad.slot1.right_time",
"pad.slot1.clock",
"pad.slot1.feedback",
"pad.slot2.right_time",
"pad.slot2.clock",
"pad.slot2.feedback",
"pad.slot3.right_time",
"pad.slot3.clock",
"pad.slot3.feedback",
"pad.slot4.right_time",
"pad.slot4.clock",
"pad.slot4.feedback",
"pad.slot5.right_time",
"pad.slot5.clock",
"pad.slot5.feedback",
"pad.slot6.right_time",
"pad.slot6.clock",
"pad.slot6.feedback",
"pad.slot7.right_time",
"pad.slot7.clock",
"pad.slot7.feedback",
"pad.slot8.right_time",
"pad.slot8.clock",
"pad.slot8.feedback",
"perc.slot1.right_time",
"perc.slot1.clock",
"perc.slot1.feedback",
"perc.slot2.right_time",
"perc.slot2.clock",
"perc.slot2.feedback",
"perc.slot3.right_time",
"perc.slot3.clock",
"perc.slot3.feedback",
"perc.slot4.right_time",
"perc.slot4.clock",
"perc.slot4.feedback",
"perc.slot5.right_time",
"perc.slot5.clock",
"perc.slot5.feedback",
"perc.slot6.right_time",
"perc.slot6.clock",
"perc.slot6.feedback",
"perc.slot7.right_time",
"perc.slot7.clock",
"perc.slot7.feedback",
"perc.slot8.right_time",
"perc.slot8.clock",
"perc.slot8.feedback",
"bass.slot1.right_time",
"bass.slot1.clock",
"bass.slot1.feedback",
"bass.slot2.right_time",
"bass.slot2.clock",
"bass.slot2.feedback",
"bass.slot3.right_time",
"bass.slot3.clock",
"bass.slot3.feedback",
"bass.slot4.right_time",
"bass.slot4.clock",
"bass.slot4.feedback",
"bass.slot5.right_time",
"bass.slot5.clock",
"bass.slot5.feedback",
"bass.slot6.right_time",
"bass.slot6.clock",
"bass.slot6.feedback",
"bass.slot7.right_time",
"bass.slot7.clock",
"bass.slot7.feedback",
"bass.slot8.right_time",
"bass.slot8.clock",
"bass.slot8.feedback",
"kick.slot1.right_time",
"kick.slot1.clock",
"kick.slot1.feedback",
"kick.slot2.right_time",
"kick.slot2.clock",
"kick.slot2.feedback",
"kick.slot3.right_time",
"kick.slot3.clock",
"kick.slot3.feedback",
"kick.slot4.right_time",
"kick.slot4.clock",
"kick.slot4.feedback",
"kick.slot5.right_time",
"kick.slot5.clock",
"kick.slot5.feedback",
"kick.slot6.right_time",
"kick.slot6.clock",
"kick.slot6.feedback",
"kick.slot7.right_time",
"kick.slot7.clock",
"kick.slot7.feedback",
"kick.slot8.right_time",
"kick.slot8.clock",
"kick.slot8.feedback",
"tonal.slot1.right_time",
"tonal.slot1.clock",
"tonal.slot1.feedback",
"tonal.slot2.right_time",
"tonal.slot2.clock",
"tonal.slot2.feedback",
"tonal.slot3.right_time",
"tonal.slot3.clock",
"tonal.slot3.feedback",
"tonal.slot4.right_time",
"tonal.slot4.clock",
"tonal.slot4.feedback",
"tonal.slot5.right_time",
"tonal.slot5.clock",
"tonal.slot5.feedback",
"tonal.slot6.right_time",
"tonal.slot6.clock",
"tonal.slot6.feedback",
"tonal.slot7.right_time",
"tonal.slot7.clock",
"tonal.slot7.feedback",
"tonal.slot8.right_time",
"tonal.slot8.clock",
"tonal.slot8.feedback",
"clap.slot1.right_time",
"clap.slot1.clock",
"clap.slot1.feedback",
"clap.slot2.right_time",
"clap.slot2.clock",
"clap.slot2.feedback",
"clap.slot3.right_time",
"clap.slot3.clock",
"clap.slot3.feedback",
"clap.slot4.right_time",
"clap.slot4.clock",
"clap.slot4.feedback",
"clap.slot5.right_time",
"clap.slot5.clock",
"clap.slot5.feedback",
"clap.slot6.right_time",
"clap.slot6.clock",
"clap.slot6.feedback",
"clap.slot7.right_time",
"clap.slot7.clock",
"clap.slot7.feedback",
"clap.slot8.right_time",
"clap.slot8.clock",
"clap.slot8.feedback",
"arp.slot1.right_time",
"arp.slot1.clock",
"arp.slot1.feedback",
"arp.slot2.right_time",
"arp.slot2.clock",
"arp.slot2.feedback",
"arp.slot3.right_time",
"arp.slot3.clock",
"arp.slot3.feedback",
"arp.slot4.right_time",
"arp.slot4.clock",
"arp.slot4.feedback",
"arp.slot5.right_time",
"arp.slot5.clock",
"arp.slot5.feedback",
"arp.slot6.right_time",
"arp.slot6.clock",
"arp.slot6.feedback",
"arp.slot7.right_time",
"arp.slot7.clock",
"arp.slot7.feedback",
"arp.slot8.right_time",
"arp.slot8.clock",
"arp.slot8.feedback",
"pad.slot1.vintage",
"pad.slot2.vintage",
"pad.slot3.vintage",
"pad.slot4.vintage",
"pad.slot5.vintage",
"pad.slot6.vintage",
"pad.slot7.vintage",
"pad.slot8.vintage",
"perc.slot1.vintage",
"perc.slot2.vintage",
"perc.slot3.vintage",
"perc.slot4.vintage",
"perc.slot5.vintage",
"perc.slot6.vintage",
"perc.slot7.vintage",
"perc.slot8.vintage",
"bass.slot1.vintage",
"bass.slot2.vintage",
"bass.slot3.vintage",
"bass.slot4.vintage",
"bass.slot5.vintage",
"bass.slot6.vintage",
"bass.slot7.vintage",
"bass.slot8.vintage",
"kick.slot1.vintage",
"kick.slot2.vintage",
"kick.slot3.vintage",
"kick.slot4.vintage",
"kick.slot5.vintage",
"kick.slot6.vintage",
"kick.slot7.vintage",
"kick.slot8.vintage",
"tonal.slot1.vintage",
"tonal.slot2.vintage",
"tonal.slot3.vintage",
"tonal.slot4.vintage",
"tonal.slot5.vintage",
"tonal.slot6.vintage",
"tonal.slot7.vintage",
"tonal.slot8.vintage",
"clap.slot1.vintage",
"clap.slot2.vintage",
"clap.slot3.vintage",
"clap.slot4.vintage",
"clap.slot5.vintage",
"clap.slot6.vintage",
"clap.slot7.vintage",
"clap.slot8.vintage",
"arp.slot1.vintage",
"arp.slot2.vintage",
"arp.slot3.vintage",
"arp.slot4.vintage",
"arp.slot5.vintage",
"arp.slot6.vintage",
"arp.slot7.vintage",
"arp.slot8.vintage",
"pad.slot1.right_clock",
"pad.slot2.right_clock",
"pad.slot3.right_clock",
"pad.slot4.right_clock",
"pad.slot5.right_clock",
"pad.slot6.right_clock",
"pad.slot7.right_clock",
"pad.slot8.right_clock",
"perc.slot1.right_clock",
"perc.slot2.right_clock",
"perc.slot3.right_clock",
"perc.slot4.right_clock",
"perc.slot5.right_clock",
"perc.slot6.right_clock",
"perc.slot7.right_clock",
"perc.slot8.right_clock",
"bass.slot1.right_clock",
"bass.slot2.right_clock",
"bass.slot3.right_clock",
"bass.slot4.right_clock",
"bass.slot5.right_clock",
"bass.slot6.right_clock",
"bass.slot7.right_clock",
"bass.slot8.right_clock",
"kick.slot1.right_clock",
"kick.slot2.right_clock",
"kick.slot3.right_clock",
"kick.slot4.right_clock",
"kick.slot5.right_clock",
"kick.slot6.right_clock",
"kick.slot7.right_clock",
"kick.slot8.right_clock",
"tonal.slot1.right_clock",
"tonal.slot2.right_clock",
"tonal.slot3.right_clock",
"tonal.slot4.right_clock",
"tonal.slot5.right_clock",
"tonal.slot6.right_clock",
"tonal.slot7.right_clock",
"tonal.slot8.right_clock",
"clap.slot1.right_clock",
"clap.slot2.right_clock",
"clap.slot3.right_clock",
"clap.slot4.right_clock",
"clap.slot5.right_clock",
"clap.slot6.right_clock",
"clap.slot7.right_clock",
"clap.slot8.right_clock",
"arp.slot1.right_clock",
"arp.slot2.right_clock",
"arp.slot3.right_clock",
"arp.slot4.right_clock",
"arp.slot5.right_clock",
"arp.slot6.right_clock",
"arp.slot7.right_clock",
"arp.slot8.right_clock",
"master.slot1.kind",
"master.slot1.amount",
"master.slot1.time",
"master.slot1.right_time",
"master.slot1.clock",
"master.slot1.feedback",
"master.slot1.vintage",
"master.slot1.right_clock",
"master.slot2.kind",
"master.slot2.amount",
"master.slot2.time",
"master.slot2.right_time",
"master.slot2.clock",
"master.slot2.feedback",
"master.slot2.vintage",
"master.slot2.right_clock",
"master.slot3.kind",
"master.slot3.amount",
"master.slot3.time",
"master.slot3.right_time",
"master.slot3.clock",
"master.slot3.feedback",
"master.slot3.vintage",
"master.slot3.right_clock",
"master.slot4.kind",
"master.slot4.amount",
"master.slot4.time",
"master.slot4.right_time",
"master.slot4.clock",
"master.slot4.feedback",
"master.slot4.vintage",
"master.slot4.right_clock",
"master.slot5.kind",
"master.slot5.amount",
"master.slot5.time",
"master.slot5.right_time",
"master.slot5.clock",
"master.slot5.feedback",
"master.slot5.vintage",
"master.slot5.right_clock",
"master.slot6.kind",
"master.slot6.amount",
"master.slot6.time",
"master.slot6.right_time",
"master.slot6.clock",
"master.slot6.feedback",
"master.slot6.vintage",
"master.slot6.right_clock",
"master.slot7.kind",
"master.slot7.amount",
"master.slot7.time",
"master.slot7.right_time",
"master.slot7.clock",
"master.slot7.feedback",
"master.slot7.vintage",
"master.slot7.right_clock",
"master.slot8.kind",
"master.slot8.amount",
"master.slot8.time",
"master.slot8.right_time",
"master.slot8.clock",
"master.slot8.feedback",
"master.slot8.vintage",
"master.slot8.right_clock",
"lead.level",
"lead.attack",
"lead.decay",
"lead.glide",
"lead.octave",
"lead.rate_beats",
"lead.offset_beats",
"lead.steps",
"lead.swing",
"lead.step1",
"lead.step2",
"lead.step3",
"lead.step4",
"lead.step5",
"lead.step6",
"lead.step7",
"lead.step8",
"lead.step9",
"lead.step10",
"lead.step11",
"lead.step12",
"lead.step13",
"lead.step14",
"lead.step15",
"lead.step16",
"lead.slot1.kind",
"lead.slot1.amount",
"lead.slot1.time",
"lead.slot1.right_time",
"lead.slot1.clock",
"lead.slot1.feedback",
"lead.slot1.vintage",
"lead.slot1.right_clock",
"lead.slot2.kind",
"lead.slot2.amount",
"lead.slot2.time",
"lead.slot2.right_time",
"lead.slot2.clock",
"lead.slot2.feedback",
"lead.slot2.vintage",
"lead.slot2.right_clock",
"lead.slot3.kind",
"lead.slot3.amount",
"lead.slot3.time",
"lead.slot3.right_time",
"lead.slot3.clock",
"lead.slot3.feedback",
"lead.slot3.vintage",
"lead.slot3.right_clock",
"lead.slot4.kind",
"lead.slot4.amount",
"lead.slot4.time",
"lead.slot4.right_time",
"lead.slot4.clock",
"lead.slot4.feedback",
"lead.slot4.vintage",
"lead.slot4.right_clock",
"lead.slot5.kind",
"lead.slot5.amount",
"lead.slot5.time",
"lead.slot5.right_time",
"lead.slot5.clock",
"lead.slot5.feedback",
"lead.slot5.vintage",
"lead.slot5.right_clock",
"lead.slot6.kind",
"lead.slot6.amount",
"lead.slot6.time",
"lead.slot6.right_time",
"lead.slot6.clock",
"lead.slot6.feedback",
"lead.slot6.vintage",
"lead.slot6.right_clock",
"lead.slot7.kind",
"lead.slot7.amount",
"lead.slot7.time",
"lead.slot7.right_time",
"lead.slot7.clock",
"lead.slot7.feedback",
"lead.slot7.vintage",
"lead.slot7.right_clock",
"lead.slot8.kind",
"lead.slot8.amount",
"lead.slot8.time",
"lead.slot8.right_time",
"lead.slot8.clock",
"lead.slot8.feedback",
"lead.slot8.vintage",
"lead.slot8.right_clock",
"lead.type",
"lead.follow",
"lead.pattern",
"pad.chord_offset",
"pad.trigger",
"pad.swing",
"pad.step1",
"pad.step2",
"pad.step3",
"pad.step4",
"pad.step5",
"pad.step6",
"pad.step7",
"pad.step8",
"pad.step9",
"pad.step10",
"pad.step11",
"pad.step12",
"pad.step13",
"pad.step14",
"pad.step15",
"pad.step16",
"pad.gate_beats",
"arp.midi_out",
"arp.midi_gate_beats",
"pad.midi_out",
"lead.midi_out",
"lead.midi_gate_beats",
"pad.chord_notes",
"pad.midi_in",
"arp.midi_in",
"lead.midi_in",
"pad.midi_trigger",
];
pub(crate) fn song_id_index(id: &str) -> Option<u16> {
SONG_ID_TABLE
.iter()
.position(|entry| *entry == id)
.map(|index| index as u16)
}
pub(crate) fn song_id_at(index: u16) -> Option<&'static str> {
SONG_ID_TABLE.get(index as usize).copied()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fluid::registry::all_specs;
use std::collections::BTreeSet;
#[test]
fn song_ids_cover_every_registry_control() {
let missing: Vec<_> = all_specs()
.map(|spec| spec.id)
.filter(|id| song_id_index(id).is_none())
.collect();
assert!(
missing.is_empty(),
"append these control ids to SONG_ID_TABLE so they can be saved: {missing:?}"
);
}
#[test]
fn song_id_table_has_no_duplicates() {
let mut seen = BTreeSet::new();
for id in SONG_ID_TABLE {
assert!(seen.insert(*id), "duplicate song id table entry: {id}");
}
}
#[test]
fn song_id_index_and_lookup_are_inverses() {
for (index, id) in SONG_ID_TABLE.iter().enumerate() {
let index = index as u16;
assert_eq!(song_id_index(id), Some(index));
assert_eq!(song_id_at(index), Some(*id));
}
assert_eq!(song_id_at(SONG_ID_TABLE.len() as u16), None);
}
}