mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-16 18:30:32 +00:00
Allocate buffers once, ahead of time, and reuse.
This commit is contained in:
parent
ddfc2e4ca4
commit
9133e25a7b
@ -18,11 +18,18 @@
|
|||||||
|
|
||||||
#define IsDry(x) (x) < 2
|
#define IsDry(x) (x) < 2
|
||||||
|
|
||||||
|
#define MaximumBacklog 4
|
||||||
|
#define NumBuffers (MaximumBacklog + 1)
|
||||||
|
|
||||||
@implementation CSAudioQueue {
|
@implementation CSAudioQueue {
|
||||||
AudioQueueRef _audioQueue;
|
AudioQueueRef _audioQueue;
|
||||||
|
|
||||||
NSLock *_deallocLock;
|
NSLock *_deallocLock;
|
||||||
NSLock *_queueLock;
|
NSLock *_queueLock;
|
||||||
|
|
||||||
atomic_int _enqueuedBuffers;
|
atomic_int _enqueuedBuffers;
|
||||||
|
AudioQueueBufferRef _buffers[NumBuffers];
|
||||||
|
int _bufferWritePointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Status
|
#pragma mark - Status
|
||||||
@ -73,19 +80,19 @@
|
|||||||
0,
|
0,
|
||||||
dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0),
|
dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0),
|
||||||
^(AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
|
^(AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
|
||||||
|
(void)inBuffer;
|
||||||
|
|
||||||
CSAudioQueue *queue = weakSelf;
|
CSAudioQueue *queue = weakSelf;
|
||||||
if(!queue) {
|
if(!queue) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if([queue->_deallocLock tryLock]) {
|
if([queue->_deallocLock tryLock]) {
|
||||||
[queue->_queueLock lock];
|
|
||||||
OSSGuard(AudioQueueFreeBuffer(inAQ, inBuffer));
|
|
||||||
[queue->_queueLock unlock];
|
|
||||||
|
|
||||||
const int buffers = atomic_fetch_add(&queue->_enqueuedBuffers, -1) - 1;
|
const int buffers = atomic_fetch_add(&queue->_enqueuedBuffers, -1) - 1;
|
||||||
if(!buffers) {
|
if(!buffers) {
|
||||||
OSSGuard(AudioQueuePause(queue->_audioQueue));
|
[queue->_queueLock lock];
|
||||||
|
OSSGuard(AudioQueuePause(inAQ));
|
||||||
|
[queue->_queueLock unlock];
|
||||||
}
|
}
|
||||||
|
|
||||||
id<CSAudioQueueDelegate> delegate = queue.delegate;
|
id<CSAudioQueueDelegate> delegate = queue.delegate;
|
||||||
@ -105,43 +112,66 @@
|
|||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[_deallocLock lock];
|
[_deallocLock lock];
|
||||||
if(_audioQueue) {
|
if(_audioQueue) {
|
||||||
OSSGuard(AudioQueueDispose(_audioQueue, true));
|
OSSGuard(AudioQueueDispose(_audioQueue, true));
|
||||||
_audioQueue = NULL;
|
_audioQueue = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// nil out the dealloc lock before entering the critical section such
|
for(size_t c = 0; c < NumBuffers; c++) {
|
||||||
// that it becomes impossible for anyone else to acquire.
|
if(_buffers[c]) {
|
||||||
NSLock *deallocLock = _deallocLock;
|
OSSGuard(AudioQueueFreeBuffer(_audioQueue, _buffers[c]));
|
||||||
_deallocLock = nil;
|
_buffers[c] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nil out the dealloc lock before entering the critical section such
|
||||||
|
// that it becomes impossible for anyone else to acquire.
|
||||||
|
NSLock *deallocLock = _deallocLock;
|
||||||
|
_deallocLock = nil;
|
||||||
[deallocLock unlock];
|
[deallocLock unlock];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Audio enqueuer
|
#pragma mark - Audio enqueuer
|
||||||
|
|
||||||
|
- (void)setBufferSize:(NSUInteger)bufferSize {
|
||||||
|
_bufferSize = bufferSize;
|
||||||
|
|
||||||
|
// Allocate future audio buffers.
|
||||||
|
[_queueLock lock];
|
||||||
|
const size_t bufferBytes = self.bufferSize * sizeof(int16_t);
|
||||||
|
for(size_t c = 0; c < NumBuffers; c++) {
|
||||||
|
if(_buffers[c]) {
|
||||||
|
OSSGuard(AudioQueueFreeBuffer(_audioQueue, _buffers[c]));
|
||||||
|
}
|
||||||
|
|
||||||
|
OSSGuard(AudioQueueAllocateBuffer(_audioQueue, (UInt32)bufferBytes * 2, &_buffers[c]));
|
||||||
|
_buffers[c]->mAudioDataByteSize = (UInt32)bufferBytes;
|
||||||
|
}
|
||||||
|
[_queueLock unlock];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)enqueueAudioBuffer:(const int16_t *)buffer {
|
- (void)enqueueAudioBuffer:(const int16_t *)buffer {
|
||||||
const size_t bufferBytes = self.bufferSize * sizeof(int16_t);
|
const size_t bufferBytes = self.bufferSize * sizeof(int16_t);
|
||||||
|
|
||||||
// Don't enqueue more than 4 buffers ahead of now, to ensure not too much latency accrues.
|
// Don't enqueue more than the allowed number of future buffers,
|
||||||
if(atomic_load_explicit(&_enqueuedBuffers, memory_order_relaxed) == 4) {
|
// to ensure not too much latency accrues.
|
||||||
|
if(atomic_load_explicit(&_enqueuedBuffers, memory_order_relaxed) == MaximumBacklog) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const int enqueuedBuffers = atomic_fetch_add(&_enqueuedBuffers, 1) + 1;
|
const int enqueuedBuffers = atomic_fetch_add(&_enqueuedBuffers, 1) + 1;
|
||||||
|
|
||||||
|
const int targetBuffer = _bufferWritePointer;
|
||||||
|
_bufferWritePointer = (_bufferWritePointer + 1) % NumBuffers;
|
||||||
|
memcpy(_buffers[targetBuffer]->mAudioData, buffer, bufferBytes);
|
||||||
|
|
||||||
[_queueLock lock];
|
[_queueLock lock];
|
||||||
|
OSSGuard(AudioQueueEnqueueBuffer(_audioQueue, _buffers[targetBuffer], 0, NULL));
|
||||||
|
|
||||||
AudioQueueBufferRef newBuffer;
|
// Starting is a no-op if the queue is already playing, but it may not have been started
|
||||||
OSSGuard(AudioQueueAllocateBuffer(_audioQueue, (UInt32)bufferBytes * 2, &newBuffer));
|
// yet, or may have been paused due to a pipeline failure if the producer is running slowly.
|
||||||
memcpy(newBuffer->mAudioData, buffer, bufferBytes);
|
if(enqueuedBuffers > 1) {
|
||||||
newBuffer->mAudioDataByteSize = (UInt32)bufferBytes;
|
OSSGuard(AudioQueueStart(_audioQueue, NULL));
|
||||||
|
}
|
||||||
OSSGuard(AudioQueueEnqueueBuffer(_audioQueue, newBuffer, 0, NULL));
|
|
||||||
|
|
||||||
// Start the queue if it isn't started yet, and there are now some packets waiting.
|
|
||||||
if(enqueuedBuffers > 1) {
|
|
||||||
OSSGuard(AudioQueueStart(_audioQueue, NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
[_queueLock unlock];
|
[_queueLock unlock];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user