#![warn(missing_docs)]
#[macro_use]
extern crate bitflags;
extern crate libc;
extern crate num;
extern crate weresocool_portaudio_sys as ffi;
use num::FromPrimitive;
use std::option::Option;
use std::os::raw;
pub use error::Error;
pub use ffi::{
PaStreamCallbackResult as StreamCallbackResult, PA_ABORT as Abort, PA_COMPLETE as Complete,
PA_CONTINUE as Continue,
};
pub use stream::{
callback_flags as stream_callback_flags, flags as stream_flags, Available as StreamAvailable,
Blocking, CallbackFlags as StreamCallbackFlags, CallbackTimeInfo as StreamCallbackTimeInfo,
Duplex, DuplexCallbackArgs as DuplexStreamCallbackArgs, DuplexSettings as DuplexStreamSettings,
Flags as StreamFlags, Flow, Info as StreamInfo, Input,
InputCallbackArgs as InputStreamCallbackArgs, InputSettings as InputStreamSettings,
NonBlocking, Output, OutputCallbackArgs as OutputStreamCallbackArgs,
OutputSettings as OutputStreamSettings, Parameters as StreamParameters,
Settings as StreamSettings, Stream,
};
pub use types::{
DeviceIndex, DeviceInfo, Frames, HostApiIndex, HostApiInfo, HostApiTypeId, HostErrorInfo,
SampleFormat, Time, FRAMES_PER_BUFFER_UNSPECIFIED,
};
use std::ptr;
#[macro_use]
mod enum_primitive;
pub mod error;
pub mod ext;
pub mod stream;
mod types;
#[derive(Debug)]
pub struct PortAudio {
life: std::sync::Arc<Life>,
}
#[derive(Debug)]
pub struct Life {
is_terminated: std::sync::Mutex<bool>,
}
impl PortAudio {
pub fn new() -> Result<Self, Error> {
unsafe {
let error = FromPrimitive::from_i32(ffi::Pa_Initialize()).unwrap();
match error {
Error::NoError => {
let life = std::sync::Arc::new(Life {
is_terminated: std::sync::Mutex::new(false),
});
Ok(PortAudio { life })
}
err => Err(err),
}
}
}
pub fn terminate(self) -> Result<(), Error> {
*self.life.is_terminated.lock().unwrap() = true;
terminate()
}
pub fn version(&self) -> i32 {
version()
}
pub fn version_text(&self) -> Result<&'static str, ::std::str::Utf8Error> {
version_text()
}
pub fn devices(&self) -> Result<Devices, Error> {
Ok(Devices {
total: self.device_count()?,
next: 0,
port_audio: self,
})
}
pub fn device_count(&self) -> Result<u32, Error> {
match unsafe { ffi::Pa_GetDeviceCount() } {
n if n >= 0 => Ok(n as u32),
-1 => Ok(0),
err => Err(::num::FromPrimitive::from_i32(err).unwrap()),
}
}
pub fn default_input_device(&self) -> Result<DeviceIndex, Error> {
match unsafe { ffi::Pa_GetDefaultInputDevice() } {
idx if idx >= 0 => Ok(DeviceIndex(idx as u32)),
err => Err(::num::FromPrimitive::from_i32(err).unwrap()),
}
}
pub fn default_output_device(&self) -> Result<DeviceIndex, Error> {
match unsafe { ffi::Pa_GetDefaultOutputDevice() } {
idx if idx >= 0 => Ok(DeviceIndex(idx as u32)),
err => Err(::num::FromPrimitive::from_i32(err).unwrap()),
}
}
pub fn device_info(&self, device: DeviceIndex) -> Result<DeviceInfo, Error> {
let c_info = unsafe { ffi::Pa_GetDeviceInfo(device.into()) };
if c_info.is_null() {
Err(Error::InvalidDevice)
} else {
Ok(DeviceInfo::from_c_info(unsafe { *c_info }))
}
}
pub fn host_apis(&self) -> HostApis {
HostApis {
total: self.host_api_count().unwrap_or(0),
next: 0,
port_audio: self,
}
}
pub fn host_api_count(&self) -> Result<HostApiIndex, Error> {
unsafe { result_from_host_api_index(ffi::Pa_GetHostApiCount()) }
}
pub fn default_host_api(&self) -> Result<HostApiIndex, Error> {
unsafe { result_from_host_api_index(ffi::Pa_GetDefaultHostApi()) }
}
pub fn host_api_info(&self, host_api: HostApiIndex) -> Option<HostApiInfo> {
let c_host_info = unsafe { ffi::Pa_GetHostApiInfo(host_api as HostApiIndex) };
if c_host_info.is_null() {
None
} else {
HostApiInfo::from_c_info(unsafe { *c_host_info })
}
}
pub fn host_api_type_id_to_host_api_index(
&self,
type_id: HostApiTypeId,
) -> Result<HostApiIndex, Error> {
let id = FromPrimitive::from_i32(type_id as i32).unwrap();
unsafe { result_from_host_api_index(ffi::Pa_HostApiTypeIdToHostApiIndex(id)) }
}
pub fn api_device_index_to_device_index(
&self,
host_api: HostApiIndex,
host_api_device_index: i32,
) -> Result<DeviceIndex, Error> {
let result =
unsafe { ffi::Pa_HostApiDeviceIndexToDeviceIndex(host_api, host_api_device_index) };
match result {
idx if idx >= 0 => Ok(DeviceIndex(idx as u32)),
err => Err(::num::FromPrimitive::from_i32(err).unwrap()),
}
}
pub fn is_input_format_supported<I>(
&self,
params: StreamParameters<I>,
sample_rate: f64,
) -> Result<(), Error>
where
I: Sample,
{
is_format_supported(Some(params.into()), None, sample_rate)
}
pub fn is_output_format_supported<O>(
&self,
params: StreamParameters<O>,
sample_rate: f64,
) -> Result<(), Error>
where
O: Sample,
{
is_format_supported(None, Some(params.into()), sample_rate)
}
pub fn is_duplex_format_supported<I, O>(
&self,
in_params: StreamParameters<I>,
out_params: StreamParameters<O>,
sample_rate: f64,
) -> Result<(), Error>
where
I: Sample,
O: Sample,
{
is_format_supported(Some(in_params.into()), Some(out_params.into()), sample_rate)
}
#[allow(clippy::type_complexity)]
pub fn open_blocking_stream<S>(
&self,
settings: S,
) -> Result<Stream<Blocking<<S::Flow as Flow>::Buffer>, S::Flow>, Error>
where
S: StreamSettings,
S::Flow: Flow,
{
Stream::<Blocking<<S::Flow as Flow>::Buffer>, S::Flow>::open(self.life.clone(), settings)
}
pub fn open_non_blocking_stream<S, C>(
&self,
settings: S,
callback: C,
) -> Result<Stream<NonBlocking, S::Flow>, Error>
where
S: StreamSettings,
S::Flow: Flow,
C: FnMut(<S::Flow as Flow>::CallbackArgs) -> ffi::PaStreamCallbackResult + 'static,
{
Stream::<NonBlocking, S::Flow>::open(self.life.clone(), settings, callback)
}
pub fn default_input_stream_params<I>(
&self,
channels: i32,
) -> Result<StreamParameters<I>, Error> {
const INTERLEAVED: bool = true;
let device = self.default_input_device()?;
let latency = self.device_info(device)?.default_low_input_latency;
Ok(StreamParameters::new(
device,
channels,
INTERLEAVED,
latency,
))
}
pub fn default_output_stream_params<O>(
&self,
channels: i32,
) -> Result<StreamParameters<O>, Error> {
const INTERLEAVED: bool = true;
let device = self.default_output_device()?;
let latency = self.device_info(device)?.default_low_output_latency;
Ok(StreamParameters::new(
device,
channels,
INTERLEAVED,
latency,
))
}
pub fn default_input_stream_settings<I>(
&self,
channels: i32,
sample_rate: f64,
frames_per_buffer: u32,
) -> Result<InputStreamSettings<I>, Error> {
let params = self.default_input_stream_params(channels)?;
Ok(InputStreamSettings::new(
params,
sample_rate,
frames_per_buffer,
))
}
pub fn default_output_stream_settings<O>(
&self,
channels: i32,
sample_rate: f64,
frames_per_buffer: u32,
) -> Result<OutputStreamSettings<O>, Error> {
let params = self.default_output_stream_params(channels)?;
Ok(OutputStreamSettings::new(
params,
sample_rate,
frames_per_buffer,
))
}
pub fn default_duplex_stream_settings<I, O>(
&self,
in_channels: i32,
out_channels: i32,
sample_rate: f64,
frames_per_buffer: u32,
) -> Result<DuplexStreamSettings<I, O>, Error> {
let in_params = self.default_input_stream_params(in_channels)?;
let out_params = self.default_output_stream_params(out_channels)?;
Ok(DuplexStreamSettings::new(
in_params,
out_params,
sample_rate,
frames_per_buffer,
))
}
pub fn sleep(&self, m_sec: i32) {
unsafe { ffi::Pa_Sleep(m_sec as raw::c_long) }
}
pub fn last_host_error_info(&self) -> HostErrorInfo {
let c_error = unsafe { ffi::Pa_GetLastHostErrorInfo() };
HostErrorInfo::from_c_error_info(unsafe { *c_error })
}
}
impl Drop for Life {
fn drop(&mut self) {
if !*self.is_terminated.lock().unwrap() {
terminate().ok();
}
}
}
pub fn version() -> i32 {
unsafe { ffi::Pa_GetVersion() }
}
pub fn version_text() -> Result<&'static str, ::std::str::Utf8Error> {
unsafe { ffi::c_str_to_str(ffi::Pa_GetVersionText()) }
}
fn terminate() -> Result<(), Error> {
unsafe {
let error = FromPrimitive::from_i32(ffi::Pa_Terminate()).unwrap();
match error {
Error::NoError => Ok(()),
err => Err(err),
}
}
}
fn is_format_supported(
maybe_input_parameters: Option<ffi::PaStreamParameters>,
maybe_output_parameters: Option<ffi::PaStreamParameters>,
sample_rate: f64,
) -> Result<(), Error> {
let c_input = maybe_input_parameters
.as_ref()
.map(|input| input as *const _);
let c_output = maybe_output_parameters
.as_ref()
.map(|output| output as *const _);
if c_input.is_none() && c_output.is_none() {
Err(Error::InvalidDevice)
} else {
unsafe {
let error_code = ffi::Pa_IsFormatSupported(
c_input.unwrap_or(ptr::null()),
c_output.unwrap_or(ptr::null()),
sample_rate as raw::c_double,
);
let error = FromPrimitive::from_i32(error_code).unwrap();
match error {
Error::NoError => Ok(()),
err => Err(err),
}
}
}
}
pub struct Devices<'a> {
total: u32,
next: u32,
port_audio: &'a PortAudio,
}
#[derive(Clone, Debug)]
pub struct HostApis<'a> {
total: HostApiIndex,
next: HostApiIndex,
port_audio: &'a PortAudio,
}
impl<'a> Iterator for Devices<'a> {
type Item = Result<(DeviceIndex, DeviceInfo<'a>), Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.next < self.total {
let idx = DeviceIndex(self.next);
self.next += 1;
return Some(self.port_audio.device_info(idx).map(|info| (idx, info)));
}
None
}
}
impl<'a> Iterator for HostApis<'a> {
type Item = (HostApiIndex, HostApiInfo<'a>);
fn next(&mut self) -> Option<Self::Item> {
while self.next < self.total {
let idx = self.next;
self.next += 1;
if let Some(info) = self.port_audio.host_api_info(idx) {
return Some((idx, info));
}
}
None
}
}
fn result_from_host_api_index(idx: ffi::PaHostApiIndex) -> Result<HostApiIndex, Error> {
match idx {
idx if idx >= 0 => Ok(idx),
err => Err(::num::FromPrimitive::from_i32(err).unwrap()),
}
}
pub fn get_sample_size(format: SampleFormat) -> Result<u8, Error> {
let result = unsafe { ffi::Pa_GetSampleSize(format as ffi::PaSampleFormat) };
if result < 0 {
Err(::num::FromPrimitive::from_i32(result).unwrap())
} else {
Ok(result as u8)
}
}
mod private {
use super::types::SampleFormat;
use num::{FromPrimitive, ToPrimitive};
use std::ops::{Add, Div, Mul, Sub};
pub trait SamplePrivate:
::std::default::Default
+ Copy
+ Clone
+ ::std::fmt::Debug
+ ToPrimitive
+ FromPrimitive
+ Add
+ Sub
+ Mul
+ Div
{
fn size<S: SamplePrivate>() -> usize {
::std::mem::size_of::<S>()
}
fn to_sample_format() -> SampleFormat;
}
}
impl private::SamplePrivate for f32 {
fn to_sample_format() -> SampleFormat {
SampleFormat::F32
}
}
impl private::SamplePrivate for i32 {
fn to_sample_format() -> SampleFormat {
SampleFormat::I32
}
}
impl private::SamplePrivate for i16 {
fn to_sample_format() -> SampleFormat {
SampleFormat::I16
}
}
impl private::SamplePrivate for i8 {
fn to_sample_format() -> SampleFormat {
SampleFormat::I8
}
}
impl private::SamplePrivate for u8 {
fn to_sample_format() -> SampleFormat {
SampleFormat::U8
}
}
pub trait Sample: private::SamplePrivate {
fn sample_format() -> SampleFormat {
Self::to_sample_format()
}
}
impl Sample for f32 {}
impl Sample for i32 {}
impl Sample for i16 {}
impl Sample for i8 {}
impl Sample for u8 {}