use sim_kernel::{
Error, Result, Symbol,
library::{ExportKind, ExportRecord, ExportState, Registry},
};
use crate::audio_provider::ModeledAudioProvider;
use crate::eval_site::{DeviceProvider, StreamEvalSite};
use crate::midi_live_eval_site::MidiLiveEvalSite;
use crate::midi_provider::ModeledMidiProvider;
use crate::placement::{DeviceDirection, DeviceKind, DeviceRecord};
use crate::{AudioRouter, Placement};
pub struct DeviceCatalog {
providers: Vec<Box<dyn DeviceProvider>>,
}
impl Default for DeviceCatalog {
fn default() -> Self {
Self::default_modeled()
}
}
impl DeviceCatalog {
pub fn new() -> Self {
Self {
providers: Vec::new(),
}
}
pub fn default_modeled() -> Self {
let mut catalog = Self::new();
catalog.register(Box::new(ModeledMidiProvider::default()));
catalog.register(Box::new(ModeledAudioProvider::default()));
catalog
}
pub fn with_registry_audio_devices(registry: &Registry) -> Self {
let mut catalog = Self::default_modeled();
catalog.register_registry_audio_devices(registry);
catalog
}
#[cfg(any(feature = "rtmidi-hardware", feature = "ble-midi-hardware"))]
pub fn with_native_midi(provider: Box<dyn DeviceProvider>) -> Self {
let mut catalog = Self::default_modeled();
catalog.register(provider);
catalog
}
#[cfg(feature = "rtmidi-hardware")]
pub fn with_alsa_midi(provider: Box<dyn DeviceProvider>) -> Self {
Self::with_native_midi(provider)
}
#[cfg(feature = "ble-midi-hardware")]
pub fn with_ble_midi(provider: Box<dyn DeviceProvider>) -> Self {
Self::with_native_midi(provider)
}
pub fn register(&mut self, provider: Box<dyn DeviceProvider>) {
self.providers.push(provider);
}
pub fn register_provider_sites(&mut self, router: &AudioRouter) {
let provider = ProviderAudioSites::from_router(router);
if !provider.is_empty() {
self.register(Box::new(provider));
}
}
pub fn register_registry_audio_devices(&mut self, registry: &Registry) {
let provider = RegistryAudioDevices::from_registry(registry);
if !provider.is_empty() {
self.register(Box::new(provider));
}
}
pub fn enumerate(&self) -> Result<Vec<DeviceRecord>> {
let mut records = Vec::new();
for provider in &self.providers {
records.extend(provider.enumerate()?);
}
Ok(records)
}
pub fn open(&self, id: &Symbol) -> Result<Box<dyn StreamEvalSite>> {
for provider in &self.providers {
let records = provider.enumerate()?;
if records.iter().any(|record| &record.id == id) {
return provider.open(id);
}
}
Err(Error::Eval(format!("DeviceCatalog: no device '{id}'")))
}
pub fn open_live(&self, id: &Symbol) -> Result<MidiLiveEvalSite> {
MidiLiveEvalSite::from_eval_site(id, self.open(id)?)
}
pub fn enumerate_midi(&self) -> Result<Vec<DeviceRecord>> {
self.enumerate_kind(DeviceKind::Midi)
}
pub fn enumerate_audio(&self) -> Result<Vec<DeviceRecord>> {
self.enumerate_kind(DeviceKind::Audio)
}
fn enumerate_kind(&self, kind: DeviceKind) -> Result<Vec<DeviceRecord>> {
Ok(self
.enumerate()?
.into_iter()
.filter(|record| record.kind == kind)
.collect())
}
}
pub fn audio_device_export_symbol(name: &str) -> Symbol {
Symbol::qualified("audio/device", name)
}
struct ProviderAudioSites {
records: Vec<DeviceRecord>,
}
impl ProviderAudioSites {
fn from_router(router: &AudioRouter) -> Self {
let mut records = router
.site_keys()
.filter_map(|key| router.site(key))
.map(|site| DeviceRecord::modeled_audio_from_card(site.card()))
.collect::<Vec<_>>();
records.sort_by_key(|record| record.id.to_string());
Self { records }
}
fn is_empty(&self) -> bool {
self.records.is_empty()
}
}
impl DeviceProvider for ProviderAudioSites {
fn enumerate(&self) -> Result<Vec<DeviceRecord>> {
Ok(self.records.clone())
}
fn open(&self, id: &Symbol) -> Result<Box<dyn StreamEvalSite>> {
let record = self
.records
.iter()
.find(|record| &record.id == id)
.ok_or_else(|| Error::Eval(format!("ProviderAudioSites: unknown id '{id}'")))?
.clone();
Ok(Box::new(ProviderAudioSite { record }))
}
}
struct RegistryAudioDevices {
records: Vec<DeviceRecord>,
}
impl RegistryAudioDevices {
fn from_registry(registry: &Registry) -> Self {
let mut records = registry
.libs()
.iter()
.flat_map(|lib| lib.exports.iter())
.filter_map(registry_audio_device_record)
.collect::<Vec<_>>();
records.sort_by_key(|record| record.id.to_string());
records.dedup_by(|left, right| left.id == right.id);
Self { records }
}
fn is_empty(&self) -> bool {
self.records.is_empty()
}
}
impl DeviceProvider for RegistryAudioDevices {
fn enumerate(&self) -> Result<Vec<DeviceRecord>> {
Ok(self.records.clone())
}
fn open(&self, id: &Symbol) -> Result<Box<dyn StreamEvalSite>> {
let record = self
.records
.iter()
.find(|record| &record.id == id)
.ok_or_else(|| Error::Eval(format!("RegistryAudioDevices: unknown id '{id}'")))?
.clone();
Ok(Box::new(ProviderAudioSite { record }))
}
}
fn registry_audio_device_record(record: &ExportRecord) -> Option<DeviceRecord> {
if record.kind != ExportKind::named(ExportKind::VALUE) {
return None;
}
if record.symbol.namespace.as_deref() != Some("audio/device") {
return None;
}
if matches!(record.state, ExportState::Invalid { .. }) {
return None;
}
Some(DeviceRecord {
id: record.symbol.clone(),
display_name: format!("{} audio device", record.symbol.name),
kind: DeviceKind::Audio,
direction: DeviceDirection::Duplex,
placement: Placement::Modeled,
})
}
struct ProviderAudioSite {
record: DeviceRecord,
}
impl StreamEvalSite for ProviderAudioSite {
fn placement(&self) -> &Placement {
&self.record.placement
}
fn device_record(&self) -> &DeviceRecord {
&self.record
}
fn close(self: Box<Self>) -> Result<()> {
Ok(())
}
}