From 31b26b811854dfd291b4a6eeeb101874cbfffc00 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 20 Jun 2016 21:47:27 -0400 Subject: [PATCH 1/2] Made a first attempt to implement the full top-to-bottom change of clock rates, giving the Atari the ability to change rate when it switches to PAL mode, as it should always have been. --- Machines/Atari2600/Atari2600.cpp | 17 ++++++++----- Machines/Atari2600/Atari2600.hpp | 5 +++- .../Documents/MachineDocument.swift | 9 +++++++ .../Mac/Clock Signal/Machine/CSMachine.mm | 25 ++++++++++++++++++- 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index ba56db751..5335d7814 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -24,7 +24,8 @@ Machine::Machine() : _upcomingEventsPointer(0), _objectCounterPointer(0), _stateByTime(_stateByExtendTime[0]), - _cycles_since_speaker_update(0) + _cycles_since_speaker_update(0), + _is_pal_region(false) { memset(_collisions, 0xff, sizeof(_collisions)); set_reset_line(true); @@ -71,7 +72,7 @@ void Machine::setup_output(float aspect_ratio) "}"); _crt->set_output_device(Outputs::CRT::Television); - _speaker.set_input_rate(1194720 / 38); + _speaker.set_input_rate((float)(get_clock_rate() / 38.0)); } void Machine::switch_region() @@ -90,8 +91,9 @@ void Machine::switch_region() "return mix(float(y) / 14.0, step(4, (iPhase + 2u) & 15u) * cos(phase + phaseOffset), amplitude);" "}"); _crt->set_new_timing(228, 312, Outputs::CRT::ColourSpace::YUV, 228, 1); - -// _speaker.set_input_rate(2 * 312 * 50); + _is_pal_region = true; + if(delegate) delegate->machine_did_change_clock_rate(this); + _speaker.set_input_rate((float)(get_clock_rate() / 38.0)); } void Machine::close_output() @@ -106,6 +108,11 @@ Machine::~Machine() close_output(); } +double Machine::get_clock_rate() +{ + return _is_pal_region ? 1182298 : 1194720; +} + void Machine::update_timers(int mask) { unsigned int upcomingPointerPlus4 = (_upcomingEventsPointer + 4)%number_of_upcoming_events; @@ -733,7 +740,6 @@ void Machine::set_switch_is_enabled(Atari2600Switch input, bool state) } } - void Machine::set_rom(size_t length, const uint8_t *data) { _rom_size = 1024; @@ -816,7 +822,6 @@ void Atari2600::Speaker::set_control(int channel, uint8_t control) #define advance_poly5(c) _poly5_counter[channel] = (_poly5_counter[channel] >> 1) | (((_poly5_counter[channel] << 4) ^ (_poly5_counter[channel] << 2))&0x010) #define advance_poly9(c) _poly9_counter[channel] = (_poly9_counter[channel] >> 1) | (((_poly9_counter[channel] << 4) ^ (_poly9_counter[channel] << 8))&0x100) - void Atari2600::Speaker::get_samples(unsigned int number_of_samples, int16_t *target) { for(unsigned int c = 0; c < number_of_samples; c++) diff --git a/Machines/Atari2600/Atari2600.hpp b/Machines/Atari2600/Atari2600.hpp index 08b3a3fef..c8e3fa53a 100644 --- a/Machines/Atari2600/Atari2600.hpp +++ b/Machines/Atari2600/Atari2600.hpp @@ -95,7 +95,7 @@ class Machine: public CPU6502::Processor, public CRTMachine::Machine { virtual Outputs::CRT::CRT *get_crt() { return _crt; } virtual Outputs::Speaker *get_speaker() { return &_speaker; } virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor::run_for_cycles(number_of_cycles); } - virtual double get_clock_rate() { return 1194720; } + virtual double get_clock_rate(); // TODO: different rate for PAL private: @@ -200,6 +200,9 @@ class Machine: public CPU6502::Processor, public CRTMachine::Machine { Outputs::CRT::CRT *_crt; Speaker _speaker; + // current mode + bool _is_pal_region; + // speaker backlog accumlation counter unsigned int _cycles_since_speaker_update; void update_audio(); diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index fa81b1509..ed7c2f7f3 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -12,6 +12,7 @@ import AudioToolbox class MachineDocument: NSDocument, NSWindowDelegate, + CSMachineDelegate, CSOpenGLViewDelegate, CSOpenGLViewResponderDelegate, CSBestEffortUpdaterDelegate, @@ -56,6 +57,14 @@ class MachineDocument: self.machine().setView(self.openGLView, aspectRatio: Float(displayAspectRatio.width / displayAspectRatio.height)) }) + setupClockRate() + } + + func machineDidChangeClockRate(machine: CSMachine!) { + setupClockRate() + } + + private func setupClockRate() { // establish and provide the audio queue, taking advice as to an appropriate sampling rate let maximumSamplingRate = CSAudioQueue.preferredSamplingRate() let selectedSamplingRate = self.machine().idealSamplingRateFromRange(NSRange(location: 0, length: NSInteger(maximumSamplingRate))) diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index f5d577749..dcc21d12c 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -12,6 +12,7 @@ @interface CSMachine() - (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; +- (void)machineDidChangeClockRate; @end struct SpeakerDelegate: public Outputs::Speaker::Delegate { @@ -21,14 +22,37 @@ struct SpeakerDelegate: public Outputs::Speaker::Delegate { } }; +struct MachineDelegate: CRTMachine::Machine::Delegate { + __weak CSMachine *machine; + void machine_did_change_clock_rate(CRTMachine::Machine *sender) { + [machine machineDidChangeClockRate]; + } +}; + @implementation CSMachine { SpeakerDelegate _speakerDelegate; + MachineDelegate _machineDelegate; +} + +- (instancetype)init { + self = [super init]; + if(self) + { + _machineDelegate.machine = self; + self.machine->set_delegate(&_machineDelegate); + _speakerDelegate.machine = self; + } + 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)dealloc { [_view performWithGLContext:^{ @synchronized(self) { @@ -50,7 +74,6 @@ struct SpeakerDelegate: public Outputs::Speaker::Delegate { - (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize { @synchronized(self) { - _speakerDelegate.machine = self; [self setSpeakerDelegate:&_speakerDelegate sampleRate:samplingRate bufferSize:bufferSize]; } } From 454e98302d519a18179ae9e44cd34d737c8d120b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 20 Jun 2016 22:18:41 -0400 Subject: [PATCH 2/2] Slight rearrangement plus a setting of the CSMachine delegate corrects audio after a clock rate change. --- Machines/Atari2600/Atari2600.cpp | 8 +++++--- .../Mac/Clock Signal/Documents/MachineDocument.swift | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 5335d7814..af59e0fba 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -58,6 +58,7 @@ Machine::Machine() : void Machine::setup_output(float aspect_ratio) { _crt = new Outputs::CRT::CRT(228, 1, 263, Outputs::CRT::ColourSpace::YIQ, 228, 1, 1); + _crt->set_output_device(Outputs::CRT::Television); // this is the NTSC phase offset function; see below for PAL _crt->set_composite_sampling_function( @@ -70,8 +71,6 @@ void Machine::setup_output(float aspect_ratio) "float phaseOffset = 6.283185308 * float(iPhase - 1u) / 13.0;" "return mix(float(y) / 14.0, step(1, iPhase) * cos(phase + phaseOffset), amplitude);" "}"); - _crt->set_output_device(Outputs::CRT::Television); - _speaker.set_input_rate((float)(get_clock_rate() / 38.0)); } @@ -90,10 +89,13 @@ void Machine::switch_region() "phaseOffset *= 6.283185308 / 12.0;" "return mix(float(y) / 14.0, step(4, (iPhase + 2u) & 15u) * cos(phase + phaseOffset), amplitude);" "}"); + _crt->set_new_timing(228, 312, Outputs::CRT::ColourSpace::YUV, 228, 1); + _is_pal_region = true; - if(delegate) delegate->machine_did_change_clock_rate(this); _speaker.set_input_rate((float)(get_clock_rate() / 38.0)); + + if(delegate) delegate->machine_did_change_clock_rate(this); } void Machine::close_output() diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index ed7c2f7f3..0a0c55194 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -58,6 +58,7 @@ class MachineDocument: }) setupClockRate() + self.machine().delegate = self } func machineDidChangeClockRate(machine: CSMachine!) {