// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Called by OALSource's BufferQueue::RemoveQueueEntryByIndex() class bool OALBuffer::ReleaseBuffer(OALSource* inSource) { #if LOG_VERBOSE DebugMessageN2("OALBuffer::ReleaseBuffer() - OALBuffer:inSource = %ld:%ld", (long int) mSelfToken, (long int) inSource->GetToken()); #endif bool returnValue = false; #if LOG_EXTRAS DebugMessageN2("OALBuffer::ReleaseBuffer - BufferToken:SourceToken = %ld:%ld", mSelfToken, inSource->GetToken()); #endif #if USE_SOURCE_LIST_MUTEX bool wasLocked = mSourceListGuard.Lock(); #endif // see if this source is in list already if (mAttachedSourceList->SourceExists(inSource) == true) { UInt32 attachmentCount = mAttachedSourceList->DecreaseAttachmentCount(inSource); if (attachmentCount == 0) { //DebugMessageN2("OALBuffer::ReleaseBuffer - BufferToken:SourceToken = %ld:%ld", (long int) mSelfToken, (long int) inSource->GetToken()); mAttachedSourceList->Remove(inSource); returnValue = true; } } #if USE_SOURCE_LIST_MUTEX if (wasLocked) mSourceListGuard.Unlock(); #endif return returnValue; }
bool CAGuard::WaitUntil(UInt64 inNanos) { bool theAnswer = false; UInt64 theCurrentNanos = CAHostTimeBase::GetCurrentTimeInNanos(); #if Log_TimedWaits DebugMessageN2("CAGuard::WaitUntil: now: %.0f, requested: %.0f", (double)theCurrentNanos, (double)inNanos); #endif if(inNanos > theCurrentNanos) { #if Log_Errors if((inNanos - theCurrentNanos) > 1000000000ULL) { DebugMessage("CAGuard::WaitUntil: about to wait for more than a second"); } #endif theAnswer = WaitFor(inNanos - theCurrentNanos); } else { #if Log_Errors DebugMessageN2("CAGuard::WaitUntil: Time has expired before waiting, now: %.0f, requested: %.0f", (double)theCurrentNanos, (double)inNanos); #endif theAnswer = true; } return theAnswer; }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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; }
OALCaptureDevice::OALCaptureDevice (const char* inDeviceName, uintptr_t inSelfToken, UInt32 inSampleRate, UInt32 inFormat, UInt32 inBufferSize) : #if LOG_CAPTUREDEVICE_VERBOSE mSelfToken (inSelfToken), #endif mCurrentError(ALC_NO_ERROR), mCaptureOn(false), mStoreSampleTime(0), mFetchSampleTime(0), mInputUnit(0), mRingBuffer(NULL), mBufferData(NULL), mSampleRateRatio(1.0), mRequestedRingFrames(inBufferSize), mAudioInputPtrs(NULL), mInUseFlag(0) { char *useThisDevice = (char *) inDeviceName; try { // translate the sample rate and data format parameters into an ASBD - this method throws FillInASBD(mRequestedFormat, inFormat, inSampleRate); // inBufferSize must be at least as big as one packet of the requested output formnat if (inBufferSize < mRequestedFormat.mBytesPerPacket) throw ((OSStatus) AL_INVALID_VALUE); // until the ALC_ENUMERATION_EXT extension is supported only use the default input device useThisDevice = NULL; InitializeAU(useThisDevice); if(mRequestedFormat.mSampleRate != mNativeFormat.mSampleRate) { #if LOG_CAPTURE DebugMessageN2("OALCaptureDevice::OALCaptureDevice - Hardware Sample Rate: %5.1f Requested Sample Rate: %5.1f", mNativeFormat.mSampleRate, mRequestedFormat.mSampleRate); #endif mSampleRateRatio = mNativeFormat.mSampleRate / mRequestedFormat.mSampleRate; mBufferData = (UInt8*)malloc(mRequestedFormat.mBytesPerFrame*mRequestedRingFrames*mSampleRateRatio); OSStatus result = AudioConverterNew(&mOutputFormat, &mRequestedFormat, &mAudioConverter); THROW_RESULT } mAudioInputPtrs = CABufferList::New("WriteBufferList", mRequestedFormat); mRingBuffer = new OALRingBuffer(); mRingBuffer->Allocate(mRequestedFormat.mBytesPerFrame, mRequestedRingFrames*mSampleRateRatio); }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Methods called from OALSource objects // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UInt32 OALBuffer::GetFramesToBytes(UInt32 inOffsetInFrames) { #if LOG_VERBOSE DebugMessageN2("OALBuffer::GetFramesToBytes() - OALBuffer:inOffsetInFrames = %ld:%ld", (long int) mSelfToken, (long int) inOffsetInFrames); #endif UInt32 returnValue = 0; if (GetFramesPerPacket() == 1) { // pcm - it's easy returnValue = inOffsetInFrames * GetBytesPerPacket(); } else { // discover which packet conatins the frame we want to offset to // CURRENTLY UNNECESSARY BECAUSE WE ARE ONLY STORING PCM DATA } return returnValue; }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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; }
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; }