spectrusty_audio/host/
cpal.rs

1/*
2    Copyright (C) 2020-2022  Rafal Michalski
3
4    This file is part of SPECTRUSTY, a Rust library for building emulators.
5
6    For the full copyright notice, see the lib.rs file.
7*/
8//! Audio device streaming implementation for [cpal](https://crates.io/crates/cpal).
9//!
10//! This module implements [carousel][crate::carousel] using the **cpal** audio layer.
11//!
12//! Requires "cpal" feature to be enabled.
13use core::convert::TryInto;
14use core::time::Duration;
15
16#[allow(unused_imports)]
17use log::{error, warn, info, debug, trace};
18
19use cpal::{
20    Stream,
21    PlayStreamError, PauseStreamError, DefaultStreamConfigError, BuildStreamError,
22    traits::{DeviceTrait, HostTrait, StreamTrait}
23};
24
25pub use cpal::SampleFormat;
26
27use spectrusty_core::audio::AudioSample;
28use crate::carousel::*;
29pub use super::{AudioHandleError, AudioHandleErrorKind};
30
31/// The struct for producing and controlling the audio playback.
32///
33/// It embeds the interconnected pair of [carousel][crate::carousel]'s [AudioFrameProducer] with the
34/// [AudioFrameConsumer] directly exposing the `producer` to the user. The consumer lives in the
35/// **cpal** audio thread and is responsible for filling up the audio output buffer with sample data
36/// sent from the `producer`.
37///
38/// The `T` parameter should be one of the [sample primitives][cpal::Sample].
39pub struct AudioHandle<T: cpal::Sample + AudioSample> {
40    /// The audio sample frequency of the output stream.
41    pub sample_rate: u32,
42    /// The number of audio channels in the output stream.
43    pub channels: u8,
44    /// The audio sample producer, interconnected with an audio consumer living in the audio thread.
45    pub producer: AudioFrameProducer<T>,
46    stream: Stream
47}
48
49/// The enum for producing and controlling the audio playback regardless of the sample format used.
50pub enum AudioHandleAnyFormat {
51    I16(AudioHandle<i16>),
52    U16(AudioHandle<u16>),
53    F32(AudioHandle<f32>),
54}
55
56impl AudioHandleAnyFormat {
57    /// Returns the sample format of the current variant.
58    pub fn sample_format(&self) -> SampleFormat {
59        use AudioHandleAnyFormat::*;
60        match self {
61            I16(..) => SampleFormat::I16,
62            U16(..) => SampleFormat::U16,
63            F32(..) => SampleFormat::F32,
64        }
65    }
66    /// Returns the audio sample frequency of the output stream.
67    pub fn sample_rate(&self) -> u32 {
68        use AudioHandleAnyFormat::*;
69        match self {
70            I16(audio) => audio.sample_rate,
71            U16(audio) => audio.sample_rate,
72            F32(audio) => audio.sample_rate,
73        }
74    }
75    /// Returns the number of audio channels in the output stream.
76    pub fn channels(&self) -> u8 {
77        use AudioHandleAnyFormat::*;
78        match self {
79            I16(audio) => audio.channels,
80            U16(audio) => audio.channels,
81            F32(audio) => audio.channels,
82        }
83    }
84    /// Starts playback of the audio device.
85    pub fn play(&self) -> Result<(), AudioHandleError> {
86        use AudioHandleAnyFormat::*;
87        match self {
88            I16(audio) => audio.play(),
89            U16(audio) => audio.play(),
90            F32(audio) => audio.play(),
91        }
92    }
93    /// Pauses playback of the audio device.
94    pub fn pause(&self) -> Result<(), AudioHandleError> {
95        use AudioHandleAnyFormat::*;
96        match self {
97            I16(audio) => audio.pause(),
98            U16(audio) => audio.pause(),
99            F32(audio) => audio.pause(),
100        }
101    }
102    /// Closes audio playback and frees underlying resources.
103    pub fn close(self) {}
104    /// Calls the underlying [AudioFrameProducer::send_frame].
105    pub fn send_frame(&mut self) -> AudioFrameResult<()> {
106        use AudioHandleAnyFormat::*;
107        match self {
108            I16(audio) => audio.producer.send_frame(),
109            U16(audio) => audio.producer.send_frame(),
110            F32(audio) => audio.producer.send_frame(),
111        }
112    }
113    /// Creates an instance of the [AudioHandleAnyFormat] from the provided **cpal** `host` with
114    /// the default output device and the default audio parameters.
115    ///
116    /// * `frame_duration_nanos` is the duration in nanoseconds of the standard emulation frame.
117    /// * `latency` is the audio latency passed to the [create_carousel].
118    pub fn create(
119            host: &cpal::Host,
120            frame_duration_nanos: u32,
121            latency: usize
122        ) -> Result<Self, AudioHandleError>
123    {
124        let device = host.default_output_device()
125                     .ok_or_else(|| ("no default output device".to_string(),
126                                    AudioHandleErrorKind::AudioSubsystem))?;
127        Self::create_with_device(&device, frame_duration_nanos, latency)
128    }
129    /// Creates an instance of the [AudioHandleAnyFormat] from the provided **cpal** `device`
130    /// with the default audio parameters.
131    ///
132    /// * `frame_duration_nanos` is the duration in nanoseconds of the standard emulation frame.
133    /// * `latency` is the audio latency passed to the [create_carousel].
134    pub fn create_with_device(
135            device: &cpal::Device,
136            frame_duration_nanos: u32,
137            latency: usize
138        ) -> Result<Self, AudioHandleError>
139    {
140        let config = device.default_output_config()?.config();
141        Self::create_with_device_and_config(
142            device,
143            &config,
144            frame_duration_nanos,
145            latency,
146        )
147    }
148    /// Creates an instance of the [AudioHandleAnyFormat] from the provided **cpal** `device`
149    /// with the desired audio parameters and sample format.
150    ///
151    /// * `config` specifies the desired audio parameters.
152    /// * `frame_duration_nanos` is the duration in nanoseconds of the standard emulation frame.
153    /// * `latency` is the audio latency passed to the [create_carousel].
154    pub fn create_with_device_and_config(
155            device: &cpal::Device,
156            config: &cpal::StreamConfig,
157            frame_duration_nanos: u32,
158            latency: usize,
159        ) -> Result<Self, AudioHandleError>
160    {
161        Ok(match device.default_output_config()?.sample_format() {
162            SampleFormat::I16 => AudioHandleAnyFormat::I16(
163                AudioHandle::<i16>::create_with_device_and_config(device, config, frame_duration_nanos, latency)?
164            ),
165            SampleFormat::U16 => AudioHandleAnyFormat::U16(
166                AudioHandle::<u16>::create_with_device_and_config(device, config, frame_duration_nanos, latency)?
167            ),
168            SampleFormat::F32 => AudioHandleAnyFormat::F32(
169                AudioHandle::<f32>::create_with_device_and_config(device, config, frame_duration_nanos, latency)?
170            )
171        })
172    }
173}
174
175impl<T: cpal::Sample + AudioSample> AudioHandle<T> {
176    /// Starts playback of the audio device.
177    pub fn play(&self) -> Result<(), AudioHandleError> {
178        self.stream.play().map_err(From::from)
179    }
180    /// Pauses playback of the audio device.
181    pub fn pause(&self) -> Result<(), AudioHandleError> {
182        self.stream.pause().map_err(From::from)
183    }
184    /// Closes audio playback and frees underlying resources.
185    pub fn close(self) {}
186    /// Creates an instance of the [AudioHandle] from the provided **cpal** `device` with the
187    /// desired audio parameters.
188    ///
189    /// * `config` specifies the desired audio parameters.
190    /// * `frame_duration_nanos` is the duration in nanoseconds of the standard emulation frame.
191    /// * `latency` is the audio latency passed to the [create_carousel].
192    pub fn create_with_device_and_config(
193            device: &cpal::Device,
194            config: &cpal::StreamConfig,
195            frame_duration_nanos: u32,
196            latency: usize,
197        ) -> Result<Self, AudioHandleError>
198    {
199        let channels: u8 = config.channels.try_into()
200                           .map_err(|_| (format!("number of channels: {} exceed the maximum value of 255", config.channels),
201                                         AudioHandleErrorKind::InvalidArguments))?;
202        let sample_rate = config.sample_rate.0;
203
204        let frame_duration_secs = Duration::from_nanos(frame_duration_nanos.into()).as_secs_f64();
205        let audio_frame_samples = (sample_rate as f64 * frame_duration_secs).ceil() as usize;
206        debug!("audio specs: {:?}", config);
207        debug!("audio frame samples: {} latency: {}", audio_frame_samples, latency);
208        let (producer, mut consumer) = create_carousel::<T>(latency, audio_frame_samples, channels);
209
210        let data_fn = move |out: &mut [T], _: &_| match consumer.fill_buffer(out, false) {
211            Ok(unfilled) => {
212                if !unfilled.is_empty() {
213                    for t in unfilled {
214                        *t = T::silence()
215                    }
216                    debug!("missing buffer");
217                }
218            }
219            Err(_) => {
220                error!("fatal: producer terminated");
221            }
222        };
223
224        let err_fn = |err| error!("an error occurred on stream: {}", err);
225
226        let stream = device.build_output_stream(config, data_fn, err_fn)?;
227
228        Ok(AudioHandle {
229            sample_rate,
230            channels,
231            producer,
232            stream
233        })
234    }
235}
236
237impl From<PlayStreamError> for AudioHandleError {
238    fn from(e: PlayStreamError) -> Self {
239        let kind = match e {
240            PlayStreamError::DeviceNotAvailable => AudioHandleErrorKind::AudioSubsystem,
241            _ => AudioHandleErrorKind::AudioStream
242        };
243        (e.to_string(), kind).into()
244    }
245}
246
247impl From<PauseStreamError> for AudioHandleError {
248    fn from(e: PauseStreamError) -> Self {
249        let kind = match e {
250            PauseStreamError::DeviceNotAvailable => AudioHandleErrorKind::AudioSubsystem,
251            _ => AudioHandleErrorKind::AudioStream
252        };
253        (e.to_string(), kind).into()
254    }
255}
256
257impl From<DefaultStreamConfigError> for AudioHandleError {
258    fn from(e: DefaultStreamConfigError) -> Self {
259        let kind = match e {
260            DefaultStreamConfigError::StreamTypeNotSupported => AudioHandleErrorKind::InvalidArguments,
261            _ => AudioHandleErrorKind::AudioSubsystem
262        };
263        (e.to_string(), kind).into()
264    }
265}
266
267impl From<BuildStreamError> for AudioHandleError {
268    fn from(e: BuildStreamError) -> Self {
269        let kind = match e {
270            BuildStreamError::DeviceNotAvailable => AudioHandleErrorKind::AudioSubsystem,
271            BuildStreamError::StreamConfigNotSupported|
272            BuildStreamError::InvalidArgument => AudioHandleErrorKind::InvalidArguments,
273            _ => AudioHandleErrorKind::AudioStream
274        };
275        (e.to_string(), kind).into()
276    }
277}