mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-01 10:05:55 +00:00
Merge pull request #28 from TomHarte/AtariClockRateAdjustment
Completes the loop on machines changing clock rate dynamically
This commit is contained in:
commit
2d13802625
@ -24,7 +24,8 @@ Machine::Machine() :
|
|||||||
_upcomingEventsPointer(0),
|
_upcomingEventsPointer(0),
|
||||||
_objectCounterPointer(0),
|
_objectCounterPointer(0),
|
||||||
_stateByTime(_stateByExtendTime[0]),
|
_stateByTime(_stateByExtendTime[0]),
|
||||||
_cycles_since_speaker_update(0)
|
_cycles_since_speaker_update(0),
|
||||||
|
_is_pal_region(false)
|
||||||
{
|
{
|
||||||
memset(_collisions, 0xff, sizeof(_collisions));
|
memset(_collisions, 0xff, sizeof(_collisions));
|
||||||
set_reset_line(true);
|
set_reset_line(true);
|
||||||
@ -57,6 +58,7 @@ Machine::Machine() :
|
|||||||
void Machine::setup_output(float aspect_ratio)
|
void Machine::setup_output(float aspect_ratio)
|
||||||
{
|
{
|
||||||
_crt = new Outputs::CRT::CRT(228, 1, 263, Outputs::CRT::ColourSpace::YIQ, 228, 1, 1);
|
_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
|
// this is the NTSC phase offset function; see below for PAL
|
||||||
_crt->set_composite_sampling_function(
|
_crt->set_composite_sampling_function(
|
||||||
@ -69,9 +71,7 @@ void Machine::setup_output(float aspect_ratio)
|
|||||||
"float phaseOffset = 6.283185308 * float(iPhase - 1u) / 13.0;"
|
"float phaseOffset = 6.283185308 * float(iPhase - 1u) / 13.0;"
|
||||||
"return mix(float(y) / 14.0, step(1, iPhase) * cos(phase + phaseOffset), amplitude);"
|
"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));
|
||||||
|
|
||||||
_speaker.set_input_rate(1194720 / 38);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Machine::switch_region()
|
void Machine::switch_region()
|
||||||
@ -89,9 +89,13 @@ void Machine::switch_region()
|
|||||||
"phaseOffset *= 6.283185308 / 12.0;"
|
"phaseOffset *= 6.283185308 / 12.0;"
|
||||||
"return mix(float(y) / 14.0, step(4, (iPhase + 2u) & 15u) * cos(phase + phaseOffset), amplitude);"
|
"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);
|
_crt->set_new_timing(228, 312, Outputs::CRT::ColourSpace::YUV, 228, 1);
|
||||||
|
|
||||||
// _speaker.set_input_rate(2 * 312 * 50);
|
_is_pal_region = true;
|
||||||
|
_speaker.set_input_rate((float)(get_clock_rate() / 38.0));
|
||||||
|
|
||||||
|
if(delegate) delegate->machine_did_change_clock_rate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Machine::close_output()
|
void Machine::close_output()
|
||||||
@ -106,6 +110,11 @@ Machine::~Machine()
|
|||||||
close_output();
|
close_output();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double Machine::get_clock_rate()
|
||||||
|
{
|
||||||
|
return _is_pal_region ? 1182298 : 1194720;
|
||||||
|
}
|
||||||
|
|
||||||
void Machine::update_timers(int mask)
|
void Machine::update_timers(int mask)
|
||||||
{
|
{
|
||||||
unsigned int upcomingPointerPlus4 = (_upcomingEventsPointer + 4)%number_of_upcoming_events;
|
unsigned int upcomingPointerPlus4 = (_upcomingEventsPointer + 4)%number_of_upcoming_events;
|
||||||
@ -733,7 +742,6 @@ void Machine::set_switch_is_enabled(Atari2600Switch input, bool state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Machine::set_rom(size_t length, const uint8_t *data)
|
void Machine::set_rom(size_t length, const uint8_t *data)
|
||||||
{
|
{
|
||||||
_rom_size = 1024;
|
_rom_size = 1024;
|
||||||
@ -816,7 +824,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_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)
|
#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)
|
void Atari2600::Speaker::get_samples(unsigned int number_of_samples, int16_t *target)
|
||||||
{
|
{
|
||||||
for(unsigned int c = 0; c < number_of_samples; c++)
|
for(unsigned int c = 0; c < number_of_samples; c++)
|
||||||
|
@ -95,7 +95,7 @@ class Machine: public CPU6502::Processor<Machine>, public CRTMachine::Machine {
|
|||||||
virtual Outputs::CRT::CRT *get_crt() { return _crt; }
|
virtual Outputs::CRT::CRT *get_crt() { return _crt; }
|
||||||
virtual Outputs::Speaker *get_speaker() { return &_speaker; }
|
virtual Outputs::Speaker *get_speaker() { return &_speaker; }
|
||||||
virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
|
virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
|
||||||
virtual double get_clock_rate() { return 1194720; }
|
virtual double get_clock_rate();
|
||||||
// TODO: different rate for PAL
|
// TODO: different rate for PAL
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -200,6 +200,9 @@ class Machine: public CPU6502::Processor<Machine>, public CRTMachine::Machine {
|
|||||||
Outputs::CRT::CRT *_crt;
|
Outputs::CRT::CRT *_crt;
|
||||||
Speaker _speaker;
|
Speaker _speaker;
|
||||||
|
|
||||||
|
// current mode
|
||||||
|
bool _is_pal_region;
|
||||||
|
|
||||||
// speaker backlog accumlation counter
|
// speaker backlog accumlation counter
|
||||||
unsigned int _cycles_since_speaker_update;
|
unsigned int _cycles_since_speaker_update;
|
||||||
void update_audio();
|
void update_audio();
|
||||||
|
@ -12,6 +12,7 @@ import AudioToolbox
|
|||||||
class MachineDocument:
|
class MachineDocument:
|
||||||
NSDocument,
|
NSDocument,
|
||||||
NSWindowDelegate,
|
NSWindowDelegate,
|
||||||
|
CSMachineDelegate,
|
||||||
CSOpenGLViewDelegate,
|
CSOpenGLViewDelegate,
|
||||||
CSOpenGLViewResponderDelegate,
|
CSOpenGLViewResponderDelegate,
|
||||||
CSBestEffortUpdaterDelegate,
|
CSBestEffortUpdaterDelegate,
|
||||||
@ -56,6 +57,15 @@ class MachineDocument:
|
|||||||
self.machine().setView(self.openGLView, aspectRatio: Float(displayAspectRatio.width / displayAspectRatio.height))
|
self.machine().setView(self.openGLView, aspectRatio: Float(displayAspectRatio.width / displayAspectRatio.height))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
setupClockRate()
|
||||||
|
self.machine().delegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
func machineDidChangeClockRate(machine: CSMachine!) {
|
||||||
|
setupClockRate()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupClockRate() {
|
||||||
// establish and provide the audio queue, taking advice as to an appropriate sampling rate
|
// establish and provide the audio queue, taking advice as to an appropriate sampling rate
|
||||||
let maximumSamplingRate = CSAudioQueue.preferredSamplingRate()
|
let maximumSamplingRate = CSAudioQueue.preferredSamplingRate()
|
||||||
let selectedSamplingRate = self.machine().idealSamplingRateFromRange(NSRange(location: 0, length: NSInteger(maximumSamplingRate)))
|
let selectedSamplingRate = self.machine().idealSamplingRateFromRange(NSRange(location: 0, length: NSInteger(maximumSamplingRate)))
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
@interface CSMachine()
|
@interface CSMachine()
|
||||||
- (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length;
|
- (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length;
|
||||||
|
- (void)machineDidChangeClockRate;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
struct SpeakerDelegate: public Outputs::Speaker::Delegate {
|
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 {
|
@implementation CSMachine {
|
||||||
SpeakerDelegate _speakerDelegate;
|
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 {
|
- (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length {
|
||||||
[self.audioQueue enqueueAudioBuffer:samples numberOfSamples:(unsigned int)length];
|
[self.audioQueue enqueueAudioBuffer:samples numberOfSamples:(unsigned int)length];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)machineDidChangeClockRate {
|
||||||
|
[self.delegate machineDidChangeClockRate:self];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[_view performWithGLContext:^{
|
[_view performWithGLContext:^{
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
@ -50,7 +74,6 @@ struct SpeakerDelegate: public Outputs::Speaker::Delegate {
|
|||||||
|
|
||||||
- (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize {
|
- (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize {
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
_speakerDelegate.machine = self;
|
|
||||||
[self setSpeakerDelegate:&_speakerDelegate sampleRate:samplingRate bufferSize:bufferSize];
|
[self setSpeakerDelegate:&_speakerDelegate sampleRate:samplingRate bufferSize:bufferSize];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user