diff options
Diffstat (limited to 'libs/appleutility/CoreAudio/PublicUtility/CARingBuffer.cpp')
-rw-r--r-- | libs/appleutility/CoreAudio/PublicUtility/CARingBuffer.cpp | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/libs/appleutility/CoreAudio/PublicUtility/CARingBuffer.cpp b/libs/appleutility/CoreAudio/PublicUtility/CARingBuffer.cpp new file mode 100644 index 0000000000..c78acd2c2d --- /dev/null +++ b/libs/appleutility/CoreAudio/PublicUtility/CARingBuffer.cpp @@ -0,0 +1,319 @@ +/* + File: CARingBuffer.cpp + Abstract: CARingBuffer.h + Version: 1.1 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2014 Apple Inc. All Rights Reserved. + +*/ +#include "CARingBuffer.h" +#include "CABitOperations.h" +#include "CAAutoDisposer.h" +#include "CAAtomic.h" + +#include <stdlib.h> +#include <string.h> +#include <algorithm> +#include <libkern/OSAtomic.h> + +CARingBuffer::CARingBuffer() : + mBuffers(NULL), mNumberChannels(0), mCapacityFrames(0), mCapacityBytes(0) +{ + +} + +CARingBuffer::~CARingBuffer() +{ + Deallocate(); +} + + +void CARingBuffer::Allocate(int nChannels, UInt32 bytesPerFrame, UInt32 capacityFrames) +{ + Deallocate(); + + capacityFrames = NextPowerOfTwo(capacityFrames); + + mNumberChannels = nChannels; + mBytesPerFrame = bytesPerFrame; + mCapacityFrames = capacityFrames; + mCapacityFramesMask = capacityFrames - 1; + mCapacityBytes = bytesPerFrame * capacityFrames; + + // put everything in one memory allocation, first the pointers, then the deinterleaved channels + UInt32 allocSize = (mCapacityBytes + sizeof(Byte *)) * nChannels; + Byte *p = (Byte *)CA_malloc(allocSize); + memset(p, 0, allocSize); + mBuffers = (Byte **)p; + p += nChannels * sizeof(Byte *); + for (int i = 0; i < nChannels; ++i) { + mBuffers[i] = p; + p += mCapacityBytes; + } + + for (UInt32 i = 0; i<kGeneralRingTimeBoundsQueueSize; ++i) + { + mTimeBoundsQueue[i].mStartTime = 0; + mTimeBoundsQueue[i].mEndTime = 0; + mTimeBoundsQueue[i].mUpdateCounter = 0; + } + mTimeBoundsQueuePtr = 0; +} + +void CARingBuffer::Deallocate() +{ + if (mBuffers) { + free(mBuffers); + mBuffers = NULL; + } + mNumberChannels = 0; + mCapacityBytes = 0; + mCapacityFrames = 0; +} + +inline void ZeroRange(Byte **buffers, int nchannels, int offset, int nbytes) +{ + while (--nchannels >= 0) { + memset(*buffers + offset, 0, nbytes); + ++buffers; + } +} + +inline void StoreABL(Byte **buffers, int destOffset, const AudioBufferList *abl, int srcOffset, int nbytes) +{ + int nchannels = abl->mNumberBuffers; + const AudioBuffer *src = abl->mBuffers; + while (--nchannels >= 0) { + if (srcOffset > (int)src->mDataByteSize) continue; + memcpy(*buffers + destOffset, (Byte *)src->mData + srcOffset, std::min(nbytes, (int)src->mDataByteSize - srcOffset)); + ++buffers; + ++src; + } +} + +inline void FetchABL(AudioBufferList *abl, int destOffset, Byte **buffers, int srcOffset, int nbytes) +{ + int nchannels = abl->mNumberBuffers; + AudioBuffer *dest = abl->mBuffers; + while (--nchannels >= 0) { + if (destOffset > (int)dest->mDataByteSize) continue; + memcpy((Byte *)dest->mData + destOffset, *buffers + srcOffset, std::min(nbytes, (int)dest->mDataByteSize - destOffset)); + ++buffers; + ++dest; + } +} + +inline void ZeroABL(AudioBufferList *abl, int destOffset, int nbytes) +{ + int nBuffers = abl->mNumberBuffers; + AudioBuffer *dest = abl->mBuffers; + while (--nBuffers >= 0) { + if (destOffset > (int)dest->mDataByteSize) continue; + memset((Byte *)dest->mData + destOffset, 0, std::min(nbytes, (int)dest->mDataByteSize - destOffset)); + ++dest; + } +} + + +CARingBufferError CARingBuffer::Store(const AudioBufferList *abl, UInt32 framesToWrite, SampleTime startWrite) +{ + if (framesToWrite == 0) + return kCARingBufferError_OK; + + if (framesToWrite > mCapacityFrames) + return kCARingBufferError_TooMuch; // too big! + + SampleTime endWrite = startWrite + framesToWrite; + + if (startWrite < EndTime()) { + // going backwards, throw everything out + SetTimeBounds(startWrite, startWrite); + } else if (endWrite - StartTime() <= mCapacityFrames) { + // the buffer has not yet wrapped and will not need to + } else { + // advance the start time past the region we are about to overwrite + SampleTime newStart = endWrite - mCapacityFrames; // one buffer of time behind where we're writing + SampleTime newEnd = std::max(newStart, EndTime()); + SetTimeBounds(newStart, newEnd); + } + + // write the new frames + Byte **buffers = mBuffers; + int nchannels = mNumberChannels; + int offset0, offset1, nbytes; + SampleTime curEnd = EndTime(); + + if (startWrite > curEnd) { + // we are skipping some samples, so zero the range we are skipping + offset0 = FrameOffset(curEnd); + offset1 = FrameOffset(startWrite); + if (offset0 < offset1) + ZeroRange(buffers, nchannels, offset0, offset1 - offset0); + else { + ZeroRange(buffers, nchannels, offset0, mCapacityBytes - offset0); + ZeroRange(buffers, nchannels, 0, offset1); + } + offset0 = offset1; + } else { + offset0 = FrameOffset(startWrite); + } + + offset1 = FrameOffset(endWrite); + if (offset0 < offset1) + StoreABL(buffers, offset0, abl, 0, offset1 - offset0); + else { + nbytes = mCapacityBytes - offset0; + StoreABL(buffers, offset0, abl, 0, nbytes); + StoreABL(buffers, 0, abl, nbytes, offset1); + } + + // now update the end time + SetTimeBounds(StartTime(), endWrite); + + return kCARingBufferError_OK; // success +} + +void CARingBuffer::SetTimeBounds(SampleTime startTime, SampleTime endTime) +{ + UInt32 nextPtr = mTimeBoundsQueuePtr + 1; + UInt32 index = nextPtr & kGeneralRingTimeBoundsQueueMask; + + mTimeBoundsQueue[index].mStartTime = startTime; + mTimeBoundsQueue[index].mEndTime = endTime; + mTimeBoundsQueue[index].mUpdateCounter = nextPtr; + CAAtomicCompareAndSwap32Barrier(mTimeBoundsQueuePtr, mTimeBoundsQueuePtr + 1, (SInt32*)&mTimeBoundsQueuePtr); +} + +CARingBufferError CARingBuffer::GetTimeBounds(SampleTime &startTime, SampleTime &endTime) +{ + for (int i=0; i<8; ++i) // fail after a few tries. + { + UInt32 curPtr = mTimeBoundsQueuePtr; + UInt32 index = curPtr & kGeneralRingTimeBoundsQueueMask; + CARingBuffer::TimeBounds* bounds = mTimeBoundsQueue + index; + + startTime = bounds->mStartTime; + endTime = bounds->mEndTime; + UInt32 newPtr = bounds->mUpdateCounter; + + if (newPtr == curPtr) + return kCARingBufferError_OK; + } + return kCARingBufferError_CPUOverload; +} + +CARingBufferError CARingBuffer::ClipTimeBounds(SampleTime& startRead, SampleTime& endRead) +{ + SampleTime startTime, endTime; + + CARingBufferError err = GetTimeBounds(startTime, endTime); + if (err) return err; + + if (startRead > endTime || endRead < startTime) { + endRead = startRead; + return kCARingBufferError_OK; + } + + startRead = std::max(startRead, startTime); + endRead = std::min(endRead, endTime); + endRead = std::max(endRead, startRead); + + return kCARingBufferError_OK; // success +} + +CARingBufferError CARingBuffer::Fetch(AudioBufferList *abl, UInt32 nFrames, SampleTime startRead) +{ + if (nFrames == 0) + return kCARingBufferError_OK; + + startRead = std::max(0LL, startRead); + + SampleTime endRead = startRead + nFrames; + + SampleTime startRead0 = startRead; + SampleTime endRead0 = endRead; + + CARingBufferError err = ClipTimeBounds(startRead, endRead); + if (err) return err; + + if (startRead == endRead) { + ZeroABL(abl, 0, nFrames * mBytesPerFrame); + return kCARingBufferError_OK; + } + + SInt32 byteSize = (SInt32)((endRead - startRead) * mBytesPerFrame); + + SInt32 destStartByteOffset = std::max((SInt32)0, (SInt32)((startRead - startRead0) * mBytesPerFrame)); + + if (destStartByteOffset > 0) { + ZeroABL(abl, 0, std::min((SInt32)(nFrames * mBytesPerFrame), destStartByteOffset)); + } + + SInt32 destEndSize = std::max((SInt32)0, (SInt32)(endRead0 - endRead)); + if (destEndSize > 0) { + ZeroABL(abl, destStartByteOffset + byteSize, destEndSize * mBytesPerFrame); + } + + Byte **buffers = mBuffers; + int offset0 = FrameOffset(startRead); + int offset1 = FrameOffset(endRead); + int nbytes; + + if (offset0 < offset1) { + nbytes = offset1 - offset0; + FetchABL(abl, destStartByteOffset, buffers, offset0, nbytes); + } else { + nbytes = mCapacityBytes - offset0; + FetchABL(abl, destStartByteOffset, buffers, offset0, nbytes); + FetchABL(abl, destStartByteOffset + nbytes, buffers, 0, offset1); + nbytes += offset1; + } + + int nchannels = abl->mNumberBuffers; + AudioBuffer *dest = abl->mBuffers; + while (--nchannels >= 0) + { + dest->mDataByteSize = nbytes; + dest++; + } + + return noErr; +} |