Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1,488 @@
//! Typification of the various AudioFormat codes and flags offered by the Core Audio API.
//!
//! See the Core Audio Data Types Reference
//! [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/Audio_Data_Format_Identifiers) for more info.
use std::os::raw::c_uint;
/// A type-safe representation of both the `AudioFormatId` and their associated flags.
#[derive(Copy, Clone, Debug)]
#[allow(non_camel_case_types)]
pub enum AudioFormat {
/// Linear PCM; a non-compressed audio data format with one frame per packet.
///
/// **Available** in OS X v10.0 and later.
LinearPCM(LinearPcmFlags), // = 1819304813,
/// An AC-3 codec.
///
/// **Available** in OS X v10.2 and later.
AC3, // = 1633889587,
/// AC-3 codec that provides data packaged for transport over an IEC 60958 compliant digital
/// audio interface.
///
/// **Available** in OS X v10.2 and later.
F60958AC3(StandardFlags), // = 1667326771,
/// Apple's implementation of the IMA 4:1 ADPCM codec.
///
/// **Available** in OS X v10.2 and later.
AppleIMA4, // = 1768775988,
/// MPEG-4 AAC codec.
///
/// **Available** in OS X v10.2 and later.
MPEG4AAC(Mpeg4ObjectId), // = 1633772320,
/// MPEG-4 CELP codec.
///
/// **Available** in OS X v10.2 and later.
MPEG4CELP(Mpeg4ObjectId), // = 1667591280,
/// MPEG-4 HVXC codec.
///
/// **Available** in OS X v10.2 and later.
MPEG4HVXC(Mpeg4ObjectId), // = 1752594531,
/// MPEG-4 TwinVQ codec.
///
/// **Available** in OS X v10.2 and later.
MPEG4TwinVQ(Mpeg4ObjectId), // = 1953986161,
/// MACE 3:1.
///
/// **Available** in OS X v10.3 and later.
MACE3, // = 1296122675,
/// MACE 6:1.
///
/// **Available** in OS X v10.3 and later.
MACE6, // = 1296122678,
/// μLaw 2:1.
///
/// **Available** in OS X v10.3 and later.
ULaw, // = 1970037111,
/// aLaw 2:1.
///
/// **Available** in OS X v10.3 and later.
ALaw, // = 1634492791,
/// QDesign Music.
///
/// **Available** in OS X v10.3 and later.
QDesign, // = 1363430723,
/// QDesign2 Music.
///
/// **Available** in OS X v10.3 and later.
QDesign2, // = 1363430706,
/// QUALCOMM PureVoice.
///
/// **Available** in OS X v10.3 and later.
QUALCOMM, // = 1365470320,
/// MPEG-1/2, Layer 1 audio.
///
/// **Available** in OS X v10.3 and later.
MPEGLayer1, // = 778924081,
/// MPEG-1/2, Layer 2 audio.
///
/// **Available** in OS X v10.3 and later.
MPEGLayer2, // = 778924082,
/// MPEG-1/2, Layer 3 audio.
///
/// **Available** in OS X v10.3 and later.
MPEGLayer3, // = 778924083,
/// A stream of IOAudioTimeStamp structures.
///
/// **Available** in OS X v10.2 and later.
TimeCode(AudioTimeStampFlags), // = 1953066341,
/// A stream of MIDIPacketList structures where the time stamps in the MIDIPacket structures
/// are sample offsets in the stream. The `sample_rate` field in the **StreamFormat** structure
/// is used to describe how time is passed in this kind of stream.
///
/// An audio unit that receives or generates this stream can use this sample rate together with
/// the number of frames it is rendering.
///
/// The sample offsets within the MIDIPacketList can be used to define the time for any MIDI
/// event within the list.
///
/// **Available** in OS X v10.2 and later.
///
/// TODO: Review whether or not this audio format should indicate some fundamental change
/// within the **StreamFormat**.
MIDIStream, // = 1835623529,
/// A "side-chain" of f32 data that can be fed or generated by an audio unit and that is used
/// to send a high density of parameter value control information.
///
/// An audio unit typically runs a parameter value stream at either the sample rate of the
/// audio unit's audio data, or some integer quotient of this (i.e. a half or a third of the
/// sample rate of the audio).
///
/// The `sample_rate` field in the **StreamFormat** type describes this relationship.
///
/// **Available** in OS X v10.2 and later.
ParameterValueStream, // = 1634760307,
/// Apple Lossless format.
///
/// **Available** in OS X v10.3 and later.
AppleLossless(AppleLosslessFlags), // = 1634492771,
/// MPEG-4 High Efficiency AAC audio object.
///
/// **Available** in OS X v10.5 and later.
MPEG4AAC_HE, // = 1633772392,
/// MPEG-4 AAC Low Delay audio object.
///
/// **Available** in OS X v10.5 and later.
MPEG4AAC_LD, // = 1633772396,
/// MPEG-4 AAC Enhanced Low Delay audio object.
///
/// **Available** in OS X v10.7 and later.
MPEG4AAC_ELD, // = 1633772389,
/// MPEG-4 AAC Enhanced Low Delay audio object with SBR (spectral band replication) extension
/// layer.
///
/// **Available** in OS X v10.7 and later.
MPEG4AAC_ELD_SBR, // = 1633772390,
MPEG4AAC_ELD_V2, // = 1633772391,
/// MPEG-4 High Efficiency AAC Version 2 audio object.
///
/// **Available** in OS X v10.5 and later.
MPEG4AAC_HE_V2, // = 1633772400,
/// MPEG-4 Apatial Audio audio object.
///
/// **Available** in OS X v10.5 and later.
MPEG4AAC_Spatial, // = 1633772403,
/// The AMR (adaptive Multi-Rate) narrow band speech codec.
///
/// **Available** in OS X v10.5 and later.
AMR, // = 1935764850,
AMR_WB, // = 1935767394,
/// The codec used for Audible, Inc. audio books.
///
/// **Available** in OS X v10.6 and later.
Audible, // = 1096107074,
/// The iLBC (internet Low Bitrate Codec) narrow band cpeech codec.
///
/// **Available** in OS X v10.6 and later.
iLBC, // = 1768710755,
/// DVI/Intel IMA ADPCM - ACM code 17.
///
/// **Available** in OS X v10.6 and later.
DVIIntelIMA, // = 1836253201,
/// Microsoft GSM 6.10 - ACM code 49.
///
/// **Available** in OS X v10.6 and later.
MicrosoftGSM, // = 1836253233,
/// The format defined by the AES3-2003 standard.
///
/// Adopted into MXF and MPEG-2 containers and SDTI transport streams with SMPTE specs
/// 203M-2002 and 331M-2000.
AES3, // = 1634038579,
}
impl AudioFormat {
/// Convert from the FFI C format and flags to a typesafe Rust enum representation.
pub fn from_format_and_flag(format: c_uint, flag: Option<u32>) -> Option<AudioFormat> {
match (format, flag) {
(1819304813, Some(i)) => Some(AudioFormat::LinearPCM(
LinearPcmFlags::from_bits_truncate(i),
)),
(1633889587, _) => Some(AudioFormat::AC3),
(1667326771, Some(i)) => {
Some(AudioFormat::F60958AC3(StandardFlags::from_bits_truncate(i)))
}
(1768775988, _) => Some(AudioFormat::AppleIMA4),
(1633772320, Some(i)) => Some(AudioFormat::MPEG4AAC(
Mpeg4ObjectId::from_u32(i).expect("Unknown Mpeg4ObjectId"),
)),
(1667591280, Some(i)) => Some(AudioFormat::MPEG4CELP(
Mpeg4ObjectId::from_u32(i).expect("Unknown Mpeg4ObjectId"),
)),
(1752594531, Some(i)) => Some(AudioFormat::MPEG4HVXC(
Mpeg4ObjectId::from_u32(i).expect("Unknown Mpeg4ObjectId"),
)),
(1953986161, Some(i)) => Some(AudioFormat::MPEG4TwinVQ(
Mpeg4ObjectId::from_u32(i).expect("Unknown Mpeg4ObjectId"),
)),
(1296122675, _) => Some(AudioFormat::MACE3),
(1296122678, _) => Some(AudioFormat::MACE6),
(1970037111, _) => Some(AudioFormat::ULaw),
(1634492791, _) => Some(AudioFormat::ALaw),
(1363430723, _) => Some(AudioFormat::QDesign),
(1363430706, _) => Some(AudioFormat::QDesign2),
(1365470320, _) => Some(AudioFormat::QUALCOMM),
(778924081, _) => Some(AudioFormat::MPEGLayer1),
(778924082, _) => Some(AudioFormat::MPEGLayer2),
(778924083, _) => Some(AudioFormat::MPEGLayer3),
(1953066341, Some(i)) => Some(AudioFormat::TimeCode(
AudioTimeStampFlags::from_bits_truncate(i),
)),
(1835623529, _) => Some(AudioFormat::MIDIStream),
(1634760307, _) => Some(AudioFormat::ParameterValueStream),
(1634492771, Some(i)) => Some(AudioFormat::AppleLossless(
AppleLosslessFlags::from_bits_truncate(i),
)),
(1633772392, _) => Some(AudioFormat::MPEG4AAC_HE),
(1633772396, _) => Some(AudioFormat::MPEG4AAC_LD),
(1633772389, _) => Some(AudioFormat::MPEG4AAC_ELD),
(1633772390, _) => Some(AudioFormat::MPEG4AAC_ELD_SBR),
(1633772391, _) => Some(AudioFormat::MPEG4AAC_ELD_V2),
(1633772400, _) => Some(AudioFormat::MPEG4AAC_HE_V2),
(1633772403, _) => Some(AudioFormat::MPEG4AAC_Spatial),
(1935764850, _) => Some(AudioFormat::AMR),
(1935767394, _) => Some(AudioFormat::AMR_WB),
(1096107074, _) => Some(AudioFormat::Audible),
(1768710755, _) => Some(AudioFormat::iLBC),
(1836253201, _) => Some(AudioFormat::DVIIntelIMA),
(1836253233, _) => Some(AudioFormat::MicrosoftGSM),
(1634038579, _) => Some(AudioFormat::AES3),
_ => None,
}
}
/// Convert from the Rust enum to the C format and flag.
pub fn as_format_and_flag(&self) -> (c_uint, Option<u32>) {
match *self {
AudioFormat::LinearPCM(flag) => (1819304813, Some(flag.bits())),
AudioFormat::AC3 => (1633889587, None),
AudioFormat::F60958AC3(flag) => (1667326771, Some(flag.bits())),
AudioFormat::AppleIMA4 => (1768775988, None),
AudioFormat::MPEG4AAC(flag) => (1633772320, Some(flag as u32)),
AudioFormat::MPEG4CELP(flag) => (1667591280, Some(flag as u32)),
AudioFormat::MPEG4HVXC(flag) => (1752594531, Some(flag as u32)),
AudioFormat::MPEG4TwinVQ(flag) => (1953986161, Some(flag as u32)),
AudioFormat::MACE3 => (1296122675, None),
AudioFormat::MACE6 => (1296122678, None),
AudioFormat::ULaw => (1970037111, None),
AudioFormat::ALaw => (1634492791, None),
AudioFormat::QDesign => (1363430723, None),
AudioFormat::QDesign2 => (1363430706, None),
AudioFormat::QUALCOMM => (1365470320, None),
AudioFormat::MPEGLayer1 => (778924081, None),
AudioFormat::MPEGLayer2 => (778924082, None),
AudioFormat::MPEGLayer3 => (778924083, None),
AudioFormat::TimeCode(flag) => (1953066341, Some(flag.bits())),
AudioFormat::MIDIStream => (1835623529, None),
AudioFormat::ParameterValueStream => (1634760307, None),
AudioFormat::AppleLossless(flag) => (1634492771, Some(flag.bits())),
AudioFormat::MPEG4AAC_HE => (1633772392, None),
AudioFormat::MPEG4AAC_LD => (1633772396, None),
AudioFormat::MPEG4AAC_ELD => (1633772389, None),
AudioFormat::MPEG4AAC_ELD_SBR => (1633772390, None),
AudioFormat::MPEG4AAC_ELD_V2 => (1633772391, None),
AudioFormat::MPEG4AAC_HE_V2 => (1633772400, None),
AudioFormat::MPEG4AAC_Spatial => (1633772403, None),
AudioFormat::AMR => (1935764850, None),
AudioFormat::AMR_WB => (1935767394, None),
AudioFormat::Audible => (1096107074, None),
AudioFormat::iLBC => (1768710755, None),
AudioFormat::DVIIntelIMA => (1836253201, None),
AudioFormat::MicrosoftGSM => (1836253233, None),
AudioFormat::AES3 => (1634038579, None),
}
}
}
bitflags! {
/// Standard flags for use in the **F60958AC3** **AudioFormat** variant.
///
/// Note: In the original Core Audio API these are consolidated with what we have named the
/// **StandardFlags** and **AppleLosslessFlags** types under the `AudioFormatFlag` type. We
/// have chosen to separate these for greater type safety and clearer compatibility with
/// the **AudioFormat** type.
///
/// Original documentation [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/AudioStreamBasicDescription_Flags).
pub struct StandardFlags: u32 {
/// Set for floating point, clear for integer.
///
/// **Available** in OS X v10.2 and later.
const IS_FLOAT = 1;
/// Set for big endian, clear for little endian.
///
/// **Available** in OS X v10.2 and later.
const IS_BIG_ENDIAN = 2;
/// Set for signed integer, clear for unsigned integer.
///
/// Note: This is only valid if `IS_FLOAT` is clear.
///
/// **Available** in OS X v10.2 and later.
const IS_SIGNED_INTEGER = 4;
/// Set if the sample bits occupy the entire available bits for the channel, clear if they
/// are high- or low-aligned within the channel.
///
/// **Available** in OS X v10.2 and later.
const IS_PACKED = 8;
/// Set if the sample bits are placed into the high bits of the channel, clear for low bit
/// placement.
///
/// Note: This is only valid if `IS_PACKED` is clear.
///
/// **Available** in OS X v10.2 and later.
const IS_ALIGNED_HIGH = 16;
/// Set if the sample for each channel are located contiguously and the channels are laid
/// out end to end.
///
/// Clear if the samples for each frame are laid out contiguously and the frames laid out
/// end to end.
///
/// **Available** in OS X v10.2 and later.
const IS_NON_INTERLEAVED = 32;
/// Set to indicate when a format is nonmixable.
///
/// Note: that this flag is only used when interacting with the HAL's stream format
/// information. It is **not** valid for any other use.
///
/// **Available** in OS X v10.3 and later.
const IS_NON_MIXABLE = 64;
}
}
bitflags! {
/// Flags for use within the **LinearPCM** **AudioFormat**.
///
/// Note: In the original Core Audio API these are consolidated with what we have named the
/// **StandardFlags** and **AppleLosslessFlags** types under the `AudioFormatFlag` type. We
/// have chosen to separate these for greater type safety and clearer compatibility with
/// the **AudioFormat** type.
///
/// Original documentation [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/AudioStreamBasicDescription_Flags).
pub struct LinearPcmFlags: u32 {
/// Synonmyn for the **IS_FLOAT** **StandardFlags**.
///
/// **Available** in OS X v10.0 and later.
const IS_FLOAT = 1;
/// Synonmyn for the **IS_BIG_ENDIAN** **StandardFlags**.
///
/// **Available** in OS X v10.0 and later.
const IS_BIG_ENDIAN = 2;
/// Synonmyn for the **IS_SIGNED_INTEGER** **StandardFlags**.
///
/// **Available** in OS X v10.0 and later.
const IS_SIGNED_INTEGER = 4;
/// Synonmyn for the **IS_PACKED** **StandardFlags**.
///
/// **Available** in OS X v10.0 and later.
const IS_PACKED = 8;
/// Synonmyn for the **IS_ALIGNED_HIGH** **StandardFlags**.
///
/// **Available** in OS X v10.0 and later.
const IS_ALIGNED_HIGH = 16;
/// Synonmyn for the **IS_NON_INTERLEAVED** **StandardFlags**.
///
/// **Available** in OS X v10.2 and later.
const IS_NON_INTERLEAVED = 32;
/// Synonmyn for the **IS_NON_MIXABLE** **StandardFlags**.
///
/// **Available** in OS X v10.3 and later.
const IS_NON_MIXABLE = 64;
/// The linear PCM flags contain a 6-bit bitfield indicating that an integer format is to
/// be interpreted as fixed point.
///
/// The value indicates the number of bits are used to represent the fractional portion of
/// each sample value.
///
/// This constant indicates the bit position (counting from the right) of the bitfield in
/// `mFormatFlags` field.
///
/// TODO: Review whether or not this flag indicates that we need to treat LinearPCM format
/// uniquely in some way.
///
/// **Available** in OS X v10.6 and later.
const FLAGS_SAMPLE_FRACTION_SHIFT = 7;
/// The number of fractional bits.
///
/// `== (<other_flags> & FLAGS_SAMPLE_FRACTION_MASK) >> FLAGS_SAMPLE_FRACTION_SHIFT`
///
/// **Available** in OS X v10.6 and later.
const FLAGS_SAMPLE_FRACTION_MASK = 8064;
}
}
bitflags! {
/// Flags set for Apple Lossless data.
///
/// **Available** in OS X v10.3 and later.
///
/// Note: In the original Core Audio API these are consolidated with what we have named the
/// **StandardFlags** and **AppleLosslessFlags** types under the `AudioFormatFlag` type. We
/// have chosen to separate these for greater type safety and clearer compatibility with
/// the **AudioFormat** type.
///
/// Original documentation [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/AudioStreamBasicDescription_Flags).
pub struct AppleLosslessFlags: u32 {
/// Sourced from 16 bit native endian signed integer data.
const BIT_16_SOURCE_DATA = 1;
/// Sourced from 20 bit native endian signed integer data aligned high in 24 bits.
const BIT_20_SOURCE_DATA = 2;
/// Sourced from 24 bit native endian signed integer data.
const BIT_24_SOURCE_DATA = 3;
/// Sourced from 32 bit native endian signed integer data.
const BIT_32_SOURCE_DATA = 4;
}
}
/// "Used in the `mFormatFlags` field of an `AudioStreamBasicDescription` structure that
/// describes an MPEG-4 audio stream to specify the type of MPEG-4 audio data.
///
/// **Available** in OS X v10.3 and later.
///
/// **Deprecated** in OS X v10.5.
///
/// Note: This type was originally represented using a bitflag field in the original API, however
/// there is only ever one flag set at a time. Thus, we use an enum as a more accurate,
/// user-friendly, type-safe representation.
///
/// Original documenation
/// [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/MPEG_4_Audio_Object_Type_Constants).
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(non_camel_case_types)]
pub enum Mpeg4ObjectId {
/// Advanced audio coding; the baisc MPEG-4 technology.
AAC_Main = 1,
/// Lossless coding; provides compression with no loss of quality.
AAC_LC = 2,
/// Scalable sampling rate; provides different sampling frequencies for different targets.
AAC_SSR = 3,
/// Long term prediction; reduces redundancy in a coded signal.
AAC_LTP = 4,
/// Spectral band replication; reconstructs high-frequency content from lower frequencies
/// and side information.
AAC_SBR = 5,
/// Scalable lossless coding.
AAC_Scalable = 6,
/// Transform-domain weighted interleaved vector quantization; an audio codec optimised for
/// audio coding at ultra low bit rates around 8kbit/s.
TwinVQ = 7,
/// Code Excited Linear Prdiction; a narrow-band/wide-band speech codec.
CELP = 8,
/// Harmonic Vector Excitation Coding; a very-low bit-rate parametric speech codec.
HVXC = 9,
}
impl Mpeg4ObjectId {
/// Create an Mpeg4ObjectId from a u32.
pub fn from_u32(u: u32) -> Option<Mpeg4ObjectId> {
match u {
1 => Some(Mpeg4ObjectId::AAC_Main),
2 => Some(Mpeg4ObjectId::AAC_LC),
3 => Some(Mpeg4ObjectId::AAC_SSR),
4 => Some(Mpeg4ObjectId::AAC_LTP),
5 => Some(Mpeg4ObjectId::AAC_SBR),
6 => Some(Mpeg4ObjectId::AAC_Scalable),
7 => Some(Mpeg4ObjectId::TwinVQ),
8 => Some(Mpeg4ObjectId::CELP),
9 => Some(Mpeg4ObjectId::HVXC),
_ => None,
}
}
}
bitflags! {
/// "These flags indicate the valuid fields in an AudioTimeStamp structure."
///
/// **Available** in OS X v10.0 and later.
///
/// Original Documentation [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/Audio_Time_Stamp_Flags).
pub struct AudioTimeStampFlags: u32 {
/// The sample frame time is valid.
const SAMPLE_TIME_VALID = 1;
/// The host time is valid.
const HOST_TIME_VALID = 2;
/// The rate scalar is valid.
const RATE_SCALAR_VALID = 4;
/// The world clock time is valid.
const WORLD_CLOCK_TIME_VALID = 8;
/// The SMPTE time is valid.
const SMPTE_TIME_VALID = 16;
}
}

