summaryrefslogtreecommitdiff
path: root/libs/appleutility/CoreAudio/PublicUtility/CARingBuffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/appleutility/CoreAudio/PublicUtility/CARingBuffer.cpp')
-rw-r--r--libs/appleutility/CoreAudio/PublicUtility/CARingBuffer.cpp319
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;
+}