2014-03-11 21:46:00 +01:00
/**
* @ author Gary P . SCAVONE
*
* @ copyright 2001 - 2013 Gary P . Scavone , all right reserved
*
* @ license like MIT ( see license file )
*/
// *************************************************** //
//
// OS/API-specific methods.
//
// *************************************************** //
2014-04-22 23:35:48 +02:00
# if defined(__MACOSX_CORE__) || defined(__IOS_CORE__)
2014-03-11 21:46:00 +01:00
# include <airtaudio/Interface.h>
2014-03-12 23:55:49 +01:00
# include <airtaudio/debug.h>
2014-03-11 21:46:00 +01:00
2014-03-11 22:37:22 +01:00
airtaudio : : Api * airtaudio : : api : : Core : : Create ( void ) {
return new airtaudio : : api : : Core ( ) ;
}
2014-03-11 21:46:00 +01:00
// The OS X CoreAudio API is designed to use a separate callback
// procedure for each of its audio devices. A single RtAudio duplex
// stream using two different devices is supported here, though it
// cannot be guaranteed to always behave correctly because we cannot
// synchronize these two callbacks.
//
// A property listener is installed for over/underrun information.
// However, no functionality is currently provided to allow property
// listeners to trigger user handlers because it is unclear what could
// be done if a critical stream parameter (buffer size, sample rate,
// device disconnect) notification arrived. The listeners entail
// quite a bit of extra code and most likely, a user program wouldn't
// be prepared for the result anyway. However, we do provide a flag
// to the client callback function to inform of an over/underrun.
// A structure to hold various information related to the CoreAudio API
// implementation.
struct CoreHandle {
2014-03-12 23:55:49 +01:00
AudioDeviceID id [ 2 ] ; // device ids
2014-03-11 21:46:00 +01:00
# if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
AudioDeviceIOProcID procId [ 2 ] ;
# endif
2014-03-12 23:55:49 +01:00
uint32_t iStream [ 2 ] ; // device stream index (or first if using multiple)
uint32_t nStreams [ 2 ] ; // number of streams to use
2014-03-11 21:46:00 +01:00
bool xrun [ 2 ] ;
char * deviceBuffer ;
2014-03-12 23:55:49 +01:00
std : : condition_variable condition ;
int32_t drainCounter ; // Tracks callback counts when draining
bool internalDrain ; // Indicates if stop is initiated from callback or not.
CoreHandle ( void ) :
deviceBuffer ( 0 ) ,
drainCounter ( 0 ) ,
internalDrain ( false ) {
nStreams [ 0 ] = 1 ;
nStreams [ 1 ] = 1 ;
id [ 0 ] = 0 ;
id [ 1 ] = 0 ;
xrun [ 0 ] = false ;
xrun [ 1 ] = false ;
}
2014-03-11 21:46:00 +01:00
} ;
2014-03-12 23:55:49 +01:00
airtaudio : : api : : Core : : Core ( void ) {
2014-03-11 21:46:00 +01:00
# if defined(AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER)
// This is a largely undocumented but absolutely necessary
// requirement starting with OS-X 10.6. If not called, queries and
// updates to various audio device properties are not handled
// correctly.
CFRunLoopRef theRunLoop = NULL ;
2014-03-12 23:55:49 +01:00
AudioObjectPropertyAddress property = {
kAudioHardwarePropertyRunLoop ,
kAudioObjectPropertyScopeGlobal ,
kAudioObjectPropertyElementMaster
} ;
OSStatus result = AudioObjectSetPropertyData ( kAudioObjectSystemObject ,
& property ,
0 ,
NULL ,
sizeof ( CFRunLoopRef ) ,
& theRunLoop ) ;
2014-03-11 21:46:00 +01:00
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::RtApiCore: error setting run loop property! " ) ;
2014-03-11 21:46:00 +01:00
}
# endif
}
2014-03-12 23:55:49 +01:00
airtaudio : : api : : Core : : ~ Core ( void ) {
2014-03-11 21:46:00 +01:00
// The subclass destructor gets called before the base class
// destructor, so close an existing stream before deallocating
// apiDeviceId memory.
2014-03-12 23:55:49 +01:00
if ( m_stream . state ! = STREAM_CLOSED ) {
closeStream ( ) ;
}
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
uint32_t airtaudio : : api : : Core : : getDeviceCount ( void ) {
2014-03-11 21:46:00 +01:00
// Find out how many audio devices there are, if any.
2014-03-12 23:55:49 +01:00
uint32_t dataSize ;
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDevices ,
kAudioObjectPropertyScopeGlobal ,
kAudioObjectPropertyElementMaster
} ;
2014-03-11 21:46:00 +01:00
OSStatus result = AudioObjectGetPropertyDataSize ( kAudioObjectSystemObject , & propertyAddress , 0 , NULL , & dataSize ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::getDeviceCount: OS-X error getting device info! " ) ;
2014-03-11 21:46:00 +01:00
return 0 ;
}
return dataSize / sizeof ( AudioDeviceID ) ;
}
2014-03-12 23:55:49 +01:00
uint32_t airtaudio : : api : : Core : : getDefaultInputDevice ( void ) {
2014-03-11 21:46:00 +01:00
uint32_t nDevices = getDeviceCount ( ) ;
2014-03-12 23:55:49 +01:00
if ( nDevices < = 1 ) {
return 0 ;
}
2014-03-11 21:46:00 +01:00
AudioDeviceID id ;
2014-03-12 23:55:49 +01:00
uint32_t dataSize = sizeof ( AudioDeviceID ) ;
AudioObjectPropertyAddress property = {
kAudioHardwarePropertyDefaultInputDevice ,
kAudioObjectPropertyScopeGlobal ,
kAudioObjectPropertyElementMaster
} ;
OSStatus result = AudioObjectGetPropertyData ( kAudioObjectSystemObject ,
& property ,
0 ,
NULL ,
& dataSize ,
& id ) ;
2014-03-11 21:46:00 +01:00
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::getDefaultInputDevice: OS-X system error getting device. " ) ;
2014-03-11 21:46:00 +01:00
return 0 ;
}
dataSize * = nDevices ;
AudioDeviceID deviceList [ nDevices ] ;
property . mSelector = kAudioHardwarePropertyDevices ;
2014-03-12 23:55:49 +01:00
result = AudioObjectGetPropertyData ( kAudioObjectSystemObject ,
& property ,
0 ,
NULL ,
& dataSize ,
( void * ) & deviceList ) ;
2014-03-11 21:46:00 +01:00
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::getDefaultInputDevice: OS-X system error getting device IDs. " ) ;
2014-03-11 21:46:00 +01:00
return 0 ;
}
2014-03-12 23:55:49 +01:00
for ( uint32_t iii = 0 ; iii < nDevices ; iii + + ) {
if ( id = = deviceList [ iii ] ) {
2014-04-22 23:35:48 +02:00
return iii ;
2014-03-12 23:55:49 +01:00
}
}
ATA_ERROR ( " airtaudio::api::Core::getDefaultInputDevice: No default device found! " ) ;
2014-03-11 21:46:00 +01:00
return 0 ;
}
2014-03-12 23:55:49 +01:00
uint32_t airtaudio : : api : : Core : : getDefaultOutputDevice ( void ) {
2014-03-11 21:46:00 +01:00
uint32_t nDevices = getDeviceCount ( ) ;
2014-03-12 23:55:49 +01:00
if ( nDevices < = 1 ) {
return 0 ;
}
2014-03-11 21:46:00 +01:00
AudioDeviceID id ;
2014-03-12 23:55:49 +01:00
uint32_t dataSize = sizeof ( AudioDeviceID ) ;
AudioObjectPropertyAddress property = {
kAudioHardwarePropertyDefaultOutputDevice ,
kAudioObjectPropertyScopeGlobal ,
kAudioObjectPropertyElementMaster
} ;
OSStatus result = AudioObjectGetPropertyData ( kAudioObjectSystemObject ,
& property ,
0 ,
NULL ,
& dataSize ,
& id ) ;
2014-03-11 21:46:00 +01:00
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::getDefaultOutputDevice: OS-X system error getting device. " ) ;
2014-03-11 21:46:00 +01:00
return 0 ;
}
dataSize = sizeof ( AudioDeviceID ) * nDevices ;
AudioDeviceID deviceList [ nDevices ] ;
property . mSelector = kAudioHardwarePropertyDevices ;
2014-03-12 23:55:49 +01:00
result = AudioObjectGetPropertyData ( kAudioObjectSystemObject ,
& property ,
0 ,
NULL ,
& dataSize ,
( void * ) & deviceList ) ;
2014-03-11 21:46:00 +01:00
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::getDefaultOutputDevice: OS-X system error getting device IDs. " ) ;
2014-03-11 21:46:00 +01:00
return 0 ;
}
2014-03-12 23:55:49 +01:00
for ( uint32_t iii = 0 ; iii < nDevices ; iii + + ) {
if ( id = = deviceList [ iii ] ) {
return iii ;
}
}
ATA_ERROR ( " airtaudio::api::Core::getDefaultOutputDevice: No default device found! " ) ;
2014-03-11 21:46:00 +01:00
return 0 ;
}
2014-04-22 23:35:48 +02:00
airtaudio : : DeviceInfo airtaudio : : api : : Core : : getDeviceInfo ( uint32_t _device ) {
airtaudio : : DeviceInfo info ;
2014-03-11 21:46:00 +01:00
info . probed = false ;
// Get device ID
uint32_t nDevices = getDeviceCount ( ) ;
if ( nDevices = = 0 ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::getDeviceInfo: no devices found! " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
2014-03-12 23:55:49 +01:00
if ( _device > = nDevices ) {
2014-04-22 23:35:48 +02:00
ATA_ERROR ( " airtaudio::api::Core::getDeviceInfo: device ID is invalid! " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
AudioDeviceID deviceList [ nDevices ] ;
2014-03-12 23:55:49 +01:00
uint32_t dataSize = sizeof ( AudioDeviceID ) * nDevices ;
AudioObjectPropertyAddress property = {
kAudioHardwarePropertyDevices ,
kAudioObjectPropertyScopeGlobal ,
kAudioObjectPropertyElementMaster
} ;
OSStatus result = AudioObjectGetPropertyData ( kAudioObjectSystemObject ,
& property ,
0 ,
NULL ,
& dataSize ,
( void * ) & deviceList ) ;
2014-03-11 21:46:00 +01:00
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::getDeviceInfo: OS-X system error getting device IDs. " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
2014-03-12 23:55:49 +01:00
AudioDeviceID id = deviceList [ _device ] ;
2014-03-11 21:46:00 +01:00
// Get the device name.
info . name . erase ( ) ;
CFStringRef cfname ;
dataSize = sizeof ( CFStringRef ) ;
property . mSelector = kAudioObjectPropertyManufacturer ;
result = AudioObjectGetPropertyData ( id , & property , 0 , NULL , & dataSize , & cfname ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceInfo: system error ( " < < getErrorCode ( result ) < < " ) getting device manufacturer. " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
//const char *mname = CFStringGetCStringPtr(cfname, CFStringGetSystemEncoding());
int32_t length = CFStringGetLength ( cfname ) ;
char * mname = ( char * ) malloc ( length * 3 + 1 ) ;
CFStringGetCString ( cfname , mname , length * 3 + 1 , CFStringGetSystemEncoding ( ) ) ;
info . name . append ( ( const char * ) mname , strlen ( mname ) ) ;
info . name . append ( " : " ) ;
CFRelease ( cfname ) ;
free ( mname ) ;
property . mSelector = kAudioObjectPropertyName ;
result = AudioObjectGetPropertyData ( id , & property , 0 , NULL , & dataSize , & cfname ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceInfo: system error ( " < < getErrorCode ( result ) < < " ) getting device name. " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
//const char *name = CFStringGetCStringPtr(cfname, CFStringGetSystemEncoding());
length = CFStringGetLength ( cfname ) ;
char * name = ( char * ) malloc ( length * 3 + 1 ) ;
CFStringGetCString ( cfname , name , length * 3 + 1 , CFStringGetSystemEncoding ( ) ) ;
info . name . append ( ( const char * ) name , strlen ( name ) ) ;
CFRelease ( cfname ) ;
free ( name ) ;
// Get the output stream "configuration".
AudioBufferList * bufferList = nil ;
property . mSelector = kAudioDevicePropertyStreamConfiguration ;
property . mScope = kAudioDevicePropertyScopeOutput ;
// property.mElement = kAudioObjectPropertyElementWildcard;
dataSize = 0 ;
result = AudioObjectGetPropertyDataSize ( id , & property , 0 , NULL , & dataSize ) ;
if ( result ! = noErr | | dataSize = = 0 ) {
2014-04-22 23:35:48 +02:00
ATA_ERROR ( " airtaudio::api::Core::getDeviceInfo: system error ( " < < getErrorCode ( result ) < < " ) getting output stream configuration info for device ( " < < _device < < " ). " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
// Allocate the AudioBufferList.
bufferList = ( AudioBufferList * ) malloc ( dataSize ) ;
if ( bufferList = = NULL ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::getDeviceInfo: memory error allocating output AudioBufferList. " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
result = AudioObjectGetPropertyData ( id , & property , 0 , NULL , & dataSize , bufferList ) ;
2014-03-12 23:55:49 +01:00
if ( result ! = noErr
| | dataSize = = 0 ) {
2014-03-11 21:46:00 +01:00
free ( bufferList ) ;
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::getDeviceInfo: system error ( " < < getErrorCode ( result ) < < " ) getting output stream configuration for device ( " < < _device < < " ). " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
// Get output channel information.
uint32_t i , nStreams = bufferList - > mNumberBuffers ;
2014-03-12 23:55:49 +01:00
for ( i = 0 ; i < nStreams ; i + + ) {
2014-03-11 21:46:00 +01:00
info . outputChannels + = bufferList - > mBuffers [ i ] . mNumberChannels ;
2014-03-12 23:55:49 +01:00
}
2014-03-11 21:46:00 +01:00
free ( bufferList ) ;
// Get the input stream "configuration".
property . mScope = kAudioDevicePropertyScopeInput ;
result = AudioObjectGetPropertyDataSize ( id , & property , 0 , NULL , & dataSize ) ;
2014-03-12 23:55:49 +01:00
if ( result ! = noErr
| | dataSize = = 0 ) {
ATA_ERROR ( " airtaudio::api::Core::getDeviceInfo: system error ( " < < getErrorCode ( result ) < < " ) getting input stream configuration info for device ( " < < _device < < " ). " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
// Allocate the AudioBufferList.
bufferList = ( AudioBufferList * ) malloc ( dataSize ) ;
if ( bufferList = = NULL ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::getDeviceInfo: memory error allocating input AudioBufferList. " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
result = AudioObjectGetPropertyData ( id , & property , 0 , NULL , & dataSize , bufferList ) ;
if ( result ! = noErr | | dataSize = = 0 ) {
free ( bufferList ) ;
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::getDeviceInfo: system error ( " < < getErrorCode ( result ) < < " ) getting input stream configuration for device ( " < < _device < < " ). " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
// Get input channel information.
nStreams = bufferList - > mNumberBuffers ;
2014-03-12 23:55:49 +01:00
for ( i = 0 ; i < nStreams ; i + + ) {
2014-03-11 21:46:00 +01:00
info . inputChannels + = bufferList - > mBuffers [ i ] . mNumberChannels ;
2014-03-12 23:55:49 +01:00
}
2014-03-11 21:46:00 +01:00
free ( bufferList ) ;
// If device opens for both playback and capture, we determine the channels.
2014-03-12 23:55:49 +01:00
if ( info . outputChannels > 0
& & info . inputChannels > 0 ) {
2014-03-11 21:46:00 +01:00
info . duplexChannels = ( info . outputChannels > info . inputChannels ) ? info . inputChannels : info . outputChannels ;
2014-03-12 23:55:49 +01:00
}
2014-03-11 21:46:00 +01:00
// Probe the device sample rates.
bool isInput = false ;
2014-03-12 23:55:49 +01:00
if ( info . outputChannels = = 0 ) {
isInput = true ;
}
2014-03-11 21:46:00 +01:00
// Determine the supported sample rates.
property . mSelector = kAudioDevicePropertyAvailableNominalSampleRates ;
if ( isInput = = false ) property . mScope = kAudioDevicePropertyScopeOutput ;
result = AudioObjectGetPropertyDataSize ( id , & property , 0 , NULL , & dataSize ) ;
2014-03-12 23:55:49 +01:00
if ( result ! = kAudioHardwareNoError
| | dataSize = = 0 ) {
ATA_ERROR ( " airtaudio::api::Core::getDeviceInfo: system error ( " < < getErrorCode ( result ) < < " ) getting sample rate info. " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
2014-03-12 23:55:49 +01:00
uint32_t nRanges = dataSize / sizeof ( AudioValueRange ) ;
2014-03-11 21:46:00 +01:00
AudioValueRange rangeList [ nRanges ] ;
result = AudioObjectGetPropertyData ( id , & property , 0 , NULL , & dataSize , & rangeList ) ;
if ( result ! = kAudioHardwareNoError ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::getDeviceInfo: system error ( " < < getErrorCode ( result ) < < " ) getting sample rates. " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
2014-03-12 23:55:49 +01:00
double minimumRate = 100000000.0 , maximumRate = 0.0 ;
for ( uint32_t i = 0 ; i < nRanges ; i + + ) {
if ( rangeList [ i ] . mMinimum < minimumRate ) {
minimumRate = rangeList [ i ] . mMinimum ;
}
if ( rangeList [ i ] . mMaximum > maximumRate ) {
maximumRate = rangeList [ i ] . mMaximum ;
}
2014-03-11 21:46:00 +01:00
}
info . sampleRates . clear ( ) ;
for ( uint32_t k = 0 ; k < MAX_SAMPLE_RATES ; k + + ) {
2014-03-12 23:55:49 +01:00
if ( SAMPLE_RATES [ k ] > = ( uint32_t ) minimumRate & & SAMPLE_RATES [ k ] < = ( uint32_t ) maximumRate ) {
2014-03-11 21:46:00 +01:00
info . sampleRates . push_back ( SAMPLE_RATES [ k ] ) ;
2014-03-12 23:55:49 +01:00
}
2014-03-11 21:46:00 +01:00
}
if ( info . sampleRates . size ( ) = = 0 ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceInfo: No supported sample rates found for device ( " < < _device < < " ). " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
// CoreAudio always uses 32-bit floating point data for PCM streams.
// Thus, any other "physical" formats supported by the device are of
// no interest to the client.
2014-03-12 23:55:49 +01:00
info . nativeFormats = FLOAT32 ;
if ( info . outputChannels > 0 ) {
if ( getDefaultOutputDevice ( ) = = _device ) {
info . isDefaultOutput = true ;
}
}
if ( info . inputChannels > 0 ) {
if ( getDefaultInputDevice ( ) = = _device ) {
info . isDefaultInput = true ;
}
}
2014-03-11 21:46:00 +01:00
info . probed = true ;
return info ;
}
2014-03-12 23:55:49 +01:00
static OSStatus callbackHandler ( AudioDeviceID _inDevice ,
const AudioTimeStamp * _inNow ,
const AudioBufferList * _inInputData ,
const AudioTimeStamp * _inInputTime ,
AudioBufferList * _outOutputData ,
const AudioTimeStamp * _inOutputTime ,
void * _infoPointer ) {
2014-04-22 23:35:48 +02:00
airtaudio : : CallbackInfo * info = ( airtaudio : : CallbackInfo * ) _infoPointer ;
airtaudio : : api : : Core * object = ( airtaudio : : api : : Core * ) info - > object ;
2014-03-12 23:55:49 +01:00
if ( object - > callbackEvent ( _inDevice , _inInputData , _outOutputData ) = = false ) {
2014-03-11 21:46:00 +01:00
return kAudioHardwareUnspecifiedError ;
2014-03-12 23:55:49 +01:00
} else {
2014-03-11 21:46:00 +01:00
return kAudioHardwareNoError ;
2014-03-12 23:55:49 +01:00
}
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
static OSStatus xrunListener ( AudioObjectID _inDevice ,
uint32_t _nAddresses ,
const AudioObjectPropertyAddress _properties [ ] ,
void * _handlePointer ) {
CoreHandle * handle = ( CoreHandle * ) _handlePointer ;
for ( uint32_t i = 0 ; i < _nAddresses ; i + + ) {
if ( _properties [ i ] . mSelector = = kAudioDeviceProcessorOverload ) {
if ( _properties [ i ] . mScope = = kAudioDevicePropertyScopeInput ) {
2014-03-11 21:46:00 +01:00
handle - > xrun [ 1 ] = true ;
2014-03-12 23:55:49 +01:00
} else {
2014-03-11 21:46:00 +01:00
handle - > xrun [ 0 ] = true ;
2014-03-12 23:55:49 +01:00
}
2014-03-11 21:46:00 +01:00
}
}
return kAudioHardwareNoError ;
}
2014-03-12 23:55:49 +01:00
static OSStatus rateListener ( AudioObjectID _inDevice ,
uint32_t _nAddresses ,
const AudioObjectPropertyAddress _properties [ ] ,
void * _ratePointer ) {
double * rate = ( double * ) _ratePointer ;
uint32_t dataSize = sizeof ( double ) ;
AudioObjectPropertyAddress property = {
kAudioDevicePropertyNominalSampleRate ,
kAudioObjectPropertyScopeGlobal ,
kAudioObjectPropertyElementMaster
} ;
AudioObjectGetPropertyData ( _inDevice , & property , 0 , NULL , & dataSize , rate ) ;
2014-03-11 21:46:00 +01:00
return kAudioHardwareNoError ;
}
2014-03-12 23:55:49 +01:00
bool airtaudio : : api : : Core : : probeDeviceOpen ( uint32_t _device ,
airtaudio : : api : : StreamMode _mode ,
uint32_t _channels ,
uint32_t _firstChannel ,
uint32_t _sampleRate ,
airtaudio : : format _format ,
uint32_t * _bufferSize ,
airtaudio : : StreamOptions * _options ) {
2014-03-11 21:46:00 +01:00
// Get device ID
uint32_t nDevices = getDeviceCount ( ) ;
if ( nDevices = = 0 ) {
// This should not happen because a check is made before this function is called.
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: no devices found! " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
if ( _device > = nDevices ) {
2014-03-11 21:46:00 +01:00
// This should not happen because a check is made before this function is called.
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: device ID is invalid! " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
AudioDeviceID deviceList [ nDevices ] ;
2014-03-12 23:55:49 +01:00
uint32_t dataSize = sizeof ( AudioDeviceID ) * nDevices ;
AudioObjectPropertyAddress property = {
kAudioHardwarePropertyDevices ,
kAudioObjectPropertyScopeGlobal ,
kAudioObjectPropertyElementMaster
} ;
OSStatus result = AudioObjectGetPropertyData ( kAudioObjectSystemObject ,
& property ,
0 ,
NULL ,
& dataSize ,
( void * ) & deviceList ) ;
2014-03-11 21:46:00 +01:00
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: OS-X system error getting device IDs. " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
AudioDeviceID id = deviceList [ _device ] ;
2014-03-11 21:46:00 +01:00
// Setup for stream mode.
bool isInput = false ;
2014-03-12 23:55:49 +01:00
if ( _mode = = INPUT ) {
2014-03-11 21:46:00 +01:00
isInput = true ;
property . mScope = kAudioDevicePropertyScopeInput ;
2014-03-12 23:55:49 +01:00
} else {
2014-03-11 21:46:00 +01:00
property . mScope = kAudioDevicePropertyScopeOutput ;
2014-03-12 23:55:49 +01:00
}
2014-03-11 21:46:00 +01:00
// Get the stream "configuration".
AudioBufferList * bufferList = nil ;
dataSize = 0 ;
property . mSelector = kAudioDevicePropertyStreamConfiguration ;
result = AudioObjectGetPropertyDataSize ( id , & property , 0 , NULL , & dataSize ) ;
2014-03-12 23:55:49 +01:00
if ( result ! = noErr
| | dataSize = = 0 ) {
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) getting stream configuration info for device ( " < < _device < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
// Allocate the AudioBufferList.
bufferList = ( AudioBufferList * ) malloc ( dataSize ) ;
if ( bufferList = = NULL ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: memory error allocating AudioBufferList. " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
result = AudioObjectGetPropertyData ( id , & property , 0 , NULL , & dataSize , bufferList ) ;
2014-03-12 23:55:49 +01:00
if ( result ! = noErr
| | dataSize = = 0 ) {
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) getting stream configuration for device ( " < < _device < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
// Search for one or more streams that contain the desired number of
// channels. CoreAudio devices can have an arbitrary number of
// streams and each stream can have an arbitrary number of channels.
// For each stream, a single buffer of interleaved samples is
// provided. RtAudio prefers the use of one stream of interleaved
// data or multiple consecutive single-channel streams. However, we
// now support multiple consecutive multi-channel streams of
// interleaved data as well.
2014-03-12 23:55:49 +01:00
uint32_t iStream , offsetCounter = _firstChannel ;
uint32_t nStreams = bufferList - > mNumberBuffers ;
2014-03-11 21:46:00 +01:00
bool monoMode = false ;
bool foundStream = false ;
// First check that the device supports the requested number of
// channels.
2014-03-12 23:55:49 +01:00
uint32_t deviceChannels = 0 ;
for ( iStream = 0 ; iStream < nStreams ; iStream + + ) {
2014-03-11 21:46:00 +01:00
deviceChannels + = bufferList - > mBuffers [ iStream ] . mNumberChannels ;
2014-03-12 23:55:49 +01:00
}
if ( deviceChannels < ( _channels + _firstChannel ) ) {
2014-03-11 21:46:00 +01:00
free ( bufferList ) ;
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: the device ( " < < _device < < " ) does not support the requested channel count. " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
// Look for a single stream meeting our needs.
2014-03-12 23:55:49 +01:00
uint32_t firstStream , streamCount = 1 , streamChannels = 0 , channelOffset = 0 ;
2014-03-11 21:46:00 +01:00
for ( iStream = 0 ; iStream < nStreams ; iStream + + ) {
streamChannels = bufferList - > mBuffers [ iStream ] . mNumberChannels ;
2014-03-12 23:55:49 +01:00
if ( streamChannels > = _channels + offsetCounter ) {
2014-03-11 21:46:00 +01:00
firstStream = iStream ;
channelOffset = offsetCounter ;
foundStream = true ;
break ;
}
2014-03-12 23:55:49 +01:00
if ( streamChannels > offsetCounter ) {
break ;
}
2014-03-11 21:46:00 +01:00
offsetCounter - = streamChannels ;
}
// If we didn't find a single stream above, then we should be able
// to meet the channel specification with multiple streams.
if ( foundStream = = false ) {
monoMode = true ;
2014-03-12 23:55:49 +01:00
offsetCounter = _firstChannel ;
2014-03-11 21:46:00 +01:00
for ( iStream = 0 ; iStream < nStreams ; iStream + + ) {
streamChannels = bufferList - > mBuffers [ iStream ] . mNumberChannels ;
2014-03-12 23:55:49 +01:00
if ( streamChannels > offsetCounter ) {
break ;
}
2014-03-11 21:46:00 +01:00
offsetCounter - = streamChannels ;
}
firstStream = iStream ;
channelOffset = offsetCounter ;
2014-04-22 23:35:48 +02:00
int32_t channelCounter = _channels + offsetCounter - streamChannels ;
2014-03-12 23:55:49 +01:00
if ( streamChannels > 1 ) {
monoMode = false ;
}
2014-03-11 21:46:00 +01:00
while ( channelCounter > 0 ) {
streamChannels = bufferList - > mBuffers [ + + iStream ] . mNumberChannels ;
2014-03-12 23:55:49 +01:00
if ( streamChannels > 1 ) {
monoMode = false ;
}
2014-03-11 21:46:00 +01:00
channelCounter - = streamChannels ;
streamCount + + ;
}
}
free ( bufferList ) ;
// Determine the buffer size.
AudioValueRange bufferRange ;
dataSize = sizeof ( AudioValueRange ) ;
property . mSelector = kAudioDevicePropertyBufferFrameSizeRange ;
result = AudioObjectGetPropertyData ( id , & property , 0 , NULL , & dataSize , & bufferRange ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) getting buffer size range for device ( " < < _device < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
if ( bufferRange . mMinimum > * _bufferSize ) {
* _bufferSize = ( uint64_t ) bufferRange . mMinimum ;
} else if ( bufferRange . mMaximum < * _bufferSize ) {
* _bufferSize = ( uint64_t ) bufferRange . mMaximum ;
}
if ( _options ! = NULL
2014-04-22 23:35:48 +02:00
& & _options - > flags & MINIMIZE_LATENCY ) {
2014-03-12 23:55:49 +01:00
* _bufferSize = ( uint64_t ) bufferRange . mMinimum ;
}
2014-03-11 21:46:00 +01:00
// Set the buffer size. For multiple streams, I'm assuming we only
// need to make this setting for the master channel.
2014-03-12 23:55:49 +01:00
uint32_t theSize = ( uint32_t ) * _bufferSize ;
dataSize = sizeof ( uint32_t ) ;
2014-03-11 21:46:00 +01:00
property . mSelector = kAudioDevicePropertyBufferFrameSize ;
result = AudioObjectSetPropertyData ( id , & property , 0 , NULL , dataSize , & theSize ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) setting the buffer size for device ( " < < _device < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
// If attempting to setup a duplex stream, the bufferSize parameter
// MUST be the same in both directions!
2014-03-12 23:55:49 +01:00
* _bufferSize = theSize ;
if ( m_stream . mode = = OUTPUT
& & _mode = = INPUT
& & * _bufferSize ! = m_stream . bufferSize ) {
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error setting buffer size for duplex stream on device ( " < < _device < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
m_stream . bufferSize = * _bufferSize ;
2014-03-11 21:46:00 +01:00
m_stream . nBuffers = 1 ;
// Try to set "hog" mode ... it's not clear to me this is working.
2014-03-12 23:55:49 +01:00
if ( _options ! = NULL
2014-04-22 23:35:48 +02:00
& & _options - > flags & HOG_DEVICE ) {
2014-03-11 21:46:00 +01:00
pid_t hog_pid ;
dataSize = sizeof ( hog_pid ) ;
property . mSelector = kAudioDevicePropertyHogMode ;
result = AudioObjectGetPropertyData ( id , & property , 0 , NULL , & dataSize , & hog_pid ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) getting 'hog' state! " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
if ( hog_pid ! = getpid ( ) ) {
hog_pid = getpid ( ) ;
result = AudioObjectSetPropertyData ( id , & property , 0 , NULL , dataSize , & hog_pid ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) setting 'hog' state! " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
}
}
// Check and if necessary, change the sample rate for the device.
2014-03-12 23:55:49 +01:00
double nominalRate ;
dataSize = sizeof ( double ) ;
2014-03-11 21:46:00 +01:00
property . mSelector = kAudioDevicePropertyNominalSampleRate ;
result = AudioObjectGetPropertyData ( id , & property , 0 , NULL , & dataSize , & nominalRate ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) getting current sample rate. " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
// Only change the sample rate if off by more than 1 Hz.
2014-03-12 23:55:49 +01:00
if ( fabs ( nominalRate - ( double ) _sampleRate ) > 1.0 ) {
2014-03-11 21:46:00 +01:00
// Set a property listener for the sample rate change
2014-03-12 23:55:49 +01:00
double reportedRate = 0.0 ;
2014-03-11 21:46:00 +01:00
AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate , kAudioObjectPropertyScopeGlobal , kAudioObjectPropertyElementMaster } ;
result = AudioObjectAddPropertyListener ( id , & tmp , rateListener , ( void * ) & reportedRate ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) setting sample rate property listener for device ( " < < _device < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
nominalRate = ( double ) _sampleRate ;
2014-03-11 21:46:00 +01:00
result = AudioObjectSetPropertyData ( id , & property , 0 , NULL , dataSize , & nominalRate ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) setting sample rate for device ( " < < _device < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
// Now wait until the reported nominal rate is what we just set.
2014-03-12 23:55:49 +01:00
uint32_t microCounter = 0 ;
2014-03-11 21:46:00 +01:00
while ( reportedRate ! = nominalRate ) {
microCounter + = 5000 ;
2014-03-12 23:55:49 +01:00
if ( microCounter > 5000000 ) {
break ;
}
2014-03-11 21:46:00 +01:00
usleep ( 5000 ) ;
}
// Remove the property listener.
AudioObjectRemovePropertyListener ( id , & tmp , rateListener , ( void * ) & reportedRate ) ;
if ( microCounter > 5000000 ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: timeout waiting for sample rate update for device ( " < < _device < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
}
// Now set the stream format for all streams. Also, check the
// physical format of the device and change that if necessary.
AudioStreamBasicDescription description ;
dataSize = sizeof ( AudioStreamBasicDescription ) ;
property . mSelector = kAudioStreamPropertyVirtualFormat ;
result = AudioObjectGetPropertyData ( id , & property , 0 , NULL , & dataSize , & description ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) getting stream format for device ( " < < _device < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
// Set the sample rate and data format id. However, only make the
// change if the sample rate is not within 1.0 of the desired
// rate and the format is not linear pcm.
bool updateFormat = false ;
2014-03-12 23:55:49 +01:00
if ( fabs ( description . mSampleRate - ( double ) _sampleRate ) > 1.0 ) {
description . mSampleRate = ( double ) _sampleRate ;
2014-03-11 21:46:00 +01:00
updateFormat = true ;
}
if ( description . mFormatID ! = kAudioFormatLinearPCM ) {
description . mFormatID = kAudioFormatLinearPCM ;
updateFormat = true ;
}
if ( updateFormat ) {
result = AudioObjectSetPropertyData ( id , & property , 0 , NULL , dataSize , & description ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) setting sample rate or data format for device ( " < < _device < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
}
// Now check the physical format.
property . mSelector = kAudioStreamPropertyPhysicalFormat ;
result = AudioObjectGetPropertyData ( id , & property , 0 , NULL , & dataSize , & description ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) getting stream physical format for device ( " < < _device < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
//std::cout << "Current physical stream format:" << std::endl;
//std::cout << " mBitsPerChan = " << description.mBitsPerChannel << std::endl;
//std::cout << " aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;
//std::cout << " bytesPerFrame = " << description.mBytesPerFrame << std::endl;
//std::cout << " sample rate = " << description.mSampleRate << std::endl;
2014-03-12 23:55:49 +01:00
if ( description . mFormatID ! = kAudioFormatLinearPCM
| | description . mBitsPerChannel < 16 ) {
2014-03-11 21:46:00 +01:00
description . mFormatID = kAudioFormatLinearPCM ;
2014-03-12 23:55:49 +01:00
//description.mSampleRate = (double) sampleRate;
2014-03-11 21:46:00 +01:00
AudioStreamBasicDescription testDescription = description ;
2014-03-12 23:55:49 +01:00
uint32_t formatFlags ;
2014-03-11 21:46:00 +01:00
// We'll try higher bit rates first and then work our way down.
2014-03-12 23:55:49 +01:00
std : : vector < std : : pair < uint32_t , uint32_t > > physicalFormats ;
2014-03-11 21:46:00 +01:00
formatFlags = ( description . mFormatFlags | kLinearPCMFormatFlagIsFloat ) & ~ kLinearPCMFormatFlagIsSignedInteger ;
2014-03-12 23:55:49 +01:00
physicalFormats . push_back ( std : : pair < float , uint32_t > ( 32 , formatFlags ) ) ;
2014-03-11 21:46:00 +01:00
formatFlags = ( description . mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked ) & ~ kLinearPCMFormatFlagIsFloat ;
2014-03-12 23:55:49 +01:00
physicalFormats . push_back ( std : : pair < float , uint32_t > ( 32 , formatFlags ) ) ;
physicalFormats . push_back ( std : : pair < float , uint32_t > ( 24 , formatFlags ) ) ; // 24-bit packed
2014-03-11 21:46:00 +01:00
formatFlags & = ~ ( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh ) ;
2014-03-12 23:55:49 +01:00
physicalFormats . push_back ( std : : pair < float , uint32_t > ( 24.2 , formatFlags ) ) ; // 24-bit in 4 bytes, aligned low
2014-03-11 21:46:00 +01:00
formatFlags | = kAudioFormatFlagIsAlignedHigh ;
2014-03-12 23:55:49 +01:00
physicalFormats . push_back ( std : : pair < float , uint32_t > ( 24.4 , formatFlags ) ) ; // 24-bit in 4 bytes, aligned high
2014-03-11 21:46:00 +01:00
formatFlags = ( description . mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked ) & ~ kLinearPCMFormatFlagIsFloat ;
2014-03-12 23:55:49 +01:00
physicalFormats . push_back ( std : : pair < float , uint32_t > ( 16 , formatFlags ) ) ;
physicalFormats . push_back ( std : : pair < float , uint32_t > ( 8 , formatFlags ) ) ;
2014-03-11 21:46:00 +01:00
bool setPhysicalFormat = false ;
for ( uint32_t i = 0 ; i < physicalFormats . size ( ) ; i + + ) {
testDescription = description ;
2014-03-12 23:55:49 +01:00
testDescription . mBitsPerChannel = ( uint32_t ) physicalFormats [ i ] . first ;
2014-03-11 21:46:00 +01:00
testDescription . mFormatFlags = physicalFormats [ i ] . second ;
2014-03-12 23:55:49 +01:00
if ( ( 24 = = ( uint32_t ) physicalFormats [ i ] . first )
& & ~ ( physicalFormats [ i ] . second & kAudioFormatFlagIsPacked ) ) {
2014-03-11 21:46:00 +01:00
testDescription . mBytesPerFrame = 4 * testDescription . mChannelsPerFrame ;
2014-03-12 23:55:49 +01:00
} else {
2014-03-11 21:46:00 +01:00
testDescription . mBytesPerFrame = testDescription . mBitsPerChannel / 8 * testDescription . mChannelsPerFrame ;
2014-03-12 23:55:49 +01:00
}
2014-03-11 21:46:00 +01:00
testDescription . mBytesPerPacket = testDescription . mBytesPerFrame * testDescription . mFramesPerPacket ;
result = AudioObjectSetPropertyData ( id , & property , 0 , NULL , dataSize , & testDescription ) ;
if ( result = = noErr ) {
setPhysicalFormat = true ;
//std::cout << "Updated physical stream format:" << std::endl;
//std::cout << " mBitsPerChan = " << testDescription.mBitsPerChannel << std::endl;
//std::cout << " aligned high = " << (testDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (testDescription.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;
//std::cout << " bytesPerFrame = " << testDescription.mBytesPerFrame << std::endl;
//std::cout << " sample rate = " << testDescription.mSampleRate << std::endl;
break ;
}
}
if ( ! setPhysicalFormat ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) setting physical data format for device ( " < < _device < < " ). " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
} // done setting virtual/physical formats.
// Get the stream / device latency.
2014-03-12 23:55:49 +01:00
uint32_t latency ;
dataSize = sizeof ( uint32_t ) ;
2014-03-11 21:46:00 +01:00
property . mSelector = kAudioDevicePropertyLatency ;
if ( AudioObjectHasProperty ( id , & property ) = = true ) {
result = AudioObjectGetPropertyData ( id , & property , 0 , NULL , & dataSize , & latency ) ;
2014-03-12 23:55:49 +01:00
if ( result = = kAudioHardwareNoError ) {
m_stream . latency [ _mode ] = latency ;
} else {
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error ( " < < getErrorCode ( result ) < < " ) getting device latency for device ( " < < _device < < " ). " ) ;
2014-04-22 23:35:48 +02:00
return false ;
2014-03-11 21:46:00 +01:00
}
}
// Byte-swapping: According to AudioHardware.h, the stream data will
// always be presented in native-endian format, so we should never
// need to byte swap.
2014-03-12 23:55:49 +01:00
m_stream . doByteSwap [ _mode ] = false ;
2014-03-11 21:46:00 +01:00
// From the CoreAudio documentation, PCM data must be supplied as
// 32-bit floats.
2014-03-12 23:55:49 +01:00
m_stream . userFormat = _format ;
m_stream . deviceFormat [ _mode ] = FLOAT32 ;
if ( streamCount = = 1 ) {
m_stream . nDeviceChannels [ _mode ] = description . mChannelsPerFrame ;
} else {
// multiple streams
m_stream . nDeviceChannels [ _mode ] = _channels ;
}
m_stream . nUserChannels [ _mode ] = _channels ;
m_stream . channelOffset [ _mode ] = channelOffset ; // offset within a CoreAudio stream
if ( _options ! = NULL
2014-04-22 23:35:48 +02:00
& & _options - > flags & NONINTERLEAVED ) {
2014-03-12 23:55:49 +01:00
m_stream . userInterleaved = false ;
} else {
m_stream . userInterleaved = true ;
}
m_stream . deviceInterleaved [ _mode ] = true ;
if ( monoMode = = true ) {
m_stream . deviceInterleaved [ _mode ] = false ;
}
2014-03-11 21:46:00 +01:00
// Set flags for buffer conversion.
2014-03-12 23:55:49 +01:00
m_stream . doConvertBuffer [ _mode ] = false ;
if ( m_stream . userFormat ! = m_stream . deviceFormat [ _mode ] ) {
m_stream . doConvertBuffer [ _mode ] = true ;
}
if ( m_stream . nUserChannels [ _mode ] < m_stream . nDeviceChannels [ _mode ] ) {
m_stream . doConvertBuffer [ _mode ] = true ;
}
2014-03-11 21:46:00 +01:00
if ( streamCount = = 1 ) {
2014-03-12 23:55:49 +01:00
if ( m_stream . nUserChannels [ _mode ] > 1
& & m_stream . userInterleaved ! = m_stream . deviceInterleaved [ _mode ] ) {
m_stream . doConvertBuffer [ _mode ] = true ;
}
} else if ( monoMode & & m_stream . userInterleaved ) {
m_stream . doConvertBuffer [ _mode ] = true ;
2014-03-11 21:46:00 +01:00
}
// Allocate our CoreHandle structure for the stream.
CoreHandle * handle = 0 ;
if ( m_stream . apiHandle = = 0 ) {
2014-03-12 23:55:49 +01:00
handle = new CoreHandle ;
2014-04-22 23:35:48 +02:00
if ( handle = = NULL ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: error allocating CoreHandle memory. " ) ;
2014-04-22 23:35:48 +02:00
return false ;
2014-03-11 21:46:00 +01:00
}
m_stream . apiHandle = ( void * ) handle ;
2014-03-12 23:55:49 +01:00
} else {
2014-03-11 21:46:00 +01:00
handle = ( CoreHandle * ) m_stream . apiHandle ;
2014-03-12 23:55:49 +01:00
}
handle - > iStream [ _mode ] = firstStream ;
handle - > nStreams [ _mode ] = streamCount ;
handle - > id [ _mode ] = id ;
2014-03-11 21:46:00 +01:00
// Allocate necessary internal buffers.
uint64_t bufferBytes ;
2014-03-12 23:55:49 +01:00
bufferBytes = m_stream . nUserChannels [ _mode ] * * _bufferSize * formatBytes ( m_stream . userFormat ) ;
// m_stream.userBuffer[_mode] = (char *) calloc(bufferBytes, 1);
m_stream . userBuffer [ _mode ] = ( char * ) malloc ( bufferBytes * sizeof ( char ) ) ;
memset ( m_stream . userBuffer [ _mode ] , 0 , bufferBytes * sizeof ( char ) ) ;
if ( m_stream . userBuffer [ _mode ] = = NULL ) {
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: error allocating user buffer memory. " ) ;
2014-03-11 21:46:00 +01:00
goto error ;
}
// If possible, we will make use of the CoreAudio stream buffers as
// "device buffers". However, we can't do this if using multiple
// streams.
2014-03-12 23:55:49 +01:00
if ( m_stream . doConvertBuffer [ _mode ]
& & handle - > nStreams [ _mode ] > 1 ) {
2014-03-11 21:46:00 +01:00
bool makeBuffer = true ;
2014-03-12 23:55:49 +01:00
bufferBytes = m_stream . nDeviceChannels [ _mode ] * formatBytes ( m_stream . deviceFormat [ _mode ] ) ;
if ( _mode = = INPUT ) {
if ( m_stream . mode = = OUTPUT
& & m_stream . deviceBuffer ) {
2014-03-11 21:46:00 +01:00
uint64_t bytesOut = m_stream . nDeviceChannels [ 0 ] * formatBytes ( m_stream . deviceFormat [ 0 ] ) ;
2014-03-12 23:55:49 +01:00
if ( bufferBytes < = bytesOut ) {
makeBuffer = false ;
}
2014-03-11 21:46:00 +01:00
}
}
if ( makeBuffer ) {
2014-03-12 23:55:49 +01:00
bufferBytes * = * _bufferSize ;
if ( m_stream . deviceBuffer ) {
free ( m_stream . deviceBuffer ) ;
m_stream . deviceBuffer = NULL ;
}
2014-03-11 21:46:00 +01:00
m_stream . deviceBuffer = ( char * ) calloc ( bufferBytes , 1 ) ;
if ( m_stream . deviceBuffer = = NULL ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: error allocating device buffer memory. " ) ;
2014-03-11 21:46:00 +01:00
goto error ;
}
}
}
2014-03-12 23:55:49 +01:00
m_stream . sampleRate = _sampleRate ;
m_stream . device [ _mode ] = _device ;
2014-03-11 21:46:00 +01:00
m_stream . state = STREAM_STOPPED ;
m_stream . callbackInfo . object = ( void * ) this ;
// Setup the buffer conversion information structure.
2014-03-12 23:55:49 +01:00
if ( m_stream . doConvertBuffer [ _mode ] ) {
if ( streamCount > 1 ) {
setConvertInfo ( _mode , 0 ) ;
} else {
setConvertInfo ( _mode , channelOffset ) ;
}
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
if ( _mode = = INPUT
& & m_stream . mode = = OUTPUT
& & m_stream . device [ 0 ] = = _device ) {
2014-03-11 21:46:00 +01:00
// Only one callback procedure per device.
m_stream . mode = DUPLEX ;
2014-03-12 23:55:49 +01:00
} else {
2014-03-11 21:46:00 +01:00
# if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
2014-03-12 23:55:49 +01:00
result = AudioDeviceCreateIOProcID ( id , callbackHandler , ( void * ) & m_stream . callbackInfo , & handle - > procId [ _mode ] ) ;
2014-03-11 21:46:00 +01:00
# else
// deprecated in favor of AudioDeviceCreateIOProcID()
result = AudioDeviceAddIOProc ( id , callbackHandler , ( void * ) & m_stream . callbackInfo ) ;
# endif
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::probeDeviceOpen: system error setting callback for device ( " < < _device < < " ). " ) ;
2014-03-11 21:46:00 +01:00
goto error ;
}
2014-03-12 23:55:49 +01:00
if ( m_stream . mode = = OUTPUT
& & _mode = = INPUT ) {
2014-03-11 21:46:00 +01:00
m_stream . mode = DUPLEX ;
2014-03-12 23:55:49 +01:00
} else {
m_stream . mode = _mode ;
}
2014-03-11 21:46:00 +01:00
}
// Setup the device property listener for over/underload.
property . mSelector = kAudioDeviceProcessorOverload ;
result = AudioObjectAddPropertyListener ( id , & property , xrunListener , ( void * ) handle ) ;
2014-03-13 21:16:30 +01:00
return true ;
2014-03-12 23:55:49 +01:00
error :
2014-03-11 21:46:00 +01:00
if ( handle ) {
delete handle ;
m_stream . apiHandle = 0 ;
}
for ( int32_t i = 0 ; i < 2 ; i + + ) {
if ( m_stream . userBuffer [ i ] ) {
free ( m_stream . userBuffer [ i ] ) ;
m_stream . userBuffer [ i ] = 0 ;
}
}
if ( m_stream . deviceBuffer ) {
free ( m_stream . deviceBuffer ) ;
m_stream . deviceBuffer = 0 ;
}
m_stream . state = STREAM_CLOSED ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
enum airtaudio : : errorType airtaudio : : api : : Core : : closeStream ( void ) {
2014-03-11 21:46:00 +01:00
if ( m_stream . state = = STREAM_CLOSED ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::closeStream(): no open stream to close! " ) ;
return airtaudio : : errorWarning ;
2014-03-11 21:46:00 +01:00
}
CoreHandle * handle = ( CoreHandle * ) m_stream . apiHandle ;
2014-03-12 23:55:49 +01:00
if ( m_stream . mode = = OUTPUT
| | m_stream . mode = = DUPLEX ) {
if ( m_stream . state = = STREAM_RUNNING ) {
2014-03-11 21:46:00 +01:00
AudioDeviceStop ( handle - > id [ 0 ] , callbackHandler ) ;
2014-03-12 23:55:49 +01:00
}
2014-03-11 21:46:00 +01:00
# if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
AudioDeviceDestroyIOProcID ( handle - > id [ 0 ] , handle - > procId [ 0 ] ) ;
# else
// deprecated in favor of AudioDeviceDestroyIOProcID()
AudioDeviceRemoveIOProc ( handle - > id [ 0 ] , callbackHandler ) ;
# endif
}
2014-03-12 23:55:49 +01:00
if ( m_stream . mode = = INPUT
| | ( m_stream . mode = = DUPLEX
& & m_stream . device [ 0 ] ! = m_stream . device [ 1 ] ) ) {
if ( m_stream . state = = STREAM_RUNNING ) {
2014-03-11 21:46:00 +01:00
AudioDeviceStop ( handle - > id [ 1 ] , callbackHandler ) ;
2014-03-12 23:55:49 +01:00
}
2014-03-11 21:46:00 +01:00
# if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
AudioDeviceDestroyIOProcID ( handle - > id [ 1 ] , handle - > procId [ 1 ] ) ;
# else
// deprecated in favor of AudioDeviceDestroyIOProcID()
AudioDeviceRemoveIOProc ( handle - > id [ 1 ] , callbackHandler ) ;
# endif
}
for ( int32_t i = 0 ; i < 2 ; i + + ) {
if ( m_stream . userBuffer [ i ] ) {
free ( m_stream . userBuffer [ i ] ) ;
2014-03-12 23:55:49 +01:00
m_stream . userBuffer [ i ] = NULL ;
2014-03-11 21:46:00 +01:00
}
}
if ( m_stream . deviceBuffer ) {
free ( m_stream . deviceBuffer ) ;
2014-03-12 23:55:49 +01:00
m_stream . deviceBuffer = NULL ;
2014-03-11 21:46:00 +01:00
}
delete handle ;
m_stream . apiHandle = 0 ;
m_stream . mode = UNINITIALIZED ;
m_stream . state = STREAM_CLOSED ;
2014-03-12 23:55:49 +01:00
return airtaudio : : errorNone ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
enum airtaudio : : errorType airtaudio : : api : : Core : : startStream ( void ) {
if ( verifyStream ( ) ! = airtaudio : : errorNone ) {
return airtaudio : : errorFail ;
}
2014-03-11 21:46:00 +01:00
if ( m_stream . state = = STREAM_RUNNING ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::startStream(): the stream is already running! " ) ;
return airtaudio : : errorWarning ;
2014-03-11 21:46:00 +01:00
}
OSStatus result = noErr ;
CoreHandle * handle = ( CoreHandle * ) m_stream . apiHandle ;
2014-03-12 23:55:49 +01:00
if ( m_stream . mode = = OUTPUT
| | m_stream . mode = = DUPLEX ) {
2014-03-11 21:46:00 +01:00
result = AudioDeviceStart ( handle - > id [ 0 ] , callbackHandler ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::startStream: system error ( " < < getErrorCode ( result ) < < " ) starting callback procedure on device ( " < < m_stream . device [ 0 ] < < " ). " ) ;
2014-03-11 21:46:00 +01:00
goto unlock ;
}
}
2014-03-12 23:55:49 +01:00
if ( m_stream . mode = = INPUT
| | ( m_stream . mode = = DUPLEX
& & m_stream . device [ 0 ] ! = m_stream . device [ 1 ] ) ) {
2014-03-11 21:46:00 +01:00
result = AudioDeviceStart ( handle - > id [ 1 ] , callbackHandler ) ;
if ( result ! = noErr ) {
2014-04-22 23:35:48 +02:00
ATA_ERROR ( " airtaudio::api::Core::startStream: system error starting input callback procedure on device ( " < < m_stream . device [ 1 ] < < " ). " ) ;
2014-03-11 21:46:00 +01:00
goto unlock ;
}
}
handle - > drainCounter = 0 ;
handle - > internalDrain = false ;
m_stream . state = STREAM_RUNNING ;
2014-03-12 23:55:49 +01:00
unlock :
if ( result = = noErr ) {
return airtaudio : : errorNone ;
}
return airtaudio : : errorSystemError ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
enum airtaudio : : errorType airtaudio : : api : : Core : : stopStream ( void ) {
if ( verifyStream ( ) ! = airtaudio : : errorNone ) {
return airtaudio : : errorFail ;
}
2014-03-11 21:46:00 +01:00
if ( m_stream . state = = STREAM_STOPPED ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::stopStream(): the stream is already stopped! " ) ;
return airtaudio : : errorWarning ;
2014-03-11 21:46:00 +01:00
}
OSStatus result = noErr ;
CoreHandle * handle = ( CoreHandle * ) m_stream . apiHandle ;
2014-03-12 23:55:49 +01:00
if ( m_stream . mode = = OUTPUT
| | m_stream . mode = = DUPLEX ) {
2014-03-11 21:46:00 +01:00
if ( handle - > drainCounter = = 0 ) {
2014-03-12 23:55:49 +01:00
std : : unique_lock < std : : mutex > lck ( m_stream . mutex ) ;
2014-03-11 21:46:00 +01:00
handle - > drainCounter = 2 ;
2014-03-12 23:55:49 +01:00
handle - > condition . wait ( lck ) ;
2014-03-11 21:46:00 +01:00
}
result = AudioDeviceStop ( handle - > id [ 0 ] , callbackHandler ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::stopStream: system error ( " < < getErrorCode ( result ) < < " ) stopping callback procedure on device ( " < < m_stream . device [ 0 ] < < " ). " ) ;
2014-03-11 21:46:00 +01:00
goto unlock ;
}
}
2014-03-12 23:55:49 +01:00
if ( m_stream . mode = = INPUT
| | ( m_stream . mode = = DUPLEX
& & m_stream . device [ 0 ] ! = m_stream . device [ 1 ] ) ) {
2014-03-11 21:46:00 +01:00
result = AudioDeviceStop ( handle - > id [ 1 ] , callbackHandler ) ;
if ( result ! = noErr ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::stopStream: system error ( " < < getErrorCode ( result ) < < " ) stopping input callback procedure on device ( " < < m_stream . device [ 1 ] < < " ). " ) ;
2014-03-11 21:46:00 +01:00
goto unlock ;
}
}
m_stream . state = STREAM_STOPPED ;
2014-03-12 23:55:49 +01:00
unlock :
if ( result = = noErr ) {
return airtaudio : : errorNone ;
}
return airtaudio : : errorSystemError ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
enum airtaudio : : errorType airtaudio : : api : : Core : : abortStream ( void ) {
if ( verifyStream ( ) ! = airtaudio : : errorNone ) {
return airtaudio : : errorFail ;
}
2014-03-11 21:46:00 +01:00
if ( m_stream . state = = STREAM_STOPPED ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::abortStream(): the stream is already stopped! " ) ;
return airtaudio : : errorWarning ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
CoreHandle * handle = ( CoreHandle * ) m_stream . apiHandle ;
2014-03-11 21:46:00 +01:00
handle - > drainCounter = 2 ;
2014-03-12 23:55:49 +01:00
return stopStream ( ) ;
2014-03-11 21:46:00 +01:00
}
// This function will be called by a spawned thread when the user
// callback function signals that the stream should be stopped or
// aborted. It is better to handle it this way because the
// callbackEvent() function probably should return before the AudioDeviceStop()
// function is called.
2014-03-12 23:55:49 +01:00
static void coreStopStream ( void * _ptr ) {
2014-04-22 23:35:48 +02:00
airtaudio : : CallbackInfo * info = ( airtaudio : : CallbackInfo * ) _ptr ;
airtaudio : : api : : Core * object = ( airtaudio : : api : : Core * ) info - > object ;
2014-03-11 21:46:00 +01:00
object - > stopStream ( ) ;
}
2014-03-12 23:55:49 +01:00
bool airtaudio : : api : : Core : : callbackEvent ( AudioDeviceID _deviceId ,
const AudioBufferList * _inBufferList ,
const AudioBufferList * _outBufferList ) {
if ( m_stream . state = = STREAM_STOPPED
| | m_stream . state = = STREAM_STOPPING ) {
2014-03-13 21:16:30 +01:00
return true ;
2014-03-12 23:55:49 +01:00
}
2014-03-11 21:46:00 +01:00
if ( m_stream . state = = STREAM_CLOSED ) {
2014-03-12 23:55:49 +01:00
ATA_ERROR ( " airtaudio::api::Core::callbackEvent(): the stream is closed ... this shouldn't happen! " ) ;
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
CallbackInfo * info = ( CallbackInfo * ) & m_stream . callbackInfo ;
CoreHandle * handle = ( CoreHandle * ) m_stream . apiHandle ;
// Check if we were draining the stream and signal is finished.
if ( handle - > drainCounter > 3 ) {
m_stream . state = STREAM_STOPPING ;
2014-03-12 23:55:49 +01:00
if ( handle - > internalDrain = = true ) {
new std : : thread ( coreStopStream , info ) ;
} else {
// external call to stopStream()
handle - > condition . notify_one ( ) ;
}
2014-03-13 21:16:30 +01:00
return true ;
2014-03-11 21:46:00 +01:00
}
AudioDeviceID outputDevice = handle - > id [ 0 ] ;
// Invoke user callback to get fresh output data UNLESS we are
// draining stream or duplex mode AND the input/output devices are
// different AND this function is called for the input device.
2014-03-12 23:55:49 +01:00
if ( handle - > drainCounter = = 0 & & ( m_stream . mode ! = DUPLEX | | _deviceId = = outputDevice ) ) {
2014-03-11 21:46:00 +01:00
airtaudio : : AirTAudioCallback callback = ( airtaudio : : AirTAudioCallback ) info - > callback ;
double streamTime = getStreamTime ( ) ;
2014-04-22 23:35:48 +02:00
airtaudio : : streamStatus status = 0 ;
2014-03-12 23:55:49 +01:00
if ( m_stream . mode ! = INPUT
& & handle - > xrun [ 0 ] = = true ) {
2014-04-22 23:35:48 +02:00
status | = OUTPUT_UNDERFLOW ;
2014-03-11 21:46:00 +01:00
handle - > xrun [ 0 ] = false ;
}
2014-03-12 23:55:49 +01:00
if ( m_stream . mode ! = OUTPUT
& & handle - > xrun [ 1 ] = = true ) {
2014-04-22 23:35:48 +02:00
status | = INPUT_OVERFLOW ;
2014-03-11 21:46:00 +01:00
handle - > xrun [ 1 ] = false ;
}
2014-03-12 23:55:49 +01:00
int32_t cbReturnValue = callback ( m_stream . userBuffer [ 0 ] ,
m_stream . userBuffer [ 1 ] ,
m_stream . bufferSize ,
streamTime ,
status ,
info - > userData ) ;
2014-03-11 21:46:00 +01:00
if ( cbReturnValue = = 2 ) {
m_stream . state = STREAM_STOPPING ;
handle - > drainCounter = 2 ;
abortStream ( ) ;
2014-03-13 21:16:30 +01:00
return true ;
2014-03-12 23:55:49 +01:00
} else if ( cbReturnValue = = 1 ) {
2014-03-11 21:46:00 +01:00
handle - > drainCounter = 1 ;
handle - > internalDrain = true ;
}
}
2014-03-12 23:55:49 +01:00
if ( m_stream . mode = = OUTPUT
| | ( m_stream . mode = = DUPLEX
& & _deviceId = = outputDevice ) ) {
if ( handle - > drainCounter > 1 ) {
// write zeros to the output stream
2014-03-11 21:46:00 +01:00
if ( handle - > nStreams [ 0 ] = = 1 ) {
2014-03-12 23:55:49 +01:00
memset ( _outBufferList - > mBuffers [ handle - > iStream [ 0 ] ] . mData ,
0 ,
_outBufferList - > mBuffers [ handle - > iStream [ 0 ] ] . mDataByteSize ) ;
} else {
// fill multiple streams with zeros
2014-03-11 21:46:00 +01:00
for ( uint32_t i = 0 ; i < handle - > nStreams [ 0 ] ; i + + ) {
2014-03-12 23:55:49 +01:00
memset ( _outBufferList - > mBuffers [ handle - > iStream [ 0 ] + i ] . mData ,
0 ,
_outBufferList - > mBuffers [ handle - > iStream [ 0 ] + i ] . mDataByteSize ) ;
2014-03-11 21:46:00 +01:00
}
}
2014-03-12 23:55:49 +01:00
} else if ( handle - > nStreams [ 0 ] = = 1 ) {
if ( m_stream . doConvertBuffer [ 0 ] ) {
// convert directly to CoreAudio stream buffer
convertBuffer ( ( char * ) _outBufferList - > mBuffers [ handle - > iStream [ 0 ] ] . mData ,
m_stream . userBuffer [ 0 ] ,
m_stream . convertInfo [ 0 ] ) ;
} else {
// copy from user buffer
memcpy ( _outBufferList - > mBuffers [ handle - > iStream [ 0 ] ] . mData ,
m_stream . userBuffer [ 0 ] ,
_outBufferList - > mBuffers [ handle - > iStream [ 0 ] ] . mDataByteSize ) ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
} else {
// fill multiple streams
float * inBuffer = ( float * ) m_stream . userBuffer [ 0 ] ;
2014-03-11 21:46:00 +01:00
if ( m_stream . doConvertBuffer [ 0 ] ) {
convertBuffer ( m_stream . deviceBuffer , m_stream . userBuffer [ 0 ] , m_stream . convertInfo [ 0 ] ) ;
2014-03-12 23:55:49 +01:00
inBuffer = ( float * ) m_stream . deviceBuffer ;
2014-03-11 21:46:00 +01:00
}
if ( m_stream . deviceInterleaved [ 0 ] = = false ) { // mono mode
2014-03-12 23:55:49 +01:00
uint32_t bufferBytes = _outBufferList - > mBuffers [ handle - > iStream [ 0 ] ] . mDataByteSize ;
2014-03-11 21:46:00 +01:00
for ( uint32_t i = 0 ; i < m_stream . nUserChannels [ 0 ] ; i + + ) {
2014-03-12 23:55:49 +01:00
memcpy ( _outBufferList - > mBuffers [ handle - > iStream [ 0 ] + i ] . mData ,
( void * ) & inBuffer [ i * m_stream . bufferSize ] ,
bufferBytes ) ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
} else {
// fill multiple multi-channel streams with interleaved data
uint32_t streamChannels , channelsLeft , inJump , outJump , inOffset ;
float * out , * in ;
2014-03-11 21:46:00 +01:00
bool inInterleaved = ( m_stream . userInterleaved ) ? true : false ;
2014-03-12 23:55:49 +01:00
uint32_t inChannels = m_stream . nUserChannels [ 0 ] ;
2014-03-11 21:46:00 +01:00
if ( m_stream . doConvertBuffer [ 0 ] ) {
inInterleaved = true ; // device buffer will always be interleaved for nStreams > 1 and not mono mode
inChannels = m_stream . nDeviceChannels [ 0 ] ;
}
2014-03-12 23:55:49 +01:00
if ( inInterleaved ) {
inOffset = 1 ;
} else {
inOffset = m_stream . bufferSize ;
}
2014-03-11 21:46:00 +01:00
channelsLeft = inChannels ;
for ( uint32_t i = 0 ; i < handle - > nStreams [ 0 ] ; i + + ) {
in = inBuffer ;
2014-03-12 23:55:49 +01:00
out = ( float * ) _outBufferList - > mBuffers [ handle - > iStream [ 0 ] + i ] . mData ;
streamChannels = _outBufferList - > mBuffers [ handle - > iStream [ 0 ] + i ] . mNumberChannels ;
2014-03-11 21:46:00 +01:00
outJump = 0 ;
// Account for possible channel offset in first stream
if ( i = = 0 & & m_stream . channelOffset [ 0 ] > 0 ) {
streamChannels - = m_stream . channelOffset [ 0 ] ;
outJump = m_stream . channelOffset [ 0 ] ;
out + = outJump ;
}
// Account for possible unfilled channels at end of the last stream
if ( streamChannels > channelsLeft ) {
outJump = streamChannels - channelsLeft ;
streamChannels = channelsLeft ;
}
// Determine input buffer offsets and skips
if ( inInterleaved ) {
inJump = inChannels ;
in + = inChannels - channelsLeft ;
2014-03-12 23:55:49 +01:00
} else {
2014-03-11 21:46:00 +01:00
inJump = 1 ;
in + = ( inChannels - channelsLeft ) * inOffset ;
}
for ( uint32_t i = 0 ; i < m_stream . bufferSize ; i + + ) {
for ( uint32_t j = 0 ; j < streamChannels ; j + + ) {
* out + + = in [ j * inOffset ] ;
}
out + = outJump ;
in + = inJump ;
}
channelsLeft - = streamChannels ;
}
}
}
if ( handle - > drainCounter ) {
handle - > drainCounter + + ;
goto unlock ;
}
}
AudioDeviceID inputDevice ;
inputDevice = handle - > id [ 1 ] ;
2014-03-12 23:55:49 +01:00
if ( m_stream . mode = = INPUT
| | ( m_stream . mode = = DUPLEX
& & _deviceId = = inputDevice ) ) {
2014-03-11 21:46:00 +01:00
if ( handle - > nStreams [ 1 ] = = 1 ) {
2014-03-12 23:55:49 +01:00
if ( m_stream . doConvertBuffer [ 1 ] ) {
// convert directly from CoreAudio stream buffer
2014-03-11 21:46:00 +01:00
convertBuffer ( m_stream . userBuffer [ 1 ] ,
2014-03-12 23:55:49 +01:00
( char * ) _inBufferList - > mBuffers [ handle - > iStream [ 1 ] ] . mData ,
m_stream . convertInfo [ 1 ] ) ;
} else { // copy to user buffer
2014-03-11 21:46:00 +01:00
memcpy ( m_stream . userBuffer [ 1 ] ,
2014-03-12 23:55:49 +01:00
_inBufferList - > mBuffers [ handle - > iStream [ 1 ] ] . mData ,
_inBufferList - > mBuffers [ handle - > iStream [ 1 ] ] . mDataByteSize ) ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
} else { // read from multiple streams
float * outBuffer = ( float * ) m_stream . userBuffer [ 1 ] ;
if ( m_stream . doConvertBuffer [ 1 ] ) {
outBuffer = ( float * ) m_stream . deviceBuffer ;
}
if ( m_stream . deviceInterleaved [ 1 ] = = false ) {
// mono mode
uint32_t bufferBytes = _inBufferList - > mBuffers [ handle - > iStream [ 1 ] ] . mDataByteSize ;
2014-03-11 21:46:00 +01:00
for ( uint32_t i = 0 ; i < m_stream . nUserChannels [ 1 ] ; i + + ) {
memcpy ( ( void * ) & outBuffer [ i * m_stream . bufferSize ] ,
2014-03-12 23:55:49 +01:00
_inBufferList - > mBuffers [ handle - > iStream [ 1 ] + i ] . mData ,
bufferBytes ) ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
} else {
// read from multiple multi-channel streams
uint32_t streamChannels , channelsLeft , inJump , outJump , outOffset ;
float * out , * in ;
2014-03-11 21:46:00 +01:00
bool outInterleaved = ( m_stream . userInterleaved ) ? true : false ;
2014-03-12 23:55:49 +01:00
uint32_t outChannels = m_stream . nUserChannels [ 1 ] ;
2014-03-11 21:46:00 +01:00
if ( m_stream . doConvertBuffer [ 1 ] ) {
outInterleaved = true ; // device buffer will always be interleaved for nStreams > 1 and not mono mode
outChannels = m_stream . nDeviceChannels [ 1 ] ;
}
2014-03-12 23:55:49 +01:00
if ( outInterleaved ) {
outOffset = 1 ;
} else {
outOffset = m_stream . bufferSize ;
}
2014-03-11 21:46:00 +01:00
channelsLeft = outChannels ;
for ( uint32_t i = 0 ; i < handle - > nStreams [ 1 ] ; i + + ) {
out = outBuffer ;
2014-03-12 23:55:49 +01:00
in = ( float * ) _inBufferList - > mBuffers [ handle - > iStream [ 1 ] + i ] . mData ;
streamChannels = _inBufferList - > mBuffers [ handle - > iStream [ 1 ] + i ] . mNumberChannels ;
2014-03-11 21:46:00 +01:00
inJump = 0 ;
// Account for possible channel offset in first stream
if ( i = = 0 & & m_stream . channelOffset [ 1 ] > 0 ) {
streamChannels - = m_stream . channelOffset [ 1 ] ;
inJump = m_stream . channelOffset [ 1 ] ;
in + = inJump ;
}
// Account for possible unread channels at end of the last stream
if ( streamChannels > channelsLeft ) {
inJump = streamChannels - channelsLeft ;
streamChannels = channelsLeft ;
}
// Determine output buffer offsets and skips
if ( outInterleaved ) {
outJump = outChannels ;
out + = outChannels - channelsLeft ;
2014-03-12 23:55:49 +01:00
} else {
2014-03-11 21:46:00 +01:00
outJump = 1 ;
out + = ( outChannels - channelsLeft ) * outOffset ;
}
for ( uint32_t i = 0 ; i < m_stream . bufferSize ; i + + ) {
for ( uint32_t j = 0 ; j < streamChannels ; j + + ) {
out [ j * outOffset ] = * in + + ;
}
out + = outJump ;
in + = inJump ;
}
channelsLeft - = streamChannels ;
}
}
if ( m_stream . doConvertBuffer [ 1 ] ) { // convert from our internal "device" buffer
convertBuffer ( m_stream . userBuffer [ 1 ] ,
2014-03-12 23:55:49 +01:00
m_stream . deviceBuffer ,
m_stream . convertInfo [ 1 ] ) ;
2014-03-11 21:46:00 +01:00
}
}
}
2014-03-12 23:55:49 +01:00
unlock :
2014-03-11 21:46:00 +01:00
//m_stream.mutex.unlock();
2014-04-22 23:35:48 +02:00
airtaudio : : Api : : tickStreamTime ( ) ;
2014-03-13 21:16:30 +01:00
return true ;
2014-03-11 21:46:00 +01:00
}
2014-03-12 23:55:49 +01:00
const char * airtaudio : : api : : Core : : getErrorCode ( OSStatus _code ) {
switch ( _code ) {
case kAudioHardwareNotRunningError :
return " kAudioHardwareNotRunningError " ;
case kAudioHardwareUnspecifiedError :
return " kAudioHardwareUnspecifiedError " ;
case kAudioHardwareUnknownPropertyError :
return " kAudioHardwareUnknownPropertyError " ;
case kAudioHardwareBadPropertySizeError :
return " kAudioHardwareBadPropertySizeError " ;
case kAudioHardwareIllegalOperationError :
return " kAudioHardwareIllegalOperationError " ;
case kAudioHardwareBadObjectError :
return " kAudioHardwareBadObjectError " ;
case kAudioHardwareBadDeviceError :
return " kAudioHardwareBadDeviceError " ;
case kAudioHardwareBadStreamError :
return " kAudioHardwareBadStreamError " ;
case kAudioHardwareUnsupportedOperationError :
return " kAudioHardwareUnsupportedOperationError " ;
case kAudioDeviceUnsupportedFormatError :
return " kAudioDeviceUnsupportedFormatError " ;
case kAudioDevicePermissionsError :
return " kAudioDevicePermissionsError " ;
default :
return " CoreAudio unknown error " ;
2014-03-11 21:46:00 +01:00
}
}
# endif