diff --git a/Machines/Apple/Macintosh/Audio.cpp b/Machines/Apple/Macintosh/Audio.cpp index 016c81f73..77f806725 100644 --- a/Machines/Apple/Macintosh/Audio.cpp +++ b/Machines/Apple/Macintosh/Audio.cpp @@ -11,7 +11,7 @@ using namespace Apple::Macintosh; namespace { -const HalfCycles sample_length(704); +const std::size_t sample_length = 352; } Audio::Audio(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {} @@ -52,13 +52,50 @@ 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; + volume_multiplier_ = range / (7 * 255); } void Audio::get_samples(std::size_t number_of_samples, int16_t *target) { - // TODO. + const auto write_pointer = sample_queue_.write_pointer.load(); + auto read_pointer = sample_queue_.read_pointer.load(); + + // TODO: the implementation below acts as if the hardware uses pulse-amplitude modulation; + // 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. + while(number_of_samples--) { + *target = volume_multiplier_ * int16_t(sample_queue_.buffer[read_pointer] * volume_ * enabled_mask_); + ++target; + ++subcycle_offset_; + + if(subcycle_offset_ == sample_length) { +// printf("%d: %d\n", sample_queue_.buffer[read_pointer], volume_multiplier_ * sample_queue_.buffer[read_pointer] * volume_ * enabled_mask_); + subcycle_offset_ = 0; + const unsigned int next_read_pointer = (read_pointer + 1) % sample_queue_.buffer.size(); + if(next_read_pointer != write_pointer) { + read_pointer = next_read_pointer; + } + } + } + + sample_queue_.read_pointer.store(read_pointer); } void Audio::skip_samples(std::size_t number_of_samples) { - // TODO. + const auto write_pointer = sample_queue_.write_pointer.load(); + auto read_pointer = sample_queue_.read_pointer.load(); + + // Number of samples that would be consumed is (number_of_samples + subcycle_offset_) / sample_length. + const unsigned int samples_passed = static_cast((number_of_samples + subcycle_offset_) / sample_length); + subcycle_offset_ = (number_of_samples + subcycle_offset_) % sample_length; + + // Get also number of samples available. + const unsigned int samples_available = static_cast((write_pointer + sample_queue_.buffer.size() - read_pointer) % sample_queue_.buffer.size()); + + // Advance by whichever of those is the lower number. + const auto samples_to_consume = std::min(samples_available, samples_passed); + read_pointer = (read_pointer + samples_to_consume) % sample_queue_.buffer.size(); + + sample_queue_.read_pointer.store(read_pointer); } diff --git a/Machines/Apple/Macintosh/Audio.hpp b/Machines/Apple/Macintosh/Audio.hpp index 375d1c247..4f9b9ff27 100644 --- a/Machines/Apple/Macintosh/Audio.hpp +++ b/Machines/Apple/Macintosh/Audio.hpp @@ -72,7 +72,7 @@ class Audio: public ::Outputs::Speaker::SampleSource { std::int16_t volume_multiplier_ = 0; - HalfCycles subcycle_offset_; + std::size_t subcycle_offset_; }; } diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 7f282086d..60e2b8082 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -340,6 +340,10 @@ class ConcreteMachine: if(port == Port::B && line == Line::Two) keyboard_.set_input(value); } + void run_for(HalfCycles duration) { + audio_.time_since_update += duration; + } + private: ConcreteMachine &machine_; RealTimeClock &clock_;