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