From 253e5a42030ed30234f3f7fc30a58906124fcd05 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 2 Jun 2016 19:50:16 -0400 Subject: [PATCH] With no regard to performance whatsoever, endeavoured to bring sound to the 2600. It's very scratchy, so something is wrong. --- Machines/Atari2600/Atari2600.cpp | 117 +++++++++++++++--- Machines/Atari2600/Atari2600.hpp | 14 ++- .../Mac/Clock Signal/Wrappers/AudioQueue.m | 2 +- .../Mac/Clock Signal/Wrappers/CSMachine.mm | 2 +- 4 files changed, 116 insertions(+), 19 deletions(-) diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 9ce5deba7..676208be3 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -73,7 +73,7 @@ void Machine::setup_output(float aspect_ratio) "}"); _crt->set_output_device(Outputs::CRT::Television); - _speaker.set_input_rate(2 * 263 * 60); + _speaker.set_input_rate(1194720 / 38); } void Machine::switch_region() @@ -93,7 +93,7 @@ void Machine::switch_region() "}"); _crt->set_new_timing(228, 312, Outputs::CRT::ColourSpace::YUV, 228, 1); - _speaker.set_input_rate(2 * 312 * 50); +// _speaker.set_input_rate(2 * 312 * 50); } void Machine::close_output() @@ -692,6 +692,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin printf("!!!DDR!!!"); break; case 0x04: + case 0x06: returnValue &= _piaTimerValue >> _piaTimerShift; if(_writtenPiaTimerShift != _piaTimerShift) { @@ -700,8 +701,9 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin } break; case 0x05: + case 0x07: returnValue &= _piaTimerStatus; - _piaTimerStatus &= ~0x40; + _piaTimerStatus &= ~0x80; break; } } else { @@ -711,9 +713,9 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin case 0x05: case 0x06: case 0x07: - _writtenPiaTimerShift = _piaTimerShift = (decodedAddress - 0x04) * 3 + (decodedAddress / 0x07); - _piaTimerValue = (unsigned int)(*value << _piaTimerShift); - _piaTimerStatus &= ~0xc0; + _writtenPiaTimerShift = _piaTimerShift = (decodedAddress - 0x04) * 3 + (decodedAddress / 0x07); // i.e. 0, 3, 6, 10 + _piaTimerValue = (unsigned int)(*value) << _piaTimerShift; + _piaTimerStatus &= ~0x40; break; } } @@ -727,7 +729,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin if(_piaTimerValue >= cycles_run_for / 3) { _piaTimerValue -= cycles_run_for / 3; } else { - _piaTimerValue += 0xff - cycles_run_for / 3; + _piaTimerValue = 0x100 + ((_piaTimerValue - (cycles_run_for / 3)) >> _piaTimerShift); _piaTimerShift = 0; _piaTimerStatus |= 0xc0; } @@ -794,6 +796,17 @@ void Machine::synchronise() update_audio(); } +Atari2600::Speaker::Speaker() +{ + _poly4_counter[0] = _poly4_counter[1] = + _poly5_counter[0] = _poly5_counter[1] = + _poly9_counter[0] = _poly9_counter[1] = ~0; +} + +Atari2600::Speaker::~Speaker() +{ +} + void Atari2600::Speaker::set_volume(int channel, uint8_t volume) { _volume[channel] = volume & 0xf; @@ -808,8 +821,15 @@ void Atari2600::Speaker::set_divider(int channel, uint8_t divider) void Atari2600::Speaker::set_control(int channel, uint8_t control) { _control[channel] = control & 0xf; +// _shift_counter[channel] = ~0; +// printf("%d\n", _control[channel]); } +#define advance_poly4(c) _poly4_counter[channel] = (_poly4_counter[channel] >> 1) | (((_poly4_counter[channel] << 3) ^ (_poly4_counter[channel] << 2))&0x008) +#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))&0x200) + + void Atari2600::Speaker::get_samples(unsigned int number_of_samples, int16_t *target) { for(unsigned int c = 0; c < number_of_samples; c++) @@ -817,22 +837,89 @@ void Atari2600::Speaker::get_samples(unsigned int number_of_samples, int16_t *ta target[c] = 0; for(int channel = 0; channel < 2; channel++) { + _divider_counter[channel] ++; + int level = 0; switch(_control[channel]) { - case 0x0: case 0xb: - target[c] += _volume[channel] * 1024; + case 0x0: case 0xb: // constant 1 + level = 1; break; - case 0x4: case 0x5: - _divider_counter[channel] ++; - target[c] += _volume[channel] * 1024 * ((_divider_counter[channel] / (_divider[channel]+1))&1); + case 0x4: case 0x5: // div2 tone + level = (_divider_counter[channel] / (_divider[channel]+1))&1; break; - case 0xc: case 0xd: - _divider_counter[channel] ++; - target[c] += _volume[channel] * 1024 * ((_divider_counter[channel] / ((_divider[channel]+1)*3))&1); + case 0xc: case 0xd: // div6 tone + level = (_divider_counter[channel] / ((_divider[channel]+1)*3))&1; + break; + + case 0x6: case 0xa: // div31 tone + level = (_divider_counter[channel] / (_divider[channel]+1))%30 <= 18; + break; + + case 0xe: // div93 tone + level = (_divider_counter[channel] / ((_divider[channel]+1)*3))%30 <= 18; + break; + + case 0x1: // 4-bit poly + level = _poly4_counter[channel]&1; + if(_divider_counter[channel] == _divider[channel]+1) + { + _divider_counter[channel] = 0; + advance_poly4(channel); + } + break; + + case 0x2: // 4-bit poly div31 + level = _poly4_counter[channel]&1; + if(_divider_counter[channel]%(30*(_divider[channel]+1)) == 18) + { + advance_poly4(channel); + } + break; + + case 0x3: // 5/4-bit poly + level = _output_state[channel]; + if(_divider_counter[channel] == _divider[channel]+1) + { + if(_poly5_counter[channel]&1) + { + _output_state[channel] = _poly4_counter[channel]&1; + advance_poly4(channel); + } + advance_poly5(channel); + } + break; + + case 0x7: case 0x9: // 5-bit poly + level = _poly5_counter[channel]&1; + if(_divider_counter[channel] == _divider[channel]+1) + { + _divider_counter[channel] = 0; + advance_poly5(channel); + } + break; + + case 0xf: // 5-bit poly div6 + level = _poly5_counter[channel]&1; + if(_divider_counter[channel] == (_divider[channel]+1)*3) + { + _divider_counter[channel] = 0; + advance_poly5(channel); + } + break; + + case 0x8: // 9-bit poly + level = _poly9_counter[channel]&1; + if(_divider_counter[channel] == _divider[channel]+1) + { + _divider_counter[channel] = 0; + advance_poly9(channel); + } break; } + + target[c] += _volume[channel] * 1024 * level; } } } diff --git a/Machines/Atari2600/Atari2600.hpp b/Machines/Atari2600/Atari2600.hpp index b6c84995b..c6c8e99a4 100644 --- a/Machines/Atari2600/Atari2600.hpp +++ b/Machines/Atari2600/Atari2600.hpp @@ -21,6 +21,9 @@ const unsigned int number_of_recorded_counters = 7; class Speaker: public ::Outputs::Filter { public: + Speaker(); + ~Speaker(); + void set_volume(int channel, uint8_t volume); void set_divider(int channel, uint8_t divider); void set_control(int channel, uint8_t control); @@ -32,9 +35,16 @@ class Speaker: public ::Outputs::Filter { uint8_t _volume[2]; uint8_t _divider[2]; uint8_t _control[2]; - int _shift_counter[2]; - int _divider_counter[2]; + + int _poly4_counter[2]; + int _poly5_counter[2]; + int _poly9_counter[2]; int _output_state[2]; + + int _divider_counter[2]; + + int _pattern_periods[16]; + int _patterns[16][512]; }; class Machine: public CPU6502::Processor, public CRTMachine::Machine { diff --git a/OSBindings/Mac/Clock Signal/Wrappers/AudioQueue.m b/OSBindings/Mac/Clock Signal/Wrappers/AudioQueue.m index 9763cd1c6..9a409ea60 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/AudioQueue.m +++ b/OSBindings/Mac/Clock Signal/Wrappers/AudioQueue.m @@ -11,7 +11,7 @@ #define AudioQueueNumAudioBuffers 4 #define AudioQueueStreamLength 1024 -#define AudioQueueBufferLength 256 +#define AudioQueueBufferLength 512 enum { AudioQueueCanProceed, diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSMachine.mm b/OSBindings/Mac/Clock Signal/Wrappers/CSMachine.mm index f17c7e31a..a52d68bec 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSMachine.mm @@ -70,7 +70,7 @@ struct SpeakerDelegate: public Outputs::Speaker::Delegate { Outputs::Speaker *speaker = self.machine->get_speaker(); if(speaker) { - speaker->set_output_rate(sampleRate, 256); + speaker->set_output_rate(sampleRate, 512); speaker->set_delegate(delegate); return YES; }