mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-08 18:55:14 +00:00
Switched to a variable-length buffer based on attempting to hit approximately a certain quality of service.
This commit is contained in:
parent
2ee78d42b9
commit
3b71d1a309
@ -23,6 +23,6 @@
|
|||||||
@property (nonatomic, weak) id<CSAudioQueueDelegate> delegate;
|
@property (nonatomic, weak) id<CSAudioQueueDelegate> delegate;
|
||||||
|
|
||||||
+ (Float64)preferredSamplingRate;
|
+ (Float64)preferredSamplingRate;
|
||||||
+ (NSUInteger)bufferSize;
|
@property (nonatomic, readonly) NSUInteger bufferSize;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
#import "CSAudioQueue.h"
|
#import "CSAudioQueue.h"
|
||||||
@import AudioToolbox;
|
@import AudioToolbox;
|
||||||
|
|
||||||
#define AudioQueueStreamLength 768
|
#define AudioQueueBufferMaxLength 2048
|
||||||
#define AudioQueueBufferLength 256
|
#define AudioQueueNumAudioBuffers 3
|
||||||
#define AudioQueueNumAudioBuffers (AudioQueueStreamLength/AudioQueueBufferLength)
|
#define AudioQueueMaxStreamLength (AudioQueueBufferMaxLength*AudioQueueNumAudioBuffers)
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
AudioQueueCanProceed,
|
AudioQueueCanProceed,
|
||||||
@ -21,10 +21,15 @@ enum {
|
|||||||
|
|
||||||
@implementation CSAudioQueue
|
@implementation CSAudioQueue
|
||||||
{
|
{
|
||||||
|
NSUInteger _bufferLength;
|
||||||
|
NSUInteger _streamLength;
|
||||||
|
|
||||||
AudioQueueRef _audioQueue;
|
AudioQueueRef _audioQueue;
|
||||||
AudioQueueBufferRef _audioBuffers[AudioQueueNumAudioBuffers];
|
AudioQueueBufferRef _audioBuffers[AudioQueueNumAudioBuffers];
|
||||||
|
|
||||||
unsigned int _audioStreamReadPosition, _audioStreamWritePosition;
|
unsigned int _audioStreamReadPosition, _audioStreamWritePosition;
|
||||||
int16_t _audioStream[AudioQueueStreamLength];
|
int16_t _audioStream[AudioQueueMaxStreamLength];
|
||||||
|
|
||||||
NSConditionLock *_writeLock;
|
NSConditionLock *_writeLock;
|
||||||
BOOL _isInvalidated;
|
BOOL _isInvalidated;
|
||||||
int _dequeuedCount;
|
int _dequeuedCount;
|
||||||
@ -45,15 +50,15 @@ enum {
|
|||||||
// TODO: if write lead is too great, skip some audio
|
// TODO: if write lead is too great, skip some audio
|
||||||
if(writeLead >= audioDataSampleSize)
|
if(writeLead >= audioDataSampleSize)
|
||||||
{
|
{
|
||||||
size_t samplesBeforeOverflow = AudioQueueStreamLength - (_audioStreamReadPosition % AudioQueueStreamLength);
|
size_t samplesBeforeOverflow = _streamLength - (_audioStreamReadPosition % _streamLength);
|
||||||
if(audioDataSampleSize <= samplesBeforeOverflow)
|
if(audioDataSampleSize <= samplesBeforeOverflow)
|
||||||
{
|
{
|
||||||
memcpy(buffer->mAudioData, &_audioStream[_audioStreamReadPosition % AudioQueueStreamLength], buffer->mAudioDataByteSize);
|
memcpy(buffer->mAudioData, &_audioStream[_audioStreamReadPosition % _streamLength], buffer->mAudioDataByteSize);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const size_t bytesRemaining = samplesBeforeOverflow * sizeof(int16_t);
|
const size_t bytesRemaining = samplesBeforeOverflow * sizeof(int16_t);
|
||||||
memcpy(buffer->mAudioData, &_audioStream[_audioStreamReadPosition % AudioQueueStreamLength], bytesRemaining);
|
memcpy(buffer->mAudioData, &_audioStream[_audioStreamReadPosition % _streamLength], bytesRemaining);
|
||||||
memcpy(buffer->mAudioData, &_audioStream[0], buffer->mAudioDataByteSize - bytesRemaining);
|
memcpy(buffer->mAudioData, &_audioStream[0], buffer->mAudioDataByteSize - bytesRemaining);
|
||||||
}
|
}
|
||||||
_audioStreamReadPosition += audioDataSampleSize;
|
_audioStreamReadPosition += audioDataSampleSize;
|
||||||
@ -95,6 +100,11 @@ static void audioOutputCallback(
|
|||||||
_writeLock = [[NSConditionLock alloc] initWithCondition:AudioQueueCanProceed];
|
_writeLock = [[NSConditionLock alloc] initWithCondition:AudioQueueCanProceed];
|
||||||
_samplingRate = samplingRate;
|
_samplingRate = samplingRate;
|
||||||
|
|
||||||
|
// determine buffer sizes
|
||||||
|
_bufferLength = AudioQueueBufferMaxLength;
|
||||||
|
while((Float64)_bufferLength*50.0 > samplingRate) _bufferLength >>= 1;
|
||||||
|
_streamLength = _bufferLength * AudioQueueNumAudioBuffers;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Describe a mono 16bit stream of the requested sampling rate
|
Describe a mono 16bit stream of the requested sampling rate
|
||||||
*/
|
*/
|
||||||
@ -123,7 +133,7 @@ static void audioOutputCallback(
|
|||||||
0,
|
0,
|
||||||
&_audioQueue))
|
&_audioQueue))
|
||||||
{
|
{
|
||||||
UInt32 bufferBytes = AudioQueueBufferLength * sizeof(int16_t);
|
UInt32 bufferBytes = (UInt32)(_bufferLength * sizeof(int16_t));
|
||||||
|
|
||||||
int c = AudioQueueNumAudioBuffers;
|
int c = AudioQueueNumAudioBuffers;
|
||||||
while(c--)
|
while(c--)
|
||||||
@ -167,18 +177,18 @@ static void audioOutputCallback(
|
|||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
[_writeLock lockWhenCondition:AudioQueueCanProceed];
|
[_writeLock lockWhenCondition:AudioQueueCanProceed];
|
||||||
if((_audioStreamReadPosition + AudioQueueStreamLength) - _audioStreamWritePosition >= lengthInSamples)
|
if((_audioStreamReadPosition + _streamLength) - _audioStreamWritePosition >= lengthInSamples)
|
||||||
{
|
{
|
||||||
size_t samplesBeforeOverflow = AudioQueueStreamLength - (_audioStreamWritePosition % AudioQueueStreamLength);
|
size_t samplesBeforeOverflow = _streamLength - (_audioStreamWritePosition % _streamLength);
|
||||||
|
|
||||||
if(samplesBeforeOverflow < lengthInSamples)
|
if(samplesBeforeOverflow < lengthInSamples)
|
||||||
{
|
{
|
||||||
memcpy(&_audioStream[_audioStreamWritePosition % AudioQueueStreamLength], buffer, samplesBeforeOverflow * sizeof(int16_t));
|
memcpy(&_audioStream[_audioStreamWritePosition % _streamLength], buffer, samplesBeforeOverflow * sizeof(int16_t));
|
||||||
memcpy(&_audioStream[0], &buffer[samplesBeforeOverflow], (lengthInSamples - samplesBeforeOverflow) * sizeof(int16_t));
|
memcpy(&_audioStream[0], &buffer[samplesBeforeOverflow], (lengthInSamples - samplesBeforeOverflow) * sizeof(int16_t));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memcpy(&_audioStream[_audioStreamWritePosition % AudioQueueStreamLength], buffer, lengthInSamples * sizeof(int16_t));
|
memcpy(&_audioStream[_audioStreamWritePosition % _streamLength], buffer, lengthInSamples * sizeof(int16_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
_audioStreamWritePosition += lengthInSamples;
|
_audioStreamWritePosition += lengthInSamples;
|
||||||
@ -194,7 +204,7 @@ static void audioOutputCallback(
|
|||||||
|
|
||||||
- (NSInteger)writeLockCondition
|
- (NSInteger)writeLockCondition
|
||||||
{
|
{
|
||||||
return ((_audioStreamWritePosition - _audioStreamReadPosition) < (AudioQueueStreamLength - AudioQueueBufferLength)) ? AudioQueueCanProceed : AudioQueueWait;
|
return ((_audioStreamWritePosition - _audioStreamReadPosition) < (_streamLength - _bufferLength)) ? AudioQueueCanProceed : AudioQueueWait;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Sampling Rate getters
|
#pragma mark - Sampling Rate getters
|
||||||
@ -223,9 +233,9 @@ static void audioOutputCallback(
|
|||||||
return AudioObjectGetPropertyData([self defaultOutputDevice], &address, sizeof(AudioObjectPropertyAddress), NULL, &size, &samplingRate) ? 0.0 : samplingRate;
|
return AudioObjectGetPropertyData([self defaultOutputDevice], &address, sizeof(AudioObjectPropertyAddress), NULL, &size, &samplingRate) ? 0.0 : samplingRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSUInteger)bufferSize
|
- (NSUInteger)bufferSize
|
||||||
{
|
{
|
||||||
return AudioQueueBufferLength;
|
return _bufferLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -63,7 +63,7 @@ class MachineDocument:
|
|||||||
audioQueue = CSAudioQueue(samplingRate: Float64(selectedSamplingRate))
|
audioQueue = CSAudioQueue(samplingRate: Float64(selectedSamplingRate))
|
||||||
audioQueue.delegate = self
|
audioQueue.delegate = self
|
||||||
self.machine().audioQueue = self.audioQueue
|
self.machine().audioQueue = self.audioQueue
|
||||||
self.machine().setAudioSamplingRate(selectedSamplingRate, bufferSize:CSAudioQueue.bufferSize() / 2)
|
self.machine().setAudioSamplingRate(selectedSamplingRate, bufferSize:audioQueue.bufferSize / 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.bestEffortUpdater.clockRate = self.machine().clockRate
|
self.bestEffortUpdater.clockRate = self.machine().clockRate
|
||||||
@ -86,8 +86,9 @@ class MachineDocument:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runForNumberOfCycles(numberOfCycles: Int32) {
|
func runForNumberOfCycles(numberOfCycles: Int32) {
|
||||||
|
let cyclesToRunFor = min(numberOfCycles, Int32(bestEffortUpdater.clockRate / 10))
|
||||||
if actionLock.tryLock() {
|
if actionLock.tryLock() {
|
||||||
self.machine().runForNumberOfCycles(numberOfCycles)
|
self.machine().runForNumberOfCycles(cyclesToRunFor)
|
||||||
actionLock.unlock()
|
actionLock.unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user