sim_lib_stream_host/
router.rs1use std::collections::HashMap;
4use std::sync::Arc;
5
6use sim_kernel::{Cx, Error, Result, Symbol};
7
8use crate::placement::{AudioPlacementRequest, AudioSiteKey};
9use crate::site::AudioSite;
10use crate::{HostOpenPlan, HostOpenStream, stream_host_capability};
11
12#[derive(Clone)]
14pub struct RegisteredAudioSite {
15 pub owner: Symbol,
17 pub site: Arc<dyn AudioSite>,
19}
20
21pub struct AudioRouter {
23 sites: HashMap<AudioSiteKey, RegisteredAudioSite>,
24}
25
26impl Default for AudioRouter {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl AudioRouter {
33 pub fn new() -> Self {
35 Self {
36 sites: HashMap::new(),
37 }
38 }
39
40 pub fn register(&mut self, site: Arc<dyn AudioSite>) {
45 self.register_owned(local_audio_site_owner_symbol(), site)
46 .expect("duplicate local audio site key");
47 }
48
49 pub fn register_owned(&mut self, owner: Symbol, site: Arc<dyn AudioSite>) -> Result<()> {
51 let key = site.key().clone();
52 if self.sites.contains_key(&key) {
53 return Err(Error::Eval(format!(
54 "audio site {} is already registered",
55 key.0
56 )));
57 }
58 self.sites.insert(key, RegisteredAudioSite { owner, site });
59 Ok(())
60 }
61
62 pub fn reload_owned(&mut self, owner: Symbol, site: Arc<dyn AudioSite>) -> Result<()> {
64 let key = site.key().clone();
65 if let Some(existing) = self.sites.get(&key)
66 && existing.owner != owner
67 {
68 return Err(Error::Eval(format!(
69 "audio site {} is already owned by {}",
70 key.0, existing.owner
71 )));
72 }
73 self.sites.insert(key, RegisteredAudioSite { owner, site });
74 Ok(())
75 }
76
77 pub fn unregister_owner(&mut self, owner: &Symbol) -> usize {
79 let before = self.sites.len();
80 self.sites
81 .retain(|_, registered| ®istered.owner != owner);
82 before - self.sites.len()
83 }
84
85 pub fn site(&self, key: &AudioSiteKey) -> Option<&Arc<dyn AudioSite>> {
87 self.sites.get(key).map(|registered| ®istered.site)
88 }
89
90 pub fn site_owner(&self, key: &AudioSiteKey) -> Option<&Symbol> {
92 self.sites.get(key).map(|registered| ®istered.owner)
93 }
94
95 pub fn registered_sites(&self) -> impl Iterator<Item = &RegisteredAudioSite> {
97 self.sites.values()
98 }
99
100 pub fn site_keys(&self) -> impl Iterator<Item = &AudioSiteKey> {
102 self.sites.keys()
103 }
104
105 pub fn sites_by_capability(
107 &self,
108 min_channels_out: u16,
109 preferred_rates: &[u32],
110 ) -> Vec<AudioSiteKey> {
111 let mut keys = self
112 .sites
113 .values()
114 .filter(|registered| {
115 let card = registered.site.card();
116 card.channels_out >= min_channels_out
117 && preferred_rates
118 .iter()
119 .any(|rate| card.sample_rates.contains(rate))
120 })
121 .map(|registered| registered.site.key().clone())
122 .collect::<Vec<_>>();
123 keys.sort_by_key(|key| key.0.to_string());
124 keys
125 }
126
127 pub fn resolve_or_modeled(
132 &self,
133 key: &AudioSiteKey,
134 modeled: &AudioSiteKey,
135 ) -> Result<AudioSiteKey> {
136 if self.sites.contains_key(key) {
137 Ok(key.clone())
138 } else if self.sites.contains_key(modeled) {
139 Ok(modeled.clone())
140 } else {
141 Err(Error::Eval(format!(
142 "no audio site registered for {:?} and no modeled fallback {:?}",
143 key, modeled
144 )))
145 }
146 }
147
148 pub fn open_placement_checked(
151 &self,
152 cx: &mut Cx,
153 request: AudioPlacementRequest,
154 ) -> Result<HostOpenStream> {
155 if !self.sites.contains_key(&request.site_key) {
156 return Err(Error::Eval(format!(
157 "no audio site registered for {:?}",
158 request.site_key
159 )));
160 }
161 HostOpenPlan::new(
162 request.stream_request.backend().clone(),
163 request.stream_request.device().clone(),
164 request.stream_request.direction().effect_kinds(),
165 vec![stream_host_capability()],
166 )
167 .enforce(cx)?;
168 self.open_placement(request)
169 }
170
171 pub fn open_placement(&self, request: AudioPlacementRequest) -> Result<HostOpenStream> {
177 self.sites
178 .get(&request.site_key)
179 .ok_or_else(|| {
180 Error::Eval(format!(
181 "no audio site registered for {:?}",
182 request.site_key
183 ))
184 })?
185 .site
186 .open(request.stream_request)
187 }
188}
189
190fn local_audio_site_owner_symbol() -> Symbol {
191 Symbol::qualified("audio/provider", "local-modeled")
192}