View File

@@ -0,0 +1,885 @@
/// This is a collection of helper functions for performing common tasks on macOS.
/// These functions are only implemented for macOS, not iOS.
use crate::error::Error;
use std::collections::VecDeque;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
use std::ptr::null;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Sender};
use std::sync::Mutex;
use std::time::Duration;
use std::{mem, thread};
use core_foundation_sys::string::{CFStringGetCString, CFStringGetCStringPtr, CFStringRef};
use sys;
use sys::pid_t;
use sys::{
kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceIsAlive,
kAudioDevicePropertyDeviceNameCFString, kAudioDevicePropertyHogMode,
kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyScopeOutput,
kAudioDevicePropertyStreamConfiguration, kAudioHardwareNoError,
kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice,
kAudioHardwarePropertyDevices, kAudioObjectPropertyElementMaster,
kAudioObjectPropertyElementWildcard, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyScopeInput, kAudioObjectPropertyScopeOutput, kAudioObjectSystemObject,
kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_EnableIO,
kAudioStreamPropertyAvailablePhysicalFormats, kAudioStreamPropertyPhysicalFormat,
kCFStringEncodingUTF8, AudioDeviceID, AudioObjectAddPropertyListener,
AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectID,
AudioObjectPropertyAddress, AudioObjectPropertyScope, AudioObjectRemovePropertyListener,
AudioObjectSetPropertyData, AudioStreamBasicDescription, AudioStreamRangedDescription,
AudioValueRange, OSStatus,
};
use crate::audio_unit::audio_format::{AudioFormat, LinearPcmFlags};
use crate::audio_unit::sample_format::SampleFormat;
use crate::audio_unit::stream_format::StreamFormat;
use crate::audio_unit::{AudioUnit, Element, IOType, Scope};
/// Helper function to get the device id of the default input or output device.
pub fn get_default_device_id(input: bool) -> Option<AudioDeviceID> {
let selector = if input {
kAudioHardwarePropertyDefaultInputDevice
} else {
kAudioHardwarePropertyDefaultOutputDevice
};
let property_address = AudioObjectPropertyAddress {
mSelector: selector,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let audio_device_id: AudioDeviceID = 0;
let data_size = mem::size_of::<AudioDeviceID>();
let status = unsafe {
AudioObjectGetPropertyData(
kAudioObjectSystemObject,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&audio_device_id as *const _ as *mut _,
)
};
if status != kAudioHardwareNoError as i32 {
return None;
}
Some(audio_device_id)
}
/// Find the device id for a device name.
pub fn get_device_id_from_name(name: &str) -> Option<AudioDeviceID> {
if let Ok(all_ids) = get_audio_device_ids() {
return all_ids
.iter()
.find(|id| get_device_name(**id).unwrap_or_else(|_| "".to_string()) == name)
.copied();
}
None
}
/// Create an AudioUnit instance from a device id.
pub fn audio_unit_from_device_id(
device_id: AudioDeviceID,
input: bool,
) -> Result<AudioUnit, Error> {
let mut audio_unit = AudioUnit::new(IOType::HalOutput)?;
if input {
// Enable input processing.
let enable_input = 1u32;
audio_unit.set_property(
kAudioOutputUnitProperty_EnableIO,
Scope::Input,
Element::Input,
Some(&enable_input),
)?;
// Disable output processing.
let disable_output = 0u32;
audio_unit.set_property(
kAudioOutputUnitProperty_EnableIO,
Scope::Output,
Element::Output,
Some(&disable_output),
)?;
}
audio_unit.set_property(
kAudioOutputUnitProperty_CurrentDevice,
Scope::Global,
Element::Output,
Some(&device_id),
)?;
Ok(audio_unit)
}
/// List all audio device ids on the system.
pub fn get_audio_device_ids_for_scope(scope: Scope) -> Result<Vec<AudioDeviceID>, Error> {
let dev_scope = match scope {
Scope::Input => kAudioObjectPropertyScopeInput,
Scope::Output => kAudioObjectPropertyScopeOutput,
_ => kAudioObjectPropertyScopeGlobal,
};
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioHardwarePropertyDevices,
mScope: dev_scope,
mElement: kAudioObjectPropertyElementMaster,
};
macro_rules! try_status_or_return {
($status:expr) => {
if $status != kAudioHardwareNoError as i32 {
return Err(Error::Unknown($status));
}
};
}
let data_size = 0u32;
let status = unsafe {
AudioObjectGetPropertyDataSize(
kAudioObjectSystemObject,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
)
};
try_status_or_return!(status);
let device_count = data_size / mem::size_of::<AudioDeviceID>() as u32;
let mut audio_devices = vec![];
audio_devices.reserve_exact(device_count as usize);
unsafe { audio_devices.set_len(device_count as usize) };
let status = unsafe {
AudioObjectGetPropertyData(
kAudioObjectSystemObject,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
audio_devices.as_mut_ptr() as *mut _,
)
};
try_status_or_return!(status);
Ok(audio_devices)
}
pub fn get_audio_device_ids() -> Result<Vec<AudioDeviceID>, Error> {
get_audio_device_ids_for_scope(Scope::Global)
}
#[test]
fn test_get_audio_device_ids() {
let _ = get_audio_device_ids().expect("Failed to get audio device ids");
}
#[test]
fn test_get_audio_device_ids_for_scope() {
for scope in &[
Scope::Global,
Scope::Input,
Scope::Output,
Scope::Group,
Scope::Part,
Scope::Note,
Scope::Layer,
Scope::LayerItem,
] {
let _ = get_audio_device_ids_for_scope(*scope).expect("Failed to get audio device ids");
}
}
/// does this device support input / ouptut?
pub fn get_audio_device_supports_scope(devid: AudioDeviceID, scope: Scope) -> Result<bool, Error> {
let dev_scope: AudioObjectPropertyScope = match scope {
Scope::Input => kAudioObjectPropertyScopeInput,
Scope::Output => kAudioObjectPropertyScopeOutput,
_ => kAudioObjectPropertyScopeGlobal,
};
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyStreamConfiguration,
mScope: dev_scope,
mElement: kAudioObjectPropertyElementWildcard,
};
macro_rules! try_status_or_return {
($status:expr) => {
if $status != kAudioHardwareNoError as i32 {
return Err(Error::Unknown($status));
}
};
}
let data_size = 0u32;
let status = unsafe {
AudioObjectGetPropertyDataSize(
devid,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
)
};
try_status_or_return!(status);
let mut bfrs: Vec<u8> = Vec::with_capacity(data_size as usize);
let buffers = bfrs.as_mut_ptr() as *mut sys::AudioBufferList;
unsafe {
let status = AudioObjectGetPropertyData(
devid,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
buffers as *mut _,
);
if status != kAudioHardwareNoError as i32 {
return Err(Error::Unknown(status));
}
for i in 0..(*buffers).mNumberBuffers {
let buf = (*buffers).mBuffers[i as usize];
if buf.mNumberChannels > 0 {
return Ok(true);
}
}
}
Ok(false)
}
/// Get the device name for a device id.
pub fn get_device_name(device_id: AudioDeviceID) -> Result<String, Error> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceNameCFString,
mScope: kAudioDevicePropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster,
};
macro_rules! try_status_or_return {
($status:expr) => {
if $status != kAudioHardwareNoError as i32 {
return Err(Error::Unknown($status));
}
};
}
let device_name: CFStringRef = null();
let data_size = mem::size_of::<CFStringRef>();
let c_str = unsafe {
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&device_name as *const _ as *mut _,
);
try_status_or_return!(status);
let c_string: *const c_char = CFStringGetCStringPtr(device_name, kCFStringEncodingUTF8);
if c_string.is_null() {
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&device_name as *const _ as *mut _,
);
try_status_or_return!(status);
let mut buf: [i8; 255] = [0; 255];
let result = CFStringGetCString(
device_name,
buf.as_mut_ptr(),
buf.len() as _,
kCFStringEncodingUTF8,
);
if result == 0 {
return Err(Error::Unknown(result as i32));
}
let name: &CStr = CStr::from_ptr(buf.as_ptr());
return Ok(name.to_str().unwrap().to_owned());
}
CStr::from_ptr(c_string as *mut _)
};
Ok(c_str.to_string_lossy().into_owned())
}
/// Change the sample rate of a device.
/// Adapted from CPAL.
pub fn set_device_sample_rate(device_id: AudioDeviceID, new_rate: f64) -> Result<(), Error> {
// Check whether or not we need to change the device sample rate to suit the one specified for the stream.
unsafe {
// Get the current sample rate.
let mut property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyNominalSampleRate,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let sample_rate: f64 = 0.0;
let data_size = mem::size_of::<f64>() as u32;
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&sample_rate as *const _ as *mut _,
);
Error::from_os_status(status)?;
// If the requested sample rate is different to the device sample rate, update the device.
if sample_rate as u32 != new_rate as u32 {
// Get available sample rate ranges.
property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
let data_size = 0u32;
let status = AudioObjectGetPropertyDataSize(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
);
Error::from_os_status(status)?;
let n_ranges = data_size as usize / mem::size_of::<AudioValueRange>();
let mut ranges: Vec<AudioValueRange> = vec![];
ranges.reserve_exact(n_ranges as usize);
ranges.set_len(n_ranges);
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
ranges.as_mut_ptr() as *mut _,
);
Error::from_os_status(status)?;
// Now that we have the available ranges, pick the one matching the desired rate.
let new_rate_integer = new_rate as u32;
let maybe_index = ranges.iter().position(|r| {
r.mMinimum as u32 == new_rate_integer && r.mMaximum as u32 == new_rate_integer
});
let range_index = match maybe_index {
None => return Err(Error::UnsupportedSampleRate),
Some(i) => i,
};
// Update the property selector to specify the nominal sample rate.
property_address.mSelector = kAudioDevicePropertyNominalSampleRate;
// Add a listener to know when the sample rate changes.
// Since the listener implements Drop, we don't need to manually unregister this later.
let (sender, receiver) = channel();
let mut listener = RateListener::new(device_id, Some(sender));
listener.register()?;
// Finally, set the sample rate.
let status = AudioObjectSetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
data_size,
&ranges[range_index] as *const _ as *const _,
);
Error::from_os_status(status)?;
// Wait for the reported_rate to change.
//
// This sometimes takes up to half a second, timeout after 2 sec to have a little margin.
let timer = ::std::time::Instant::now();
loop {
if let Ok(reported_rate) = receiver.recv_timeout(Duration::from_millis(100)) {
if new_rate as usize == reported_rate as usize {
break;
}
}
if timer.elapsed() > Duration::from_secs(2) {
return Err(Error::UnsupportedSampleRate);
}
}
};
Ok(())
}
}
/// Find the closest match of the physical formats to the provided `StreamFormat`.
/// This function will pick the first format it finds that supports the provided sample format, rate and number of channels.
/// The provided format flags in the `StreamFormat` are ignored.
pub fn find_matching_physical_format(
device_id: AudioDeviceID,
stream_format: StreamFormat,
) -> Option<AudioStreamBasicDescription> {
if let Ok(all_formats) = get_supported_physical_stream_formats(device_id) {
let requested_samplerate = stream_format.sample_rate as usize;
let requested_bits = stream_format.sample_format.size_in_bits();
let requested_float = stream_format.sample_format == SampleFormat::F32;
let requested_channels = stream_format.channels;
for fmt in all_formats {
let min_rate = fmt.mSampleRateRange.mMinimum as usize;
let max_rate = fmt.mSampleRateRange.mMaximum as usize;
let rate = fmt.mFormat.mSampleRate as usize;
let channels = fmt.mFormat.mChannelsPerFrame;
if let Some(AudioFormat::LinearPCM(flags)) = AudioFormat::from_format_and_flag(
fmt.mFormat.mFormatID,
Some(fmt.mFormat.mFormatFlags),
) {
let is_float = flags.contains(LinearPcmFlags::IS_FLOAT);
let is_int = flags.contains(LinearPcmFlags::IS_SIGNED_INTEGER);
if is_int && is_float {
// Probably never occurs, check just in case
continue;
}
if requested_float && !is_float {
// Wrong number type
continue;
}
if !requested_float && !is_int {
// Wrong number type
continue;
}
if requested_bits != fmt.mFormat.mBitsPerChannel {
// Wrong number of bits
continue;
}
if requested_channels > channels {
// Too few channels
continue;
}
if rate == requested_samplerate
|| (requested_samplerate >= min_rate && requested_samplerate <= max_rate)
{
return Some(fmt.mFormat);
}
}
}
}
None
}
/// Change the physical stream format (sample rate and format) of a device.
pub fn set_device_physical_stream_format(
device_id: AudioDeviceID,
new_asbd: AudioStreamBasicDescription,
) -> Result<(), Error> {
unsafe {
// Get the current format.
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioStreamPropertyPhysicalFormat,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let maybe_asbd: mem::MaybeUninit<AudioStreamBasicDescription> = mem::MaybeUninit::zeroed();
let data_size = mem::size_of::<AudioStreamBasicDescription>() as u32;
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&maybe_asbd as *const _ as *mut _,
);
Error::from_os_status(status)?;
let asbd = maybe_asbd.assume_init();
if !asbds_are_equal(&asbd, &new_asbd) {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioStreamPropertyPhysicalFormat,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let reported_asbd: mem::MaybeUninit<AudioStreamBasicDescription> =
mem::MaybeUninit::zeroed();
let reported_asbd = reported_asbd.assume_init();
let status = AudioObjectSetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
data_size,
&new_asbd as *const _ as *const _,
);
Error::from_os_status(status)?;
// Wait for the reported format to change.
// This can take up to half a second, but we timeout after 2 sec just in case.
let timer = ::std::time::Instant::now();
loop {
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&reported_asbd as *const _ as *mut _,
);
Error::from_os_status(status)?;
if asbds_are_equal(&reported_asbd, &new_asbd) {
break;
}
thread::sleep(Duration::from_millis(5));
if timer.elapsed() > Duration::from_secs(2) {
return Err(Error::UnsupportedStreamFormat);
}
}
}
Ok(())
}
}
/// Helper to check if two ASBDs are equal.
fn asbds_are_equal(
left: &AudioStreamBasicDescription,
right: &AudioStreamBasicDescription,
) -> bool {
left.mSampleRate as u32 == right.mSampleRate as u32
&& left.mFormatID == right.mFormatID
&& left.mFormatFlags == right.mFormatFlags
&& left.mBytesPerPacket == right.mBytesPerPacket
&& left.mFramesPerPacket == right.mFramesPerPacket
&& left.mBytesPerFrame == right.mBytesPerFrame
&& left.mChannelsPerFrame == right.mChannelsPerFrame
&& left.mBitsPerChannel == right.mBitsPerChannel
}
/// Get a vector with all supported physical formats as AudioBasicRangedDescriptions.
pub fn get_supported_physical_stream_formats(
device_id: AudioDeviceID,
) -> Result<Vec<AudioStreamRangedDescription>, Error> {
// Get available formats.
let mut property_address = AudioObjectPropertyAddress {
mSelector: kAudioStreamPropertyPhysicalFormat,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let allformats = unsafe {
property_address.mSelector = kAudioStreamPropertyAvailablePhysicalFormats;
let mut data_size = 0u32;
let status = AudioObjectGetPropertyDataSize(
device_id,
&property_address as *const _,
0,
null(),
&mut data_size as *mut _,
);
Error::from_os_status(status)?;
let n_formats = data_size as usize / mem::size_of::<AudioStreamRangedDescription>();
let mut formats: Vec<AudioStreamRangedDescription> = vec![];
formats.reserve_exact(n_formats as usize);
formats.set_len(n_formats);
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
formats.as_mut_ptr() as *mut _,
);
Error::from_os_status(status)?;
formats
};
Ok(allformats)
}
/// Changing the sample rate is an asynchonous process.
/// A RateListener can be used to get notified when the rate is changed.
pub struct RateListener {
pub queue: Mutex<VecDeque<f64>>,
sync_channel: Option<Sender<f64>>,
device_id: AudioDeviceID,
property_address: AudioObjectPropertyAddress,
rate_listener: Option<
unsafe extern "C" fn(u32, u32, *const AudioObjectPropertyAddress, *mut c_void) -> i32,
>,
}
impl Drop for RateListener {
fn drop(&mut self) {
let _ = self.unregister();
}
}
impl RateListener {
/// Create a new RateListener for the given AudioDeviceID.
/// If an `std::sync::mpsc::Sender` is provided, then events will be pushed to that channel.
/// If not, they will instead be stored in an internal queue that will need to be polled.
/// The listener must be registered by calling `register()` in order to start receiving notifications.
pub fn new(device_id: AudioDeviceID, sync_channel: Option<Sender<f64>>) -> RateListener {
// Add our sample rate change listener callback.
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyNominalSampleRate,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let queue = Mutex::new(VecDeque::new());
RateListener {
queue,
sync_channel,
device_id,
property_address,
rate_listener: None,
}
}
/// Register this listener to receive notifications.
pub fn register(&mut self) -> Result<(), Error> {
unsafe extern "C" fn rate_listener(
device_id: AudioObjectID,
_n_addresses: u32,
_properties: *const AudioObjectPropertyAddress,
self_ptr: *mut ::std::os::raw::c_void,
) -> OSStatus {
let self_ptr: &mut RateListener = &mut *(self_ptr as *mut RateListener);
let rate: f64 = 0.0;
let data_size = mem::size_of::<f64>();
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyNominalSampleRate,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let result = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&rate as *const _ as *mut _,
);
if let Some(sender) = &self_ptr.sync_channel {
sender.send(rate).unwrap();
} else {
let mut queue = self_ptr.queue.lock().unwrap();
queue.push_back(rate);
}
result
}
// Add our sample rate change listener callback.
let status = unsafe {
AudioObjectAddPropertyListener(
self.device_id,
&self.property_address as *const _,
Some(rate_listener),
self as *const _ as *mut _,
)
};
Error::from_os_status(status)?;
self.rate_listener = Some(rate_listener);
Ok(())
}
/// Unregister this listener to stop receiving notifications.
pub fn unregister(&mut self) -> Result<(), Error> {
if self.rate_listener.is_some() {
let status = unsafe {
AudioObjectRemovePropertyListener(
self.device_id,
&self.property_address as *const _,
self.rate_listener,
self as *const _ as *mut _,
)
};
Error::from_os_status(status)?;
self.rate_listener = None;
}
Ok(())
}
/// Get the number of sample rate values received (equals the number of change events).
/// Not used if the RateListener was created with a `std::sync::mpsc::Sender`.
pub fn get_nbr_values(&self) -> usize {
self.queue.lock().unwrap().len()
}
/// Copy all received values to a Vec. The latest value is the last element.
/// The internal buffer is preserved.
/// Not used if the RateListener was created with a `std::sync::mpsc::Sender`.
pub fn copy_values(&self) -> Vec<f64> {
self.queue
.lock()
.unwrap()
.iter()
.copied()
.collect::<Vec<f64>>()
}
/// Get all received values as a Vec. The latest value is the last element.
/// This clears the internal buffer.
/// Not used if the RateListener was created with a `std::sync::mpsc::Sender`.
pub fn drain_values(&mut self) -> Vec<f64> {
self.queue.lock().unwrap().drain(..).collect::<Vec<f64>>()
}
}
/// An AliveListener is used to get notified when a device is disconnected.
pub struct AliveListener {
alive: Box<AtomicBool>,
device_id: AudioDeviceID,
property_address: AudioObjectPropertyAddress,
alive_listener: Option<
unsafe extern "C" fn(u32, u32, *const AudioObjectPropertyAddress, *mut c_void) -> i32,
>,
}
impl Drop for AliveListener {
fn drop(&mut self) {
let _ = self.unregister();
}
}
impl AliveListener {
/// Create a new AliveListener for the given AudioDeviceID.
/// The listener must be registered by calling `register()` in order to start receiving notifications.
pub fn new(device_id: AudioDeviceID) -> AliveListener {
// Add our listener callback.
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceIsAlive,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
AliveListener {
alive: Box::new(AtomicBool::new(true)),
device_id,
property_address,
alive_listener: None,
}
}
/// Register this listener to receive notifications.
pub fn register(&mut self) -> Result<(), Error> {
unsafe extern "C" fn alive_listener(
device_id: AudioObjectID,
_n_addresses: u32,
_properties: *const AudioObjectPropertyAddress,
self_ptr: *mut ::std::os::raw::c_void,
) -> OSStatus {
let self_ptr: &mut AliveListener = &mut *(self_ptr as *mut AliveListener);
let alive: u32 = 0;
let data_size = mem::size_of::<u32>();
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceIsAlive,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let result = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&alive as *const _ as *mut _,
);
self_ptr.alive.store(alive > 0, Ordering::SeqCst);
result
}
// Add our listener callback.
let status = unsafe {
AudioObjectAddPropertyListener(
self.device_id,
&self.property_address as *const _,
Some(alive_listener),
self as *const _ as *mut _,
)
};
Error::from_os_status(status)?;
self.alive_listener = Some(alive_listener);
Ok(())
}
/// Unregister this listener to stop receiving notifications
pub fn unregister(&mut self) -> Result<(), Error> {
if self.alive_listener.is_some() {
let status = unsafe {
AudioObjectRemovePropertyListener(
self.device_id,
&self.property_address as *const _,
self.alive_listener,
self as *const _ as *mut _,
)
};
Error::from_os_status(status)?;
self.alive_listener = None;
}
Ok(())
}
/// Check if the device is still alive.
pub fn is_alive(&self) -> bool {
self.alive.load(Ordering::SeqCst)
}
}
/// Helper for hog mode (exclusive access).
/// Get the pid of the process that currently owns exclusive access to a device.
/// A pid value of -1 means no process owns exclusive access.
pub fn get_hogging_pid(device_id: AudioDeviceID) -> Result<pid_t, Error> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyHogMode,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let pid = unsafe {
let temp_pid: pid_t = 0;
let data_size = mem::size_of::<pid_t>();
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&temp_pid as *const _ as *mut _,
);
Error::from_os_status(status)?;
temp_pid
};
Ok(pid)
}
/// Helper for hog mode (exclusive access).
/// Toggle hog mode for a device.
/// If no process owns exclusive access, then the calling process takes ownership.
/// If the calling process already has ownership, this is released.
/// If another process owns access, then nothing will happen.
/// Returns the pid of the new owning process.
/// A pid value of -1 means no process owns exclusive access.
pub fn toggle_hog_mode(device_id: AudioDeviceID) -> Result<pid_t, Error> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyHogMode,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
let pid = unsafe {
let temp_pid: pid_t = -1;
let data_size = mem::size_of::<pid_t>();
let status = AudioObjectSetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
data_size as u32,
&temp_pid as *const _ as *mut _,
);
Error::from_os_status(status)?;
let status = AudioObjectGetPropertyData(
device_id,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
&temp_pid as *const _ as *mut _,
);
Error::from_os_status(status)?;
temp_pid
};
Ok(pid)
}

