major rewrite of the CoreAudio code to avoid the runtime warning about depriciated functions/modules
This commit is contained in:
parent
6a55eb3e7e
commit
14d5460fd1
2 changed files with 199 additions and 355 deletions
512
mac/sound.cpp
512
mac/sound.cpp
|
@ -37,10 +37,11 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
|
||||||
// notifications (for most apps, this will be the main thread), add the
|
// notifications (for most apps, this will be the main thread), add the
|
||||||
// following lines of code:
|
// following lines of code:
|
||||||
// tell the HAL to use the current thread as it's run loop
|
// tell the HAL to use the current thread as it's run loop
|
||||||
CFRunLoopRef theRunLoop = CFRunLoopGetCurrent();
|
CFRunLoopRef theRunLoop = CFRunLoopGetCurrent();
|
||||||
AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,
|
AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,
|
||||||
kAudioObjectPropertyScopeGlobal,
|
kAudioObjectPropertyScopeGlobal,
|
||||||
kAudioObjectPropertyElementMaster };
|
kAudioObjectPropertyElementMaster };
|
||||||
|
|
||||||
AudioObjectSetPropertyData ( kAudioObjectSystemObject,
|
AudioObjectSetPropertyData ( kAudioObjectSystemObject,
|
||||||
&property,
|
&property,
|
||||||
0,
|
0,
|
||||||
|
@ -48,113 +49,6 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
|
||||||
sizeof ( CFRunLoopRef ),
|
sizeof ( CFRunLoopRef ),
|
||||||
&theRunLoop );
|
&theRunLoop );
|
||||||
|
|
||||||
// set up stream format
|
|
||||||
streamFormat.mSampleRate = SYSTEM_SAMPLE_RATE_HZ;
|
|
||||||
streamFormat.mFormatID = kAudioFormatLinearPCM;
|
|
||||||
streamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger;
|
|
||||||
streamFormat.mFramesPerPacket = 1;
|
|
||||||
streamFormat.mBytesPerFrame = 4;
|
|
||||||
streamFormat.mBytesPerPacket = 4;
|
|
||||||
streamFormat.mChannelsPerFrame = 2; // stereo
|
|
||||||
streamFormat.mBitsPerChannel = 16;
|
|
||||||
|
|
||||||
// set up a callback struct for new input data
|
|
||||||
inputCallbackStruct.inputProc = processInput;
|
|
||||||
inputCallbackStruct.inputProcRefCon = this;
|
|
||||||
|
|
||||||
// set up a callback struct for new output data
|
|
||||||
outputCallbackStruct.inputProc = processOutput;
|
|
||||||
outputCallbackStruct.inputProcRefCon = this;
|
|
||||||
|
|
||||||
// allocate memory for buffer struct
|
|
||||||
pBufferList = (AudioBufferList*) malloc ( offsetof ( AudioBufferList,
|
|
||||||
mBuffers[0] ) + sizeof ( AudioBuffer ) );
|
|
||||||
|
|
||||||
// open the default unit
|
|
||||||
ComponentDescription desc;
|
|
||||||
desc.componentType = kAudioUnitType_Output;
|
|
||||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
|
||||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
|
||||||
desc.componentFlags = 0;
|
|
||||||
desc.componentFlagsMask = 0;
|
|
||||||
|
|
||||||
Component comp = FindNextComponent ( NULL, &desc );
|
|
||||||
if ( comp == NULL )
|
|
||||||
{
|
|
||||||
throw CGenErr ( tr ( "No CoreAudio next component found" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( OpenAComponent ( comp, &audioInputUnit ) )
|
|
||||||
{
|
|
||||||
throw CGenErr ( tr ( "CoreAudio creating input component instance failed" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( OpenAComponent ( comp, &audioOutputUnit ) )
|
|
||||||
{
|
|
||||||
throw CGenErr ( tr ( "CoreAudio creating output component instance failed" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// we enable input and disable output for input component
|
|
||||||
UInt32 enableIO = 1;
|
|
||||||
AudioUnitSetProperty ( audioInputUnit,
|
|
||||||
kAudioOutputUnitProperty_EnableIO,
|
|
||||||
kAudioUnitScope_Input,
|
|
||||||
1, // input element
|
|
||||||
&enableIO,
|
|
||||||
sizeof ( enableIO ) );
|
|
||||||
|
|
||||||
enableIO = 0;
|
|
||||||
AudioUnitSetProperty ( audioInputUnit,
|
|
||||||
kAudioOutputUnitProperty_EnableIO,
|
|
||||||
kAudioUnitScope_Output,
|
|
||||||
0, // output element
|
|
||||||
&enableIO,
|
|
||||||
sizeof ( enableIO ) );
|
|
||||||
|
|
||||||
// set up a callback function for new input data
|
|
||||||
if ( AudioUnitSetProperty ( audioInputUnit,
|
|
||||||
kAudioOutputUnitProperty_SetInputCallback,
|
|
||||||
kAudioUnitScope_Global,
|
|
||||||
0,
|
|
||||||
&inputCallbackStruct,
|
|
||||||
sizeof ( inputCallbackStruct ) ) )
|
|
||||||
{
|
|
||||||
throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// set input stream format
|
|
||||||
if ( AudioUnitSetProperty ( audioInputUnit,
|
|
||||||
kAudioUnitProperty_StreamFormat,
|
|
||||||
kAudioUnitScope_Output,
|
|
||||||
1,
|
|
||||||
&streamFormat,
|
|
||||||
sizeof ( streamFormat ) ) )
|
|
||||||
{
|
|
||||||
throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up a callback function for new output data
|
|
||||||
if ( AudioUnitSetProperty ( audioOutputUnit,
|
|
||||||
kAudioUnitProperty_SetRenderCallback,
|
|
||||||
kAudioUnitScope_Global,
|
|
||||||
0,
|
|
||||||
&outputCallbackStruct,
|
|
||||||
sizeof ( outputCallbackStruct ) ) )
|
|
||||||
{
|
|
||||||
throw CGenErr ( tr ( "CoreAudio audio unit set property failed" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// set output stream format
|
|
||||||
if ( AudioUnitSetProperty ( audioOutputUnit,
|
|
||||||
kAudioUnitProperty_StreamFormat,
|
|
||||||
kAudioUnitScope_Input,
|
|
||||||
0,
|
|
||||||
&streamFormat,
|
|
||||||
sizeof ( streamFormat ) ) )
|
|
||||||
{
|
|
||||||
throw CGenErr ( tr ( "CoreAudio stream format set property failed" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Get available input/output devices --------------------------------------
|
// Get available input/output devices --------------------------------------
|
||||||
UInt32 iPropertySize;
|
UInt32 iPropertySize;
|
||||||
|
@ -183,7 +77,7 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
|
||||||
audioDevices );
|
audioDevices );
|
||||||
|
|
||||||
// calculate device count based on size of returned data array
|
// calculate device count based on size of returned data array
|
||||||
const UInt32 deviceCount = ( iPropertySize / sizeof ( AudioDeviceID ) );
|
const UInt32 deviceCount = iPropertySize / sizeof ( AudioDeviceID );
|
||||||
|
|
||||||
// always add system default devices for input and output as first entry
|
// always add system default devices for input and output as first entry
|
||||||
lNumDevs = 0;
|
lNumDevs = 0;
|
||||||
|
@ -263,6 +157,8 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free ( audioDevices );
|
||||||
|
|
||||||
// init device index as not initialized (invalid)
|
// init device index as not initialized (invalid)
|
||||||
lCurDev = INVALID_SNC_CARD_DEVICE;
|
lCurDev = INVALID_SNC_CARD_DEVICE;
|
||||||
CurrentAudioInputDeviceID = 0;
|
CurrentAudioInputDeviceID = 0;
|
||||||
|
@ -273,41 +169,46 @@ void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
|
||||||
bool& bIsInput,
|
bool& bIsInput,
|
||||||
bool& bIsOutput )
|
bool& bIsOutput )
|
||||||
{
|
{
|
||||||
// check if device is input or output or both (is that possible?)
|
UInt32 iPropertySize;
|
||||||
// we do this by trying to set the current device for the audio unit
|
AudioObjectPropertyAddress stPropertyAddress;
|
||||||
// with the parameter input and output and then we simply check the
|
|
||||||
// error/ok result
|
|
||||||
bIsInput = !AudioUnitSetProperty ( audioInputUnit,
|
|
||||||
kAudioOutputUnitProperty_CurrentDevice,
|
|
||||||
kAudioUnitScope_Global,
|
|
||||||
1,
|
|
||||||
&DeviceID,
|
|
||||||
sizeof ( AudioDeviceID ) );
|
|
||||||
|
|
||||||
bIsOutput = !AudioUnitSetProperty ( audioOutputUnit,
|
// init return values
|
||||||
kAudioOutputUnitProperty_CurrentDevice,
|
strDeviceName = "UNKNOWN"; // init value in case no name is available
|
||||||
kAudioUnitScope_Global,
|
bIsInput = false;
|
||||||
0,
|
bIsOutput = false;
|
||||||
&DeviceID,
|
|
||||||
sizeof ( AudioDeviceID ) );
|
// check if device is input or output or both (is that possible?)
|
||||||
|
stPropertyAddress.mSelector = kAudioDevicePropertyStreams;
|
||||||
|
stPropertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
|
||||||
|
// input check
|
||||||
|
stPropertyAddress.mScope = kAudioDevicePropertyScopeInput;
|
||||||
|
|
||||||
|
AudioObjectGetPropertyDataSize ( DeviceID,
|
||||||
|
&stPropertyAddress,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
&iPropertySize );
|
||||||
|
|
||||||
|
bIsInput = ( iPropertySize > 0 ); // check if any input streams are available
|
||||||
|
|
||||||
|
// output check
|
||||||
|
stPropertyAddress.mScope = kAudioDevicePropertyScopeOutput;
|
||||||
|
|
||||||
|
AudioObjectGetPropertyDataSize ( DeviceID,
|
||||||
|
&stPropertyAddress,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
&iPropertySize );
|
||||||
|
|
||||||
|
bIsOutput = ( iPropertySize > 0 ); // check if any output streams are available
|
||||||
|
|
||||||
// get property name
|
// get property name
|
||||||
UInt32 iPropertySize = sizeof ( CFStringRef );
|
|
||||||
CFStringRef sPropertyStringValue;
|
CFStringRef sPropertyStringValue;
|
||||||
|
|
||||||
AudioObjectPropertyAddress stPropertyAddress;
|
|
||||||
stPropertyAddress.mSelector = kAudioObjectPropertyName;
|
stPropertyAddress.mSelector = kAudioObjectPropertyName;
|
||||||
|
stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
if ( bIsInput )
|
iPropertySize = sizeof ( CFStringRef );
|
||||||
{
|
|
||||||
stPropertyAddress.mScope = kAudioDevicePropertyScopeInput;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stPropertyAddress.mScope = kAudioDevicePropertyScopeOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
stPropertyAddress.mElement = 0;
|
|
||||||
|
|
||||||
AudioObjectGetPropertyData ( DeviceID,
|
AudioObjectGetPropertyData ( DeviceID,
|
||||||
&stPropertyAddress,
|
&stPropertyAddress,
|
||||||
|
@ -317,8 +218,6 @@ void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
|
||||||
&sPropertyStringValue );
|
&sPropertyStringValue );
|
||||||
|
|
||||||
// first check if the string is not empty
|
// first check if the string is not empty
|
||||||
strDeviceName = "UNKNOWN"; // init value in case no name is available
|
|
||||||
|
|
||||||
if ( CFStringGetLength ( sPropertyStringValue ) > 0 )
|
if ( CFStringGetLength ( sPropertyStringValue ) > 0 )
|
||||||
{
|
{
|
||||||
// convert CFString in c-string (quick hack!) and then in QString
|
// convert CFString in c-string (quick hack!) and then in QString
|
||||||
|
@ -332,33 +231,13 @@ void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
|
||||||
{
|
{
|
||||||
strDeviceName = sC_strPropValue;
|
strDeviceName = sC_strPropValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free ( sC_strPropValue );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CSound::LoadAndInitializeDriver ( int iDriverIdx )
|
QString CSound::LoadAndInitializeDriver ( int iDriverIdx )
|
||||||
{
|
{
|
||||||
// set input device
|
|
||||||
if ( AudioUnitSetProperty ( audioInputUnit,
|
|
||||||
kAudioOutputUnitProperty_CurrentDevice,
|
|
||||||
kAudioUnitScope_Global,
|
|
||||||
1,
|
|
||||||
&audioInputDevice[iDriverIdx],
|
|
||||||
sizeof ( AudioDeviceID ) ) )
|
|
||||||
{
|
|
||||||
throw CGenErr ( tr ( "CoreAudio input AudioUnitSetProperty call failed" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// set output device
|
|
||||||
if ( AudioUnitSetProperty ( audioOutputUnit,
|
|
||||||
kAudioOutputUnitProperty_CurrentDevice,
|
|
||||||
kAudioUnitScope_Global,
|
|
||||||
0,
|
|
||||||
&audioOutputDevice[iDriverIdx],
|
|
||||||
sizeof ( AudioDeviceID ) ) )
|
|
||||||
{
|
|
||||||
throw CGenErr ( tr ( "CoreAudio output AudioUnitSetProperty call failed" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// check device capabilities if it fullfills our requirements
|
// check device capabilities if it fullfills our requirements
|
||||||
const QString strStat = CheckDeviceCapabilities ( iDriverIdx );
|
const QString strStat = CheckDeviceCapabilities ( iDriverIdx );
|
||||||
|
|
||||||
|
@ -368,19 +247,6 @@ QString CSound::LoadAndInitializeDriver ( int iDriverIdx )
|
||||||
// store ID of selected driver if initialization was successful
|
// store ID of selected driver if initialization was successful
|
||||||
lCurDev = iDriverIdx;
|
lCurDev = iDriverIdx;
|
||||||
CurrentAudioInputDeviceID = audioInputDevice[iDriverIdx];
|
CurrentAudioInputDeviceID = audioInputDevice[iDriverIdx];
|
||||||
|
|
||||||
// TODO why is only the input enough...?
|
|
||||||
|
|
||||||
// setup callback for xruns (only for input is enough)
|
|
||||||
AudioObjectPropertyAddress stPropertyAddress;
|
|
||||||
stPropertyAddress.mSelector = kAudioDeviceProcessorOverload;
|
|
||||||
stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
|
||||||
stPropertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
|
||||||
|
|
||||||
AudioObjectAddPropertyListener ( audioInputDevice[lCurDev],
|
|
||||||
&stPropertyAddress,
|
|
||||||
deviceNotification,
|
|
||||||
this );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return strStat;
|
return strStat;
|
||||||
|
@ -388,30 +254,31 @@ QString CSound::LoadAndInitializeDriver ( int iDriverIdx )
|
||||||
|
|
||||||
QString CSound::CheckDeviceCapabilities ( const int iDriverIdx )
|
QString CSound::CheckDeviceCapabilities ( const int iDriverIdx )
|
||||||
{
|
{
|
||||||
UInt32 size;
|
UInt32 iPropertySize;
|
||||||
const Float64 fSystemSampleRate = (Float64) SYSTEM_SAMPLE_RATE_HZ;
|
Float64 inputSampleRate;
|
||||||
AudioObjectPropertyAddress stSRatePropertyAddress;
|
AudioStreamBasicDescription CurDevStreamFormat;
|
||||||
|
const Float64 fSystemSampleRate = static_cast<Float64> ( SYSTEM_SAMPLE_RATE_HZ );
|
||||||
|
AudioObjectPropertyAddress stPropertyAddress;
|
||||||
|
|
||||||
stSRatePropertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
|
stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
stSRatePropertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
stPropertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
stSRatePropertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
|
||||||
|
|
||||||
// check input device sample rate
|
// check input device sample rate
|
||||||
size = sizeof ( Float64 );
|
stPropertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
|
||||||
Float64 inputSampleRate;
|
iPropertySize = sizeof ( Float64 );
|
||||||
|
|
||||||
AudioUnitGetProperty ( audioInputUnit,
|
AudioObjectGetPropertyData ( audioInputDevice[iDriverIdx],
|
||||||
kAudioUnitProperty_SampleRate,
|
&stPropertyAddress,
|
||||||
kAudioUnitScope_Input,
|
0,
|
||||||
1,
|
NULL,
|
||||||
&inputSampleRate,
|
&iPropertySize,
|
||||||
&size );
|
&inputSampleRate );
|
||||||
|
|
||||||
if ( static_cast<int> ( inputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ )
|
if ( inputSampleRate != fSystemSampleRate )
|
||||||
{
|
{
|
||||||
// try to change the sample rate
|
// try to change the sample rate
|
||||||
if ( AudioObjectSetPropertyData ( audioInputDevice[iDriverIdx],
|
if ( AudioObjectSetPropertyData ( audioInputDevice[iDriverIdx],
|
||||||
&stSRatePropertyAddress,
|
&stPropertyAddress,
|
||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
sizeof ( Float64 ),
|
sizeof ( Float64 ),
|
||||||
|
@ -425,20 +292,21 @@ QString CSound::CheckDeviceCapabilities ( const int iDriverIdx )
|
||||||
}
|
}
|
||||||
|
|
||||||
// check output device sample rate
|
// check output device sample rate
|
||||||
size = sizeof ( Float64 );
|
iPropertySize = sizeof ( Float64 );
|
||||||
Float64 outputSampleRate;
|
Float64 outputSampleRate;
|
||||||
AudioUnitGetProperty ( audioOutputUnit,
|
|
||||||
kAudioUnitProperty_SampleRate,
|
|
||||||
kAudioUnitScope_Output,
|
|
||||||
0,
|
|
||||||
&outputSampleRate,
|
|
||||||
&size );
|
|
||||||
|
|
||||||
if ( static_cast<int> ( outputSampleRate ) != SYSTEM_SAMPLE_RATE_HZ )
|
AudioObjectGetPropertyData ( audioOutputDevice[iDriverIdx],
|
||||||
|
&stPropertyAddress,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
&iPropertySize,
|
||||||
|
&outputSampleRate );
|
||||||
|
|
||||||
|
if ( outputSampleRate != fSystemSampleRate )
|
||||||
{
|
{
|
||||||
// try to change the sample rate
|
// try to change the sample rate
|
||||||
if ( AudioObjectSetPropertyData ( audioOutputDevice[iDriverIdx],
|
if ( AudioObjectSetPropertyData ( audioOutputDevice[iDriverIdx],
|
||||||
&stSRatePropertyAddress,
|
&stPropertyAddress,
|
||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
sizeof ( Float64 ),
|
sizeof ( Float64 ),
|
||||||
|
@ -451,24 +319,77 @@ QString CSound::CheckDeviceCapabilities ( const int iDriverIdx )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// According to the AudioHardware documentation: "If the format is a linear PCM
|
||||||
|
// format, the data will always be presented as 32 bit, native endian floating
|
||||||
|
// point. All conversions to and from the true physical format of the hardware
|
||||||
|
// is handled by the devices driver."
|
||||||
|
// So we check for the fixed values here.
|
||||||
|
iPropertySize = sizeof ( AudioStreamBasicDescription );
|
||||||
|
stPropertyAddress.mSelector = kAudioStreamPropertyVirtualFormat;
|
||||||
|
|
||||||
|
AudioObjectGetPropertyData ( audioOutputDevice[iDriverIdx],
|
||||||
|
&stPropertyAddress,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
&iPropertySize,
|
||||||
|
&CurDevStreamFormat );
|
||||||
|
|
||||||
|
if ( ( CurDevStreamFormat.mFormatID != kAudioFormatLinearPCM ) ||
|
||||||
|
( CurDevStreamFormat.mFramesPerPacket != 1 ) ||
|
||||||
|
( CurDevStreamFormat.mBytesPerFrame != 8 ) ||
|
||||||
|
( CurDevStreamFormat.mBytesPerPacket != 8 ) ||
|
||||||
|
( CurDevStreamFormat.mChannelsPerFrame != 2 ) ||
|
||||||
|
( CurDevStreamFormat.mBitsPerChannel != 32 ) )
|
||||||
|
{
|
||||||
|
return QString ( tr ( "The audio stream format for this audio device is"
|
||||||
|
"not compatible with the requirements." ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO mSampleRate, mFormatFlags = kAudioFormatFlagIsSignedInteger
|
||||||
|
|
||||||
|
// TODO check input device, too!
|
||||||
|
|
||||||
|
qDebug() << "mBitsPerChannel" << CurDevStreamFormat.mBitsPerChannel;
|
||||||
|
qDebug() << "mBytesPerFrame" << CurDevStreamFormat.mBytesPerFrame;
|
||||||
|
qDebug() << "mBytesPerPacket" << CurDevStreamFormat.mBytesPerPacket;
|
||||||
|
qDebug() << "mChannelsPerFrame" << CurDevStreamFormat.mChannelsPerFrame;
|
||||||
|
qDebug() << "mFramesPerPacket" << CurDevStreamFormat.mFramesPerPacket;
|
||||||
|
qDebug() << "mSampleRate" << CurDevStreamFormat.mSampleRate;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// everything is ok, return empty string for "no error" case
|
// everything is ok, return empty string for "no error" case
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSound::CloseCoreAudio()
|
|
||||||
{
|
|
||||||
// clean up
|
|
||||||
AudioUnitUninitialize ( audioInputUnit );
|
|
||||||
AudioUnitUninitialize ( audioOutputUnit );
|
|
||||||
CloseComponent ( audioInputUnit );
|
|
||||||
CloseComponent ( audioOutputUnit );
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSound::Start()
|
void CSound::Start()
|
||||||
{
|
{
|
||||||
// start the rendering
|
// setup callback for xruns (only for input is enough)
|
||||||
AudioOutputUnitStart ( audioInputUnit );
|
AudioObjectPropertyAddress stPropertyAddress;
|
||||||
AudioOutputUnitStart ( audioOutputUnit );
|
|
||||||
|
stPropertyAddress.mSelector = kAudioDeviceProcessorOverload;
|
||||||
|
stPropertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
|
||||||
|
AudioObjectAddPropertyListener ( audioInputDevice[lCurDev],
|
||||||
|
&stPropertyAddress,
|
||||||
|
deviceNotification,
|
||||||
|
this );
|
||||||
|
|
||||||
|
// register the callback function for input and output
|
||||||
|
AudioDeviceCreateIOProcID ( audioInputDevice[lCurDev],
|
||||||
|
callbackIO,
|
||||||
|
this,
|
||||||
|
&audioInputProcID );
|
||||||
|
|
||||||
|
AudioDeviceCreateIOProcID ( audioOutputDevice[lCurDev],
|
||||||
|
callbackIO,
|
||||||
|
this,
|
||||||
|
&audioOutputProcID );
|
||||||
|
|
||||||
|
// start the audio stream
|
||||||
|
AudioDeviceStart ( audioInputDevice[lCurDev], audioInputProcID );
|
||||||
|
AudioDeviceStart ( audioOutputDevice[lCurDev], audioOutputProcID );
|
||||||
|
|
||||||
// call base class
|
// call base class
|
||||||
CSoundBase::Start();
|
CSoundBase::Start();
|
||||||
|
@ -477,8 +398,24 @@ void CSound::Start()
|
||||||
void CSound::Stop()
|
void CSound::Stop()
|
||||||
{
|
{
|
||||||
// stop the audio stream
|
// stop the audio stream
|
||||||
AudioOutputUnitStop ( audioInputUnit );
|
AudioDeviceStop ( audioInputDevice[lCurDev], audioInputProcID );
|
||||||
AudioOutputUnitStop ( audioOutputUnit );
|
AudioDeviceStop ( audioOutputDevice[lCurDev], audioOutputProcID );
|
||||||
|
|
||||||
|
// unregister the callback function for input and output
|
||||||
|
AudioDeviceDestroyIOProcID ( audioInputDevice[lCurDev], audioInputProcID );
|
||||||
|
AudioDeviceDestroyIOProcID ( audioOutputDevice[lCurDev], audioOutputProcID );
|
||||||
|
|
||||||
|
// unregister the callback function for xruns
|
||||||
|
AudioObjectPropertyAddress stPropertyAddress;
|
||||||
|
|
||||||
|
stPropertyAddress.mSelector = kAudioDeviceProcessorOverload;
|
||||||
|
stPropertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
|
||||||
|
AudioObjectRemovePropertyListener( audioInputDevice[lCurDev],
|
||||||
|
&stPropertyAddress,
|
||||||
|
deviceNotification,
|
||||||
|
this );
|
||||||
|
|
||||||
// call base class
|
// call base class
|
||||||
CSoundBase::Stop();
|
CSoundBase::Stop();
|
||||||
|
@ -530,41 +467,6 @@ int CSound::Init ( const int iNewPrefMonoBufferSize )
|
||||||
// create memory for intermediate audio buffer
|
// create memory for intermediate audio buffer
|
||||||
vecsTmpAudioSndCrdStereo.Init ( iCoreAudioBufferSizeStereo );
|
vecsTmpAudioSndCrdStereo.Init ( iCoreAudioBufferSizeStereo );
|
||||||
|
|
||||||
// fill audio unit buffer struct
|
|
||||||
pBufferList->mNumberBuffers = 1;
|
|
||||||
pBufferList->mBuffers[0].mNumberChannels = 2; // stereo
|
|
||||||
pBufferList->mBuffers[0].mDataByteSize = iCoreAudioBufferSizeMono * 4; // 2 bytes, 2 channels
|
|
||||||
pBufferList->mBuffers[0].mData = &vecsTmpAudioSndCrdStereo[0];
|
|
||||||
|
|
||||||
// initialize units
|
|
||||||
if ( AudioUnitInitialize ( audioInputUnit ) )
|
|
||||||
{
|
|
||||||
throw CGenErr ( tr ( "Initialization of CoreAudio failed" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( AudioUnitInitialize ( audioOutputUnit ) )
|
|
||||||
{
|
|
||||||
throw CGenErr ( tr ( "Initialization of CoreAudio failed" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
// TEST
|
|
||||||
AudioDeviceIOProcID testIn;
|
|
||||||
AudioDeviceCreateIOProcID ( audioInputDevice[lCurDev],
|
|
||||||
callbackIO,
|
|
||||||
this,
|
|
||||||
&testIn );
|
|
||||||
|
|
||||||
AudioDeviceIOProcID testOut;
|
|
||||||
AudioDeviceCreateIOProcID ( audioOutputDevice[lCurDev],
|
|
||||||
callbackIO,
|
|
||||||
this,
|
|
||||||
&testOut );
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return iCoreAudioBufferSizeMono;
|
return iCoreAudioBufferSizeMono;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -584,7 +486,7 @@ UInt32 CSound::SetBufferSize ( AudioDeviceID& audioDeviceID,
|
||||||
stPropertyAddress.mScope = kAudioDevicePropertyScopeOutput;
|
stPropertyAddress.mScope = kAudioDevicePropertyScopeOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
stPropertyAddress.mElement = 0;
|
stPropertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
|
||||||
// first set the value
|
// first set the value
|
||||||
UInt32 iSizeBufValue = sizeof ( UInt32 );
|
UInt32 iSizeBufValue = sizeof ( UInt32 );
|
||||||
|
@ -609,29 +511,25 @@ UInt32 CSound::SetBufferSize ( AudioDeviceID& audioDeviceID,
|
||||||
|
|
||||||
OSStatus CSound::deviceNotification ( AudioDeviceID,
|
OSStatus CSound::deviceNotification ( AudioDeviceID,
|
||||||
UInt32,
|
UInt32,
|
||||||
const AudioObjectPropertyAddress* inAddresses,
|
const AudioObjectPropertyAddress* /* inAddresses */,
|
||||||
void* inRefCon )
|
void* /* inRefCon */ )
|
||||||
{
|
{
|
||||||
|
// TODO: Do we need this anymore? If not, we can completely remove this function...
|
||||||
|
/*
|
||||||
CSound* pSound = static_cast<CSound*> ( inRefCon );
|
CSound* pSound = static_cast<CSound*> ( inRefCon );
|
||||||
|
|
||||||
if ( inAddresses->mSelector == kAudioDeviceProcessorOverload )
|
if ( inAddresses->mSelector == kAudioDeviceProcessorOverload )
|
||||||
{
|
{
|
||||||
|
|
||||||
// TODO: Do we need this anymore? If not, we can completely remove this function...
|
|
||||||
/*
|
|
||||||
// xrun handling (it is important to act on xruns under CoreAudio
|
// xrun handling (it is important to act on xruns under CoreAudio
|
||||||
// since it seems that the xrun situation stays stable for a
|
// since it seems that the xrun situation stays stable for a
|
||||||
// while and would give you a long time bad audio)
|
// while and would give you a long time bad audio)
|
||||||
pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART );
|
pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART );
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
return noErr;
|
return noErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
// TEST
|
|
||||||
OSStatus CSound::callbackIO ( AudioDeviceID inDevice,
|
OSStatus CSound::callbackIO ( AudioDeviceID inDevice,
|
||||||
const AudioTimeStamp*,
|
const AudioTimeStamp*,
|
||||||
const AudioBufferList* inInputData,
|
const AudioBufferList* inInputData,
|
||||||
|
@ -642,70 +540,46 @@ OSStatus CSound::callbackIO ( AudioDeviceID inDevice,
|
||||||
{
|
{
|
||||||
CSound* pSound = static_cast<CSound*> ( inRefCon );
|
CSound* pSound = static_cast<CSound*> ( inRefCon );
|
||||||
|
|
||||||
|
// both, the input and output device use the same callback function
|
||||||
|
QMutexLocker locker ( &pSound->Mutex );
|
||||||
|
|
||||||
if ( inDevice == pSound->CurrentAudioInputDeviceID )
|
if ( inDevice == pSound->CurrentAudioInputDeviceID )
|
||||||
{
|
{
|
||||||
// audio input callback, copy data first
|
// check size (float32 has four bytes)
|
||||||
memcpy ( &pSound->vecsTmpAudioSndCrdStereo[0],
|
if ( inInputData->mBuffers[0].mDataByteSize ==
|
||||||
inInputData->mBuffers[0].mData,
|
static_cast<UInt32> ( pSound->iCoreAudioBufferSizeStereo * 4 ) )
|
||||||
inInputData->mBuffers[0].mDataByteSize );
|
{
|
||||||
|
// get a pointer to the input data of the correct type
|
||||||
|
Float32* pInData = static_cast<Float32*> ( inInputData->mBuffers[0].mData );
|
||||||
|
|
||||||
// call processing callback function
|
// copy input data
|
||||||
pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo );
|
for ( int i = 0; i < pSound->iCoreAudioBufferSizeStereo; i++ )
|
||||||
|
{
|
||||||
|
pSound->vecsTmpAudioSndCrdStereo[i] =
|
||||||
|
(short) ( pInData[i] * _MAXSHORT );
|
||||||
|
}
|
||||||
|
|
||||||
|
// call processing callback function
|
||||||
|
pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// audio output callback, simply copy data
|
// check size (float32 has four bytes)
|
||||||
memcpy ( outOutputData->mBuffers[0].mData,
|
if ( outOutputData->mBuffers[0].mDataByteSize ==
|
||||||
&pSound->vecsTmpAudioSndCrdStereo[0],
|
static_cast<UInt32> ( pSound->iCoreAudioBufferSizeStereo * 4 ) )
|
||||||
outOutputData->mBuffers[0].mDataByteSize );
|
{
|
||||||
|
// get a pointer to the input data of the correct type
|
||||||
|
Float32* pOutData = static_cast<Float32*> ( outOutputData->mBuffers[0].mData );
|
||||||
|
|
||||||
|
// copy output data
|
||||||
|
for ( int i = 0; i < pSound->iCoreAudioBufferSizeStereo; i++ )
|
||||||
|
{
|
||||||
|
pOutData[i] = (Float32)
|
||||||
|
pSound->vecsTmpAudioSndCrdStereo[i] / _MAXSHORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return kAudioHardwareNoError;
|
return kAudioHardwareNoError;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
OSStatus CSound::processInput ( void* inRefCon,
|
|
||||||
AudioUnitRenderActionFlags* ioActionFlags,
|
|
||||||
const AudioTimeStamp* inTimeStamp,
|
|
||||||
UInt32 inBusNumber,
|
|
||||||
UInt32 inNumberFrames,
|
|
||||||
AudioBufferList* )
|
|
||||||
{
|
|
||||||
CSound* pSound = static_cast<CSound*> ( inRefCon );
|
|
||||||
|
|
||||||
QMutexLocker locker ( &pSound->Mutex );
|
|
||||||
|
|
||||||
// get the new audio data
|
|
||||||
AudioUnitRender ( pSound->audioInputUnit,
|
|
||||||
ioActionFlags,
|
|
||||||
inTimeStamp,
|
|
||||||
inBusNumber,
|
|
||||||
inNumberFrames,
|
|
||||||
pSound->pBufferList );
|
|
||||||
|
|
||||||
// call processing callback function
|
|
||||||
pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo );
|
|
||||||
|
|
||||||
return noErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
OSStatus CSound::processOutput ( void* inRefCon,
|
|
||||||
AudioUnitRenderActionFlags*,
|
|
||||||
const AudioTimeStamp*,
|
|
||||||
UInt32,
|
|
||||||
UInt32,
|
|
||||||
AudioBufferList* ioData )
|
|
||||||
{
|
|
||||||
CSound* pSound = static_cast<CSound*> ( inRefCon );
|
|
||||||
|
|
||||||
QMutexLocker locker ( &pSound->Mutex );
|
|
||||||
|
|
||||||
memcpy ( ioData->mBuffers[0].mData,
|
|
||||||
&pSound->vecsTmpAudioSndCrdStereo[0],
|
|
||||||
pSound->pBufferList->mBuffers[0].mDataByteSize );
|
|
||||||
|
|
||||||
return noErr;
|
|
||||||
}
|
|
||||||
|
|
42
mac/sound.h
42
mac/sound.h
|
@ -25,11 +25,8 @@
|
||||||
#if !defined(_SOUND_H__9518A621345F78_363456876UZGSDF82CF549__INCLUDED_)
|
#if !defined(_SOUND_H__9518A621345F78_363456876UZGSDF82CF549__INCLUDED_)
|
||||||
#define _SOUND_H__9518A621345F78_363456876UZGSDF82CF549__INCLUDED_
|
#define _SOUND_H__9518A621345F78_363456876UZGSDF82CF549__INCLUDED_
|
||||||
|
|
||||||
#include <CoreServices/CoreServices.h>
|
#include <CoreAudio/CoreAudio.h>
|
||||||
#include <AudioUnit/AudioUnit.h>
|
|
||||||
#include <CoreAudio/AudioHardware.h>
|
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include "util.h"
|
|
||||||
#include "soundbase.h"
|
#include "soundbase.h"
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
|
@ -39,7 +36,6 @@ class CSound : public CSoundBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ), void* arg );
|
CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ), void* arg );
|
||||||
virtual ~CSound() { CloseCoreAudio(); }
|
|
||||||
|
|
||||||
virtual int Init ( const int iNewPrefMonoBufferSize );
|
virtual int Init ( const int iNewPrefMonoBufferSize );
|
||||||
virtual void Start();
|
virtual void Start();
|
||||||
|
@ -57,8 +53,6 @@ protected:
|
||||||
|
|
||||||
QString CheckDeviceCapabilities ( const int iDriverIdx );
|
QString CheckDeviceCapabilities ( const int iDriverIdx );
|
||||||
|
|
||||||
void CloseCoreAudio();
|
|
||||||
|
|
||||||
UInt32 SetBufferSize ( AudioDeviceID& audioDeviceID,
|
UInt32 SetBufferSize ( AudioDeviceID& audioDeviceID,
|
||||||
const bool bIsInput,
|
const bool bIsInput,
|
||||||
UInt32 iPrefBufferSize );
|
UInt32 iPrefBufferSize );
|
||||||
|
@ -74,8 +68,6 @@ protected:
|
||||||
const AudioObjectPropertyAddress* inAddresses,
|
const AudioObjectPropertyAddress* inAddresses,
|
||||||
void* inRefCon );
|
void* inRefCon );
|
||||||
|
|
||||||
/*
|
|
||||||
// TEST
|
|
||||||
static OSStatus callbackIO ( AudioDeviceID inDevice,
|
static OSStatus callbackIO ( AudioDeviceID inDevice,
|
||||||
const AudioTimeStamp*,
|
const AudioTimeStamp*,
|
||||||
const AudioBufferList* inInputData,
|
const AudioBufferList* inInputData,
|
||||||
|
@ -83,35 +75,13 @@ protected:
|
||||||
AudioBufferList* outOutputData,
|
AudioBufferList* outOutputData,
|
||||||
const AudioTimeStamp*,
|
const AudioTimeStamp*,
|
||||||
void* inRefCon );
|
void* inRefCon );
|
||||||
*/
|
|
||||||
|
|
||||||
static OSStatus processInput ( void* inRefCon,
|
AudioDeviceID audioInputDevice[MAX_NUMBER_SOUND_CARDS];
|
||||||
AudioUnitRenderActionFlags* ioActionFlags,
|
AudioDeviceID audioOutputDevice[MAX_NUMBER_SOUND_CARDS];
|
||||||
const AudioTimeStamp* inTimeStamp,
|
AudioDeviceIOProcID audioInputProcID;
|
||||||
UInt32 inBusNumber,
|
AudioDeviceIOProcID audioOutputProcID;
|
||||||
UInt32 inNumberFrames,
|
|
||||||
AudioBufferList* );
|
|
||||||
|
|
||||||
static OSStatus processOutput ( void* inRefCon,
|
QMutex Mutex;
|
||||||
AudioUnitRenderActionFlags*,
|
|
||||||
const AudioTimeStamp*,
|
|
||||||
UInt32,
|
|
||||||
UInt32,
|
|
||||||
AudioBufferList* ioData );
|
|
||||||
|
|
||||||
AudioStreamBasicDescription streamFormat;
|
|
||||||
|
|
||||||
AURenderCallbackStruct inputCallbackStruct;
|
|
||||||
AURenderCallbackStruct outputCallbackStruct;
|
|
||||||
|
|
||||||
ComponentInstance audioInputUnit;
|
|
||||||
AudioDeviceID audioInputDevice[MAX_NUMBER_SOUND_CARDS];
|
|
||||||
ComponentInstance audioOutputUnit;
|
|
||||||
AudioDeviceID audioOutputDevice[MAX_NUMBER_SOUND_CARDS];
|
|
||||||
|
|
||||||
AudioBufferList* pBufferList;
|
|
||||||
|
|
||||||
QMutex Mutex;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // !defined(_SOUND_H__9518A621345F78_363456876UZGSDF82CF549__INCLUDED_)
|
#endif // !defined(_SOUND_H__9518A621345F78_363456876UZGSDF82CF549__INCLUDED_)
|
||||||
|
|
Loading…
Reference in a new issue