nandi/jolt-nativepublic Fork 0
cfd3e3677bed92e80cfe447469a249cfdc0b1401
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

mod.rs · 438 lines · 13.0 KBRust Blame HistoryRaw
Lift freeq's AV media plane out of sleek 90f8b89 nandi 19d ago1//! Custom host backend.
2//!
3//! Allows user-defined host implementations with the `custom` feature.
4//! See `examples/custom.rs` for usage.
5
6use crate::traits::{DeviceTrait, HostTrait, StreamTrait};
7use crate::{
8 BuildStreamError, Data, DefaultStreamConfigError, DeviceDescription, DeviceId, DeviceIdError,
9 DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo, PauseStreamError,
10 PlayStreamError, SampleFormat, StreamConfig, StreamError, SupportedStreamConfig,
11 SupportedStreamConfigRange, SupportedStreamConfigsError,
12};
13use core::time::Duration;
14
15/// A host that can be used to write custom [`HostTrait`] implementations.
16///
17/// # Usage
18///
19/// A [`CustomHost`](Host) can be used on its own, but most crates that depend on `cpal` use a [`cpal::Host`](crate::Host) instead.
20/// You can turn a `CustomHost` into a `Host` fairly easily:
21///
22/// ```ignore
23/// let custom = cpal::platform::CustomHost::from_host(/* ... */);
24/// let host = cpal::Host::from(custom);
25/// ```
26///
27/// Custom hosts are marked as unavailable and will not appear in [`cpal::available_hosts`](crate::available_hosts).
28pub struct Host(Box<dyn HostErased>);
29
30impl Host {
31 // this only exists for impl_platform_host, which requires it
32 pub(crate) fn new() -> Result<Self, crate::HostUnavailable> {
33 Err(crate::HostUnavailable)
34 }
35
36 /// Construct a custom host from an arbitrary [`HostTrait`] implementation.
37 pub fn from_host<T>(host: T) -> Self
38 where
39 T: HostTrait + Send + Sync + 'static,
40 T::Device: Send + Sync + Clone,
41 <T::Device as DeviceTrait>::SupportedInputConfigs: Clone,
42 <T::Device as DeviceTrait>::SupportedOutputConfigs: Clone,
43 <T::Device as DeviceTrait>::Stream: Send + Sync,
44 {
45 Self(Box::new(host))
46 }
47}
48
49/// A device that can be used to write custom [`DeviceTrait`] implementations.
50///
51/// # Usage
52///
53/// A [`CustomDevice`](Device) can be used on its own, but most crates that depend on `cpal` use a [`cpal::Device`](crate::Device) instead.
54/// You can turn a `Device` into a `Device` fairly easily:
55///
56/// ```ignore
57/// let custom = cpal::platform::CustomDevice::from_device(/* ... */);
58/// let device = cpal::Device::from(custom);
59/// ```
60///
61/// `rodio`, for example, lets you build an `OutputStream` with a [`cpal::Device`](crate::Device):
62/// ```ignore
63/// let custom = cpal::platform::CustomDevice::from_device(/* ... */);
64/// let device = cpal::Device::from(custom);
65///
66/// let stream_builder = rodio::OutputStreamBuilder::from_device(device).expect("failed to build stream");
67/// ```
68pub struct Device(Box<dyn DeviceErased>);
69
70impl Device {
71 /// Construct a custom device from an arbitrary [`DeviceTrait`] implementation.
72 pub fn from_device<T>(device: T) -> Self
73 where
74 T: DeviceTrait + Send + Sync + Clone + 'static,
75 T::SupportedInputConfigs: Clone,
76 T::SupportedOutputConfigs: Clone,
77 T::Stream: Send + Sync,
78 {
79 Self(Box::new(device))
80 }
81}
82
83impl Clone for Device {
84 fn clone(&self) -> Self {
85 self.0.clone()
86 }
87}
88
89/// A stream that can be used with custom [`StreamTrait`] implementations.
90pub struct Stream(Box<dyn StreamErased>);
91
92impl Stream {
93 /// Construct a custom stream from an arbitrary [`StreamTrait`] implementation.
94 pub fn from_stream<T>(stream: T) -> Self
95 where
96 T: StreamTrait + Send + Sync + 'static,
97 {
98 Self(Box::new(stream))
99 }
100}
101
102// dyn-compatible versions of DeviceTrait, HostTrait, and StreamTrait
103// these only accept/return things via trait objects
104
105type Devices = Box<dyn Iterator<Item = Device>>;
106trait HostErased: Send + Sync {
107 fn devices(&self) -> Result<Devices, DevicesError>;
108 fn default_input_device(&self) -> Option<Device>;
109 fn default_output_device(&self) -> Option<Device>;
110}
111
112pub struct SupportedConfigs(Box<dyn SupportedConfigsErased>);
113
114// A trait for supported configs. This only adds a dyn compatible clone function
115// This is required because `SupportedInputConfigsInner` & `SupportedOutputConfigsInner` are `Clone`
116trait SupportedConfigsErased: Iterator<Item = SupportedStreamConfigRange> {
117 fn clone(&self) -> SupportedConfigs;
118}
119
120impl<T> SupportedConfigsErased for T
121where
122 T: Iterator<Item = SupportedStreamConfigRange> + Clone + 'static,
123{
124 fn clone(&self) -> SupportedConfigs {
125 SupportedConfigs(Box::new(Clone::clone(self)))
126 }
127}
128
129impl Iterator for SupportedConfigs {
130 type Item = SupportedStreamConfigRange;
131
132 fn next(&mut self) -> Option<Self::Item> {
133 self.0.next()
134 }
135}
136
137impl Clone for SupportedConfigs {
138 fn clone(&self) -> Self {
139 self.0.clone()
140 }
141}
142
143type ErrorCallback = Box<dyn FnMut(StreamError) + Send + 'static>;
144type InputCallback = Box<dyn FnMut(&Data, &InputCallbackInfo) + Send + 'static>;
145type OutputCallback = Box<dyn FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static>;
146
147trait DeviceErased: Send + Sync {
148 fn name(&self) -> Result<String, DeviceNameError>;
149 fn description(&self) -> Result<DeviceDescription, DeviceNameError>;
150 fn id(&self) -> Result<DeviceId, DeviceIdError>;
151 fn supports_input(&self) -> bool;
152 fn supports_output(&self) -> bool;
153 fn supported_input_configs(&self) -> Result<SupportedConfigs, SupportedStreamConfigsError>;
154 fn supported_output_configs(&self) -> Result<SupportedConfigs, SupportedStreamConfigsError>;
155 fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError>;
156 fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError>;
157 fn build_input_stream_raw(
158 &self,
159 config: StreamConfig,
160 sample_format: SampleFormat,
161 data_callback: InputCallback,
162 error_callback: ErrorCallback,
163 timeout: Option<Duration>,
164 ) -> Result<Stream, BuildStreamError>;
165 fn build_output_stream_raw(
166 &self,
167 config: StreamConfig,
168 sample_format: SampleFormat,
169 data_callback: OutputCallback,
170 error_callback: ErrorCallback,
171 timeout: Option<Duration>,
172 ) -> Result<Stream, BuildStreamError>;
173 // Required because `DeviceInner` is clone
174 fn clone(&self) -> Device;
175}
176
177trait StreamErased: Send + Sync {
178 fn play(&self) -> Result<(), PlayStreamError>;
179 fn pause(&self) -> Result<(), PauseStreamError>;
180}
181
182fn device_to_erased(d: impl DeviceErased + 'static) -> Device {
183 Device(Box::new(d))
184}
185
186impl<T> HostErased for T
187where
188 T: HostTrait + Send + Sync,
189 T::Devices: 'static,
190 T::Device: DeviceErased + 'static,
191{
192 fn devices(&self) -> Result<Devices, DevicesError> {
193 let iter = <T as HostTrait>::devices(self)?;
194 let erased = Box::new(iter.map(device_to_erased));
195 Ok(erased)
196 }
197
198 fn default_input_device(&self) -> Option<Device> {
199 <T as HostTrait>::default_input_device(self).map(device_to_erased)
200 }
201
202 fn default_output_device(&self) -> Option<Device> {
203 <T as HostTrait>::default_output_device(self).map(device_to_erased)
204 }
205}
206
207fn supported_configs_to_erased(
208 i: impl Iterator<Item = SupportedStreamConfigRange> + Clone + 'static,
209) -> SupportedConfigs {
210 SupportedConfigs(Box::new(i))
211}
212
213fn stream_to_erased(s: impl StreamTrait + Send + Sync + 'static) -> Stream {
214 Stream(Box::new(s))
215}
216
217impl<T> DeviceErased for T
218where
219 T: DeviceTrait + Send + Sync + Clone + 'static,
220 T::SupportedInputConfigs: Clone + 'static,
221 T::SupportedOutputConfigs: Clone + 'static,
222 T::Stream: Send + Sync + 'static,
223{
224 #[allow(deprecated)]
225 fn name(&self) -> Result<String, DeviceNameError> {
226 <T as DeviceTrait>::name(self)
227 }
228
229 fn description(&self) -> Result<DeviceDescription, DeviceNameError> {
230 <T as DeviceTrait>::description(self)
231 }
232
233 fn id(&self) -> Result<DeviceId, DeviceIdError> {
234 <T as DeviceTrait>::id(self)
235 }
236
237 fn supports_input(&self) -> bool {
238 <T as DeviceTrait>::supports_input(self)
239 }
240
241 fn supports_output(&self) -> bool {
242 <T as DeviceTrait>::supports_output(self)
243 }
244
245 fn supported_input_configs(&self) -> Result<SupportedConfigs, SupportedStreamConfigsError> {
246 <T as DeviceTrait>::supported_input_configs(self).map(supported_configs_to_erased)
247 }
248
249 fn supported_output_configs(&self) -> Result<SupportedConfigs, SupportedStreamConfigsError> {
250 <T as DeviceTrait>::supported_output_configs(self).map(supported_configs_to_erased)
251 }
252
253 fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
254 <T as DeviceTrait>::default_input_config(self)
255 }
256
257 fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
258 <T as DeviceTrait>::default_output_config(self)
259 }
260
261 fn build_input_stream_raw(
262 &self,
263 config: StreamConfig,
264 sample_format: SampleFormat,
265 data_callback: InputCallback,
266 error_callback: ErrorCallback,
267 timeout: Option<Duration>,
268 ) -> Result<Stream, BuildStreamError> {
269 <T as DeviceTrait>::build_input_stream_raw(
270 self,
271 config,
272 sample_format,
273 data_callback,
274 error_callback,
275 timeout,
276 )
277 .map(stream_to_erased)
278 }
279
280 fn build_output_stream_raw(
281 &self,
282 config: StreamConfig,
283 sample_format: SampleFormat,
284 data_callback: OutputCallback,
285 error_callback: ErrorCallback,
286 timeout: Option<Duration>,
287 ) -> Result<Stream, BuildStreamError> {
288 <T as DeviceTrait>::build_output_stream_raw(
289 self,
290 config,
291 sample_format,
292 data_callback,
293 error_callback,
294 timeout,
295 )
296 .map(stream_to_erased)
297 }
298
299 fn clone(&self) -> Device {
300 device_to_erased(Clone::clone(self))
301 }
302}
303
304impl<T> StreamErased for T
305where
306 T: StreamTrait + Send + Sync,
307{
308 fn play(&self) -> Result<(), PlayStreamError> {
309 <T as StreamTrait>::play(self)
310 }
311
312 fn pause(&self) -> Result<(), PauseStreamError> {
313 <T as StreamTrait>::pause(self)
314 }
315}
316
317// implementations of HostTrait, DeviceTrait, and StreamTrait for custom versions
318
319impl HostTrait for Host {
320 type Devices = Devices;
321 type Device = Device;
322
323 fn is_available() -> bool {
324 false
325 }
326
327 fn devices(&self) -> Result<Self::Devices, DevicesError> {
328 self.0.devices()
329 }
330
331 fn default_input_device(&self) -> Option<Self::Device> {
332 self.0.default_input_device()
333 }
334
335 fn default_output_device(&self) -> Option<Self::Device> {
336 self.0.default_output_device()
337 }
338}
339
340impl DeviceTrait for Device {
341 type SupportedInputConfigs = SupportedConfigs;
342
343 type SupportedOutputConfigs = SupportedConfigs;
344
345 type Stream = Stream;
346
347 fn name(&self) -> Result<String, DeviceNameError> {
348 self.0.name()
349 }
350
351 fn description(&self) -> Result<DeviceDescription, DeviceNameError> {
352 self.0.description()
353 }
354
355 fn id(&self) -> Result<DeviceId, DeviceIdError> {
356 self.0.id()
357 }
358
359 fn supports_input(&self) -> bool {
360 self.0.supports_input()
361 }
362
363 fn supports_output(&self) -> bool {
364 self.0.supports_output()
365 }
366
367 fn supported_input_configs(
368 &self,
369 ) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
370 self.0.supported_input_configs()
371 }
372
373 fn supported_output_configs(
374 &self,
375 ) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
376 self.0.supported_output_configs()
377 }
378
379 fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
380 self.0.default_input_config()
381 }
382
383 fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
384 self.0.default_output_config()
385 }
386
387 fn build_input_stream_raw<D, E>(
388 &self,
389 config: StreamConfig,
390 sample_format: SampleFormat,
391 data_callback: D,
392 error_callback: E,
393 timeout: Option<Duration>,
394 ) -> Result<Self::Stream, BuildStreamError>
395 where
396 D: FnMut(&Data, &InputCallbackInfo) + Send + 'static,
397 E: FnMut(StreamError) + Send + 'static,
398 {
399 self.0.build_input_stream_raw(
400 config,
401 sample_format,
402 Box::new(data_callback),
403 Box::new(error_callback),
404 timeout,
405 )
406 }
407
408 fn build_output_stream_raw<D, E>(
409 &self,
410 config: StreamConfig,
411 sample_format: SampleFormat,
412 data_callback: D,
413 error_callback: E,
414 timeout: Option<Duration>,
415 ) -> Result<Self::Stream, BuildStreamError>
416 where
417 D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static,
418 E: FnMut(StreamError) + Send + 'static,
419 {
420 self.0.build_output_stream_raw(
421 config,
422 sample_format,
423 Box::new(data_callback),
424 Box::new(error_callback),
425 timeout,
426 )
427 }
428}
429
430impl StreamTrait for Stream {
431 fn play(&self) -> Result<(), PlayStreamError> {
432 self.0.play()
433 }
434
435 fn pause(&self) -> Result<(), PauseStreamError> {
436 self.0.pause()
437 }
438}