View File

@@ -0,0 +1,432 @@
//! This module is an attempt to provide a friendly, rust-esque interface to Apple's Audio Unit API.
//!
//! Learn more about the Audio Unit API [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40003278-CH1-SW2)
//! and [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
//!
//! TODO: The following are `kAudioUnitSubType`s (along with their const u32) generated by
//! rust-bindgen that we could not find any documentation on:
//!
//! - MIDISynth = 1836284270,
//! - RoundTripAAC = 1918984547,
//! - SpatialMixer = 862217581,
//! - SphericalHeadPanner = 1936746610,
//! - VectorPanner = 1986158963,
//! - SoundFieldPanner = 1634558569,
//! - HRTFPanner = 1752331366,
//! - NetReceive = 1852990326,
//!
//! If you can find documentation on these, please feel free to submit an issue or PR with the
//! fixes!
use crate::error::Error;
use std::mem;
use std::os::raw::{c_uint, c_void};
use std::ptr;
use sys;
pub use self::audio_format::AudioFormat;
pub use self::sample_format::{Sample, SampleFormat};
pub use self::stream_format::StreamFormat;
pub use self::types::{
EffectType, FormatConverterType, GeneratorType, IOType, MixerType, MusicDeviceType, Type,
};
#[cfg(target_os = "macos")]
pub mod macos_helpers;
pub mod audio_format;
pub mod render_callback;
pub mod sample_format;
pub mod stream_format;
pub mod types;
/// The input and output **Scope**s.
///
/// More info [here](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Scopes)
/// and [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
#[derive(Copy, Clone, Debug)]
pub enum Scope {
Global = 0,
Input = 1,
Output = 2,
Group = 3,
Part = 4,
Note = 5,
Layer = 6,
LayerItem = 7,
}
/// Represents the **Input** and **Output** **Element**s.
///
/// These are used when specifying which **Element** we're setting the properties of.
#[derive(Copy, Clone, Debug)]
pub enum Element {
Output = 0,
Input = 1,
}
/// A rust representation of the sys::AudioUnit, including a pointer to the current rendering callback.
///
/// Find the original Audio Unit Programming Guide [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
pub struct AudioUnit {
instance: sys::AudioUnit,
maybe_render_callback: Option<*mut render_callback::InputProcFnWrapper>,
maybe_input_callback: Option<InputCallback>,
}
struct InputCallback {
// The audio buffer list to which input data is rendered.
buffer_list: *mut sys::AudioBufferList,
callback: *mut render_callback::InputProcFnWrapper,
}
macro_rules! try_os_status {
($expr:expr) => {
Error::from_os_status($expr)?
};
}
impl AudioUnit {
/// Construct a new AudioUnit with any type that may be automatically converted into
/// [**Type**](./enum.Type).
///
/// Here is a list of compatible types:
///
/// - [**Type**](./types/enum.Type)
/// - [**IOType**](./types/enum.IOType)
/// - [**MusicDeviceType**](./types/enum.MusicDeviceType)
/// - [**GeneratorType**](./types/enum.GeneratorType)
/// - [**FormatConverterType**](./types/enum.FormatConverterType)
/// - [**EffectType**](./types/enum.EffectType)
/// - [**MixerType**](./types/enum.MixerType)
///
/// To construct the **AudioUnit** with some component flags, see
/// [**AudioUnit::new_with_flags**](./struct.AudioUnit#method.new_with_flags).
///
/// Note: the `AudioUnit` is constructed with the `kAudioUnitManufacturer_Apple` Manufacturer
/// Identifier, as this is the only Audio Unit Manufacturer Identifier documented by Apple in
/// the AudioUnit reference (see [here](https://developer.apple.com/library/prerelease/mac/documentation/AudioUnit/Reference/AUComponentServicesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Manufacturer_Identifier)).
pub fn new<T>(ty: T) -> Result<AudioUnit, Error>
where
T: Into<Type>,
{
AudioUnit::new_with_flags(ty, 0, 0)
}
/// The same as [**AudioUnit::new**](./struct.AudioUnit#method.new) but with the given
/// component flags and mask.
pub fn new_with_flags<T>(ty: T, flags: u32, mask: u32) -> Result<AudioUnit, Error>
where
T: Into<Type>,
{
const MANUFACTURER_IDENTIFIER: u32 = sys::kAudioUnitManufacturer_Apple;
let au_type: Type = ty.into();
let sub_type_u32 = match au_type.as_subtype_u32() {
Some(u) => u,
None => return Err(Error::NoKnownSubtype),
};
// A description of the audio unit we desire.
let desc = sys::AudioComponentDescription {
componentType: au_type.as_u32() as c_uint,
componentSubType: sub_type_u32 as c_uint,
componentManufacturer: MANUFACTURER_IDENTIFIER,
componentFlags: flags,
componentFlagsMask: mask,
};
unsafe {
// Find the default audio unit for the description.
//
// From the "Audio Unit Hosting Guide for iOS":
//
// Passing NULL to the first parameter of AudioComponentFindNext tells this function to
// find the first system audio unit matching the description, using a system-defined
// ordering. If you instead pass a previously found audio unit reference in this
// parameter, the function locates the next audio unit matching the description.
let component = sys::AudioComponentFindNext(ptr::null_mut(), &desc as *const _);
if component.is_null() {
return Err(Error::NoMatchingDefaultAudioUnitFound);
}
// Create an instance of the default audio unit using the component.
let mut instance_uninit = mem::MaybeUninit::<sys::AudioUnit>::uninit();
try_os_status!(sys::AudioComponentInstanceNew(
component,
instance_uninit.as_mut_ptr() as *mut sys::AudioUnit
));
let instance: sys::AudioUnit = instance_uninit.assume_init();
// Initialise the audio unit!
try_os_status!(sys::AudioUnitInitialize(instance));
Ok(AudioUnit {
instance,
maybe_render_callback: None,
maybe_input_callback: None,
})
}
}
/// On successful initialization, the audio formats for input and output are valid
/// and the audio unit is ready to render. During initialization, an audio unit
/// allocates memory according to the maximum number of audio frames it can produce
/// in response to a single render call.
///
/// Usually, the state of an audio unit (such as its I/O formats and memory allocations)
/// cannot be changed while an audio unit is initialized.
pub fn initialize(&mut self) -> Result<(), Error> {
unsafe {
try_os_status!(sys::AudioUnitInitialize(self.instance));
}
Ok(())
}
/// Before you change an initialize audio units processing characteristics,
/// such as its input or output audio data format or its sample rate, you must
/// first uninitialize it. Calling this function deallocates the audio units resources.
///
/// After calling this function, you can reconfigure the audio unit and then call
/// AudioUnitInitialize to reinitialize it.
pub fn uninitialize(&mut self) -> Result<(), Error> {
unsafe {
try_os_status!(sys::AudioUnitUninitialize(self.instance));
}
Ok(())
}
/// Sets the value for some property of the **AudioUnit**.
///
/// To clear an audio unit property value, set the data paramater with `None::<()>`.
///
/// Clearing properties only works for those properties that do not have a default value.
///
/// For more on "properties" see [the reference](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/uid/TP40007288).
///
/// **Available** in iOS 2.0 and later.
///
/// Parameters
/// ----------
///
/// - **id**: The identifier of the property.
/// - **scope**: The audio unit scope for the property.
/// - **elem**: The audio unit element for the property.
/// - **maybe_data**: The value that you want to apply to the property.
pub fn set_property<T>(
&mut self,
id: u32,
scope: Scope,
elem: Element,
maybe_data: Option<&T>,
) -> Result<(), Error> {
set_property(self.instance, id, scope, elem, maybe_data)
}
/// Gets the value of an **AudioUnit** property.
///
/// **Available** in iOS 2.0 and later.
///
/// Parameters
/// ----------
///
/// - **id**: The identifier of the property.
/// - **scope**: The audio unit scope for the property.
/// - **elem**: The audio unit element for the property.
pub fn get_property<T>(&self, id: u32, scope: Scope, elem: Element) -> Result<T, Error> {
get_property(self.instance, id, scope, elem)
}
/// Starts an I/O **AudioUnit**, which in turn starts the audio unit processing graph that it is
/// connected to.
///
/// **Available** in OS X v10.0 and later.
pub fn start(&mut self) -> Result<(), Error> {
unsafe {
try_os_status!(sys::AudioOutputUnitStart(self.instance));
}
Ok(())
}
/// Stops an I/O **AudioUnit**, which in turn stops the audio unit processing graph that it is
/// connected to.
///
/// **Available** in OS X v10.0 and later.
pub fn stop(&mut self) -> Result<(), Error> {
unsafe {
try_os_status!(sys::AudioOutputUnitStop(self.instance));
}
Ok(())
}
/// Set the **AudioUnit**'s sample rate.
///
/// **Available** in iOS 2.0 and later.
pub fn set_sample_rate(&mut self, sample_rate: f64) -> Result<(), Error> {
let id = sys::kAudioUnitProperty_SampleRate;
self.set_property(id, Scope::Input, Element::Output, Some(&sample_rate))
}
/// Get the **AudioUnit**'s sample rate.
pub fn sample_rate(&self) -> Result<f64, Error> {
let id = sys::kAudioUnitProperty_SampleRate;
self.get_property(id, Scope::Input, Element::Output)
}
/// Sets the current **StreamFormat** for the AudioUnit.
///
/// Core Audio uses slightly different defaults depending on the platform.
///
/// From the Core Audio Overview:
///
/// > The canonical formats in Core Audio are as follows:
/// >
/// > - iOS input and output: Linear PCM with 16-bit integer samples.
/// > - iOS audio units and other audio processing: Noninterleaved linear PCM with 8.24-bit
/// fixed-point samples
/// > - Mac input and output: Linear PCM with 32-bit floating point samples.
/// > - Mac audio units and other audio processing: Noninterleaved linear PCM with 32-bit
/// floating-point
pub fn set_stream_format(
&mut self,
stream_format: StreamFormat,
scope: Scope,
) -> Result<(), Error> {
let id = sys::kAudioUnitProperty_StreamFormat;
let asbd = stream_format.to_asbd();
self.set_property(id, scope, Element::Output, Some(&asbd))
}
/// Return the current Stream Format for the AudioUnit.
pub fn stream_format(&self, scope: Scope) -> Result<StreamFormat, Error> {
let id = sys::kAudioUnitProperty_StreamFormat;
let asbd = self.get_property(id, scope, Element::Output)?;
StreamFormat::from_asbd(asbd)
}
/// Return the current output Stream Format for the AudioUnit.
pub fn output_stream_format(&self) -> Result<StreamFormat, Error> {
self.stream_format(Scope::Output)
}
/// Return the current input Stream Format for the AudioUnit.
pub fn input_stream_format(&self) -> Result<StreamFormat, Error> {
self.stream_format(Scope::Input)
}
}
unsafe impl Send for AudioUnit {}
impl Drop for AudioUnit {
fn drop(&mut self) {
unsafe {
use crate::error;
// We don't want to panic in `drop`, so we'll ignore returned errors.
//
// A user should explicitly terminate the `AudioUnit` if they want to handle errors (we
// still need to provide a way to actually do that).
self.stop().ok();
error::Error::from_os_status(sys::AudioUnitUninitialize(self.instance)).ok();
self.free_render_callback();
self.free_input_callback();
error::Error::from_os_status(sys::AudioComponentInstanceDispose(self.instance)).ok();
}
}
}
/// Sets the value for some property of the **AudioUnit**.
///
/// To clear an audio unit property value, set the data paramater with `None::<()>`.
///
/// Clearing properties only works for those properties that do not have a default value.
///
/// For more on "properties" see [the reference](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/uid/TP40007288).
///
/// **Available** in iOS 2.0 and later.
///
/// Parameters
/// ----------
///
/// - **au**: The AudioUnit instance.
/// - **id**: The identifier of the property.
/// - **scope**: The audio unit scope for the property.
/// - **elem**: The audio unit element for the property.
/// - **maybe_data**: The value that you want to apply to the property.
pub fn set_property<T>(
au: sys::AudioUnit,
id: u32,
scope: Scope,
elem: Element,
maybe_data: Option<&T>,
) -> Result<(), Error> {
let (data_ptr, size) = maybe_data
.map(|data| {
let ptr = data as *const _ as *const c_void;
let size = ::std::mem::size_of::<T>() as u32;
(ptr, size)
})
.unwrap_or_else(|| (::std::ptr::null(), 0));
let scope = scope as c_uint;
let elem = elem as c_uint;
unsafe {
try_os_status!(sys::AudioUnitSetProperty(
au, id, scope, elem, data_ptr, size
))
}
Ok(())
}
/// Gets the value of an **AudioUnit** property.
///
/// **Available** in iOS 2.0 and later.
///
/// Parameters
/// ----------
///
/// - **au**: The AudioUnit instance.
/// - **id**: The identifier of the property.
/// - **scope**: The audio unit scope for the property.
/// - **elem**: The audio unit element for the property.
pub fn get_property<T>(
au: sys::AudioUnit,
id: u32,
scope: Scope,
elem: Element,
) -> Result<T, Error> {
let scope = scope as c_uint;
let elem = elem as c_uint;
let mut size = ::std::mem::size_of::<T>() as u32;
unsafe {
let mut data_uninit = ::std::mem::MaybeUninit::<T>::uninit();
let data_ptr = data_uninit.as_mut_ptr() as *mut _ as *mut c_void;
let size_ptr = &mut size as *mut _;
try_os_status!(sys::AudioUnitGetProperty(
au, id, scope, elem, data_ptr, size_ptr
));
let data: T = data_uninit.assume_init();
Ok(data)
}
}
/// Gets the value of a specified audio session property.
///
/// **Available** in iOS 2.0 and later.
///
/// Parameters
/// ----------
///
/// - **id**: The identifier of the property.
#[cfg(target_os = "ios")]
pub fn audio_session_get_property<T>(id: u32) -> Result<T, Error> {
let mut size = ::std::mem::size_of::<T>() as u32;
unsafe {
let mut data_uninit = ::std::mem::MaybeUninit::<T>::uninit();
let data_ptr = data_uninit.as_mut_ptr() as *mut _ as *mut c_void;
let size_ptr = &mut size as *mut _;
try_os_status!(sys::AudioSessionGetProperty(id, size_ptr, data_ptr));
let data: T = data_uninit.assume_init();
Ok(data)
}
}

