Path: blob/main/common/audio_streams/src/audio_streams.rs
5394 views
// Copyright 2019 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34//! Provides an interface for playing and recording audio.5//!6//! When implementing an audio playback system, the `StreamSource` trait is implemented.7//! Implementors of this trait allow creation of `PlaybackBufferStream` objects. The8//! `PlaybackBufferStream` provides the actual audio buffers to be filled with audio samples. These9//! buffers can be filled with `write_playback_buffer`.10//!11//! Users playing audio fill the provided buffers with audio. When a `PlaybackBuffer` is dropped,12//! the samples written to it are committed to the `PlaybackBufferStream` it came from.13//!14//! ```15//! use audio_streams::{BoxError, PlaybackBuffer, SampleFormat, StreamSource, NoopStreamSource};16//! use std::io::Write;17//!18//! const buffer_size: usize = 120;19//! const num_channels: usize = 2;20//!21//! # fn main() -> std::result::Result<(), BoxError> {22//! let mut stream_source = NoopStreamSource::new();23//! let sample_format = SampleFormat::S16LE;24//! let frame_size = num_channels * sample_format.sample_bytes();25//!26//! let (_, mut stream) = stream_source27//! .new_playback_stream(num_channels, sample_format, 48000, buffer_size)?;28//! // Play 10 buffers of DC.29//! let mut buf = Vec::new();30//! buf.resize(buffer_size * frame_size, 0xa5u8);31//! for _ in 0..10 {32//! let mut copy_cb = |stream_buffer: &mut PlaybackBuffer| {33//! assert_eq!(stream_buffer.write(&buf)?, buffer_size * frame_size);34//! Ok(())35//! };36//! stream.write_playback_buffer(&mut copy_cb)?;37//! }38//! # Ok (())39//! # }40//! ```41pub mod async_api;4243use std::cmp::min;44use std::error;45use std::fmt;46use std::fmt::Display;47use std::io;48use std::io::Read;49use std::io::Write;50#[cfg(unix)]51use std::os::unix::io::RawFd as RawDescriptor;52#[cfg(windows)]53use std::os::windows::io::RawHandle as RawDescriptor;54use std::result::Result;55use std::str::FromStr;56use std::time::Duration;57use std::time::Instant;5859pub use async_api::AsyncStream;60pub use async_api::AudioStreamsExecutor;61use async_trait::async_trait;62use remain::sorted;63use serde::Deserialize;64use serde::Serialize;65use thiserror::Error;6667#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]68pub enum SampleFormat {69U8,70S16LE,71S24LE,72S32LE,73}7475impl SampleFormat {76pub fn sample_bytes(self) -> usize {77use SampleFormat::*;78match self {79U8 => 1,80S16LE => 2,81S24LE => 4, // Not a typo, S24_LE samples are stored in 4 byte chunks.82S32LE => 4,83}84}85}8687impl Display for SampleFormat {88fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {89use SampleFormat::*;90match self {91U8 => write!(f, "Unsigned 8 bit"),92S16LE => write!(f, "Signed 16 bit Little Endian"),93S24LE => write!(f, "Signed 24 bit Little Endian"),94S32LE => write!(f, "Signed 32 bit Little Endian"),95}96}97}9899impl FromStr for SampleFormat {100type Err = SampleFormatError;101fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {102match s {103"U8" => Ok(SampleFormat::U8),104"S16_LE" => Ok(SampleFormat::S16LE),105"S24_LE" => Ok(SampleFormat::S24LE),106"S32_LE" => Ok(SampleFormat::S32LE),107_ => Err(SampleFormatError::InvalidSampleFormat),108}109}110}111112/// Errors that are possible from a `SampleFormat`.113#[sorted]114#[derive(Error, Debug)]115pub enum SampleFormatError {116#[error("Must be in [U8, S16_LE, S24_LE, S32_LE]")]117InvalidSampleFormat,118}119120/// Valid directions of an audio stream.121#[derive(Copy, Clone, Debug, PartialEq, Eq)]122pub enum StreamDirection {123Playback,124Capture,125}126127/// Valid effects for an audio stream.128#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]129pub enum StreamEffect {130#[default]131NoEffect,132#[serde(alias = "aec")]133EchoCancellation,134}135136pub mod capture;137pub mod shm_streams;138139/// Errors that can pass across threads.140pub type BoxError = Box<dyn error::Error + Send + Sync>;141142/// Errors that are possible from a `StreamEffect`.143#[sorted]144#[derive(Error, Debug)]145pub enum StreamEffectError {146#[error("Must be in [EchoCancellation, aec]")]147InvalidEffect,148}149150impl FromStr for StreamEffect {151type Err = StreamEffectError;152fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {153match s {154"EchoCancellation" | "aec" => Ok(StreamEffect::EchoCancellation),155_ => Err(StreamEffectError::InvalidEffect),156}157}158}159160#[sorted]161#[derive(Error, Debug)]162pub enum Error {163#[error("Unimplemented")]164Unimplemented,165}166167/// `StreamSourceGenerator` is a trait used to abstract types that create [`StreamSource`].168/// It can be used when multiple types of `StreamSource` are needed.169pub trait StreamSourceGenerator: Sync + Send {170fn generate(&self) -> Result<Box<dyn StreamSource>, BoxError>;171}172173/// `StreamSource` creates streams for playback or capture of audio.174#[async_trait(?Send)]175pub trait StreamSource: Send {176/// Returns a stream control and buffer generator object. These are separate as the buffer177/// generator might want to be passed to the audio stream.178#[allow(clippy::type_complexity)]179fn new_playback_stream(180&mut self,181num_channels: usize,182format: SampleFormat,183frame_rate: u32,184buffer_size: usize,185) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>;186187/// Returns a stream control and async buffer generator object. These are separate as the buffer188/// generator might want to be passed to the audio stream.189#[allow(clippy::type_complexity)]190fn new_async_playback_stream(191&mut self,192_num_channels: usize,193_format: SampleFormat,194_frame_rate: u32,195_buffer_size: usize,196_ex: &dyn AudioStreamsExecutor,197) -> Result<(Box<dyn StreamControl>, Box<dyn AsyncPlaybackBufferStream>), BoxError> {198Err(Box::new(Error::Unimplemented))199}200201/// Returns a stream control and async buffer generator object asynchronously.202/// Default implementation calls and blocks on `new_async_playback_stream()`.203#[allow(clippy::type_complexity)]204async fn async_new_async_playback_stream(205&mut self,206num_channels: usize,207format: SampleFormat,208frame_rate: u32,209buffer_size: usize,210ex: &dyn AudioStreamsExecutor,211) -> Result<(Box<dyn StreamControl>, Box<dyn AsyncPlaybackBufferStream>), BoxError> {212self.new_async_playback_stream(num_channels, format, frame_rate, buffer_size, ex)213}214215/// Returns a stream control and buffer generator object. These are separate as the buffer216/// generator might want to be passed to the audio stream.217/// Default implementation returns `NoopStreamControl` and `NoopCaptureStream`.218#[allow(clippy::type_complexity)]219fn new_capture_stream(220&mut self,221num_channels: usize,222format: SampleFormat,223frame_rate: u32,224buffer_size: usize,225_effects: &[StreamEffect],226) -> Result<227(228Box<dyn StreamControl>,229Box<dyn capture::CaptureBufferStream>,230),231BoxError,232> {233Ok((234Box::new(NoopStreamControl::new()),235Box::new(capture::NoopCaptureStream::new(236num_channels,237format,238frame_rate,239buffer_size,240)),241))242}243244/// Returns a stream control and async buffer generator object. These are separate as the buffer245/// generator might want to be passed to the audio stream.246/// Default implementation returns `NoopStreamControl` and `NoopCaptureStream`.247#[allow(clippy::type_complexity)]248fn new_async_capture_stream(249&mut self,250num_channels: usize,251format: SampleFormat,252frame_rate: u32,253buffer_size: usize,254_effects: &[StreamEffect],255_ex: &dyn AudioStreamsExecutor,256) -> Result<257(258Box<dyn StreamControl>,259Box<dyn capture::AsyncCaptureBufferStream>,260),261BoxError,262> {263Ok((264Box::new(NoopStreamControl::new()),265Box::new(capture::NoopCaptureStream::new(266num_channels,267format,268frame_rate,269buffer_size,270)),271))272}273274/// Returns a stream control and async buffer generator object asynchronously.275/// Default implementation calls and blocks on `new_async_capture_stream()`.276#[allow(clippy::type_complexity)]277async fn async_new_async_capture_stream(278&mut self,279num_channels: usize,280format: SampleFormat,281frame_rate: u32,282buffer_size: usize,283effects: &[StreamEffect],284ex: &dyn AudioStreamsExecutor,285) -> Result<286(287Box<dyn StreamControl>,288Box<dyn capture::AsyncCaptureBufferStream>,289),290BoxError,291> {292self.new_async_capture_stream(num_channels, format, frame_rate, buffer_size, effects, ex)293}294295/// Returns any open file descriptors needed by the implementor. The FD list helps users of the296/// StreamSource enter Linux jails making sure not to close needed FDs.297fn keep_rds(&self) -> Option<Vec<RawDescriptor>> {298None299}300}301302/// `PlaybackBufferStream` provides `PlaybackBuffer`s to fill with audio samples for playback.303pub trait PlaybackBufferStream: Send {304fn next_playback_buffer<'b, 's: 'b>(&'s mut self) -> Result<PlaybackBuffer<'b>, BoxError>;305306/// Call `f` with a `PlaybackBuffer`, and trigger the buffer done call back after. `f` should307/// write playback data to the given `PlaybackBuffer`.308fn write_playback_buffer<'b, 's: 'b>(309&'s mut self,310f: &mut dyn FnMut(&mut PlaybackBuffer<'b>) -> Result<(), BoxError>,311) -> Result<(), BoxError> {312let mut buf = self.next_playback_buffer()?;313f(&mut buf)?;314buf.commit();315Ok(())316}317}318319impl<S: PlaybackBufferStream + ?Sized> PlaybackBufferStream for &mut S {320fn next_playback_buffer<'b, 's: 'b>(&'s mut self) -> Result<PlaybackBuffer<'b>, BoxError> {321(**self).next_playback_buffer()322}323}324325/// `PlaybackBufferStream` provides `PlaybackBuffer`s asynchronously to fill with audio samples for326/// playback.327#[async_trait(?Send)]328pub trait AsyncPlaybackBufferStream: Send {329async fn next_playback_buffer<'a>(330&'a mut self,331_ex: &dyn AudioStreamsExecutor,332) -> Result<AsyncPlaybackBuffer<'a>, BoxError>;333}334335#[async_trait(?Send)]336impl<S: AsyncPlaybackBufferStream + ?Sized> AsyncPlaybackBufferStream for &mut S {337async fn next_playback_buffer<'a>(338&'a mut self,339ex: &dyn AudioStreamsExecutor,340) -> Result<AsyncPlaybackBuffer<'a>, BoxError> {341(**self).next_playback_buffer(ex).await342}343}344345/// Call `f` with a `AsyncPlaybackBuffer`, and trigger the buffer done call back after. `f` should346/// write playback data to the given `AsyncPlaybackBuffer`.347///348/// This cannot be a trait method because trait methods with generic parameters are not object safe.349pub async fn async_write_playback_buffer<F>(350stream: &mut dyn AsyncPlaybackBufferStream,351f: F,352ex: &dyn AudioStreamsExecutor,353) -> Result<(), BoxError>354where355F: for<'a> FnOnce(&'a mut AsyncPlaybackBuffer) -> Result<(), BoxError>,356{357let mut buf = stream.next_playback_buffer(ex).await?;358f(&mut buf)?;359buf.commit().await;360Ok(())361}362363/// `StreamControl` provides a way to set the volume and mute states of a stream. `StreamControl`364/// is separate from the stream so it can be owned by a different thread if needed.365pub trait StreamControl: Send + Sync {366fn set_volume(&mut self, _scaler: f64) {}367fn set_mute(&mut self, _mute: bool) {}368}369370/// `BufferCommit` is a cleanup funcion that must be called before dropping the buffer,371/// allowing arbitrary code to be run after the buffer is filled or read by the user.372pub trait BufferCommit {373/// `write_playback_buffer` or `read_capture_buffer` would trigger this automatically. `nframes`374/// indicates the number of audio frames that were read or written to the device.375fn commit(&mut self, nframes: usize);376/// `latency_bytes` the current device latency.377/// For playback it means how many bytes need to be consumed378/// before the current playback buffer will be played.379/// For capture it means the latency in terms of bytes that the capture buffer was recorded.380fn latency_bytes(&self) -> u32 {3810382}383}384385/// `AsyncBufferCommit` is a cleanup funcion that must be called before dropping the buffer,386/// allowing arbitrary code to be run after the buffer is filled or read by the user.387#[async_trait(?Send)]388pub trait AsyncBufferCommit {389/// `async_write_playback_buffer` or `async_read_capture_buffer` would trigger this390/// automatically. `nframes` indicates the number of audio frames that were read or written to391/// the device.392async fn commit(&mut self, nframes: usize);393/// `latency_bytes` the current device latency.394/// For playback it means how many bytes need to be consumed395/// before the current playback buffer will be played.396/// For capture it means the latency in terms of bytes that the capture buffer was recorded.397fn latency_bytes(&self) -> u32 {3980399}400}401402/// Errors that are possible from a `PlaybackBuffer`.403#[sorted]404#[derive(Error, Debug)]405pub enum PlaybackBufferError {406#[error("Invalid buffer length")]407InvalidLength,408#[error("Slicing of playback buffer out of bounds")]409SliceOutOfBounds,410}411412/// `AudioBuffer` is one buffer that holds buffer_size audio frames.413/// It is the inner data of `PlaybackBuffer` and `CaptureBuffer`.414struct AudioBuffer<'a> {415buffer: &'a mut [u8],416offset: usize, // Read or Write offset in frames.417frame_size: usize, // Size of a frame in bytes.418}419420impl AudioBuffer<'_> {421/// Returns the number of audio frames that fit in the buffer.422pub fn frame_capacity(&self) -> usize {423self.buffer.len() / self.frame_size424}425426fn calc_len(&self, size: usize) -> usize {427min(428size / self.frame_size * self.frame_size,429self.buffer.len() - self.offset,430)431}432433/// Writes up to `size` bytes directly to this buffer inside of the given callback function.434pub fn write_copy_cb<F: FnOnce(&mut [u8])>(&mut self, size: usize, cb: F) -> io::Result<usize> {435// only write complete frames.436let len = self.calc_len(size);437cb(&mut self.buffer[self.offset..(self.offset + len)]);438self.offset += len;439Ok(len)440}441442/// Reads up to `size` bytes directly from this buffer inside of the given callback function.443pub fn read_copy_cb<F: FnOnce(&[u8])>(&mut self, size: usize, cb: F) -> io::Result<usize> {444let len = self.calc_len(size);445cb(&self.buffer[self.offset..(self.offset + len)]);446self.offset += len;447Ok(len)448}449450/// Copy data from an io::Reader451pub fn copy_from(&mut self, reader: &mut dyn Read) -> io::Result<usize> {452let bytes = reader.read(&mut self.buffer[self.offset..])?;453self.offset += bytes;454Ok(bytes)455}456457/// Copy data to an io::Write458pub fn copy_to(&mut self, writer: &mut dyn Write) -> io::Result<usize> {459let bytes = writer.write(&self.buffer[self.offset..])?;460self.offset += bytes;461Ok(bytes)462}463}464465impl Write for AudioBuffer<'_> {466fn write(&mut self, buf: &[u8]) -> io::Result<usize> {467let written = (&mut self.buffer[self.offset..]).write(&buf[..buf.len()])?;468self.offset += written;469Ok(written)470}471472fn flush(&mut self) -> io::Result<()> {473Ok(())474}475}476477impl Read for AudioBuffer<'_> {478fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {479let len = buf.len() / self.frame_size * self.frame_size;480let written = (&mut buf[..len]).write(&self.buffer[self.offset..])?;481self.offset += written;482Ok(written)483}484}485486/// `PlaybackBuffer` is one buffer that holds buffer_size audio frames. It is used to temporarily487/// allow access to an audio buffer and notifes the owning stream of write completion when dropped.488pub struct PlaybackBuffer<'a> {489buffer: AudioBuffer<'a>,490drop: &'a mut dyn BufferCommit,491}492493impl<'a> PlaybackBuffer<'a> {494/// Creates a new `PlaybackBuffer` that holds a reference to the backing memory specified in495/// `buffer`.496pub fn new<F>(497frame_size: usize,498buffer: &'a mut [u8],499drop: &'a mut F,500) -> Result<Self, PlaybackBufferError>501where502F: BufferCommit,503{504if buffer.len() % frame_size != 0 {505return Err(PlaybackBufferError::InvalidLength);506}507508Ok(PlaybackBuffer {509buffer: AudioBuffer {510buffer,511offset: 0,512frame_size,513},514drop,515})516}517518/// Returns the number of audio frames that fit in the buffer.519pub fn frame_capacity(&self) -> usize {520self.buffer.frame_capacity()521}522523/// This triggers the commit of `BufferCommit`. This should be called after the data is copied524/// to the buffer.525pub fn commit(&mut self) {526self.drop527.commit(self.buffer.offset / self.buffer.frame_size);528}529530/// It returns how many bytes need to be consumed531/// before the current playback buffer will be played.532pub fn latency_bytes(&self) -> u32 {533self.drop.latency_bytes()534}535536/// Writes up to `size` bytes directly to this buffer inside of the given callback function.537pub fn copy_cb<F: FnOnce(&mut [u8])>(&mut self, size: usize, cb: F) -> io::Result<usize> {538self.buffer.write_copy_cb(size, cb)539}540}541542impl Write for PlaybackBuffer<'_> {543fn write(&mut self, buf: &[u8]) -> io::Result<usize> {544self.buffer.write(buf)545}546547fn flush(&mut self) -> io::Result<()> {548self.buffer.flush()549}550}551552/// `AsyncPlaybackBuffer` is the async version of `PlaybackBuffer`.553pub struct AsyncPlaybackBuffer<'a> {554buffer: AudioBuffer<'a>,555trigger: &'a mut dyn AsyncBufferCommit,556}557558impl<'a> AsyncPlaybackBuffer<'a> {559/// Creates a new `AsyncPlaybackBuffer` that holds a reference to the backing memory specified560/// in `buffer`.561pub fn new<F>(562frame_size: usize,563buffer: &'a mut [u8],564trigger: &'a mut F,565) -> Result<Self, PlaybackBufferError>566where567F: AsyncBufferCommit,568{569if buffer.len() % frame_size != 0 {570return Err(PlaybackBufferError::InvalidLength);571}572573Ok(AsyncPlaybackBuffer {574buffer: AudioBuffer {575buffer,576offset: 0,577frame_size,578},579trigger,580})581}582583/// Returns the number of audio frames that fit in the buffer.584pub fn frame_capacity(&self) -> usize {585self.buffer.frame_capacity()586}587588/// This triggers the callback of `AsyncBufferCommit`. This should be called after the data is589/// copied to the buffer.590pub async fn commit(&mut self) {591self.trigger592.commit(self.buffer.offset / self.buffer.frame_size)593.await;594}595596/// It returns the latency in terms of bytes that the capture buffer was recorded.597pub fn latency_bytes(&self) -> u32 {598self.trigger.latency_bytes()599}600601/// Writes up to `size` bytes directly to this buffer inside of the given callback function.602pub fn copy_cb<F: FnOnce(&mut [u8])>(&mut self, size: usize, cb: F) -> io::Result<usize> {603self.buffer.write_copy_cb(size, cb)604}605606/// Copy data from an io::Reader607pub fn copy_from(&mut self, reader: &mut dyn Read) -> io::Result<usize> {608self.buffer.copy_from(reader)609}610}611612impl Write for AsyncPlaybackBuffer<'_> {613fn write(&mut self, buf: &[u8]) -> io::Result<usize> {614self.buffer.write(buf)615}616617fn flush(&mut self) -> io::Result<()> {618self.buffer.flush()619}620}621/// Stream that accepts playback samples but drops them.622pub struct NoopStream {623buffer: Vec<u8>,624frame_size: usize,625interval: Duration,626next_frame: Duration,627start_time: Option<Instant>,628buffer_drop: NoopBufferCommit,629}630631/// NoopStream data that is needed from the buffer complete callback.632struct NoopBufferCommit {633which_buffer: bool,634}635636impl BufferCommit for NoopBufferCommit {637fn commit(&mut self, _nwritten: usize) {638// When a buffer completes, switch to the other one.639self.which_buffer ^= true;640}641}642643#[async_trait(?Send)]644impl AsyncBufferCommit for NoopBufferCommit {645async fn commit(&mut self, _nwritten: usize) {646// When a buffer completes, switch to the other one.647self.which_buffer ^= true;648}649}650651impl NoopStream {652pub fn new(653num_channels: usize,654format: SampleFormat,655frame_rate: u32,656buffer_size: usize,657) -> Self {658let frame_size = format.sample_bytes() * num_channels;659let interval = Duration::from_millis(buffer_size as u64 * 1000 / frame_rate as u64);660NoopStream {661buffer: vec![0; buffer_size * frame_size],662frame_size,663interval,664next_frame: interval,665start_time: None,666buffer_drop: NoopBufferCommit {667which_buffer: false,668},669}670}671}672673impl PlaybackBufferStream for NoopStream {674fn next_playback_buffer<'b, 's: 'b>(&'s mut self) -> Result<PlaybackBuffer<'b>, BoxError> {675if let Some(start_time) = self.start_time {676let elapsed = start_time.elapsed();677if elapsed < self.next_frame {678std::thread::sleep(self.next_frame - elapsed);679}680self.next_frame += self.interval;681} else {682self.start_time = Some(Instant::now());683self.next_frame = self.interval;684}685Ok(PlaybackBuffer::new(686self.frame_size,687&mut self.buffer,688&mut self.buffer_drop,689)?)690}691}692693#[async_trait(?Send)]694impl AsyncPlaybackBufferStream for NoopStream {695async fn next_playback_buffer<'a>(696&'a mut self,697ex: &dyn AudioStreamsExecutor,698) -> Result<AsyncPlaybackBuffer<'a>, BoxError> {699if let Some(start_time) = self.start_time {700let elapsed = start_time.elapsed();701if elapsed < self.next_frame {702ex.delay(self.next_frame - elapsed).await?;703}704self.next_frame += self.interval;705} else {706self.start_time = Some(Instant::now());707self.next_frame = self.interval;708}709Ok(AsyncPlaybackBuffer::new(710self.frame_size,711&mut self.buffer,712&mut self.buffer_drop,713)?)714}715}716717/// No-op control for `NoopStream`s.718#[derive(Default)]719pub struct NoopStreamControl;720721impl NoopStreamControl {722pub fn new() -> Self {723NoopStreamControl {}724}725}726727impl StreamControl for NoopStreamControl {}728729/// Source of `NoopStream` and `NoopStreamControl` objects.730#[derive(Default)]731pub struct NoopStreamSource;732733impl NoopStreamSource {734pub fn new() -> Self {735NoopStreamSource {}736}737}738739impl StreamSource for NoopStreamSource {740#[allow(clippy::type_complexity)]741fn new_playback_stream(742&mut self,743num_channels: usize,744format: SampleFormat,745frame_rate: u32,746buffer_size: usize,747) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> {748Ok((749Box::new(NoopStreamControl::new()),750Box::new(NoopStream::new(751num_channels,752format,753frame_rate,754buffer_size,755)),756))757}758759#[allow(clippy::type_complexity)]760fn new_async_playback_stream(761&mut self,762num_channels: usize,763format: SampleFormat,764frame_rate: u32,765buffer_size: usize,766_ex: &dyn AudioStreamsExecutor,767) -> Result<(Box<dyn StreamControl>, Box<dyn AsyncPlaybackBufferStream>), BoxError> {768Ok((769Box::new(NoopStreamControl::new()),770Box::new(NoopStream::new(771num_channels,772format,773frame_rate,774buffer_size,775)),776))777}778}779780/// `NoopStreamSourceGenerator` is a struct that implements [`StreamSourceGenerator`]781/// to generate [`NoopStreamSource`].782pub struct NoopStreamSourceGenerator;783784impl NoopStreamSourceGenerator {785pub fn new() -> Self {786NoopStreamSourceGenerator {}787}788}789790impl Default for NoopStreamSourceGenerator {791fn default() -> Self {792Self::new()793}794}795796impl StreamSourceGenerator for NoopStreamSourceGenerator {797fn generate(&self) -> Result<Box<dyn StreamSource>, BoxError> {798Ok(Box::new(NoopStreamSource))799}800}801802#[cfg(test)]803mod tests {804use futures::FutureExt;805use io::Write;806807use super::async_api::test::TestExecutor;808use super::*;809810#[test]811fn invalid_buffer_length() {812// Playback buffers can't be created with a size that isn't divisible by the frame size.813let mut pb_buf = [0xa5u8; 480 * 2 * 2 + 1];814let mut buffer_drop = NoopBufferCommit {815which_buffer: false,816};817assert!(PlaybackBuffer::new(2, &mut pb_buf, &mut buffer_drop).is_err());818}819820#[test]821fn audio_buffer_copy_from() {822const PERIOD_SIZE: usize = 8192;823const NUM_CHANNELS: usize = 6;824const FRAME_SIZE: usize = NUM_CHANNELS * 2;825let mut dst_buf = [0u8; PERIOD_SIZE * FRAME_SIZE];826let src_buf = [0xa5u8; PERIOD_SIZE * FRAME_SIZE];827let mut aud_buf = AudioBuffer {828buffer: &mut dst_buf,829offset: 0,830frame_size: FRAME_SIZE,831};832aud_buf833.copy_from(&mut &src_buf[..])834.expect("all data should be copied.");835assert_eq!(dst_buf, src_buf);836}837838#[test]839fn audio_buffer_copy_from_repeat() {840const PERIOD_SIZE: usize = 8192;841const NUM_CHANNELS: usize = 6;842const FRAME_SIZE: usize = NUM_CHANNELS * 2;843let mut dst_buf = [0u8; PERIOD_SIZE * FRAME_SIZE];844let mut aud_buf = AudioBuffer {845buffer: &mut dst_buf,846offset: 0,847frame_size: FRAME_SIZE,848};849let bytes = aud_buf850.copy_from(&mut io::repeat(1))851.expect("all data should be copied.");852assert_eq!(bytes, PERIOD_SIZE * FRAME_SIZE);853assert_eq!(dst_buf, [1u8; PERIOD_SIZE * FRAME_SIZE]);854}855856#[test]857fn audio_buffer_copy_to() {858const PERIOD_SIZE: usize = 8192;859const NUM_CHANNELS: usize = 6;860const FRAME_SIZE: usize = NUM_CHANNELS * 2;861let mut dst_buf = [0u8; PERIOD_SIZE * FRAME_SIZE];862let mut src_buf = [0xa5u8; PERIOD_SIZE * FRAME_SIZE];863let mut aud_buf = AudioBuffer {864buffer: &mut src_buf,865offset: 0,866frame_size: FRAME_SIZE,867};868aud_buf869.copy_to(&mut &mut dst_buf[..])870.expect("all data should be copied.");871assert_eq!(dst_buf, src_buf);872}873874#[test]875fn audio_buffer_copy_to_sink() {876const PERIOD_SIZE: usize = 8192;877const NUM_CHANNELS: usize = 6;878const FRAME_SIZE: usize = NUM_CHANNELS * 2;879let mut src_buf = [0xa5u8; PERIOD_SIZE * FRAME_SIZE];880let mut aud_buf = AudioBuffer {881buffer: &mut src_buf,882offset: 0,883frame_size: FRAME_SIZE,884};885let bytes = aud_buf886.copy_to(&mut io::sink())887.expect("all data should be copied.");888assert_eq!(bytes, PERIOD_SIZE * FRAME_SIZE);889}890891#[test]892fn io_copy_audio_buffer() {893const PERIOD_SIZE: usize = 8192;894const NUM_CHANNELS: usize = 6;895const FRAME_SIZE: usize = NUM_CHANNELS * 2;896let mut dst_buf = [0u8; PERIOD_SIZE * FRAME_SIZE];897let src_buf = [0xa5u8; PERIOD_SIZE * FRAME_SIZE];898let mut aud_buf = AudioBuffer {899buffer: &mut dst_buf,900offset: 0,901frame_size: FRAME_SIZE,902};903io::copy(&mut &src_buf[..], &mut aud_buf).expect("all data should be copied.");904assert_eq!(dst_buf, src_buf);905}906907#[test]908fn commit() {909struct TestCommit {910frame_count: usize,911}912impl BufferCommit for TestCommit {913fn commit(&mut self, nwritten: usize) {914self.frame_count += nwritten;915}916}917let mut test_commit = TestCommit { frame_count: 0 };918{919const FRAME_SIZE: usize = 4;920let mut buf = [0u8; 480 * FRAME_SIZE];921let mut pb_buf = PlaybackBuffer::new(FRAME_SIZE, &mut buf, &mut test_commit).unwrap();922pb_buf.write_all(&[0xa5u8; 480 * FRAME_SIZE]).unwrap();923pb_buf.commit();924}925assert_eq!(test_commit.frame_count, 480);926}927928#[test]929fn sixteen_bit_stereo() {930let mut server = NoopStreamSource::new();931let (_, mut stream) = server932.new_playback_stream(2, SampleFormat::S16LE, 48000, 480)933.unwrap();934let mut copy_cb = |buf: &mut PlaybackBuffer| {935assert_eq!(buf.buffer.frame_capacity(), 480);936let pb_buf = [0xa5u8; 480 * 2 * 2];937assert_eq!(buf.write(&pb_buf).unwrap(), 480 * 2 * 2);938Ok(())939};940stream.write_playback_buffer(&mut copy_cb).unwrap();941}942943#[test]944fn consumption_rate() {945let mut server = NoopStreamSource::new();946let (_, mut stream) = server947.new_playback_stream(2, SampleFormat::S16LE, 48000, 480)948.unwrap();949let start = Instant::now();950{951let mut copy_cb = |buf: &mut PlaybackBuffer| {952let pb_buf = [0xa5u8; 480 * 2 * 2];953assert_eq!(buf.write(&pb_buf).unwrap(), 480 * 2 * 2);954Ok(())955};956stream.write_playback_buffer(&mut copy_cb).unwrap();957}958// The second call should block until the first buffer is consumed.959let mut assert_cb = |_: &mut PlaybackBuffer| {960let elapsed = start.elapsed();961assert!(962elapsed > Duration::from_millis(10),963"next_playback_buffer didn't block long enough {}",964elapsed.subsec_millis()965);966Ok(())967};968stream.write_playback_buffer(&mut assert_cb).unwrap();969}970971#[test]972fn async_commit() {973struct TestCommit {974frame_count: usize,975}976#[async_trait(?Send)]977impl AsyncBufferCommit for TestCommit {978async fn commit(&mut self, nwritten: usize) {979self.frame_count += nwritten;980}981}982async fn this_test() {983let mut test_commit = TestCommit { frame_count: 0 };984{985const FRAME_SIZE: usize = 4;986let mut buf = [0u8; 480 * FRAME_SIZE];987let mut pb_buf =988AsyncPlaybackBuffer::new(FRAME_SIZE, &mut buf, &mut test_commit).unwrap();989pb_buf.write_all(&[0xa5u8; 480 * FRAME_SIZE]).unwrap();990pb_buf.commit().await;991}992assert_eq!(test_commit.frame_count, 480);993}994995this_test().now_or_never();996}997998#[test]999fn consumption_rate_async() {1000async fn this_test(ex: &TestExecutor) {1001let mut server = NoopStreamSource::new();1002let (_, mut stream) = server1003.new_async_playback_stream(2, SampleFormat::S16LE, 48000, 480, ex)1004.unwrap();1005let start = Instant::now();1006{1007let copy_func = |buf: &mut AsyncPlaybackBuffer| {1008let pb_buf = [0xa5u8; 480 * 2 * 2];1009assert_eq!(buf.write(&pb_buf).unwrap(), 480 * 2 * 2);1010Ok(())1011};1012async_write_playback_buffer(&mut *stream, copy_func, ex)1013.await1014.unwrap();1015}1016// The second call should block until the first buffer is consumed.1017let assert_func = |_: &mut AsyncPlaybackBuffer| {1018let elapsed = start.elapsed();1019assert!(1020elapsed > Duration::from_millis(10),1021"write_playback_buffer didn't block long enough {}",1022elapsed.subsec_millis()1023);1024Ok(())1025};10261027async_write_playback_buffer(&mut *stream, assert_func, ex)1028.await1029.unwrap();1030}10311032let ex = TestExecutor {};1033this_test(&ex).now_or_never();1034}10351036#[test]1037fn generate_noop_stream_source() {1038let generator: Box<dyn StreamSourceGenerator> = Box::new(NoopStreamSourceGenerator::new());1039generator1040.generate()1041.expect("failed to generate stream source");1042}1043}104410451046