From a8dd4660b26d6bd8a157b2954ed74b47d14e73d9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 1 Dec 2021 05:37:58 -0500 Subject: [PATCH] Adds a pipeline for audio output. --- Machines/Amiga/Amiga.cpp | 8 ++++++++ Machines/Amiga/Audio.cpp | 33 +++++++++++++++++++++++++++++++++ Machines/Amiga/Audio.hpp | 24 ++++++++++++++++++++---- Machines/Amiga/Chipset.cpp | 2 +- Machines/Amiga/Chipset.hpp | 5 +++++ 5 files changed, 67 insertions(+), 5 deletions(-) diff --git a/Machines/Amiga/Amiga.cpp b/Machines/Amiga/Amiga.cpp index 94617d592..a0aed3471 100644 --- a/Machines/Amiga/Amiga.cpp +++ b/Machines/Amiga/Amiga.cpp @@ -40,6 +40,7 @@ namespace Amiga { class ConcreteMachine: public Activity::Source, public CPU::MC68000::BusHandler, + public MachineTypes::AudioProducer, public MachineTypes::JoystickMachine, public MachineTypes::MappedKeyboardMachine, public MachineTypes::MediaTarget, @@ -183,10 +184,17 @@ class ConcreteMachine: Chipset chipset_; // MARK: - Activity Source + void set_activity_observer(Activity::Observer *observer) final { chipset_.set_activity_observer(observer); } + // MARK: - MachineTypes::AudioProducer. + + Outputs::Speaker::Speaker *get_speaker() final { + return chipset_.get_speaker(); + } + // MARK: - MachineTypes::ScanProducer. void set_scan_target(Outputs::Display::ScanTarget *scan_target) final { diff --git a/Machines/Amiga/Audio.cpp b/Machines/Amiga/Audio.cpp index a8b5f1bd2..5893d6550 100644 --- a/Machines/Amiga/Audio.cpp +++ b/Machines/Amiga/Audio.cpp @@ -17,6 +17,17 @@ using namespace Amiga; +Audio::Audio(Chipset &chipset, uint16_t *ram, size_t word_size, float output_rate) : + DMADevice<4>(chipset, ram, word_size) { + + // Mark all buffers as available. + for(auto &flag: buffer_available_) { + flag.store(true, std::memory_order::memory_order_relaxed); + } + + speaker_.set_input_rate(output_rate); +} + bool Audio::advance_dma(int channel) { switch(channels_[channel].state) { case Channel::State::WaitingForDMA: @@ -90,6 +101,28 @@ void Audio::output() { posit_interrupt(interrupts[c]); } } + + // TEMPORARY: just fill the audio buffer with silence. + if(!sample_pointer_) { + while(!buffer_available_[buffer_pointer_].load(std::memory_order::memory_order_relaxed)); + } + + buffer_[buffer_pointer_][sample_pointer_] = 0; + ++sample_pointer_; + + if(sample_pointer_ == buffer_[buffer_pointer_].size()) { + const auto &buffer = buffer_[buffer_pointer_]; + auto &flag = buffer_available_[buffer_pointer_]; + + flag.store(false, std::memory_order::memory_order_release); + queue_.enqueue([this, &buffer, &flag] { + speaker_.push(buffer.data(), buffer.size()); + flag.store(true, std::memory_order::memory_order_relaxed); + }); + + buffer_pointer_ = (buffer_pointer_ + 1) % BufferCount; + sample_pointer_ = 0; + } } /* diff --git a/Machines/Amiga/Audio.hpp b/Machines/Amiga/Audio.hpp index 87c8d171a..0fd1cd2f6 100644 --- a/Machines/Amiga/Audio.hpp +++ b/Machines/Amiga/Audio.hpp @@ -9,18 +9,19 @@ #ifndef Audio_hpp #define Audio_hpp +#include +#include + #include "DMADevice.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" #include "../../Concurrency/AsyncTaskQueue.hpp" +#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" namespace Amiga { class Audio: public DMADevice<4> { public: - Audio( - Chipset &chipset, uint16_t *ram, size_t word_size, - [[maybe_unused]] double output_rate) : - DMADevice<4>(chipset, ram, word_size) {} + Audio(Chipset &chipset, uint16_t *ram, size_t word_size, float output_rate); /// Idiomatic call-in for DMA scheduling; indicates that this class may /// perform a DMA access for the stated channel now. @@ -56,6 +57,11 @@ class Audio: public DMADevice<4> { /// Sets which interrupt requests are currently active. void set_interrupt_requests(uint16_t); + /// Obtains the output source. + Outputs::Speaker::Speaker *get_speaker() { + return &speaker_; + } + private: struct Channel { // The data latch plus a count of unused samples @@ -98,6 +104,16 @@ class Audio: public DMADevice<4> { template bool output(); template bool transit(); } channels_[4]; + + // Transient output state, and its destination. + Outputs::Speaker::PushLowpass speaker_; + Concurrency::AsyncTaskQueue queue_; + + using AudioBuffer = std::array; + static constexpr int BufferCount = 3; + AudioBuffer buffer_[BufferCount]; + std::atomic buffer_available_[BufferCount]; + size_t buffer_pointer_ = 0, sample_pointer_ = 0; }; } diff --git a/Machines/Amiga/Chipset.cpp b/Machines/Amiga/Chipset.cpp index 047976262..8d8309188 100644 --- a/Machines/Amiga/Chipset.cpp +++ b/Machines/Amiga/Chipset.cpp @@ -45,7 +45,7 @@ Chipset::Chipset(MemoryMap &map, int input_clock_rate) : }, bitplanes_(DMA_CONSTRUCT), copper_(DMA_CONSTRUCT), - audio_(DMA_CONSTRUCT, input_clock_rate), + audio_(DMA_CONSTRUCT, float(input_clock_rate / 2.0)), crt_(908, 4, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Red4Green4Blue4), cia_a_handler_(map, disk_controller_, mouse_), cia_b_handler_(disk_controller_), diff --git a/Machines/Amiga/Chipset.hpp b/Machines/Amiga/Chipset.hpp index 080c07fc4..d198f4cb1 100644 --- a/Machines/Amiga/Chipset.hpp +++ b/Machines/Amiga/Chipset.hpp @@ -115,6 +115,11 @@ class Chipset: private ClockingHint::Observer { // Input for receiving collected bitplanes. void post_bitplanes(const BitplaneData &data); + // Obtains the source of audio output. + Outputs::Speaker::Speaker *get_speaker() { + return audio_.get_speaker(); + } + private: friend class DMADeviceBase;