Skip to main content

sim_lib_music_core/
registry.rs

1use std::collections::BTreeMap;
2
3use sim_kernel::{Error, Expr, Result, Symbol};
4
5use crate::{
6    MusicCapability, MusicComponentDescriptor, arpeggio_lab_player_descriptor,
7    automation_curve_modulator_descriptor, bassline_player_descriptor, beat_map_player_descriptor,
8    chord_sequencer_player_descriptor, default_instrument_descriptor,
9    drum_key_map_player_descriptor, dual_arpeggio_player_descriptor, envelope_modulator_descriptor,
10    euclid_player_descriptor, keyboard_performance_source_descriptor, lfo_modulator_descriptor,
11    note_echo_player_descriptor, oscillator_modulator_descriptor,
12    pattern_mutator_player_descriptor, polystep_player_descriptor, quad_note_player_descriptor,
13    random_walk_modulator_descriptor, scales_chords_player_descriptor, tempo_lfo_descriptor,
14};
15
16/// A single registered music component, wrapping its descriptor.
17///
18/// Each entry holds one [`MusicComponentDescriptor`] and exposes the lookup
19/// keys and capability checks the registry needs.
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct MusicComponentRegistryEntry {
22    descriptor: MusicComponentDescriptor,
23}
24
25impl MusicComponentRegistryEntry {
26    /// Wraps a descriptor in a registry entry.
27    pub fn new(descriptor: MusicComponentDescriptor) -> Self {
28        Self { descriptor }
29    }
30
31    /// Returns the component's unique identifier symbol.
32    pub fn id(&self) -> &Symbol {
33        &self.descriptor.id
34    }
35
36    /// Returns the wrapped component descriptor.
37    pub fn descriptor(&self) -> &MusicComponentDescriptor {
38        &self.descriptor
39    }
40
41    /// Reports whether the component declares the given capability.
42    pub fn has_capability(&self, capability: MusicCapability) -> bool {
43        self.descriptor.has_capability(capability)
44    }
45
46    /// Encodes the entry's descriptor as an [`Expr`].
47    pub fn to_expr(&self) -> Expr {
48        self.descriptor.to_expr()
49    }
50}
51
52/// An ordered collection of music components keyed by qualified id.
53///
54/// Entries are stored in a [`BTreeMap`] so iteration order is deterministic.
55#[derive(Clone, Debug, Default, PartialEq, Eq)]
56pub struct MusicComponentRegistry {
57    entries: BTreeMap<String, MusicComponentRegistryEntry>,
58}
59
60impl MusicComponentRegistry {
61    /// Creates an empty registry.
62    pub fn new() -> Self {
63        Self::default()
64    }
65
66    /// Registers an entry, failing if its id is already present.
67    pub fn register(&mut self, entry: MusicComponentRegistryEntry) -> Result<()> {
68        let key = entry.id().as_qualified_str();
69        if self.entries.contains_key(&key) {
70            return Err(Error::Eval(format!(
71                "duplicate music component registry id: {key}"
72            )));
73        }
74        self.entries.insert(key, entry);
75        Ok(())
76    }
77
78    /// Looks up an entry by its qualified id, if registered.
79    pub fn get(&self, id: &Symbol) -> Option<&MusicComponentRegistryEntry> {
80        self.entries.get(&id.as_qualified_str())
81    }
82
83    /// Iterates over all registered entries in id order.
84    pub fn entries(&self) -> impl Iterator<Item = &MusicComponentRegistryEntry> {
85        self.entries.values()
86    }
87
88    /// Collects every entry that declares the given capability.
89    pub fn by_capability(&self, capability: MusicCapability) -> Vec<&MusicComponentRegistryEntry> {
90        self.entries
91            .values()
92            .filter(|entry| entry.has_capability(capability))
93            .collect()
94    }
95
96    /// Looks up an entry and requires it to declare the given capability.
97    ///
98    /// Returns an error if the id is not registered or the component lacks the
99    /// capability.
100    pub fn require_capability(
101        &self,
102        id: &Symbol,
103        capability: MusicCapability,
104    ) -> Result<&MusicComponentRegistryEntry> {
105        let entry = self
106            .get(id)
107            .ok_or_else(|| Error::Eval(format!("music component not registered: {id}")))?;
108        if !entry.has_capability(capability) {
109            return Err(Error::Eval(format!(
110                "music component {id} missing capability {}",
111                capability.wire_label()
112            )));
113        }
114        Ok(entry)
115    }
116
117    /// Encodes the full registry as a tagged inventory [`Expr`] map.
118    pub fn inventory_expr(&self) -> Expr {
119        Expr::Map(vec![
120            (
121                field("tag"),
122                Expr::Symbol(Symbol::qualified("music", "component-registry")),
123            ),
124            (
125                field("entries"),
126                Expr::Vector(
127                    self.entries()
128                        .map(MusicComponentRegistryEntry::to_expr)
129                        .collect(),
130                ),
131            ),
132        ])
133    }
134}
135
136/// Builds a registry populated with every built-in music component descriptor.
137pub fn default_music_component_registry() -> MusicComponentRegistry {
138    let mut registry = MusicComponentRegistry::new();
139    for descriptor in [
140        scales_chords_player_descriptor(),
141        dual_arpeggio_player_descriptor(),
142        arpeggio_lab_player_descriptor(),
143        note_echo_player_descriptor(),
144        beat_map_player_descriptor(),
145        euclid_player_descriptor(),
146        drum_key_map_player_descriptor(),
147        chord_sequencer_player_descriptor(),
148        bassline_player_descriptor(),
149        polystep_player_descriptor(),
150        quad_note_player_descriptor(),
151        pattern_mutator_player_descriptor(),
152        default_instrument_descriptor(),
153        keyboard_performance_source_descriptor(),
154        tempo_lfo_descriptor(),
155        lfo_modulator_descriptor(),
156        envelope_modulator_descriptor(),
157        oscillator_modulator_descriptor(),
158        random_walk_modulator_descriptor(),
159        automation_curve_modulator_descriptor(),
160    ] {
161        registry
162            .register(MusicComponentRegistryEntry::new(
163                descriptor.expect("default music component descriptors are valid"),
164            ))
165            .expect("default music component registry ids are unique");
166    }
167    registry
168}
169
170/// Returns the registry id of the dual-arpeggio player.
171pub fn dual_arpeggio_player_id() -> Symbol {
172    Symbol::qualified("music/player-family", "dual-arpeggio")
173}
174
175/// Returns the registry id of the arpeggio-lab player.
176pub fn arpeggio_lab_player_id() -> Symbol {
177    Symbol::qualified("music/player-family", "arpeggio-lab")
178}
179
180/// Returns the registry id of the scales-chords player.
181pub fn scales_chords_player_id() -> Symbol {
182    Symbol::qualified("music/player-family", "scales-chords")
183}
184
185/// Returns the registry id of the note-echo player.
186pub fn note_echo_player_id() -> Symbol {
187    Symbol::qualified("music/player-family", "note-echo")
188}
189
190/// Returns the registry id of the beat-map player.
191pub fn beat_map_player_id() -> Symbol {
192    Symbol::qualified("music/player-family", "beat-map")
193}
194
195/// Returns the registry id of the euclid player.
196pub fn euclid_player_id() -> Symbol {
197    Symbol::qualified("music/player-family", "euclid")
198}
199
200/// Returns the registry id of the drum-key-map player.
201pub fn drum_key_map_player_id() -> Symbol {
202    Symbol::qualified("music/player-family", "drum-key-map")
203}
204
205/// Returns the registry id of the chord-sequencer player.
206pub fn chord_sequencer_player_id() -> Symbol {
207    Symbol::qualified("music/player-family", "chord-sequencer")
208}
209
210/// Returns the registry id of the bassline-generator player.
211pub fn bassline_player_id() -> Symbol {
212    Symbol::qualified("music/player-family", "bassline-generator")
213}
214
215/// Returns the registry id of the polystep player.
216pub fn polystep_player_id() -> Symbol {
217    Symbol::qualified("music/player-family", "polystep")
218}
219
220/// Returns the registry id of the quad-note-generator player.
221pub fn quad_note_player_id() -> Symbol {
222    Symbol::qualified("music/player-family", "quad-note-generator")
223}
224
225/// Returns the registry id of the pattern-mutator player.
226pub fn pattern_mutator_player_id() -> Symbol {
227    Symbol::qualified("music/player-family", "pattern-mutator")
228}
229
230/// Returns the registry id of the LFO modulator.
231pub fn lfo_modulator_id() -> Symbol {
232    Symbol::qualified("music/modulator", "lfo")
233}
234
235/// Returns the registry id of the envelope modulator.
236pub fn envelope_modulator_id() -> Symbol {
237    Symbol::qualified("music/modulator", "envelope")
238}
239
240/// Returns the registry id of the oscillator modulator.
241pub fn oscillator_modulator_id() -> Symbol {
242    Symbol::qualified("music/modulator", "oscillator")
243}
244
245/// Returns the registry id of the random-walk modulator.
246pub fn random_walk_modulator_id() -> Symbol {
247    Symbol::qualified("music/modulator", "random-walk")
248}
249
250/// Returns the registry id of the automation-curve modulator.
251pub fn automation_curve_modulator_id() -> Symbol {
252    Symbol::qualified("music/modulator", "automation-curve")
253}
254
255fn field(name: &'static str) -> Expr {
256    sim_value::build::qsym("music/component-registry", name)
257}