diff --git a/Machines/Amiga/Audio.cpp b/Machines/Amiga/Audio.cpp index c603f0614..8b21431da 100644 --- a/Machines/Amiga/Audio.cpp +++ b/Machines/Amiga/Audio.cpp @@ -29,18 +29,7 @@ Audio::Audio(Chipset &chipset, uint16_t *ram, size_t word_size, float output_rat speaker_.set_input_rate(output_rate); } -bool Audio::advance_dma(int channel) { - if(!channels_[channel].wants_data) { - return false; - } - - set_data(channel, ram_[pointer_[size_t(channel) & ram_mask_]]); - if(channels_[channel].state != Channel::State::WaitingForDummyDMA) { - ++pointer_[size_t(channel)]; - } - - return true; -} +// MARK: - Exposed setters. void Audio::set_length(int channel, uint16_t length) { assert(channel >= 0 && channel < 4); @@ -80,6 +69,21 @@ void Audio::set_interrupt_requests(uint16_t requests) { channels_[3].interrupt_pending = requests & uint16_t(InterruptFlag::AudioChannel3); } +// MARK: - DMA and mixing. + +bool Audio::advance_dma(int channel) { + if(!channels_[channel].wants_data) { + return false; + } + + set_data(channel, ram_[pointer_[size_t(channel) & ram_mask_]]); + if(channels_[channel].state != Channel::State::WaitingForDummyDMA) { + ++pointer_[size_t(channel)]; + } + + return true; +} + void Audio::output() { constexpr InterruptFlag interrupts[] = { InterruptFlag::AudioChannel0, @@ -133,6 +137,8 @@ void Audio::output() { } } +// MARK: - Per-channel logic. + /* Big spiel on the state machine: @@ -300,13 +306,19 @@ void Audio::output() { // -// Non-action fallback transition. +// Non-action fallback transition and setter, plus specialised begin_state declarations. // +template void Audio::Channel::begin_state() { + state = end; +} +template <> void Audio::Channel::begin_state(); +template <> void Audio::Channel::begin_state(); + template < Audio::Channel::State begin, Audio::Channel::State end> bool Audio::Channel::transit() { - state = end; + begin_state(); return false; } @@ -317,7 +329,7 @@ template < template <> bool Audio::Channel::transit< Audio::Channel::State::Disabled, Audio::Channel::State::WaitingForDummyDMA>() { - state = State::WaitingForDummyDMA; + begin_state(); period_counter = period; // i.e. percntrld length_counter = length; // i.e. lencntrld @@ -328,7 +340,7 @@ template <> bool Audio::Channel::transit< template <> bool Audio::Channel::transit< Audio::Channel::State::Disabled, Audio::Channel::State::PlayingHigh>() { - state = State::PlayingHigh; + begin_state(); data_latch = data; // i.e. pbufld1 wants_data = true; @@ -359,7 +371,7 @@ template <> bool Audio::Channel::output() { template <> bool Audio::Channel::transit< Audio::Channel::State::WaitingForDummyDMA, Audio::Channel::State::WaitingForDMA>() { - state = State::WaitingForDMA; + begin_state(); wants_data = true; if(length == 1) { @@ -389,7 +401,7 @@ template <> bool Audio::Channel::output bool Audio::Channel::transit< Audio::Channel::State::WaitingForDMA, Audio::Channel::State::PlayingHigh>() { - state = State::PlayingHigh; + begin_state(); data_latch = data; wants_data = true; @@ -417,7 +429,7 @@ template <> bool Audio::Channel::output() template <> bool Audio::Channel::transit< Audio::Channel::State::PlayingHigh, Audio::Channel::State::PlayingLow>() { - state = State::PlayingLow; + begin_state(); // TODO: if AUDxAP, then pubfid2 // TODO: if AUDxAP and AUDxON, then AUDxDR @@ -441,6 +453,13 @@ template <> bool Audio::Channel::transit< return false; } +template <> void Audio::Channel::begin_state() { + state = Audio::Channel::State::PlayingHigh; + + // Output high byte. + output_level = int8_t(data_latch >> 8); +} + template <> bool Audio::Channel::output() { -- period_counter; @@ -450,9 +469,6 @@ template <> bool Audio::Channel::output() { return transit(); } - // Output high byte. - output_level = int8_t(data_latch >> 8); - return false; } @@ -463,7 +479,7 @@ template <> bool Audio::Channel::output() { template <> bool Audio::Channel::transit< Audio::Channel::State::PlayingLow, Audio::Channel::State::PlayingHigh>() { - state = State::PlayingHigh; + begin_state(); // TODO: include napnav in tests @@ -484,6 +500,13 @@ template <> bool Audio::Channel::transit< return false; } +template <> void Audio::Channel::begin_state() { + state = Audio::Channel::State::PlayingLow; + + // Output low byte. + output_level = int8_t(data_latch & 0xff); +} + template <> bool Audio::Channel::output() { -- period_counter; @@ -497,8 +520,6 @@ template <> bool Audio::Channel::output() { return transit(); } - // Output low byte. - output_level = int8_t(data_latch & 0xff); return false; } diff --git a/Machines/Amiga/Audio.hpp b/Machines/Amiga/Audio.hpp index e328d8e7a..3683e057d 100644 --- a/Machines/Amiga/Audio.hpp +++ b/Machines/Amiga/Audio.hpp @@ -100,10 +100,23 @@ class Audio: public DMADevice<4> { PlayingLow, // 011 } state = State::Disabled; + /// Dispatches to the appropriate templatised output for the current state. + /// @returns @c true if an interrupt should be posted; @c false otherwise. bool output(); + + /// Applies dynamic logic for @c state, mostly testing for potential state transitions. + /// @returns @c true if an interrupt should be posted; @c false otherwise. template bool output(); + + /// Transitions from @c begin to @c end, calling the appropriate @c begin_state + /// and taking any steps specific to that particular transition. + /// @returns @c true if an interrupt should be posted; @c false otherwise. template bool transit(); + /// Begins @c state, performing all fixed logic that would otherwise have to be + /// repeated endlessly in the relevant @c output. + template void begin_state(); + // Output state. int8_t output_level = 0; uint8_t output_phase = 0; // TODO: this should count down, not up.