1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-30 22:29:56 +00:00

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.

This commit is contained in:
Thomas Harte 2016-06-16 20:51:35 -04:00
parent 00a9f1bf24
commit 2ee78d42b9
5 changed files with 39 additions and 21 deletions

View File

@ -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<AudioQueueDelegate> delegate;
@property (nonatomic, weak) id<CSAudioQueueDelegate> delegate;
+ (Float64)preferredSamplingRate;
+ (NSUInteger)bufferSize;
@end

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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;
}