1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-04 18:29:40 +00:00

Provides a greater wealth of audio data.

This commit is contained in:
Thomas Harte 2021-11-11 09:24:15 -05:00
parent 7be3578497
commit 0a94184d6b
4 changed files with 114 additions and 20 deletions

View File

@ -7,21 +7,60 @@
//
#include "Audio.hpp"
#include <cassert>
using namespace Amiga;
bool Audio::advance(int) {
bool Audio::advance(int channel) {
if(channels_[channel].samples_remaining || !channels_[channel].length) {
return false;
}
set_data(channel, ram_[pointer_[size_t(channel)]]);
++pointer_[size_t(channel)];
--channels_[channel].length;
return false;
}
void Audio::set_length(int, uint16_t) {
void Audio::set_length(int channel, uint16_t length) {
assert(channel >= 0 && channel < 4);
channels_[channel].length = length;
}
void Audio::set_period(int, uint16_t) {
void Audio::set_period(int channel, uint16_t period) {
assert(channel >= 0 && channel < 4);
channels_[channel].period = period;
}
void Audio::set_volume(int, uint16_t) {
void Audio::set_volume(int channel, uint16_t volume) {
assert(channel >= 0 && channel < 4);
channels_[channel].volume = (volume & 0x40) ? 64 : (volume & 0x3f);
}
void Audio::set_data(int, uint16_t) {
void Audio::set_data(int channel, uint16_t data) {
assert(channel >= 0 && channel < 4);
if(!channels_[channel].samples_remaining) {
channels_[channel].period_counter = channels_[channel].period;
}
channels_[channel].samples_remaining = 2;
channels_[channel].data = data;
}
void Audio::set_channel_enables(uint16_t enables) {
channels_[0].dma_enabled = enables & 1;
channels_[1].dma_enabled = enables & 2;
channels_[2].dma_enabled = enables & 4;
channels_[3].dma_enabled = enables & 8;
}
void Audio::set_modulation_flags(uint16_t) {
}
void Audio::run_for(HalfCycles) {
// TODO:
//
// Check whether any channel's period counter is exhausted and, if
// so, attempt to consume another sample. If there are no more samples
// and length is 0, trigger an interrupt.
}

View File

@ -10,19 +10,70 @@
#define Audio_hpp
#include "DMADevice.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "../../Concurrency/AsyncTaskQueue.hpp"
namespace Amiga {
class Audio: public DMADevice<4> {
public:
using DMADevice::DMADevice;
Audio(
Chipset &chipset, uint16_t *ram, size_t word_size,
[[maybe_unused]] double output_rate) :
DMADevice<4>(chipset, ram, word_size) {}
/// Idiomatic call-in for DMA scheduling; indicates that this class may
/// perform a DMA access for the stated channel now.
bool advance(int channel);
void set_length(int, uint16_t);
void set_period(int, uint16_t);
void set_volume(int, uint16_t);
void set_data(int, uint16_t);
/// Standard JustInTimeActor item; allows this class to track the
/// amount of time between other events.
void run_for(HalfCycles);
/// Sets the total number of words to fetch for the given channel.
void set_length(int channel, uint16_t);
/// Sets the number of DMA windows between each 8-bit output,
/// in the same time base as @c ticks_per_line.
void set_period(int channel, uint16_t);
/// Sets the output volume for the given channel; if bit 6 is set
/// then output is maximal; otherwise bits 05 select
/// a volume of [063]/64, on a logarithmic scale.
void set_volume(int channel, uint16_t);
/// Sets the next two samples of audio to output.
void set_data(int channel, uint16_t);
/// Provides a copy of the DMA enable flags, for the purpose of
/// determining which channels are enabled for DMA.
void set_channel_enables(uint16_t);
/// Sets which channels, if any, modulate period or volume of
/// their neighbours.
void set_modulation_flags(uint16_t);
private:
struct Channel {
// The data latch plus a count of unused samples
// in the latch, which will always be 0, 1 or 2.
uint16_t data = 0x0000;
int samples_remaining = 0;
// Number of words remaining in DMA data.
uint16_t length = 0;
// Number of ticks between each sample, plus the
// current counter, which counts downward.
uint16_t period = 0;
uint16_t period_counter = 0;
// Output volume, [0, 64].
uint8_t volume;
// Indicates whether DMA is enabled for this channel.
bool dma_enabled = false;
} channels_[4];
};
}

View File

@ -73,7 +73,7 @@ Chipset::Chipset(MemoryMap &map, int input_clock_rate) :
},
bitplanes_(DMA_CONSTRUCT),
copper_(DMA_CONSTRUCT),
audio_(DMA_CONSTRUCT),
audio_(DMA_CONSTRUCT, input_clock_rate),
crt_(908, 4, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Red4Green4Blue4),
cia_a_handler_(map, disk_controller_, mouse_),
cia_b_handler_(disk_controller_),
@ -368,7 +368,7 @@ template <int cycle, bool stop_if_cpu> bool Chipset::perform_cycle() {
if constexpr (cycle >= 0xd && cycle < 0x14) {
constexpr auto channel = (cycle - 0xd) >> 1;
if((dma_control_ & AudioFlags[channel]) == AudioFlags[channel]) {
if(audio_.advance(channel)) {
if(audio_->advance(channel)) {
return false;
}
}
@ -444,6 +444,8 @@ template <bool stop_on_cpu> int Chipset::advance_slots(int first_slot, int last_
template <bool stop_on_cpu> Chipset::Changes Chipset::run(HalfCycles length) {
Changes changes;
// TODO: incorporate audio timing here or deeper.
// This code uses 'pixels' as a measure, which is equivalent to one pixel clock time,
// or half a cycle.
auto pixels_remaining = length.as<int>();
@ -693,7 +695,7 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
disk_controller_.set_control(paula_disk_control_);
disk_.set_control(paula_disk_control_);
// TODO: should also post to Paula.
audio_->set_modulation_flags(paula_disk_control_);
break;
case Read(0x010): // ADKCONR
LOG("Read disk control");
@ -732,6 +734,7 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
break;
case Write(0x096): // DMACON
ApplySetClear(dma_control_, 0x1fff);
audio_->set_channel_enables(dma_control_);
break;
// Interrupts.
@ -868,12 +871,12 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
// Audio.
#define Audio(index, pointer) \
case Write(pointer + 0): audio_.set_pointer<index, 16>(cycle.value16()); break; \
case Write(pointer + 2): audio_.set_pointer<index, 0>(cycle.value16()); break; \
case Write(pointer + 4): audio_.set_length(index, cycle.value16()); break; \
case Write(pointer + 6): audio_.set_period(index, cycle.value16()); break; \
case Write(pointer + 8): audio_.set_volume(index, cycle.value16()); break; \
case Write(pointer + 10): audio_.set_data(index, cycle.value16()); break; \
case Write(pointer + 0): audio_->set_pointer<index, 16>(cycle.value16()); break; \
case Write(pointer + 2): audio_->set_pointer<index, 0>(cycle.value16()); break; \
case Write(pointer + 4): audio_->set_length(index, cycle.value16()); break; \
case Write(pointer + 6): audio_->set_period(index, cycle.value16()); break; \
case Write(pointer + 8): audio_->set_volume(index, cycle.value16()); break; \
case Write(pointer + 10): audio_->set_data(index, cycle.value16()); break; \
Audio(0, 0x0a0);
Audio(1, 0x0b0);

View File

@ -17,6 +17,7 @@
#include "../../Activity/Source.hpp"
#include "../../ClockReceiver/ClockingHintSource.hpp"
#include "../../ClockReceiver/JustInTime.hpp"
#include "../../Components/6526/6526.hpp"
#include "../../Inputs/Mouse.hpp"
#include "../../Outputs/CRT/CRT.hpp"
@ -308,7 +309,7 @@ class Chipset: private ClockingHint::Observer {
// MARK: - Audio.
Audio audio_;
JustInTimeActor<Audio> audio_;
// MARK: - Serial port.