View File

@@ -0,0 +1,776 @@
use super::audio_format::LinearPcmFlags;
use super::{AudioUnit, Element, Scope};
use crate::error::{self, Error};
use std::mem;
use std::os::raw::c_void;
use std::slice;
use sys;
pub use self::action_flags::ActionFlags;
pub use self::data::Data;
/// When `set_render_callback` is called, a closure of this type will be used to wrap the given
/// render callback function.
///
/// This allows the user to provide a custom, more rust-esque callback function type that takes
/// greater advantage of rust's type safety.
pub type InputProcFn = dyn FnMut(
*mut sys::AudioUnitRenderActionFlags,
*const sys::AudioTimeStamp,
sys::UInt32,
sys::UInt32,
*mut sys::AudioBufferList,
) -> sys::OSStatus;
/// This type allows us to safely wrap a boxed `RenderCallback` to use within the input proc.
pub struct InputProcFnWrapper {
callback: Box<InputProcFn>,
}
/// Arguments given to the render callback function.
#[derive(Debug)]
pub struct Args<D> {
/// A type wrapping the the buffer that matches the expected audio format.
pub data: D,
/// Timing information for the callback.
pub time_stamp: sys::AudioTimeStamp,
/// TODO
pub bus_number: u32,
/// The number of frames in the buffer as `usize` for easier indexing.
pub num_frames: usize,
/// Flags for configuring audio unit rendering.
///
/// This parameter lets a callback provide various hints to the audio unit.
///
/// For example: if there is no audio to process, we can insert the `OUTPUT_IS_SILENCE` flag to
/// indicate to the audio unit that the buffer does not need to be processed.
pub flags: action_flags::Handle,
}
/// Format specific render callback data.
pub mod data {
use super::super::Sample;
use super::super::StreamFormat;
use crate::audio_unit::audio_format::LinearPcmFlags;
use std::marker::PhantomData;
use std::slice;
use sys;
/// Audio data wrappers specific to the `AudioUnit`'s `AudioFormat`.
pub trait Data {
/// Check whether or not the stream format matches this type of data.
fn does_stream_format_match(stream_format: &StreamFormat) -> bool;
/// We must be able to construct Self from arguments given to the `input_proc`.
/// # Safety
/// TODO document how to use this function safely.
unsafe fn from_input_proc_args(num_frames: u32, io_data: *mut sys::AudioBufferList)
-> Self;
}
/// A raw pointer to the audio data so that the user may handle it themselves.
#[derive(Debug)]
pub struct Raw {
pub data: *mut sys::AudioBufferList,
}
impl Data for Raw {
fn does_stream_format_match(_: &StreamFormat) -> bool {
true
}
unsafe fn from_input_proc_args(
_num_frames: u32,
io_data: *mut sys::AudioBufferList,
) -> Self {
Raw { data: io_data }
}
}
/// An interleaved linear PCM buffer with samples of type `S`.
pub struct Interleaved<S: 'static> {
/// The audio buffer.
pub buffer: &'static mut [S],
pub channels: usize,
sample_format: PhantomData<S>,
}
/// An interleaved linear PCM buffer with samples stored as plain bytes.
pub struct InterleavedBytes<S: 'static> {
/// The audio buffer.
pub buffer: &'static mut [u8],
pub channels: usize,
sample_format: PhantomData<S>,
}
/// A wrapper around the pointer to the `mBuffers` array.
pub struct NonInterleaved<S> {
/// The list of audio buffers.
buffers: &'static mut [sys::AudioBuffer],
/// The number of frames in each channel.
frames: usize,
sample_format: PhantomData<S>,
}
/// An iterator produced by a `NonInterleaved`, yielding a reference to each channel.
pub struct Channels<'a, S: 'a> {
buffers: slice::Iter<'a, sys::AudioBuffer>,
frames: usize,
sample_format: PhantomData<S>,
}
/// An iterator produced by a `NonInterleaved`, yielding a mutable reference to each channel.
pub struct ChannelsMut<'a, S: 'a> {
buffers: slice::IterMut<'a, sys::AudioBuffer>,
frames: usize,
sample_format: PhantomData<S>,
}
unsafe impl<S> Send for NonInterleaved<S> where S: Send {}
impl<'a, S> Iterator for Channels<'a, S> {
type Item = &'a [S];
#[allow(non_snake_case)]
fn next(&mut self) -> Option<Self::Item> {
self.buffers.next().map(
|&sys::AudioBuffer {
mNumberChannels,
mData,
..
}| {
let len = mNumberChannels as usize * self.frames;
let ptr = mData as *mut S;
unsafe { slice::from_raw_parts(ptr, len) }
},
)
}
}
impl<'a, S> Iterator for ChannelsMut<'a, S> {
type Item = &'a mut [S];
#[allow(non_snake_case)]
fn next(&mut self) -> Option<Self::Item> {
self.buffers.next().map(
|&mut sys::AudioBuffer {
mNumberChannels,
mData,
..
}| {
let len = mNumberChannels as usize * self.frames;
let ptr = mData as *mut S;
unsafe { slice::from_raw_parts_mut(ptr, len) }
},
)
}
}
impl<S> NonInterleaved<S> {
/// An iterator yielding a reference to each channel in the array.
pub fn channels(&self) -> Channels<S> {
Channels {
buffers: self.buffers.iter(),
frames: self.frames,
sample_format: PhantomData,
}
}
/// An iterator yielding a mutable reference to each channel in the array.
pub fn channels_mut(&mut self) -> ChannelsMut<S> {
ChannelsMut {
buffers: self.buffers.iter_mut(),
frames: self.frames,
sample_format: PhantomData,
}
}
}
// Implementation for a non-interleaved linear PCM audio format.
impl<S> Data for NonInterleaved<S>
where
S: Sample,
{
fn does_stream_format_match(stream_format: &StreamFormat) -> bool {
stream_format
.flags
.contains(LinearPcmFlags::IS_NON_INTERLEAVED)
&& S::sample_format().does_match_flags(stream_format.flags)
}
#[allow(non_snake_case)]
unsafe fn from_input_proc_args(frames: u32, io_data: *mut sys::AudioBufferList) -> Self {
let ptr = (*io_data).mBuffers.as_ptr() as *mut sys::AudioBuffer;
let len = (*io_data).mNumberBuffers as usize;
let buffers = slice::from_raw_parts_mut(ptr, len);
NonInterleaved {
buffers,
frames: frames as usize,
sample_format: PhantomData,
}
}
}
// Implementation for an interleaved linear PCM audio format.
impl<S> Data for Interleaved<S>
where
S: Sample,
{
fn does_stream_format_match(stream_format: &StreamFormat) -> bool {
!stream_format
.flags
.contains(LinearPcmFlags::IS_NON_INTERLEAVED)
&& S::sample_format().does_match_flags(stream_format.flags)
}
#[allow(non_snake_case)]
unsafe fn from_input_proc_args(frames: u32, io_data: *mut sys::AudioBufferList) -> Self {
// // We're expecting a single interleaved buffer which will be the first in the array.
let sys::AudioBuffer {
mNumberChannels,
mDataByteSize,
mData,
} = (*io_data).mBuffers[0];
// // Ensure that the size of the data matches the size of the sample format
// // multiplied by the number of frames.
// //
// // TODO: Return an Err instead of `panic`ing.
let buffer_len = frames as usize * mNumberChannels as usize;
let expected_size = ::std::mem::size_of::<S>() * buffer_len;
assert!(mDataByteSize as usize == expected_size);
let buffer: &mut [S] = {
let buffer_ptr = mData as *mut S;
slice::from_raw_parts_mut(buffer_ptr, buffer_len)
};
Interleaved {
buffer,
channels: mNumberChannels as usize,
sample_format: PhantomData,
}
}
}
// Implementation for an interleaved linear PCM audio format using plain bytes.
impl<S> Data for InterleavedBytes<S>
where
S: Sample,
{
fn does_stream_format_match(stream_format: &StreamFormat) -> bool {
!stream_format
.flags
.contains(LinearPcmFlags::IS_NON_INTERLEAVED)
&& S::sample_format().does_match_flags(stream_format.flags)
}
#[allow(non_snake_case)]
unsafe fn from_input_proc_args(frames: u32, io_data: *mut sys::AudioBufferList) -> Self {
// // We're expecting a single interleaved buffer which will be the first in the array.
let sys::AudioBuffer {
mNumberChannels,
mDataByteSize,
mData,
} = (*io_data).mBuffers[0];
// // Ensure that the size of the data matches the size of the sample format
// // multiplied by the number of frames.
// //
// // TODO: Return an Err instead of `panic`ing.
let buffer_len = frames as usize * mNumberChannels as usize;
let expected_size = ::std::mem::size_of::<S>() * buffer_len;
assert!(mDataByteSize as usize == expected_size);
let buffer: &mut [u8] = {
let buffer_ptr = mData as *mut u8;
slice::from_raw_parts_mut(buffer_ptr, mDataByteSize as usize)
};
InterleavedBytes {
buffer,
channels: mNumberChannels as usize,
sample_format: PhantomData,
}
}
}
}
pub mod action_flags {
use std::fmt;
use sys;
bitflags! {
pub struct ActionFlags: u32 {
/// Called on a render notification Proc, which is called either before or after the
/// render operation of the audio unit. If this flag is set, the proc is being called
/// before the render operation is performed.
///
/// **Available** in OS X v10.0 and later.
const PRE_RENDER = sys::kAudioUnitRenderAction_PreRender;
/// Called on a render notification Proc, which is called either before or after the
/// render operation of the audio unit. If this flag is set, the proc is being called
/// after the render operation is completed.
///
/// **Available** in OS X v10.0 and later.
const POST_RENDER = sys::kAudioUnitRenderAction_PostRender;
/// This flag can be set in a render input callback (or in the audio unit's render
/// operation itself) and is used to indicate that the render buffer contains only
/// silence. It can then be used by the caller as a hint to whether the buffer needs to
/// be processed or not.
///
/// **Available** in OS X v10.2 and later.
const OUTPUT_IS_SILENCE = sys::kAudioUnitRenderAction_OutputIsSilence;
/// This is used with offline audio units (of type 'auol'). It is used when an offline
/// unit is being preflighted, which is performed prior to when the actual offline
/// rendering actions are performed. It is used for those cases where the offline
/// process needs it (for example, with an offline unit that normalizes an audio file,
/// it needs to see all of the audio data first before it can perform its
/// normalization).
///
/// **Available** in OS X v10.3 and later.
const OFFLINE_PREFLIGHT = sys::kAudioOfflineUnitRenderAction_Preflight;
/// Once an offline unit has been successfully preflighted, it is then put into its
/// render mode. This flag is set to indicate to the audio unit that it is now in that
/// state and that it should perform processing on the input data.
///
/// **Available** in OS X v10.3 and later.
const OFFLINE_RENDER = sys::kAudioOfflineUnitRenderAction_Render;
/// This flag is set when an offline unit has completed either its preflight or
/// performed render operation.
///
/// **Available** in OS X v10.3 and later.
const OFFLINE_COMPLETE = sys::kAudioOfflineUnitRenderAction_Complete;
/// If this flag is set on the post-render call an error was returned by the audio
/// unit's render operation. In this case, the error can be retrieved through the
/// `lastRenderError` property and the audio data in `ioData` handed to the post-render
/// notification will be invalid.
///
/// **Available** in OS X v10.5 and later.
const POST_RENDER_ERROR = sys::kAudioUnitRenderAction_PostRenderError;
/// If this flag is set, then checks that are done on the arguments provided to render
/// are not performed. This can be useful to use to save computation time in situations
/// where you are sure you are providing the correct arguments and structures to the
/// various render calls.
///
/// **Available** in OS X v10.7 and later.
const DO_NOT_CHECK_RENDER_ARGS = sys::kAudioUnitRenderAction_DoNotCheckRenderArgs;
}
}
/// A safe handle around the `AudioUnitRenderActionFlags` pointer provided by the render
/// callback.
///
/// This type lets a callback provide various hints to the audio unit.
///
/// For example: if there is no audio to process, we can insert the `OUTPUT_IS_SILENCE` flag to
/// indicate to the audio unit that the buffer does not need to be processed.
pub struct Handle {
ptr: *mut sys::AudioUnitRenderActionFlags,
}
impl fmt::Debug for Handle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.ptr.is_null() {
write!(f, "{:?}", self.ptr)
} else {
unsafe { write!(f, "{:?}", *self.ptr) }
}
}
}
impl Handle {
/// Retrieve the current state of the `ActionFlags`.
pub fn get(&self) -> ActionFlags {
ActionFlags::from_bits_truncate(unsafe { *self.ptr })
}
fn set(&mut self, flags: ActionFlags) {
unsafe { *self.ptr = flags.bits() }
}
/// The raw value of the flags currently stored.
pub fn bits(&self) -> u32 {
self.get().bits()
}
/// Returns `true` if no flags are currently stored.
pub fn is_empty(&self) -> bool {
self.get().is_empty()
}
/// Returns `true` if all flags are currently stored.
pub fn is_all(&self) -> bool {
self.get().is_all()
}
/// Returns `true` if there are flags common to both `self` and `other`.
pub fn intersects(&self, other: ActionFlags) -> bool {
self.get().intersects(other)
}
/// Returns `true` if all of the flags in `other` are contained within `self`.
pub fn contains(&self, other: ActionFlags) -> bool {
self.get().contains(other)
}
/// Insert the specified flags in-place.
pub fn insert(&mut self, other: ActionFlags) {
let mut flags = self.get();
flags.insert(other);
self.set(flags);
}
/// Remove the specified flags in-place.
pub fn remove(&mut self, other: ActionFlags) {
let mut flags = self.get();
flags.remove(other);
self.set(flags);
}
/// Toggles the specified flags in-place.
pub fn toggle(&mut self, other: ActionFlags) {
let mut flags = self.get();
flags.toggle(other);
self.set(flags);
}
/// Wrap the given pointer with a `Handle`.
pub fn from_ptr(ptr: *mut sys::AudioUnitRenderActionFlags) -> Self {
Handle { ptr }
}
}
unsafe impl Send for Handle {}
impl ::std::fmt::Display for ActionFlags {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(
f,
"{:?}",
match self.bits() {
sys::kAudioUnitRenderAction_PreRender => "PRE_RENDER",
sys::kAudioUnitRenderAction_PostRender => "POST_RENDER",
sys::kAudioUnitRenderAction_OutputIsSilence => "OUTPUT_IS_SILENCE",
sys::kAudioOfflineUnitRenderAction_Preflight => "OFFLINE_PREFLIGHT",
sys::kAudioOfflineUnitRenderAction_Render => "OFFLINE_RENDER",
sys::kAudioOfflineUnitRenderAction_Complete => "OFFLINE_COMPLETE",
sys::kAudioUnitRenderAction_PostRenderError => "POST_RENDER_ERROR",
sys::kAudioUnitRenderAction_DoNotCheckRenderArgs => "DO_NOT_CHECK_RENDER_ARGS",
_ => "<Unknown ActionFlags>",
}
)
}
}
}
impl AudioUnit {
/// Pass a render callback (aka "Input Procedure") to the **AudioUnit**.
pub fn set_render_callback<F, D>(&mut self, mut f: F) -> Result<(), Error>
where
F: FnMut(Args<D>) -> Result<(), ()> + 'static,
D: Data,
{
// First, we'll retrieve the stream format so that we can ensure that the given callback
// format matches the audio unit's format.
let id = sys::kAudioUnitProperty_StreamFormat;
let asbd = self.get_property(id, Scope::Input, Element::Output)?;
let stream_format = super::StreamFormat::from_asbd(asbd)?;
// If the stream format does not match, return an error indicating this.
if !D::does_stream_format_match(&stream_format) {
return Err(Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat);
}
// Here, we call the given render callback function within a closure that matches the
// arguments of the required coreaudio "input_proc".
//
// This allows us to take advantage of rust's type system and provide format-specific
// `Args` types which can be checked at compile time.
let input_proc_fn = move |io_action_flags: *mut sys::AudioUnitRenderActionFlags,
in_time_stamp: *const sys::AudioTimeStamp,
in_bus_number: sys::UInt32,
in_number_frames: sys::UInt32,
io_data: *mut sys::AudioBufferList|
-> sys::OSStatus {
let args = unsafe {
let data = D::from_input_proc_args(in_number_frames, io_data);
let flags = action_flags::Handle::from_ptr(io_action_flags);
Args {
data,
time_stamp: *in_time_stamp,
flags,
bus_number: in_bus_number as u32,
num_frames: in_number_frames as usize,
}
};
match f(args) {
Ok(()) => 0,
Err(()) => error::Error::Unspecified.as_os_status(),
}
};
let input_proc_fn_wrapper = Box::new(InputProcFnWrapper {
callback: Box::new(input_proc_fn),
});
// Setup render callback. Notice that we relinquish ownership of the Callback
// here so that it can be used as the C render callback via a void pointer.
// We do however store the *mut so that we can convert back to a Box<InputProcFnWrapper>
// within our AudioUnit's Drop implementation (otherwise it would leak).
let input_proc_fn_wrapper_ptr = Box::into_raw(input_proc_fn_wrapper) as *mut c_void;
let render_callback = sys::AURenderCallbackStruct {
inputProc: Some(input_proc),
inputProcRefCon: input_proc_fn_wrapper_ptr,
};
self.set_property(
sys::kAudioUnitProperty_SetRenderCallback,
Scope::Input,
Element::Output,
Some(&render_callback),
)?;
self.free_render_callback();
self.maybe_render_callback = Some(input_proc_fn_wrapper_ptr as *mut InputProcFnWrapper);
Ok(())
}
/// Pass an input callback (aka "Input Procedure") to the **AudioUnit**.
pub fn set_input_callback<F, D>(&mut self, mut f: F) -> Result<(), Error>
where
F: FnMut(Args<D>) -> Result<(), ()> + 'static,
D: Data,
{
// First, we'll retrieve the stream format so that we can ensure that the given callback
// format matches the audio unit's format.
let id = sys::kAudioUnitProperty_StreamFormat;
let asbd = self.get_property(id, Scope::Output, Element::Input)?;
let stream_format = super::StreamFormat::from_asbd(asbd)?;
// If the stream format does not match, return an error indicating this.
if !D::does_stream_format_match(&stream_format) {
return Err(Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat);
}
// Interleaved or non-interleaved?
let non_interleaved = stream_format
.flags
.contains(LinearPcmFlags::IS_NON_INTERLEAVED);
// Pre-allocate a buffer list for input stream.
//
// First, get the current buffer size for pre-allocating the `AudioBuffer`s.
#[cfg(target_os = "macos")]
let mut buffer_frame_size: u32 = {
let id = sys::kAudioDevicePropertyBufferFrameSize;
let buffer_frame_size: u32 = self.get_property(id, Scope::Global, Element::Output)?;
buffer_frame_size
};
#[cfg(target_os = "ios")]
let mut buffer_frame_size: u32 = {
let id = sys::kAudioSessionProperty_CurrentHardwareIOBufferDuration;
let seconds: f32 = super::audio_session_get_property(id)?;
let id = sys::kAudioSessionProperty_CurrentHardwareSampleRate;
let sample_rate: f64 = super::audio_session_get_property(id)?;
(sample_rate * seconds as f64).round() as u32
};
let sample_bytes = stream_format.sample_format.size_in_bytes();
let n_channels = stream_format.channels;
if non_interleaved && n_channels > 1 {
return Err(Error::NonInterleavedInputOnlySupportsMono);
}
let data_byte_size = buffer_frame_size * sample_bytes as u32 * n_channels;
let mut data = vec![0u8; data_byte_size as usize];
let mut buffer_capacity = data_byte_size as usize;
let audio_buffer = sys::AudioBuffer {
mDataByteSize: data_byte_size,
mNumberChannels: n_channels,
mData: data.as_mut_ptr() as *mut _,
};
// Relieve ownership of the `Vec` until we're ready to drop the `AudioBufferList`.
// TODO: This leaks the len & capacity fields, since only the buffer pointer is released
mem::forget(data);
let audio_buffer_list = Box::new(sys::AudioBufferList {
mNumberBuffers: 1,
mBuffers: [audio_buffer],
});
// Relinquish ownership of the audio buffer list. Instead, we'll store a raw pointer and
// convert it back into a `Box` when `free_input_callback` is next called.
let audio_buffer_list_ptr = Box::into_raw(audio_buffer_list);
// Here, we call the given input callback function within a closure that matches the
// arguments of the required coreaudio "input_proc".
//
// This allows us to take advantage of rust's type system and provide format-specific
// `Args` types which can be checked at compile time.
let audio_unit = self.instance;
let input_proc_fn = move |io_action_flags: *mut sys::AudioUnitRenderActionFlags,
in_time_stamp: *const sys::AudioTimeStamp,
in_bus_number: sys::UInt32,
in_number_frames: sys::UInt32,
_io_data: *mut sys::AudioBufferList|
-> sys::OSStatus {
// If the buffer size has changed, ensure the AudioBuffer is the correct size.
if buffer_frame_size != in_number_frames {
unsafe {
// Retrieve the up-to-date stream format.
let id = sys::kAudioUnitProperty_StreamFormat;
let asbd =
match super::get_property(audio_unit, id, Scope::Output, Element::Input) {
Err(err) => return err.as_os_status(),
Ok(asbd) => asbd,
};
let stream_format = match super::StreamFormat::from_asbd(asbd) {
Err(err) => return err.as_os_status(),
Ok(fmt) => fmt,
};
let sample_bytes = stream_format.sample_format.size_in_bytes();
let n_channels = stream_format.channels;
let data_byte_size =
in_number_frames as usize * sample_bytes * n_channels as usize;
let ptr = (*audio_buffer_list_ptr).mBuffers.as_ptr() as *mut sys::AudioBuffer;
let len = (*audio_buffer_list_ptr).mNumberBuffers as usize;
let buffers: &mut [sys::AudioBuffer] = slice::from_raw_parts_mut(ptr, len);
let old_capacity = buffer_capacity;
for buffer in buffers {
let current_len = buffer.mDataByteSize as usize;
let audio_buffer_ptr = buffer.mData as *mut u8;
let mut vec: Vec<u8> =
Vec::from_raw_parts(audio_buffer_ptr, current_len, old_capacity);
vec.resize(data_byte_size, 0u8);
buffer_capacity = vec.capacity();
buffer.mData = vec.as_mut_ptr() as *mut _;
buffer.mDataByteSize = data_byte_size as u32;
mem::forget(vec);
}
}
buffer_frame_size = in_number_frames;
}
unsafe {
let status = sys::AudioUnitRender(
audio_unit,
io_action_flags,
in_time_stamp,
in_bus_number,
in_number_frames,
audio_buffer_list_ptr,
);
if status != 0 {
return status;
}
}
let args = unsafe {
let data = D::from_input_proc_args(in_number_frames, audio_buffer_list_ptr);
let flags = action_flags::Handle::from_ptr(io_action_flags);
Args {
data,
time_stamp: *in_time_stamp,
flags,
bus_number: in_bus_number as u32,
num_frames: in_number_frames as usize,
}
};
match f(args) {
Ok(()) => 0,
Err(()) => error::Error::Unspecified.as_os_status(),
}
};
let input_proc_fn_wrapper = Box::new(InputProcFnWrapper {
callback: Box::new(input_proc_fn),
});
// Setup input callback. Notice that we relinquish ownership of the Callback
// here so that it can be used as the C render callback via a void pointer.
// We do however store the *mut so that we can convert back to a Box<InputProcFnWrapper>
// within our AudioUnit's Drop implementation (otherwise it would leak).
let input_proc_fn_wrapper_ptr = Box::into_raw(input_proc_fn_wrapper) as *mut c_void;
let render_callback = sys::AURenderCallbackStruct {
inputProc: Some(input_proc),
inputProcRefCon: input_proc_fn_wrapper_ptr,
};
self.set_property(
sys::kAudioOutputUnitProperty_SetInputCallback,
Scope::Global,
Element::Output,
Some(&render_callback),
)?;
let input_callback = super::InputCallback {
buffer_list: audio_buffer_list_ptr,
callback: input_proc_fn_wrapper_ptr as *mut InputProcFnWrapper,
};
self.free_input_callback();
self.maybe_input_callback = Some(input_callback);
Ok(())
}
/// Retrieves ownership over the render callback and returns it where it can be re-used or
/// safely dropped.
pub fn free_render_callback(&mut self) -> Option<Box<InputProcFnWrapper>> {
if let Some(callback) = self.maybe_render_callback.take() {
// Here, we transfer ownership of the callback back to the current scope so that it
// is dropped and cleaned up. Without this line, we would leak the Boxed callback.
let callback: Box<InputProcFnWrapper> = unsafe { Box::from_raw(callback) };
return Some(callback);
}
None
}
/// Retrieves ownership over the input callback and returns it where it can be re-used or
/// safely dropped.
pub fn free_input_callback(&mut self) -> Option<Box<InputProcFnWrapper>> {
if let Some(input_callback) = self.maybe_input_callback.take() {
let super::InputCallback {
buffer_list,
callback,
} = input_callback;
unsafe {
// Take ownership over the AudioBufferList in order to safely free it.
let buffer_list: Box<sys::AudioBufferList> = Box::from_raw(buffer_list);
// Free the allocated data from the individual audio buffers.
let ptr = buffer_list.mBuffers.as_ptr() as *const sys::AudioBuffer;
let len = buffer_list.mNumberBuffers as usize;
let buffers: &[sys::AudioBuffer] = slice::from_raw_parts(ptr, len);
for &buffer in buffers {
let ptr = buffer.mData as *mut u8;
let len = buffer.mDataByteSize as usize;
let cap = len;
let _ = Vec::from_raw_parts(ptr, len, cap);
}
// Take ownership over the callback so that it can be freed.
let callback: Box<InputProcFnWrapper> = Box::from_raw(callback);
return Some(callback);
}
}
None
}
}
/// Callback procedure that will be called each time our audio_unit requests audio.
extern "C" fn input_proc(
in_ref_con: *mut c_void,
io_action_flags: *mut sys::AudioUnitRenderActionFlags,
in_time_stamp: *const sys::AudioTimeStamp,
in_bus_number: sys::UInt32,
in_number_frames: sys::UInt32,
io_data: *mut sys::AudioBufferList,
) -> sys::OSStatus {
let wrapper = in_ref_con as *mut InputProcFnWrapper;
unsafe {
(*(*wrapper).callback)(
io_action_flags,
in_time_stamp,
in_bus_number,
in_number_frames,
io_data,
)
}
}

