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}