mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-13 22:32:03 +00:00
Provides a greater wealth of audio data.
This commit is contained in:
parent
7be3578497
commit
0a94184d6b
@ -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.
|
||||
}
|
||||
|
@ -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 0–5 select
|
||||
/// a volume of [0–63]/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];
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user