View File

@@ -0,0 +1,103 @@
use super::audio_format::{self, LinearPcmFlags};
/// Dynamic representation of audio data sample format.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SampleFormat {
/// 32-bit float.
F32,
/// 32-bit signed integer.
I32,
/// 24-bit signed integer. Can be packed or not depending on the flags.
I24,
/// 16-bit signed integer.
I16,
/// 8-bit signed integer.
I8,
}
impl SampleFormat {
/// Check if the format flags are appropriate for the given format.
pub fn does_match_flags(&self, flags: audio_format::LinearPcmFlags) -> bool {
let is_float = flags.contains(LinearPcmFlags::IS_FLOAT);
let is_signed_integer = flags.contains(LinearPcmFlags::IS_SIGNED_INTEGER);
let is_packed = flags.contains(LinearPcmFlags::IS_PACKED);
match *self {
SampleFormat::F32 => is_float && !is_signed_integer && is_packed,
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
is_signed_integer && !is_float && is_packed
}
SampleFormat::I24 => is_signed_integer && !is_float,
}
}
/// Convert format flags and bits_per_sample to a SampleFormat.
pub fn from_flags_and_bits_per_sample(
flags: audio_format::LinearPcmFlags,
bits_per_sample: u32,
) -> Option<Self> {
let packed = flags.contains(LinearPcmFlags::IS_PACKED);
let sample_format = if flags.contains(LinearPcmFlags::IS_FLOAT) {
match (bits_per_sample, packed) {
(32, true) => SampleFormat::F32,
_ => return None,
}
} else if flags.contains(LinearPcmFlags::IS_SIGNED_INTEGER) {
match (bits_per_sample, packed) {
(8, true) => SampleFormat::I8,
(16, true) => SampleFormat::I16,
(24, _) => SampleFormat::I24,
(32, true) => SampleFormat::I32,
_ => return None,
}
} else {
// TODO: Check whether or not we need to consider other formats, like unsigned ints.
return None;
};
Some(sample_format)
}
/// Return the size of one sample in bytes, assuming that the format is packed.
pub fn size_in_bytes(&self) -> usize {
use std::mem::size_of;
match *self {
SampleFormat::F32 => size_of::<f32>(),
SampleFormat::I32 => size_of::<i32>(),
SampleFormat::I24 => 3 * size_of::<u8>(),
SampleFormat::I16 => size_of::<i16>(),
SampleFormat::I8 => size_of::<i8>(),
}
}
/// Return the number of valid bits for one sample.
pub fn size_in_bits(&self) -> u32 {
match *self {
SampleFormat::F32 => 32,
SampleFormat::I32 => 32,
SampleFormat::I24 => 24,
SampleFormat::I16 => 16,
SampleFormat::I8 => 8,
}
}
}
/// Audio data sample types.
pub trait Sample {
/// Dynamic representation of audio data sample format.
fn sample_format() -> SampleFormat;
}
/// Simplified implementation of the `Sample` trait for sample types.
/// This is only implemented for the sample types that map directly to a numeric type.
macro_rules! impl_sample {
($($T:ident $format:ident),* $(,)*) => {
$(
impl Sample for $T {
fn sample_format() -> SampleFormat {
SampleFormat::$format
}
}
)*
}
}
impl_sample!(f32 F32, i32 I32, i16 I16, i8 I8);

