summaryrefslogtreecommitdiff
path: root/audio_streams/src/audio_streams.rs
diff options
context:
space:
mode:
Diffstat (limited to 'audio_streams/src/audio_streams.rs')
-rw-r--r--audio_streams/src/audio_streams.rs471
1 files changed, 0 insertions, 471 deletions
diff --git a/audio_streams/src/audio_streams.rs b/audio_streams/src/audio_streams.rs
deleted file mode 100644
index e5fc83cb..00000000
--- a/audio_streams/src/audio_streams.rs
+++ /dev/null
@@ -1,471 +0,0 @@
-// Copyright 2019 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-//! Provides an interface for playing and recording audio.
-//!
-//! When implementing an audio playback system, the `StreamSource` trait is implemented.
-//! Implementors of this trait allow creation of `PlaybackBufferStream` objects. The
-//! `PlaybackBufferStream` provides the actual audio buffers to be filled with audio samples. These
-//! buffers are obtained by calling `next_playback_buffer`.
-//!
-//! Users playing audio fill the provided buffers with audio. When a `PlaybackBuffer` is dropped,
-//! the samples written to it are committed to the `PlaybackBufferStream` it came from.
-//!
-//! ```
-//! use audio_streams::{BoxError, SampleFormat, StreamSource, NoopStreamSource};
-//! use std::io::Write;
-//!
-//! const buffer_size: usize = 120;
-//! const num_channels: usize = 2;
-//!
-//! # fn main() -> std::result::Result<(), BoxError> {
-//! let mut stream_source = NoopStreamSource::new();
-//! let sample_format = SampleFormat::S16LE;
-//! let frame_size = num_channels * sample_format.sample_bytes();
-//!
-//! let (_, mut stream) = stream_source
-//! .new_playback_stream(num_channels, sample_format, 48000, buffer_size)?;
-//! // Play 10 buffers of DC.
-//! let mut buf = Vec::new();
-//! buf.resize(buffer_size * frame_size, 0xa5u8);
-//! for _ in 0..10 {
-//! let mut stream_buffer = stream.next_playback_buffer()?;
-//! assert_eq!(stream_buffer.write(&buf)?, buffer_size * frame_size);
-//! }
-//! # Ok (())
-//! # }
-//! ```
-
-use std::cmp::min;
-use std::error;
-use std::fmt::{self, Display};
-use std::io::{self, Write};
-use std::os::unix::io::RawFd;
-use std::result::Result;
-use std::str::FromStr;
-use std::time::{Duration, Instant};
-
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum SampleFormat {
- U8,
- S16LE,
- S24LE,
- S32LE,
-}
-
-impl SampleFormat {
- pub fn sample_bytes(self) -> usize {
- use SampleFormat::*;
- match self {
- U8 => 1,
- S16LE => 2,
- S24LE => 4, // Not a typo, S24_LE samples are stored in 4 byte chunks.
- S32LE => 4,
- }
- }
-}
-
-impl Display for SampleFormat {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- use SampleFormat::*;
- match self {
- U8 => write!(f, "Unsigned 8 bit"),
- S16LE => write!(f, "Signed 16 bit Little Endian"),
- S24LE => write!(f, "Signed 24 bit Little Endian"),
- S32LE => write!(f, "Signed 32 bit Little Endian"),
- }
- }
-}
-
-/// Valid directions of an audio stream.
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum StreamDirection {
- Playback,
- Capture,
-}
-
-/// Valid effects for an audio stream.
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum StreamEffect {
- NoEffect,
- EchoCancellation,
-}
-
-pub mod capture;
-pub mod shm_streams;
-
-impl Default for StreamEffect {
- fn default() -> Self {
- StreamEffect::NoEffect
- }
-}
-
-/// Errors that can pass across threads.
-pub type BoxError = Box<dyn error::Error + Send + Sync>;
-
-/// Errors that are possible from a `StreamEffect`.
-#[derive(Debug)]
-pub enum StreamEffectError {
- InvalidEffect,
-}
-
-impl error::Error for StreamEffectError {}
-
-impl Display for StreamEffectError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- StreamEffectError::InvalidEffect => write!(f, "Must be in [EchoCancellation, aec]"),
- }
- }
-}
-
-impl FromStr for StreamEffect {
- type Err = StreamEffectError;
- fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
- match s {
- "EchoCancellation" | "aec" => Ok(StreamEffect::EchoCancellation),
- _ => Err(StreamEffectError::InvalidEffect),
- }
- }
-}
-
-/// `StreamSource` creates streams for playback or capture of audio.
-pub trait StreamSource: Send {
- /// Returns a stream control and buffer generator object. These are separate as the buffer
- /// generator might want to be passed to the audio stream.
- #[allow(clippy::type_complexity)]
- fn new_playback_stream(
- &mut self,
- num_channels: usize,
- format: SampleFormat,
- frame_rate: u32,
- buffer_size: usize,
- ) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError>;
-
- /// Returns a stream control and buffer generator object. These are separate as the buffer
- /// generator might want to be passed to the audio stream.
- /// Default implementation returns `NoopStreamControl` and `NoopCaptureStream`.
- #[allow(clippy::type_complexity)]
- fn new_capture_stream(
- &mut self,
- num_channels: usize,
- format: SampleFormat,
- frame_rate: u32,
- buffer_size: usize,
- ) -> Result<
- (
- Box<dyn StreamControl>,
- Box<dyn capture::CaptureBufferStream>,
- ),
- BoxError,
- > {
- Ok((
- Box::new(NoopStreamControl::new()),
- Box::new(capture::NoopCaptureStream::new(
- num_channels,
- format,
- frame_rate,
- buffer_size,
- )),
- ))
- }
-
- /// Returns any open file descriptors needed by the implementor. The FD list helps users of the
- /// StreamSource enter Linux jails making sure not to close needed FDs.
- fn keep_fds(&self) -> Option<Vec<RawFd>> {
- None
- }
-}
-
-/// `PlaybackBufferStream` provides `PlaybackBuffer`s to fill with audio samples for playback.
-pub trait PlaybackBufferStream: Send {
- fn next_playback_buffer(&mut self) -> Result<PlaybackBuffer, BoxError>;
-}
-
-/// `StreamControl` provides a way to set the volume and mute states of a stream. `StreamControl`
-/// is separate from the stream so it can be owned by a different thread if needed.
-pub trait StreamControl: Send + Sync {
- fn set_volume(&mut self, _scaler: f64) {}
- fn set_mute(&mut self, _mute: bool) {}
-}
-
-/// `BufferDrop` is used as a callback mechanism for `PlaybackBuffer` objects. It is meant to be
-/// implemented by the audio stream, allowing arbitrary code to be run after a buffer is filled or
-/// read by the user.
-pub trait BufferDrop {
- /// Called when an audio buffer is dropped. `nframes` indicates the number of audio frames that
- /// were read or written to the device.
- fn trigger(&mut self, nframes: usize);
-}
-
-/// Errors that are possible from a `PlaybackBuffer`.
-#[derive(Debug)]
-pub enum PlaybackBufferError {
- InvalidLength,
-}
-
-impl error::Error for PlaybackBufferError {}
-
-impl Display for PlaybackBufferError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- PlaybackBufferError::InvalidLength => write!(f, "Invalid buffer length"),
- }
- }
-}
-
-/// `AudioBuffer` is one buffer that holds buffer_size audio frames and its drop function.
-/// It is the inner data of `PlaybackBuffer` and `CaptureBuffer`.
-struct AudioBuffer<'a> {
- buffer: &'a mut [u8],
- offset: usize, // Read or Write offset in frames.
- frame_size: usize, // Size of a frame in bytes.
- drop: &'a mut dyn BufferDrop,
-}
-
-/// `PlaybackBuffer` is one buffer that holds buffer_size audio frames. It is used to temporarily
-/// allow access to an audio buffer and notifes the owning stream of write completion when dropped.
-pub struct PlaybackBuffer<'a> {
- buffer: AudioBuffer<'a>,
-}
-
-impl<'a> PlaybackBuffer<'a> {
- /// Creates a new `PlaybackBuffer` that holds a reference to the backing memory specified in
- /// `buffer`.
- pub fn new<F>(
- frame_size: usize,
- buffer: &'a mut [u8],
- drop: &'a mut F,
- ) -> Result<Self, PlaybackBufferError>
- where
- F: BufferDrop,
- {
- if buffer.len() % frame_size != 0 {
- return Err(PlaybackBufferError::InvalidLength);
- }
-
- Ok(PlaybackBuffer {
- buffer: AudioBuffer {
- buffer,
- offset: 0,
- frame_size,
- drop,
- },
- })
- }
-
- /// Returns the number of audio frames that fit in the buffer.
- pub fn frame_capacity(&self) -> usize {
- self.buffer.buffer.len() / self.buffer.frame_size
- }
-
- /// Writes up to `size` bytes directly to this buffer inside of the given callback function.
- pub fn copy_cb<F: FnOnce(&mut [u8])>(&mut self, size: usize, cb: F) {
- // only write complete frames.
- let len = min(
- size / self.buffer.frame_size * self.buffer.frame_size,
- self.buffer.buffer.len() - self.buffer.offset,
- );
- cb(&mut self.buffer.buffer[self.buffer.offset..(self.buffer.offset + len)]);
- self.buffer.offset += len;
- }
-}
-
-impl<'a> Write for PlaybackBuffer<'a> {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- // only write complete frames.
- let len = buf.len() / self.buffer.frame_size * self.buffer.frame_size;
- let written = (&mut self.buffer.buffer[self.buffer.offset..]).write(&buf[..len])?;
- self.buffer.offset += written;
- Ok(written)
- }
-
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
-}
-
-impl<'a> Drop for PlaybackBuffer<'a> {
- fn drop(&mut self) {
- self.buffer
- .drop
- .trigger(self.buffer.offset / self.buffer.frame_size);
- }
-}
-
-/// Stream that accepts playback samples but drops them.
-pub struct NoopStream {
- buffer: Vec<u8>,
- frame_size: usize,
- interval: Duration,
- next_frame: Duration,
- start_time: Option<Instant>,
- buffer_drop: NoopBufferDrop,
-}
-
-/// NoopStream data that is needed from the buffer complete callback.
-struct NoopBufferDrop {
- which_buffer: bool,
-}
-
-impl BufferDrop for NoopBufferDrop {
- fn trigger(&mut self, _nwritten: usize) {
- // When a buffer completes, switch to the other one.
- self.which_buffer ^= true;
- }
-}
-
-impl NoopStream {
- pub fn new(
- num_channels: usize,
- format: SampleFormat,
- frame_rate: u32,
- buffer_size: usize,
- ) -> Self {
- let frame_size = format.sample_bytes() * num_channels;
- let interval = Duration::from_millis(buffer_size as u64 * 1000 / frame_rate as u64);
- NoopStream {
- buffer: vec![0; buffer_size * frame_size],
- frame_size,
- interval,
- next_frame: interval,
- start_time: None,
- buffer_drop: NoopBufferDrop {
- which_buffer: false,
- },
- }
- }
-}
-
-impl PlaybackBufferStream for NoopStream {
- fn next_playback_buffer(&mut self) -> Result<PlaybackBuffer, BoxError> {
- if let Some(start_time) = self.start_time {
- let elapsed = start_time.elapsed();
- if elapsed < self.next_frame {
- std::thread::sleep(self.next_frame - elapsed);
- }
- self.next_frame += self.interval;
- } else {
- self.start_time = Some(Instant::now());
- self.next_frame = self.interval;
- }
- Ok(PlaybackBuffer::new(
- self.frame_size,
- &mut self.buffer,
- &mut self.buffer_drop,
- )?)
- }
-}
-
-/// No-op control for `NoopStream`s.
-#[derive(Default)]
-pub struct NoopStreamControl;
-
-impl NoopStreamControl {
- pub fn new() -> Self {
- NoopStreamControl {}
- }
-}
-
-impl StreamControl for NoopStreamControl {}
-
-/// Source of `NoopStream` and `NoopStreamControl` objects.
-#[derive(Default)]
-pub struct NoopStreamSource;
-
-impl NoopStreamSource {
- pub fn new() -> Self {
- NoopStreamSource {}
- }
-}
-
-impl StreamSource for NoopStreamSource {
- #[allow(clippy::type_complexity)]
- fn new_playback_stream(
- &mut self,
- num_channels: usize,
- format: SampleFormat,
- frame_rate: u32,
- buffer_size: usize,
- ) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> {
- Ok((
- Box::new(NoopStreamControl::new()),
- Box::new(NoopStream::new(
- num_channels,
- format,
- frame_rate,
- buffer_size,
- )),
- ))
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn invalid_buffer_length() {
- // Playback buffers can't be created with a size that isn't divisible by the frame size.
- let mut pb_buf = [0xa5u8; 480 * 2 * 2 + 1];
- let mut buffer_drop = NoopBufferDrop {
- which_buffer: false,
- };
- assert!(PlaybackBuffer::new(2, &mut pb_buf, &mut buffer_drop).is_err());
- }
-
- #[test]
- fn trigger() {
- struct TestDrop {
- frame_count: usize,
- };
- impl BufferDrop for TestDrop {
- fn trigger(&mut self, nwritten: usize) {
- self.frame_count += nwritten;
- }
- }
- let mut test_drop = TestDrop { frame_count: 0 };
- {
- const FRAME_SIZE: usize = 4;
- let mut buf = [0u8; 480 * FRAME_SIZE];
- let mut pb_buf = PlaybackBuffer::new(FRAME_SIZE, &mut buf, &mut test_drop).unwrap();
- pb_buf.write_all(&[0xa5u8; 480 * FRAME_SIZE]).unwrap();
- }
- assert_eq!(test_drop.frame_count, 480);
- }
-
- #[test]
- fn sixteen_bit_stereo() {
- let mut server = NoopStreamSource::new();
- let (_, mut stream) = server
- .new_playback_stream(2, SampleFormat::S16LE, 48000, 480)
- .unwrap();
- let mut stream_buffer = stream.next_playback_buffer().unwrap();
- assert_eq!(stream_buffer.frame_capacity(), 480);
- let pb_buf = [0xa5u8; 480 * 2 * 2];
- assert_eq!(stream_buffer.write(&pb_buf).unwrap(), 480 * 2 * 2);
- }
-
- #[test]
- fn consumption_rate() {
- let mut server = NoopStreamSource::new();
- let (_, mut stream) = server
- .new_playback_stream(2, SampleFormat::S16LE, 48000, 480)
- .unwrap();
- let start = Instant::now();
- {
- let mut stream_buffer = stream.next_playback_buffer().unwrap();
- let pb_buf = [0xa5u8; 480 * 2 * 2];
- assert_eq!(stream_buffer.write(&pb_buf).unwrap(), 480 * 2 * 2);
- }
- // The second call should block until the first buffer is consumed.
- let _stream_buffer = stream.next_playback_buffer().unwrap();
- let elapsed = start.elapsed();
- assert!(
- elapsed > Duration::from_millis(10),
- "next_playback_buffer didn't block long enough {}",
- elapsed.subsec_millis()
- );
- }
-}