static void AQTestBufferCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer) { AQTestInfo * myInfo = (AQTestInfo *)inUserData; if (myInfo->mDone) return; UInt32 numBytes; UInt32 nPackets = myInfo->mNumPacketsToRead; OSStatus result = AudioFileReadPackets(myInfo->mAudioFile, false, &numBytes, myInfo->mPacketDescs, myInfo->mCurrentPacket, &nPackets, inCompleteAQBuffer->mAudioData); if (result) { DebugMessageN1 ("Error reading from file: %d\n", (int)result); exit(1); } if (nPackets > 0) { inCompleteAQBuffer->mAudioDataByteSize = numBytes; AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, (myInfo->mPacketDescs ? nPackets : 0), myInfo->mPacketDescs); myInfo->mCurrentPacket += nPackets; } else { result = AudioQueueStop(myInfo->mQueue, false); if (result) { DebugMessageN1 ("AudioQueueStop(false) failed: %d", (int)result); exit(1); } // reading nPackets == 0 is our EOF condition myInfo->mDone = true; } }
void CAHALAudioSystemObject::LogBasicDeviceInfo() { UInt32 theNumberDevices = GetNumberAudioDevices(); CAAutoArrayDelete<AudioObjectID> theDeviceList(theNumberDevices); GetAudioDevices(theNumberDevices, theDeviceList); DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: %d devices", (int)theNumberDevices); for(UInt32 theDeviceIndex = 0; theDeviceIndex < theNumberDevices; ++theDeviceIndex) { char theCString[256]; UInt32 theCStringSize = sizeof(theCString); DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: Device %d", (int)theDeviceIndex); CAHALAudioDevice theDevice(theDeviceList[theDeviceIndex]); DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: Object ID: %d", (int)theDeviceList[theDeviceIndex]); CACFString theDeviceName(theDevice.CopyName()); theCStringSize = sizeof(theCString); theDeviceName.GetCString(theCString, theCStringSize); DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: Name: %s", theCString); CACFString theDeviceUID(theDevice.CopyDeviceUID()); theCStringSize = sizeof(theCString); theDeviceUID.GetCString(theCString, theCStringSize); DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: UID: %s", theCString); } }
static void AQTestBufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer) { AQTestInfo * myInfo = (AQTestInfo *)inUserData; if (myInfo->mDone) return; UInt32 numBytes; UInt32 nPackets = myInfo->mNumPacketsToRead; OSStatus result = AudioFileReadPackets(myInfo->mAudioFile, // The audio file from which packets of audio data are to be read. false, // Set to true to cache the data. Otherwise, set to false. &numBytes, // On output, a pointer to the number of bytes actually returned. myInfo->mPacketDescs, // A pointer to an array of packet descriptions that have been allocated. myInfo->mCurrentPacket, // The packet index of the first packet you want to be returned. &nPackets, // On input, a pointer to the number of packets to read. On output, the number of packets actually read. inCompleteAQBuffer->mAudioData); // A pointer to user-allocated memory. if (result) { DebugMessageN1 ("Error reading from file: %d\n", (int)result); exit(1); } // we have some data if (nPackets > 0) { inCompleteAQBuffer->mAudioDataByteSize = numBytes; result = AudioQueueEnqueueBuffer(inAQ, // The audio queue that owns the audio queue buffer. inCompleteAQBuffer, // The audio queue buffer to add to the buffer queue. (myInfo->mPacketDescs ? nPackets : 0), // The number of packets of audio data in the inBuffer parameter. See Docs. myInfo->mPacketDescs); // An array of packet descriptions. Or NULL. See Docs. if (result) { DebugMessageN1 ("Error enqueuing buffer: %d\n", (int)result); exit(1); } myInfo->mCurrentPacket += nPackets; } else { // **** This ensures that we flush the queue when done -- ensures you get all the data out **** if (!myInfo->mFlushed) { result = AudioQueueFlush(myInfo->mQueue); if (result) { DebugMessageN1("AudioQueueFlush failed: %d", (int)result); exit(1); } myInfo->mFlushed = true; } result = AudioQueueStop(myInfo->mQueue, false); if (result) { DebugMessageN1("AudioQueueStop(false) failed: %d", (int)result); exit(1); } // reading nPackets == 0 is our EOF condition myInfo->mDone = true; } }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bool OALBuffer::UseThisBuffer(OALSource* inSource) { #if LOG_VERBOSE DebugMessageN1("OALBuffer::UseThisBuffer() - OALBuffer = %ld", (long int) mSelfToken); #endif #if USE_SOURCE_LIST_MUTEX bool wasLocked = mSourceListGuard.Lock(); #endif // see if this source is in list already if (mAttachedSourceList->SourceExists(inSource) == true) mAttachedSourceList->IncreaseAttachmentCount(inSource); else { SourceAttachedInfo nuSourceInfo; nuSourceInfo.mSource = inSource; nuSourceInfo.mAttachedCount = 1; mAttachedSourceList->Add(nuSourceInfo); } #if USE_SOURCE_LIST_MUTEX if (wasLocked) mSourceListGuard.Unlock(); #endif #if LOG_EXTRAS DebugMessageN2("OALBuffer::UseThisBuffer - BufferToken:SourceToken = %ld:%ld", (long int)mSelfToken, (long int)inSource->GetToken()); #endif return noErr; }
void ZKMORHP_IOThreadSlave::WorkLoopTeardown() { // The other thread holds the lock during teardown try { mWorkLoopPhase = kTeardownPhase; mDevice->GetIOCycleTelemetry().IOCycleTeardownBegin(mIOCycleCounter); // the work loop has finished, clear the time constraints ClearTimeConstraints(); // tell the device that the IO thread is torn down mDevice->StopIOCycleTimingServices(); } catch(const CAException& inException) { DebugMessageN1("ZKMORHP_IOThreadSlave::WorkLoopTeardown: Caught a CAException, code == %ld", (long int)inException.GetError()); } catch(...) { DebugMessage("ZKMORHP_IOThreadSlave::WorkLoopTeardown: Caught an unknown exception."); } // set the device state to know the engine has stopped mDevice->IOEngineStopped(); // Notify clients that the IO thread is stopping CAPropertyAddress theIsRunningAddress(kAudioDevicePropertyDeviceIsRunning); mDevice->PropertiesChanged(1, &theIsRunningAddress); mDevice->GetIOCycleTelemetry().IOCycleTeardownEnd(mIOCycleCounter); mWorkLoopPhase = kNotRunningPhase; mIOGuard.NotifyAll(); mIOCycleCounter = 0; }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OSStatus OALBuffer::AddAudioDataStatic(char* inAudioData, UInt32 inAudioDataSize, ALenum format, ALsizei freq) { #if LOG_VERBOSE DebugMessageN5("OALBuffer::AddAudioDataStatic() - OALBuffer:inAudioData:inAudioDataSize:format:freq = %ld:%p:%ld:%d:%d", (long int) mSelfToken, inAudioData, (long int) inAudioDataSize, format, freq); #endif #if LOG_EXTRAS DebugMessage("AddAudioDataStatic called: Converting Data Now"); #endif CAGuard::Locker bufferLock(mBufferLock); try { if (!IsFormatSupported(format)) throw ((OSStatus) AL_INVALID_VALUE); // this is not a valid buffer token or is an invalid format // don't allow if the buffer is in a queue if (mAttachedSourceList->Size() > 0) { DebugMessage("AddAudioDataStatic ATTACHMENT > 0"); throw ((OSStatus) AL_INVALID_OPERATION); } mPreConvertedDataSize = (UInt32) inAudioDataSize; OSStatus result = noErr; // if this buffer was using memory created by the library, free it now and initialize mData if (!mAppOwnsBufferMemory && (mData != NULL)) { free (mData); mData = NULL; } mData = (UInt8*) inAudioData; mDataSize = (UInt32) inAudioDataSize; result = FillInASBD(mDataFormat, format, freq); THROW_RESULT mPreConvertedDataFormat.SetFrom(mDataFormat); // make sure they are the same so original format info can be returned to caller } catch (OSStatus result) { mData = NULL; mAppOwnsBufferMemory = false; DebugMessageN1("AddAudioDataStatic Failed - err = %ld\n", (long int) result); alSetError(result); } catch (...) { mData = NULL; mAppOwnsBufferMemory = false; DebugMessage("AddAudioDataStatic Failed"); alSetError(AL_INVALID_OPERATION); } mAppOwnsBufferMemory = true; return noErr; }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UInt32 OALBuffer::GetFrameCount() { #if LOG_VERBOSE DebugMessageN1("OALBuffer::GetFrameCount() - OALBuffer = %ld", (long int) mSelfToken); #endif UInt32 totalPackets = (mDataFormat.mBytesPerPacket == 0) ? 0 : mDataSize/mDataFormat.mBytesPerPacket; return totalPackets * mDataFormat.mFramesPerPacket; }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OALBuffer::~OALBuffer() { #if LOG_VERBOSE DebugMessageN1("OALBuffer::~OALBuffer() - OALBuffer = %ld", (long int) mSelfToken); #endif if (!mAppOwnsBufferMemory && (mData != NULL)) free (mData); // should never be deleted unless this object said it was ok #if USE_SOURCE_LIST_MUTEX bool wasLocked = mSourceListGuard.Lock(); #endif if (mAttachedSourceList) delete mAttachedSourceList; #if USE_SOURCE_LIST_MUTEX if (wasLocked) mSourceListGuard.Unlock(); #endif }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bool OALBuffer::IsPurgable() { #if LOG_VERBOSE DebugMessageN1("OALBuffer::IsPurgable() - OALBuffer = %ld", (long int) mSelfToken); #endif bool returnValue = false; #if USE_SOURCE_LIST_MUTEX bool wasLocked = mSourceListGuard.Lock(); #endif // make sure that no other source has attached this buffer, and that no other thread is editing it if ((mAttachedSourceList->Size() == 0) && mBufferLock.IsFree() && (mInUseFlag <= 0) && (!IsInPostRenderMessageQueue())) returnValue = true; #if USE_SOURCE_LIST_MUTEX if (wasLocked) mSourceListGuard.Unlock(); #endif return returnValue; }
OALBuffer::OALBuffer (ALuint inSelfToken) : mSelfToken (inSelfToken), #if USE_SOURCE_LIST_MUTEX mSourceListGuard ("OALBuffer::SourceListGuard"), #endif mBufferLock ("OALBuffer::EditLock"), mInUseFlag(0), mData(NULL), mAppOwnsBufferMemory(false), mDataSize (0), mPreConvertedDataSize(0), mDataHasBeenConverted(false), mAttachedSourceList(NULL), mIsInPostRenderMessageQueue(false) { #if LOG_VERBOSE DebugMessageN1("OALBuffer::OALBuffer() - OALBuffer = %ld", (long int) mSelfToken); #endif mAttachedSourceList = new AttachedSourceList (); // create a source list map mAttachedSourceList->Reserve(128); }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bool OALBuffer::CanBeRemovedFromBufferMap() { #if LOG_VERBOSE DebugMessageN1("OALBuffer::CanBeRemovedFromBufferMap() - OALBuffer = %ld", (long int) mSelfToken); #endif bool returnValue = true; #if USE_SOURCE_LIST_MUTEX bool wasLocked = mSourceListGuard.Lock(); #endif // if the buffer object is not attached to a source, it's ok to remove if (mAttachedSourceList->Size() == 0) returnValue = true; else { // if all attached sources are transitioning to flush, it can be removed from buffer map, // but the OALBuffer object must be deleted at a later time for (UInt32 i = 0; i < mAttachedSourceList->Size(); i++) { OALSource *curSource = mAttachedSourceList->GetSourceByIndex(i); if (curSource) { if (!curSource->IsSourceTransitioningToFlushQ()) { returnValue = false; goto Finished; } } } } Finished: #if USE_SOURCE_LIST_MUTEX if (wasLocked) mSourceListGuard.Unlock(); #endif return returnValue; // all attached sources are transitioning to flush }
void CAHostTimeBase::Initialize() { // get the info about Absolute time #if TARGET_OS_MAC struct mach_timebase_info theTimeBaseInfo; mach_timebase_info(&theTimeBaseInfo); sMinDelta = 1; sToNanosNumerator = theTimeBaseInfo.numer; sToNanosDenominator = theTimeBaseInfo.denom; sFromNanosNumerator = sToNanosDenominator; sFromNanosDenominator = sToNanosNumerator; // the frequency of that clock is: (sToNanosDenominator / sToNanosNumerator) * 10^9 sFrequency = static_cast<Float64>(sToNanosDenominator) / static_cast<Float64>(sToNanosNumerator); sFrequency *= 1000000000.0; #elif TARGET_OS_WIN32 LARGE_INTEGER theFrequency; QueryPerformanceFrequency(&theFrequency); sMinDelta = 1; sToNanosNumerator = 1000000000ULL; sToNanosDenominator = *((UInt64*)&theFrequency); sFromNanosNumerator = sToNanosDenominator; sFromNanosDenominator = sToNanosNumerator; sFrequency = static_cast<Float64>(*((UInt64*)&theFrequency)); #endif sInverseFrequency = 1.0 / sFrequency; #if Log_Host_Time_Base_Parameters DebugMessage( "Host Time Base Parameters"); DebugMessageN1(" Minimum Delta: %lu", sMinDelta); DebugMessageN1(" Frequency: %f", sFrequency); DebugMessageN1(" To Nanos Numerator: %lu", sToNanosNumerator); DebugMessageN1(" To Nanos Denominator: %lu", sToNanosDenominator); DebugMessageN1(" From Nanos Numerator: %lu", sFromNanosNumerator); DebugMessageN1(" From Nanos Denominator: %lu", sFromNanosDenominator); #endif sIsInited = true; }
IOReturn AREngine::clipOutputSamples(const void* inMixBuffer, void* outTargetBuffer, UInt32 inFirstFrame, UInt32 inNumberFrames, const IOAudioStreamFormat* inFormat, IOAudioStream* /*inStream*/) { // figure out what sort of blit we need to do if((inFormat->fSampleFormat == kIOAudioStreamSampleFormatLinearPCM) && inFormat->fIsMixable) { // it's mixable linear PCM, which means we will be calling a blitter, which works in samples not frames Float32* theMixBuffer = (Float32*)inMixBuffer; UInt32 theFirstSample = inFirstFrame * inFormat->fNumChannels; UInt32 theNumberSamples = inNumberFrames * inFormat->fNumChannels; if(inFormat->fNumericRepresentation == kIOAudioStreamNumericRepresentationSignedInt) { // it's some kind of signed integer, which we handle as some kind of even byte length bool nativeEndianInts; #if TARGET_RT_BIG_ENDIAN nativeEndianInts = (inFormat->fByteOrder == kIOAudioStreamByteOrderBigEndian); #else nativeEndianInts = (inFormat->fByteOrder == kIOAudioStreamByteOrderLittleEndian); #endif switch(inFormat->fBitWidth) { case 8: { DebugMessage("AREngine::clipOutputSamples: can't handle signed integers with a bit width of 8 at the moment"); } break; case 16: { SInt16* theTargetBuffer = (SInt16*)outTargetBuffer; if (nativeEndianInts) Float32ToNativeInt16(mHasVectorUnit, &(theMixBuffer[theFirstSample]), &(theTargetBuffer[theFirstSample]), theNumberSamples); else Float32ToSwapInt16(mHasVectorUnit, &(theMixBuffer[theFirstSample]), &(theTargetBuffer[theFirstSample]), theNumberSamples); } break; case 24: { UInt8* theTargetBuffer = (UInt8*)outTargetBuffer; if (nativeEndianInts) Float32ToNativeInt24(mHasVectorUnit, &(theMixBuffer[theFirstSample]), &(theTargetBuffer[3*theFirstSample]), theNumberSamples); else Float32ToSwapInt24(mHasVectorUnit, &(theMixBuffer[theFirstSample]), &(theTargetBuffer[3*theFirstSample]), theNumberSamples); } break; case 32: { SInt32* theTargetBuffer = (SInt32*)outTargetBuffer; if (nativeEndianInts) Float32ToNativeInt32(mHasVectorUnit, &(theMixBuffer[theFirstSample]), &(theTargetBuffer[theFirstSample]), theNumberSamples); else Float32ToSwapInt32(mHasVectorUnit, &(theMixBuffer[theFirstSample]), &(theTargetBuffer[theFirstSample]), theNumberSamples); } break; default: DebugMessageN1("AREngine::clipOutputSamples: can't handle signed integers with a bit width of %d", inFormat->fBitWidth); break; } } else if(inFormat->fNumericRepresentation == kIOAudioStreamNumericRepresentationIEEE754Float) { // it is some kind of floating point format #if TARGET_RT_BIG_ENDIAN if((inFormat->fBitWidth == 32) && (inFormat->fBitDepth == 32) && (inFormat->fByteOrder == kIOAudioStreamByteOrderBigEndian)) #else if((inFormat->fBitWidth == 32) && (inFormat->fBitDepth == 32) && (inFormat->fByteOrder == kIOAudioStreamByteOrderLittleEndian)) #endif { // it's Float32, so we are just going to copy the data Float32* theTargetBuffer = (Float32*)outTargetBuffer; memcpy(&(theTargetBuffer[theFirstSample]), &(theMixBuffer[theFirstSample]), theNumberSamples * sizeof(Float32)); } else { DebugMessageN2("AREngine::clipOutputSamples: can't handle floats with a bit width of %d, bit depth of %d, and/or the given byte order", inFormat->fBitWidth, inFormat->fBitDepth); } } } else { // it's not linear PCM or it's not mixable, so just copy the data into the target buffer SInt8* theMixBuffer = (SInt8*)inMixBuffer; SInt8* theTargetBuffer = (SInt8*)outTargetBuffer; UInt32 theFirstByte = inFirstFrame * (inFormat->fBitWidth / 8) * inFormat->fNumChannels; UInt32 theNumberBytes = inNumberFrames * (inFormat->fBitWidth / 8) * inFormat->fNumChannels; memcpy(&(theTargetBuffer[theFirstByte]), &(theMixBuffer[theFirstByte]), theNumberBytes); } return kIOReturnSuccess; }
bool ZKMORHP_IOThreadSlave::WorkLoopInit() { // grab the IO guard bool wasLocked = mIOGuard.Lock(); bool isInNeedOfResynch = false; // initialize some stuff mWorkLoopPhase = kInitializingPhase; mIOCycleCounter = 0; mOverloadCounter = 0; CAPropertyAddress theIsRunningAddress(kAudioDevicePropertyDeviceIsRunning); mDevice->GetIOCycleTelemetry().IOCycleInitializeBegin(mIOCycleCounter); try { // and signal that the IO thread is running mIOGuard.NotifyAll(); // initialize the work loop stopping conditions mStopWorkLoop = false; // Tell the device that the IO thread has initialized. Note that we unlock around this call // due to the fact that IOCycleInitialize might not return for a while because it might // have to wait for the hardware to start. if(wasLocked) { mIOGuard.Unlock(); } // tell the device that the IO cycle is initializing to start the timing services mDevice->StartIOCycleTimingServices(); // set the device state to know the engine is running mDevice->IOEngineStarted(); // notify clients that the engine is running mDevice->PropertiesChanged(1, &theIsRunningAddress); // re-lock the guard wasLocked = mIOGuard.Lock(); // make sure the thread is still running before moving on if(!mStopWorkLoop) { // set the time constraints for the IOThread SetTimeConstraints(); // initialize the clock mDevice->EstablishIOCycleAnchorTime(mAnchorTime); mFrameCounter = 0; #if Offset_For_Input if(mDevice->HasInputStreams()) { // the first sleep cycle as to be at least the input safety offset and a buffer's // worth of time to be sure that the input data is all there mFrameCounter += mDevice->GetSafetyOffset(true); } #endif // enter the work loop mWorkLoopPhase = kRunningPhase; isInNeedOfResynch = false; mDevice->GetIOCycleTelemetry().IOCycleInitializeEnd(mIOCycleCounter, mAnchorTime); // the work loop interation is called from another thread // while(!mStopWorkLoop) // { // WorkLoopIteration(isInNeedOfResynch, wasLocked); // } if (wasLocked) mIOGuard.Unlock(); } } catch(const CAException& inException) { DebugMessageN1("ZKMORHP_IOThreadSlave::WorkLoopInit: Caught a CAException, code == %ld", (long int)inException.GetError()); } catch(...) { DebugMessage("ZKMORHP_IOThreadSlave::WorkLoopInit: Caught an unknown exception."); } return isInNeedOfResynch; }
void HP_IOThread::WorkLoop() { // grab the IO guard bool wasLocked = mIOGuard.Lock(); // initialize some stuff mWorkLoopPhase = kInitializingPhase; mIOCycleCounter = 0; mOverloadCounter = 0; CAPropertyAddress theIsRunningAddress(kAudioDevicePropertyDeviceIsRunning); #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleInitializeBegin(mIOCycleCounter); #else HAL_IOCYCLEINITIALIZEBEGIN(mIOCycleCounter); #endif try { // and signal that the IO thread is running mIOGuard.NotifyAll(); // initialize the work loop stopping conditions mStopWorkLoop = false; // Tell the device that the IO thread has initialized. Note that we unlock around this call // due to the fact that IOCycleInitialize might not return for a while because it might // have to wait for the hardware to start. if(wasLocked) { mIOGuard.Unlock(); } // tell the device that the IO cycle is initializing to start the timing services mDevice->StartIOCycleTimingServices(); // set the device state to know the engine is running mDevice->IOEngineStarted(); // notify clients that the engine is running mDevice->PropertiesChanged(1, &theIsRunningAddress); // re-lock the guard wasLocked = mIOGuard.Lock(); // make sure the thread is still running before moving on if(!mStopWorkLoop) { // set the time constraints for the IOThread SetTimeConstraints(); // initialize the clock mDevice->EstablishIOCycleAnchorTime(mAnchorTime); mFrameCounter = 0; #if Offset_For_Input if(mDevice->HasInputStreams()) { // the first sleep cycle as to be at least the input safety offset and a buffer's // worth of time to be sure that the input data is all there mFrameCounter += mDevice->GetSafetyOffset(true); } #endif // get the current time AudioTimeStamp theCurrentTime; mDevice->GetCurrentTime(theCurrentTime); // enter the work loop mWorkLoopPhase = kRunningPhase; bool isInNeedOfResynch = false; bool isCheckingForOverloads = false; Float64 theIOBufferFrameSize = mDevice->GetIOBufferFrameSize(); #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleInitializeEnd(mIOCycleCounter, mAnchorTime); mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime); #else HAL_IOCYCLEINITIALIZEEND(mIOCycleCounter, mAnchorTime); HAL_IOCYCLEWORKLOOPBEGIN(mIOCycleCounter, theCurrentTime); #endif while(!mStopWorkLoop) { // get the new IO buffer frame size Float64 theNewIOBufferFrameSize = mDevice->GetIOBufferFrameSize(); // initialize the next wake up time AudioTimeStamp theNextWakeUpTime = CAAudioTimeStamp::kZero; theNextWakeUpTime.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid; // get the current time mDevice->GetCurrentTime(theCurrentTime); // we have to run a special, untimed IO cycle if the IO buffer size changed if((theNewIOBufferFrameSize != theIOBufferFrameSize) && (theCurrentTime.mSampleTime >= (mAnchorTime.mSampleTime + mFrameCounter))) { // mark the end of the previous cycle #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleWorkLoopEnd(mIOCycleCounter, theCurrentTime, theNextWakeUpTime); #else HAL_IOCYCLEWORKLOOPEND(mIOCycleCounter, theCurrentTime, theNextWakeUpTime); #endif // increment the cycle counter ++mIOCycleCounter; // increment the frame counter mFrameCounter += theIOBufferFrameSize; // the new cycle is starting #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime); #else HAL_IOCYCLEWORKLOOPBEGIN(mIOCycleCounter, theCurrentTime); #endif // do the IO, note that we don't need to update the timing services for this special cycle isInNeedOfResynch = PerformIO(theCurrentTime, theIOBufferFrameSize); // turn off overload checking for the next cycle to be nice to clients isCheckingForOverloads = false; } // calculate the next wake up time if(CalculateNextWakeUpTime(theCurrentTime, theIOBufferFrameSize, theNextWakeUpTime, isCheckingForOverloads, isInNeedOfResynch, wasLocked)) { // sleep until the next wake up time #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleWorkLoopEnd(mIOCycleCounter, theCurrentTime, theNextWakeUpTime); #else HAL_IOCYCLEWORKLOOPEND(mIOCycleCounter, theCurrentTime, theNextWakeUpTime); #endif mIOGuard.WaitUntil(CAHostTimeBase::ConvertToNanos(theNextWakeUpTime.mHostTime)); // increment the cycle counter ++mIOCycleCounter; // make sure overload checking is enabled isCheckingForOverloads = true; // do IO if the thread wasn't stopped if(!mStopWorkLoop) { // get the current time mDevice->GetCurrentTime(theCurrentTime); if(theCurrentTime.mSampleTime >= (mAnchorTime.mSampleTime + mFrameCounter)) { // increment the frame counter mFrameCounter += theIOBufferFrameSize; // refresh the current buffer size theIOBufferFrameSize = theNewIOBufferFrameSize; // the new cycle is starting #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime); #else HAL_IOCYCLEWORKLOOPBEGIN(mIOCycleCounter, theCurrentTime); #endif if(mDevice->UpdateIOCycleTimingServices()) { // something unexpected happenned with the time stamp, so resynch prior to doing IO AudioTimeStamp theNewAnchor = CAAudioTimeStamp::kZero; theNewAnchor.mSampleTime = 0; theNewAnchor.mHostTime = 0; theNewAnchor.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid; if(mDevice->EstablishIOCycleAnchorTime(theNewAnchor)) { Resynch(&theNewAnchor, false); } else { Resynch(NULL, false); } // re-get the current time too mDevice->GetCurrentTime(theCurrentTime); // mark the telemetry #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().Resynch(GetIOCycleNumber(), mAnchorTime); #else HAL_RESYNCH(GetIOCycleNumber(), mAnchorTime); #endif } // do the IO isInNeedOfResynch = PerformIO(theCurrentTime, theIOBufferFrameSize); } } } else { // calculating the next wake up time failed, so we just stop everything (which // will get picked up when the commands are executed mDevice->ClearAllCommands(); mDevice->Do_StopAllIOProcs(); } // execute any deferred commands mDevice->ExecuteAllCommands(); } } mWorkLoopPhase = kTeardownPhase; #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleTeardownBegin(mIOCycleCounter); #else HAL_IOCYCLETEARDOWNBEGIN(mIOCycleCounter); #endif // the work loop has finished, clear the time constraints ClearTimeConstraints(); // tell the device that the IO thread is torn down mDevice->StopIOCycleTimingServices(); } catch(const CAException& inException) { DebugMessageN1("HP_IOThread::WorkLoop: Caught a CAException, code == %ld", (long int)inException.GetError()); } catch(...) { DebugMessage("HP_IOThread::WorkLoop: Caught an unknown exception."); } // set the device state to know the engine has stopped mDevice->IOEngineStopped(); // Notify clients that the IO thread is stopping. Note that we unlock around this call // due to the fact that clients might want to call back into the HAL. if(wasLocked) { mIOGuard.Unlock(); } // Notify clients that the IO thread is stopping mDevice->PropertiesChanged(1, &theIsRunningAddress); // re-lock the guard wasLocked = mIOGuard.Lock(); #if Use_HAL_Telemetry mDevice->GetIOCycleTelemetry().IOCycleTeardownEnd(mIOCycleCounter); #else HAL_IOCYCLETEARDOWNEND(mIOCycleCounter); #endif mWorkLoopPhase = kNotRunningPhase; mIOGuard.NotifyAll(); mIOCycleCounter = 0; if(wasLocked) { mIOGuard.Unlock(); } }
void ZKMORHP_IOThreadSlave::WorkLoopIteration(bool& isInNeedOfResynch) { if (mStopWorkLoop) { WorkLoopTeardown(); return; } try { bool wasLocked = mIOGuard.Lock(); // bool wasLocked; wasLocked = mIOGuard.Try(wasLocked); // get the current time AudioTimeStamp theCurrentTime; mDevice->GetCurrentTime(theCurrentTime); // increment the counter ++mIOCycleCounter; // do IO if the thread wasn't stopped if(!mStopWorkLoop) { if(theCurrentTime.mSampleTime >= (mAnchorTime.mSampleTime + mFrameCounter)) { // increment the frame counter mFrameCounter += mDevice->GetIOBufferFrameSize(); // the new cycle is starting mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime); if(mDevice->UpdateIOCycleTimingServices()) { // something unexpected happened with the time stamp, so resynch prior to doing IO AudioTimeStamp theNewAnchor = CAAudioTimeStamp::kZero; theNewAnchor.mSampleTime = 0; theNewAnchor.mHostTime = 0; theNewAnchor.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid; if(mDevice->EstablishIOCycleAnchorTime(theNewAnchor)) { Resynch(&theNewAnchor, false); } else { Resynch(NULL, false); } // re-get the current time too mDevice->GetCurrentTime(theCurrentTime); } // do the IO isInNeedOfResynch = PerformIO(theCurrentTime); } } // calculate the next wake up time AudioTimeStamp theNextWakeUpTime = CAAudioTimeStamp::kZero; theNextWakeUpTime.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid; // bool wasLocked = false; // bool wasLocked = mIOGuard.Lock(); if(CalculateNextWakeUpTime(theCurrentTime, theNextWakeUpTime, isInNeedOfResynch, wasLocked)) { mDevice->GetIOCycleTelemetry().IOCycleWorkLoopEnd(mIOCycleCounter, theCurrentTime, theNextWakeUpTime); } // execute any deferred commands mDevice->ExecuteAllCommands(); if (wasLocked) mIOGuard.Unlock(); } catch(const CAException& inException) { DebugMessageN1("ZKMORHP_IOThreadSlave::WorkLoopIteration: Caught a CAException, code == %ld", (long int)inException.GetError()); } catch(...) { DebugMessage("ZKMORHP_IOThreadSlave::WorkLoopIteration: Caught an unknown exception."); } }
// _______________________________________________________________________________________ // OSStatus CAAudioFile::ReadInputProc( AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData) { CAAudioFile *This = static_cast<CAAudioFile *>(inUserData); #if 0 SInt64 remainingPacketsInFile = This->mNumberPackets - This->mPacketMark; if (remainingPacketsInFile <= 0) { *ioNumberDataPackets = 0; ioData->mBuffers[0].mDataByteSize = 0; if (outDataPacketDescription) *outDataPacketDescription = This->mPacketDescs; #if VERBOSE_IO printf("CAAudioFile::ReadInputProc: EOF\n"); #endif return noErr; // not eofErr; EOF is signified by 0 packets/0 bytes } #endif // determine how much to read AudioBufferList *readBuffer; UInt32 readPackets; if (inAudioConverter != NULL) { // getting called from converter, need to use our I/O buffer readBuffer = &This->mIOBufferList; readPackets = This->mIOBufferSizePackets; } else { // getting called directly from ReadPackets, use supplied buffer if (This->mFileMaxPacketSize == 0) return kExtAudioFileError_MaxPacketSizeUnknown; readBuffer = ioData; readPackets = std::min(*ioNumberDataPackets, readBuffer->mBuffers[0].mDataByteSize / This->mFileMaxPacketSize); // don't attempt to read more packets than will fit in the buffer } // don't try to read past EOF // if (readPackets > remainingPacketsInFile) // readPackets = remainingPacketsInFile; // don't read more packets than necessary to produce the requested amount of converted data if (readPackets > This->mMaxPacketsToRead) { #if VERBOSE_IO printf("CAAudioFile::ReadInputProc: limiting read to %ld packets (from %ld)\n", This->mMaxPacketsToRead, readPackets); #endif readPackets = This->mMaxPacketsToRead; } // read UInt32 bytesRead; OSStatus err; StartTiming(This, read); StartTiming(This, readinconv); err = AudioFileReadPackets(This->mAudioFile, This->mUseCache, &bytesRead, This->mPacketDescs, This->mPacketMark, &readPackets, readBuffer->mBuffers[0].mData); #if CAAUDIOFILE_PROFILE if (This->mInConverter) ElapsedTime(This, readinconv, This->mTicksInReadInConverter); #endif ElapsedTime(This, read, This->mTicksInIO); if (err) { DebugMessageN1("Error %ld from AudioFileReadPackets!!!\n", err); return err; } #if VERBOSE_IO printf("CAAudioFile::ReadInputProc: read %ld packets (%qd-%qd), %ld bytes, err %ld\n", readPackets, This->mPacketMark, This->mPacketMark + readPackets, bytesRead, err); #if VERBOSE_IO >= 2 if (This->mPacketDescs) { for (UInt32 i = 0; i < readPackets; ++i) { printf(" read packet %qd : offset %qd, length %ld\n", This->mPacketMark + i, This->mPacketDescs[i].mStartOffset, This->mPacketDescs[i].mDataByteSize); } } printf(" read buffer:"); CAShowAudioBufferList(readBuffer, 0, 4); #endif #endif if (readPackets == 0) { *ioNumberDataPackets = 0; ioData->mBuffers[0].mDataByteSize = 0; return noErr; } if (outDataPacketDescription) *outDataPacketDescription = This->mPacketDescs; ioData->mBuffers[0].mDataByteSize = bytesRead; ioData->mBuffers[0].mData = readBuffer->mBuffers[0].mData; This->mPacketMark += readPackets; if (This->mClientDataFormat.mFramesPerPacket != 1) { // for PCM client formats we update in Read // but for non-PCM client format (weird case) we must update here/now if (This->mFileDataFormat.mFramesPerPacket > 0) This->mFrameMark += readPackets * This->mFileDataFormat.mFramesPerPacket; else { for (UInt32 i = 0; i < readPackets; ++i) This->mFrameMark += This->mPacketDescs[i].mVariableFramesInPacket; } } *ioNumberDataPackets = readPackets; return noErr; }
bool HP_Object::IsSubClass(AudioClassID inClassID, AudioClassID inBaseClassID) { bool theAnswer = false; switch(inBaseClassID) { case kAudioObjectClassID: { // all classes are subclasses of AudioObject theAnswer = true; } break; case kAudioControlClassID: { switch(inClassID) { case kAudioControlClassID: case kAudioLevelControlClassID: case kAudioBooleanControlClassID: case kAudioSelectorControlClassID: case kAudioStereoPanControlClassID: case kAudioVolumeControlClassID: case kAudioLFEVolumeControlClassID: case kAudioBootChimeVolumeControlClassID: case kAudioMuteControlClassID: case kAudioSoloControlClassID: case kAudioJackControlClassID: case kAudioLFEMuteControlClassID: case kAudioISubOwnerControlClassID: case kAudioDataSourceControlClassID: case kAudioDataDestinationControlClassID: case kAudioClockSourceControlClassID: case kAudioLineLevelControlClassID: { theAnswer = true; } break; }; } break; case kAudioLevelControlClassID: { switch(inClassID) { case kAudioLevelControlClassID: case kAudioVolumeControlClassID: case kAudioLFEVolumeControlClassID: case kAudioBootChimeVolumeControlClassID: { theAnswer = true; } break; }; } break; case kAudioBooleanControlClassID: { switch(inClassID) { case kAudioBooleanControlClassID: case kAudioMuteControlClassID: case kAudioSoloControlClassID: case kAudioJackControlClassID: case kAudioLFEMuteControlClassID: case kAudioISubOwnerControlClassID: { theAnswer = true; } break; }; } break; case kAudioSelectorControlClassID: { switch(inClassID) { case kAudioSelectorControlClassID: case kAudioDataSourceControlClassID: case kAudioDataDestinationControlClassID: case kAudioClockSourceControlClassID: case kAudioLineLevelControlClassID: { theAnswer = true; } break; }; } break; case kAudioDeviceClassID: { switch(inClassID) { case kAudioDeviceClassID: case kAudioAggregateDeviceClassID: { theAnswer = true; } break; }; } break; // leaf classes case kAudioStereoPanControlClassID: case kAudioVolumeControlClassID: case kAudioLFEVolumeControlClassID: case kAudioBootChimeVolumeControlClassID: case kAudioMuteControlClassID: case kAudioSoloControlClassID: case kAudioJackControlClassID: case kAudioLFEMuteControlClassID: case kAudioISubOwnerControlClassID: case kAudioDataSourceControlClassID: case kAudioDataDestinationControlClassID: case kAudioClockSourceControlClassID: case kAudioLineLevelControlClassID: case kAudioSystemObjectClassID: case kAudioPlugInClassID: case kAudioStreamClassID: case kAudioAggregateDeviceClassID: case kAudioSubDeviceClassID: { theAnswer = inClassID == inBaseClassID; } break; default: { #if CoreAudio_Debug char theClassIDString[5] = CA4CCToCString(inBaseClassID); DebugMessageN1("HP_Object::IsSubClass: unknown base class '%s'", theClassIDString); #endif theAnswer = inClassID == inBaseClassID; } break; }; return theAnswer; }
void ZKMORHP_IOThreadSlave::WorkLoop() { // grab the IO guard bool wasLocked = mIOGuard.Lock(); // initialize some stuff mWorkLoopPhase = kInitializingPhase; mIOCycleCounter = 0; mOverloadCounter = 0; CAPropertyAddress theIsRunningAddress(kAudioDevicePropertyDeviceIsRunning); mDevice->GetIOCycleTelemetry().IOCycleInitializeBegin(mIOCycleCounter); try { // and signal that the IO thread is running mIOGuard.NotifyAll(); // initialize the work loop stopping conditions mStopWorkLoop = false; // Tell the device that the IO thread has initialized. Note that we unlock around this call // due to the fact that IOCycleInitialize might not return for a while because it might // have to wait for the hardware to start. if(wasLocked) { mIOGuard.Unlock(); } // tell the device that the IO cycle is initializing to start the timing services mDevice->StartIOCycleTimingServices(); // set the device state to know the engine is running mDevice->IOEngineStarted(); // notify clients that the engine is running mDevice->PropertiesChanged(1, &theIsRunningAddress); // re-lock the guard wasLocked = mIOGuard.Lock(); // make sure the thread is still running before moving on if(!mStopWorkLoop) { // set the time constraints for the IOThread SetTimeConstraints(); // initialize the clock mDevice->EstablishIOCycleAnchorTime(mAnchorTime); mFrameCounter = 0; #if Offset_For_Input if(mDevice->HasInputStreams()) { // the first sleep cycle as to be at least the input safety offset and a buffer's // worth of time to be sure that the input data is all there mFrameCounter += mDevice->GetSafetyOffset(true); } #endif // enter the work loop mWorkLoopPhase = kRunningPhase; bool isInNeedOfResynch = false; mDevice->GetIOCycleTelemetry().IOCycleInitializeEnd(mIOCycleCounter, mAnchorTime); while(!mStopWorkLoop) { // get the current time AudioTimeStamp theCurrentTime; mDevice->GetCurrentTime(theCurrentTime); // calculate the next wake up time AudioTimeStamp theNextWakeUpTime = CAAudioTimeStamp::kZero; theNextWakeUpTime.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid; if(CalculateNextWakeUpTime(theCurrentTime, theNextWakeUpTime, isInNeedOfResynch, wasLocked)) { // sleep until the next wake up time mDevice->GetIOCycleTelemetry().IOCycleWorkLoopEnd(mIOCycleCounter, theCurrentTime, theNextWakeUpTime); mIOGuard.WaitUntil(CAHostTimeBase::ConvertToNanos(theNextWakeUpTime.mHostTime)); // increment the counter ++mIOCycleCounter; // do IO if the thread wasn't stopped if(!mStopWorkLoop) { // get the current time mDevice->GetCurrentTime(theCurrentTime); #if Log_SchedulingLatency // check to see if we have incurred a large scheduling latency if(theCurrentTime.mHostTime > (theNextWakeUpTime.mHostTime + mAllowedLatency)) { // log it mLatencyLog->Capture(theNextWakeUpTime.mHostTime - mAllowedLatency, theCurrentTime.mHostTime, true); // print how late we are DebugMessageN1("HP_IOThread::WorkLoop: woke up late by %f milliseconds", ((Float64)CAHostTimeBase::ConvertToNanos(theCurrentTime.mHostTime - theNextWakeUpTime.mHostTime)) / (1000.0 * 1000.0)); } #endif if(theCurrentTime.mSampleTime >= (mAnchorTime.mSampleTime + mFrameCounter)) { // increment the frame counter mFrameCounter += mDevice->GetIOBufferFrameSize(); // the new cycle is starting mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime); if(mDevice->UpdateIOCycleTimingServices()) { // something unexpected happenned with the time stamp, so resynch prior to doing IO AudioTimeStamp theNewAnchor = CAAudioTimeStamp::kZero; theNewAnchor.mSampleTime = 0; theNewAnchor.mHostTime = 0; theNewAnchor.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid; if(mDevice->EstablishIOCycleAnchorTime(theNewAnchor)) { Resynch(&theNewAnchor, false); } else { Resynch(NULL, false); } // re-get the current time too mDevice->GetCurrentTime(theCurrentTime); } // do the IO isInNeedOfResynch = PerformIO(theCurrentTime); } } } else { // calculating the next wake up time failed, so we just stop everything (which // will get picked up when the commands are executed mDevice->ClearAllCommands(); mDevice->Do_StopAllIOProcs(); } // execute any deferred commands mDevice->ExecuteAllCommands(); } } mWorkLoopPhase = kTeardownPhase; mDevice->GetIOCycleTelemetry().IOCycleTeardownBegin(mIOCycleCounter); // the work loop has finished, clear the time constraints ClearTimeConstraints(); // tell the device that the IO thread is torn down mDevice->StopIOCycleTimingServices(); } catch(const CAException& inException) { DebugMessageN1("HP_IOThread::WorkLoop: Caught a CAException, code == %ld", (long int)inException.GetError()); } catch(...) { DebugMessage("HP_IOThread::WorkLoop: Caught an unknown exception."); } // set the device state to know the engine has stopped mDevice->IOEngineStopped(); // Notify clients that the IO thread is stopping. Note that we unlock around this call // due to the fact that clients might want to call back into the HAL. if(wasLocked) { mIOGuard.Unlock(); } // Notify clients that the IO thread is stopping mDevice->PropertiesChanged(1, &theIsRunningAddress); // re-lock the guard wasLocked = mIOGuard.Lock(); mDevice->GetIOCycleTelemetry().IOCycleTeardownEnd(mIOCycleCounter); mWorkLoopPhase = kNotRunningPhase; mIOGuard.NotifyAll(); mIOCycleCounter = 0; if(wasLocked) { mIOGuard.Unlock(); } }
bool ZKMORHP_IOThreadSlave::CalculateNextWakeUpTime(const AudioTimeStamp& inCurrentTime, AudioTimeStamp& outNextWakeUpTime, bool inMustResynch, bool& inIOGuardWasLocked) { bool theAnswer = true; // static const Float64 kOverloadThreshold = 0.050; static const Float64 kOverloadThreshold = 0.000; bool isDone = false; AudioTimeStamp theCurrentTime = inCurrentTime; int theNumberIterations = 0; while(!isDone) { ++theNumberIterations; Float64 theIOBufferFrameSize = mDevice->GetIOBufferFrameSize(); // set up the outNextWakeUpTime outNextWakeUpTime = CAAudioTimeStamp::kZero; outNextWakeUpTime.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid; // set up the overload time AudioTimeStamp theOverloadTime = CAAudioTimeStamp::kZero; theOverloadTime.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid; // calculate the sample time for the next wake up time AudioTimeStamp theSampleTime = mAnchorTime; theSampleTime.mFlags = kAudioTimeStampSampleTimeValid; theSampleTime.mSampleTime += mFrameCounter; theSampleTime.mSampleTime += theIOBufferFrameSize; // translate that to a host time mDevice->TranslateTime(theSampleTime, outNextWakeUpTime); // calculate the overload time Float64 theReservedAmount = std::max(0.0, mIOCycleUsage - kOverloadThreshold); theSampleTime = mAnchorTime; theSampleTime.mFlags = kAudioTimeStampSampleTimeValid; theSampleTime.mSampleTime += mFrameCounter; theSampleTime.mSampleTime += theReservedAmount * theIOBufferFrameSize; // translate that to a host time mDevice->TranslateTime(theSampleTime, theOverloadTime); if(inMustResynch || (theCurrentTime.mHostTime >= theOverloadTime.mHostTime)) { // tell the device what happenned mDevice->GetIOCycleTelemetry().IOCycleWorkLoopOverloadBegin(mIOCycleCounter, theCurrentTime, theOverloadTime); // the current time is beyond the overload time, have to resynchronize #if Log_Resynchs if(inMustResynch) { DebugMessageN1("ZKMORHP_IOThreadSlave::CalculateNextWakeUpTime: resynch was forced %d", theNumberIterations); } else { DebugMessageN1("ZKMORHP_IOThreadSlave::CalculateNextWakeUpTime: wake up time is in the past... resynching %d", theNumberIterations); DebugMessageN3(" Now: %qd Overload: %qd Difference: %qd", CAHostTimeBase::ConvertToNanos(theCurrentTime.mHostTime), CAHostTimeBase::ConvertToNanos(theOverloadTime.mHostTime), CAHostTimeBase::ConvertToNanos(theCurrentTime.mHostTime - theOverloadTime.mHostTime)); } #endif // notify clients that the overload has taken place if(inIOGuardWasLocked) { mIOGuard.Unlock(); } CAPropertyAddress theOverloadAddress(kAudioDeviceProcessorOverload); mDevice->PropertiesChanged(1, &theOverloadAddress); inIOGuardWasLocked = mIOGuard.Lock(); // re-anchor at the current time theCurrentTime.mSampleTime = 0; theCurrentTime.mHostTime = 0; if(mDevice->EstablishIOCycleAnchorTime(theCurrentTime)) { Resynch(&theCurrentTime, false); } else { theAnswer = false; isDone = true; } // reset the forced resynch flag inMustResynch = false; mDevice->GetIOCycleTelemetry().IOCycleWorkLoopOverloadEnd(mIOCycleCounter, mAnchorTime); } else { // still within the limits isDone = true; } } // adjust the counter depending on what happenned if(theNumberIterations > 1) { // we went through the calculation more than once, which means an overload happenned ++mOverloadCounter; } else { // only did the calculation once, so no overload occurred mOverloadCounter = 0; } return theAnswer; }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OSStatus OALBuffer::AddAudioData(char* inAudioData, UInt32 inAudioDataSize, ALenum format, ALsizei freq, bool inPreConvertToHalFormat) { #if LOG_VERBOSE DebugMessageN6("OALBuffer::AddAudioData() - OALBuffer:inAudioData:inAudioDataSize:format:freq:inPreConvertToHalFormat = %ld:%p:%ld:%d:%d:%d", (long int) mSelfToken, inAudioData, (long int) inAudioDataSize, format, freq, inPreConvertToHalFormat); #endif // creates memory if needed // reallocs if needed // returns an error if buffer is in use CAGuard::Locker bufferLock(mBufferLock); try { if (!IsFormatSupported(format)) throw ((OSStatus) AL_INVALID_VALUE); // this is not a valid buffer token or is an invalid format #if USE_SOURCE_LIST_MUTEX bool wasLocked = mSourceListGuard.Lock(); #endif // don't allow if the buffer is in a queue UInt32 attachedCount = mAttachedSourceList->Size(); #if USE_SOURCE_LIST_MUTEX if (wasLocked) mSourceListGuard.Unlock(); #endif if (attachedCount > 0) { DebugMessage("WAITING: AddAudioData ---> WaitOneRenderCycle"); // Let a render cycle go by and try again WaitOneRenderCycle(); #if USE_SOURCE_LIST_MUTEX wasLocked = mSourceListGuard.Lock(); #endif attachedCount = mAttachedSourceList->Size(); #if USE_SOURCE_LIST_MUTEX if (wasLocked) mSourceListGuard.Unlock(); #endif if (attachedCount > 0){ DebugMessageN2("OALBuffer::AddAudioData: buffer ATTACHMENT > 0 - mSelfToken:mAttachedSourceList->Size() = %ld:%ld", (long int) mSelfToken, (long int) mAttachedSourceList->Size()); throw ((OSStatus) AL_INVALID_OPERATION); } } if (mAppOwnsBufferMemory) { mData = NULL; // we were using the apps memory before so just initialize mData incase we fail mAppOwnsBufferMemory = false; } mPreConvertedDataSize = (UInt32) inAudioDataSize; // do not pre-convert stereo sounds, let the AC do the deinterleaving OSStatus result = noErr; if (!inPreConvertToHalFormat || ((format == AL_FORMAT_STEREO16) || (format == AL_FORMAT_STEREO8))) { if (mData != NULL) { if (mDataSize != (UInt32) inAudioDataSize) { mDataSize = (UInt32) inAudioDataSize; void *newDataPtr = realloc(mData, mDataSize); mData = (UInt8 *) newDataPtr; } } else { mDataSize = (UInt32) inAudioDataSize; mData = (UInt8 *) malloc (mDataSize); } if (mData) { result = FillInASBD(mDataFormat, format, freq); THROW_RESULT mPreConvertedDataFormat.SetFrom(mDataFormat); // make sure they are the same so original format info can be returned to caller memcpy (mData, inAudioData, mDataSize); } } else { #if LOG_EXTRAS DebugMessage("alBufferData called: Converting Data Now"); #endif result = ConvertDataForBuffer(inAudioData, inAudioDataSize, format, freq); // convert the data to the mixer's format and copy to the buffer THROW_RESULT } } catch (OSStatus result) { DebugMessageN1("OALBuffer::AddAudioData Failed - err = %ld\n", (long int) result); alSetError(result); throw result; } catch (...) { DebugMessage("OALBuffer::AddAudioData Failed"); alSetError(AL_INVALID_OPERATION); throw -1; } return noErr; }
bool CAGuard::WaitFor(UInt64 inNanos) { bool theAnswer = false; #if TARGET_OS_MAC ThrowIf(!pthread_equal(pthread_self(), mOwner), CAException(1), "CAGuard::WaitFor: A thread has to have locked a guard be for it can wait"); #if Log_TimedWaits DebugMessageN1("CAGuard::WaitFor: waiting %.0f", (Float64)inNanos); #endif struct timespec theTimeSpec; static const UInt64 kNanosPerSecond = 1000000000ULL; if(inNanos >= kNanosPerSecond) { theTimeSpec.tv_sec = static_cast<UInt32>(inNanos / kNanosPerSecond); theTimeSpec.tv_nsec = static_cast<UInt32>(inNanos % kNanosPerSecond); } else { theTimeSpec.tv_sec = 0; theTimeSpec.tv_nsec = static_cast<UInt32>(inNanos); } #if Log_TimedWaits || Log_Latency || Log_Average_Latency UInt64 theStartNanos = CAHostTimeBase::GetCurrentTimeInNanos(); #endif mOwner = 0; #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAGuard::WaitFor: thread %p is waiting on %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner); #endif OSStatus theError = pthread_cond_timedwait_relative_np(&mCondVar, &mMutex, &theTimeSpec); ThrowIf((theError != 0) && (theError != ETIMEDOUT), CAException(theError), "CAGuard::WaitFor: Wait got an error"); mOwner = pthread_self(); #if Log_TimedWaits || Log_Latency || Log_Average_Latency UInt64 theEndNanos = CAHostTimeBase::GetCurrentTimeInNanos(); #endif #if Log_TimedWaits DebugMessageN1("CAGuard::WaitFor: waited %.0f", (Float64)(theEndNanos - theStartNanos)); #endif #if Log_Latency DebugMessageN1("CAGuard::WaitFor: latency %.0f", (Float64)((theEndNanos - theStartNanos) - inNanos)); #endif #if Log_Average_Latency ++mAverageLatencyCount; mAverageLatencyAccumulator += (theEndNanos - theStartNanos) - inNanos; if(mAverageLatencyCount >= 50) { DebugMessageN2("CAGuard::WaitFor: average latency %.3f ns over %ld waits", mAverageLatencyAccumulator / mAverageLatencyCount, mAverageLatencyCount); mAverageLatencyCount = 0; mAverageLatencyAccumulator = 0.0; } #endif #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAGuard::WaitFor: thread %p waited on %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner); #endif theAnswer = theError == ETIMEDOUT; #elif TARGET_OS_WIN32 ThrowIf(GetCurrentThreadId() != mOwner, CAException(1), "CAGuard::WaitFor: A thread has to have locked a guard be for it can wait"); #if Log_TimedWaits DebugMessageN1("CAGuard::WaitFor: waiting %.0f", (Float64)inNanos); #endif // the time out is specified in milliseconds(!) UInt32 theWaitTime = static_cast<UInt32>(inNanos / 1000000ULL); #if Log_TimedWaits || Log_Latency || Log_Average_Latency UInt64 theStartNanos = CAHostTimeBase::GetCurrentTimeInNanos(); #endif mOwner = 0; #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAGuard::WaitFor: thread %lu is waiting on %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif ReleaseMutex(mMutex); HANDLE theHandles[] = { mMutex, mEvent }; OSStatus theError = WaitForMultipleObjects(2, theHandles, true, theWaitTime); ThrowIf((theError != WAIT_OBJECT_0) && (theError != WAIT_TIMEOUT), CAException(GetLastError()), "CAGuard::WaitFor: Wait got an error"); mOwner = GetCurrentThreadId(); ResetEvent(mEvent); #if Log_TimedWaits || Log_Latency || Log_Average_Latency UInt64 theEndNanos = CAHostTimeBase::GetCurrentTimeInNanos(); #endif #if Log_TimedWaits DebugMessageN1("CAGuard::WaitFor: waited %.0f", (Float64)(theEndNanos - theStartNanos)); #endif #if Log_Latency DebugMessageN1("CAGuard::WaitFor: latency %.0f", (Float64)((theEndNanos - theStartNanos) - inNanos)); #endif #if Log_Average_Latency ++mAverageLatencyCount; mAverageLatencyAccumulator += (theEndNanos - theStartNanos) - inNanos; if(mAverageLatencyCount >= 50) { DebugMessageN2("CAGuard::WaitFor: average latency %.3f ns over %ld waits", mAverageLatencyAccumulator / mAverageLatencyCount, mAverageLatencyCount); mAverageLatencyCount = 0; mAverageLatencyAccumulator = 0.0; } #endif #if Log_WaitOwnership DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAGuard::WaitFor: thread %lu waited on %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner); #endif theAnswer = theError == WAIT_TIMEOUT; #endif return theAnswer; }