use std::collections::HashMap;
use std::sync::Arc;
use sim_kernel::{Cx, Error, Result, Symbol};
use crate::placement::{AudioPlacementRequest, AudioSiteKey};
use crate::site::AudioSite;
use crate::{HostOpenPlan, HostOpenStream, stream_host_capability};
#[derive(Clone)]
pub struct RegisteredAudioSite {
pub owner: Symbol,
pub site: Arc<dyn AudioSite>,
}
pub struct AudioRouter {
sites: HashMap<AudioSiteKey, RegisteredAudioSite>,
}
impl Default for AudioRouter {
fn default() -> Self {
Self::new()
}
}
impl AudioRouter {
pub fn new() -> Self {
Self {
sites: HashMap::new(),
}
}
pub fn register(&mut self, site: Arc<dyn AudioSite>) {
self.register_owned(local_audio_site_owner_symbol(), site)
.expect("duplicate local audio site key");
}
pub fn register_owned(&mut self, owner: Symbol, site: Arc<dyn AudioSite>) -> Result<()> {
let key = site.key().clone();
if self.sites.contains_key(&key) {
return Err(Error::Eval(format!(
"audio site {} is already registered",
key.0
)));
}
self.sites.insert(key, RegisteredAudioSite { owner, site });
Ok(())
}
pub fn reload_owned(&mut self, owner: Symbol, site: Arc<dyn AudioSite>) -> Result<()> {
let key = site.key().clone();
if let Some(existing) = self.sites.get(&key)
&& existing.owner != owner
{
return Err(Error::Eval(format!(
"audio site {} is already owned by {}",
key.0, existing.owner
)));
}
self.sites.insert(key, RegisteredAudioSite { owner, site });
Ok(())
}
pub fn unregister_owner(&mut self, owner: &Symbol) -> usize {
let before = self.sites.len();
self.sites
.retain(|_, registered| ®istered.owner != owner);
before - self.sites.len()
}
pub fn site(&self, key: &AudioSiteKey) -> Option<&Arc<dyn AudioSite>> {
self.sites.get(key).map(|registered| ®istered.site)
}
pub fn site_owner(&self, key: &AudioSiteKey) -> Option<&Symbol> {
self.sites.get(key).map(|registered| ®istered.owner)
}
pub fn registered_sites(&self) -> impl Iterator<Item = &RegisteredAudioSite> {
self.sites.values()
}
pub fn site_keys(&self) -> impl Iterator<Item = &AudioSiteKey> {
self.sites.keys()
}
pub fn sites_by_capability(
&self,
min_channels_out: u16,
preferred_rates: &[u32],
) -> Vec<AudioSiteKey> {
let mut keys = self
.sites
.values()
.filter(|registered| {
let card = registered.site.card();
card.channels_out >= min_channels_out
&& preferred_rates
.iter()
.any(|rate| card.sample_rates.contains(rate))
})
.map(|registered| registered.site.key().clone())
.collect::<Vec<_>>();
keys.sort_by_key(|key| key.0.to_string());
keys
}
pub fn resolve_or_modeled(
&self,
key: &AudioSiteKey,
modeled: &AudioSiteKey,
) -> Result<AudioSiteKey> {
if self.sites.contains_key(key) {
Ok(key.clone())
} else if self.sites.contains_key(modeled) {
Ok(modeled.clone())
} else {
Err(Error::Eval(format!(
"no audio site registered for {:?} and no modeled fallback {:?}",
key, modeled
)))
}
}
pub fn open_placement_checked(
&self,
cx: &mut Cx,
request: AudioPlacementRequest,
) -> Result<HostOpenStream> {
if !self.sites.contains_key(&request.site_key) {
return Err(Error::Eval(format!(
"no audio site registered for {:?}",
request.site_key
)));
}
HostOpenPlan::new(
request.stream_request.backend().clone(),
request.stream_request.device().clone(),
request.stream_request.direction().effect_kinds(),
vec![stream_host_capability()],
)
.enforce(cx)?;
self.open_placement(request)
}
pub fn open_placement(&self, request: AudioPlacementRequest) -> Result<HostOpenStream> {
self.sites
.get(&request.site_key)
.ok_or_else(|| {
Error::Eval(format!(
"no audio site registered for {:?}",
request.site_key
))
})?
.site
.open(request.stream_request)
}
}
fn local_audio_site_owner_symbol() -> Symbol {
Symbol::qualified("audio/provider", "local-modeled")
}