Skip to main content

sim_lib_stream_host/
router.rs

1//! Audio placement router for registered audio sites.
2
3use std::collections::HashMap;
4use std::sync::Arc;
5
6use sim_kernel::{Error, Result};
7
8use crate::HostOpenStream;
9use crate::placement::{AudioPlacementRequest, AudioSiteKey};
10use crate::site::AudioSite;
11
12/// Registry and dispatcher for audio placement sites.
13pub struct AudioRouter {
14    sites: HashMap<AudioSiteKey, Arc<dyn AudioSite>>,
15}
16
17impl Default for AudioRouter {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl AudioRouter {
24    /// Builds an empty audio placement router.
25    pub fn new() -> Self {
26        Self {
27            sites: HashMap::new(),
28        }
29    }
30
31    /// Registers or replaces a site by its stable key.
32    pub fn register(&mut self, site: Arc<dyn AudioSite>) {
33        self.sites.insert(site.key().clone(), site);
34    }
35
36    /// Returns an audio site by key.
37    pub fn site(&self, key: &AudioSiteKey) -> Option<&Arc<dyn AudioSite>> {
38        self.sites.get(key)
39    }
40
41    /// Iterates over registered audio site keys.
42    pub fn site_keys(&self) -> impl Iterator<Item = &AudioSiteKey> {
43        self.sites.keys()
44    }
45
46    /// Returns site keys whose device cards satisfy the requested output shape.
47    pub fn sites_by_capability(
48        &self,
49        min_channels_out: u16,
50        preferred_rates: &[u32],
51    ) -> Vec<AudioSiteKey> {
52        let mut keys = self
53            .sites
54            .values()
55            .filter(|site| {
56                let card = site.card();
57                card.channels_out >= min_channels_out
58                    && preferred_rates
59                        .iter()
60                        .any(|rate| card.sample_rates.contains(rate))
61            })
62            .map(|site| site.key().clone())
63            .collect::<Vec<_>>();
64        keys.sort_by_key(|key| key.0.to_string());
65        keys
66    }
67
68    /// Resolves `key` to a registered site, or returns the modeled fallback key.
69    ///
70    /// Missing native providers are treated as a placement miss rather than an
71    /// error when the modeled site is available.
72    pub fn resolve_or_modeled(
73        &self,
74        key: &AudioSiteKey,
75        modeled: &AudioSiteKey,
76    ) -> Result<AudioSiteKey> {
77        if self.sites.contains_key(key) {
78            Ok(key.clone())
79        } else if self.sites.contains_key(modeled) {
80            Ok(modeled.clone())
81        } else {
82            Err(Error::Eval(format!(
83                "no audio site registered for {:?} and no modeled fallback {:?}",
84                key, modeled
85            )))
86        }
87    }
88
89    /// Opens a placement request through its registered audio site.
90    pub fn open_placement(&self, request: AudioPlacementRequest) -> Result<HostOpenStream> {
91        self.sites
92            .get(&request.site_key)
93            .ok_or_else(|| {
94                Error::Eval(format!(
95                    "no audio site registered for {:?}",
96                    request.site_key
97                ))
98            })?
99            .open(request.stream_request)
100    }
101}