1use std::io::{Read, Seek};
2use std::sync::{Arc, Weak};
3use std::{error, fmt};
4
5use crate::decoder;
6use crate::dynamic_mixer::{self, DynamicMixerController};
7use crate::sink::Sink;
8use crate::source::Source;
9use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
10use cpal::{Sample, SupportedStreamConfig};
11
12pub struct OutputStream {
16 mixer: Arc<DynamicMixerController<f32>>,
17 _stream: cpal::Stream,
18}
19
20#[derive(Clone)]
22pub struct OutputStreamHandle {
23 mixer: Weak<DynamicMixerController<f32>>,
24}
25
26impl OutputStream {
27 pub fn try_from_device(
30 device: &cpal::Device,
31 ) -> Result<(Self, OutputStreamHandle), StreamError> {
32 let default_config = device
33 .default_output_config()
34 .map_err(StreamError::DefaultStreamConfigError)?;
35 OutputStream::try_from_device_config(device, default_config)
36 }
37
38 pub fn try_from_device_config(
43 device: &cpal::Device,
44 config: SupportedStreamConfig,
45 ) -> Result<(Self, OutputStreamHandle), StreamError> {
46 let (mixer, _stream) = device.try_new_output_stream_config(config)?;
47 _stream.play().map_err(StreamError::PlayStreamError)?;
48 let out = Self { mixer, _stream };
49 let handle = OutputStreamHandle {
50 mixer: Arc::downgrade(&out.mixer),
51 };
52 Ok((out, handle))
53 }
54
55 pub fn try_default() -> Result<(Self, OutputStreamHandle), StreamError> {
59 let default_device = cpal::default_host()
60 .default_output_device()
61 .ok_or(StreamError::NoDevice)?;
62
63 let default_stream = Self::try_from_device(&default_device);
64
65 default_stream.or_else(|original_err| {
66 let mut devices = match cpal::default_host().output_devices() {
68 Ok(d) => d,
69 Err(_) => return Err(original_err),
70 };
71
72 devices
73 .find_map(|d| Self::try_from_device(&d).ok())
74 .ok_or(original_err)
75 })
76 }
77}
78
79impl OutputStreamHandle {
80 pub fn play_raw<S>(&self, source: S) -> Result<(), PlayError>
82 where
83 S: Source<Item = f32> + Send + 'static,
84 {
85 let mixer = self.mixer.upgrade().ok_or(PlayError::NoDevice)?;
86 mixer.add(source);
87 Ok(())
88 }
89
90 pub fn play_once<R>(&self, input: R) -> Result<Sink, PlayError>
92 where
93 R: Read + Seek + Send + Sync + 'static,
94 {
95 let input = decoder::Decoder::new(input)?;
96 let sink = Sink::try_new(self)?;
97 sink.append(input);
98 Ok(sink)
99 }
100}
101
102#[derive(Debug)]
104pub enum PlayError {
105 DecoderError(decoder::DecoderError),
107 NoDevice,
109}
110
111impl From<decoder::DecoderError> for PlayError {
112 fn from(err: decoder::DecoderError) -> Self {
113 Self::DecoderError(err)
114 }
115}
116
117impl fmt::Display for PlayError {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 match self {
120 Self::DecoderError(e) => e.fmt(f),
121 Self::NoDevice => write!(f, "NoDevice"),
122 }
123 }
124}
125
126impl error::Error for PlayError {
127 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
128 match self {
129 Self::DecoderError(e) => Some(e),
130 Self::NoDevice => None,
131 }
132 }
133}
134
135#[derive(Debug)]
137pub enum StreamError {
138 PlayStreamError(cpal::PlayStreamError),
141 DefaultStreamConfigError(cpal::DefaultStreamConfigError),
144 BuildStreamError(cpal::BuildStreamError),
146 SupportedStreamConfigsError(cpal::SupportedStreamConfigsError),
149 NoDevice,
151}
152
153impl fmt::Display for StreamError {
154 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
155 match self {
156 Self::PlayStreamError(e) => e.fmt(f),
157 Self::BuildStreamError(e) => e.fmt(f),
158 Self::DefaultStreamConfigError(e) => e.fmt(f),
159 Self::SupportedStreamConfigsError(e) => e.fmt(f),
160 Self::NoDevice => write!(f, "NoDevice"),
161 }
162 }
163}
164
165impl error::Error for StreamError {
166 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
167 match self {
168 Self::PlayStreamError(e) => Some(e),
169 Self::BuildStreamError(e) => Some(e),
170 Self::DefaultStreamConfigError(e) => Some(e),
171 Self::SupportedStreamConfigsError(e) => Some(e),
172 Self::NoDevice => None,
173 }
174 }
175}
176
177pub(crate) trait CpalDeviceExt {
179 fn new_output_stream_with_format(
180 &self,
181 format: cpal::SupportedStreamConfig,
182 ) -> Result<(Arc<DynamicMixerController<f32>>, cpal::Stream), cpal::BuildStreamError>;
183
184 fn try_new_output_stream_config(
185 &self,
186 config: cpal::SupportedStreamConfig,
187 ) -> Result<(Arc<DynamicMixerController<f32>>, cpal::Stream), StreamError>;
188}
189
190impl CpalDeviceExt for cpal::Device {
191 fn new_output_stream_with_format(
192 &self,
193 format: cpal::SupportedStreamConfig,
194 ) -> Result<(Arc<DynamicMixerController<f32>>, cpal::Stream), cpal::BuildStreamError> {
195 let (mixer_tx, mut mixer_rx) =
196 dynamic_mixer::mixer::<f32>(format.channels(), format.sample_rate().0);
197
198 let error_callback = |err| {
199 #[cfg(feature = "tracing")]
200 tracing::error!("an error occurred on output stream: {err}");
201 #[cfg(not(feature = "tracing"))]
202 eprintln!("an error occurred on output stream: {err}");
203 };
204
205 match format.sample_format() {
206 cpal::SampleFormat::F32 => self.build_output_stream::<f32, _, _>(
207 &format.config(),
208 move |data, _| {
209 data.iter_mut()
210 .for_each(|d| *d = mixer_rx.next().unwrap_or(0f32))
211 },
212 error_callback,
213 None,
214 ),
215 cpal::SampleFormat::F64 => self.build_output_stream::<f64, _, _>(
216 &format.config(),
217 move |data, _| {
218 data.iter_mut()
219 .for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0f64))
220 },
221 error_callback,
222 None,
223 ),
224 cpal::SampleFormat::I8 => self.build_output_stream::<i8, _, _>(
225 &format.config(),
226 move |data, _| {
227 data.iter_mut()
228 .for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0i8))
229 },
230 error_callback,
231 None,
232 ),
233 cpal::SampleFormat::I16 => self.build_output_stream::<i16, _, _>(
234 &format.config(),
235 move |data, _| {
236 data.iter_mut()
237 .for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0i16))
238 },
239 error_callback,
240 None,
241 ),
242 cpal::SampleFormat::I32 => self.build_output_stream::<i32, _, _>(
243 &format.config(),
244 move |data, _| {
245 data.iter_mut()
246 .for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0i32))
247 },
248 error_callback,
249 None,
250 ),
251 cpal::SampleFormat::I64 => self.build_output_stream::<i64, _, _>(
252 &format.config(),
253 move |data, _| {
254 data.iter_mut()
255 .for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0i64))
256 },
257 error_callback,
258 None,
259 ),
260 cpal::SampleFormat::U8 => self.build_output_stream::<u8, _, _>(
261 &format.config(),
262 move |data, _| {
263 data.iter_mut().for_each(|d| {
264 *d = mixer_rx
265 .next()
266 .map(Sample::from_sample)
267 .unwrap_or(u8::MAX / 2)
268 })
269 },
270 error_callback,
271 None,
272 ),
273 cpal::SampleFormat::U16 => self.build_output_stream::<u16, _, _>(
274 &format.config(),
275 move |data, _| {
276 data.iter_mut().for_each(|d| {
277 *d = mixer_rx
278 .next()
279 .map(Sample::from_sample)
280 .unwrap_or(u16::MAX / 2)
281 })
282 },
283 error_callback,
284 None,
285 ),
286 cpal::SampleFormat::U32 => self.build_output_stream::<u32, _, _>(
287 &format.config(),
288 move |data, _| {
289 data.iter_mut().for_each(|d| {
290 *d = mixer_rx
291 .next()
292 .map(Sample::from_sample)
293 .unwrap_or(u32::MAX / 2)
294 })
295 },
296 error_callback,
297 None,
298 ),
299 cpal::SampleFormat::U64 => self.build_output_stream::<u64, _, _>(
300 &format.config(),
301 move |data, _| {
302 data.iter_mut().for_each(|d| {
303 *d = mixer_rx
304 .next()
305 .map(Sample::from_sample)
306 .unwrap_or(u64::MAX / 2)
307 })
308 },
309 error_callback,
310 None,
311 ),
312 _ => return Err(cpal::BuildStreamError::StreamConfigNotSupported),
313 }
314 .map(|stream| (mixer_tx, stream))
315 }
316
317 fn try_new_output_stream_config(
318 &self,
319 config: SupportedStreamConfig,
320 ) -> Result<(Arc<DynamicMixerController<f32>>, cpal::Stream), StreamError> {
321 self.new_output_stream_with_format(config).or_else(|err| {
322 supported_output_formats(self)?
324 .find_map(|format| self.new_output_stream_with_format(format).ok())
325 .ok_or(StreamError::BuildStreamError(err))
327 })
328 }
329}
330
331fn supported_output_formats(
333 device: &cpal::Device,
334) -> Result<impl Iterator<Item = cpal::SupportedStreamConfig>, StreamError> {
335 const HZ_44100: cpal::SampleRate = cpal::SampleRate(44_100);
336
337 let mut supported: Vec<_> = device
338 .supported_output_configs()
339 .map_err(StreamError::SupportedStreamConfigsError)?
340 .collect();
341 supported.sort_by(|a, b| b.cmp_default_heuristics(a));
342
343 Ok(supported.into_iter().flat_map(|sf| {
344 let max_rate = sf.max_sample_rate();
345 let min_rate = sf.min_sample_rate();
346 let mut formats = vec![sf.with_max_sample_rate()];
347 if HZ_44100 < max_rate && HZ_44100 > min_rate {
348 formats.push(sf.with_sample_rate(HZ_44100))
349 }
350 formats.push(sf.with_sample_rate(min_rate));
351 formats
352 }))
353}