View File

@@ -0,0 +1,144 @@
//! A rustification of the `AudioStreamBasicDescription` type.
//!
//! Find the original `AudioStreamBasicDescription` reference [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/c/tdef/AudioStreamBasicDescription).
use super::audio_format::AudioFormat;
use super::audio_format::LinearPcmFlags;
use super::SampleFormat;
use crate::error::{self, Error};
use sys;
/// A representation of the AudioStreamBasicDescription specifically for use with the AudioUnit API.
///
/// By using a type specific to the audio unit API, we can remove a lot of unnecessary boilerplate
/// that is normally associated with the AudioStreamBasicDescription.
///
/// Seeing as `LinearPCM` data (the `AudioFormat` used by the `AudioUnit` API) implies a single
/// frame per packet, we can infer many of the fields in an ASBD from the sample type.
///
/// `bytes_per_packet = size_of::<S>()`
/// `bytes_per_frame = size_of::<S>()`
/// `frames_per_packet` = 1
/// `bits_per_channel = size_of::<S>()` / channels_per_frame * 8
///
/// > A *packet* is a collection of one or more contiguous frames. In linear PCM audio, a packet is
/// always a single frame.
///
/// [from *Core Audio Overview*](https://developer.apple.com/library/ios/documentation/MusicAudio/Conceptual/CoreAudioOverview/WhatisCoreAudio/WhatisCoreAudio.html)
///
/// > The canonical formats in Core Audio are as follows:
/// >
/// > - iOS input and output: Linear PCM with 16-bit integer samples.
/// > - iOS audio units and other audio processing: Noninterleaved linear PCM with 8.24-bit
/// fixed-point samples
/// > - Mac input and output: Linear PCM with 32-bit floating point samples.
/// > - Mac audio units and other audio processing: Noninterleaved linear PCM with 32-bit floating
/// point samples.
#[derive(Copy, Clone, Debug)]
pub struct StreamFormat {
/// The number of frames of audio data per second used to represent a signal.
pub sample_rate: f64,
/// The sample format used to represent the audio data.
///
/// In OS X, Core Audio expects audio data to be in native-endian, 32-bit floating-point,
/// linear PCM format.
///
/// iOS uses integer and fixed-point audio data. The result is faster calculations and less
/// battery drain when processing audio. iOS provides a Converter audio unit and inclues the
/// interfaces from Audio Converter Services (TODO: look into exposing this).
pub sample_format: SampleFormat,
/// The format flags for the given StreamFormat.
pub flags: super::audio_format::LinearPcmFlags,
/// The number of channels.
pub channels: u32,
}
impl StreamFormat {
/// Convert an AudioStreamBasicDescription into a StreamFormat.
///
/// Note: `audio_unit::StreamFormat` exclusively uses the `LinearPCM` `AudioFormat`. This is as
/// specified in the documentation:
///
/// > Specify kAudioFormatLinearPCM for the mFormatID field. Audio units use uncompressed audio
/// data, so this is the correct format identifier to use whenever you work with audio units.
///
/// [*Audio Unit Hosting Guide for iOS*](https://developer.apple.com/library/ios/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/AudioUnitHostingFundamentals/AudioUnitHostingFundamentals.html)
///
/// Returns an `Error` if the `AudioFormat` inferred by the ASBD is not `LinearPCM`.
///
/// Returns an `Error` if the sample format of the asbd cannot be matched to a format supported by SampleFormat.
#[allow(non_snake_case)]
pub fn from_asbd(asbd: sys::AudioStreamBasicDescription) -> Result<StreamFormat, Error> {
const NOT_SUPPORTED: Error = Error::AudioUnit(error::audio_unit::Error::FormatNotSupported);
let sys::AudioStreamBasicDescription {
mSampleRate,
mFormatID,
mFormatFlags,
mBytesPerFrame: _,
mChannelsPerFrame,
mBitsPerChannel,
..
} = asbd;
// Retrieve the LinearPCM flags.
let flags = match AudioFormat::from_format_and_flag(mFormatID, Some(mFormatFlags)) {
Some(AudioFormat::LinearPCM(flags)) => flags,
_ => return Err(NOT_SUPPORTED),
};
// Determine the `SampleFormat` to use.
let sample_format =
match SampleFormat::from_flags_and_bits_per_sample(flags, mBitsPerChannel) {
Some(sample_format) => sample_format,
None => return Err(NOT_SUPPORTED),
};
let channels = mChannelsPerFrame;
Ok(StreamFormat {
sample_rate: mSampleRate,
flags,
sample_format,
channels,
})
}
/// Convert a StreamFormat into an AudioStreamBasicDescription.
/// Note that this function assumes that only packed formats are used.
/// This only affects I24, since all other formats supported by `StreamFormat`
/// are always packed.
pub fn to_asbd(self) -> sys::AudioStreamBasicDescription {
let StreamFormat {
sample_rate,
flags,
sample_format,
channels,
} = self;
let (format, maybe_flag) =
AudioFormat::LinearPCM(flags | LinearPcmFlags::IS_PACKED).as_format_and_flag();
let flag = maybe_flag.unwrap_or(::std::u32::MAX - 2147483647);
let non_interleaved = flags.contains(LinearPcmFlags::IS_NON_INTERLEAVED);
let bytes_per_frame = if non_interleaved {
sample_format.size_in_bytes() as u32
} else {
sample_format.size_in_bytes() as u32 * channels
};
const FRAMES_PER_PACKET: u32 = 1;
let bytes_per_packet = bytes_per_frame * FRAMES_PER_PACKET;
let bits_per_channel = sample_format.size_in_bits();
sys::AudioStreamBasicDescription {
mSampleRate: sample_rate,
mFormatID: format,
mFormatFlags: flag,
mBytesPerPacket: bytes_per_packet,
mFramesPerPacket: FRAMES_PER_PACKET,
mBytesPerFrame: bytes_per_frame,
mChannelsPerFrame: channels,
mBitsPerChannel: bits_per_channel,
mReserved: 0,
}
}
}

