Remove old, buggy driver.
This commit is contained in:
@@ -1,535 +0,0 @@
|
||||
#include "global.h"
|
||||
#include "RageUtil.h"
|
||||
#include "RageSoundDriver_CA.h"
|
||||
#include "RageLog.h"
|
||||
#include "RageTimer.h"
|
||||
#include "PrefsManager.h"
|
||||
#include "archutils/Darwin/DarwinThreadHelpers.h"
|
||||
|
||||
REGISTER_SOUND_DRIVER_CLASS2( CoreAudio, CA );
|
||||
|
||||
static const UInt8 kAudioDeviceSectionInput = 0x01;
|
||||
static const UInt8 kAudioDeviceSectionOutput = 0x00;
|
||||
static const UInt8 kAudioDeviceSectionGlobal = 0x00;
|
||||
static const UInt8 kAudioDeviceSectionWildcard = 0xFF;
|
||||
|
||||
static const UInt32 kFramesPerPacket = 1;
|
||||
static const UInt32 kChannelsPerFrame = 2;
|
||||
static const UInt32 kBitsPerChannel = 16;
|
||||
static const UInt32 kBytesPerPacket = kChannelsPerFrame * kBitsPerChannel / 8;
|
||||
static const UInt32 kBytesPerFrame = kBytesPerPacket;
|
||||
static const UInt32 kFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger;
|
||||
|
||||
struct Desc : public AudioStreamBasicDescription
|
||||
{
|
||||
Desc( Float64 sampleRate, UInt32 formatID, UInt32 formatFlags, UInt32 bytesPerPacket,
|
||||
UInt32 framesPerPacket, UInt32 bytesPerFrame, UInt32 channelsPerFrame, UInt32 bitsPerChannel );
|
||||
};
|
||||
|
||||
Desc::Desc( Float64 sampleRate, UInt32 formatID, UInt32 formatFlags, UInt32 bytesPerPacket,
|
||||
UInt32 framesPerPacket, UInt32 bytesPerFrame, UInt32 channelsPerFrame, UInt32 bitsPerChannel )
|
||||
{
|
||||
mSampleRate = sampleRate;
|
||||
mFormatID = formatID;
|
||||
mFormatFlags = formatFlags;
|
||||
mBytesPerPacket = bytesPerPacket;
|
||||
mFramesPerPacket = framesPerPacket;
|
||||
mBytesPerFrame = bytesPerFrame;
|
||||
mChannelsPerFrame = channelsPerFrame;
|
||||
mBitsPerChannel = bitsPerChannel;
|
||||
mReserved = 0;
|
||||
}
|
||||
|
||||
/* temporary hack: */
|
||||
static float g_fLastIOProcTime = 0;
|
||||
static const int NUM_MIX_TIMES = 16;
|
||||
static float g_fLastMixTimes[NUM_MIX_TIMES];
|
||||
static int g_iLastMixTimePos = 0;
|
||||
static int g_iNumIOProcCalls = 0;
|
||||
|
||||
RageSoundDriver_CA::RageSoundDriver_CA() : m_fLatency(0.0f), m_Converter(NULL), m_bStarted(false), m_iSampleRate(0),
|
||||
m_iLastSampleTime(0), m_iOffset(0), m_pIOThread(NULL), m_pNotificationThread(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
static inline RString FourCCToString( uint32_t num )
|
||||
{
|
||||
RString s( 4, '?' );
|
||||
char c;
|
||||
|
||||
c = (num >> 24) & 0xFF;
|
||||
if( c >='\x20' && c <= '\x7e' )
|
||||
s[0] = c;
|
||||
c = (num >> 16) & 0xFF;
|
||||
if( c >='\x20' && c <= '\x7e' )
|
||||
s[1] = c;
|
||||
c = (num >> 8) & 0xFF;
|
||||
if( c >='\x20' && c <= '\x7e' )
|
||||
s[2] = c;
|
||||
c = num & 0xFF;
|
||||
if( c >= '\x20' && c <= '\x7e' )
|
||||
s[3] = c;
|
||||
|
||||
return s;
|
||||
}
|
||||
#define WERROR(str, num, extra...) str ": '%s' (%lu).", ## extra, FourCCToString(num).c_str(), (num)
|
||||
#define ERROR(str, num, extra...) (ssprintf(WERROR(str, (num), ## extra)))
|
||||
|
||||
RString RageSoundDriver_CA::Init()
|
||||
{
|
||||
OSStatus error;
|
||||
UInt32 size = sizeof(m_OutputDevice);
|
||||
|
||||
if( (error = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &m_OutputDevice)) )
|
||||
return ERROR( "Couldn't create default output device", error );
|
||||
|
||||
// Get the HAL's runloop and attach an observer
|
||||
{
|
||||
CFRunLoopRef runLoopRef;
|
||||
CFRunLoopObserverRef observerRef;
|
||||
CFRunLoopObserverContext context = { 0, this, NULL, NULL, NULL };
|
||||
|
||||
size = sizeof(CFRunLoopRef);
|
||||
if( (error = AudioHardwareGetProperty(kAudioHardwarePropertyRunLoop, &size, &runLoopRef)) )
|
||||
{
|
||||
LOG->Warn( WERROR("Couldn't get the HAL's run loop", error) );
|
||||
}
|
||||
else
|
||||
{
|
||||
observerRef = CFRunLoopObserverCreate( kCFAllocatorDefault, kCFRunLoopEntry,
|
||||
false, 0, NameHALThread, &context );
|
||||
CFRunLoopAddObserver( runLoopRef, observerRef, kCFRunLoopDefaultMode );
|
||||
}
|
||||
observerRef = CFRunLoopObserverCreate( kCFAllocatorDefault, kCFRunLoopEntry,
|
||||
false, 0, NameHALThread, &context );
|
||||
CFRunLoopAddObserver( runLoopRef, observerRef, kCFRunLoopDefaultMode );
|
||||
}
|
||||
|
||||
// Log the name and manufacturer
|
||||
{
|
||||
char str[256];
|
||||
CFStringRef ref;
|
||||
|
||||
str[0] = '\0';
|
||||
size = sizeof(CFStringRef);
|
||||
if( !AudioDeviceGetProperty(m_OutputDevice, 0, kAudioDeviceSectionGlobal,
|
||||
kAudioDevicePropertyDeviceNameCFString, &size, &ref) )
|
||||
{
|
||||
CFStringGetCString( ref, str, sizeof(str), kCFStringEncodingUTF8 );
|
||||
CFRelease( ref );
|
||||
}
|
||||
if( str[0] == '\0' )
|
||||
strcpy( str, "(unknown)" );
|
||||
|
||||
LOG->Info( "Audio device: %s", str );
|
||||
str[0] = '\0';
|
||||
size = sizeof(CFStringRef);
|
||||
if( !AudioDeviceGetProperty(m_OutputDevice, 0, kAudioDeviceSectionGlobal,
|
||||
kAudioDevicePropertyDeviceManufacturerCFString, &size, &ref) )
|
||||
{
|
||||
CFStringGetCString( ref, str, sizeof(str), kCFStringEncodingUTF8 );
|
||||
CFRelease( ref );
|
||||
}
|
||||
if( str[0] == '\0' )
|
||||
strcpy( str, "(unknown)" );
|
||||
LOG->Info( "Audio device manufacturer: %s", str );
|
||||
}
|
||||
|
||||
AddListener( kAudioDeviceProcessorOverload, OverloadListener, "overload" );
|
||||
AddListener( kAudioDevicePropertyDeviceHasChanged, DeviceChanged, "device changed" );
|
||||
AddListener( kAudioDevicePropertyJackIsConnected, JackChanged, "jack changed" );
|
||||
AddListener( kAudioDevicePropertyStreamConfiguration, DeviceChanged, "configuration changed" );
|
||||
AddListener( kAudioDevicePropertyPreferredChannelsForStereo, DeviceChanged, "preferred channels changed" );
|
||||
AddListener( kAudioHardwarePropertyDefaultOutputDevice, DeviceChanged, "default output device changed" );
|
||||
// AddListener( kAudioDevicePropertyStreamFormat, FormatChanged, "stream format changed" );
|
||||
|
||||
// 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->Trace( "Preferred stereo channels { %lu, %lu }.", channels[0], channels[1] );
|
||||
}
|
||||
UInt32 iFirstChannel = min( channels[0], channels[1] );
|
||||
|
||||
m_iSampleRate = PREFSMAN->m_iSoundPreferredSampleRate;
|
||||
if( m_iSampleRate == 0 )
|
||||
m_iSampleRate = 44100;
|
||||
Float64 nominalSampleRate = m_iSampleRate;
|
||||
Float64 actualSampleRate;
|
||||
|
||||
if( (error = AudioDeviceSetProperty(m_OutputDevice, NULL, iFirstChannel, kAudioDeviceSectionGlobal,
|
||||
kAudioDevicePropertyNominalSampleRate, sizeof(Float64), &nominalSampleRate)) )
|
||||
{
|
||||
LOG->Warn( WERROR("Couldn't set the nominal sample rate", error) );
|
||||
nominalSampleRate = 0.0;
|
||||
}
|
||||
size = sizeof(Float64);
|
||||
if( (error = AudioDeviceGetProperty(m_OutputDevice, iFirstChannel, kAudioDeviceSectionGlobal,
|
||||
kAudioDevicePropertyNominalSampleRate, &size, &actualSampleRate)) )
|
||||
{
|
||||
return ERROR( "Couldn't get the nominal sample rate", error );
|
||||
}
|
||||
if( actualSampleRate == nominalSampleRate )
|
||||
{
|
||||
LOG->Info( "Set the nominal sample rate to %f.", nominalSampleRate );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->Warn( "Reported nominal sample rate of %f.", actualSampleRate );
|
||||
m_iSampleRate = int( actualSampleRate );
|
||||
}
|
||||
|
||||
AudioStreamID *streams, stream;
|
||||
|
||||
if( (error = AudioDeviceGetPropertyInfo(m_OutputDevice, iFirstChannel, kAudioDeviceSectionOutput,
|
||||
kAudioDevicePropertyStreams, &size, NULL)) )
|
||||
{
|
||||
return ERROR( "Couldn't get the stream size", error );
|
||||
}
|
||||
if( size == 0 )
|
||||
return "No streams.";
|
||||
streams = new AudioStreamID[size/sizeof(AudioStreamID)];
|
||||
if( (error = AudioDeviceGetProperty(m_OutputDevice, iFirstChannel, kAudioDeviceSectionOutput,
|
||||
kAudioDevicePropertyStreams, &size, streams)) )
|
||||
{
|
||||
delete[] streams;
|
||||
return ERROR( "Couldn't get streams", error );
|
||||
}
|
||||
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;
|
||||
|
||||
// The canonical format
|
||||
Desc IOProcFormat( actualSampleRate, kAudioFormatLinearPCM, kAudioFormatFlagsNativeFloatPacked, 8, 1, 8, 2, 32 );
|
||||
const Desc SMFormat( actualSampleRate, kAudioFormatLinearPCM, kFormatFlags, kBytesPerPacket,
|
||||
kFramesPerPacket, kBytesPerFrame, kChannelsPerFrame, kBitsPerChannel );
|
||||
|
||||
if( (error = AudioStreamSetProperty(stream, NULL, 0, kAudioDevicePropertyStreamFormat,
|
||||
sizeof(Desc), &IOProcFormat)) )
|
||||
{
|
||||
LOG->Warn( WERROR("Couldn't set the IOProc format to the canonical format.", error) );
|
||||
size = sizeof(Desc);
|
||||
if( (error = AudioStreamGetProperty(stream, 0, kAudioDevicePropertyStreamFormat, &size, &IOProcFormat)) )
|
||||
return ERROR( "Couldn't get the IOProc format", error );
|
||||
}
|
||||
|
||||
if( (error = AudioConverterNew(&SMFormat, &IOProcFormat, &m_Converter)) )
|
||||
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;
|
||||
|
||||
size = sizeof(UInt32);
|
||||
if( (error = AudioDeviceGetProperty(m_OutputDevice, iFirstChannel, kAudioDeviceSectionOutput,
|
||||
kAudioDevicePropertyBufferFrameSize, &size, &bufferSize)) )
|
||||
{
|
||||
LOG->Warn( WERROR("Couldn't determine buffer size", error) );
|
||||
bufferSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG->Trace( "I/O buffer size: %lu frames.", bufferSize );
|
||||
}
|
||||
|
||||
UInt32 frames;
|
||||
|
||||
size = sizeof(UInt32);
|
||||
if( (error = AudioDeviceGetProperty(m_OutputDevice, iFirstChannel, kAudioDeviceSectionOutput,
|
||||
kAudioDevicePropertyLatency, &size, &frames)) )
|
||||
{
|
||||
return ERROR( "Couldn't get device latency", error );
|
||||
}
|
||||
bufferSize += frames;
|
||||
LOG->Trace( "Frames of device latency: %lu", frames );
|
||||
size = sizeof(UInt32);
|
||||
if( (error = AudioDeviceGetProperty(m_OutputDevice, iFirstChannel, kAudioDeviceSectionOutput,
|
||||
kAudioDevicePropertySafetyOffset, &size, &frames)) )
|
||||
{
|
||||
return ERROR( "Couldn't get device safety offset", error );
|
||||
}
|
||||
bufferSize += frames;
|
||||
LOG->Trace( "Frames of safety offset: %lu", frames );
|
||||
size = sizeof(UInt32);
|
||||
if( (error = AudioStreamGetProperty(stream, 0, kAudioDevicePropertyLatency, &size, &frames)) )
|
||||
{
|
||||
LOG->Warn( WERROR("Stream does not report latency", error) );
|
||||
}
|
||||
else
|
||||
{
|
||||
bufferSize += frames;
|
||||
LOG->Trace( "Frames of stream latency: %lu", frames );
|
||||
}
|
||||
m_fLatency = bufferSize / nominalSampleRate;
|
||||
LOG->Info( "Frames of latency: %lu\n"
|
||||
"Seconds of latency: %f", bufferSize, m_fLatency );
|
||||
|
||||
StartDecodeThread();
|
||||
|
||||
if( (error = AudioDeviceAddIOProc(m_OutputDevice, GetData, this)) )
|
||||
return ERROR( "Couldn't add the IOProc", error );
|
||||
if( (error = AudioDeviceStart(m_OutputDevice, GetData)) )
|
||||
{
|
||||
AudioDeviceRemoveIOProc( m_OutputDevice, GetData );
|
||||
return ERROR( "Couldn't start the IOProc", error );
|
||||
}
|
||||
m_bStarted = true;
|
||||
return RString();
|
||||
}
|
||||
|
||||
RageSoundDriver_CA::~RageSoundDriver_CA()
|
||||
{
|
||||
if( m_bStarted )
|
||||
{
|
||||
AudioDeviceStop( m_OutputDevice, GetData );
|
||||
AudioDeviceRemoveIOProc( m_OutputDevice, GetData );
|
||||
RemoveListeners();
|
||||
delete m_pIOThread;
|
||||
delete m_pNotificationThread;
|
||||
}
|
||||
|
||||
if( m_Converter != NULL )
|
||||
AudioConverterDispose( m_Converter );
|
||||
AudioHardwareUnload();
|
||||
}
|
||||
|
||||
void RageSoundDriver_CA::AddListener( AudioDevicePropertyID propertyID, AudioDevicePropertyListenerProc handler,
|
||||
const char *name )
|
||||
{
|
||||
OSStatus error;
|
||||
|
||||
if( (error = AudioDeviceAddPropertyListener(m_OutputDevice, kAudioPropertyWildcardChannel,
|
||||
kAudioDeviceSectionOutput, propertyID, handler, this)) )
|
||||
{
|
||||
LOG->Warn( WERROR("Couldn't install %s listener", error, name) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vPropertyListeners.push_back( pair<AudioDevicePropertyID,
|
||||
AudioDevicePropertyListenerProc>(propertyID, handler) );
|
||||
}
|
||||
}
|
||||
|
||||
void RageSoundDriver_CA::RemoveListeners()
|
||||
{
|
||||
while( m_vPropertyListeners.size() )
|
||||
{
|
||||
pair<AudioHardwarePropertyID, AudioDevicePropertyListenerProc>& p = m_vPropertyListeners.back();
|
||||
|
||||
AudioDeviceRemovePropertyListener( m_OutputDevice, kAudioPropertyWildcardChannel,
|
||||
kAudioDeviceSectionOutput, p.first, p.second );
|
||||
|
||||
m_vPropertyListeners.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t RageSoundDriver_CA::GetPosition() const
|
||||
{
|
||||
AudioTimeStamp time;
|
||||
OSStatus error;
|
||||
|
||||
if( (error = AudioDeviceGetCurrentTime(m_OutputDevice, &time)) )
|
||||
{
|
||||
if( error != kAudioHardwareNotRunningError )
|
||||
FAIL_M( ERROR("GetCurrentTime() failed", error) );
|
||||
return m_iLastSampleTime;
|
||||
}
|
||||
m_iLastSampleTime = int64_t( time.mSampleTime ) + m_iOffset;
|
||||
return m_iLastSampleTime;
|
||||
}
|
||||
|
||||
void RageSoundDriver_CA::NameHALThread( CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info )
|
||||
{
|
||||
RageSoundDriver_CA *This = (RageSoundDriver_CA *)info;
|
||||
|
||||
This->m_pNotificationThread = new RageThreadRegister( "HAL notification thread" );
|
||||
|
||||
// Remove and release the observer.
|
||||
CFRunLoopObserverInvalidate( observer );
|
||||
CFRelease( observer );
|
||||
}
|
||||
|
||||
OSStatus RageSoundDriver_CA::GetData( AudioDeviceID inDevice,
|
||||
const AudioTimeStamp *inNow,
|
||||
const AudioBufferList *inInputData,
|
||||
const AudioTimeStamp *inInputTime,
|
||||
AudioBufferList *outOutputData,
|
||||
const AudioTimeStamp *inOutputTime,
|
||||
void *inClientData )
|
||||
{
|
||||
RageTimer tm;
|
||||
RageSoundDriver_CA *This = (RageSoundDriver_CA *)inClientData;
|
||||
|
||||
if( unlikely(This->m_pIOThread == NULL) )
|
||||
This->m_pIOThread = new RageThreadRegister( "HAL I/O thread" );
|
||||
|
||||
AudioBuffer& buf = outOutputData->mBuffers[This->m_iBufferNumber];
|
||||
UInt32 dataPackets = buf.mDataByteSize >> 3; // 8 byes per packet
|
||||
int64_t decodePos = int64_t( inOutputTime->mSampleTime ) + This->m_iOffset;
|
||||
int64_t now = int64_t( inNow->mSampleTime ) + This->m_iOffset;
|
||||
|
||||
This->m_iLastSampleTime = now;
|
||||
RageTimer tm2;
|
||||
int16_t buffer[dataPackets * (kBytesPerPacket >> 1)];
|
||||
|
||||
This->Mix( buffer, dataPackets, decodePos, now) ;
|
||||
g_fLastMixTimes[g_iLastMixTimePos] = tm2.GetDeltaTime();
|
||||
++g_iLastMixTimePos;
|
||||
wrap( g_iLastMixTimePos, NUM_MIX_TIMES );
|
||||
|
||||
AudioConverterConvertBuffer( This->m_Converter, dataPackets * kBytesPerPacket,
|
||||
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_iNumIOProcCalls;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
|
||||
OSStatus RageSoundDriver_CA::OverloadListener( AudioDeviceID inDevice,
|
||||
UInt32 inChannel,
|
||||
Boolean isInput,
|
||||
AudioDevicePropertyID inPropertyID,
|
||||
void *inData )
|
||||
{
|
||||
if( isInput )
|
||||
return noErr;
|
||||
RString Output;
|
||||
for( int i = NUM_MIX_TIMES-1; i >= 0; --i )
|
||||
{
|
||||
int pos = (g_iLastMixTimePos+i) % NUM_MIX_TIMES;
|
||||
Output += ssprintf( "%.3f ", g_fLastMixTimes[pos] );
|
||||
}
|
||||
|
||||
LOG->Warn( "Audio overload. Last IOProc time: %f IOProc calls: %i (%s)",
|
||||
g_fLastIOProcTime, g_iNumIOProcCalls, Output.c_str() );
|
||||
g_iNumIOProcCalls = 0;
|
||||
return noErr;
|
||||
}
|
||||
|
||||
OSStatus RageSoundDriver_CA::DeviceChanged( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput,
|
||||
AudioDevicePropertyID inPropertyID, void *inData )
|
||||
{
|
||||
if( isInput )
|
||||
return noErr;
|
||||
LOG->Trace( "Device configuration changed." );
|
||||
return noErr;
|
||||
}
|
||||
|
||||
OSStatus RageSoundDriver_CA::JackChanged( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput,
|
||||
AudioDevicePropertyID inPropertyID, void *inData )
|
||||
{
|
||||
if( isInput )
|
||||
return noErr;
|
||||
RageSoundDriver_CA *This = (RageSoundDriver_CA *)inData;
|
||||
UInt32 result;
|
||||
UInt32 size = sizeof( result );
|
||||
AudioTimeStamp time;
|
||||
OSStatus error;
|
||||
|
||||
AudioDeviceGetProperty( inDevice, inChannel, 0, inPropertyID, &size, &result );
|
||||
CHECKPOINT_M( ssprintf("Channel %lu's has %s plugged into its jack.", inChannel,
|
||||
result ? "something" : "nothing") );
|
||||
|
||||
if( (error = AudioDeviceGetCurrentTime(inDevice, &time)) )
|
||||
{
|
||||
if( error != kAudioHardwareNotRunningError )
|
||||
FAIL_M( ERROR("Couldn't get current time when jack changed", error) );
|
||||
This->m_iOffset = This->m_iLastSampleTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
This->m_iOffset = This->m_iLastSampleTime - int64_t( time.mSampleTime );
|
||||
}
|
||||
return noErr;
|
||||
}
|
||||
|
||||
|
||||
void RageSoundDriver_CA::SetupDecodingThread()
|
||||
{
|
||||
/* Increase the scheduling precedence of the decoder thread. */
|
||||
SetThreadPrecedence( 0.75f );
|
||||
}
|
||||
|
||||
/*
|
||||
* (c) 2004-2006 Steve Checkoway
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
@@ -1,80 +0,0 @@
|
||||
#ifndef RAGE_SOUND_DRIVER_CA_H
|
||||
#define RAGE_SOUND_DRIVER_CA_H
|
||||
|
||||
#include "RageSoundDriver_Generic_Software.h"
|
||||
#include <vector>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
class RageSoundBase;
|
||||
class RageThreadRegister;
|
||||
|
||||
class RageSoundDriver_CA : public RageSound_Generic_Software
|
||||
{
|
||||
public:
|
||||
RageSoundDriver_CA();
|
||||
RString Init();
|
||||
~RageSoundDriver_CA();
|
||||
float GetPlayLatency() const { return m_fLatency; }
|
||||
int GetSampleRate() const { return m_iSampleRate; }
|
||||
int64_t GetPosition() const;
|
||||
void SetupDecodingThread();
|
||||
|
||||
private:
|
||||
float m_fLatency;
|
||||
AudioDeviceID m_OutputDevice;
|
||||
AudioConverterRef m_Converter;
|
||||
bool m_bStarted;
|
||||
int m_iSampleRate;
|
||||
mutable int64_t m_iLastSampleTime;
|
||||
int64_t m_iOffset;
|
||||
UInt32 m_iBufferNumber;
|
||||
vector<pair<AudioDevicePropertyID, AudioDevicePropertyListenerProc> > m_vPropertyListeners;
|
||||
RageThreadRegister *m_pIOThread, *m_pNotificationThread;
|
||||
|
||||
void AddListener( AudioDevicePropertyID propertyID,
|
||||
AudioDevicePropertyListenerProc handler, const char *name );
|
||||
void RemoveListeners();
|
||||
static OSStatus GetData( AudioDeviceID inDevice,
|
||||
const AudioTimeStamp *inNow,
|
||||
const AudioBufferList *inInputData,
|
||||
const AudioTimeStamp *inInputTime,
|
||||
AudioBufferList *outOutputData,
|
||||
const AudioTimeStamp *inOutputTime,
|
||||
void *inClientData );
|
||||
|
||||
static void NameHALThread( CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info );
|
||||
static OSStatus OverloadListener( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput,
|
||||
AudioDevicePropertyID inPropertyID, void *inData );
|
||||
static OSStatus DeviceChanged( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput,
|
||||
AudioDevicePropertyID inPropertyID, void *inData );
|
||||
static OSStatus JackChanged( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput,
|
||||
AudioDevicePropertyID inPropertyID, void *inData );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* (c) 2004-2006 Steve Checkoway
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
Reference in New Issue
Block a user