Make sure MSVC knows which version of 'floor()' we want
[ardour.git] / libs / appleutility / CoreAudio / PublicUtility / CARingBuffer.cpp
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
57 CARingBuffer::CARingBuffer() :
58         mBuffers(NULL), mNumberChannels(0), mCapacityFrames(0), mCapacityBytes(0)
59 {
60
61 }
62
63 CARingBuffer::~CARingBuffer()
64 {
65         Deallocate();
66 }
67
68
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
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
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
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
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
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
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
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
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
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
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 }