1 /*
2      File: CARingBuffer.cpp
3  Abstract: CARingBuffer.h
4   Version: 1.1
5 
6  Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
7  Inc. ("Apple") in consideration of your agreement to the following
8  terms, and your use, installation, modification or redistribution of
9  this Apple software constitutes acceptance of these terms.  If you do
10  not agree with these terms, please do not use, install, modify or
11  redistribute this Apple software.
12 
13  In consideration of your agreement to abide by the following terms, and
14  subject to these terms, Apple grants you a personal, non-exclusive
15  license, under Apple's copyrights in this original Apple software (the
16  "Apple Software"), to use, reproduce, modify and redistribute the Apple
17  Software, with or without modifications, in source and/or binary forms;
18  provided that if you redistribute the Apple Software in its entirety and
19  without modifications, you must retain this notice and the following
20  text and disclaimers in all such redistributions of the Apple Software.
21  Neither the name, trademarks, service marks or logos of Apple Inc. may
22  be used to endorse or promote products derived from the Apple Software
23  without specific prior written permission from Apple.  Except as
24  expressly stated in this notice, no other rights or licenses, express or
25  implied, are granted by Apple herein, including but not limited to any
26  patent rights that may be infringed by your derivative works or by other
27  works in which the Apple Software may be incorporated.
28 
29  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
30  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34 
35  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42  POSSIBILITY OF SUCH DAMAGE.
43 
44  Copyright (C) 2014 Apple Inc. All Rights Reserved.
45 
46 */
47 #include "CARingBuffer.h"
48 #include "CABitOperations.h"
49 #include "CAAutoDisposer.h"
50 #include "CAAtomic.h"
51 
52 #include <stdlib.h>
53 #include <string.h>
54 #include <algorithm>
55 #include <libkern/OSAtomic.h>
56 
CARingBuffer()57 CARingBuffer::CARingBuffer() :
58 	mBuffers(NULL), mNumberChannels(0), mCapacityFrames(0), mCapacityBytes(0)
59 {
60 
61 }
62 
~CARingBuffer()63 CARingBuffer::~CARingBuffer()
64 {
65 	Deallocate();
66 }
67 
68 
Allocate(int nChannels,UInt32 bytesPerFrame,UInt32 capacityFrames)69 void	CARingBuffer::Allocate(int nChannels, UInt32 bytesPerFrame, UInt32 capacityFrames)
70 {
71 	Deallocate();
72 
73 	capacityFrames = NextPowerOfTwo(capacityFrames);
74 
75 	mNumberChannels = nChannels;
76 	mBytesPerFrame = bytesPerFrame;
77 	mCapacityFrames = capacityFrames;
78 	mCapacityFramesMask = capacityFrames - 1;
79 	mCapacityBytes = bytesPerFrame * capacityFrames;
80 
81 	// put everything in one memory allocation, first the pointers, then the deinterleaved channels
82 	UInt32 allocSize = (mCapacityBytes + sizeof(Byte *)) * nChannels;
83 	Byte *p = (Byte *)CA_malloc(allocSize);
84 	memset(p, 0, allocSize);
85 	mBuffers = (Byte **)p;
86 	p += nChannels * sizeof(Byte *);
87 	for (int i = 0; i < nChannels; ++i) {
88 		mBuffers[i] = p;
89 		p += mCapacityBytes;
90 	}
91 
92 	for (UInt32 i = 0; i<kGeneralRingTimeBoundsQueueSize; ++i)
93 	{
94 		mTimeBoundsQueue[i].mStartTime = 0;
95 		mTimeBoundsQueue[i].mEndTime = 0;
96 		mTimeBoundsQueue[i].mUpdateCounter = 0;
97 	}
98 	mTimeBoundsQueuePtr = 0;
99 }
100 
Deallocate()101 void	CARingBuffer::Deallocate()
102 {
103 	if (mBuffers) {
104 		free(mBuffers);
105 		mBuffers = NULL;
106 	}
107 	mNumberChannels = 0;
108 	mCapacityBytes = 0;
109 	mCapacityFrames = 0;
110 }
111 
ZeroRange(Byte ** buffers,int nchannels,int offset,int nbytes)112 inline void ZeroRange(Byte **buffers, int nchannels, int offset, int nbytes)
113 {
114 	while (--nchannels >= 0) {
115 		memset(*buffers + offset, 0, nbytes);
116 		++buffers;
117 	}
118 }
119 
StoreABL(Byte ** buffers,int destOffset,const AudioBufferList * abl,int srcOffset,int nbytes)120 inline void StoreABL(Byte **buffers, int destOffset, const AudioBufferList *abl, int srcOffset, int nbytes)
121 {
122 	int nchannels = abl->mNumberBuffers;
123 	const AudioBuffer *src = abl->mBuffers;
124 	while (--nchannels >= 0) {
125 		if (srcOffset > (int)src->mDataByteSize) continue;
126 		memcpy(*buffers + destOffset, (Byte *)src->mData + srcOffset, std::min(nbytes, (int)src->mDataByteSize - srcOffset));
127 		++buffers;
128 		++src;
129 	}
130 }
131 
FetchABL(AudioBufferList * abl,int destOffset,Byte ** buffers,int srcOffset,int nbytes)132 inline void FetchABL(AudioBufferList *abl, int destOffset, Byte **buffers, int srcOffset, int nbytes)
133 {
134 	int nchannels = abl->mNumberBuffers;
135 	AudioBuffer *dest = abl->mBuffers;
136 	while (--nchannels >= 0) {
137 		if (destOffset > (int)dest->mDataByteSize) continue;
138 		memcpy((Byte *)dest->mData + destOffset, *buffers + srcOffset, std::min(nbytes, (int)dest->mDataByteSize - destOffset));
139 		++buffers;
140 		++dest;
141 	}
142 }
143 
ZeroABL(AudioBufferList * abl,int destOffset,int nbytes)144 inline void ZeroABL(AudioBufferList *abl, int destOffset, int nbytes)
145 {
146 	int nBuffers = abl->mNumberBuffers;
147 	AudioBuffer *dest = abl->mBuffers;
148 	while (--nBuffers >= 0) {
149 		if (destOffset > (int)dest->mDataByteSize) continue;
150 		memset((Byte *)dest->mData + destOffset, 0, std::min(nbytes, (int)dest->mDataByteSize - destOffset));
151 		++dest;
152 	}
153 }
154 
155 
Store(const AudioBufferList * abl,UInt32 framesToWrite,SampleTime startWrite)156 CARingBufferError	CARingBuffer::Store(const AudioBufferList *abl, UInt32 framesToWrite, SampleTime startWrite)
157 {
158 	if (framesToWrite == 0)
159 		return kCARingBufferError_OK;
160 
161 	if (framesToWrite > mCapacityFrames)
162 		return kCARingBufferError_TooMuch;		// too big!
163 
164 	SampleTime endWrite = startWrite + framesToWrite;
165 
166 	if (startWrite < EndTime()) {
167 		// going backwards, throw everything out
168 		SetTimeBounds(startWrite, startWrite);
169 	} else if (endWrite - StartTime() <= mCapacityFrames) {
170 		// the buffer has not yet wrapped and will not need to
171 	} else {
172 		// advance the start time past the region we are about to overwrite
173 		SampleTime newStart = endWrite - mCapacityFrames;	// one buffer of time behind where we're writing
174 		SampleTime newEnd = std::max(newStart, EndTime());
175 		SetTimeBounds(newStart, newEnd);
176 	}
177 
178 	// write the new frames
179 	Byte **buffers = mBuffers;
180 	int nchannels = mNumberChannels;
181 	int offset0, offset1, nbytes;
182 	SampleTime curEnd = EndTime();
183 
184 	if (startWrite > curEnd) {
185 		// we are skipping some samples, so zero the range we are skipping
186 		offset0 = FrameOffset(curEnd);
187 		offset1 = FrameOffset(startWrite);
188 		if (offset0 < offset1)
189 			ZeroRange(buffers, nchannels, offset0, offset1 - offset0);
190 		else {
191 			ZeroRange(buffers, nchannels, offset0, mCapacityBytes - offset0);
192 			ZeroRange(buffers, nchannels, 0, offset1);
193 		}
194 		offset0 = offset1;
195 	} else {
196 		offset0 = FrameOffset(startWrite);
197 	}
198 
199 	offset1 = FrameOffset(endWrite);
200 	if (offset0 < offset1)
201 		StoreABL(buffers, offset0, abl, 0, offset1 - offset0);
202 	else {
203 		nbytes = mCapacityBytes - offset0;
204 		StoreABL(buffers, offset0, abl, 0, nbytes);
205 		StoreABL(buffers, 0, abl, nbytes, offset1);
206 	}
207 
208 	// now update the end time
209 	SetTimeBounds(StartTime(), endWrite);
210 
211 	return kCARingBufferError_OK;	// success
212 }
213 
SetTimeBounds(SampleTime startTime,SampleTime endTime)214 void	CARingBuffer::SetTimeBounds(SampleTime startTime, SampleTime endTime)
215 {
216 	UInt32 nextPtr = mTimeBoundsQueuePtr + 1;
217 	UInt32 index = nextPtr & kGeneralRingTimeBoundsQueueMask;
218 
219 	mTimeBoundsQueue[index].mStartTime = startTime;
220 	mTimeBoundsQueue[index].mEndTime = endTime;
221 	mTimeBoundsQueue[index].mUpdateCounter = nextPtr;
222 	CAAtomicCompareAndSwap32Barrier(mTimeBoundsQueuePtr, mTimeBoundsQueuePtr + 1, (SInt32*)&mTimeBoundsQueuePtr);
223 }
224 
GetTimeBounds(SampleTime & startTime,SampleTime & endTime)225 CARingBufferError	CARingBuffer::GetTimeBounds(SampleTime &startTime, SampleTime &endTime)
226 {
227 	for (int i=0; i<8; ++i) // fail after a few tries.
228 	{
229 		UInt32 curPtr = mTimeBoundsQueuePtr;
230 		UInt32 index = curPtr & kGeneralRingTimeBoundsQueueMask;
231 		CARingBuffer::TimeBounds* bounds = mTimeBoundsQueue + index;
232 
233 		startTime = bounds->mStartTime;
234 		endTime = bounds->mEndTime;
235 		UInt32 newPtr = bounds->mUpdateCounter;
236 
237 		if (newPtr == curPtr)
238 			return kCARingBufferError_OK;
239 	}
240 	return kCARingBufferError_CPUOverload;
241 }
242 
ClipTimeBounds(SampleTime & startRead,SampleTime & endRead)243 CARingBufferError	CARingBuffer::ClipTimeBounds(SampleTime& startRead, SampleTime& endRead)
244 {
245 	SampleTime startTime, endTime;
246 
247 	CARingBufferError err = GetTimeBounds(startTime, endTime);
248 	if (err) return err;
249 
250 	if (startRead > endTime || endRead < startTime) {
251 		endRead = startRead;
252 		return kCARingBufferError_OK;
253 	}
254 
255 	startRead = std::max(startRead, startTime);
256 	endRead = std::min(endRead, endTime);
257 	endRead = std::max(endRead, startRead);
258 
259 	return kCARingBufferError_OK;	// success
260 }
261 
Fetch(AudioBufferList * abl,UInt32 nFrames,SampleTime startRead)262 CARingBufferError	CARingBuffer::Fetch(AudioBufferList *abl, UInt32 nFrames, SampleTime startRead)
263 {
264 	if (nFrames == 0)
265 		return kCARingBufferError_OK;
266 
267 	startRead = std::max(0LL, startRead);
268 
269 	SampleTime endRead = startRead + nFrames;
270 
271 	SampleTime startRead0 = startRead;
272 	SampleTime endRead0 = endRead;
273 
274 	CARingBufferError err = ClipTimeBounds(startRead, endRead);
275 	if (err) return err;
276 
277 	if (startRead == endRead) {
278 		ZeroABL(abl, 0, nFrames * mBytesPerFrame);
279 		return kCARingBufferError_OK;
280 	}
281 
282 	SInt32 byteSize = (SInt32)((endRead - startRead) * mBytesPerFrame);
283 
284 	SInt32 destStartByteOffset = std::max((SInt32)0, (SInt32)((startRead - startRead0) * mBytesPerFrame));
285 
286 	if (destStartByteOffset > 0) {
287 		ZeroABL(abl, 0, std::min((SInt32)(nFrames * mBytesPerFrame), destStartByteOffset));
288 	}
289 
290 	SInt32 destEndSize = std::max((SInt32)0, (SInt32)(endRead0 - endRead));
291 	if (destEndSize > 0) {
292 		ZeroABL(abl, destStartByteOffset + byteSize, destEndSize * mBytesPerFrame);
293 	}
294 
295 	Byte **buffers = mBuffers;
296 	int offset0 = FrameOffset(startRead);
297 	int offset1 = FrameOffset(endRead);
298 	int nbytes;
299 
300 	if (offset0 < offset1) {
301 		nbytes = offset1 - offset0;
302 		FetchABL(abl, destStartByteOffset, buffers, offset0, nbytes);
303 	} else {
304 		nbytes = mCapacityBytes - offset0;
305 		FetchABL(abl, destStartByteOffset, buffers, offset0, nbytes);
306 		FetchABL(abl, destStartByteOffset + nbytes, buffers, 0, offset1);
307 		nbytes += offset1;
308 	}
309 
310 	int nchannels = abl->mNumberBuffers;
311 	AudioBuffer *dest = abl->mBuffers;
312 	while (--nchannels >= 0)
313 	{
314 		dest->mDataByteSize = nbytes;
315 		dest++;
316 	}
317 
318 	return noErr;
319 }
320