//! This module aims to provide a user-friendly rust-esque wrapper around the portaudio Stream
//! types.
//!
//! The primary type of interest is [**Stream**](./struct.Stream).
use ffi;
use libc;
use num::FromPrimitive;
use std::os::raw;
use std::{self, ptr};
use super::error::Error;
use super::types::{
sample_format_flags, DeviceIndex, DeviceKind, SampleFormat, SampleFormatFlags, Time,
};
use super::Sample;
pub use self::callback_flags::CallbackFlags;
pub use self::flags::Flags;
/// There are two **Mode**s with which a **Stream** can be set: [**Blocking**](./struct.Blocking)
/// and [**NonBlocking**](./struct.NonBlocking).
pub trait Mode {}
/// Types used to open a **Stream** via the
/// [**PortAudio::open_blocking_stream**](../struct.PortAudio.html#method.open_blocking_stream) and
/// [**PortAudio::open_non_blocking_stream**](../struct.PortAudio.html#method.open_blocking_stream)
/// methods.
pub trait Settings {
/// The **Flow** of the **Stream** (**Input**, **Output** or **Duplex**).
type Flow;
/// Construct the **Stream**'s **Flow** alongside the rest of its settings.
fn into_flow_and_settings(self) -> (Self::Flow, f64, u32, Flags);
}
/// # Safety
/// There are three possible **Flow**s available for a **Stream**: [**Input**](./struct.Input),
/// [**Out**](./struct.Output) and [**Duplex**](./struct.Duplex).
pub unsafe trait Flow {
/// The type of buffer(s) necessary for transferring audio in a Blocking stream.
type Buffer;
/// The arguments passed to the non-blocking stream callback.
type CallbackArgs;
/// Timing information for the buffer passed to the stream callback.
type CallbackTimeInfo;
/// Construct a new **Self::Buffer**.
fn new_buffer(&self, frames_per_buffer: u32) -> Self::Buffer;
/// Necessary for dynamically acquiring bi-directional params for Pa_OpenStream.
fn params_both_directions(
&self,
) -> (
Option<ffi::PaStreamParameters>,
Option<ffi::PaStreamParameters>,
);
/// # Safety
/// Constructs the **Flow**'s associated **CallbackArgs** from the non-blocking C API stream
/// parameters.
unsafe fn new_callback_args(
input: *const raw::c_void,
output: *mut raw::c_void,
frame_count: raw::c_ulong,
time_info: *const ffi::PaStreamCallbackTimeInfo,
flags: ffi::PaStreamCallbackFlags,
in_channels: i32,
out_channels: i32,
) -> Self::CallbackArgs;
}
/// **Streams** that can be read by the user.
pub trait Reader: Flow {
/// The sample format for the readable buffer.
type Sample;
/// Borrow the readable **Buffer**.
fn readable_buffer(blocking: &Blocking<Self::Buffer>) -> &Buffer;
/// The number of channels in the readable **Buffer**.
fn channel_count(&self) -> i32;
}
/// **Streams** that can be written to by the user for output to some DAC.
pub trait Writer: Flow {
/// The sample format for the writable buffer.
type Sample;
/// Mutably borrow the the writable **Buffer**.
fn writable_buffer(blocking: &mut Blocking<Self::Buffer>) -> &mut Buffer;
/// The number of channels in the writable **Buffer**.
fn channel_count(&self) -> i32;
}
/// An alias for the boxed Callback function type.
type CallbackFn = dyn FnMut(
*const raw::c_void,
*mut raw::c_void,
raw::c_ulong,
*const ffi::PaStreamCallbackTimeInfo,
ffi::PaStreamCallbackFlags,
) -> ffi::PaStreamCallbackResult;
/// A wrapper around a user-given **CallbackFn** that can be sent to PortAudio.
struct CallbackFnWrapper {
f: Box<CallbackFn>,
}
/// Timing information for the buffer passed to the input stream callback.
///
/// Time values are expressed in seconds and are synchronised with the time base used by
/// `Stream::time` method for the associated stream.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct InputCallbackTimeInfo {
/// The time when the stream callback was invoked.
pub current: Time,
/// The time when the first sample of the input buffer was captured at the ADC input.
pub buffer_adc: Time,
}
/// Timing information for the buffer passed to the output stream callback.
///
/// Time values are expressed in seconds and are synchronised with the time base used by
/// `Stream::time` method for the associated stream.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct OutputCallbackTimeInfo {
/// The time when the stream callback was invoked.
pub current: Time,
/// The time when the first sample of the output buffer will output the DAC.
pub buffer_dac: Time,
}
/// Timing information for the buffers passed to the stream callback.
///
/// Time values are expressed in seconds and are synchronised with the time base used by
/// `Stream::time` method for the associated stream.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DuplexCallbackTimeInfo {
/// The time when the stream callback was invoked.
pub current: Time,
/// The time when the first sample of the input buffer was captured at the ADC input.
pub in_buffer_adc: Time,
/// The time when the first sample of the output buffer will output the DAC.
pub out_buffer_dac: Time,
}
/// Arguments given to a **NonBlocking** **Input** **Stream**'s **CallbackFn**.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct InputCallbackArgs<'a, I: 'a> {
/// The buffer of interleaved samples read from the **Input** **Stream**'s ADC.
pub buffer: &'a [I],
/// The number of frames of audio data stored within the `buffer`.
pub frames: usize,
/// Flags indicating the current state of the stream and whether or not any special edge cases
/// have occurred.
pub flags: CallbackFlags,
/// Timing information relevant to the callback.
pub time: InputCallbackTimeInfo,
}
/// Arguments given to a **NonBlocking** **Input** **Stream**'s **CallbackFn**.
#[derive(Debug, PartialEq)]
pub struct OutputCallbackArgs<'a, O: 'a> {
/// The **Output** **Stream**'s buffer, to which we will write our interleaved audio data.
pub buffer: &'a mut [O],
/// The number of frames of audio data stored within the `buffer`.
pub frames: usize,
/// Flags indicating the current state of the stream and whether or not any special edge cases
/// have occurred.
pub flags: CallbackFlags,
/// Timing information relevant to the callback.
pub time: OutputCallbackTimeInfo,
}
/// Arguments given to a **NonBlocking** **Input** **Stream**'s **CallbackFn**.
#[derive(Debug, PartialEq)]
pub struct DuplexCallbackArgs<'a, I: 'a, O: 'a> {
/// The buffer of interleaved samples read from the **Stream**'s ADC.
pub in_buffer: &'a [I],
/// The **Stream**'s output buffer, to which we will write interleaved audio data.
pub out_buffer: &'a mut [O],
/// The number of frames of audio data stored within the `buffer`.
pub frames: usize,
/// Flags indicating the current state of the stream and whether or not any special edge cases
/// have occurred.
pub flags: CallbackFlags,
/// Timing information relevant to the callback.
pub time: DuplexCallbackTimeInfo,
}
/// A **Stream** **Mode** representing a blocking stream.
///
/// Unlike the **NonBlocking** stream, PortAudio requires that we manually manage the audio data
/// buffer for the **Blocking** stream.
pub struct Blocking<B> {
buffer: B,
}
/// A **Stream** **Mode** representing a non-blocking stream.
pub struct NonBlocking {
callback: Box<CallbackFnWrapper>,
}
/// A type-safe PortAudio PaStream wrapper.
///
/// **F** is the stream's directional [**Flow**][1]:
///
/// - [**Input**][2] - Receives data from an input device's ADC.
/// - [**Output**][3] - Sends data to an output device's DAC.
/// - [**Duplex**][4] - Receives and Sends data on two devices synchronously.
///
/// A **Stream** of a particular [**Flow**][1] type can be opened by passing the **Flow**'s
/// associated **Settings** type to either of the [**PortAudio::open_blocking_stream**][12] or
/// [**PortAudio::open_non_blocking_stream**][13] methods.
///
/// - [**InputSettings**][14] -> [**Input**][2]
/// - [**OutputSettings**][15] -> [**Output**][3]
/// - [**DuplexSettings**][16] -> [**Duplex**][4]
///
/// **M** is the stream's [**Mode**][5]:
///
/// - [**Blocking**][6] - The stream will be run on the caller's thread. For [**Blocking**][6]
/// streams, a user can read from [**Input**][2] and [**Duplex**][4] streams using the
/// [**Stream::read_available**][8] and [**Stream::read**][9] methods and write to [**Output**][3]
/// and [**Duplex**][4] streams using the [**Stream::write_available**][10] and
/// [**Stream::write**][11] methods. A [**Blocking**][6] **Stream** can be opened using the
/// [**PortAudio::open_blocking_stream][12]** method.
/// - [**NonBlocking**][7] - The stream will be run on a separate thread. [**NonBlocking][7]
/// streams are read and written to via the callback arguments that are associated with the
/// **Stream**'s [**Flow**][1] type:
/// - **Input** -> [**InputCallbackArgs**](./struct.InputCallbackArgs.html)
/// - **Output** -> [**OutputCallbackArgs**](./struct.OutputCallbackArgs.html)
/// - **Duplex** -> [**DuplexCallbackArgs**](./struct.DuplexCallbackArgs.html)
/// A [**NonBlocking**][7] **Stream** can be opened using the
/// [**PortAudio::open_non_blocking_stream][13]** method.
///
/// A **Stream** may only live as long as the **PortAudio** instance from which it was spawned and
/// no longer.
///
/// The original PortAudio documentation for the **PaStream** type can be found [here][17].
///
/// [1]: ./trait.Flow.html
/// [2]: ./struct.Input.html
/// [3]: ./struct.Output.html
/// [4]: ./struct.Duplex.html
/// [5]: ./trait.Mode.html
/// [6]: ./struct.Blocking.html
/// [7]: ./struct.NonBlocking.html
/// [8]: ./struct.Stream.html#method.read_available
/// [9]: ./struct.Stream.html#method.read
/// [10]: ./struct.Stream.html#method.write_available
/// [11]: ./struct.Stream.html#method.write
/// [12]: ../struct.PortAudio.html#method.open_blocking_stream
/// [13]: ../struct.PortAudio.html#method.open_non_blocking_stream
/// [14]: ./struct.InputSettings.html
/// [15]: ./struct.OutputSettings.html
/// [16]: ./struct.DuplexSettings.html
/// [17]: http://portaudio.com/docs/v19-doxydocs/portaudio_8h.html#a19874734f89958fccf86785490d53b4c
#[allow(dead_code)]
pub struct Stream<M, F> {
pa_stream: *mut ffi::PaStream,
mode: M,
flow: F,
port_audio_life: std::sync::Arc<super::Life>,
}
/// Parameters for one direction (input or output) of a stream.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Parameters<S> {
/// Index of the device to be used, or a variant indicating to use the host-specific API.
pub device: DeviceKind,
/// The number of channels for this device
pub channel_count: i32,
/// The suggested latency for this device
pub suggested_latency: Time,
/// Indicates the format of the audio buffer.
///
/// If `true`, audio data is passed as a single buffer with all channels interleaved.
///
/// If `false`, audio data is passed as an array of pointers to separate buffers, one buffer
/// for each channel.
pub is_interleaved: bool,
/// Sample format of the audio data provided to/by the device.
sample_format: std::marker::PhantomData<S>,
}
/// Settings used to construct an **Input** **Stream**.
#[derive(Copy, Clone, Debug)]
pub struct InputSettings<I> {
/// The set of Parameters necessary for constructing the **Stream**.
pub params: Parameters<I>,
/// The number of audio frames read per second.
pub sample_rate: f64,
/// The number of audio frames that are read per buffer.
pub frames_per_buffer: u32,
/// Any special **Stream** behaviour we require given as a set of flags.
pub flags: Flags,
}
/// Settings used to construct an **Out** **Stream**.
#[derive(Copy, Clone, Debug)]
pub struct OutputSettings<O> {
/// The set of Parameters necessary for constructing the **Stream**.
pub params: Parameters<O>,
/// The number of audio frames written per second.
pub sample_rate: f64,
/// The number of audio frames requested per buffer.
pub frames_per_buffer: u32,
/// Any special **Stream** behaviour we require given as a set of flags.
pub flags: Flags,
}
/// Settings used to construct a **Duplex** **Stream**.
#[derive(Copy, Clone, Debug)]
pub struct DuplexSettings<I, O> {
/// The set of Parameters necessary for constructing the input **Stream**.
pub in_params: Parameters<I>,
/// The set of Parameters necessary for constructing the output **Stream**.
pub out_params: Parameters<O>,
/// The number of audio frames written per second.
pub sample_rate: f64,
/// The number of audio frames requested per buffer.
pub frames_per_buffer: u32,
/// Any special **Stream** behaviour we require given as a set of flags.
pub flags: Flags,
}
/// A type of **Flow** that describes an input-only **Stream**.
pub struct Input<I> {
params: Parameters<I>,
}
/// A type of **Flow** that describes an output-only **Stream**.
pub struct Output<O> {
params: Parameters<O>,
}
/// A type of **Flow** that describes a bi-directional (input *and* output) **Stream**.
pub struct Duplex<I, O> {
in_params: Parameters<I>,
out_params: Parameters<O>,
}
unsafe impl Send for NonBlocking {}
unsafe impl<M, F> Send for Stream<M, F>
where
M: Send,
F: Send,
{
}
impl<S> Parameters<S> {
/// Construct a new **Parameters**.
pub fn new(
device: DeviceIndex,
channel_count: i32,
is_interleaved: bool,
suggested_latency: Time,
) -> Self {
Self::new_internal(
device.into(),
channel_count,
is_interleaved,
suggested_latency,
)
}
/// The same as **Parameters::new**, but the device(s) to be used are specified in the host
/// api specific stream info structure.
///
/// **NOTE:** This has not yet been tested.
pub fn host_api_specific_device(
channel_count: i32,
is_interleaved: bool,
suggested_latency: Time,
) -> Self {
let kind = DeviceKind::UseHostApiSpecificDeviceSpecification;
Self::new_internal(kind, channel_count, is_interleaved, suggested_latency)
}
fn new_internal(
device_kind: DeviceKind,
channel_count: i32,
is_interleaved: bool,
suggested_latency: Time,
) -> Self {
Parameters {
device: device_kind,
channel_count,
is_interleaved,
suggested_latency,
sample_format: std::marker::PhantomData,
}
}
}
/// Simplify implementation of one-way-Stream Settings types.
macro_rules! impl_half_duplex_settings {
($name:ident) => {
impl<S> $name<S> {
/// Construct the settings from the given `params`, `sample_rate` and
/// `frames_per_buffer` with an empty set of **StreamFlags**.
pub fn new(params: Parameters<S>, sample_rate: f64, frames_per_buffer: u32) -> Self {
Self::with_flags(params, sample_rate, frames_per_buffer, Flags::empty())
}
/// Construct the settings with the given **Parameters**, `sample_rate`,
/// `frames_per_buffer` and **StreamFlags**.
pub fn with_flags(
params: Parameters<S>,
sample_rate: f64,
frames_per_buffer: u32,
flags: Flags,
) -> Self {
$name {
params,
sample_rate,
frames_per_buffer,
flags,
}
}
}
};
}
impl_half_duplex_settings!(OutputSettings);
impl_half_duplex_settings!(InputSettings);
impl<I, O> DuplexSettings<I, O> {
/// Construct the settings from the given `params`, `sample_rate` and
/// `frames_per_buffer` with an empty set of **StreamFlags**.
pub fn new(
in_params: Parameters<I>,
out_params: Parameters<O>,
sample_rate: f64,
frames_per_buffer: u32,
) -> Self {
Self::with_flags(
in_params,
out_params,
sample_rate,
frames_per_buffer,
Flags::empty(),
)
}
/// Construct the settings with the given **Parameters**, `sample_rate`,
/// `frames_per_buffer` and **StreamFlags**.
pub fn with_flags(
in_params: Parameters<I>,
out_params: Parameters<O>,
sample_rate: f64,
frames_per_buffer: u32,
flags: Flags,
) -> Self {
DuplexSettings {
in_params,
out_params,
sample_rate,
frames_per_buffer,
flags,
}
}
}
unsafe impl<I> Flow for Input<I>
where
I: Sample + 'static,
{
type Buffer = Buffer;
type CallbackArgs = InputCallbackArgs<'static, I>;
type CallbackTimeInfo = InputCallbackTimeInfo;
fn new_buffer(&self, frames_per_buffer: u32) -> Self::Buffer {
let channel_count = self.params.channel_count;
Buffer::new::<I>(frames_per_buffer, channel_count)
}
fn params_both_directions(
&self,
) -> (
Option<ffi::PaStreamParameters>,
Option<ffi::PaStreamParameters>,
) {
(Some(self.params.into()), None)
}
unsafe fn new_callback_args(
input: *const raw::c_void,
_output: *mut raw::c_void,
frame_count: raw::c_ulong,
time_info: *const ffi::PaStreamCallbackTimeInfo,
flags: ffi::PaStreamCallbackFlags,
in_channels: i32,
_out_channels: i32,
) -> Self::CallbackArgs {
let flags = CallbackFlags::from_bits(flags).unwrap_or_else(CallbackFlags::empty);
let time = unsafe {
InputCallbackTimeInfo {
current: (*time_info).currentTime,
buffer_adc: (*time_info).inputBufferAdcTime,
}
};
// TODO: At the moment, we assume the buffer is interleaved. We need to check whether or
// not buffer is interleaved here. This should probably an extra type parameter (along-side
// the Sample type param).
let buffer: &[I] = {
let buffer_len = in_channels as usize * frame_count as usize;
let buffer_ptr = input as *const I;
unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_len) }
};
InputCallbackArgs {
buffer,
frames: frame_count as usize,
flags,
time,
}
}
}
unsafe impl<O> Flow for Output<O>
where
O: Sample + 'static,
{
type Buffer = Buffer;
type CallbackArgs = OutputCallbackArgs<'static, O>;
type CallbackTimeInfo = OutputCallbackTimeInfo;
fn params_both_directions(
&self,
) -> (
Option<ffi::PaStreamParameters>,
Option<ffi::PaStreamParameters>,
) {
(None, Some(self.params.into()))
}
fn new_buffer(&self, frames_per_buffer: u32) -> Self::Buffer {
let channel_count = self.params.channel_count;
Buffer::new::<O>(frames_per_buffer, channel_count)
}
unsafe fn new_callback_args(
_input: *const raw::c_void,
output: *mut raw::c_void,
frame_count: raw::c_ulong,
time_info: *const ffi::PaStreamCallbackTimeInfo,
flags: ffi::PaStreamCallbackFlags,
_in_channels: i32,
out_channels: i32,
) -> Self::CallbackArgs {
let flags = CallbackFlags::from_bits(flags).unwrap_or_else(CallbackFlags::empty);
let time = unsafe {
OutputCallbackTimeInfo {
current: (*time_info).currentTime,
buffer_dac: (*time_info).outputBufferDacTime,
}
};
// TODO: At the moment, we assume the buffer is interleaved. We need to check whether or
// not buffer is interleaved here. This should probably an extra type parameter (along-side
// the Sample type param).
let buffer: &mut [O] = {
let buffer_len = out_channels as usize * frame_count as usize;
let buffer_ptr = output as *mut O;
unsafe { std::slice::from_raw_parts_mut(buffer_ptr, buffer_len) }
};
OutputCallbackArgs {
buffer,
frames: frame_count as usize,
flags,
time,
}
}
}
unsafe impl<I, O> Flow for Duplex<I, O>
where
I: Sample + 'static,
O: Sample + 'static,
{
type Buffer = (Buffer, Buffer);
type CallbackArgs = DuplexCallbackArgs<'static, I, O>;
type CallbackTimeInfo = DuplexCallbackTimeInfo;
fn params_both_directions(
&self,
) -> (
Option<ffi::PaStreamParameters>,
Option<ffi::PaStreamParameters>,
) {
(Some(self.in_params.into()), Some(self.out_params.into()))
}
fn new_buffer(&self, frames_per_buffer: u32) -> Self::Buffer {
let in_channel_count = self.in_params.channel_count;
let in_buffer = Buffer::new::<I>(frames_per_buffer, in_channel_count);
let out_channel_count = self.out_params.channel_count;
let out_buffer = Buffer::new::<O>(frames_per_buffer, out_channel_count);
(in_buffer, out_buffer)
}
unsafe fn new_callback_args(
input: *const raw::c_void,
output: *mut raw::c_void,
frame_count: raw::c_ulong,
time_info: *const ffi::PaStreamCallbackTimeInfo,
flags: ffi::PaStreamCallbackFlags,
in_channels: i32,
out_channels: i32,
) -> Self::CallbackArgs {
let flags = CallbackFlags::from_bits(flags).unwrap_or_else(CallbackFlags::empty);
let time = unsafe {
DuplexCallbackTimeInfo {
current: (*time_info).currentTime,
in_buffer_adc: (*time_info).inputBufferAdcTime,
out_buffer_dac: (*time_info).outputBufferDacTime,
}
};
// TODO: At the moment, we assume these buffers are interleaved. We need to check whether
// or not buffer is interleaved here. This should probably an extra type parameter
// (along-side the Sample type param).
let in_buffer: &[I] = {
let buffer_len = in_channels as usize * frame_count as usize;
let buffer_ptr = input as *const I;
unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_len) }
};
let out_buffer: &mut [O] = {
let buffer_len = out_channels as usize * frame_count as usize;
let buffer_ptr = output as *mut O;
unsafe { std::slice::from_raw_parts_mut(buffer_ptr, buffer_len) }
};
DuplexCallbackArgs {
in_buffer,
out_buffer,
frames: frame_count as usize,
flags,
time,
}
}
}
impl<I> Reader for Input<I>
where
I: Sample + 'static,
{
type Sample = I;
fn readable_buffer(blocking: &Blocking<<Input<I> as Flow>::Buffer>) -> &Buffer {
&blocking.buffer
}
fn channel_count(&self) -> i32 {
self.params.channel_count
}
}
impl<I, O> Reader for Duplex<I, O>
where
I: Sample + 'static,
O: Sample + 'static,
{
type Sample = I;
fn readable_buffer(blocking: &Blocking<<Duplex<I, O> as Flow>::Buffer>) -> &Buffer {
&blocking.buffer.0
}
fn channel_count(&self) -> i32 {
self.in_params.channel_count
}
}
impl<O> Writer for Output<O>
where
O: Sample + 'static,
{
type Sample = O;
fn writable_buffer(blocking: &mut Blocking<<Output<O> as Flow>::Buffer>) -> &mut Buffer {
&mut blocking.buffer
}
fn channel_count(&self) -> i32 {
self.params.channel_count
}
}
impl<I, O> Writer for Duplex<I, O>
where
I: Sample + 'static,
O: Sample + 'static,
{
type Sample = O;
fn writable_buffer(blocking: &mut Blocking<<Duplex<I, O> as Flow>::Buffer>) -> &mut Buffer {
&mut blocking.buffer.1
}
fn channel_count(&self) -> i32 {
self.out_params.channel_count
}
}
/// The buffer used to transfer audio data between the input and output streams.
pub struct Buffer {
data: *mut libc::c_void,
}
pub mod flags {
//! A type safe wrapper around PortAudio's stream flags.
use ffi;
bitflags! {
/// Flags used to control the behaviour of a stream. They are passed as parameters to
/// Stream::open or Stream::open_default. Multiple flags may be used together.
///
/// See the [bitflags repo](https://github.com/rust-lang/bitflags/blob/master/src/lib.rs)
/// for examples of composing flags together.
pub struct Flags: ::std::os::raw::c_ulong {
/// No flags.
const NO_FLAG = ffi::PA_NO_FLAG;
/// Disable default clipping of out of range samples.
const CLIP_OFF = ffi::PA_CLIP_OFF;
/// Disable default dithering.
const DITHER_OFF = ffi::PA_DITHER_OFF;
/// Flag requests that where possible a full duplex stream will not discard overflowed
/// input samples without calling the stream callback.
const NEVER_DROP_INPUT = ffi::PA_NEVER_DROP_INPUT;
/// Call the stream callback to fill initial output buffers, rather than the default
/// behavior of priming the buffers with zeros (silence)
const PA_PRIME_OUTPUT_BUFFERS_USING_STREAM_CALLBACK = ffi::PA_PRIME_OUTPUT_BUFFERS_USING_STREAM_CALLBACK;
/// A mask specifying the platform specific bits.
const PA_PLATFORM_SPECIFIC_FLAGS = ffi::PA_PLATFORM_SPECIFIC_FLAGS;
}
}
impl ::std::fmt::Display for Flags {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(
f,
"{:?}",
match self.bits() {
ffi::PA_NO_FLAG => "NO_FLAG",
ffi::PA_CLIP_OFF => "CLIP_OFF",
ffi::PA_DITHER_OFF => "DITHER_OFF",
ffi::PA_NEVER_DROP_INPUT => "NEVER_DROP_INPUT",
ffi::PA_PRIME_OUTPUT_BUFFERS_USING_STREAM_CALLBACK =>
"PRIME_OUTPUT_BUFFERS_USING_STREAM_CALLBACK",
ffi::PA_PLATFORM_SPECIFIC_FLAGS => "PLATFORM_SPECIFIC_FLAGS",
_ => "<Unknown StreamFlags>",
}
)
}
}
}
/// Describes stream availability and the number for frames available for reading/writing if there
/// is any.
#[derive(Copy, Clone, PartialEq, Debug, Eq)]
pub enum Available {
/// The number of frames available for reading.
Frames(::std::os::raw::c_long),
/// The input stream has overflowed.
InputOverflowed,
/// The output stream has underflowed.
OutputUnderflowed,
}
pub mod callback_flags {
//! A type safe wrapper around PortAudio's stream callback flags.
use ffi;
bitflags! {
/// Flag bit constants for the status flags passed to the stream's callback function.
pub struct CallbackFlags: ::std::os::raw::c_ulong {
/// No flags.
const NO_FLAG = ffi::PA_NO_FLAG;
/// In a stream opened with paFramesPerBufferUnspecified, indicates that input data is
/// all silence (zeros) because no real data is available. In a stream opened without
/// `FramesPerBufferUnspecified`, it indicates that one or more zero samples have been
/// inserted into the input buffer to compensate for an input underflow.
const INPUT_UNDERFLOW = ffi::INPUT_UNDERFLOW;
/// In a stream opened with paFramesPerBufferUnspecified, indicates that data prior to
/// the first sample of the input buffer was discarded due to an overflow, possibly
/// because the stream callback is using too much CPU time. Otherwise indicates that
/// data prior to one or more samples in the input buffer was discarded.
const INPUT_OVERFLOW = ffi::INPUT_OVERFLOW;
/// Indicates that output data (or a gap) was inserted, possibly because the stream
/// callback is using too much CPU time.
const OUTPUT_UNDERFLOW = ffi::OUTPUT_UNDERFLOW;
/// Indicates that output data will be discarded because no room is available.
const OUTPUT_OVERFLOW = ffi::OUTPUT_OVERFLOW;
/// Some of all of the output data will be used to prime the stream, input data may be
/// zero.
const PRIMING_OUTPUT = ffi::PRIMING_OUTPUT;
}
}
impl ::std::fmt::Display for CallbackFlags {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(
f,
"{:?}",
match self.bits() {
ffi::PA_NO_FLAG => "NO_FLAG",
ffi::INPUT_UNDERFLOW => "INPUT_UNDERFLOW",
ffi::INPUT_OVERFLOW => "INPUT_OVERFLOW",
ffi::OUTPUT_UNDERFLOW => "OUTPUT_UNDERFLOW",
ffi::OUTPUT_OVERFLOW => "OUTPUT_OVERFLOW",
ffi::PRIMING_OUTPUT => "PRIMING_INPUT",
_ => "<Unknown StreamCallbackFlags>",
}
)
}
}
}
/// Timing information for the buffers passed to the stream callback.
///
/// Time values are expressed in seconds and are synchronised with the time base used by
/// `Stream::time` method for the associated stream.
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct CallbackTimeInfo {
/// The time when the first sample of the input buffer was captured by the
pub input_buffer_adc_time: Time,
/// The time when the tream callback was invoked.
pub current_time: Time,
pub output_buffer_dac_time: Time,
}
/// A structure containing unchanging information about an open stream.
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
#[repr(C)]
pub struct Info {
/// Struct version
pub struct_version: i32,
/// The input latency for this open stream
pub input_latency: Time,
/// The output latency for this open stream
pub output_latency: Time,
/// The sample rate for this open stream
pub sample_rate: f64,
}
impl From<ffi::PaStreamInfo> for Info {
fn from(info: ffi::PaStreamInfo) -> Info {
Info {
struct_version: info.structVersion,
input_latency: info.inputLatency,
output_latency: info.outputLatency,
sample_rate: info.sampleRate,
}
}
}
impl<B> Mode for Blocking<B> {}
impl Mode for NonBlocking {}
impl<S: Sample> Parameters<S> {
/// Converts the given `C_PaStreamParameters` into their respective **Parameters**.
///
/// Returns `None` if the `sample_format` differs to that of the **S** **Sample** parameter.
///
/// Returns `None` if the `device` index is neither a valid index or a
/// `UseHostApiSpecificDeviceSpecification` flag.
pub fn from_c_params(c_params: ffi::PaStreamParameters) -> Option<Self> {
let sample_format_flags: SampleFormatFlags = c_params.sampleFormat.into();
let is_interleaved =
!sample_format_flags.contains(sample_format_flags::SampleFormatFlags::NON_INTERLEAVED);
let c_sample_format = SampleFormat::from_flags(c_params.sampleFormat.into());
if S::sample_format() != c_sample_format {
return None;
}
let device = match c_params.device {
n if n >= 0 => DeviceIndex(n as u32).into(),
-1 => DeviceKind::UseHostApiSpecificDeviceSpecification,
_ => return None,
};
Some(Parameters {
device,
channel_count: c_params.channelCount,
suggested_latency: c_params.suggestedLatency,
is_interleaved,
sample_format: std::marker::PhantomData,
})
}
}
impl<S: Sample> From<Parameters<S>> for ffi::PaStreamParameters {
/// Converts the **Parameters** into its matching `C_PaStreamParameters`.
fn from(params: Parameters<S>) -> Self {
let Parameters {
device,
channel_count,
suggested_latency,
is_interleaved,
..
} = params;
let sample_format = S::sample_format();
let mut sample_format_flags = sample_format.flags();
if !is_interleaved {
sample_format_flags.insert(sample_format_flags::SampleFormatFlags::NON_INTERLEAVED);
}
ffi::PaStreamParameters {
device: device.into(),
channelCount: channel_count as raw::c_int,
sampleFormat: sample_format_flags.bits(),
suggestedLatency: suggested_latency,
hostApiSpecificStreamInfo: ptr::null_mut(),
}
}
}
impl<I> Settings for InputSettings<I> {
type Flow = Input<I>;
fn into_flow_and_settings(self) -> (Self::Flow, f64, u32, Flags) {
let InputSettings {
params,
sample_rate,
frames_per_buffer,
flags,
} = self;
let flow = Input { params };
(flow, sample_rate, frames_per_buffer, flags)
}
}
impl<O> Settings for OutputSettings<O> {
type Flow = Output<O>;
fn into_flow_and_settings(self) -> (Self::Flow, f64, u32, Flags) {
let OutputSettings {
params,
sample_rate,
frames_per_buffer,
flags,
} = self;
let flow = Output { params };
(flow, sample_rate, frames_per_buffer, flags)
}
}
impl<I, O> Settings for DuplexSettings<I, O> {
type Flow = Duplex<I, O>;
fn into_flow_and_settings(self) -> (Self::Flow, f64, u32, Flags) {
let DuplexSettings {
in_params,
out_params,
sample_rate,
frames_per_buffer,
flags,
} = self;
let flow = Duplex {
in_params,
out_params,
};
(flow, sample_rate, frames_per_buffer, flags)
}
}
impl Buffer {
/// Construct a new **Buffer** for transferring audio on a stream with the given format.
fn new<S>(frames_per_buffer: u32, channel_count: i32) -> Buffer {
let sample_format_bytes = ::std::mem::size_of::<S>() as libc::size_t;
let n_frames = frames_per_buffer as libc::size_t;
let n_channels = channel_count as libc::size_t;
let malloc_size = sample_format_bytes * n_frames * n_channels;
Buffer {
data: unsafe { libc::malloc(malloc_size) as *mut libc::c_void },
}
}
/// Convert the **Buffer**'s data field into a slice with the given format.
unsafe fn slice<S>(&self, frames: u32, channels: i32) -> &[S] {
let len = (frames * channels as u32) as usize;
// TODO: At the moment, we assume this buffer is interleaved. We need to check whether
// or not buffer is interleaved here. This should probably an extra type parameter
// (along-side the Sample type param).
std::slice::from_raw_parts(self.data as *const S, len)
}
/// Convert the **Buffer**'s data field into a mutable slice with the given format.
unsafe fn slice_mut<S>(&mut self, frames: u32, channels: i32) -> &mut [S] {
let len = (frames * channels as u32) as usize;
// TODO: At the moment, we assume this buffer is interleaved. We need to check whether
// or not buffer is interleaved here. This should probably an extra type parameter
// (along-side the Sample type param).
std::slice::from_raw_parts_mut(self.data as *mut S, len)
}
}
impl Drop for Buffer {
fn drop(&mut self) {
unsafe { libc::free(self.data) }
}
}
fn open_blocking_stream(
in_params: Option<ffi::PaStreamParameters>,
out_params: Option<ffi::PaStreamParameters>,
sample_rate: f64,
frames_per_buffer: u32,
flags: Flags,
) -> Result<*mut raw::c_void, Error> {
// The pointer to which PortAudio will attach the stream.
let mut c_stream_ptr: *mut raw::c_void = ptr::null_mut();
let in_c_params = in_params;
let out_c_params = out_params;
let in_c_params_ptr = in_c_params
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null());
let out_c_params_ptr = out_c_params
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null());
let c_flags = flags.bits();
// open the PortAudio stream.
unsafe {
let error_code = ffi::Pa_OpenStream(
&mut c_stream_ptr,
in_c_params_ptr,
out_c_params_ptr,
sample_rate,
frames_per_buffer as raw::c_ulong,
c_flags,
None,
ptr::null_mut(),
);
let error = FromPrimitive::from_i32(error_code).unwrap();
match error {
Error::NoError => Ok(c_stream_ptr),
err => Err(err),
}
}
}
fn open_non_blocking_stream(
in_params: Option<ffi::PaStreamParameters>,
out_params: Option<ffi::PaStreamParameters>,
sample_rate: f64,
frames_per_buffer: u32,
flags: Flags,
callback: &mut CallbackFnWrapper,
) -> Result<*mut raw::c_void, Error> {
// The pointer to which PortAudio will attach the stream.
let mut c_stream_ptr: *mut raw::c_void = ptr::null_mut();
let in_c_params = in_params;
let out_c_params = out_params;
let in_c_params_ptr = in_c_params
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null());
let out_c_params_ptr = out_c_params
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null());
let c_flags = flags.bits();
// Here we create an alias to the `Box` ptr held by the `CallbackFnWrapper`. We do this in
// order to pass the pointer to the Pa_OpenStream function so that we may use it later as
// `user_data` within the `stream_callback_proc`. The reason we don't pass ownership entirely
// is so that we can still automatically clean up the data when the **Stream** (owner of the
// `CallbackFnWrapper`) falls out of scope.
// We know that this is safe because:
// 1. We never call the aliased function ourselves.
// 2. We always stop the stream and in turn stop the PortAudio lib from accessing the function
// before dropping it.
// 3. The aliased function is a private member and can't be accessed outside this module.
let user_data = {
let callback_fn_ptr = callback as *mut CallbackFnWrapper;
callback_fn_ptr as *mut raw::c_void
};
// open the PortAudio stream.
unsafe {
let error_code = ffi::Pa_OpenStream(
&mut c_stream_ptr,
in_c_params_ptr,
out_c_params_ptr,
sample_rate,
frames_per_buffer as raw::c_ulong,
c_flags,
Some(stream_callback_proc),
user_data,
);
let error = FromPrimitive::from_i32(error_code).unwrap();
match error {
Error::NoError => Ok(c_stream_ptr),
err => Err(err),
}
}
}
impl<M, F> Stream<M, F> {
fn new_unopened(mode: M, flow: F, life: std::sync::Arc<super::Life>) -> Self {
Stream {
pa_stream: ptr::null_mut(),
mode,
flow,
port_audio_life: life,
}
}
/// Closes an audio stream.
///
/// If the audio stream is active it discards any pending buffers as if Stream::abort had been
/// called.
pub fn close(&mut self) -> Result<(), Error> {
let error_code = unsafe { ffi::Pa_CloseStream(self.pa_stream) };
let error = FromPrimitive::from_i32(error_code).unwrap();
match error {
Error::NoError => Ok(()),
err => Err(err),
}
}
/// Commences audio processing.
pub fn start(&mut self) -> Result<(), Error> {
let error_code = unsafe { ffi::Pa_StartStream(self.pa_stream) };
let error = FromPrimitive::from_i32(error_code).unwrap();
match error {
0 => Ok(()),
err => Err(FromPrimitive::from_i32(err).unwrap()),
}
}
/// Terminates audio processing.
///
/// It waits until all pending audio buffers have been played before it returns.
pub fn stop(&mut self) -> Result<(), Error> {
let error_code = unsafe { ffi::Pa_StopStream(self.pa_stream) };
let error = FromPrimitive::from_i32(error_code).unwrap();
match error {
0 => Ok(()),
err => Err(FromPrimitive::from_i32(err).unwrap()),
}
}
/// Terminates audio processing immediately without waiting for pending buffers to complete.
pub fn abort(&mut self) -> Result<(), Error> {
let error_code = unsafe { ffi::Pa_AbortStream(self.pa_stream) };
let error = FromPrimitive::from_i32(error_code).unwrap();
match error {
0 => Ok(()),
err => Err(FromPrimitive::from_i32(err).unwrap()),
}
}
/// Determine whether the stream is stopped.
///
/// A stream is considered to be stopped prior to a successful call to start_stream and after a
/// successful call to stop_stream or abort_stream.
///
/// If a stream callback returns a value other than Continue the stream is NOT considered to be
/// stopped.
///
/// Return `true` when the stream is stopped.
///
/// Returns `false` when the stream is running.
///
/// Returnes `Error` if an error is encountered.
///
/// TODO: Clarify what errors can actually an occur.
pub fn is_stopped(&self) -> Result<bool, Error> {
let error_code = unsafe { ffi::Pa_IsStreamStopped(self.pa_stream) };
match error_code {
1 => Ok(true),
0 => Ok(false),
err => Err(FromPrimitive::from_i32(err).unwrap()),
}
}
/// Determine whether the stream is active.
///
/// A stream is active after a successful call to `Stream::start`, until it becomes inactive
/// either as a result of a call to `Stream::stop` or `Stream::abort`, or as a result of a
/// return value other than `Continue` from the stream callback. In the latter case, the stream
/// is considered inactive after the last buffer has finished playing.
///
/// Returns `true` when the stream is active (ie playing or recording audio).
///
/// Returns `false` when not playing.
///
/// Returns an `Error` if an error is encountered.
///
/// TODO: Clarify what errors can actually an occur.
pub fn is_active(&self) -> Result<bool, Error> {
let error_code = unsafe { ffi::Pa_IsStreamActive(self.pa_stream) };
match error_code {
0 => Ok(false),
1 => Ok(true),
err => Err(FromPrimitive::from_i32(err).unwrap()),
}
}
/// Returns the current time in seconds for a stream according to the same clock used to
/// generate callback CallbackTimeInfo timestamps.
///
/// The time values are monotonically increasing and have unspecified origin.
///
/// This returns valid time values for the entire life of the stream, from when the stream is
/// opened until it is closed.
///
/// Starting and stopping the stream does not affect the passage of time returned by this
/// method.
///
/// Returns the stream's current time in seconds, or 0 if an error occurred.
pub fn time(&self) -> Time {
unsafe { ffi::Pa_GetStreamTime(self.pa_stream) }
}
/// Retrieve a Info structure containing information about the stream.
pub fn info(&self) -> Info {
unsafe {
let info = ffi::Pa_GetStreamInfo(self.pa_stream);
Info::from(*info)
}
}
/// This function is solely for use within the extension modules for interacting with PortAudio
/// platform-specific extension APIs.
pub fn unsafe_pa_stream(&self) -> *mut ffi::PaStream {
self.pa_stream
}
}
impl<F> Stream<Blocking<F::Buffer>, F>
where
F: Flow,
{
/// Open a new **Blocking** **Stream** with the given **Flow** and settings.
pub fn open<S>(life: std::sync::Arc<super::Life>, settings: S) -> Result<Self, Error>
where
S: Settings<Flow = F>,
{
let (flow, sample_rate, frames_per_buffer, flags) = settings.into_flow_and_settings();
let buffer = flow.new_buffer(frames_per_buffer);
let blocking = Blocking { buffer };
let (in_params, out_params) = flow.params_both_directions();
let mut stream = Stream::new_unopened(blocking, flow, life);
open_blocking_stream(in_params, out_params, sample_rate, frames_per_buffer, flags).map(
|pa_stream| {
stream.pa_stream = pa_stream;
stream
},
)
}
}
impl<F> Stream<Blocking<F::Buffer>, F>
where
F: Flow + Reader,
{
/// Retrieve the number of frames that can be read from the stream without waiting.
///
/// Returns a Result with either:
/// - An Ok variant with a `Available` enum describing either:
/// - The number of frames available to be read from the stream (without blocking or busy
/// waiting) or
/// - Flags indicating whether or not there has been input overflow or output underflow.
/// - An Err variant in the case PortAudio is not initialized or some error is encountered.
///
/// See the blocking.rs example for a usage example.
pub fn read_available(&self) -> Result<Available, Error> {
match unsafe { ffi::Pa_GetStreamReadAvailable(self.pa_stream) } {
n if n >= 0 => Ok(Available::Frames(n)),
n => match FromPrimitive::from_i64(n as i64) {
Some(Error::InputOverflowed) => Ok(Available::InputOverflowed),
Some(Error::OutputUnderflowed) => Ok(Available::OutputUnderflowed),
Some(err) => Err(err),
_ => panic!("Undefined error code: {:?}", n),
},
}
}
/// Read samples from an input stream.
///
/// The function doesn't return until the entire buffer has been filled - this may involve
/// waiting for the operating system to supply the data.
///
/// # Arguments
/// * frames - The number of frames in the buffer.
///
/// Returns an interleaved slice containing the read audio data.
///
/// Returns an `Error` if some error occurred.
///
/// TODO: Research and document exactly what errors can occur.
pub fn read(&self, frames: u32) -> Result<&[F::Sample], Error> {
let buffer = F::readable_buffer(&self.mode);
let err = unsafe {
ffi::Pa_ReadStream(
self.pa_stream,
buffer.data as *mut raw::c_void,
frames as raw::c_ulong,
)
};
match err {
0 => unsafe {
let channel_count = Reader::channel_count(&self.flow);
Ok(buffer.slice(frames, channel_count))
},
err => Err(FromPrimitive::from_i32(err).unwrap()),
}
}
}
impl<F> Stream<Blocking<F::Buffer>, F>
where
F: Flow + Writer,
{
/// Retrieve the number of frames that can be written to the stream without waiting.
///
/// Returns a Result with either:
/// - An Ok variant with a `Available` enum describing either:
/// - The number of frames available to be written to the stream (without blocking or busy
/// waiting) or
/// - Flags indicating whether or not there has been input overflow or output underflow.
/// - An Err variant in the case PortAudio is not initialized or some error is encountered.
///
/// See the blocking.rs example for a usage example.
pub fn write_available(&self) -> Result<Available, Error> {
match unsafe { ffi::Pa_GetStreamWriteAvailable(self.pa_stream) } {
n if n >= 0 => Ok(Available::Frames(n)),
n => match FromPrimitive::from_i64(n as i64) {
Some(Error::InputOverflowed) => Ok(Available::InputOverflowed),
Some(Error::OutputUnderflowed) => Ok(Available::OutputUnderflowed),
Some(err) => Err(err),
_ => panic!("Undefined error code: {:?}", n),
},
}
}
/// Write samples to an output stream.
///
/// This function doesn't return until the entire buffer has been consumed
///
/// - this may involve waiting for the operating system to consume the data.
///
/// # Arguments
/// * frames - The number of frames in the buffer.
/// * write_fn - The buffer contains samples in the format specified by S.
///
/// Returns Ok(()) on success and an Err(Error) variant on failure.
pub fn write<WF>(&mut self, frames: u32, write_fn: WF) -> Result<(), Error>
where
WF: for<'b> FnOnce(&'b mut [F::Sample]),
{
let pa_stream = self.pa_stream;
let channels = Writer::channel_count(&self.flow);
let out_buffer = F::writable_buffer(&mut self.mode);
let written_slice = {
let slice = unsafe { out_buffer.slice_mut(frames, channels) };
write_fn(slice);
slice
};
let result = unsafe {
let written_slice_ptr = written_slice.as_ptr() as *mut raw::c_void;
ffi::Pa_WriteStream(pa_stream, written_slice_ptr, frames as raw::c_ulong)
};
match result {
0 => Ok(()),
err => Err(FromPrimitive::from_i32(err).unwrap()),
}
}
}
impl<F> Stream<NonBlocking, F> {
/// Open a new **NonBlocking** **Stream** with the given **Flow** and settings.
pub fn open<S, C>(
life: std::sync::Arc<super::Life>,
settings: S,
mut callback: C,
) -> Result<Self, Error>
where
S: Settings<Flow = F>,
F: Flow,
C: FnMut(F::CallbackArgs) -> ffi::PaStreamCallbackResult + 'static,
{
let (flow, sample_rate, frames_per_buffer, flags) = settings.into_flow_and_settings();
let (in_params, out_params) = flow.params_both_directions();
let in_channels = in_params.map(|p| p.channelCount).unwrap_or(0);
let out_channels = out_params.map(|p| p.channelCount).unwrap_or(0);
let callback_wrapper_fn = move |input: *const raw::c_void,
output: *mut raw::c_void,
frame_count: raw::c_ulong,
time_info: *const ffi::PaStreamCallbackTimeInfo,
flags: ffi::PaStreamCallbackFlags|
-> ffi::PaStreamCallbackResult {
let args = unsafe {
F::new_callback_args(
input,
output,
frame_count,
time_info,
flags,
in_channels,
out_channels,
)
};
callback(args)
};
let non_blocking = NonBlocking {
// Here, we `Box` the wrapper so that we can collect the pointer from the callback.
//
// TODO: See if it is possible to pass a ptr to the callback_fn itself instead of
// requiring the wrapper at all. It seems like DST will be a problem here though.
callback: Box::new(CallbackFnWrapper {
// Here we `Box` the callback fn as we can't handle generic types in the c callback
// function.
f: Box::new(callback_wrapper_fn),
}),
};
let mut stream = Stream::new_unopened(non_blocking, flow, life);
open_non_blocking_stream(
in_params,
out_params,
sample_rate,
frames_per_buffer,
flags,
&mut stream.mode.callback,
)
.map(|pa_stream| {
stream.pa_stream = pa_stream;
stream
})
}
/// Retrieve CPU usage information for the specified stream.
///
/// The "CPU Load" is a fraction of total CPU time consumed by a callback stream's audio
/// processing routines including, but not limited to the client supplied stream callback.
pub fn cpu_load(&self) -> f64 {
unsafe { ffi::Pa_GetStreamCpuLoad(self.pa_stream) }
}
}
impl<M, F> Drop for Stream<M, F> {
fn drop(&mut self) {
self.stop().ok();
self.close().ok();
}
}
/// A callback procedure to be used by portaudio in the case that a user_callback has been given
/// upon opening the stream (`Stream::open`).
extern "C" fn stream_callback_proc(
input: *const raw::c_void,
output: *mut raw::c_void,
frame_count: raw::c_ulong,
time_info: *const ffi::PaStreamCallbackTimeInfo,
flags: ffi::PaStreamCallbackFlags,
user_callback_ptr: *mut raw::c_void,
) -> ffi::PaStreamCallbackResult {
let callback = user_callback_ptr as *mut CallbackFnWrapper;
unsafe { ((*callback).f)(input, output, frame_count, time_info, flags) }
}