use std::collections::BTreeMap;
use std::sync::Arc;
use sim_kernel::{CapabilityName, Cx, Error, Lib, LibSource, LoaderRegistry, Result, Symbol};
use crate::{AudioRouter, AudioSite};
pub const AUDIO_PROVIDER_ENTRY_V1: &str = "sim_audio_provider_v1";
pub const AUDIO_PROVIDER_ABI_VERSION: u32 = 1;
pub fn native_audio_provider_capability() -> CapabilityName {
CapabilityName::new("audio.provider.native")
}
pub trait AudioProviderRegistrar {
fn register_site(&mut self, site: Arc<dyn AudioSite>);
fn host_abi_version(&self) -> u32;
}
pub type AudioProviderEntry = fn(&mut dyn AudioProviderRegistrar) -> Result<()>;
#[derive(Clone, Default)]
pub struct AudioProviderEntries {
entries: BTreeMap<Symbol, AudioProviderEntry>,
}
impl AudioProviderEntries {
pub fn new() -> Self {
Self::default()
}
pub fn with_entry(mut self, provider: Symbol, entry: AudioProviderEntry) -> Self {
self.insert(provider, entry);
self
}
pub fn insert(&mut self, provider: Symbol, entry: AudioProviderEntry) {
self.entries.insert(provider, entry);
}
fn entry(&self, provider: &Symbol) -> Result<AudioProviderEntry> {
self.entries.get(provider).copied().ok_or_else(|| {
Error::HostError(format!(
"audio provider {} did not expose {}",
provider, AUDIO_PROVIDER_ENTRY_V1
))
})
}
}
pub struct AudioProviderHost<'a> {
cx: &'a mut Cx,
loaders: &'a LoaderRegistry,
entries: AudioProviderEntries,
}
impl<'a> AudioProviderHost<'a> {
pub fn new(cx: &'a mut Cx, loaders: &'a LoaderRegistry) -> Self {
Self {
cx,
loaders,
entries: AudioProviderEntries::new(),
}
}
pub fn with_entry(mut self, provider: Symbol, entry: AudioProviderEntry) -> Self {
self.entries.insert(provider, entry);
self
}
pub fn load_into(&mut self, source: LibSource, router: &mut AudioRouter) -> Result<()> {
self.cx.require(&native_audio_provider_capability())?;
let lib = self.loaders.load_lib(self.cx, source)?;
register_provider_lib(lib.as_ref(), &self.entries, router)
}
}
fn register_provider_lib(
lib: &dyn Lib,
entries: &AudioProviderEntries,
router: &mut AudioRouter,
) -> Result<()> {
let entry = entries.entry(&lib.manifest().id)?;
let mut registrar = RouterAudioProviderRegistrar::new(router);
entry(&mut registrar)
}
pub struct RouterAudioProviderRegistrar<'a> {
router: &'a mut AudioRouter,
}
impl<'a> RouterAudioProviderRegistrar<'a> {
pub fn new(router: &'a mut AudioRouter) -> Self {
Self { router }
}
}
impl AudioProviderRegistrar for RouterAudioProviderRegistrar<'_> {
fn register_site(&mut self, site: Arc<dyn AudioSite>) {
self.router.register(site);
}
fn host_abi_version(&self) -> u32 {
AUDIO_PROVIDER_ABI_VERSION
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use crate::{
AudioDeviceCard, AudioProviderRegistrar, AudioRouter, AudioSiteKey, FakeBackend,
ModeledAudioSite, RouterAudioProviderRegistrar,
};
#[test]
fn provider_seam_registers_site_into_router() {
let key = AudioSiteKey::new("audio/native/jack-spike");
let card = AudioDeviceCard::modeled(key.clone(), "JACK Provider Spike");
let site = Arc::new(ModeledAudioSite::new(card, Arc::new(FakeBackend::new())));
let mut router = AudioRouter::new();
{
let mut registrar = RouterAudioProviderRegistrar::new(&mut router);
assert_eq!(registrar.host_abi_version(), 1);
registrar.register_site(site);
}
assert!(router.site(&key).is_some());
}
}