2006-06-18 19:05:40 +00:00
|
|
|
#include "RageSoundDriver_AU.h"
|
|
|
|
|
#include "RageUtil.h"
|
|
|
|
|
#include "RageLog.h"
|
|
|
|
|
#include "PrefsManager.h"
|
|
|
|
|
#include "archutils/Darwin/DarwinThreadHelpers.h"
|
2006-11-12 09:04:14 +00:00
|
|
|
#include "Foreach.h"
|
2006-06-18 19:05:40 +00:00
|
|
|
#include <CoreServices/CoreServices.h>
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
2006-11-12 09:04:14 +00:00
|
|
|
RageSound_AU::RageSound_AU() : m_OutputUnit(NULL), m_OutputDevice(NULL), m_iSampleRate(0), m_fLatency(0.0f),
|
|
|
|
|
m_iLastSampleTime(0), m_iOffset(0), m_bDone(false), m_pIOThread(NULL), m_Semaphore("Sound shutdown")
|
2006-06-18 19:05:40 +00:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 RageSound_AU::Init()
|
|
|
|
|
{
|
|
|
|
|
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 )
|
|
|
|
|
return "Failed to find the default output unit.";
|
|
|
|
|
|
|
|
|
|
OSStatus error = OpenAComponent( comp, &m_OutputUnit );
|
|
|
|
|
|
|
|
|
|
if( error != noErr || m_OutputUnit == NULL )
|
|
|
|
|
return ERROR( "Could not open the default output unit", error );
|
|
|
|
|
|
|
|
|
|
// Set up a callback function to generate output to the output unit
|
|
|
|
|
AURenderCallbackStruct input;
|
|
|
|
|
input.inputProc = Render;
|
|
|
|
|
input.inputProcRefCon = this;
|
|
|
|
|
|
|
|
|
|
error = AudioUnitSetProperty( m_OutputUnit,
|
|
|
|
|
kAudioUnitProperty_SetRenderCallback,
|
|
|
|
|
kAudioUnitScope_Input,
|
|
|
|
|
0,
|
|
|
|
|
&input,
|
|
|
|
|
sizeof(input) );
|
|
|
|
|
if( error != noErr )
|
|
|
|
|
return ERROR( "Failed to set render callback", error );
|
|
|
|
|
|
|
|
|
|
AudioStreamBasicDescription streamFormat;
|
|
|
|
|
|
|
|
|
|
streamFormat.mSampleRate = PREFSMAN->m_iSoundPreferredSampleRate;
|
|
|
|
|
streamFormat.mFormatID = kAudioFormatLinearPCM;
|
|
|
|
|
streamFormat.mFormatFlags = kFormatFlags;
|
|
|
|
|
streamFormat.mBytesPerPacket = kBytesPerPacket;
|
|
|
|
|
streamFormat.mFramesPerPacket = kFramesPerPacket;
|
|
|
|
|
streamFormat.mBytesPerFrame = kBytesPerFrame;
|
|
|
|
|
streamFormat.mChannelsPerFrame = kChannelsPerFrame;
|
|
|
|
|
streamFormat.mBitsPerChannel = kBitsPerChannel;
|
|
|
|
|
|
|
|
|
|
if( streamFormat.mSampleRate <= 0 )
|
|
|
|
|
streamFormat.mSampleRate = 44100.0;
|
|
|
|
|
m_iSampleRate = int( streamFormat.mSampleRate );
|
|
|
|
|
error = AudioUnitSetProperty( m_OutputUnit,
|
|
|
|
|
kAudioUnitProperty_StreamFormat,
|
|
|
|
|
kAudioUnitScope_Input,
|
|
|
|
|
0,
|
|
|
|
|
&streamFormat,
|
|
|
|
|
sizeof(AudioStreamBasicDescription) );
|
|
|
|
|
if( error != noErr )
|
|
|
|
|
return ERROR( "Failed to set AU stream format", error );
|
|
|
|
|
UInt32 renderQuality = kRenderQuality_Max;
|
|
|
|
|
|
|
|
|
|
error = AudioUnitSetProperty( m_OutputUnit,
|
|
|
|
|
kAudioUnitProperty_RenderQuality,
|
|
|
|
|
kAudioUnitScope_Global,
|
|
|
|
|
0,
|
|
|
|
|
&renderQuality,
|
|
|
|
|
sizeof(renderQuality) );
|
|
|
|
|
if( error != noErr )
|
|
|
|
|
LOG->Warn( WERROR("Failed to set the maximum render quality", error) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UInt32 data = 1;
|
|
|
|
|
error = AudioUnitSetProperty( m_OutputUnit,
|
|
|
|
|
kAudioOutputUnitProperty_EnableIO,
|
|
|
|
|
kAudioUnitScope_Output,
|
|
|
|
|
0,
|
|
|
|
|
&data,
|
|
|
|
|
sizeof(data) );
|
|
|
|
|
if( error != noErr )
|
|
|
|
|
return ERROR( "Could not enable output I/O", error );
|
|
|
|
|
|
|
|
|
|
UInt32 size = sizeof(AudioDeviceID);
|
|
|
|
|
|
|
|
|
|
error = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &size, &m_OutputDevice );
|
|
|
|
|
if( error != noErr )
|
|
|
|
|
return ERROR( "Could not get the default output device", error );
|
|
|
|
|
|
|
|
|
|
error = AudioUnitSetProperty( m_OutputUnit,
|
|
|
|
|
kAudioOutputUnitProperty_CurrentDevice,
|
|
|
|
|
kAudioUnitScope_Global,
|
|
|
|
|
0,
|
|
|
|
|
&m_OutputDevice,
|
|
|
|
|
sizeof(AudioDeviceID) );
|
|
|
|
|
if( error != noErr )
|
|
|
|
|
return ERROR( "Could not set the default output device", error );
|
|
|
|
|
|
|
|
|
|
// XXX set channel map
|
|
|
|
|
#if 1
|
|
|
|
|
data = 0;
|
|
|
|
|
error = AudioUnitSetProperty( m_OutputUnit,
|
|
|
|
|
2007, //kAudioOutputUnitProperty_StartTimestampsAtZero,
|
|
|
|
|
kAudioUnitScope_Global,
|
|
|
|
|
0,
|
|
|
|
|
&data,
|
|
|
|
|
sizeof(data) );
|
|
|
|
|
if( error != noErr )
|
|
|
|
|
return ERROR( "Could not set AudioUnit to use the device's time stamps", error );
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Initialize the AU.
|
|
|
|
|
error = AudioUnitInitialize( m_OutputUnit );
|
|
|
|
|
if( error != noErr )
|
|
|
|
|
return ERROR( "Could not initialize the AudioUnit", error );
|
|
|
|
|
|
|
|
|
|
Float64 latency = 0.0;
|
|
|
|
|
|
|
|
|
|
size = sizeof(latency);
|
|
|
|
|
error = AudioUnitGetProperty( m_OutputUnit,
|
|
|
|
|
kAudioUnitProperty_Latency,
|
2006-11-12 09:04:14 +00:00
|
|
|
kAudioUnitScope_Global,
|
2006-06-18 19:05:40 +00:00
|
|
|
0,
|
|
|
|
|
&latency,
|
|
|
|
|
&size );
|
|
|
|
|
if( error != noErr )
|
|
|
|
|
LOG->Warn( WERROR("Could not get latency", error) );
|
|
|
|
|
LOG->Info( "Latency: %lf", latency );
|
|
|
|
|
m_fLatency = latency;
|
|
|
|
|
|
2006-11-12 09:04:14 +00:00
|
|
|
// Add property listeners.
|
|
|
|
|
for( AudioUnitPropertyID prop = kAudioUnitProperty_ClassInfo; prop <= kAudioUnitProperty_PresentPreset; ++prop )
|
|
|
|
|
AddAUPropertyListener( prop );
|
|
|
|
|
for( AudioUnitPropertyID prop = kAudioOutputUnitProperty_CurrentDevice; prop <= kAudioOutputUnitProperty_HasIO; ++prop )
|
|
|
|
|
AddAUPropertyListener( prop );
|
|
|
|
|
#define ADD(x) AddADPropertyListener( kAudioDeviceProperty ## x )
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
AddADPropertyListener( kAudioPropertyWildcardPropertyID );
|
|
|
|
|
#else
|
|
|
|
|
ADD( DeviceIsAlive );
|
|
|
|
|
ADD( DeviceHasChanged );
|
|
|
|
|
ADD( DeviceIsRunning );
|
|
|
|
|
ADD( DeviceIsRunningSomewhere );
|
|
|
|
|
AddADPropertyListener( kAudioDeviceProcessorOverload );
|
|
|
|
|
ADD( Streams );
|
|
|
|
|
ADD( StreamConfiguration );
|
|
|
|
|
ADD( StreamFormat );
|
|
|
|
|
#endif
|
|
|
|
|
#undef ADD
|
|
|
|
|
|
2006-06-18 19:05:40 +00:00
|
|
|
StartDecodeThread();
|
|
|
|
|
error = AudioOutputUnitStart( m_OutputUnit );
|
|
|
|
|
if( error != noErr )
|
|
|
|
|
return ERROR( "Could not start the AudioUnit", error );
|
|
|
|
|
return RString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RageSound_AU::~RageSound_AU()
|
|
|
|
|
{
|
2006-11-12 09:04:14 +00:00
|
|
|
ShutDown();
|
|
|
|
|
delete m_pIOThread;
|
2006-06-18 19:05:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int64_t RageSound_AU::GetPosition( const RageSoundBase *sound ) const
|
|
|
|
|
{
|
|
|
|
|
AudioTimeStamp time;
|
|
|
|
|
OSStatus error;
|
|
|
|
|
|
2006-11-12 09:04:14 +00:00
|
|
|
if( m_OutputDevice == NULL )
|
|
|
|
|
return m_iLastSampleTime;
|
2006-06-18 19:05:40 +00:00
|
|
|
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 RageSound_AU::SetupDecodingThread()
|
|
|
|
|
{
|
|
|
|
|
/* Increase the scheduling precedence of the decoder thread. */
|
|
|
|
|
SetThreadPrecedence( 0.75f );
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-12 09:04:14 +00:00
|
|
|
void RageSound_AU::ShutDown()
|
|
|
|
|
{
|
|
|
|
|
if( !m_OutputUnit )
|
|
|
|
|
return;
|
|
|
|
|
FOREACH_CONST( AudioUnitPropertyID, m_vAUProperties, prop )
|
|
|
|
|
AudioUnitRemovePropertyListener( m_OutputUnit, *prop, PropertyChanged );
|
|
|
|
|
FOREACH_CONST( AudioDevicePropertyID, m_vADProperties, prop )
|
|
|
|
|
AudioDeviceRemovePropertyListener( m_OutputDevice, 0, false, *prop, DevicePropertyChanged );
|
|
|
|
|
m_bDone = true;
|
|
|
|
|
m_Semaphore.Wait();
|
|
|
|
|
m_vADProperties.clear();
|
|
|
|
|
m_vAUProperties.clear();
|
|
|
|
|
AudioUnitUninitialize( m_OutputUnit );
|
|
|
|
|
CloseComponent( m_OutputUnit );
|
|
|
|
|
m_OutputUnit = NULL;
|
|
|
|
|
m_OutputDevice = NULL;
|
|
|
|
|
m_iSampleRate = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RageSound_AU::AddAUPropertyListener( AudioUnitPropertyID prop )
|
|
|
|
|
{
|
|
|
|
|
ComponentResult result = AudioUnitAddPropertyListener( m_OutputUnit, prop, PropertyChanged, this );
|
|
|
|
|
|
|
|
|
|
if( result )
|
|
|
|
|
LOG->Warn( "Failed to add property listener for property %d.", int(prop) );
|
|
|
|
|
else
|
|
|
|
|
m_vAUProperties.push_back( prop );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RageSound_AU::AddADPropertyListener( AudioDevicePropertyID prop )
|
|
|
|
|
{
|
|
|
|
|
OSStatus error = AudioDeviceAddPropertyListener( m_OutputDevice, kAudioPropertyWildcardChannel, false,
|
|
|
|
|
prop, DevicePropertyChanged, this );
|
|
|
|
|
|
|
|
|
|
if( error )
|
|
|
|
|
LOG->Warn( WERROR("Failed to add device property listener for property %s",
|
|
|
|
|
error, FourCCToString(prop).c_str()) );
|
|
|
|
|
else
|
|
|
|
|
m_vADProperties.push_back( prop );
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-18 19:05:40 +00:00
|
|
|
OSStatus RageSound_AU::Render( void *inRefCon,
|
|
|
|
|
AudioUnitRenderActionFlags *ioActionFlags,
|
|
|
|
|
const AudioTimeStamp *inTimeStamp,
|
|
|
|
|
UInt32 inBusNumber,
|
|
|
|
|
UInt32 inNumberFrames,
|
|
|
|
|
AudioBufferList *ioData )
|
|
|
|
|
{
|
|
|
|
|
RageSound_AU *This = (RageSound_AU *)inRefCon;
|
2006-11-12 09:04:14 +00:00
|
|
|
|
2006-06-18 19:05:40 +00:00
|
|
|
if( unlikely(This->m_pIOThread == NULL) )
|
|
|
|
|
This->m_pIOThread = new RageThreadRegister( "HAL I/O thread" );
|
|
|
|
|
AudioBuffer &buf = ioData->mBuffers[0];
|
|
|
|
|
int64_t decodePos = int64_t( inTimeStamp->mSampleTime ) + This->m_iOffset;
|
|
|
|
|
|
|
|
|
|
This->Mix( (int16_t *)buf.mData, inNumberFrames, decodePos, This->GetPosition(NULL) );
|
2006-11-12 09:04:14 +00:00
|
|
|
if( unlikely(This->m_bDone) )
|
|
|
|
|
{
|
|
|
|
|
AudioOutputUnitStop( This->m_OutputUnit );
|
|
|
|
|
This->m_Semaphore.Post();
|
|
|
|
|
}
|
2006-06-18 19:05:40 +00:00
|
|
|
return noErr;
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-12 09:04:14 +00:00
|
|
|
void RageSound_AU::PropertyChanged( void *inRefCon,
|
|
|
|
|
AudioUnit au,
|
|
|
|
|
AudioUnitPropertyID inID,
|
|
|
|
|
AudioUnitScope inScope,
|
|
|
|
|
AudioUnitElement inElement )
|
|
|
|
|
{
|
|
|
|
|
RageSound_AU *This = (RageSound_AU *)inRefCon;
|
|
|
|
|
OSStatus error;
|
|
|
|
|
UInt32 running;
|
|
|
|
|
UInt32 size = sizeof( running );
|
|
|
|
|
AudioTimeStamp time;
|
|
|
|
|
|
|
|
|
|
ASSERT( au == This->m_OutputUnit );
|
|
|
|
|
|
|
|
|
|
switch( inID )
|
|
|
|
|
{
|
|
|
|
|
case kAudioOutputUnitProperty_CurrentDevice:
|
|
|
|
|
{
|
|
|
|
|
LOG->Info( "Audio device changed!" );
|
|
|
|
|
This->ShutDown();
|
|
|
|
|
RString result = This->Init();
|
|
|
|
|
ASSERT_M( result.empty(), result );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
case kAudioOutputUnitProperty_IsRunning:
|
|
|
|
|
if( (error = AudioUnitGetProperty(au, inID, inScope, inElement, &running, &size)) )
|
|
|
|
|
FAIL_M( ERROR("Couldn't get running property", error) );
|
|
|
|
|
LOG->Trace( "Audio device %s running.", running ? "started" : "stopped" );
|
|
|
|
|
if( !running )
|
|
|
|
|
return;
|
|
|
|
|
// Fall through.
|
|
|
|
|
case kAudioUnitProperty_StreamFormat:
|
|
|
|
|
if( inScope != kAudioUnitScope_Output )
|
|
|
|
|
return;
|
|
|
|
|
if( (error = AudioDeviceGetCurrentTime(This->m_OutputDevice, &time)) )
|
|
|
|
|
{
|
|
|
|
|
if( error != kAudioHardwareNotRunningError )
|
|
|
|
|
FAIL_M( ERROR("Couldn't get the current time", error) );
|
|
|
|
|
LOG->Trace( "Old offset %lld, last sample time %lld.",
|
|
|
|
|
This->m_iOffset, This->m_iLastSampleTime );
|
|
|
|
|
This->m_iOffset = This->m_iLastSampleTime;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LOG->Trace( "Old offset %lld, last sample time %lld, current sample time %lld, new offset %lld.",
|
|
|
|
|
This->m_iOffset, This->m_iLastSampleTime, int64_t(time.mSampleTime), This->m_iLastSampleTime - int64_t(time.mSampleTime) );
|
|
|
|
|
This->m_iOffset = This->m_iLastSampleTime - int64_t( time.mSampleTime );
|
|
|
|
|
}
|
|
|
|
|
//return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *scope;
|
|
|
|
|
switch( inScope )
|
|
|
|
|
{
|
|
|
|
|
case kAudioUnitScope_Global: scope = "global"; break;
|
|
|
|
|
case kAudioUnitScope_Input: scope = "input"; break;
|
|
|
|
|
case kAudioUnitScope_Output: scope = "output"; break;
|
|
|
|
|
case kAudioUnitScope_Group: scope = "group"; break;
|
|
|
|
|
case kAudioUnitScope_Part: scope = "part"; break;
|
|
|
|
|
DEFAULT_FAIL( int(inScope) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG->Trace( "PropertyChanged: id = %d, scope = %s, element %d.", int(inID), scope, int(inElement) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OSStatus RageSound_AU::DevicePropertyChanged( AudioDeviceID inDevice,
|
|
|
|
|
UInt32 inChannel,
|
|
|
|
|
Boolean isInput,
|
|
|
|
|
AudioDevicePropertyID inID,
|
|
|
|
|
void *inRefCon )
|
|
|
|
|
{
|
|
|
|
|
RageSound_AU *This = (RageSound_AU *)inRefCon;
|
|
|
|
|
UInt32 value;
|
|
|
|
|
UInt32 size = sizeof(value);
|
|
|
|
|
bool somewhere = true;
|
|
|
|
|
OSStatus error;
|
|
|
|
|
|
|
|
|
|
switch( inID )
|
|
|
|
|
{
|
|
|
|
|
case kAudioDevicePropertyDeviceIsAlive:
|
|
|
|
|
LOG->Warn( "Audio device died." );
|
|
|
|
|
This->m_OutputDevice = NULL; // The AudioDeviceID is now invalid.
|
|
|
|
|
This->m_vADProperties.clear();
|
|
|
|
|
break;
|
|
|
|
|
case kAudioDevicePropertyDeviceHasChanged:
|
|
|
|
|
// Hopefully this will be handled sanely by the AudioUnit.
|
|
|
|
|
LOG->Trace( "Audio device has changed in some way." );
|
|
|
|
|
break;
|
|
|
|
|
case kAudioDevicePropertyDeviceIsRunning:
|
|
|
|
|
somewhere = false;
|
|
|
|
|
// fall through.
|
|
|
|
|
case kAudioDevicePropertyDeviceIsRunningSomewhere:
|
|
|
|
|
if( (error = AudioDeviceGetProperty(inDevice, inChannel, isInput, inID, &size, &value)) )
|
|
|
|
|
FAIL_M( ERROR("AudioDeviceGetProperty('%s') failed", error, FourCCToString(inID).c_str()) );
|
|
|
|
|
LOG->Trace( "Audio device has %s running%s.", value ? "started" : "stopped", somewhere ? " somewhere" : "" );
|
|
|
|
|
break;
|
|
|
|
|
case kAudioDeviceProcessorOverload:
|
|
|
|
|
LOG->Trace( "Audio overload." ); // This is being called on the real time thread. hmm.
|
|
|
|
|
return 0; // Do nothing else.
|
|
|
|
|
case kAudioDevicePropertyStreams:
|
|
|
|
|
// Hopefully this will be handled sanely by the AudioUnit.
|
|
|
|
|
LOG->Trace( "Audio device's streams have changed." );
|
|
|
|
|
break;
|
|
|
|
|
case kAudioDevicePropertyStreamConfiguration:
|
|
|
|
|
// Hopefully this will be handled sanely by the AudioUnit.
|
|
|
|
|
LOG->Trace( "Audio device's stream configuration has changed." );
|
|
|
|
|
break;
|
|
|
|
|
case kAudioDevicePropertyStreamFormat:
|
|
|
|
|
// Hopefully this will be handled sanely by the AudioUnit.
|
|
|
|
|
LOG->Trace( "Audio device's stream format has changed." );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
LOG->Trace( "Audio device property '%s' changed.", FourCCToString(inID).c_str() );
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-18 19:05:40 +00:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* (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.
|
|
|
|
|
*/
|