// // CSMachine.m // Clock Signal // // Created by Thomas Harte on 04/01/2016. // Copyright © 2016 Thomas Harte. All rights reserved. // #import "CSMachine.h" #import "CSMachine+Subclassing.h" #import "CSMachine+Target.h" #include "Typer.hpp" #include "ConfigurationTarget.hpp" @interface CSMachine() - (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; - (void)machineDidChangeClockRate; - (void)machineDidChangeClockIsUnlimited; @end struct SpeakerDelegate: public Outputs::Speaker::Delegate { __weak CSMachine *machine; void speaker_did_complete_samples(Outputs::Speaker *speaker, const std::vector &buffer) { [machine speaker:speaker didCompleteSamples:buffer.data() length:(int)buffer.size()]; } }; struct MachineDelegate: CRTMachine::Machine::Delegate { __weak CSMachine *machine; void machine_did_change_clock_rate(CRTMachine::Machine *sender) { [machine machineDidChangeClockRate]; } void machine_did_change_clock_is_unlimited(CRTMachine::Machine *sender) { [machine machineDidChangeClockIsUnlimited]; } }; @implementation CSMachine { SpeakerDelegate _speakerDelegate; MachineDelegate _machineDelegate; CRTMachine::Machine *_machine; } - (instancetype)initWithMachine:(void *)machine { self = [super init]; if(self) { _machine = (CRTMachine::Machine *)machine; _machineDelegate.machine = self; _speakerDelegate.machine = self; _machine->set_delegate(&_machineDelegate); } return self; } - (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length { [self.audioQueue enqueueAudioBuffer:samples numberOfSamples:(unsigned int)length]; } - (void)machineDidChangeClockRate { [self.delegate machineDidChangeClockRate:self]; } - (void)machineDidChangeClockIsUnlimited { [self.delegate machineDidChangeClockIsUnlimited:self]; } - (void)dealloc { [_view performWithGLContext:^{ @synchronized(self) { _machine->close_output(); } }]; } - (float)idealSamplingRateFromRange:(NSRange)range { @synchronized(self) { std::shared_ptr speaker = _machine->get_speaker(); if(speaker) { return speaker->get_ideal_clock_rate_in_range((float)range.location, (float)(range.location + range.length)); } return 0; } } - (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize { @synchronized(self) { [self setSpeakerDelegate:&_speakerDelegate sampleRate:samplingRate bufferSize:bufferSize]; } } - (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(float)sampleRate bufferSize:(NSUInteger)bufferSize { @synchronized(self) { std::shared_ptr speaker = _machine->get_speaker(); if(speaker) { speaker->set_output_rate(sampleRate, (int)bufferSize); speaker->set_delegate(delegate); return YES; } return NO; } } - (void)runForNumberOfCycles:(int)numberOfCycles { @synchronized(self) { _machine->run_for(Cycles(numberOfCycles)); } } - (void)setView:(CSOpenGLView *)view aspectRatio:(float)aspectRatio { _view = view; [view performWithGLContext:^{ [self setupOutputWithAspectRatio:aspectRatio]; }]; } - (void)setupOutputWithAspectRatio:(float)aspectRatio { _machine->setup_output(aspectRatio); // Since OS X v10.6, Macs have had a gamma of 2.2. _machine->get_crt()->set_output_gamma(2.2f); } - (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty { _machine->get_crt()->draw_frame((unsigned int)pixelSize.width, (unsigned int)pixelSize.height, onlyIfDirty ? true : false); } - (double)clockRate { return _machine->get_clock_rate(); } - (BOOL)clockIsUnlimited { return _machine->get_clock_is_unlimited() ? YES : NO; } - (void)paste:(NSString *)paste { Utility::TypeRecipient *typeRecipient = dynamic_cast(_machine); if(typeRecipient) typeRecipient->set_typer_for_string([paste UTF8String]); } - (void)applyTarget:(const StaticAnalyser::Target &)target { @synchronized(self) { ConfigurationTarget::Machine *const configurationTarget = dynamic_cast(_machine); if(configurationTarget) configurationTarget->configure_as_target(target); } } - (void)applyMedia:(const StaticAnalyser::Media &)media { @synchronized(self) { ConfigurationTarget::Machine *const configurationTarget = dynamic_cast(_machine); if(configurationTarget) configurationTarget->insert_media(media); } } @end