diff --git a/Components/6560/6560.cpp b/Components/6560/6560.cpp index 1377ea9fc..7bb013ebb 100644 --- a/Components/6560/6560.cpp +++ b/Components/6560/6560.cpp @@ -95,9 +95,14 @@ 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 update(r, m, up) _counters[r]++; if((_counters[r] >> m) == 0x7f) { up(r); _counters[r] = _control_registers[r]&0x7f; } +#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) == 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) { diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index 3b14c40be..b445c5dd3 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((float)(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..408ae8735 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; } } @@ -193,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 261965912..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; @@ -114,7 +124,7 @@ template class Filter: public Speaker { 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) { @@ -215,7 +225,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;