| 1 |
/* |
| 2 |
* MDAudioUtility.c |
| 3 |
* Alchemusica |
| 4 |
* |
| 5 |
* Created by Toshi Nagata on 09/11/05. |
| 6 |
* Copyright 2009-2011 Toshi Nagata. All rights reserved. |
| 7 |
* |
| 8 |
This program is free software; you can redistribute it and/or modify |
| 9 |
it under the terms of the GNU General Public License as published by |
| 10 |
the Free Software Foundation version 2 of the License. |
| 11 |
|
| 12 |
This program is distributed in the hope that it will be useful, |
| 13 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 |
GNU General Public License for more details. |
| 16 |
*/ |
| 17 |
|
| 18 |
#include "MDAudioUtility.h" |
| 19 |
|
| 20 |
#pragma mark ====== MDRingBuffer ====== |
| 21 |
|
| 22 |
static UInt32 |
| 23 |
sNextPowerOfTwo(UInt32 x) |
| 24 |
{ |
| 25 |
UInt32 n; |
| 26 |
for (n = 1; n != 0; n <<= 1) { |
| 27 |
if (n > x) |
| 28 |
return n; |
| 29 |
} |
| 30 |
return 0; |
| 31 |
} |
| 32 |
|
| 33 |
MDRingBuffer * |
| 34 |
MDRingBufferNew(void) |
| 35 |
{ |
| 36 |
return (MDRingBuffer *)calloc(sizeof(MDRingBuffer), 1); |
| 37 |
} |
| 38 |
|
| 39 |
int |
| 40 |
MDRingBufferAllocate(MDRingBuffer *ring, int nChannels, UInt32 bytesPerFrame, UInt32 capacityFrames) |
| 41 |
{ |
| 42 |
UInt32 allocSize; |
| 43 |
Byte *p; |
| 44 |
int i; |
| 45 |
UInt32 j; |
| 46 |
|
| 47 |
MDRingBufferDeallocate(ring); |
| 48 |
capacityFrames = sNextPowerOfTwo(capacityFrames); |
| 49 |
|
| 50 |
ring->numberChannels = 1; /* Assumes non-interleaved stream */ |
| 51 |
ring->numberBuffers = nChannels; |
| 52 |
ring->bytesPerFrame = bytesPerFrame; |
| 53 |
ring->capacityFrames = capacityFrames; |
| 54 |
ring->capacityFramesMask = capacityFrames - 1; |
| 55 |
ring->capacityBytes = bytesPerFrame * capacityFrames; |
| 56 |
|
| 57 |
// put everything in one memory allocation, first the pointers, then the deinterleaved channels |
| 58 |
allocSize = (ring->capacityBytes + sizeof(Byte *)) * ring->numberBuffers; |
| 59 |
p = (Byte *)malloc(allocSize); |
| 60 |
memset(p, 0, allocSize); |
| 61 |
ring->buffers = (Byte **)p; |
| 62 |
p += ring->numberBuffers * sizeof(Byte *); |
| 63 |
for (i = 0; i < ring->numberBuffers; ++i) { |
| 64 |
ring->buffers[i] = p; |
| 65 |
p += ring->capacityBytes; |
| 66 |
} |
| 67 |
|
| 68 |
for (j = 0; j < kMDRingTimeBoundsQueueSize; ++j) |
| 69 |
{ |
| 70 |
ring->timeBoundsQueue[j].startTime = 0; |
| 71 |
ring->timeBoundsQueue[j].endTime = 0; |
| 72 |
ring->timeBoundsQueue[j].updateCounter = 0; |
| 73 |
} |
| 74 |
ring->timeBoundsQueuePtr = 0; |
| 75 |
return kMDRingBufferError_OK; |
| 76 |
} |
| 77 |
|
| 78 |
void |
| 79 |
MDRingBufferDeallocate(MDRingBuffer *ring) |
| 80 |
{ |
| 81 |
if (ring == NULL) |
| 82 |
return; |
| 83 |
if (ring->buffers != NULL) { |
| 84 |
free(ring->buffers); |
| 85 |
ring->buffers = NULL; |
| 86 |
} |
| 87 |
ring->numberChannels = 0; |
| 88 |
ring->numberBuffers = 0; |
| 89 |
ring->capacityBytes = 0; |
| 90 |
ring->capacityFrames = 0; |
| 91 |
} |
| 92 |
|
| 93 |
void |
| 94 |
MDRingBufferRelease(MDRingBuffer *ring) |
| 95 |
{ |
| 96 |
MDRingBufferDeallocate(ring); |
| 97 |
free(ring); |
| 98 |
} |
| 99 |
|
| 100 |
static inline void |
| 101 |
sZeroRange(Byte **buffers, int nchannels, int offset, int nbytes) |
| 102 |
{ |
| 103 |
while (--nchannels >= 0) { |
| 104 |
memset(*buffers + offset, 0, nbytes); |
| 105 |
++buffers; |
| 106 |
} |
| 107 |
} |
| 108 |
|
| 109 |
static inline void |
| 110 |
sStoreABL(Byte **buffers, int destOffset, const AudioBufferList *abl, int srcOffset, int nbytes) |
| 111 |
{ |
| 112 |
int nchannels = abl->mNumberBuffers; |
| 113 |
const AudioBuffer *src = abl->mBuffers; |
| 114 |
// printf("Store %d bytes at [%d] from [%d]\n", nbytes, destOffset, srcOffset); |
| 115 |
while (--nchannels >= 0) { |
| 116 |
memcpy(*buffers + destOffset, (Byte *)src->mData + srcOffset, nbytes); |
| 117 |
++buffers; |
| 118 |
++src; |
| 119 |
} |
| 120 |
} |
| 121 |
|
| 122 |
static inline void |
| 123 |
sFetchABL(AudioBufferList *abl, int destOffset, Byte **buffers, int srcOffset, int nbytes) |
| 124 |
{ |
| 125 |
int nchannels = abl->mNumberBuffers; |
| 126 |
AudioBuffer *dest = abl->mBuffers; |
| 127 |
// printf("Fetch %d bytes from [%d] to [%d]\n", nbytes, srcOffset, destOffset); |
| 128 |
while (--nchannels >= 0) { |
| 129 |
memcpy((Byte *)dest->mData + destOffset, *buffers + srcOffset, nbytes); |
| 130 |
++buffers; |
| 131 |
++dest; |
| 132 |
} |
| 133 |
} |
| 134 |
|
| 135 |
int |
| 136 |
MDRingBufferStore(MDRingBuffer *ring, const AudioBufferList *abl, UInt32 framesToWrite, MDSampleTime startWrite) |
| 137 |
{ |
| 138 |
MDSampleTime endWrite; |
| 139 |
|
| 140 |
if (abl->mNumberBuffers != ring->numberBuffers) |
| 141 |
return kMDRingBufferError_NumberBuffersMismatch; |
| 142 |
|
| 143 |
if (framesToWrite > ring->capacityFrames) |
| 144 |
return kMDRingBufferError_TooMuch; // too big! |
| 145 |
|
| 146 |
endWrite = startWrite + framesToWrite; |
| 147 |
|
| 148 |
if (startWrite < MDRingBufferEndTime(ring)) { |
| 149 |
// going backwards, throw everything out |
| 150 |
MDRingBufferSetTimeBounds(ring, startWrite, startWrite); |
| 151 |
} else if (endWrite - MDRingBufferStartTime(ring) <= ring->capacityFrames) { |
| 152 |
// the buffer has not yet wrapped and will not need to |
| 153 |
} else { |
| 154 |
// advance the start time past the region we are about to overwrite |
| 155 |
MDSampleTime newStart = endWrite - ring->capacityFrames; // one buffer of time behind where we're writing |
| 156 |
MDSampleTime newEnd = MDRingBufferEndTime(ring); |
| 157 |
if (newStart > newEnd) |
| 158 |
newEnd = newStart; |
| 159 |
MDRingBufferSetTimeBounds(ring, newStart, newEnd); |
| 160 |
} |
| 161 |
|
| 162 |
// write the new frames |
| 163 |
Byte **buffers = ring->buffers; |
| 164 |
int nchannels = ring->numberChannels; |
| 165 |
int offset0, offset1, nbytes; |
| 166 |
MDSampleTime curEnd = MDRingBufferEndTime(ring); |
| 167 |
|
| 168 |
if (startWrite > curEnd) { |
| 169 |
// we are skipping some samples, so zero the range we are skipping |
| 170 |
offset0 = MDRingBufferFrameOffset(ring, curEnd); |
| 171 |
offset1 = MDRingBufferFrameOffset(ring, startWrite); |
| 172 |
if (offset0 < offset1) |
| 173 |
sZeroRange(buffers, nchannels, offset0, offset1 - offset0); |
| 174 |
else { |
| 175 |
sZeroRange(buffers, nchannels, offset0, ring->capacityBytes - offset0); |
| 176 |
sZeroRange(buffers, nchannels, 0, offset1); |
| 177 |
} |
| 178 |
offset0 = offset1; |
| 179 |
} else { |
| 180 |
offset0 = MDRingBufferFrameOffset(ring, startWrite); |
| 181 |
} |
| 182 |
|
| 183 |
offset1 = MDRingBufferFrameOffset(ring, endWrite); |
| 184 |
if (offset0 < offset1) |
| 185 |
sStoreABL(buffers, offset0, abl, 0, offset1 - offset0); |
| 186 |
else { |
| 187 |
nbytes = ring->capacityBytes - offset0; |
| 188 |
sStoreABL(buffers, offset0, abl, 0, nbytes); |
| 189 |
sStoreABL(buffers, 0, abl, nbytes, offset1); |
| 190 |
} |
| 191 |
|
| 192 |
// now update the end time |
| 193 |
MDRingBufferSetTimeBounds(ring, MDRingBufferStartTime(ring), endWrite); |
| 194 |
|
| 195 |
return kMDRingBufferError_OK; // success |
| 196 |
} |
| 197 |
|
| 198 |
int |
| 199 |
MDRingBufferFetch(MDRingBuffer *ring, AudioBufferList *abl, UInt32 nFrames, MDSampleTime startRead, bool aheadOK) |
| 200 |
{ |
| 201 |
MDSampleTime endRead = startRead + nFrames; |
| 202 |
int err; |
| 203 |
|
| 204 |
if (abl->mNumberBuffers != ring->numberBuffers) |
| 205 |
return kMDRingBufferError_NumberBuffersMismatch; |
| 206 |
|
| 207 |
err = MDRingBufferCheckTimeBounds(ring, startRead, endRead, aheadOK); |
| 208 |
if (err) |
| 209 |
return err; |
| 210 |
|
| 211 |
Byte **buffers = ring->buffers; |
| 212 |
int offset0 = MDRingBufferFrameOffset(ring, startRead); |
| 213 |
int offset1 = MDRingBufferFrameOffset(ring, endRead); |
| 214 |
int nbytes; |
| 215 |
|
| 216 |
if (offset0 < offset1) { |
| 217 |
sFetchABL(abl, 0, buffers, offset0, nbytes = offset1 - offset0); |
| 218 |
} else { |
| 219 |
nbytes = ring->capacityBytes - offset0; |
| 220 |
sFetchABL(abl, 0, buffers, offset0, nbytes); |
| 221 |
sFetchABL(abl, nbytes, buffers, 0, offset1); |
| 222 |
nbytes += offset1; |
| 223 |
} |
| 224 |
|
| 225 |
int nchannels = abl->mNumberBuffers; |
| 226 |
AudioBuffer *dest = abl->mBuffers; |
| 227 |
while (--nchannels >= 0) |
| 228 |
{ |
| 229 |
dest->mDataByteSize = nbytes; |
| 230 |
dest++; |
| 231 |
} |
| 232 |
|
| 233 |
// now update the end time |
| 234 |
MDRingBufferSetTimeBounds(ring, endRead, MDRingBufferEndTime(ring)); |
| 235 |
|
| 236 |
return kMDRingBufferError_OK; |
| 237 |
} |
| 238 |
|
| 239 |
int |
| 240 |
MDRingBufferGetTimeBounds(MDRingBuffer *ring, MDSampleTime *startTime, MDSampleTime *endTime) |
| 241 |
{ |
| 242 |
int i; |
| 243 |
for (i = 0; i < 8; ++i) // fail after a few tries. |
| 244 |
{ |
| 245 |
UInt32 curPtr = ring->timeBoundsQueuePtr; |
| 246 |
UInt32 index = curPtr & kMDRingTimeBoundsQueueMask; |
| 247 |
MDTimeBounds* bounds = ring->timeBoundsQueue + index; |
| 248 |
|
| 249 |
*startTime = bounds->startTime; |
| 250 |
*endTime = bounds->endTime; |
| 251 |
UInt32 newPtr = bounds->updateCounter; |
| 252 |
|
| 253 |
if (newPtr == curPtr) |
| 254 |
return kMDRingBufferError_OK; |
| 255 |
} |
| 256 |
return kMDRingBufferError_CPUOverload; |
| 257 |
} |
| 258 |
|
| 259 |
int |
| 260 |
MDRingBufferFrameOffset(MDRingBuffer *ring, MDSampleTime frameNumber) |
| 261 |
{ |
| 262 |
return (int)((frameNumber & ring->capacityFramesMask) * ring->bytesPerFrame); |
| 263 |
} |
| 264 |
|
| 265 |
int |
| 266 |
MDRingBufferCheckTimeBounds(MDRingBuffer *ring, MDSampleTime startRead, MDSampleTime endRead, bool aheadOK) |
| 267 |
{ |
| 268 |
MDSampleTime startTime, endTime; |
| 269 |
|
| 270 |
int err = MDRingBufferGetTimeBounds(ring, &startTime, &endTime); |
| 271 |
if (err) |
| 272 |
return err; |
| 273 |
|
| 274 |
if (startRead < startTime) |
| 275 |
{ |
| 276 |
if (endRead > endTime) |
| 277 |
return kMDRingBufferError_TooMuch; |
| 278 |
|
| 279 |
if (endRead < startTime) |
| 280 |
return kMDRingBufferError_WayBehind; |
| 281 |
else |
| 282 |
return kMDRingBufferError_SlightlyBehind; |
| 283 |
} |
| 284 |
|
| 285 |
if (endRead > endTime) // we are going to read chunks of zeros its okay |
| 286 |
{ |
| 287 |
if (aheadOK) |
| 288 |
return kMDRingBufferError_OK; |
| 289 |
else if (startRead > endTime) |
| 290 |
return kMDRingBufferError_WayAhead; |
| 291 |
else |
| 292 |
return kMDRingBufferError_SlightlyAhead; |
| 293 |
} |
| 294 |
|
| 295 |
return kMDRingBufferError_OK; // success |
| 296 |
} |
| 297 |
|
| 298 |
MDSampleTime |
| 299 |
MDRingBufferStartTime(MDRingBuffer *ring) |
| 300 |
{ |
| 301 |
return ring->timeBoundsQueue[ring->timeBoundsQueuePtr & kMDRingTimeBoundsQueueMask].startTime; |
| 302 |
} |
| 303 |
|
| 304 |
MDSampleTime |
| 305 |
MDRingBufferEndTime(MDRingBuffer *ring) |
| 306 |
{ |
| 307 |
return ring->timeBoundsQueue[ring->timeBoundsQueuePtr & kMDRingTimeBoundsQueueMask].endTime; |
| 308 |
} |
| 309 |
|
| 310 |
void |
| 311 |
MDRingBufferSetTimeBounds(MDRingBuffer *ring, MDSampleTime startTime, MDSampleTime endTime) |
| 312 |
{ |
| 313 |
UInt32 nextPtr = ring->timeBoundsQueuePtr + 1; |
| 314 |
UInt32 index = (nextPtr & kMDRingTimeBoundsQueueMask); |
| 315 |
|
| 316 |
ring->timeBoundsQueue[index].startTime = startTime; |
| 317 |
ring->timeBoundsQueue[index].endTime = endTime; |
| 318 |
ring->timeBoundsQueue[index].updateCounter = nextPtr; |
| 319 |
|
| 320 |
CompareAndSwap(ring->timeBoundsQueuePtr, ring->timeBoundsQueuePtr + 1, &ring->timeBoundsQueuePtr); |
| 321 |
} |