add support for channel selection for MacOS
This commit is contained in:
parent
d5bcb0fc1a
commit
3d647400db
4 changed files with 304 additions and 77 deletions
11
ChangeLog
11
ChangeLog
|
@ -1,3 +1,14 @@
|
||||||
|
|
||||||
|
|
||||||
|
3.3.11
|
||||||
|
|
||||||
|
- changed the MacOS audio interface to be future proof (do not use
|
||||||
|
the Carbon Component Manager anymore)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
3.3.10
|
3.3.10
|
||||||
|
|
||||||
- changed the default central server URL
|
- changed the default central server URL
|
||||||
|
|
341
mac/sound.cpp
341
mac/sound.cpp
|
@ -66,7 +66,7 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
|
||||||
NULL,
|
NULL,
|
||||||
&iPropertySize );
|
&iPropertySize );
|
||||||
|
|
||||||
AudioDeviceID* audioDevices = (AudioDeviceID*) malloc ( iPropertySize );
|
CVector<AudioDeviceID> vAudioDevices ( iPropertySize );
|
||||||
|
|
||||||
// now actually query all devices present in the system
|
// now actually query all devices present in the system
|
||||||
AudioObjectGetPropertyData ( kAudioObjectSystemObject,
|
AudioObjectGetPropertyData ( kAudioObjectSystemObject,
|
||||||
|
@ -74,10 +74,10 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
|
||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
&iPropertySize,
|
&iPropertySize,
|
||||||
audioDevices );
|
&vAudioDevices[0] );
|
||||||
|
|
||||||
// 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 iDeviceCount = 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;
|
||||||
|
@ -118,9 +118,9 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
|
||||||
// we add combined entries for input and output for each device so that we
|
// we add combined entries for input and output for each device so that we
|
||||||
// do not need two combo boxes in the GUI for input and output (therefore
|
// do not need two combo boxes in the GUI for input and output (therefore
|
||||||
// all possible combinations are required which can be a large number)
|
// all possible combinations are required which can be a large number)
|
||||||
for ( UInt32 i = 0; i < deviceCount; i++ )
|
for ( UInt32 i = 0; i < iDeviceCount; i++ )
|
||||||
{
|
{
|
||||||
for ( UInt32 j = 0; j < deviceCount; j++ )
|
for ( UInt32 j = 0; j < iDeviceCount; j++ )
|
||||||
{
|
{
|
||||||
// get device infos for both current devices
|
// get device infos for both current devices
|
||||||
QString strDeviceName_i;
|
QString strDeviceName_i;
|
||||||
|
@ -130,12 +130,12 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
|
||||||
bool bIsOutput_i;
|
bool bIsOutput_i;
|
||||||
bool bIsOutput_j;
|
bool bIsOutput_j;
|
||||||
|
|
||||||
GetAudioDeviceInfos ( audioDevices[i],
|
GetAudioDeviceInfos ( vAudioDevices[i],
|
||||||
strDeviceName_i,
|
strDeviceName_i,
|
||||||
bIsInput_i,
|
bIsInput_i,
|
||||||
bIsOutput_i );
|
bIsOutput_i );
|
||||||
|
|
||||||
GetAudioDeviceInfos ( audioDevices[j],
|
GetAudioDeviceInfos ( vAudioDevices[j],
|
||||||
strDeviceName_j,
|
strDeviceName_j,
|
||||||
bIsInput_j,
|
bIsInput_j,
|
||||||
bIsOutput_j );
|
bIsOutput_j );
|
||||||
|
@ -149,19 +149,18 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* ar
|
||||||
strDeviceName_j;
|
strDeviceName_j;
|
||||||
|
|
||||||
// store audio device IDs
|
// store audio device IDs
|
||||||
audioInputDevice[lNumDevs] = audioDevices[i];
|
audioInputDevice[lNumDevs] = vAudioDevices[i];
|
||||||
audioOutputDevice[lNumDevs] = audioDevices[j];
|
audioOutputDevice[lNumDevs] = vAudioDevices[j];
|
||||||
|
|
||||||
lNumDevs++; // next device
|
lNumDevs++; // next device
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
CurrentAudioOutputDeviceID = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
|
void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
|
||||||
|
@ -173,15 +172,15 @@ void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
|
||||||
AudioObjectPropertyAddress stPropertyAddress;
|
AudioObjectPropertyAddress stPropertyAddress;
|
||||||
|
|
||||||
// init return values
|
// init return values
|
||||||
strDeviceName = "UNKNOWN"; // init value in case no name is available
|
bIsInput = false;
|
||||||
bIsInput = false;
|
bIsOutput = false;
|
||||||
bIsOutput = false;
|
|
||||||
|
|
||||||
// check if device is input or output or both (is that possible?)
|
// check if device is input or output or both (is that possible?)
|
||||||
stPropertyAddress.mSelector = kAudioDevicePropertyStreams;
|
stPropertyAddress.mSelector = kAudioDevicePropertyStreams;
|
||||||
stPropertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
stPropertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||||
|
|
||||||
// input check
|
// input check
|
||||||
|
iPropertySize = 0;
|
||||||
stPropertyAddress.mScope = kAudioDevicePropertyScopeInput;
|
stPropertyAddress.mScope = kAudioDevicePropertyScopeInput;
|
||||||
|
|
||||||
AudioObjectGetPropertyDataSize ( DeviceID,
|
AudioObjectGetPropertyDataSize ( DeviceID,
|
||||||
|
@ -193,6 +192,7 @@ void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
|
||||||
bIsInput = ( iPropertySize > 0 ); // check if any input streams are available
|
bIsInput = ( iPropertySize > 0 ); // check if any input streams are available
|
||||||
|
|
||||||
// output check
|
// output check
|
||||||
|
iPropertySize = 0;
|
||||||
stPropertyAddress.mScope = kAudioDevicePropertyScopeOutput;
|
stPropertyAddress.mScope = kAudioDevicePropertyScopeOutput;
|
||||||
|
|
||||||
AudioObjectGetPropertyDataSize ( DeviceID,
|
AudioObjectGetPropertyDataSize ( DeviceID,
|
||||||
|
@ -217,22 +217,11 @@ void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
|
||||||
&iPropertySize,
|
&iPropertySize,
|
||||||
&sPropertyStringValue );
|
&sPropertyStringValue );
|
||||||
|
|
||||||
// first check if the string is not empty
|
// convert string
|
||||||
if ( CFStringGetLength ( sPropertyStringValue ) > 0 )
|
if ( !ConvertCFStringToQString ( sPropertyStringValue, strDeviceName ) )
|
||||||
{
|
{
|
||||||
// convert CFString in c-string (quick hack!) and then in QString
|
// use a default name in case the conversion did not succeed
|
||||||
char* sC_strPropValue =
|
strDeviceName = "UNKNOWN";
|
||||||
(char*) malloc ( CFStringGetLength ( sPropertyStringValue ) + 1 );
|
|
||||||
|
|
||||||
if ( CFStringGetCString ( sPropertyStringValue,
|
|
||||||
sC_strPropValue,
|
|
||||||
CFStringGetLength ( sPropertyStringValue ) + 1,
|
|
||||||
kCFStringEncodingISOLatin1 ) )
|
|
||||||
{
|
|
||||||
strDeviceName = sC_strPropValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
free ( sC_strPropValue );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,8 +234,16 @@ QString CSound::LoadAndInitializeDriver ( int iDriverIdx )
|
||||||
if ( strStat.isEmpty() )
|
if ( strStat.isEmpty() )
|
||||||
{
|
{
|
||||||
// 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];
|
||||||
|
CurrentAudioOutputDeviceID = audioOutputDevice[iDriverIdx];
|
||||||
|
|
||||||
|
// the device has changed, per definition we reset the channel
|
||||||
|
// mapping to the defaults (first two available channels)
|
||||||
|
iSelInputLeftChannel = 0;
|
||||||
|
iSelInputRightChannel = min ( iNumInChan - 1, 1 );
|
||||||
|
iSelOutputLeftChannel = 0;
|
||||||
|
iSelOutputRightChannel = min ( iNumOutChan - 1, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
return strStat;
|
return strStat;
|
||||||
|
@ -319,59 +316,208 @@ QString CSound::CheckDeviceCapabilities ( const int iDriverIdx )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// According to the AudioHardware documentation: "If the format is a linear PCM
|
// get the stream ID of the input device (at least one stream must always exist)
|
||||||
// format, the data will always be presented as 32 bit, native endian floating
|
iPropertySize = 0;
|
||||||
// point. All conversions to and from the true physical format of the hardware
|
stPropertyAddress.mSelector = kAudioDevicePropertyStreams;
|
||||||
// is handled by the devices driver."
|
stPropertyAddress.mScope = kAudioObjectPropertyScopeInput;
|
||||||
// So we check for the fixed values here.
|
|
||||||
iPropertySize = sizeof ( AudioStreamBasicDescription );
|
AudioObjectGetPropertyDataSize ( audioInputDevice[iDriverIdx],
|
||||||
stPropertyAddress.mSelector = kAudioStreamPropertyVirtualFormat;
|
&stPropertyAddress,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
&iPropertySize );
|
||||||
|
|
||||||
|
CVector<AudioStreamID> vInputStreamIDList ( iPropertySize );
|
||||||
|
|
||||||
|
AudioObjectGetPropertyData ( audioInputDevice[iDriverIdx],
|
||||||
|
&stPropertyAddress,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
&iPropertySize,
|
||||||
|
&vInputStreamIDList[0] );
|
||||||
|
|
||||||
|
const AudioStreamID inputStreamID = vInputStreamIDList[0];
|
||||||
|
|
||||||
|
// get the stream ID of the output device (at least one stream must always exist)
|
||||||
|
iPropertySize = 0;
|
||||||
|
stPropertyAddress.mSelector = kAudioDevicePropertyStreams;
|
||||||
|
stPropertyAddress.mScope = kAudioObjectPropertyScopeOutput;
|
||||||
|
|
||||||
|
AudioObjectGetPropertyDataSize ( audioOutputDevice[iDriverIdx],
|
||||||
|
&stPropertyAddress,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
&iPropertySize );
|
||||||
|
|
||||||
|
CVector<AudioStreamID> vOutputStreamIDList ( iPropertySize );
|
||||||
|
|
||||||
AudioObjectGetPropertyData ( audioOutputDevice[iDriverIdx],
|
AudioObjectGetPropertyData ( audioOutputDevice[iDriverIdx],
|
||||||
|
&stPropertyAddress,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
&iPropertySize,
|
||||||
|
&vOutputStreamIDList[0] );
|
||||||
|
|
||||||
|
const AudioStreamID outputStreamID = vOutputStreamIDList[0];
|
||||||
|
|
||||||
|
// 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.".
|
||||||
|
// check the input
|
||||||
|
iPropertySize = sizeof ( AudioStreamBasicDescription );
|
||||||
|
stPropertyAddress.mSelector = kAudioStreamPropertyVirtualFormat;
|
||||||
|
stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
|
||||||
|
AudioObjectGetPropertyData ( inputStreamID,
|
||||||
&stPropertyAddress,
|
&stPropertyAddress,
|
||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
&iPropertySize,
|
&iPropertySize,
|
||||||
&CurDevStreamFormat );
|
&CurDevStreamFormat );
|
||||||
|
|
||||||
if ( ( CurDevStreamFormat.mFormatID != kAudioFormatLinearPCM ) ||
|
if ( ( CurDevStreamFormat.mFormatID != kAudioFormatLinearPCM ) ||
|
||||||
( CurDevStreamFormat.mFramesPerPacket != 1 ) ||
|
( CurDevStreamFormat.mFramesPerPacket != 1 ) ||
|
||||||
( CurDevStreamFormat.mBytesPerFrame != 8 ) ||
|
( CurDevStreamFormat.mBitsPerChannel != 32 ) ||
|
||||||
( CurDevStreamFormat.mBytesPerPacket != 8 ) ||
|
|
||||||
( CurDevStreamFormat.mChannelsPerFrame != 2 ) ||
|
|
||||||
( CurDevStreamFormat.mBitsPerChannel != 32 ) ||
|
|
||||||
( !( CurDevStreamFormat.mFormatFlags & kAudioFormatFlagIsFloat ) ) ||
|
( !( CurDevStreamFormat.mFormatFlags & kAudioFormatFlagIsFloat ) ) ||
|
||||||
( !( CurDevStreamFormat.mFormatFlags & kAudioFormatFlagIsPacked ) ) )
|
( !( CurDevStreamFormat.mFormatFlags & kAudioFormatFlagIsPacked ) ) )
|
||||||
{
|
{
|
||||||
return QString ( tr ( "The audio stream format for this audio device is "
|
return tr ( "The audio input stream format for this audio device is "
|
||||||
"not compatible with the requirements. Maybe the "
|
"not compatible with this software." );
|
||||||
"number of channels is incompatible, e.g., if the "
|
|
||||||
"device is a mono device or has too many channels. "
|
|
||||||
"This software requires a stereo device.") );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// store the input number of channels for this stream
|
||||||
// TODO check input device, too!
|
iNumInChan = CurDevStreamFormat.mChannelsPerFrame;
|
||||||
// It seems that all queried values are zero somehow so the check does not work...
|
|
||||||
AudioObjectGetPropertyData ( audioInputDevice[iDriverIdx],
|
|
||||||
&stPropertyAddress,
|
|
||||||
0,
|
|
||||||
NULL,
|
|
||||||
&iPropertySize,
|
|
||||||
&CurDevStreamFormat );
|
|
||||||
|
|
||||||
qDebug() << "mBitsPerChannel" << CurDevStreamFormat.mBitsPerChannel;
|
// check the output
|
||||||
qDebug() << "mBytesPerFrame" << CurDevStreamFormat.mBytesPerFrame;
|
AudioObjectGetPropertyData ( outputStreamID,
|
||||||
qDebug() << "mBytesPerPacket" << CurDevStreamFormat.mBytesPerPacket;
|
&stPropertyAddress,
|
||||||
qDebug() << "mChannelsPerFrame" << CurDevStreamFormat.mChannelsPerFrame;
|
0,
|
||||||
qDebug() << "mFramesPerPacket" << CurDevStreamFormat.mFramesPerPacket;
|
NULL,
|
||||||
qDebug() << "mSampleRate" << CurDevStreamFormat.mSampleRate;
|
&iPropertySize,
|
||||||
*/
|
&CurDevStreamFormat );
|
||||||
|
|
||||||
|
if ( ( CurDevStreamFormat.mFormatID != kAudioFormatLinearPCM ) ||
|
||||||
|
( CurDevStreamFormat.mFramesPerPacket != 1 ) ||
|
||||||
|
( CurDevStreamFormat.mBitsPerChannel != 32 ) ||
|
||||||
|
( !( CurDevStreamFormat.mFormatFlags & kAudioFormatFlagIsFloat ) ) ||
|
||||||
|
( !( CurDevStreamFormat.mFormatFlags & kAudioFormatFlagIsPacked ) ) )
|
||||||
|
{
|
||||||
|
return tr ( "The audio output stream format for this audio device is "
|
||||||
|
"not compatible with this software." );
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the output number of channels for this stream
|
||||||
|
iNumOutChan = CurDevStreamFormat.mChannelsPerFrame;
|
||||||
|
|
||||||
|
// clip the number of input/output channels to our allowed maximum
|
||||||
|
if ( iNumInChan > MAX_NUM_IN_OUT_CHANNELS )
|
||||||
|
{
|
||||||
|
iNumInChan = MAX_NUM_IN_OUT_CHANNELS;
|
||||||
|
}
|
||||||
|
if ( iNumOutChan > MAX_NUM_IN_OUT_CHANNELS )
|
||||||
|
{
|
||||||
|
iNumOutChan = MAX_NUM_IN_OUT_CHANNELS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the channel names of the input device
|
||||||
|
for ( int iCurInCH = 0; iCurInCH < iNumInChan; iCurInCH++ )
|
||||||
|
{
|
||||||
|
CFStringRef sPropertyStringValue;
|
||||||
|
|
||||||
|
stPropertyAddress.mSelector = kAudioObjectPropertyElementName;
|
||||||
|
stPropertyAddress.mElement = iCurInCH + 1;
|
||||||
|
stPropertyAddress.mScope = kAudioObjectPropertyScopeInput;
|
||||||
|
iPropertySize = sizeof ( CFStringRef );
|
||||||
|
|
||||||
|
AudioObjectGetPropertyData ( audioInputDevice[iDriverIdx],
|
||||||
|
&stPropertyAddress,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
&iPropertySize,
|
||||||
|
&sPropertyStringValue );
|
||||||
|
|
||||||
|
// convert string
|
||||||
|
const bool bConvOK = ConvertCFStringToQString ( sPropertyStringValue,
|
||||||
|
sChannelNamesInput[iCurInCH] );
|
||||||
|
|
||||||
|
// use a defalut name in case there was an error or the name is empty
|
||||||
|
if ( !bConvOK || ( iPropertySize == 0 ) )
|
||||||
|
{
|
||||||
|
sChannelNamesInput[iCurInCH] =
|
||||||
|
QString ( "Channel %1" ).arg ( iCurInCH + 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the channel names of the output device
|
||||||
|
for ( int iCurOutCH = 0; iCurOutCH < iNumOutChan; iCurOutCH++ )
|
||||||
|
{
|
||||||
|
CFStringRef sPropertyStringValue;
|
||||||
|
|
||||||
|
stPropertyAddress.mSelector = kAudioObjectPropertyElementName;
|
||||||
|
stPropertyAddress.mElement = iCurOutCH + 1;
|
||||||
|
stPropertyAddress.mScope = kAudioObjectPropertyScopeOutput;
|
||||||
|
iPropertySize = sizeof ( CFStringRef );
|
||||||
|
|
||||||
|
AudioObjectGetPropertyData ( audioOutputDevice[iDriverIdx],
|
||||||
|
&stPropertyAddress,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
&iPropertySize,
|
||||||
|
&sPropertyStringValue );
|
||||||
|
|
||||||
|
// convert string
|
||||||
|
const bool bConvOK = ConvertCFStringToQString ( sPropertyStringValue,
|
||||||
|
sChannelNamesOutput[iCurOutCH] );
|
||||||
|
|
||||||
|
// use a defalut name in case there was an error or the name is empty
|
||||||
|
if ( !bConvOK || ( iPropertySize == 0 ) )
|
||||||
|
{
|
||||||
|
sChannelNamesOutput[iCurOutCH] =
|
||||||
|
QString ( "Channel %1" ).arg ( iCurOutCH + 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// everything is ok, return empty string for "no error" case
|
// everything is ok, return empty string for "no error" case
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSound::SetLeftInputChannel ( const int iNewChan )
|
||||||
|
{
|
||||||
|
// apply parameter after input parameter check
|
||||||
|
if ( ( iNewChan >= 0 ) && ( iNewChan < iNumInChan ) )
|
||||||
|
{
|
||||||
|
iSelInputLeftChannel = iNewChan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::SetRightInputChannel ( const int iNewChan )
|
||||||
|
{
|
||||||
|
// apply parameter after input parameter check
|
||||||
|
if ( ( iNewChan >= 0 ) && ( iNewChan < iNumInChan ) )
|
||||||
|
{
|
||||||
|
iSelInputRightChannel = iNewChan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::SetLeftOutputChannel ( const int iNewChan )
|
||||||
|
{
|
||||||
|
// apply parameter after input parameter check
|
||||||
|
if ( ( iNewChan >= 0 ) && ( iNewChan < iNumOutChan ) )
|
||||||
|
{
|
||||||
|
iSelOutputLeftChannel = iNewChan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSound::SetRightOutputChannel ( const int iNewChan )
|
||||||
|
{
|
||||||
|
// apply parameter after input parameter check
|
||||||
|
if ( ( iNewChan >= 0 ) && ( iNewChan < iNumOutChan ) )
|
||||||
|
{
|
||||||
|
iSelOutputRightChannel = iNewChan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CSound::Start()
|
void CSound::Start()
|
||||||
{
|
{
|
||||||
// setup callback for xruns (only for input is enough)
|
// setup callback for xruns (only for input is enough)
|
||||||
|
@ -553,20 +699,33 @@ OSStatus CSound::callbackIO ( AudioDeviceID inDevice,
|
||||||
// both, the input and output device use the same callback function
|
// both, the input and output device use the same callback function
|
||||||
QMutexLocker locker ( &pSound->Mutex );
|
QMutexLocker locker ( &pSound->Mutex );
|
||||||
|
|
||||||
|
const int iCoreAudioBufferSizeMono = pSound->iCoreAudioBufferSizeMono;
|
||||||
|
const int iNumInChan = pSound->iNumInChan;
|
||||||
|
const int iNumOutChan = pSound->iNumOutChan;
|
||||||
|
const int iSelInputLeftChannel = pSound->iSelInputLeftChannel;
|
||||||
|
const int iSelInputRightChannel = pSound->iSelInputRightChannel;
|
||||||
|
const int iSelOutputLeftChannel = pSound->iSelOutputLeftChannel;
|
||||||
|
const int iSelOutputRightChannel = pSound->iSelOutputRightChannel;
|
||||||
|
|
||||||
if ( inDevice == pSound->CurrentAudioInputDeviceID )
|
if ( inDevice == pSound->CurrentAudioInputDeviceID )
|
||||||
{
|
{
|
||||||
// check size (float32 has four bytes)
|
// check size (float32 has four bytes)
|
||||||
if ( inInputData->mBuffers[0].mDataByteSize ==
|
if ( inInputData->mBuffers[0].mDataByteSize ==
|
||||||
static_cast<UInt32> ( pSound->iCoreAudioBufferSizeStereo * 4 ) )
|
static_cast<UInt32> ( iCoreAudioBufferSizeMono * iNumInChan * 4 ) )
|
||||||
{
|
{
|
||||||
// get a pointer to the input data of the correct type
|
// get a pointer to the input data of the correct type
|
||||||
Float32* pInData = static_cast<Float32*> ( inInputData->mBuffers[0].mData );
|
Float32* pInData = static_cast<Float32*> ( inInputData->mBuffers[0].mData );
|
||||||
|
|
||||||
// copy input data
|
// copy input data
|
||||||
for ( int i = 0; i < pSound->iCoreAudioBufferSizeStereo; i++ )
|
for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ )
|
||||||
{
|
{
|
||||||
pSound->vecsTmpAudioSndCrdStereo[i] =
|
// left
|
||||||
(short) ( pInData[i] * _MAXSHORT );
|
pSound->vecsTmpAudioSndCrdStereo[2 * i] =
|
||||||
|
(short) ( pInData[iNumInChan * i + iSelInputLeftChannel] * _MAXSHORT );
|
||||||
|
|
||||||
|
// right
|
||||||
|
pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] =
|
||||||
|
(short) ( pInData[iNumInChan * i + iSelInputRightChannel] * _MAXSHORT );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -578,23 +737,53 @@ OSStatus CSound::callbackIO ( AudioDeviceID inDevice,
|
||||||
// call processing callback function
|
// call processing callback function
|
||||||
pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo );
|
pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo );
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if ( inDevice == pSound->CurrentAudioOutputDeviceID )
|
||||||
{
|
{
|
||||||
// check size (float32 has four bytes)
|
// check size (float32 has four bytes)
|
||||||
if ( outOutputData->mBuffers[0].mDataByteSize ==
|
if ( outOutputData->mBuffers[0].mDataByteSize ==
|
||||||
static_cast<UInt32> ( pSound->iCoreAudioBufferSizeStereo * 4 ) )
|
static_cast<UInt32> ( iCoreAudioBufferSizeMono * iNumOutChan * 4 ) )
|
||||||
{
|
{
|
||||||
// get a pointer to the input data of the correct type
|
// get a pointer to the input data of the correct type
|
||||||
Float32* pOutData = static_cast<Float32*> ( outOutputData->mBuffers[0].mData );
|
Float32* pOutData = static_cast<Float32*> ( outOutputData->mBuffers[0].mData );
|
||||||
|
|
||||||
// copy output data
|
// copy output data
|
||||||
for ( int i = 0; i < pSound->iCoreAudioBufferSizeStereo; i++ )
|
for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ )
|
||||||
{
|
{
|
||||||
pOutData[i] =
|
// left
|
||||||
(Float32) pSound->vecsTmpAudioSndCrdStereo[i] / _MAXSHORT;
|
pOutData[iNumOutChan * i + iSelOutputLeftChannel] =
|
||||||
|
(Float32) pSound->vecsTmpAudioSndCrdStereo[2 * i] / _MAXSHORT;
|
||||||
|
|
||||||
|
// right
|
||||||
|
pOutData[iNumOutChan * i + iSelOutputRightChannel] =
|
||||||
|
(Float32) pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] / _MAXSHORT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return kAudioHardwareNoError;
|
return kAudioHardwareNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CSound::ConvertCFStringToQString ( const CFStringRef stringRef, QString& sOut )
|
||||||
|
{
|
||||||
|
// first check if the string is not empty
|
||||||
|
if ( CFStringGetLength ( stringRef ) > 0 )
|
||||||
|
{
|
||||||
|
// convert CFString in c-string (quick hack!) and then in QString
|
||||||
|
char* sC_strPropValue =
|
||||||
|
(char*) malloc ( CFStringGetLength ( stringRef ) * 3 + 1 );
|
||||||
|
|
||||||
|
if ( CFStringGetCString ( stringRef,
|
||||||
|
sC_strPropValue,
|
||||||
|
CFStringGetLength ( stringRef ) * 3 + 1,
|
||||||
|
kCFStringEncodingUTF8 ) )
|
||||||
|
{
|
||||||
|
sOut = sC_strPropValue;
|
||||||
|
free ( sC_strPropValue );
|
||||||
|
|
||||||
|
return true; // OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // not OK
|
||||||
|
}
|
||||||
|
|
27
mac/sound.h
27
mac/sound.h
|
@ -41,12 +41,34 @@ public:
|
||||||
virtual void Start();
|
virtual void Start();
|
||||||
virtual void Stop();
|
virtual void Stop();
|
||||||
|
|
||||||
|
// channel selection
|
||||||
|
virtual int GetNumInputChannels() { return iNumInChan; }
|
||||||
|
virtual QString GetInputChannelName ( const int iDiD ) { return sChannelNamesInput[iDiD]; }
|
||||||
|
virtual void SetLeftInputChannel ( const int iNewChan );
|
||||||
|
virtual void SetRightInputChannel ( const int iNewChan );
|
||||||
|
virtual int GetLeftInputChannel() { return iSelInputLeftChannel; }
|
||||||
|
virtual int GetRightInputChannel() { return iSelInputRightChannel; }
|
||||||
|
|
||||||
|
virtual int GetNumOutputChannels() { return iNumOutChan; }
|
||||||
|
virtual QString GetOutputChannelName ( const int iDiD ) { return sChannelNamesOutput[iDiD]; }
|
||||||
|
virtual void SetLeftOutputChannel ( const int iNewChan );
|
||||||
|
virtual void SetRightOutputChannel ( const int iNewChan );
|
||||||
|
virtual int GetLeftOutputChannel() { return iSelOutputLeftChannel; }
|
||||||
|
virtual int GetRightOutputChannel() { return iSelOutputRightChannel; }
|
||||||
|
|
||||||
// these variables should be protected but cannot since we want
|
// these variables should be protected but cannot since we want
|
||||||
// to access them from the callback function
|
// to access them from the callback function
|
||||||
CVector<short> vecsTmpAudioSndCrdStereo;
|
CVector<short> vecsTmpAudioSndCrdStereo;
|
||||||
int iCoreAudioBufferSizeMono;
|
int iCoreAudioBufferSizeMono;
|
||||||
int iCoreAudioBufferSizeStereo;
|
int iCoreAudioBufferSizeStereo;
|
||||||
AudioDeviceID CurrentAudioInputDeviceID;
|
AudioDeviceID CurrentAudioInputDeviceID;
|
||||||
|
AudioDeviceID CurrentAudioOutputDeviceID;
|
||||||
|
int iNumInChan;
|
||||||
|
int iNumOutChan;
|
||||||
|
int iSelInputLeftChannel;
|
||||||
|
int iSelInputRightChannel;
|
||||||
|
int iSelOutputLeftChannel;
|
||||||
|
int iSelOutputRightChannel;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual QString LoadAndInitializeDriver ( int iIdx );
|
virtual QString LoadAndInitializeDriver ( int iIdx );
|
||||||
|
@ -62,6 +84,8 @@ protected:
|
||||||
bool& bIsInput,
|
bool& bIsInput,
|
||||||
bool& bIsOutput );
|
bool& bIsOutput );
|
||||||
|
|
||||||
|
bool ConvertCFStringToQString ( const CFStringRef stringRef, QString& sOut );
|
||||||
|
|
||||||
// callbacks
|
// callbacks
|
||||||
static OSStatus deviceNotification ( AudioDeviceID,
|
static OSStatus deviceNotification ( AudioDeviceID,
|
||||||
UInt32,
|
UInt32,
|
||||||
|
@ -81,6 +105,9 @@ protected:
|
||||||
AudioDeviceIOProcID audioInputProcID;
|
AudioDeviceIOProcID audioInputProcID;
|
||||||
AudioDeviceIOProcID audioOutputProcID;
|
AudioDeviceIOProcID audioOutputProcID;
|
||||||
|
|
||||||
|
QString sChannelNamesInput[MAX_NUM_IN_OUT_CHANNELS];
|
||||||
|
QString sChannelNamesOutput[MAX_NUM_IN_OUT_CHANNELS];
|
||||||
|
|
||||||
QMutex Mutex;
|
QMutex Mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -497,7 +497,7 @@ void CClientSettingsDlg::UpdateSoundCardFrame()
|
||||||
|
|
||||||
void CClientSettingsDlg::UpdateSoundChannelSelectionFrame()
|
void CClientSettingsDlg::UpdateSoundChannelSelectionFrame()
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#if defined ( _WIN32 ) || defined ( __APPLE__ ) || defined ( __MACOSX )
|
||||||
int iSndChanIdx;
|
int iSndChanIdx;
|
||||||
|
|
||||||
// Definition: The channel selection frame shall only be visible,
|
// Definition: The channel selection frame shall only be visible,
|
||||||
|
|
Loading…
Reference in a new issue