sim_lib_stream_host/
catalog.rs1use sim_kernel::{
4 Cx, Error, Result, Symbol,
5 library::{ExportKind, ExportRecord, ExportState, Registry},
6};
7
8use crate::audio_provider::ModeledAudioProvider;
9use crate::eval_site::{DeviceProvider, StreamEvalSite};
10use crate::midi_live_eval_site::MidiLiveEvalSite;
11use crate::midi_provider::ModeledMidiProvider;
12use crate::placement::{DeviceDirection, DeviceKind, DeviceRecord};
13use crate::{AudioRouter, Placement};
14
15pub struct DeviceCatalog {
17 providers: Vec<Box<dyn DeviceProvider>>,
18}
19
20impl Default for DeviceCatalog {
21 fn default() -> Self {
22 Self::default_modeled()
23 }
24}
25
26impl DeviceCatalog {
27 pub fn new() -> Self {
29 Self {
30 providers: Vec::new(),
31 }
32 }
33
34 pub fn default_modeled() -> Self {
36 let mut catalog = Self::new();
37 catalog.register(Box::new(ModeledMidiProvider::default()));
38 catalog.register(Box::new(ModeledAudioProvider::default()));
39 catalog
40 }
41
42 pub fn with_registry_audio_devices(registry: &Registry) -> Self {
44 let mut catalog = Self::default_modeled();
45 catalog.register_registry_audio_devices(registry);
46 catalog
47 }
48
49 #[cfg(any(feature = "rtmidi-hardware", feature = "ble-midi-hardware"))]
51 pub fn with_native_midi(provider: Box<dyn DeviceProvider>) -> Self {
52 let mut catalog = Self::default_modeled();
53 catalog.register(provider);
54 catalog
55 }
56
57 #[cfg(feature = "rtmidi-hardware")]
59 pub fn with_alsa_midi(provider: Box<dyn DeviceProvider>) -> Self {
60 Self::with_native_midi(provider)
61 }
62
63 #[cfg(feature = "ble-midi-hardware")]
65 pub fn with_ble_midi(provider: Box<dyn DeviceProvider>) -> Self {
66 Self::with_native_midi(provider)
67 }
68
69 pub fn register(&mut self, provider: Box<dyn DeviceProvider>) {
71 self.providers.push(provider);
72 }
73
74 pub fn register_provider_sites(&mut self, router: &AudioRouter) {
76 let provider = ProviderAudioSites::from_router(router);
77 if !provider.is_empty() {
78 self.register(Box::new(provider));
79 }
80 }
81
82 pub fn register_registry_audio_devices(&mut self, registry: &Registry) {
84 let provider = RegistryAudioDevices::from_registry(registry);
85 if !provider.is_empty() {
86 self.register(Box::new(provider));
87 }
88 }
89
90 pub fn enumerate(&self) -> Result<Vec<DeviceRecord>> {
92 let mut records = Vec::new();
93 for provider in &self.providers {
94 records.extend(provider.enumerate()?);
95 }
96 Ok(records)
97 }
98
99 pub fn open_checked(&self, cx: &mut Cx, id: &Symbol) -> Result<Box<dyn StreamEvalSite>> {
102 for record in self.enumerate()? {
103 if &record.id == id {
104 record.open_plan().enforce(cx)?;
105 return self.open(id);
106 }
107 }
108 Err(Error::Eval(format!("DeviceCatalog: no device '{id}'")))
109 }
110
111 pub fn open(&self, id: &Symbol) -> Result<Box<dyn StreamEvalSite>> {
117 for provider in &self.providers {
118 let records = provider.enumerate()?;
119 if records.iter().any(|record| &record.id == id) {
120 return provider.open(id);
121 }
122 }
123 Err(Error::Eval(format!("DeviceCatalog: no device '{id}'")))
124 }
125
126 pub fn open_live_checked(&self, cx: &mut Cx, id: &Symbol) -> Result<MidiLiveEvalSite> {
129 MidiLiveEvalSite::from_eval_site(id, self.open_checked(cx, id)?)
130 }
131
132 pub fn open_live(&self, id: &Symbol) -> Result<MidiLiveEvalSite> {
138 MidiLiveEvalSite::from_eval_site(id, self.open(id)?)
139 }
140
141 pub fn enumerate_midi(&self) -> Result<Vec<DeviceRecord>> {
143 self.enumerate_kind(DeviceKind::Midi)
144 }
145
146 pub fn enumerate_audio(&self) -> Result<Vec<DeviceRecord>> {
148 self.enumerate_kind(DeviceKind::Audio)
149 }
150
151 pub fn audio_backend_names(&self) -> Result<Vec<String>> {
156 self.backend_names(DeviceKind::Audio, true)
157 }
158
159 pub fn midi_backend_names(&self) -> Result<Vec<String>> {
164 self.backend_names(DeviceKind::Midi, true)
165 }
166
167 pub fn audio_hardware_backend_names(&self) -> Result<Vec<String>> {
169 self.backend_names(DeviceKind::Audio, false)
170 }
171
172 pub fn midi_hardware_backend_names(&self) -> Result<Vec<String>> {
174 self.backend_names(DeviceKind::Midi, false)
175 }
176
177 fn enumerate_kind(&self, kind: DeviceKind) -> Result<Vec<DeviceRecord>> {
178 Ok(self
179 .enumerate()?
180 .into_iter()
181 .filter(|record| record.kind == kind)
182 .collect())
183 }
184
185 fn backend_names(
186 &self,
187 kind: DeviceKind,
188 include_modeled_fallback: bool,
189 ) -> Result<Vec<String>> {
190 let mut names = Vec::new();
191 let mut has_modeled = false;
192 for record in self
193 .enumerate()?
194 .into_iter()
195 .filter(|record| record.kind == kind)
196 {
197 match record.placement {
198 Placement::Modeled => has_modeled = true,
199 Placement::Hardware { transport } => push_unique(&mut names, transport.name),
200 }
201 }
202 if include_modeled_fallback && (has_modeled || names.is_empty()) {
203 push_unique(&mut names, "modeled");
204 }
205 Ok(names)
206 }
207}
208
209fn push_unique(names: &mut Vec<String>, name: impl ToString) {
210 let name = name.to_string();
211 if !names.iter().any(|existing| existing == &name) {
212 names.push(name);
213 }
214}
215
216pub fn audio_site_export_symbol(name: &str) -> Symbol {
218 Symbol::qualified("audio/site", name)
219}
220
221pub fn audio_device_export_symbol(name: &str) -> Symbol {
225 Symbol::qualified("audio/device", name)
226}
227
228struct ProviderAudioSites {
229 records: Vec<DeviceRecord>,
230}
231
232impl ProviderAudioSites {
233 fn from_router(router: &AudioRouter) -> Self {
234 let mut records = router
235 .registered_sites()
236 .map(|registered| {
237 let card = registered.site.card();
238 let placement = if card.hardware_required {
239 Placement::Hardware {
240 transport: registered.owner.clone(),
241 }
242 } else {
243 Placement::Modeled
244 };
245 DeviceRecord::audio_from_card(card, placement)
246 })
247 .collect::<Vec<_>>();
248 records.sort_by_key(|record| record.id.to_string());
249 Self { records }
250 }
251
252 fn is_empty(&self) -> bool {
253 self.records.is_empty()
254 }
255}
256
257impl DeviceProvider for ProviderAudioSites {
258 fn enumerate(&self) -> Result<Vec<DeviceRecord>> {
259 Ok(self.records.clone())
260 }
261
262 fn open(&self, id: &Symbol) -> Result<Box<dyn StreamEvalSite>> {
263 let record = self
264 .records
265 .iter()
266 .find(|record| &record.id == id)
267 .ok_or_else(|| Error::Eval(format!("ProviderAudioSites: unknown id '{id}'")))?
268 .clone();
269 Ok(Box::new(ProviderAudioSite { record }))
270 }
271}
272
273struct RegistryAudioDevices {
274 records: Vec<DeviceRecord>,
275}
276
277impl RegistryAudioDevices {
278 fn from_registry(registry: &Registry) -> Self {
279 let mut records = registry
280 .libs()
281 .iter()
282 .flat_map(|lib| lib.exports.iter())
283 .filter_map(registry_audio_site_record)
284 .collect::<Vec<_>>();
285 records.sort_by_key(|record| record.id.to_string());
286 records.dedup_by(|left, right| left.id == right.id);
287 Self { records }
288 }
289
290 fn is_empty(&self) -> bool {
291 self.records.is_empty()
292 }
293}
294
295impl DeviceProvider for RegistryAudioDevices {
296 fn enumerate(&self) -> Result<Vec<DeviceRecord>> {
297 Ok(self.records.clone())
298 }
299
300 fn open(&self, id: &Symbol) -> Result<Box<dyn StreamEvalSite>> {
301 let record = self
302 .records
303 .iter()
304 .find(|record| &record.id == id)
305 .ok_or_else(|| Error::Eval(format!("RegistryAudioDevices: unknown id '{id}'")))?
306 .clone();
307 Ok(Box::new(ProviderAudioSite { record }))
308 }
309}
310
311fn registry_audio_site_record(record: &ExportRecord) -> Option<DeviceRecord> {
312 if record.kind != ExportKind::named(ExportKind::SITE) {
313 return None;
314 }
315 if record.symbol.namespace.as_deref() != Some("audio/site") {
316 return None;
317 }
318 if matches!(record.state, ExportState::Invalid { .. }) {
319 return None;
320 }
321 Some(DeviceRecord {
322 id: record.symbol.clone(),
323 display_name: format!("{} audio site", record.symbol.name),
324 kind: DeviceKind::Audio,
325 direction: DeviceDirection::Duplex,
326 placement: Placement::Hardware {
327 transport: record.symbol.clone(),
328 },
329 })
330}
331
332struct ProviderAudioSite {
333 record: DeviceRecord,
334}
335
336impl StreamEvalSite for ProviderAudioSite {
337 fn placement(&self) -> &Placement {
338 &self.record.placement
339 }
340
341 fn device_record(&self) -> &DeviceRecord {
342 &self.record
343 }
344
345 fn close(self: Box<Self>) -> Result<()> {
346 Ok(())
347 }
348}