Skip to main content

sim_lib_stream_host/
backend.rs

1//! Generic host backend trait and opened stream handle.
2
3use std::{rc::Rc, sync::Arc};
4
5use sim_kernel::Result;
6use sim_lib_stream_core::StreamValue;
7
8use crate::{
9    HostBackendInfo, HostCallbackQueue, HostDeviceInventory, HostStreamConfig,
10    HostStreamConfigRequest,
11};
12
13/// Backend contract for host-controlled stream devices.
14pub trait HostBackend: Send + Sync {
15    /// Stable backend metadata.
16    fn info(&self) -> &HostBackendInfo;
17
18    /// Enumerates available devices and ports without opening hardware streams.
19    fn enumerate(&self) -> Result<HostDeviceInventory>;
20
21    /// Opens a stream according to the requested host configuration.
22    fn open(&self, request: HostStreamConfigRequest) -> Result<HostOpenStream>;
23}
24
25/// Native or external stream resource attached to an opened host stream.
26///
27/// Implementations own platform resources that must outlive the SIM-side queue.
28/// Closing or canceling the host stream calls [`HostStreamDriver::shutdown`];
29/// dropping the driver should also release the underlying resource.
30///
31/// The trait is intentionally thread-affinity-neutral because some platform
32/// stream handles cannot be sent or shared across threads.
33pub trait HostStreamDriver {
34    /// Stops the external stream resource.
35    fn shutdown(&self) -> Result<()>;
36}
37
38/// Open stream plus host-side callback queue.
39#[derive(Clone)]
40pub struct HostOpenStream {
41    config: HostStreamConfig,
42    queue: HostCallbackQueue,
43    driver: Option<Rc<dyn HostStreamDriver>>,
44}
45
46impl HostOpenStream {
47    /// Creates an opened host stream backed by a push stream value.
48    pub fn new(config: HostStreamConfig) -> Self {
49        let stream = Arc::new(StreamValue::push(config.metadata()));
50        Self {
51            config,
52            queue: HostCallbackQueue::new(stream),
53            driver: None,
54        }
55    }
56
57    /// Creates an opened host stream with an attached external driver.
58    pub fn new_with_driver(config: HostStreamConfig, driver: Rc<dyn HostStreamDriver>) -> Self {
59        let stream = Arc::new(StreamValue::push(config.metadata()));
60        Self {
61            config,
62            queue: HostCallbackQueue::new(stream),
63            driver: Some(driver),
64        }
65    }
66
67    /// Creates an opened host stream by building its driver from the callback
68    /// queue that will be stored on the stream.
69    pub fn try_new_with_driver(
70        config: HostStreamConfig,
71        build_driver: impl FnOnce(HostCallbackQueue) -> Result<Rc<dyn HostStreamDriver>>,
72    ) -> Result<Self> {
73        let stream = Arc::new(StreamValue::push(config.metadata()));
74        let queue = HostCallbackQueue::new(stream);
75        let driver = build_driver(queue.clone())?;
76        Ok(Self {
77            config,
78            queue,
79            driver: Some(driver),
80        })
81    }
82
83    /// Creates a realtime local audio stream after validating callback limits.
84    pub fn new_realtime_local_audio(config: HostStreamConfig) -> Result<Self> {
85        config.validate_realtime_local_audio()?;
86        Ok(Self::new(config))
87    }
88
89    /// Creates a realtime local audio stream with an attached external driver.
90    pub fn new_realtime_local_audio_with_driver(
91        config: HostStreamConfig,
92        driver: Rc<dyn HostStreamDriver>,
93    ) -> Result<Self> {
94        config.validate_realtime_local_audio()?;
95        Ok(Self::new_with_driver(config, driver))
96    }
97
98    /// Creates a realtime local audio stream by building its driver from the
99    /// stored callback queue.
100    pub fn try_new_realtime_local_audio_with_driver(
101        config: HostStreamConfig,
102        build_driver: impl FnOnce(HostCallbackQueue) -> Result<Rc<dyn HostStreamDriver>>,
103    ) -> Result<Self> {
104        config.validate_realtime_local_audio()?;
105        Self::try_new_with_driver(config, build_driver)
106    }
107
108    /// Creates a LAN MIDI/control stream after validating media and clock shape.
109    pub fn new_lan_midi_control(config: HostStreamConfig) -> Result<Self> {
110        config.validate_lan_midi_control()?;
111        Ok(Self::new(config))
112    }
113
114    /// Creates a LAN buffered audio preview stream after validating media shape.
115    pub fn new_lan_buffered_audio_preview(config: HostStreamConfig) -> Result<Self> {
116        config.validate_lan_buffered_audio_preview()?;
117        Ok(Self::new(config))
118    }
119
120    /// Returns the accepted stream configuration.
121    pub fn config(&self) -> &HostStreamConfig {
122        &self.config
123    }
124
125    /// Returns the callback queue used by host callbacks or deterministic fakes.
126    pub fn queue(&self) -> &HostCallbackQueue {
127        &self.queue
128    }
129
130    /// Returns the stream value consumed by graph/runtime code.
131    pub fn stream(&self) -> Arc<StreamValue> {
132        self.queue.stream()
133    }
134
135    /// Closes the host callback queue.
136    pub fn close(&self) -> Result<()> {
137        self.queue.close()?;
138        self.shutdown_driver()
139    }
140
141    /// Cancels the callback queue and drops buffered packets.
142    pub fn cancel(&self) -> Result<()> {
143        self.queue.cancel()?;
144        self.shutdown_driver()
145    }
146
147    fn shutdown_driver(&self) -> Result<()> {
148        if let Some(driver) = &self.driver {
149            driver.shutdown()?;
150        }
151        Ok(())
152    }
153}