#include "global.h" #include "RageSoundDriver_CA.h" #include "CAHelpers.h" #include "RageLog.h" #include "CAAudioHardwareSystem.h" #include "CAAudioHardwareDevice.h" #include "CAAudioHardwareStream.h" #include "CAStreamBasicDescription.h" #include "CAException.h" #include "archutils/Unix/CrashHandler.h" #include #include #include static AudioConverter *gConverter; static Desc FindClosestFormat(const vector& formats) { vector v; vector::const_iterator i; for (i = formats.begin(); i != formats.end(); ++i) { const Desc& format = *i; if (!format.IsPCM() || format.mSampleRate != 44100.0) continue; if (format.SampleWordSize() == 2 && (format.mFormatFlags & kAudioFormatFlagIsSignedInteger) == kAudioFormatFlagIsSignedInteger) { // exact match return format; } v.push_back(format); } for (i = v.begin(); i != v.end(); ++i) { const Desc& format = *i; if (format.SampleWordSize() == 2) { return format; // close } } if (v.empty()) RageException::ThrowNonfatal("Couldn't find a close format."); return v[0]; // something is better than nothing. } static void GetIOProcFormats( CAAudioHardwareStream stream, vector &procFormats ) { UInt32 numFormats = stream.GetNumberAvailableIOProcFormats(); for( UInt32 i=0; i &physicalFormats ) { UInt32 numFormats = stream.GetNumberAvailablePhysicalFormats(); for( UInt32 i=0; iGetLatency(kAudioDeviceSectionOutput); mLatency = frames / 44100.0; LOG->Info("Frames of latency: %lu\n" "Seconds of latency: %f", frames, mLatency); } catch (const CAException& e) { delete mOutputDevice; RageException::ThrowNonfatal("Couldn't get Latency."); } AudioStreamID sID = mOutputDevice->GetStreamByIndex( kAudioDeviceSectionOutput, 0 ); CAAudioHardwareStream stream( sID ); vector physicalFormats; GetPhysicalFormats( sID, physicalFormats ); unsigned i; LOG->Info("Available physical formats:"); for (i = 0; i < physicalFormats.size(); ++i) { const Desc& f = physicalFormats[i]; LOG->Info("Format %u: Rate: %f ID: %lu Flags 0x%lx bpp %lu fpp %lu bpf %lu channels %lu bits %lu", i, f.mSampleRate, f.mFormatID, f.mFormatFlags, f.mBytesPerPacket, f.mFramesPerPacket, f.mBytesPerFrame, f.mChannelsPerFrame, f.mBitsPerChannel); } const Desc& physicalFormat = FindClosestFormat( physicalFormats ); stream.SetCurrentPhysicalFormat( physicalFormat ); vector procFormats; GetIOProcFormats( sID, procFormats ); LOG->Info("Available I/O procedure formats:"); for (i = 0; i < procFormats.size(); ++i) { const Desc& f = procFormats[i]; LOG->Info("Format %u: Rate: %f ID: %lu Flags 0x%lx bpp %lu fpp %lu bpf %lu channels %lu bits %lu", i, f.mSampleRate, f.mFormatID, f.mFormatFlags, f.mBytesPerPacket, f.mFramesPerPacket, f.mBytesPerFrame, f.mChannelsPerFrame, f.mBitsPerChannel); } const Desc& procFormat = FindClosestFormat( procFormats ); stream.SetCurrentIOProcFormat( procFormat ); StartDecodeThread(); gConverter = new AudioConverter( this, procFormat ); try { mOutputDevice->AddIOProc(GetData, this); mOutputDevice->StartIOProc(GetData); } catch(const CAException& e) { delete gConverter; delete mOutputDevice; RageException::Throw("Couldn't start the IOProc."); } } RageSound_CA::~RageSound_CA() { mOutputDevice->StopIOProc(GetData); delete gConverter; delete mOutputDevice; } int64_t RageSound_CA::GetPosition(const RageSoundBase *sound) const { AudioTimeStamp time; mOutputDevice->GetCurrentTime(time); return int64_t(time.mSampleTime); } void RageSound_CA::FillConverter(void *data, UInt32 dataByteSize) { int frames = dataByteSize / gConverter->GetInputFormat().mBytesPerPacket; this->Mix((int16_t *)data, frames, mDecodePos, GetPosition(NULL)); } OSStatus RageSound_CA::GetData(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData) { RageSound_CA *This = (RageSound_CA *)inClientData; UInt32 dataPackets = outOutputData->mBuffers[0].mDataByteSize; dataPackets /= gConverter->GetOutputFormat().mBytesPerPacket; This->mDecodePos = int64_t(inOutputTime->mSampleTime); gConverter->FillComplexBuffer(dataPackets, *outOutputData, NULL); return noErr; } void RageSound_CA::SetupDecodingThread() { /* Increase the scheduling precedence of the decoder thread. */ thread_precedence_policy po; po.importance = 32; kern_return_t ret = thread_policy_set( mach_thread_self(), THREAD_PRECEDENCE_POLICY, (int *)&po, THREAD_PRECEDENCE_POLICY_COUNT ); if( ret != KERN_SUCCESS ) LOG->Warn( "thread_policy_set(THREAD_PRECEDENCE_POLICY) failed: %s", mach_error_string(ret) ); }