1use sim_kernel::{Error, Result, Symbol};
4use sim_lib_stream_core::{BufferPolicy, ClockDomain, StreamMedia, StreamMetadata};
5
6use crate::model::HostDirection;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct HostStreamConfigRequest {
11 backend: Symbol,
12 device: Symbol,
13 media: StreamMedia,
14 direction: HostDirection,
15 buffer: BufferPolicy,
16 clock: Symbol,
17 reconnect: HostReconnectPolicy,
18}
19
20#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct HostStreamConfig {
23 backend: Symbol,
24 device: Symbol,
25 media: StreamMedia,
26 direction: HostDirection,
27 buffer: BufferPolicy,
28 clock: HostClockInfo,
29 latency: HostLatencyInfo,
30 reconnect: HostReconnectPolicy,
31}
32
33#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
35pub struct HostLatencyInfo {
36 input_frames: u32,
37 output_frames: u32,
38}
39
40#[derive(Clone, Debug, PartialEq, Eq)]
42pub struct HostClockInfo {
43 clock: Symbol,
44 sample_rate_hz: Option<u32>,
45 stable: bool,
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
50pub struct HostReconnectPolicy {
51 enabled: bool,
52 max_attempts: u32,
53 backoff_ms: u32,
54}
55
56impl HostStreamConfigRequest {
57 pub fn new(
60 backend: Symbol,
61 device: Symbol,
62 media: StreamMedia,
63 direction: HostDirection,
64 buffer: BufferPolicy,
65 ) -> Self {
66 Self {
67 backend,
68 device,
69 media,
70 direction,
71 buffer,
72 clock: ClockDomain::ServerFrame.symbol(),
73 reconnect: HostReconnectPolicy::disabled(),
74 }
75 }
76
77 pub fn with_clock(mut self, clock: Symbol) -> Self {
79 self.clock = clock;
80 self
81 }
82
83 pub fn with_reconnect(mut self, reconnect: HostReconnectPolicy) -> Self {
85 self.reconnect = reconnect;
86 self
87 }
88
89 pub fn backend(&self) -> &Symbol {
91 &self.backend
92 }
93
94 pub fn device(&self) -> &Symbol {
96 &self.device
97 }
98
99 pub fn media(&self) -> StreamMedia {
101 self.media
102 }
103
104 pub fn direction(&self) -> HostDirection {
106 self.direction
107 }
108
109 pub fn buffer(&self) -> &BufferPolicy {
111 &self.buffer
112 }
113
114 pub fn clock(&self) -> &Symbol {
116 &self.clock
117 }
118
119 pub fn reconnect(&self) -> &HostReconnectPolicy {
121 &self.reconnect
122 }
123}
124
125impl HostStreamConfig {
126 pub fn from_request(
129 request: HostStreamConfigRequest,
130 latency: HostLatencyInfo,
131 clock: HostClockInfo,
132 ) -> Self {
133 Self {
134 backend: request.backend,
135 device: request.device,
136 media: request.media,
137 direction: request.direction,
138 buffer: request.buffer,
139 clock,
140 latency,
141 reconnect: request.reconnect,
142 }
143 }
144
145 pub fn backend(&self) -> &Symbol {
147 &self.backend
148 }
149
150 pub fn device(&self) -> &Symbol {
152 &self.device
153 }
154
155 pub fn media(&self) -> StreamMedia {
157 self.media
158 }
159
160 pub fn direction(&self) -> HostDirection {
162 self.direction
163 }
164
165 pub fn buffer(&self) -> &BufferPolicy {
167 &self.buffer
168 }
169
170 pub fn clock(&self) -> &HostClockInfo {
172 &self.clock
173 }
174
175 pub fn latency(&self) -> HostLatencyInfo {
177 self.latency
178 }
179
180 pub fn reconnect(&self) -> &HostReconnectPolicy {
182 &self.reconnect
183 }
184
185 pub fn metadata(&self) -> StreamMetadata {
187 StreamMetadata::new(
188 self.device.clone(),
189 self.media,
190 self.direction.stream_direction(),
191 self.clock.clock.clone(),
192 self.buffer.clone(),
193 )
194 }
195
196 pub fn validate_realtime_local_audio(&self) -> Result<()> {
201 if self.media != StreamMedia::Pcm {
202 return Err(Error::Eval(
203 "realtime local audio host streams require PCM media".to_owned(),
204 ));
205 }
206 if ClockDomain::from_symbol(self.clock.clock())? != ClockDomain::Sample {
207 return Err(Error::Eval(
208 "realtime local audio host streams require the sample clock domain".to_owned(),
209 ));
210 }
211 if self.buffer.capacity() == 0 {
212 return Err(Error::Eval(
213 "realtime local audio host streams require a bounded buffer".to_owned(),
214 ));
215 }
216 Ok(())
217 }
218
219 pub fn validate_lan_midi_control(&self) -> Result<()> {
224 if self.media != StreamMedia::Midi {
225 return Err(Error::Eval(
226 "LAN MIDI/control host streams require MIDI media".to_owned(),
227 ));
228 }
229 match ClockDomain::from_symbol(self.clock.clock())? {
230 ClockDomain::MidiTick | ClockDomain::Control => {}
231 _ => {
232 return Err(Error::Eval(
233 "LAN MIDI/control host streams require a MIDI tick or control clock domain"
234 .to_owned(),
235 ));
236 }
237 }
238 if self.buffer.capacity() == 0 {
239 return Err(Error::Eval(
240 "LAN MIDI/control host streams require a bounded buffer".to_owned(),
241 ));
242 }
243 Ok(())
244 }
245
246 pub fn validate_lan_buffered_audio_preview(&self) -> Result<()> {
252 if self.media != StreamMedia::Pcm {
253 return Err(Error::Eval(
254 "LAN buffered audio preview host streams require PCM media".to_owned(),
255 ));
256 }
257 if self.buffer.capacity() == 0 {
258 return Err(Error::Eval(
259 "LAN buffered audio preview host streams require a bounded buffer".to_owned(),
260 ));
261 }
262 Ok(())
263 }
264}
265
266impl HostLatencyInfo {
267 pub fn new(input_frames: u32, output_frames: u32) -> Self {
279 Self {
280 input_frames,
281 output_frames,
282 }
283 }
284
285 pub fn input_frames(self) -> u32 {
287 self.input_frames
288 }
289
290 pub fn output_frames(self) -> u32 {
292 self.output_frames
293 }
294}
295
296impl HostClockInfo {
297 pub fn new(clock: Symbol, sample_rate_hz: Option<u32>, stable: bool) -> Self {
300 Self {
301 clock,
302 sample_rate_hz,
303 stable,
304 }
305 }
306
307 pub fn clock(&self) -> &Symbol {
309 &self.clock
310 }
311
312 pub fn sample_rate_hz(&self) -> Option<u32> {
314 self.sample_rate_hz
315 }
316
317 pub fn stable(&self) -> bool {
319 self.stable
320 }
321}
322
323impl HostReconnectPolicy {
324 pub fn disabled() -> Self {
326 Self {
327 enabled: false,
328 max_attempts: 0,
329 backoff_ms: 0,
330 }
331 }
332
333 pub fn bounded(max_attempts: u32, backoff_ms: u32) -> Self {
347 Self {
348 enabled: true,
349 max_attempts,
350 backoff_ms,
351 }
352 }
353
354 pub fn enabled(&self) -> bool {
356 self.enabled
357 }
358
359 pub fn max_attempts(&self) -> u32 {
361 self.max_attempts
362 }
363
364 pub fn backoff_ms(&self) -> u32 {
366 self.backoff_ms
367 }
368}