Attempt to handle devices other than the built-in audio correctly. Not tested on anything but the built-in audio.
This commit is contained in:
@@ -136,18 +136,34 @@ RString RageSound_CA::Init()
|
|||||||
LOG->Info( "Audio device manufacturer: %s", str );
|
LOG->Info( "Audio device manufacturer: %s", str );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the stereo channels
|
||||||
|
UInt32 channels[2];
|
||||||
|
|
||||||
|
size = sizeof( channels );
|
||||||
|
if( (error = AudioDeviceGetProperty(m_OutputDevice, 0, kAudioDeviceSectionOutput,
|
||||||
|
kAudioDevicePropertyPreferredChannelsForStereo, &size, channels)) )
|
||||||
|
{
|
||||||
|
LOG->Warn( WERROR( "Couldn't get the preferred stereo channels", error) );
|
||||||
|
channels[0] = channels[1] = 0; // Master channel
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG->Info( "Preferred stereo channels { %lu, %lu }.", channels[0], channels[1] );
|
||||||
|
}
|
||||||
|
UInt32 iFirstChannel = min( channels[0], channels[1] );
|
||||||
|
|
||||||
m_iSampleRate = PREFSMAN->m_iSoundPreferredSampleRate;
|
m_iSampleRate = PREFSMAN->m_iSoundPreferredSampleRate;
|
||||||
Float64 nominalSampleRate = m_iSampleRate;
|
Float64 nominalSampleRate = m_iSampleRate;
|
||||||
Float64 actualSampleRate;
|
Float64 actualSampleRate;
|
||||||
|
|
||||||
if( (error = AudioDeviceSetProperty(m_OutputDevice, NULL, 0, kAudioDeviceSectionGlobal,
|
if( (error = AudioDeviceSetProperty(m_OutputDevice, NULL, iFirstChannel, kAudioDeviceSectionGlobal,
|
||||||
kAudioDevicePropertyNominalSampleRate, sizeof(Float64), &nominalSampleRate)) )
|
kAudioDevicePropertyNominalSampleRate, sizeof(Float64), &nominalSampleRate)) )
|
||||||
{
|
{
|
||||||
LOG->Warn( WERROR("Couldn't set the nominal sample rate", error) );
|
LOG->Warn( WERROR("Couldn't set the nominal sample rate", error) );
|
||||||
nominalSampleRate = 0.0;
|
nominalSampleRate = 0.0;
|
||||||
}
|
}
|
||||||
size = sizeof(Float64);
|
size = sizeof(Float64);
|
||||||
if( (error = AudioDeviceGetProperty(m_OutputDevice, 0, kAudioDeviceSectionGlobal,
|
if( (error = AudioDeviceGetProperty(m_OutputDevice, iFirstChannel, kAudioDeviceSectionGlobal,
|
||||||
kAudioDevicePropertyNominalSampleRate, &size, &actualSampleRate)) )
|
kAudioDevicePropertyNominalSampleRate, &size, &actualSampleRate)) )
|
||||||
{
|
{
|
||||||
return ERROR( "Couldn't get the nominal sample rate", error );
|
return ERROR( "Couldn't get the nominal sample rate", error );
|
||||||
@@ -164,7 +180,7 @@ RString RageSound_CA::Init()
|
|||||||
|
|
||||||
AudioStreamID *streams, stream;
|
AudioStreamID *streams, stream;
|
||||||
|
|
||||||
if( (error = AudioDeviceGetPropertyInfo(m_OutputDevice, 0, kAudioDeviceSectionOutput,
|
if( (error = AudioDeviceGetPropertyInfo(m_OutputDevice, iFirstChannel, kAudioDeviceSectionOutput,
|
||||||
kAudioDevicePropertyStreams, &size, NULL)) )
|
kAudioDevicePropertyStreams, &size, NULL)) )
|
||||||
{
|
{
|
||||||
return ERROR( "Couldn't get the stream size", error );
|
return ERROR( "Couldn't get the stream size", error );
|
||||||
@@ -172,13 +188,35 @@ RString RageSound_CA::Init()
|
|||||||
if( size == 0 )
|
if( size == 0 )
|
||||||
return "No streams.";
|
return "No streams.";
|
||||||
streams = new AudioStreamID[size/sizeof(AudioStreamID)];
|
streams = new AudioStreamID[size/sizeof(AudioStreamID)];
|
||||||
if( (error = AudioDeviceGetProperty(m_OutputDevice, 0, kAudioDeviceSectionOutput,
|
if( (error = AudioDeviceGetProperty(m_OutputDevice, iFirstChannel, kAudioDeviceSectionOutput,
|
||||||
kAudioDevicePropertyStreams, &size, streams)) )
|
kAudioDevicePropertyStreams, &size, streams)) )
|
||||||
{
|
{
|
||||||
delete[] streams;
|
delete[] streams;
|
||||||
return ERROR( "Couldn't get streams", error );
|
return ERROR( "Couldn't get streams", error );
|
||||||
}
|
}
|
||||||
stream = *min_element( streams, streams + size/sizeof(AudioStreamID) );
|
stream = streams[0];
|
||||||
|
size = sizeof( UInt32 );
|
||||||
|
for( unsigned i = 0; i < size/sizeof(AudioStreamID); ++i )
|
||||||
|
{
|
||||||
|
UInt32 firstChannel;
|
||||||
|
|
||||||
|
// I'm not really sure why I'm doing this except it's what the c++ wrapper did, more or less.
|
||||||
|
if( streams[i] < stream )
|
||||||
|
stream = streams[i];
|
||||||
|
// Look for the matching stream.
|
||||||
|
if( (error = AudioStreamGetProperty(streams[i], 0, kAudioStreamPropertyStartingChannel,
|
||||||
|
&size, &firstChannel)) )
|
||||||
|
{
|
||||||
|
LOG->Warn( WERROR("Failed to get stream starting channel", error) );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if( firstChannel != iFirstChannel )
|
||||||
|
{
|
||||||
|
stream = streams[i];
|
||||||
|
LOG->Trace( "Found a stream with a matching channel." );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
delete[] streams;
|
delete[] streams;
|
||||||
|
|
||||||
AddListener( kAudioDeviceProcessorOverload, OverloadListener, "overload" );
|
AddListener( kAudioDeviceProcessorOverload, OverloadListener, "overload" );
|
||||||
@@ -202,11 +240,34 @@ RString RageSound_CA::Init()
|
|||||||
|
|
||||||
if( (error = AudioConverterNew(&SMFormat, &IOProcFormat, &m_Converter)) )
|
if( (error = AudioConverterNew(&SMFormat, &IOProcFormat, &m_Converter)) )
|
||||||
return ERROR( "Couldn't create the audio converter", error );
|
return ERROR( "Couldn't create the audio converter", error );
|
||||||
|
|
||||||
|
// Find which stream of the ones passed to the GetData should be used.
|
||||||
|
AudioBufferList abl;
|
||||||
|
|
||||||
|
size = sizeof(abl);
|
||||||
|
if( (error = AudioDeviceGetProperty(m_OutputDevice, 0, kAudioDeviceSectionOutput,
|
||||||
|
kAudioDevicePropertyStreamConfiguration, &size, &abl)) )
|
||||||
|
{
|
||||||
|
return ERROR( "Couldn't get the stream configuration", error );
|
||||||
|
}
|
||||||
|
|
||||||
|
m_iBufferNumber = 0;
|
||||||
|
for( UInt32 i = 0, channelCount = 0; i < abl.mNumberBuffers; ++i )
|
||||||
|
{
|
||||||
|
// 0 is the master channel so others start at 1 (I hope) so >= instead of >.
|
||||||
|
channelCount += abl.mBuffers[i].mNumberChannels;
|
||||||
|
if( channelCount >= iFirstChannel )
|
||||||
|
{
|
||||||
|
m_iBufferNumber = i;
|
||||||
|
LOG->Trace( "Found the correct buffer number: %lu", i );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UInt32 bufferSize;
|
UInt32 bufferSize;
|
||||||
|
|
||||||
size = sizeof(UInt32);
|
size = sizeof(UInt32);
|
||||||
if( (error = AudioDeviceGetProperty(m_OutputDevice, 0, kAudioDeviceSectionGlobal,
|
if( (error = AudioDeviceGetProperty(m_OutputDevice, iFirstChannel, kAudioDeviceSectionOutput,
|
||||||
kAudioDevicePropertyBufferFrameSize, &size, &bufferSize)) )
|
kAudioDevicePropertyBufferFrameSize, &size, &bufferSize)) )
|
||||||
{
|
{
|
||||||
LOG->Warn( WERROR("Couldn't determine buffer size", error) );
|
LOG->Warn( WERROR("Couldn't determine buffer size", error) );
|
||||||
@@ -220,14 +281,14 @@ RString RageSound_CA::Init()
|
|||||||
UInt32 frames;
|
UInt32 frames;
|
||||||
|
|
||||||
size = sizeof(UInt32);
|
size = sizeof(UInt32);
|
||||||
if( (error = AudioDeviceGetProperty(m_OutputDevice, 0, kAudioDeviceSectionOutput,
|
if( (error = AudioDeviceGetProperty(m_OutputDevice, iFirstChannel, kAudioDeviceSectionOutput,
|
||||||
kAudioDevicePropertyLatency, &size, &frames)) )
|
kAudioDevicePropertyLatency, &size, &frames)) )
|
||||||
{
|
{
|
||||||
return ERROR( "Couldn't get device latency", error );
|
return ERROR( "Couldn't get device latency", error );
|
||||||
}
|
}
|
||||||
bufferSize += frames;
|
bufferSize += frames;
|
||||||
size = sizeof(UInt32);
|
size = sizeof(UInt32);
|
||||||
if( (error = AudioDeviceGetProperty(m_OutputDevice, 0, kAudioDeviceSectionOutput,
|
if( (error = AudioDeviceGetProperty(m_OutputDevice, iFirstChannel, kAudioDeviceSectionOutput,
|
||||||
kAudioDevicePropertySafetyOffset, &size, &frames)) )
|
kAudioDevicePropertySafetyOffset, &size, &frames)) )
|
||||||
{
|
{
|
||||||
return ERROR( "Couldn't get device safety offset", error );
|
return ERROR( "Couldn't get device safety offset", error );
|
||||||
@@ -245,7 +306,7 @@ RString RageSound_CA::Init()
|
|||||||
}
|
}
|
||||||
m_fLatency = bufferSize / nominalSampleRate;
|
m_fLatency = bufferSize / nominalSampleRate;
|
||||||
LOG->Info( "Frames of latency: %lu\n"
|
LOG->Info( "Frames of latency: %lu\n"
|
||||||
"Seconds of latency: %f", frames, m_fLatency );
|
"Seconds of latency: %f", bufferSize, m_fLatency );
|
||||||
|
|
||||||
StartDecodeThread();
|
StartDecodeThread();
|
||||||
|
|
||||||
@@ -342,7 +403,7 @@ OSStatus RageSound_CA::GetData( AudioDeviceID inDevice,
|
|||||||
if( unlikely(This->m_pIOThread == NULL) )
|
if( unlikely(This->m_pIOThread == NULL) )
|
||||||
This->m_pIOThread = new RageThreadRegister( "HAL I/O thread" );
|
This->m_pIOThread = new RageThreadRegister( "HAL I/O thread" );
|
||||||
|
|
||||||
AudioBuffer& buf = outOutputData->mBuffers[0];
|
AudioBuffer& buf = outOutputData->mBuffers[This->m_iBufferNumber];
|
||||||
UInt32 dataPackets = buf.mDataByteSize >> 3; // 8 byes per packet
|
UInt32 dataPackets = buf.mDataByteSize >> 3; // 8 byes per packet
|
||||||
int64_t decodePos = int64_t( inOutputTime->mSampleTime ) + This->m_iOffset;
|
int64_t decodePos = int64_t( inOutputTime->mSampleTime ) + This->m_iOffset;
|
||||||
int64_t now = int64_t( inNow->mSampleTime ) + This->m_iOffset;
|
int64_t now = int64_t( inNow->mSampleTime ) + This->m_iOffset;
|
||||||
@@ -358,7 +419,14 @@ OSStatus RageSound_CA::GetData( AudioDeviceID inDevice,
|
|||||||
|
|
||||||
AudioConverterConvertBuffer( This->m_Converter, dataPackets * kBytesPerPacket,
|
AudioConverterConvertBuffer( This->m_Converter, dataPackets * kBytesPerPacket,
|
||||||
buffer, &buf.mDataByteSize, buf.mData );
|
buffer, &buf.mDataByteSize, buf.mData );
|
||||||
|
|
||||||
|
// Zero other buffers. Not sure if this is needed, but I suspect it is.
|
||||||
|
for( unsigned i = 0; i < outOutputData->mNumberBuffers; ++i )
|
||||||
|
{
|
||||||
|
if( i == This->m_iBufferNumber )
|
||||||
|
continue;
|
||||||
|
memset( outOutputData->mBuffers[i].mData, 0, outOutputData->mBuffers[i].mDataByteSize );
|
||||||
|
}
|
||||||
g_fLastIOProcTime = tm.GetDeltaTime();
|
g_fLastIOProcTime = tm.GetDeltaTime();
|
||||||
++g_iNumIOProcCalls;
|
++g_iNumIOProcCalls;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user