From 1e0fcbbee8c2f49c812dffd6c8e370c44d70ef81 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 1 Jun 2016 19:53:16 -0400 Subject: [PATCH] Made a very basic stab at a couple of the tone generators, added straight-through path for the speaker when input rate exactly equals output rate. --- Machines/Atari2600/Atari2600.cpp | 20 ++++++-- Machines/Atari2600/Atari2600.hpp | 4 +- Outputs/Speaker.hpp | 84 ++++++++++++++++++++++---------- 3 files changed, 76 insertions(+), 32 deletions(-) diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index f9229f97c..9ce5deba7 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -802,6 +802,7 @@ void Atari2600::Speaker::set_volume(int channel, uint8_t volume) void Atari2600::Speaker::set_divider(int channel, uint8_t divider) { _divider[channel] = divider & 0x1f; + _divider_counter[channel] = 0; } void Atari2600::Speaker::set_control(int channel, uint8_t control) @@ -816,12 +817,21 @@ void Atari2600::Speaker::get_samples(unsigned int number_of_samples, int16_t *ta target[c] = 0; for(int channel = 0; channel < 2; channel++) { - if(!_control[channel]) - { - target[c] += _volume[channel] * 1024; - } - else + switch(_control[channel]) { + case 0x0: case 0xb: + target[c] += _volume[channel] * 1024; + break; + + case 0x4: case 0x5: + _divider_counter[channel] ++; + target[c] += _volume[channel] * 1024 * ((_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); + break; } } } diff --git a/Machines/Atari2600/Atari2600.hpp b/Machines/Atari2600/Atari2600.hpp index 2965a26c4..b6c84995b 100644 --- a/Machines/Atari2600/Atari2600.hpp +++ b/Machines/Atari2600/Atari2600.hpp @@ -32,7 +32,9 @@ class Speaker: public ::Outputs::Filter { uint8_t _volume[2]; uint8_t _divider[2]; uint8_t _control[2]; - int _shift_counters[2]; + int _shift_counter[2]; + int _divider_counter[2]; + int _output_state[2]; }; class Machine: public CPU6502::Processor, public CRTMachine::Machine { diff --git a/Outputs/Speaker.hpp b/Outputs/Speaker.hpp index efd1fe772..9742e86df 100644 --- a/Outputs/Speaker.hpp +++ b/Outputs/Speaker.hpp @@ -88,20 +88,16 @@ template class Filter: public Speaker { { if(_coefficients_are_dirty) update_filter_coefficients(); - // TODO: what if output rate is greater than input rate? - - // fill up as much of the input buffer as possible - while(input_cycles) + // if input and output rates exactly match, just accumulate results and pass on + if(_input_cycles_per_second == _output_cycles_per_second) { - unsigned int cycles_to_read = (unsigned int)std::min((int)input_cycles, _number_of_taps - _input_buffer_depth); - static_cast(this)->get_samples(cycles_to_read, &_input_buffer.get()[_input_buffer_depth]); - input_cycles -= cycles_to_read; - _input_buffer_depth += cycles_to_read; - - if(_input_buffer_depth == _number_of_taps) + while(input_cycles) { - _buffer_in_progress.get()[_buffer_in_progress_pointer] = _filter->apply(_input_buffer.get()); - _buffer_in_progress_pointer++; + unsigned int cycles_to_read = (unsigned int)(_buffer_size - _buffer_in_progress_pointer); + if(cycles_to_read > input_cycles) cycles_to_read = input_cycles; + + static_cast(this)->get_samples(cycles_to_read, &_buffer_in_progress.get()[_buffer_in_progress_pointer]); + _buffer_in_progress_pointer += cycles_to_read; // announce to delegate if full if(_buffer_in_progress_pointer == _buffer_size) @@ -113,24 +109,60 @@ template class Filter: public Speaker { } } - // If the next loop around is going to reuse some of the samples just collected, use a memmove to - // preserve them in the correct locations (TODO: use a longer buffer to fix that) and don't skip - // anything. Otherwise skip as required to get to the next sample batch and don't expect to reuse. - uint64_t steps = _stepper->step(); - if(steps < _number_of_taps) + input_cycles -= cycles_to_read; + } + + return; + } + + // if the output rate is less than the input rate, use the filter + if(_input_cycles_per_second > _output_cycles_per_second) + { + while(input_cycles) + { + unsigned int cycles_to_read = (unsigned int)std::min((int)input_cycles, _number_of_taps - _input_buffer_depth); + static_cast(this)->get_samples(cycles_to_read, &_input_buffer.get()[_input_buffer_depth]); + input_cycles -= cycles_to_read; + _input_buffer_depth += cycles_to_read; + + if(_input_buffer_depth == _number_of_taps) { - int16_t *input_buffer = _input_buffer.get(); - memmove(input_buffer, &input_buffer[steps], sizeof(int16_t) * ((size_t)_number_of_taps - (size_t)steps)); - _input_buffer_depth -= steps; - } - else - { - if(steps > _number_of_taps) - static_cast(this)->skip_samples((unsigned int)steps - (unsigned int)_number_of_taps); - _input_buffer_depth = 0; + _buffer_in_progress.get()[_buffer_in_progress_pointer] = _filter->apply(_input_buffer.get()); + _buffer_in_progress_pointer++; + + // announce to delegate if full + if(_buffer_in_progress_pointer == _buffer_size) + { + _buffer_in_progress_pointer = 0; + if(_delegate) + { + _delegate->speaker_did_complete_samples(this, _buffer_in_progress.get(), _buffer_size); + } + } + + // If the next loop around is going to reuse some of the samples just collected, use a memmove to + // preserve them in the correct locations (TODO: use a longer buffer to fix that) and don't skip + // anything. Otherwise skip as required to get to the next sample batch and don't expect to reuse. + uint64_t steps = _stepper->step(); + if(steps < _number_of_taps) + { + int16_t *input_buffer = _input_buffer.get(); + memmove(input_buffer, &input_buffer[steps], sizeof(int16_t) * ((size_t)_number_of_taps - (size_t)steps)); + _input_buffer_depth -= steps; + } + else + { + if(steps > _number_of_taps) + static_cast(this)->skip_samples((unsigned int)steps - (unsigned int)_number_of_taps); + _input_buffer_depth = 0; + } } } + + return; } + + // TODO: input rate is less than output rate } private: