From 99157ad6b22319795feac0cba92e371913121912 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 19 Aug 2016 13:35:34 -0400 Subject: [PATCH 1/5] Ensured the 6560 knows its real audio clock on a PAL machine; removed some stray semicolons. --- Components/6560/6560.cpp | 4 ++-- Components/6560/6560.hpp | 6 +++++- Machines/Commodore/Vic-20/Vic20.cpp | 12 ++++++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Components/6560/6560.cpp b/Components/6560/6560.cpp index 1377ea9fc..bd07fa194 100644 --- a/Components/6560/6560.cpp +++ b/Components/6560/6560.cpp @@ -95,8 +95,8 @@ static uint8_t noise_pattern[] = { 0xf0, 0xe1, 0xe0, 0x78, 0x70, 0x38, 0x3c, 0x3e, 0x1e, 0x3c, 0x1e, 0x1c, 0x70, 0x3c, 0x38, 0x3f, }; -#define shift(r) _shift_registers[r] = (_shift_registers[r] << 1) | (((_shift_registers[r]^0x80)&_control_registers[r]) >> 7); -#define increment(r) _shift_registers[r] = (_shift_registers[r]+1)%8191; +#define shift(r) _shift_registers[r] = (_shift_registers[r] << 1) | (((_shift_registers[r]^0x80)&_control_registers[r]) >> 7) +#define increment(r) _shift_registers[r] = (_shift_registers[r]+1)%8191 #define update(r, m, up) _counters[r]++; if((_counters[r] >> m) == 0x7f) { up(r); _counters[r] = _control_registers[r]&0x7f; } void Speaker::get_samples(unsigned int number_of_samples, int16_t *target) diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index 3b14c40be..c30e074a0 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -68,7 +68,11 @@ template class MOS6560 { // show only the centre _crt->set_visible_area(_crt->get_rect_for_area(16, 237, 11*4, 55*4, 4.0f / 3.0f)); - _speaker->set_input_rate(255681.75); // assuming NTSC; clock rate / 4 + } + + void set_clock_rate(double clock_rate) + { + _speaker->set_input_rate(clock_rate / 4.0); } std::shared_ptr get_crt() { return _crt; } diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index cbe58f3f4..6c2f206a5 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -181,11 +181,19 @@ void Machine::set_region(Commodore::Vic20::Region region) { case PAL: set_clock_rate(1108404); - if(_mos6560) _mos6560->set_output_mode(MOS::MOS6560::OutputMode::PAL); + if(_mos6560) + { + _mos6560->set_output_mode(MOS::MOS6560::OutputMode::PAL); + _mos6560->set_clock_rate(1108404); + } break; case NTSC: set_clock_rate(1022727); - if(_mos6560) _mos6560->set_output_mode(MOS::MOS6560::OutputMode::NTSC); + if(_mos6560) + { + _mos6560->set_output_mode(MOS::MOS6560::OutputMode::NTSC); + _mos6560->set_clock_rate(1022727); + } break; } } From 7c9455251d7275cc63217c0fe39fcaef13edffab Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 19 Aug 2016 17:34:01 -0400 Subject: [PATCH 2/5] Fixed reload for the lower-frequency audio channels. --- Components/6560/6560.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Components/6560/6560.cpp b/Components/6560/6560.cpp index bd07fa194..6d56c386c 100644 --- a/Components/6560/6560.cpp +++ b/Components/6560/6560.cpp @@ -97,7 +97,7 @@ static uint8_t noise_pattern[] = { #define shift(r) _shift_registers[r] = (_shift_registers[r] << 1) | (((_shift_registers[r]^0x80)&_control_registers[r]) >> 7) #define increment(r) _shift_registers[r] = (_shift_registers[r]+1)%8191 -#define update(r, m, up) _counters[r]++; if((_counters[r] >> m) == 0x7f) { up(r); _counters[r] = _control_registers[r]&0x7f; } +#define update(r, m, up) _counters[r]++; if((_counters[r] >> m) == 0x7f) { up(r); _counters[r] = (unsigned int)(_control_registers[r]&0x7f) << m; } void Speaker::get_samples(unsigned int number_of_samples, int16_t *target) { From 8d839c5278c41e2aeaaeae2d17900eaa4ff35c0f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 20 Aug 2016 16:54:43 -0400 Subject: [PATCH 3/5] I think this is correct. And have explained why. --- Components/6560/6560.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Components/6560/6560.cpp b/Components/6560/6560.cpp index 6d56c386c..7bb013ebb 100644 --- a/Components/6560/6560.cpp +++ b/Components/6560/6560.cpp @@ -97,7 +97,12 @@ static uint8_t noise_pattern[] = { #define shift(r) _shift_registers[r] = (_shift_registers[r] << 1) | (((_shift_registers[r]^0x80)&_control_registers[r]) >> 7) #define increment(r) _shift_registers[r] = (_shift_registers[r]+1)%8191 -#define update(r, m, up) _counters[r]++; if((_counters[r] >> m) == 0x7f) { up(r); _counters[r] = (unsigned int)(_control_registers[r]&0x7f) << m; } +#define update(r, m, up) _counters[r]++; if((_counters[r] >> m) == 0x80) { up(r); _counters[r] = (unsigned int)(_control_registers[r]&0x7f) << m; } +// Note on slightly askew test: as far as I can make out, if the value in the register is 0x7f then what's supposed to happen +// is that the 0x7f is loaded, on the next clocked cycle the Vic spots a 0x7f, pumps the output, reloads, etc. No increment +// ever occurs. It's conditional. I don't really want two conditionals if I can avoid it so I'm incrementing regardless and +// testing against 0x80. The effect should be the same: loading with 0x7f means an output update every cycle, loading with 0x7e +// means every second cycle, etc. void Speaker::get_samples(unsigned int number_of_samples, int16_t *target) { From c7830909804d273ba74be9bdcad7770474e99334 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 21 Aug 2016 12:13:41 -0400 Subject: [PATCH 4/5] It turns out that the Vic has a 1.6Khz low-pass filter. So added that. --- Components/6560/6560.cpp | 6 +++++- Components/6560/6560.hpp | 2 +- Outputs/Speaker.hpp | 23 +++++++++++++++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/Components/6560/6560.cpp b/Components/6560/6560.cpp index 7bb013ebb..de405a735 100644 --- a/Components/6560/6560.cpp +++ b/Components/6560/6560.cpp @@ -15,7 +15,11 @@ Speaker::Speaker() : _control_registers{0, 0, 0, 0}, _shift_registers{0, 0, 0, 0}, _counters{2, 1, 0, 0} // create a slight phase offset for the three channels -{} +{ + // The Vic has a 1.6Khz low-pass filter; + // TODO: this is part of the Vic-20, not part of the 6560. So relocate it. + set_high_frequency_cut_off(1600); +} void Speaker::set_volume(uint8_t volume) { diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index c30e074a0..b445c5dd3 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -72,7 +72,7 @@ template class MOS6560 { void set_clock_rate(double clock_rate) { - _speaker->set_input_rate(clock_rate / 4.0); + _speaker->set_input_rate((float)(clock_rate / 4.0)); } std::shared_ptr get_crt() { return _crt; } diff --git a/Outputs/Speaker.hpp b/Outputs/Speaker.hpp index 261965912..cc5a39639 100644 --- a/Outputs/Speaker.hpp +++ b/Outputs/Speaker.hpp @@ -109,12 +109,20 @@ class Speaker { */ template class Filter: public Speaker { public: + Filter() : _high_frequency_cut_off(-1.0) {} + + void set_high_frequency_cut_off(float high_frequency) + { + _high_frequency_cut_off = high_frequency; + set_needs_updated_filter_coefficients(); + } + void run_for_cycles(unsigned int input_cycles) { if(_coefficients_are_dirty) update_filter_coefficients(); // if input and output rates exactly match, just accumulate results and pass on - if(_input_cycles_per_second == _output_cycles_per_second) + if(_input_cycles_per_second == _output_cycles_per_second && _high_frequency_cut_off < 0.0) { while(input_cycles) { @@ -196,6 +204,7 @@ template class Filter: public Speaker { std::unique_ptr _input_buffer; int _input_buffer_depth; + float _high_frequency_cut_off; void update_filter_coefficients() { @@ -215,7 +224,17 @@ template class Filter: public Speaker { _buffer_in_progress_pointer = 0; _stepper.reset(new SignalProcessing::Stepper((uint64_t)_input_cycles_per_second, (uint64_t)_output_cycles_per_second)); - _filter.reset(new SignalProcessing::FIRFilter((unsigned int)_number_of_taps, (float)_input_cycles_per_second, 0.0, (float)_output_cycles_per_second / 2.0f, SignalProcessing::FIRFilter::DefaultAttenuation)); + + float high_pass_frequency; + if(_high_frequency_cut_off > 0.0) + { + high_pass_frequency = std::min((float)_output_cycles_per_second / 2.0f, _high_frequency_cut_off); + } + else + { + high_pass_frequency = (float)_output_cycles_per_second / 2.0f; + } + _filter.reset(new SignalProcessing::FIRFilter((unsigned int)_number_of_taps, (float)_input_cycles_per_second, 0.0, high_pass_frequency, SignalProcessing::FIRFilter::DefaultAttenuation)); _input_buffer.reset(new int16_t[_number_of_taps]); _input_buffer_depth = 0; From 1541273785bfbcbe8dfbef6f6a7a5c463bd4dc87 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 21 Aug 2016 18:13:31 -0400 Subject: [PATCH 5/5] Moved responsibility for throwing in a low-pass filter up to the Vic, appropriately. --- Components/6560/6560.cpp | 6 +----- Machines/Commodore/Vic-20/Vic20.cpp | 1 + Outputs/Speaker.hpp | 21 +++++++++++---------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/Components/6560/6560.cpp b/Components/6560/6560.cpp index de405a735..7bb013ebb 100644 --- a/Components/6560/6560.cpp +++ b/Components/6560/6560.cpp @@ -15,11 +15,7 @@ Speaker::Speaker() : _control_registers{0, 0, 0, 0}, _shift_registers{0, 0, 0, 0}, _counters{2, 1, 0, 0} // create a slight phase offset for the three channels -{ - // The Vic has a 1.6Khz low-pass filter; - // TODO: this is part of the Vic-20, not part of the 6560. So relocate it. - set_high_frequency_cut_off(1600); -} +{} void Speaker::set_volume(uint8_t volume) { diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 6c2f206a5..408ae8735 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -201,6 +201,7 @@ void Machine::set_region(Commodore::Vic20::Region region) void Machine::setup_output(float aspect_ratio) { _mos6560.reset(new Vic6560()); + _mos6560->get_speaker()->set_high_frequency_cut_off(1600); // There is a 1.6Khz low-pass filter in the Vic-20. set_region(_region); memset(_mos6560->_videoMemoryMap, 0, sizeof(_mos6560->_videoMemoryMap)); diff --git a/Outputs/Speaker.hpp b/Outputs/Speaker.hpp index cc5a39639..72e595aa9 100644 --- a/Outputs/Speaker.hpp +++ b/Outputs/Speaker.hpp @@ -72,10 +72,20 @@ class Speaker { set_needs_updated_filter_coefficients(); } - Speaker() : _buffer_in_progress_pointer(0), _requested_number_of_taps(0) {} + /*! + Sets the cut-off frequency for a low-pass filter attached to the output of this speaker; optional. + */ + void set_high_frequency_cut_off(float high_frequency) + { + _high_frequency_cut_off = high_frequency; + set_needs_updated_filter_coefficients(); + } + + Speaker() : _buffer_in_progress_pointer(0), _requested_number_of_taps(0), _high_frequency_cut_off(-1.0) {} protected: std::unique_ptr _buffer_in_progress; + float _high_frequency_cut_off; int _buffer_size; int _buffer_in_progress_pointer; int _number_of_taps, _requested_number_of_taps; @@ -109,14 +119,6 @@ class Speaker { */ template class Filter: public Speaker { public: - Filter() : _high_frequency_cut_off(-1.0) {} - - void set_high_frequency_cut_off(float high_frequency) - { - _high_frequency_cut_off = high_frequency; - set_needs_updated_filter_coefficients(); - } - void run_for_cycles(unsigned int input_cycles) { if(_coefficients_are_dirty) update_filter_coefficients(); @@ -204,7 +206,6 @@ template class Filter: public Speaker { std::unique_ptr _input_buffer; int _input_buffer_depth; - float _high_frequency_cut_off; void update_filter_coefficients() {