sim_lib_stream_host/
catalog.rs1use sim_kernel::{
4 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(&self, id: &Symbol) -> Result<Box<dyn StreamEvalSite>> {
101 for provider in &self.providers {
102 let records = provider.enumerate()?;
103 if records.iter().any(|record| &record.id == id) {
104 return provider.open(id);
105 }
106 }
107 Err(Error::Eval(format!("DeviceCatalog: no device '{id}'")))
108 }
109
110 pub fn open_live(&self, id: &Symbol) -> Result<MidiLiveEvalSite> {
112 MidiLiveEvalSite::from_eval_site(id, self.open(id)?)
113 }
114
115 pub fn enumerate_midi(&self) -> Result<Vec<DeviceRecord>> {
117 self.enumerate_kind(DeviceKind::Midi)
118 }
119
120 pub fn enumerate_audio(&self) -> Result<Vec<DeviceRecord>> {
122 self.enumerate_kind(DeviceKind::Audio)
123 }
124
125 pub fn audio_backend_names(&self) -> Result<Vec<String>> {
130 self.backend_names(DeviceKind::Audio)
131 }
132
133 pub fn midi_backend_names(&self) -> Result<Vec<String>> {
138 self.backend_names(DeviceKind::Midi)
139 }
140
141 fn enumerate_kind(&self, kind: DeviceKind) -> Result<Vec<DeviceRecord>> {
142 Ok(self
143 .enumerate()?
144 .into_iter()
145 .filter(|record| record.kind == kind)
146 .collect())
147 }
148
149 fn backend_names(&self, kind: DeviceKind) -> Result<Vec<String>> {
150 let mut names = Vec::new();
151 let mut has_modeled = false;
152 for record in self
153 .enumerate()?
154 .into_iter()
155 .filter(|record| record.kind == kind)
156 {
157 match record.placement {
158 Placement::Modeled => has_modeled = true,
159 Placement::Hardware { transport } => push_unique(&mut names, transport.name),
160 }
161 }
162 if has_modeled || names.is_empty() {
163 push_unique(&mut names, "modeled");
164 }
165 Ok(names)
166 }
167}
168
169fn push_unique(names: &mut Vec<String>, name: impl ToString) {
170 let name = name.to_string();
171 if !names.iter().any(|existing| existing == &name) {
172 names.push(name);
173 }
174}
175
176pub fn audio_device_export_symbol(name: &str) -> Symbol {
178 Symbol::qualified("audio/device", name)
179}
180
181struct ProviderAudioSites {
182 records: Vec<DeviceRecord>,
183}
184
185impl ProviderAudioSites {
186 fn from_router(router: &AudioRouter) -> Self {
187 let mut records = router
188 .site_keys()
189 .filter_map(|key| router.site(key))
190 .map(|site| DeviceRecord::modeled_audio_from_card(site.card()))
191 .collect::<Vec<_>>();
192 records.sort_by_key(|record| record.id.to_string());
193 Self { records }
194 }
195
196 fn is_empty(&self) -> bool {
197 self.records.is_empty()
198 }
199}
200
201impl DeviceProvider for ProviderAudioSites {
202 fn enumerate(&self) -> Result<Vec<DeviceRecord>> {
203 Ok(self.records.clone())
204 }
205
206 fn open(&self, id: &Symbol) -> Result<Box<dyn StreamEvalSite>> {
207 let record = self
208 .records
209 .iter()
210 .find(|record| &record.id == id)
211 .ok_or_else(|| Error::Eval(format!("ProviderAudioSites: unknown id '{id}'")))?
212 .clone();
213 Ok(Box::new(ProviderAudioSite { record }))
214 }
215}
216
217struct RegistryAudioDevices {
218 records: Vec<DeviceRecord>,
219}
220
221impl RegistryAudioDevices {
222 fn from_registry(registry: &Registry) -> Self {
223 let mut records = registry
224 .libs()
225 .iter()
226 .flat_map(|lib| lib.exports.iter())
227 .filter_map(registry_audio_device_record)
228 .collect::<Vec<_>>();
229 records.sort_by_key(|record| record.id.to_string());
230 records.dedup_by(|left, right| left.id == right.id);
231 Self { records }
232 }
233
234 fn is_empty(&self) -> bool {
235 self.records.is_empty()
236 }
237}
238
239impl DeviceProvider for RegistryAudioDevices {
240 fn enumerate(&self) -> Result<Vec<DeviceRecord>> {
241 Ok(self.records.clone())
242 }
243
244 fn open(&self, id: &Symbol) -> Result<Box<dyn StreamEvalSite>> {
245 let record = self
246 .records
247 .iter()
248 .find(|record| &record.id == id)
249 .ok_or_else(|| Error::Eval(format!("RegistryAudioDevices: unknown id '{id}'")))?
250 .clone();
251 Ok(Box::new(ProviderAudioSite { record }))
252 }
253}
254
255fn registry_audio_device_record(record: &ExportRecord) -> Option<DeviceRecord> {
256 if record.kind != ExportKind::named(ExportKind::VALUE) {
257 return None;
258 }
259 if record.symbol.namespace.as_deref() != Some("audio/device") {
260 return None;
261 }
262 if matches!(record.state, ExportState::Invalid { .. }) {
263 return None;
264 }
265 Some(DeviceRecord {
266 id: record.symbol.clone(),
267 display_name: format!("{} audio device", record.symbol.name),
268 kind: DeviceKind::Audio,
269 direction: DeviceDirection::Duplex,
270 placement: Placement::Modeled,
271 })
272}
273
274struct ProviderAudioSite {
275 record: DeviceRecord,
276}
277
278impl StreamEvalSite for ProviderAudioSite {
279 fn placement(&self) -> &Placement {
280 &self.record.placement
281 }
282
283 fn device_record(&self) -> &DeviceRecord {
284 &self.record
285 }
286
287 fn close(self: Box<Self>) -> Result<()> {
288 Ok(())
289 }
290}