mirror of
https://github.com/TomHarte/CLK.git
synced 2024-08-25 15:28:58 +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 "Audio.hpp"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
using namespace Amiga;
|
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;
|
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
|
#define Audio_hpp
|
||||||
|
|
||||||
#include "DMADevice.hpp"
|
#include "DMADevice.hpp"
|
||||||
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||||
|
#include "../../Concurrency/AsyncTaskQueue.hpp"
|
||||||
|
|
||||||
namespace Amiga {
|
namespace Amiga {
|
||||||
|
|
||||||
class Audio: public DMADevice<4> {
|
class Audio: public DMADevice<4> {
|
||||||
public:
|
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);
|
bool advance(int channel);
|
||||||
|
|
||||||
void set_length(int, uint16_t);
|
/// Standard JustInTimeActor item; allows this class to track the
|
||||||
void set_period(int, uint16_t);
|
/// amount of time between other events.
|
||||||
void set_volume(int, uint16_t);
|
void run_for(HalfCycles);
|
||||||
void set_data(int, uint16_t);
|
|
||||||
|
/// 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),
|
bitplanes_(DMA_CONSTRUCT),
|
||||||
copper_(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),
|
crt_(908, 4, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Red4Green4Blue4),
|
||||||
cia_a_handler_(map, disk_controller_, mouse_),
|
cia_a_handler_(map, disk_controller_, mouse_),
|
||||||
cia_b_handler_(disk_controller_),
|
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) {
|
if constexpr (cycle >= 0xd && cycle < 0x14) {
|
||||||
constexpr auto channel = (cycle - 0xd) >> 1;
|
constexpr auto channel = (cycle - 0xd) >> 1;
|
||||||
if((dma_control_ & AudioFlags[channel]) == AudioFlags[channel]) {
|
if((dma_control_ & AudioFlags[channel]) == AudioFlags[channel]) {
|
||||||
if(audio_.advance(channel)) {
|
if(audio_->advance(channel)) {
|
||||||
return false;
|
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) {
|
template <bool stop_on_cpu> Chipset::Changes Chipset::run(HalfCycles length) {
|
||||||
Changes changes;
|
Changes changes;
|
||||||
|
|
||||||
|
// TODO: incorporate audio timing here or deeper.
|
||||||
|
|
||||||
// This code uses 'pixels' as a measure, which is equivalent to one pixel clock time,
|
// This code uses 'pixels' as a measure, which is equivalent to one pixel clock time,
|
||||||
// or half a cycle.
|
// or half a cycle.
|
||||||
auto pixels_remaining = length.as<int>();
|
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_controller_.set_control(paula_disk_control_);
|
||||||
disk_.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;
|
break;
|
||||||
case Read(0x010): // ADKCONR
|
case Read(0x010): // ADKCONR
|
||||||
LOG("Read disk control");
|
LOG("Read disk control");
|
||||||
@ -732,6 +734,7 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
|
|||||||
break;
|
break;
|
||||||
case Write(0x096): // DMACON
|
case Write(0x096): // DMACON
|
||||||
ApplySetClear(dma_control_, 0x1fff);
|
ApplySetClear(dma_control_, 0x1fff);
|
||||||
|
audio_->set_channel_enables(dma_control_);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Interrupts.
|
// Interrupts.
|
||||||
@ -868,12 +871,12 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
|
|||||||
|
|
||||||
// Audio.
|
// Audio.
|
||||||
#define Audio(index, pointer) \
|
#define Audio(index, pointer) \
|
||||||
case Write(pointer + 0): audio_.set_pointer<index, 16>(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 + 2): audio_->set_pointer<index, 0>(cycle.value16()); break; \
|
||||||
case Write(pointer + 4): audio_.set_length(index, 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 + 6): audio_->set_period(index, cycle.value16()); break; \
|
||||||
case Write(pointer + 8): audio_.set_volume(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 + 10): audio_->set_data(index, cycle.value16()); break; \
|
||||||
|
|
||||||
Audio(0, 0x0a0);
|
Audio(0, 0x0a0);
|
||||||
Audio(1, 0x0b0);
|
Audio(1, 0x0b0);
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include "../../Activity/Source.hpp"
|
#include "../../Activity/Source.hpp"
|
||||||
#include "../../ClockReceiver/ClockingHintSource.hpp"
|
#include "../../ClockReceiver/ClockingHintSource.hpp"
|
||||||
|
#include "../../ClockReceiver/JustInTime.hpp"
|
||||||
#include "../../Components/6526/6526.hpp"
|
#include "../../Components/6526/6526.hpp"
|
||||||
#include "../../Inputs/Mouse.hpp"
|
#include "../../Inputs/Mouse.hpp"
|
||||||
#include "../../Outputs/CRT/CRT.hpp"
|
#include "../../Outputs/CRT/CRT.hpp"
|
||||||
@ -308,7 +309,7 @@ class Chipset: private ClockingHint::Observer {
|
|||||||
|
|
||||||
// MARK: - Audio.
|
// MARK: - Audio.
|
||||||
|
|
||||||
Audio audio_;
|
JustInTimeActor<Audio> audio_;
|
||||||
|
|
||||||
// MARK: - Serial port.
|
// MARK: - Serial port.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user