dynwave/
lib.rs

1//! Dynamic audio player based on fixed samples stream
2//!
3//! This crate provides a dynamic audio player that can play audio samples stream coming
4//! from an external generating source, such as an emulator.
5//!
6//! The [`AudioPlayer`] acts as an audio stream player that will play the samples as they come.
7//! And will resample the audio if the generated sample rate is not supported by the audio device,
8//!
9//! # Supported sample types
10//! For now, we rely on [`rubato`] crate for resampling, it has the trait [`Sample`] that is implemented for:
11//! - [`f32`]
12//! - [`f64`]
13//!
14//! # Example
15//!
16//! Here's an example of how to use the `AudioPlayer`:
17//! ```rust,no_run
18//! # use dynwave::{AudioPlayer, BufferSize};
19//! // create a buffer, that can hold 1 second worth of samples
20//! // (base it depend on how fast you generate samples, less buffer is better for latency)
21//! let mut player = AudioPlayer::<f32>::new(44100, BufferSize::OneSecond).unwrap();
22//!
23//! // Start playing the audio
24//! player.play().unwrap();
25//!
26//! // generate audio samples (can be done in a emulation loop for example)
27//! let samples = generate_samples();
28//! player.queue(&samples);
29//!
30//! // pause the audio
31//! player.pause().unwrap();
32//!
33//! # fn generate_samples() -> Vec<f32> {
34//! #     vec![0.0; 1]
35//! # }
36//! ```
37pub mod error;
38mod utils;
39
40use cpal::{
41    traits::{DeviceTrait, HostTrait, StreamTrait},
42    FromSample, SizedSample,
43};
44use error::{AudioPlayerError, PlayError};
45use ringbuf::{
46    traits::{Producer, Split},
47    HeapProd, HeapRb,
48};
49use rubato::{FftFixedInOut, Resampler, Sample};
50
51struct AudioResampler<T: Sample> {
52    resampler: FftFixedInOut<T>,
53    pre_resampled_buffer: Vec<T>,
54    pre_resampled_split_buffers: [Vec<T>; 2],
55    resample_process_buffers: [Vec<T>; 2],
56    resampled_buffer: Vec<T>,
57}
58
59impl<T: Sample + SizedSample> AudioResampler<T> {
60    fn new(input_rate: usize, output_rate: usize) -> Result<Self, AudioPlayerError> {
61        let resampler = FftFixedInOut::<T>::new(
62            input_rate,
63            output_rate,
64            // the number of samples for one video frame in 60 FPS
65            input_rate / 60,
66            2,
67        )?;
68
69        Ok(Self {
70            resampler,
71            pre_resampled_buffer: Vec::new(),
72            pre_resampled_split_buffers: [Vec::new(), Vec::new()],
73            resample_process_buffers: [Vec::new(), Vec::new()],
74            resampled_buffer: Vec::new(),
75        })
76    }
77
78    fn resample_into_producer(&mut self, data: &[T], producer: &mut HeapProd<T>) {
79        // helper method to split channels into separate vectors
80        fn read_frames<T: Copy>(inbuffer: &[T], n_frames: usize, outputs: &mut [Vec<T>]) {
81            for output in outputs.iter_mut() {
82                output.clear();
83                output.reserve(n_frames);
84            }
85            let mut value: T;
86            let mut inbuffer_iter = inbuffer.iter();
87            for _ in 0..n_frames {
88                for output in outputs.iter_mut() {
89                    value = *inbuffer_iter.next().unwrap();
90                    output.push(value);
91                }
92            }
93        }
94
95        /// Helper to merge channels into a single vector
96        /// the number of channels is the size of `waves` slice
97        fn write_frames<T: Copy>(waves: &[Vec<T>], outbuffer: &mut Vec<T>) {
98            let nbr = waves[0].len();
99            for frame in 0..nbr {
100                for wave in waves.iter() {
101                    outbuffer.push(wave[frame]);
102                }
103            }
104        }
105
106        self.pre_resampled_buffer.extend_from_slice(data);
107        // finish all the frames, as sometimes after appending many data
108        // we might get 2 loops worth of unprocessed audio
109        loop {
110            let frames = self.resampler.input_frames_next();
111
112            if self.pre_resampled_buffer.len() < frames * 2 {
113                return;
114            }
115
116            // only read the needed frames
117            read_frames(
118                &self.pre_resampled_buffer,
119                frames,
120                &mut self.pre_resampled_split_buffers,
121            );
122
123            self.resample_process_buffers[0].clear();
124            self.resample_process_buffers[0].clear();
125
126            let output_frames = self.resampler.output_frames_next();
127            self.resample_process_buffers[0].resize(output_frames, T::EQUILIBRIUM);
128            self.resample_process_buffers[1].resize(output_frames, T::EQUILIBRIUM);
129
130            self.resampler
131                .process_into_buffer(
132                    &self.pre_resampled_split_buffers,
133                    &mut self.resample_process_buffers,
134                    None,
135                )
136                .unwrap();
137
138            // resample
139            if self.resampled_buffer.len() < output_frames * 2 {
140                self.resampled_buffer
141                    .reserve(output_frames * 2 - self.resampled_buffer.len());
142            }
143            self.resampled_buffer.clear();
144            write_frames(&self.resample_process_buffers, &mut self.resampled_buffer);
145
146            producer.push_slice(&self.resampled_buffer);
147
148            self.pre_resampled_buffer = self.pre_resampled_buffer.split_off(frames * 2);
149        }
150    }
151}
152
153/// The `BufferSize` enum represents the amount of audio samples that can be stored in the buffer.
154/// Limiting the number of samples in the buffer is crucial for minimizing audio delay in audio playing.
155///
156/// We will use `emulation` as an example to refer to the process of generating audio samples.
157///
158/// minimizing the buffer size will help minimize audio delay such as audio coming from an emulator.
159/// This is due to the fact that emulation speed does not always perfectly
160/// match the audio playing speed (e.g., 44100Hz).
161///
162/// A smaller buffer size can help maintain better synchronization,
163/// but it may cause noise or other issues on slower machines.
164/// This can occur if the emulation process is slow, or if a CPU-intensive
165/// process starts while the emulator is running.
166#[derive(Debug, Clone, Copy, Default)]
167pub enum BufferSize {
168    #[default]
169    /// 1/4 second worth of samples
170    QuarterSecond,
171    /// 1/2 second worth of samples
172    HalfSecond,
173    /// 1 second worth of samples
174    OneSecond,
175    /// Number of samples to store
176    /// Be careful, here you have to calculate based on the sample rate manually
177    Samples(usize),
178}
179
180impl BufferSize {
181    /// Returns the number of samples in the buffer
182    #[inline]
183    #[must_use]
184    fn store_for_samples(&self, sample_rate: usize, channels: usize) -> usize {
185        match self {
186            Self::QuarterSecond => sample_rate / 4 * channels,
187            Self::HalfSecond => sample_rate / 2 * channels,
188            Self::OneSecond => sample_rate * channels,
189            Self::Samples(alternative_samples) => *alternative_samples,
190        }
191    }
192}
193
194/// The `AudioPlayer` struct represents an audio player that can play audio samples stream
195/// coming from an external generating source, such as an emulator.
196///
197/// The `AudioPlayer` may resample the audio if the generated sample rate is not supported by the audio device,
198/// which may cause a slight performance hit due to the resampling process. If the machine supports the input sample rate,
199/// no resampling will be done, and the audio samples will be used as is.
200///
201/// # Example
202///
203/// Here's an example of how to use the `AudioPlayer`:
204/// ```rust,no_run
205/// # use dynwave::{AudioPlayer, BufferSize};
206/// // create a buffer, that can hold 1 second worth of samples
207/// // (base it depend on how fast you generate samples, less buffer is better for latency)
208/// let mut player = AudioPlayer::<f32>::new(44100, BufferSize::OneSecond).unwrap();
209///
210/// // Start playing the audio
211/// player.play().unwrap();
212///
213/// // generate audio samples (can be done in a emulation loop for example)
214/// let samples = generate_samples();
215/// player.queue(&samples);
216///
217/// // pause the audio
218/// player.pause().unwrap();
219///
220/// # fn generate_samples() -> Vec<f32> {
221/// #     vec![0.0; 1]
222/// # }
223/// ```
224pub struct AudioPlayer<T: Sample> {
225    buffer_producer: HeapProd<T>,
226    resampler: Option<AudioResampler<T>>,
227    output_stream: cpal::Stream,
228}
229
230impl<T: Sample + SizedSample> AudioPlayer<T>
231where
232    // sadly, cpal uses macro to generate those, and there is no auto way
233    // to use the type system to, even though it seems that it makes sense
234    // to have `T : FromSample<W> where W: SizedSample`?
235    i8: FromSample<T>,
236    i16: FromSample<T>,
237    i32: FromSample<T>,
238    i64: FromSample<T>,
239    u8: FromSample<T>,
240    u16: FromSample<T>,
241    u32: FromSample<T>,
242    u64: FromSample<T>,
243    f32: FromSample<T>,
244    f64: FromSample<T>,
245{
246    /// Creates a new instance of `AudioPlayer`.
247    ///
248    /// # Parameters
249    /// * `sample_rate`: The sample rate of the audio player in Hz. Common values are `44100` or `48000`.
250    /// * `buffer_size`: The size of the buffer that will store the audio samples. See [`BufferSize`] for options.
251    ///
252    /// # Returns
253    /// Might return an `Error` if:
254    /// - No output device is found
255    /// - The output device does not support dual channel
256    /// - Some error happened with the device backend
257    /// - Could not create the audio stream
258    ///
259    /// Check [`AudioPlayerError`] for more information about the possible errors.
260    ///
261    /// # Example
262    ///
263    /// ```rust,no_run
264    /// # use dynwave::{AudioPlayer, BufferSize};
265    /// let sample_rate = 44100;
266    /// let buffer_size = BufferSize::HalfSecond;
267    /// let player = AudioPlayer::<f32>::new(sample_rate, buffer_size).unwrap();
268    /// ```
269    ///
270    /// This example creates a new `AudioPlayer` with a sample rate of 44100 Hz and a buffer size of half a second.
271    pub fn new(sample_rate: u32, buffer_size: BufferSize) -> Result<Self, AudioPlayerError> {
272        let host = cpal::default_host();
273        let output_device = host
274            .default_output_device()
275            .ok_or(AudioPlayerError::NoOutputDevice)?;
276
277        let sample_rate = cpal::SampleRate(sample_rate);
278
279        let conf = output_device
280            .supported_output_configs()?
281            .collect::<Vec<_>>();
282
283        let mut found_conf = false;
284
285        for c in &conf {
286            // must have 2 channels and <T> format
287            // (almost all? devices will have at least one configuration with these)
288            if c.channels() == 2
289                && c.sample_format() == T::FORMAT
290                && c.min_sample_rate() <= sample_rate
291                && c.max_sample_rate() >= sample_rate
292            {
293                found_conf = true;
294                break;
295            }
296        }
297
298        let (output_sample_rate, output_format, resampler) = if found_conf {
299            (sample_rate, T::FORMAT, None)
300        } else {
301            // second time, try to find something that is 2 channels, but format and sample range can
302            // be different, match with highest value
303            let mut max_match = 0;
304            let mut matched_conf = None;
305            for c in &conf {
306                let mut curr_match = 0;
307                if c.channels() == 2 {
308                    curr_match += 1;
309                    if c.sample_format() == T::FORMAT {
310                        curr_match += 3;
311                    }
312                    if c.min_sample_rate() <= sample_rate && c.max_sample_rate() >= sample_rate {
313                        curr_match += 2;
314                    }
315                }
316                if curr_match > max_match {
317                    max_match = curr_match;
318                    matched_conf = Some(c);
319                }
320            }
321
322            let used_conf = match matched_conf {
323                Some(conf) => conf
324                    .try_with_sample_rate(sample_rate)
325                    .unwrap_or_else(|| conf.with_max_sample_rate()),
326                None => output_device.default_output_config()?,
327            };
328
329            if used_conf.channels() != 2 {
330                eprintln!("No supported configuration found for audio device, please open an issue in github `Amjad50/dynwave`\n\
331                      list of supported configurations: {:#?}", conf);
332                return Err(AudioPlayerError::DualChannelNotSupported);
333            }
334
335            (
336                used_conf.sample_rate(),
337                used_conf.sample_format(),
338                Some(AudioResampler::new(
339                    sample_rate.0 as usize,
340                    used_conf.sample_rate().0 as usize,
341                )?),
342            )
343        };
344
345        let config = cpal::StreamConfig {
346            channels: 2,
347            sample_rate: output_sample_rate,
348            buffer_size: cpal::BufferSize::Default,
349        };
350
351        let ring_buffer_len = buffer_size.store_for_samples(output_sample_rate.0 as usize, 2);
352        let buffer = HeapRb::new(ring_buffer_len);
353        let (buffer_producer, buffer_consumer) = buffer.split();
354
355        let output_data_fn = utils::create_output_processor(output_format, buffer_consumer);
356
357        let output_stream = output_device.build_output_stream_raw(
358            &config,
359            output_format,
360            output_data_fn,
361            Self::err_fn,
362            None,
363        )?;
364
365        Ok(Self {
366            buffer_producer,
367            output_stream,
368            resampler,
369        })
370    }
371
372    /// Start the player
373    ///
374    /// If the player is playing and if the buffer is emptied (played until finished without adding more data), popping sound might be heard.
375    ///
376    /// Might return an `Error` if:
377    /// - The device associated with the stream is no longer available
378    /// - Some error happened with the device backend
379    ///
380    /// Check [`PlayError`] for more information about the possible errors.
381    pub fn play(&self) -> Result<(), PlayError> {
382        self.output_stream.play().map_err(|e| e.into())
383    }
384
385    /// Pause the player
386    ///
387    /// Might return an `Error` if:
388    /// - The device associated with the stream is no longer available
389    /// - Some error happened with the device backend
390    ///
391    /// Check [`PlayError`] for more information about the possible errors.
392    pub fn pause(&self) -> Result<(), PlayError> {
393        self.output_stream.pause().map_err(|e| e.into())
394    }
395
396    /// Queues audio samples to be played.
397    ///
398    /// The `queue` function takes a slice of audio samples and adds them to the buffer. If a `resampler` is present,
399    /// it resamples the audio data before adding it to the buffer.
400    ///
401    /// If the buffer is full, the function will drop the audio samples that don't fit in the buffer and won't block.
402    ///
403    /// If the player is playing, the audio samples will be played immediately, and if the buffer is emptied, popping sound might be heard.
404    ///
405    /// # Parameters
406    /// * `data`: A slice of audio samples to be played.
407    ///
408    /// # Example
409    /// ```rust,no_run
410    /// # use dynwave::{AudioPlayer, BufferSize};
411    /// let sample_rate = 44100;
412    /// let buffer_size = BufferSize::HalfSecond;
413    /// let mut player = AudioPlayer::new(sample_rate, buffer_size).unwrap();
414    /// let samples = vec![0.5, 0.7, 0.9, 1.0, 0.9, 0.7, 0.5, 0.3, 0.1];
415    /// player.queue(&samples);
416    /// ```
417    /// This example creates a new `AudioPlayer` with a sample rate of 44100 Hz and a buffer size of half a second, queues some audio samples, and then starts playing the audio.
418    pub fn queue(&mut self, data: &[T]) {
419        if let Some(resampler) = &mut self.resampler {
420            resampler.resample_into_producer(data, &mut self.buffer_producer);
421        } else {
422            // no resampling
423            self.buffer_producer.push_slice(data);
424        }
425    }
426
427    fn err_fn(err: cpal::StreamError) {
428        eprintln!("an error occurred on audio stream: {}", err);
429    }
430}