use std::collections::BTreeMap;
use sim_kernel::{Error, Expr, Result, Symbol};
use crate::{
MusicCapability, MusicComponentDescriptor, arpeggio_lab_player_descriptor,
automation_curve_modulator_descriptor, bassline_player_descriptor, beat_map_player_descriptor,
chord_sequencer_player_descriptor, default_instrument_descriptor,
drum_key_map_player_descriptor, dual_arpeggio_player_descriptor, envelope_modulator_descriptor,
euclid_player_descriptor, keyboard_performance_source_descriptor, lfo_modulator_descriptor,
note_echo_player_descriptor, oscillator_modulator_descriptor,
pattern_mutator_player_descriptor, polystep_player_descriptor, quad_note_player_descriptor,
random_walk_modulator_descriptor, scales_chords_player_descriptor, tempo_lfo_descriptor,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MusicComponentRegistryEntry {
descriptor: MusicComponentDescriptor,
}
impl MusicComponentRegistryEntry {
pub fn new(descriptor: MusicComponentDescriptor) -> Self {
Self { descriptor }
}
pub fn id(&self) -> &Symbol {
&self.descriptor.id
}
pub fn descriptor(&self) -> &MusicComponentDescriptor {
&self.descriptor
}
pub fn has_capability(&self, capability: MusicCapability) -> bool {
self.descriptor.has_capability(capability)
}
pub fn to_expr(&self) -> Expr {
self.descriptor.to_expr()
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct MusicComponentRegistry {
entries: BTreeMap<String, MusicComponentRegistryEntry>,
}
impl MusicComponentRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, entry: MusicComponentRegistryEntry) -> Result<()> {
let key = entry.id().as_qualified_str();
if self.entries.contains_key(&key) {
return Err(Error::Eval(format!(
"duplicate music component registry id: {key}"
)));
}
self.entries.insert(key, entry);
Ok(())
}
pub fn get(&self, id: &Symbol) -> Option<&MusicComponentRegistryEntry> {
self.entries.get(&id.as_qualified_str())
}
pub fn entries(&self) -> impl Iterator<Item = &MusicComponentRegistryEntry> {
self.entries.values()
}
pub fn by_capability(&self, capability: MusicCapability) -> Vec<&MusicComponentRegistryEntry> {
self.entries
.values()
.filter(|entry| entry.has_capability(capability))
.collect()
}
pub fn require_capability(
&self,
id: &Symbol,
capability: MusicCapability,
) -> Result<&MusicComponentRegistryEntry> {
let entry = self
.get(id)
.ok_or_else(|| Error::Eval(format!("music component not registered: {id}")))?;
if !entry.has_capability(capability) {
return Err(Error::Eval(format!(
"music component {id} missing capability {}",
capability.wire_label()
)));
}
Ok(entry)
}
pub fn inventory_expr(&self) -> Expr {
Expr::Map(vec![
(
field("tag"),
Expr::Symbol(Symbol::qualified("music", "component-registry")),
),
(
field("entries"),
Expr::Vector(
self.entries()
.map(MusicComponentRegistryEntry::to_expr)
.collect(),
),
),
])
}
}
pub fn default_music_component_registry() -> MusicComponentRegistry {
let mut registry = MusicComponentRegistry::new();
for descriptor in [
scales_chords_player_descriptor(),
dual_arpeggio_player_descriptor(),
arpeggio_lab_player_descriptor(),
note_echo_player_descriptor(),
beat_map_player_descriptor(),
euclid_player_descriptor(),
drum_key_map_player_descriptor(),
chord_sequencer_player_descriptor(),
bassline_player_descriptor(),
polystep_player_descriptor(),
quad_note_player_descriptor(),
pattern_mutator_player_descriptor(),
default_instrument_descriptor(),
keyboard_performance_source_descriptor(),
tempo_lfo_descriptor(),
lfo_modulator_descriptor(),
envelope_modulator_descriptor(),
oscillator_modulator_descriptor(),
random_walk_modulator_descriptor(),
automation_curve_modulator_descriptor(),
] {
registry
.register(MusicComponentRegistryEntry::new(
descriptor.expect("default music component descriptors are valid"),
))
.expect("default music component registry ids are unique");
}
registry
}
pub fn dual_arpeggio_player_id() -> Symbol {
Symbol::qualified("music/player-family", "dual-arpeggio")
}
pub fn arpeggio_lab_player_id() -> Symbol {
Symbol::qualified("music/player-family", "arpeggio-lab")
}
pub fn scales_chords_player_id() -> Symbol {
Symbol::qualified("music/player-family", "scales-chords")
}
pub fn note_echo_player_id() -> Symbol {
Symbol::qualified("music/player-family", "note-echo")
}
pub fn beat_map_player_id() -> Symbol {
Symbol::qualified("music/player-family", "beat-map")
}
pub fn euclid_player_id() -> Symbol {
Symbol::qualified("music/player-family", "euclid")
}
pub fn drum_key_map_player_id() -> Symbol {
Symbol::qualified("music/player-family", "drum-key-map")
}
pub fn chord_sequencer_player_id() -> Symbol {
Symbol::qualified("music/player-family", "chord-sequencer")
}
pub fn bassline_player_id() -> Symbol {
Symbol::qualified("music/player-family", "bassline-generator")
}
pub fn polystep_player_id() -> Symbol {
Symbol::qualified("music/player-family", "polystep")
}
pub fn quad_note_player_id() -> Symbol {
Symbol::qualified("music/player-family", "quad-note-generator")
}
pub fn pattern_mutator_player_id() -> Symbol {
Symbol::qualified("music/player-family", "pattern-mutator")
}
pub fn lfo_modulator_id() -> Symbol {
Symbol::qualified("music/modulator", "lfo")
}
pub fn envelope_modulator_id() -> Symbol {
Symbol::qualified("music/modulator", "envelope")
}
pub fn oscillator_modulator_id() -> Symbol {
Symbol::qualified("music/modulator", "oscillator")
}
pub fn random_walk_modulator_id() -> Symbol {
Symbol::qualified("music/modulator", "random-walk")
}
pub fn automation_curve_modulator_id() -> Symbol {
Symbol::qualified("music/modulator", "automation-curve")
}
fn field(name: &'static str) -> Expr {
sim_value::build::qsym("music/component-registry", name)
}