mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
Consolidated a little more within the common code, adding sampling rate selection based on querying the machine.
This commit is contained in:
parent
50543e9676
commit
8623dc2833
@ -21,11 +21,6 @@ class Atari2600Document: MachineDocument {
|
||||
self.intendedCyclesPerSecond = 1194720
|
||||
}
|
||||
|
||||
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
||||
super.windowControllerDidLoadNib(aController)
|
||||
atari2600.setView(openGLView, aspectRatio: 4.0 / 3.0)
|
||||
}
|
||||
|
||||
override class func autosavesInPlace() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
@ -16,9 +16,12 @@ class ElectronDocument: MachineDocument {
|
||||
return electron
|
||||
}
|
||||
|
||||
override func aspectRatio() -> NSSize {
|
||||
return NSSize(width: 11.0, height: 10.0)
|
||||
}
|
||||
|
||||
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
||||
super.windowControllerDidLoadNib(aController)
|
||||
aController.window?.contentAspectRatio = NSSize(width: 11.0, height: 10.0)
|
||||
|
||||
self.intendedCyclesPerSecond = 2000000
|
||||
|
||||
@ -29,10 +32,6 @@ class ElectronDocument: MachineDocument {
|
||||
self.electron.setBASICROM(NSData(contentsOfFile: basicPath)!)
|
||||
}
|
||||
|
||||
openGLView.performWithGLContext({
|
||||
self.electron.setView(self.openGLView, aspectRatio: 11.0 / 10.0)
|
||||
})
|
||||
|
||||
establishStoredOptions()
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,10 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe
|
||||
return nil
|
||||
}
|
||||
|
||||
func aspectRatio() -> NSSize {
|
||||
return NSSize(width: 4.0, height: 3.0)
|
||||
}
|
||||
|
||||
@IBOutlet weak var openGLView: CSOpenGLView! {
|
||||
didSet {
|
||||
openGLView.delegate = self
|
||||
@ -29,16 +33,24 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe
|
||||
optionsPanel?.setIsVisible(true)
|
||||
}
|
||||
|
||||
lazy var audioQueue = AudioQueue()
|
||||
var audioQueue : AudioQueue! = nil
|
||||
|
||||
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
||||
super.windowControllerDidLoadNib(aController)
|
||||
|
||||
// bind the content aspect ratio to remain 4:3 from now on as a default
|
||||
aController.window?.contentAspectRatio = NSSize(width: 4.0, height: 3.0)
|
||||
// establish the output aspect ratio and audio
|
||||
let displayAspectRatio = self.aspectRatio()
|
||||
aController.window?.contentAspectRatio = displayAspectRatio
|
||||
openGLView.performWithGLContext({
|
||||
self.machine().setView(self.openGLView, aspectRatio: Float(displayAspectRatio.width / displayAspectRatio.height))
|
||||
})
|
||||
|
||||
// provide the audio queue
|
||||
// establish and provide the audio queue, taking advice as to an appropriate sampling rate
|
||||
let maximumSamplingRate = AudioQueue.preferredSamplingRate()
|
||||
let selectedSamplingRate = self.machine().idealSamplingRateFromRange(NSRange(location: 0, length: NSInteger(maximumSamplingRate)))
|
||||
audioQueue = AudioQueue(samplingRate: Float64(selectedSamplingRate))
|
||||
self.machine().audioQueue = self.audioQueue
|
||||
self.machine().setAudioSamplingRate(selectedSamplingRate)
|
||||
}
|
||||
|
||||
override func close() {
|
||||
|
@ -10,6 +10,11 @@
|
||||
|
||||
@interface AudioQueue : NSObject
|
||||
|
||||
- (instancetype)initWithSamplingRate:(Float64)samplingRate;
|
||||
- (void)enqueueAudioBuffer:(const int16_t *)buffer numberOfSamples:(size_t)lengthInSamples;
|
||||
|
||||
@property (nonatomic, readonly) Float64 samplingRate;
|
||||
|
||||
+ (Float64)preferredSamplingRate;
|
||||
|
||||
@end
|
||||
|
@ -85,20 +85,21 @@ static void audioOutputCallback(
|
||||
[(__bridge AudioQueue *)inUserData audioQueue:inAQ didCallbackWithBuffer:inBuffer];
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
- (instancetype)initWithSamplingRate:(Float64)samplingRate
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if(self)
|
||||
{
|
||||
_writeLock = [[NSConditionLock alloc] initWithCondition:AudioQueueCanProceed];
|
||||
_samplingRate = samplingRate;
|
||||
|
||||
/*
|
||||
Describe a mono, 16bit, 44.1Khz audio format
|
||||
*/
|
||||
AudioStreamBasicDescription outputDescription;
|
||||
|
||||
outputDescription.mSampleRate = 44100;
|
||||
outputDescription.mSampleRate = samplingRate;
|
||||
|
||||
outputDescription.mFormatID = kAudioFormatLinearPCM;
|
||||
outputDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||
@ -113,13 +114,13 @@ static void audioOutputCallback(
|
||||
|
||||
// create an audio output queue along those lines
|
||||
if(!AudioQueueNewOutput(
|
||||
&outputDescription,
|
||||
audioOutputCallback,
|
||||
(__bridge void *)(self),
|
||||
NULL,
|
||||
kCFRunLoopCommonModes,
|
||||
0,
|
||||
&_audioQueue))
|
||||
&outputDescription,
|
||||
audioOutputCallback,
|
||||
(__bridge void *)(self),
|
||||
NULL,
|
||||
kCFRunLoopCommonModes,
|
||||
0,
|
||||
&_audioQueue))
|
||||
{
|
||||
UInt32 bufferBytes = AudioQueueBufferLength * sizeof(int16_t);
|
||||
|
||||
@ -139,6 +140,11 @@ static void audioOutputCallback(
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithSamplingRate:[[self class] preferredSamplingRate]];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_writeLock lock];
|
||||
@ -190,4 +196,28 @@ static void audioOutputCallback(
|
||||
return ((_audioStreamWritePosition - _audioStreamReadPosition) < (AudioQueueStreamLength - AudioQueueBufferLength)) ? AudioQueueCanProceed : AudioQueueWait;
|
||||
}
|
||||
|
||||
+ (AudioDeviceID)defaultOutputDevice
|
||||
{
|
||||
AudioObjectPropertyAddress address;
|
||||
address.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||
address.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
address.mElement = kAudioObjectPropertyElementMaster;
|
||||
|
||||
AudioDeviceID deviceID;
|
||||
UInt32 size = sizeof(AudioDeviceID);
|
||||
return AudioHardwareServiceGetPropertyData(kAudioObjectSystemObject, &address, 0, NULL, &size, &deviceID) ? 0 : deviceID;
|
||||
}
|
||||
|
||||
+ (Float64)preferredSamplingRate
|
||||
{
|
||||
AudioObjectPropertyAddress address;
|
||||
address.mSelector = kAudioDevicePropertyNominalSampleRate;
|
||||
address.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
address.mElement = kAudioObjectPropertyElementMaster;
|
||||
|
||||
Float64 samplingRate;
|
||||
UInt32 size = sizeof(Float64);
|
||||
return AudioHardwareServiceGetPropertyData([self defaultOutputDevice], &address, 0, NULL, &size, &samplingRate) ? 0.0 : samplingRate;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -14,6 +14,9 @@
|
||||
|
||||
- (void)runForNumberOfCycles:(int)numberOfCycles;
|
||||
|
||||
- (int)idealSamplingRateFromRange:(NSRange)range;
|
||||
- (void)setAudioSamplingRate:(int)samplingRate;
|
||||
|
||||
- (void)setView:(CSOpenGLView *)view aspectRatio:(float)aspectRatio;
|
||||
- (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty;
|
||||
|
||||
|
@ -34,8 +34,6 @@ struct SpeakerDelegate: public Outputs::Speaker::Delegate {
|
||||
|
||||
if(self) {
|
||||
_serialDispatchQueue = dispatch_queue_create("Machine queue", DISPATCH_QUEUE_SERIAL);
|
||||
_speakerDelegate.machine = self;
|
||||
[self setSpeakerDelegate:&_speakerDelegate sampleRate:44100];
|
||||
}
|
||||
|
||||
return self;
|
||||
@ -49,6 +47,24 @@ struct SpeakerDelegate: public Outputs::Speaker::Delegate {
|
||||
}];
|
||||
}
|
||||
|
||||
- (int)idealSamplingRateFromRange:(NSRange)range {
|
||||
@synchronized(self) {
|
||||
Outputs::Speaker *speaker = self.machine->get_speaker();
|
||||
if(speaker)
|
||||
{
|
||||
return speaker->get_ideal_clock_rate_in_range((int)range.location, (int)(range.location + range.length));
|
||||
}
|
||||
return (int)range.location;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAudioSamplingRate:(int)samplingRate {
|
||||
@synchronized(self) {
|
||||
_speakerDelegate.machine = self;
|
||||
[self setSpeakerDelegate:&_speakerDelegate sampleRate:samplingRate];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(int)sampleRate {
|
||||
@synchronized(self) {
|
||||
Outputs::Speaker *speaker = self.machine->get_speaker();
|
||||
|
Loading…
Reference in New Issue
Block a user