diff --git a/Machines/Apple/Macintosh/Audio.cpp b/Machines/Apple/Macintosh/Audio.cpp index 3dedb6d0a..3946553fc 100644 --- a/Machines/Apple/Macintosh/Audio.cpp +++ b/Machines/Apple/Macintosh/Audio.cpp @@ -12,7 +12,8 @@ using namespace Apple::Macintosh; namespace { -// The sample_length is coupled with the clock rate selected within the Macintosh proper. +// The sample_length is coupled with the clock rate selected within the Macintosh proper; +// as per the header-declaration a divide-by-two clock is expected to arrive here. const std::size_t sample_length = 352 / 2; } @@ -36,6 +37,7 @@ void Audio::set_volume(int volume) { // Post the volume change as a deferred event. task_queue_.defer([=] () { volume_ = volume; + set_volume_multiplier(); }); } @@ -47,6 +49,7 @@ void Audio::set_enabled(bool on) { // Post the enabled mask change as a deferred event. task_queue_.defer([=] () { enabled_mask_ = int(on); + set_volume_multiplier(); }); } @@ -58,7 +61,12 @@ bool Audio::is_zero_level() { void Audio::set_sample_volume_range(std::int16_t range) { // Some underflow here doesn't really matter. - volume_multiplier_ = range / (7 * 255); + output_volume_ = range / (7 * 255); + set_volume_multiplier(); +} + +void Audio::set_volume_multiplier() { + volume_multiplier_ = int16_t(output_volume_ * volume_ * enabled_mask_); } void Audio::get_samples(std::size_t number_of_samples, int16_t *target) { @@ -66,14 +74,24 @@ void Audio::get_samples(std::size_t number_of_samples, int16_t *target) { // in fact it uses pulse-width modulation. But the scale for pulses isn't specified, so // that's something to return to. - // TODO: temporary implementation. Very inefficient. Replace. - for(std::size_t sample = 0; sample < number_of_samples; ++sample) { - target[sample] = volume_multiplier_ * int16_t(sample_queue_.buffer[sample_queue_.read_pointer] * volume_ * enabled_mask_); - ++subcycle_offset_; + while(number_of_samples) { + // Determine how many output samples will be at the same level. + const auto cycles_left_in_sample = std::min(number_of_samples, sample_length - subcycle_offset_); - if(subcycle_offset_ == sample_length) { - subcycle_offset_ = 0; - sample_queue_.read_pointer = (sample_queue_.read_pointer + 1) % sample_queue_.buffer.size(); + // Determine the output level, and output that many samples. + // (Hoping that the copiler substitutes an effective memset16-type operation here). + const int16_t output_level = volume_multiplier_ * (int16_t(sample_queue_.buffer[sample_queue_.read_pointer]) - 128); + for(size_t c = 0; c < cycles_left_in_sample; ++c) { + target[c] = output_level; } + target += cycles_left_in_sample; + + // Advance the sample pointer. + subcycle_offset_ += cycles_left_in_sample; + sample_queue_.read_pointer = (sample_queue_.read_pointer + (subcycle_offset_ / sample_length)) % sample_queue_.buffer.size(); + subcycle_offset_ %= sample_length; + + // Decreate the number of samples left to write. + number_of_samples -= cycles_left_in_sample; } } diff --git a/Machines/Apple/Macintosh/Audio.hpp b/Machines/Apple/Macintosh/Audio.hpp index c1348cbf1..cfec007d6 100644 --- a/Machines/Apple/Macintosh/Audio.hpp +++ b/Machines/Apple/Macintosh/Audio.hpp @@ -75,9 +75,11 @@ class Audio: public ::Outputs::Speaker::SampleSource { // thread only. int volume_ = 0; int enabled_mask_ = 0; + std::int16_t output_volume_ = 0; std::int16_t volume_multiplier_ = 0; std::size_t subcycle_offset_ = 0; + void set_volume_multiplier(); }; }