From 2ee78d42b9c36c9887f59a401ce145bd5c7f9b68 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 16 Jun 2016 20:51:35 -0400 Subject: [PATCH] Made an attempt also to trigger updates upon audio packets being returned, and significantly to reduce the size and quantity of those. This should get down to 11ms latency if the output wave is 44100; I might need to scale up the buffer size as the sampling rate increases or this is going to get crazy at 192Khz. --- .../Mac/Clock Signal/Audio/CSAudioQueue.h | 5 +-- .../Mac/Clock Signal/Audio/CSAudioQueue.m | 12 ++++--- .../Documents/MachineDocument.swift | 33 +++++++++++++------ .../Mac/Clock Signal/Machine/CSMachine.h | 2 +- .../Mac/Clock Signal/Machine/CSMachine.mm | 8 ++--- 5 files changed, 39 insertions(+), 21 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.h b/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.h index 1e0357927..add6e78c7 100644 --- a/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.h +++ b/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.h @@ -10,7 +10,7 @@ @class CSAudioQueue; -@protocol AudioQueueDelegate +@protocol CSAudioQueueDelegate - (void)audioQueueDidCompleteBuffer:(nonnull CSAudioQueue *)audioQueue; @end @@ -20,8 +20,9 @@ - (void)enqueueAudioBuffer:(nonnull const int16_t *)buffer numberOfSamples:(size_t)lengthInSamples; @property (nonatomic, readonly) Float64 samplingRate; -@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id delegate; + (Float64)preferredSamplingRate; ++ (NSUInteger)bufferSize; @end diff --git a/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.m b/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.m index b7df1b614..8617b64a6 100644 --- a/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.m +++ b/OSBindings/Mac/Clock Signal/Audio/CSAudioQueue.m @@ -9,9 +9,9 @@ #import "CSAudioQueue.h" @import AudioToolbox; -#define AudioQueueNumAudioBuffers 4 -#define AudioQueueStreamLength 4096 -#define AudioQueueBufferLength 512 +#define AudioQueueStreamLength 768 +#define AudioQueueBufferLength 256 +#define AudioQueueNumAudioBuffers (AudioQueueStreamLength/AudioQueueBufferLength) enum { AudioQueueCanProceed, @@ -30,7 +30,6 @@ enum { int _dequeuedCount; } - #pragma mark - #pragma mark AudioQueue callbacks and setup; for pushing audio out @@ -224,4 +223,9 @@ static void audioOutputCallback( return AudioObjectGetPropertyData([self defaultOutputDevice], &address, sizeof(AudioObjectPropertyAddress), NULL, &size, &samplingRate) ? 0.0 : samplingRate; } ++ (NSUInteger)bufferSize +{ + return AudioQueueBufferLength; +} + @end diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index fbccbad61..c8d4c0610 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -9,8 +9,14 @@ import Cocoa import AudioToolbox -class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDelegate, CSBestEffortUpdaterDelegate, NSWindowDelegate { - +class MachineDocument: + NSDocument, + NSWindowDelegate, + CSOpenGLViewDelegate, + CSOpenGLViewResponderDelegate, + CSBestEffortUpdaterDelegate, + CSAudioQueueDelegate +{ lazy var actionLock = NSLock() lazy var drawLock = NSLock() func machine() -> CSMachine! { @@ -55,8 +61,9 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe let selectedSamplingRate = self.machine().idealSamplingRateFromRange(NSRange(location: 0, length: NSInteger(maximumSamplingRate))) if selectedSamplingRate > 0 { audioQueue = CSAudioQueue(samplingRate: Float64(selectedSamplingRate)) + audioQueue.delegate = self self.machine().audioQueue = self.audioQueue - self.machine().setAudioSamplingRate(selectedSamplingRate) + self.machine().setAudioSamplingRate(selectedSamplingRate, bufferSize:CSAudioQueue.bufferSize() / 2) } self.bestEffortUpdater.clockRate = self.machine().clockRate @@ -73,10 +80,18 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe super.close() } + // MARK: CSBestEffortUpdaterDelegate final func bestEffortUpdater(bestEffortUpdater: CSBestEffortUpdater!, runForCycles cycles: UInt, didSkipPreviousUpdate: Bool) { runForNumberOfCycles(Int32(cycles)) } + func runForNumberOfCycles(numberOfCycles: Int32) { + if actionLock.tryLock() { + self.machine().runForNumberOfCycles(numberOfCycles) + actionLock.unlock() + } + } + // MARK: Utilities for children func dataForResource(name : String, ofType type: String, inDirectory directory: String) -> NSData? { if let path = NSBundle.mainBundle().pathForResource(name, ofType: type, inDirectory: directory) { @@ -86,15 +101,13 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe return nil } - // MARK: CSOpenGLViewDelegate - func runForNumberOfCycles(numberOfCycles: Int32) { - if actionLock.tryLock() { - self.machine().runForNumberOfCycles(numberOfCycles) - actionLock.unlock() - } + // MARK: CSAudioQueueDelegate + final func audioQueueDidCompleteBuffer(audioQueue: CSAudioQueue) { + bestEffortUpdater.update() } - func openGLView(view: CSOpenGLView, drawViewOnlyIfDirty onlyIfDirty: Bool) { + // MARK: CSOpenGLViewDelegate + final func openGLView(view: CSOpenGLView, drawViewOnlyIfDirty onlyIfDirty: Bool) { bestEffortUpdater.update() if drawLock.tryLock() { self.machine().drawViewForPixelSize(view.backingSize, onlyIfDirty: onlyIfDirty) diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index 4feb5b866..7d35033a1 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -20,7 +20,7 @@ - (void)runForNumberOfCycles:(int)numberOfCycles; - (float)idealSamplingRateFromRange:(NSRange)range; -- (void)setAudioSamplingRate:(float)samplingRate; +- (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize; - (void)setView:(CSOpenGLView *)view aspectRatio:(float)aspectRatio; - (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 8c1b0074b..15c17ce83 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -47,19 +47,19 @@ struct SpeakerDelegate: public Outputs::Speaker::Delegate { } } -- (void)setAudioSamplingRate:(float)samplingRate { +- (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize { @synchronized(self) { _speakerDelegate.machine = self; - [self setSpeakerDelegate:&_speakerDelegate sampleRate:samplingRate]; + [self setSpeakerDelegate:&_speakerDelegate sampleRate:samplingRate bufferSize:bufferSize]; } } -- (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(float)sampleRate { +- (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(float)sampleRate bufferSize:(NSUInteger)bufferSize { @synchronized(self) { Outputs::Speaker *speaker = self.machine->get_speaker(); if(speaker) { - speaker->set_output_rate(sampleRate, 512); + speaker->set_output_rate(sampleRate, (int)bufferSize); speaker->set_delegate(delegate); return YES; }