View File

@@ -0,0 +1,445 @@
//! Core Audio's various const audio unit types identifiers represented as typesafe enums.
//!
//! Oirginal documentation [here](https://developer.apple.com/library/prerelease/mac/documentation/AudioUnit/Reference/AUComponentServicesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Types).
/// Represents the different kinds of Audio Units that are available.
///
/// Original documentation [here](https://developer.apple.com/library/prerelease/mac/documentation/AudioUnit/Reference/AUComponentServicesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Types).
#[derive(Copy, Clone, Debug)]
pub enum Type {
/// Provides input, output, or both input and output simultaneously.
///
/// It can be used as the head of an audio unit processing graph.
///
/// **Available** in OS X v10.2 and later.
IO(IOType),
/// An instrument unit can be used as a software musical instrument, such as a sampler or
/// synthesizer.
///
/// It responds to MIDI (Musical Instrument Digital Interface) control signals and can create
/// notes.
///
/// **Available** in OS X v10.2 and later.
MusicDevice(MusicDeviceType),
/// An effect unit that can respond to MIDI control messages, typically through a mapping of
/// MIDI messages to parameters of the audio unit's DSP algorithm.
///
/// **Available** in OS X v10.2 and later.
MusicEffect,
/// A format converter unit can transform audio formats, such as performing sample rate
/// conversion.
///
/// A format converter is also appropriate for dferred rendering and for effects such as
/// varispeed.
///
/// A format converter unit can ask for as much or as little audio input as it needs to produce
/// a given output, while still completing its rendering within the time represented by the
/// output buffer.
///
/// For effect-like format converters, such as pitch shifters, it is common to provide both a
/// real-time and an offline version. OS X, for example, includes Time-Pitch and Varispeed
/// audio units in both real-time and offline versions.
///
/// **Available** in OS X v10.2 and later.
FormatConverter(FormatConverterType),
/// An effect unit repeatedly processes a number of audio input samples to produce the same
/// number of audio output samples.
///
/// Most commonly, an effect unit has a single input and a single output.
///
/// Some effects take side-chain inputs as well.
///
/// Effect units can be run offline, such as to process a file without playing it, but are
/// expected to run in real-time.
///
/// **Available** in OS X v10.2 and later.
Effect(EffectType),
/// A mixer unit takes a number of input channels and mixes them to provide one or more output
/// channels.
///
/// For example, the **StereoMixer** **SubType** in OS X takes multiple mono or stereo inputs
/// and produces a single stereo output.
///
/// **Available** in OS X v10.2 and later.
Mixer(MixerType),
/// A panner unit is a specialised effect unit that distributes one or more channels in a
/// single input to one or more channels in a single output.
///
/// Panner units must support a set of standard audio unit parameters that specify panning
/// coordinates.
///
/// **Available** in OS X v10.3 and later.
Panner,
/// A generator unit provides audio output that has no audio input.
///
/// This audio unit type is appropriate for a tone generator.
///
/// Unlike an instrument unit, a generator unit does not have a control input.
///
/// **Available** in OS X v10.3 and later.
Generator(GeneratorType),
/// An offline effect unit provides digital signal processing of a sort that cannot proceed in
/// real-time.
///
/// For example, level normalisation requires examination of an entire sound, beginning to end,
/// before the normalisation factor can be calculated.
///
/// As such, offline effect units also have a notion of a priming stage that can be performed
/// before the actual rendering/processing phase is executed.
///
/// **Available** in OS X v10.3 and later.
OfflineEffect,
/// FIXME: Could not find any documenation for this type - it seems it was added very recently
/// (around 2013) and Apple's documentation doesn't seem to have updated to include it.
MidiProcessor,
}
impl Type {
/// Convert the `Type` to its associated `u32` for compatibility with original API.
pub fn as_u32(&self) -> u32 {
match *self {
Type::IO(_) => 1635086197,
Type::MusicDevice(_) => 1635085685,
Type::MusicEffect => 1635085670,
Type::FormatConverter(_) => 1635083875,
Type::Effect(_) => 1635083896,
Type::Mixer(_) => 1635085688,
Type::Panner => 1635086446,
Type::Generator(_) => 1635084142,
Type::OfflineEffect => 1635086188,
Type::MidiProcessor => 1635085673,
}
}
/// Convert the `Type` to the const `u32` that is associated with its subtype.
pub fn as_subtype_u32(&self) -> Option<u32> {
match *self {
Type::IO(ty) => Some(ty as u32),
Type::MusicDevice(ty) => Some(ty as u32),
Type::FormatConverter(ty) => Some(ty as u32),
Type::Effect(ty) => Some(ty as u32),
Type::Mixer(ty) => Some(ty as u32),
Type::Generator(ty) => Some(ty as u32),
_ => None,
}
}
}
impl From<EffectType> for Type {
fn from(ty: EffectType) -> Self {
Type::Effect(ty)
}
}
impl From<FormatConverterType> for Type {
fn from(ty: FormatConverterType) -> Self {
Type::FormatConverter(ty)
}
}
impl From<MixerType> for Type {
fn from(ty: MixerType) -> Self {
Type::Mixer(ty)
}
}
impl From<GeneratorType> for Type {
fn from(ty: GeneratorType) -> Self {
Type::Generator(ty)
}
}
impl From<MusicDeviceType> for Type {
fn from(ty: MusicDeviceType) -> Self {
Type::MusicDevice(ty)
}
}
impl From<IOType> for Type {
fn from(ty: IOType) -> Self {
Type::IO(ty)
}
}
/// Effect (digital signal processing) audio unit subtypes for audio units provided by Apple.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum EffectType {
/// An audio unit that enforces an upper dynamic limit on an audio signal.
///
/// **Available** in OS X v10.2 and later.
PeakLimiter = 1819112562,
/// An audio unit that provides dynamic compression or expansion.
///
/// **Available** in OS X v10.3 and later.
DynamicsProcessor = 1684237680,
/// An audio unit that passes frequencies below a specified cutoff frequency and blocks
/// frequencies above that cutoff frequency.
///
/// **Available** in OS X v10.2 and later.
LowPassFilter = 1819304307,
/// An audio unit that passes frequencies above a specified cutoff frequency and blocks
/// frequencies below that cutoff frequency.
///
/// **Available** in OS X v10.2 and later.
HighPassFilter = 1752195443,
/// An audio unit that passes frequencies between specified upper and lower cutoff frequencies,
/// and blocks frequencies outside that band.
///
/// **Available** in OS X v10.2 and later.
BandPassFilter = 1651532147,
/// An audio unit suitable for implementing a treble control in an audio playback or recording
/// system.
///
/// **Available** in OS X v10.2 and later.
HighShelfFilter = 1752393830,
/// An audio unit suitable for implementing a bass control in an audio playback or recording
/// system.
///
/// **Available** in OS X v10.2 and later.
LowShelfFilter = 1819502694,
/// An audio unit that provides a filter whose center frequency, boost/cut level, and Q can be
/// adjusted.
///
/// **Available** in OS X v10.2 and later.
ParametricEQ = 1886217585,
/// An audio unit that provides a distortion effect.
///
/// **Available** in OS X v10.5 and later.
Distortion = 1684632436,
/// An audio unit that introduces a time delay to a signal.
///
/// **Available** in OS X v10.2 and later.
Delay = 1684368505,
/// An audio unit that provides a time delay for a specified number of samples.
///
/// **Available** in OS X v10.4 and later.
SampleDelay = 1935961209,
/// An audio unit that provides a 10- or 31-band graphic equalizer.
///
/// Available in OS X v10.2 and later.
GraphicEQ = 1735550321,
/// An audio unit that provides four-bands of dynamic compression or expansion.
///
/// **Available** in OS X v10.3 and later.
MultiBandCompressor = 1835232624,
/// An audio unit that provides a reverberation effect that can be used to simulate a variety
/// of acoustic spaces.
///
/// **Available** in OS X v10.2 and later.
MatrixReverb = 1836213622,
/// An audio unit for modifying the pitch of a signal.
///
/// **Available** in OS X v10.4 and later.
Pitch = 1953329268,
/// An audio unit that provides a combination of five filters: low-frequency, three
/// mid-frequencies, and high-frequency.
///
/// **Available** in OS X v10.4 and later.
AUFilter = 1718185076,
/// An audio unit for use in conjunction with a kAudioUnitSubType_NetReceive audio unit for
/// sending audio across a network or from one application to another.
///
/// **Available** in OS X v10.4 and later.
NetSend = 1853058660,
/// An audio unit that detects gaps between segments of speech and fills the gaps with a short
/// tone, simulating the sound of a walkie-talkie communication device.
///
/// **Available** in OS X v10.5 and later.
RogerBeep = 1919903602,
/// A multi-band equalizer with specifiable filter type for each band.
///
/// **Available** in OS X v10.9 and later.
NBandEQ = 1851942257,
}
/// Audio data format converter audio unit subtypes for **AudioUnit**s provided by Apple.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum FormatConverterType {
/// An audio unit that uses an audio converter to do linear PCM conversions, such as changes to
/// sample rate, bit depth, or interleaving.
///
/// **Available** in OS X v10.2 and later.
AUConverter = 1668247158,
/// An audio unit that can be used to have independent control of both playback rate and pitch.
///
/// In OS X it provides a generic view, so it can be used in both a UI and programmatic
/// context.
///
/// It also comes in an offline version for processing audio files.
///
/// **Available** in OS X v10.7 and later.
NewTimePitch = 1853191280,
/// An audio unit that can provide independent control of playback rate and pitch. This subtype
/// provides a generic view, making it suitable for UI and programmatic context. OS X provides
/// realtime and offline audio units of this subtype.
///
/// **Available** in OS X v10.3 and later.
TimePitch = 1953329268,
/// An audio unit that acquires audio input from a separate thread than the thread on which its
/// render method is called.
///
/// You can use this subtype to introduce multiple threads into an audio unit processing graph.
///
/// There is a delay, equal to the buffer size, introduced between the audio input and output.
///
/// **Available** in OS X v10.4 and later.
DeferredRenderer = 1684366962,
/// An audio unit with one input bus and two output buses. The audio unit duplicates the input
/// signal to each of its two output buses.
///
/// **Available** in OS X v10.4 and later.
Splitter = 1936747636,
/// An audio unit with two input buses and one output bus. The audio unit merges the two input
/// signals to the single output.
///
/// **Available** in OS X v10.4 and later.
Merger = 1835364967,
/// An audio unit that can control playback rate. As the playback rate increases, so does
/// pitch.
///
/// This subtype provides a generic view, making it suitable for UI and programmatic context.
///
/// OS X provides realtime and offline audio units of this subtype.
///
/// **Available** in OS X v10.3 and later.
Varispeed = 1986097769,
/// **Available** in OS X v10.9 and later.
AUiPodTimeOther = 1768977519,
}
/// Audio mixing **AudioUnit** subtypes for **AudioUnit**s provided by Apple.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MixerType {
/// An audio unit that can have any number of input buses, with any number of channels on each
/// input bus, and one output bus.
///
/// In OS X, the output bus can have any number of channels.
///
/// In iPhone OS, the output bus always has two channels.
///
/// **Available** in OS X v10.5 and later.
MultiChannelMixer = 1835232632,
/// An audio unit that can have any number of input buses, each of which is mono or stereo, and
/// one stereo output bus.
///
/// **Available** in OS X v10.2 and later.
StereoMixer = 1936554098,
/// An audio unit that can have any number of input buses and one output bus.
///
/// Each input bus can be mono, in which case it can be panned using 3D coordinates and
/// parameters.
///
/// Stereo input buses pass directly through to the output.
///
/// Four-channel ambisonic inputs are rendered to the output configuration.
///
/// The single output bus can be configured with 2, 4, 5, 6, 7 or 8 channels.
///
/// **Available** in OS X v10.3 and later.
///
/// **Deprecated** in OS X v10.10.
Mixer3D = 862219640,
/// An audio unit that can have any number of input and output buses with any number of
/// channels on each bus.
///
/// You configure the mix using a matrix of channels with a separate input level control for
/// each channel.
///
/// The audio unit also provides individual level control for each
/// input-channel-to-output-channel combination, as well as level control for each output
/// channel.
///
/// Finally, the audio unit provides a global level control for the matrix as a whole.
///
/// **Available** in OS X v10.3 and later.
MatrixMixer = 1836608888,
}
/// Audio units that serve as sound sources.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum GeneratorType {
/// A generator unit that can be used to schedule slices of audio to be played at specified
/// times.
///
/// The audio is scheduled using the time stamps for the render operation and can be scheduled
/// from any thread.
///
/// **Available** in OS X v10.4 and later.
ScheduledSoundPlayer = 1936945260,
/// A generator unit that is used to play a file. In OS X it presents a custom UI so can be
/// used in a UI context as well as in a programmatic context.
///
/// **Available** in OS X v10.4 and later.
AudioFilePlayer = 1634103404,
}
/// Audio units that can be played as musical instruments via MIDI control.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MusicDeviceType {
/// A multitimbral instrument unit that can use sample banks in either DLS or SoundFont
/// formats.
///
/// It fully supports GM-MIDI and the basic extensions of GS-MIDI
///
/// **Available** in OS X v10.2 and later.
DLSSynth = 1684828960,
/// A monotimbral instrument unit that functions a a sampler-synthesizer and supports full
/// interactive editing of its state.
///
/// **Available** in OS X v10.7 and later.
Sampler = 1935764848,
}
/// Input/output **AudioUnit** subtypes for **AudioUnit**s provided by Apple.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum IOType {
/// An audio unit that responds to start/stop calls and provides basic services for converting
/// to and from linear PCM formats.
///
/// Use this audio unit when sending the output of an audio processing graph to your
/// application rather than to the output audio hardware. You would typically use the Generic
/// Output unit for offline audio processing. Just like the other I/O units, the Generic Output
/// unit incorporates a Format Converter unit. This lets the Generic Output unit perform format
/// conversion between the stream format used in an audio processing graph and the format you
/// want.
///
/// You can also use a Generic Output unit as the final node in a subgraph that you place into
/// a parent audio processing graph.
///
/// **Available** in OS X v10.2 and later.
GenericOutput = 1734700658,
/// An audio unit that can provides input/output connection to an a specified audio device.
///
/// Bus 0 provides output to the audio device and bus 1 accepts input from the audio device.
///
/// **Available** in OS X v10.2 and later.
HalOutput = 1634230636,
/// A specialized **HalOutput** audio unit that connects to the users selected default device
/// in Sound Preferences.
///
/// **Available** in OS X v10.2 and later.
DefaultOutput = 1684366880,
/// A specialized **HalOutput** audio unit that connects to the users selected device for
/// sound effects, alerts, and other user-interface sounds.
///
/// **Available** in OS X v10.2 and later.
SystemOutput = 1937339168,
/// An audio unit that interfaces to the audio inputs and outputs of iPhone OS devices and
/// provides voice processing features.
///
/// Bus 0 provides output to hardware and bus 1 accepts input from hardware.
///
/// See the [Voice-Processing I/O Audio Unit
/// Properties](https://developer.apple.com/library/prerelease/mac/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/constant_group/Voice_Processing_I_O_Audio_Unit_Properties)
/// enumeration for the identifiers for this audio units properties.
///
/// **Available** in OS X v10.7 and later.
VoiceProcessingIO = 1987078511,
/// Connects to device hardware for input, output, or simultaneous input and output.
/// Use it for playback, recording, or low-latency simultaneous input and output where echo
/// cancelation is not needed.
///
/// See <https://developer.apple.com/library/content/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/UsingSpecificAudioUnits/UsingSpecificAudioUnits.html>
/// **Available** in iOS.
RemoteIO = 1919512419,
}

