Skip to main content

sim_lib_midi_live/
runtime.rs

1use sim_kernel::{
2    AbiVersion, Cx, Export, ExportKind, ExportRecord, ExportState, Lib, LibManifest, LibTarget,
3    Linker, Result, RuntimeId, Symbol, Version,
4};
5
6const MIDI_LIVE_LIB_ID: &str = "midi-live";
7const SOURCE_EXPORT_KIND: &str = "MidiSourceFactory";
8const SINK_EXPORT_KIND: &str = "MidiSinkFactory";
9const TRACKED_SOURCE_EXPORT_KIND: &str = "TrackedMidiSourceFactory";
10const REGISTRY_SYMBOL_NAME: &str = "MidiLiveRegistry";
11
12/// Host-registered lib exposing the ring-buffer source/sink cards and their
13/// registry to a running runtime.
14pub struct MidiLiveLib;
15
16impl Lib for MidiLiveLib {
17    fn manifest(&self) -> LibManifest {
18        LibManifest {
19            id: Symbol::new(MIDI_LIVE_LIB_ID),
20            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
21            abi: AbiVersion { major: 0, minor: 1 },
22            target: LibTarget::HostRegistered,
23            requires: Vec::new(),
24            capabilities: Vec::new(),
25            exports: live_symbols()
26                .into_iter()
27                .chain(std::iter::once(registry_symbol()))
28                .map(|symbol| Export::Value { symbol })
29                .collect(),
30        }
31    }
32
33    fn load(&self, cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
34        for symbol in live_symbols() {
35            linker.value(symbol.clone(), live_value(cx, symbol.clone())?)?;
36        }
37        linker.value(registry_symbol(), registry_value(cx)?)?;
38        Ok(())
39    }
40}
41
42/// Installs [`MidiLiveLib`] into `cx` once and registers the ring-buffer
43/// source, sink, and tracked-source export records.
44pub fn install_midi_live_lib(cx: &mut Cx) -> Result<()> {
45    let lib = Symbol::new(MIDI_LIVE_LIB_ID);
46    if !sim_lib_core::install_once(cx, &MidiLiveLib)? {
47        return Ok(());
48    }
49    for symbol in source_symbols() {
50        cx.registry_mut().append_export_record(
51            &lib,
52            ExportRecord {
53                kind: ExportKind::named(SOURCE_EXPORT_KIND),
54                symbol,
55                state: ExportState::Resolved {
56                    id: RuntimeId::Value,
57                },
58            },
59        )?;
60    }
61    for symbol in sink_symbols() {
62        cx.registry_mut().append_export_record(
63            &lib,
64            ExportRecord {
65                kind: ExportKind::named(SINK_EXPORT_KIND),
66                symbol,
67                state: ExportState::Resolved {
68                    id: RuntimeId::Value,
69                },
70            },
71        )?;
72    }
73    for symbol in tracked_source_symbols() {
74        cx.registry_mut().append_export_record(
75            &lib,
76            ExportRecord {
77                kind: ExportKind::named(TRACKED_SOURCE_EXPORT_KIND),
78                symbol,
79                state: ExportState::Resolved {
80                    id: RuntimeId::Value,
81                },
82            },
83        )?;
84    }
85    Ok(())
86}
87
88fn source_symbols() -> Vec<Symbol> {
89    vec![Symbol::qualified("midi", "RingMidiBuffer")]
90}
91
92fn sink_symbols() -> Vec<Symbol> {
93    vec![Symbol::qualified("midi", "RingMidiBuffer")]
94}
95
96fn tracked_source_symbols() -> Vec<Symbol> {
97    vec![Symbol::qualified("midi", "RingTrackedMidiBuffer")]
98}
99
100fn live_symbols() -> Vec<Symbol> {
101    vec![
102        Symbol::qualified("midi", "RingMidiBuffer"),
103        Symbol::qualified("midi", "RingTrackedMidiBuffer"),
104    ]
105}
106
107fn registry_symbol() -> Symbol {
108    Symbol::qualified("midi", REGISTRY_SYMBOL_NAME)
109}
110
111fn registry_value(cx: &mut sim_kernel::LoadCx) -> Result<sim_kernel::Value> {
112    let buffers = cx.factory().list(
113        live_symbols()
114            .into_iter()
115            .map(|symbol| cx.factory().symbol(symbol))
116            .collect::<Result<Vec<_>>>()?,
117    )?;
118    cx.factory().table(vec![
119        (
120            Symbol::new("symbol"),
121            cx.factory().symbol(registry_symbol())?,
122        ),
123        (
124            Symbol::new("layer"),
125            cx.factory().string("midi".to_owned())?,
126        ),
127        (
128            Symbol::new("kind"),
129            cx.factory().string("plugin".to_owned())?,
130        ),
131        (
132            Symbol::new("shape"),
133            cx.factory()
134                .symbol(Symbol::qualified("midi", "TrackedMidiSourceFactory"))?,
135        ),
136        (Symbol::new("dependencies"), cx.factory().list(Vec::new())?),
137        (Symbol::new("lossless"), cx.factory().bool(true)?),
138        (Symbol::new("capabilities"), cx.factory().list(Vec::new())?),
139        (Symbol::new("buffers"), buffers),
140    ])
141}
142
143fn live_value(cx: &mut sim_kernel::LoadCx, symbol: Symbol) -> Result<sim_kernel::Value> {
144    let (shape, role) = match symbol.name.as_ref() {
145        "RingMidiBuffer" => (
146            Symbol::qualified("midi", "MidiSinkFactory"),
147            "ring buffer source/sink",
148        ),
149        "RingTrackedMidiBuffer" => (
150            Symbol::qualified("midi", "TrackedMidiSourceFactory"),
151            "ring buffer tracked source",
152        ),
153        _ => (Symbol::qualified("midi", "MidiSourceFactory"), "unknown"),
154    };
155    cx.factory().table(vec![
156        (Symbol::new("symbol"), cx.factory().symbol(symbol)?),
157        (
158            Symbol::new("layer"),
159            cx.factory().string("midi".to_owned())?,
160        ),
161        (
162            Symbol::new("kind"),
163            cx.factory().string("plugin".to_owned())?,
164        ),
165        (Symbol::new("shape"), cx.factory().symbol(shape)?),
166        (
167            Symbol::new("dependencies"),
168            cx.factory().list(vec![
169                cx.factory().string("midi-core".to_owned())?,
170                cx.factory().string("midi-live".to_owned())?,
171            ])?,
172        ),
173        (Symbol::new("lossless"), cx.factory().bool(true)?),
174        (Symbol::new("capabilities"), cx.factory().list(Vec::new())?),
175        (Symbol::new("role"), cx.factory().string(role.to_owned())?),
176    ])
177}