1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-23 20:29:42 +00:00

Pulled away my crazy rationalisation of an audio queue into a circular buffer and decided just to trust the OS. This should reduce latency.

This commit is contained in:
Thomas Harte 2016-10-10 07:30:00 -04:00
parent c284818af5
commit de397799ed
3 changed files with 17 additions and 122 deletions

View File

@ -23,6 +23,6 @@
@property (nonatomic, weak, nullable) id<CSAudioQueueDelegate> delegate;
+ (Float64)preferredSamplingRate;
@property (nonatomic, readonly) NSUInteger bufferSize;
@property (nonatomic, readonly) NSUInteger preferredBufferSize;
@end

View File

@ -10,77 +10,18 @@
@import AudioToolbox;
#define AudioQueueBufferMaxLength 8192
#define AudioQueueNumAudioBuffers 3
#define AudioQueueMaxStreamLength (AudioQueueBufferMaxLength*AudioQueueNumAudioBuffers)
enum {
AudioQueueCanProceed,
AudioQueueWait,
AudioQueueIsInvalidated
};
@implementation CSAudioQueue
{
NSUInteger _bufferLength;
NSUInteger _streamLength;
AudioQueueRef _audioQueue;
AudioQueueBufferRef _audioBuffers[AudioQueueNumAudioBuffers];
unsigned int _audioStreamReadPosition, _audioStreamWritePosition;
int16_t _audioStream[AudioQueueMaxStreamLength];
NSConditionLock *_writeLock;
BOOL _isInvalidated;
int _dequeuedCount;
}
#pragma mark -
#pragma mark AudioQueue callbacks and setup; for pushing audio out
#pragma mark - AudioQueue callbacks
- (void)audioQueue:(AudioQueueRef)theAudioQueue didCallbackWithBuffer:(AudioQueueBufferRef)buffer
{
[self.delegate audioQueueDidCompleteBuffer:self];
[_writeLock lock];
const unsigned int writeLead = _audioStreamWritePosition - _audioStreamReadPosition;
const size_t audioDataSampleSize = buffer->mAudioDataByteSize / sizeof(int16_t);
// TODO: if write lead is too great, skip some audio
if(writeLead >= audioDataSampleSize)
{
size_t samplesBeforeOverflow = _streamLength - (_audioStreamReadPosition % _streamLength);
if(audioDataSampleSize <= samplesBeforeOverflow)
{
memcpy(buffer->mAudioData, &_audioStream[_audioStreamReadPosition % _streamLength], buffer->mAudioDataByteSize);
}
else
{
const size_t bytesRemaining = samplesBeforeOverflow * sizeof(int16_t);
memcpy(buffer->mAudioData, &_audioStream[_audioStreamReadPosition % _streamLength], bytesRemaining);
memcpy(buffer->mAudioData, &_audioStream[0], buffer->mAudioDataByteSize - bytesRemaining);
}
_audioStreamReadPosition += audioDataSampleSize;
}
else
{
memset(buffer->mAudioData, 0, buffer->mAudioDataByteSize);
}
if(!_isInvalidated)
{
[_writeLock unlockWithCondition:AudioQueueCanProceed];
AudioQueueEnqueueBuffer(theAudioQueue, buffer, 0, NULL);
}
else
{
_dequeuedCount++;
if(_dequeuedCount == AudioQueueNumAudioBuffers)
[_writeLock unlockWithCondition:AudioQueueIsInvalidated];
else
[_writeLock unlockWithCondition:AudioQueueCanProceed];
}
AudioQueueFreeBuffer(_audioQueue, buffer);
}
static void audioOutputCallback(
@ -91,19 +32,19 @@ static void audioOutputCallback(
[(__bridge CSAudioQueue *)inUserData audioQueue:inAQ didCallbackWithBuffer:inBuffer];
}
#pragma mark - Standard object lifecycle
- (instancetype)initWithSamplingRate:(Float64)samplingRate
{
self = [super init];
if(self)
{
_writeLock = [[NSConditionLock alloc] initWithCondition:AudioQueueCanProceed];
_samplingRate = samplingRate;
// determine buffer sizes
_bufferLength = AudioQueueBufferMaxLength;
while((Float64)_bufferLength*50.0 > samplingRate) _bufferLength >>= 1;
_streamLength = _bufferLength * AudioQueueNumAudioBuffers;
// determine preferred buffer sizes
_preferredBufferSize = AudioQueueBufferMaxLength;
while((Float64)_preferredBufferSize*50.0 > samplingRate) _preferredBufferSize >>= 1;
/*
Describe a mono 16bit stream of the requested sampling rate
@ -133,17 +74,6 @@ static void audioOutputCallback(
0,
&_audioQueue))
{
UInt32 bufferBytes = (UInt32)(_bufferLength * sizeof(int16_t));
int c = AudioQueueNumAudioBuffers;
while(c--)
{
AudioQueueAllocateBuffer(_audioQueue, bufferBytes, &_audioBuffers[c]);
memset(_audioBuffers[c]->mAudioData, 0, bufferBytes);
_audioBuffers[c]->mAudioDataByteSize = bufferBytes;
AudioQueueEnqueueBuffer(_audioQueue, _audioBuffers[c], 0, NULL);
}
AudioQueueStart(_audioQueue, NULL);
}
}
@ -158,51 +88,21 @@ static void audioOutputCallback(
- (void)dealloc
{
[_writeLock lock];
_isInvalidated = YES;
[_writeLock unlock];
[_writeLock lockWhenCondition:AudioQueueIsInvalidated];
[_writeLock unlock];
int c = AudioQueueNumAudioBuffers;
while(c--)
AudioQueueFreeBuffer(_audioQueue, _audioBuffers[c]);
if(_audioQueue) AudioQueueDispose(_audioQueue, NO);
}
#pragma mark - Audio enqueuer
- (void)enqueueAudioBuffer:(const int16_t *)buffer numberOfSamples:(size_t)lengthInSamples
{
if([_writeLock tryLockWhenCondition:AudioQueueCanProceed])
{
if((_audioStreamReadPosition + _streamLength) - _audioStreamWritePosition >= lengthInSamples)
{
size_t samplesBeforeOverflow = _streamLength - (_audioStreamWritePosition % _streamLength);
AudioQueueBufferRef newBuffer;
size_t bufferBytes = lengthInSamples * sizeof(int16_t);
if(samplesBeforeOverflow < lengthInSamples)
{
memcpy(&_audioStream[_audioStreamWritePosition % _streamLength], buffer, samplesBeforeOverflow * sizeof(int16_t));
memcpy(&_audioStream[0], &buffer[samplesBeforeOverflow], (lengthInSamples - samplesBeforeOverflow) * sizeof(int16_t));
}
else
{
memcpy(&_audioStream[_audioStreamWritePosition % _streamLength], buffer, lengthInSamples * sizeof(int16_t));
}
AudioQueueAllocateBuffer(_audioQueue, (UInt32)bufferBytes, &newBuffer);
memcpy(newBuffer->mAudioData, buffer, bufferBytes);
newBuffer->mAudioDataByteSize = (UInt32)bufferBytes;
_audioStreamWritePosition += lengthInSamples;
[_writeLock unlockWithCondition:[self writeLockCondition]];
}
else
{
[_writeLock unlockWithCondition:AudioQueueWait];
}
}
}
- (NSInteger)writeLockCondition
{
return ((_audioStreamWritePosition - _audioStreamReadPosition) < (_streamLength - _bufferLength)) ? AudioQueueCanProceed : AudioQueueWait;
AudioQueueEnqueueBuffer(_audioQueue, newBuffer, 0, NULL);
}
#pragma mark - Sampling Rate getters
@ -231,9 +131,4 @@ static void audioOutputCallback(
return AudioObjectGetPropertyData([self defaultOutputDevice], &address, sizeof(AudioObjectPropertyAddress), NULL, &size, &samplingRate) ? 0.0 : samplingRate;
}
- (NSUInteger)bufferSize
{
return _bufferLength;
}
@end

View File

@ -85,7 +85,7 @@ class MachineDocument:
audioQueue = CSAudioQueue(samplingRate: Float64(selectedSamplingRate))
audioQueue.delegate = self
self.machine.audioQueue = self.audioQueue
self.machine.setAudioSamplingRate(selectedSamplingRate, bufferSize:audioQueue.bufferSize / 2)
self.machine.setAudioSamplingRate(selectedSamplingRate, bufferSize:audioQueue.preferredBufferSize / 2)
}
self.bestEffortUpdater.clockRate = self.machine.clockRate