334
vendor/coreaudio-rs/src/error.rs vendored Normal file
View File

@@ -0,0 +1,334 @@
//! This module is an attempt at rustifying the OSStatus result.
pub use self::audio::Error as AudioError;
pub use self::audio_codec::Error as AudioCodecError;
pub use self::audio_format::Error as AudioFormatError;
pub use self::audio_unit::Error as AudioUnitError;
use sys::OSStatus;
pub mod audio {
use sys::OSStatus;
#[derive(Copy, Clone, Debug)]
pub enum Error {
Unimplemented = -4,
FileNotFound = -43,
FilePermission = -54,
TooManyFilesOpen = -42,
BadFilePath = 561017960,
Param = -50,
MemFull = -108,
Unknown,
}
impl Error {
pub fn from_os_status(os_status: OSStatus) -> Result<(), Error> {
match os_status {
0 => Ok(()),
-4 => Err(Error::Unimplemented),
-43 => Err(Error::FileNotFound),
-54 => Err(Error::FilePermission),
-42 => Err(Error::TooManyFilesOpen),
561017960 => Err(Error::BadFilePath),
-50 => Err(Error::Param),
-108 => Err(Error::MemFull),
_ => Err(Error::Unknown),
}
}
pub fn as_os_status(&self) -> OSStatus {
*self as OSStatus
}
}
impl std::error::Error for Error {}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let description = match *self {
Error::Unimplemented => "Unimplemented",
Error::FileNotFound => "File not found",
Error::FilePermission => "File permission",
Error::TooManyFilesOpen => "Too many files open",
Error::BadFilePath => "Bad file path",
Error::Param => "Param",
Error::MemFull => "Memory full",
Error::Unknown => "An unknown error occurred",
};
write!(f, "{}", description)
}
}
}
pub mod audio_codec {
use sys::OSStatus;
#[derive(Copy, Clone, Debug)]
pub enum Error {
Unspecified = 2003329396,
UnknownProperty = 2003332927,
BadPropertySize = 561211770,
IllegalOperation = 1852797029,
UnsupportedFormat = 560226676,
State = 561214580,
NotEnoughBufferSpace = 560100710,
Unknown,
}
impl Error {
pub fn from_os_status(os_status: OSStatus) -> Result<(), Error> {
match os_status {
0 => Ok(()),
2003329396 => Err(Error::Unspecified),
2003332927 => Err(Error::UnknownProperty),
561211770 => Err(Error::BadPropertySize),
1852797029 => Err(Error::IllegalOperation),
560226676 => Err(Error::UnsupportedFormat),
561214580 => Err(Error::State),
560100710 => Err(Error::NotEnoughBufferSpace),
_ => Err(Error::Unknown),
}
}
pub fn as_os_status(&self) -> OSStatus {
*self as OSStatus
}
}
impl std::error::Error for Error {}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let description = match *self {
Error::Unspecified => "Unspecified",
Error::UnknownProperty => "Unknown property",
Error::BadPropertySize => "Bad property size",
Error::IllegalOperation => "Illegal operation",
Error::UnsupportedFormat => "Unsupported format",
Error::State => "State",
Error::NotEnoughBufferSpace => "Not enough buffer space",
Error::Unknown => "Unknown error occurred",
};
write!(f, "{}", description)
}
}
}
pub mod audio_format {
use sys::OSStatus;
// TODO: Finish implementing these values.
#[derive(Copy, Clone, Debug)]
pub enum Error {
Unspecified, // 'what'
UnsupportedProperty, // 'prop'
BadPropertySize, // '!siz'
BadSpecifierSize, // '!spc'
UnsupportedDataFormat = 1718449215, // 'fmt?'
UnknownFormat, // '!fmt'
Unknown, //
}
impl Error {
pub fn from_os_status(os_status: OSStatus) -> Result<(), Error> {
match os_status {
0 => Ok(()),
1718449215 => Err(Error::UnsupportedDataFormat),
_ => Err(Error::Unknown),
}
}
pub fn as_os_status(&self) -> OSStatus {
*self as OSStatus
}
}
impl std::error::Error for Error {}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let description = match *self {
Error::Unspecified => "An unspecified error",
Error::UnsupportedProperty => "The specified property is not supported",
Error::BadPropertySize => "Bad property size",
Error::BadSpecifierSize => "Bad specifier size",
Error::UnsupportedDataFormat => "The specified data format is not supported",
Error::UnknownFormat => "The specified data format is not a known format",
Error::Unknown => "Unknown error occurred",
};
write!(f, "{}", description)
}
}
}
pub mod audio_unit {
use sys::OSStatus;
#[derive(Copy, Clone, Debug)]
pub enum Error {
InvalidProperty = -10879,
InvalidParameter = -10878,
InvalidElement = -10877,
NoConnection = -10876,
FailedInitialization = -10875,
TooManyFramesToProcess = -10874,
InvalidFile = -10871,
FormatNotSupported = -10868,
Uninitialized = -10867,
InvalidScope = -10866,
PropertyNotWritable = -10865,
CannotDoInCurrentContext = -10863,
InvalidPropertyValue = -10851,
PropertyNotInUse = -10850,
Initialized = -10849,
InvalidOfflineRender = -10848,
Unauthorized = -10847,
Unknown,
}
impl Error {
pub fn from_os_status(os_status: OSStatus) -> Result<(), Error> {
match os_status {
-10879 => Err(Error::InvalidProperty),
-10878 => Err(Error::InvalidParameter),
-10877 => Err(Error::InvalidElement),
-10876 => Err(Error::NoConnection),
-10875 => Err(Error::FailedInitialization),
-10874 => Err(Error::TooManyFramesToProcess),
-10871 => Err(Error::InvalidFile),
-10868 => Err(Error::FormatNotSupported),
-10867 => Err(Error::Uninitialized),
-10866 => Err(Error::InvalidScope),
-10865 => Err(Error::PropertyNotWritable),
-10863 => Err(Error::CannotDoInCurrentContext),
-10851 => Err(Error::InvalidPropertyValue),
-10850 => Err(Error::PropertyNotInUse),
-10849 => Err(Error::Initialized),
-10848 => Err(Error::InvalidOfflineRender),
-10847 => Err(Error::Unauthorized),
_ => Err(Error::Unknown),
}
}
pub fn as_os_status(&self) -> OSStatus {
*self as OSStatus
}
}
impl std::error::Error for Error {}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let description = match *self {
Error::InvalidProperty => "Invalid property",
Error::InvalidParameter => "Invalid parameter",
Error::InvalidElement => "Invalid element",
Error::NoConnection => "No connection",
Error::FailedInitialization => "Failed initialization",
Error::TooManyFramesToProcess => "Too many frames to process",
Error::InvalidFile => "Invalid file",
Error::FormatNotSupported => "Format not supported",
Error::Uninitialized => "Uninitialized",
Error::InvalidScope => "Invalid scope",
Error::PropertyNotWritable => "Property not writable",
Error::CannotDoInCurrentContext => "Cannot do in current context",
Error::InvalidPropertyValue => "Invalid property value",
Error::PropertyNotInUse => "Property not in use",
Error::Initialized => "Initialized",
Error::InvalidOfflineRender => "Invalid offline render",
Error::Unauthorized => "Unauthorized",
Error::Unknown => "Unknown error occurred",
};
write!(f, "{}", description)
}
}
}
/// A wrapper around all possible Core Audio errors.
#[derive(Copy, Clone, Debug)]
pub enum Error {
Unspecified,
SystemSoundClientMessageTimedOut,
NoMatchingDefaultAudioUnitFound,
RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat,
NoKnownSubtype,
NonInterleavedInputOnlySupportsMono,
UnsupportedSampleRate,
UnsupportedStreamFormat,
Audio(AudioError),
AudioCodec(AudioCodecError),
AudioFormat(AudioFormatError),
AudioUnit(AudioUnitError),
Unknown(OSStatus),
}
impl Error {
/// Convert an OSStatus to a std Rust Result.
pub fn from_os_status(os_status: OSStatus) -> Result<(), Error> {
match os_status {
0 => Ok(()),
-1500 => Err(Error::Unspecified),
-1501 => Err(Error::SystemSoundClientMessageTimedOut),
_ => {
match AudioError::from_os_status(os_status) {
Ok(()) => return Ok(()),
Err(AudioError::Unknown) => (),
Err(err) => return Err(Error::Audio(err)),
}
match AudioCodecError::from_os_status(os_status) {
Ok(()) => return Ok(()),
Err(AudioCodecError::Unknown) => (),
Err(err) => return Err(Error::AudioCodec(err)),
}
match AudioFormatError::from_os_status(os_status) {
Ok(()) => return Ok(()),
Err(AudioFormatError::Unknown) => (),
Err(err) => return Err(Error::AudioFormat(err)),
}
match AudioUnitError::from_os_status(os_status) {
Ok(()) => return Ok(()),
Err(AudioUnitError::Unknown) => (),
Err(err) => return Err(Error::AudioUnit(err)),
}
Err(Error::Unknown(os_status))
}
}
}
/// Convert an Error to an OSStatus.
pub fn as_os_status(&self) -> OSStatus {
match *self {
Error::Unspecified => -1500,
Error::NoMatchingDefaultAudioUnitFound => -1500,
Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat => -1500,
Error::SystemSoundClientMessageTimedOut => -1501,
Error::Audio(err) => err as OSStatus,
Error::AudioCodec(err) => err as OSStatus,
Error::AudioUnit(err) => err as OSStatus,
_ => -1500,
}
}
}
impl std::error::Error for Error {}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
match *self {
Error::Unspecified => write!(f, "An unspecified error has occurred"),
Error::NoMatchingDefaultAudioUnitFound => write!(f, "No matching default audio unit found"),
Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat =>
write!(f, "The given render callback buffer format does not match the `AudioUnit` `StreamFormat`"),
Error::SystemSoundClientMessageTimedOut => write!(f, "The system sound client message timed out"),
Error::NoKnownSubtype => write!(f, "The type has no known subtypes"),
Error::NonInterleavedInputOnlySupportsMono => write!(f, "In non-interleaved mode input only supports one channel"),
Error::UnsupportedSampleRate => write!(f, "The requested sample rate is not available"),
Error::UnsupportedStreamFormat => write!(f, "The requested stream format is not available"),
Error::Audio(ref err) => write!(f, "{}", err),
Error::AudioCodec(ref err) => write!(f, "{}", err),
Error::AudioFormat(ref err) => write!(f, "{}", err),
Error::AudioUnit(ref err) => write!(f, "{}", err),
Error::Unknown(_) => write!(f, "An unknown error unknown to the coreaudio-rs API occurred"),
}
}
}

20
vendor/coreaudio-rs/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,20 @@
//! coreaudio-rs
//! ------------
//!
//! A friendly rust interface for Apple's CoreAudio API.
//!
//! Read the CoreAudio Overview [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html).
//!
//! Currently, work has only been started on the [audio_unit](./audio_unit/index.html) module, but
//! eventually we'd like to cover at least the majority of the C API.
#[macro_use]
extern crate bitflags;
extern crate core_foundation_sys;
pub extern crate coreaudio_sys as sys;
pub use error::Error;
#[cfg(feature = "audio_unit")]
pub mod audio_unit;
pub mod error;