From 3234f21cab7ec722b3facf964ad31ce29f23a579 Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Sat, 11 Feb 2023 14:51:03 +0100 Subject: [PATCH] Overhaul audio codec classes. --- devices/common/dbdma.cpp | 2 + devices/common/hwcomponent.h | 1 + devices/ioctrl/amic.cpp | 2 +- devices/ioctrl/grandcentral.cpp | 4 +- devices/ioctrl/heathrow.cpp | 4 +- devices/ioctrl/ohare.cpp | 4 +- devices/sound/awacs.cpp | 121 ++++++++++++++------------------ devices/sound/awacs.h | 58 ++++++++------- 8 files changed, 95 insertions(+), 101 deletions(-) diff --git a/devices/common/dbdma.cpp b/devices/common/dbdma.cpp index 3a516e1..e71c768 100644 --- a/devices/common/dbdma.cpp +++ b/devices/common/dbdma.cpp @@ -324,6 +324,8 @@ void DMAChannel::resume() { void DMAChannel::abort() { LOG_F(9, "Aborting DMA channel"); + if (this->stop_cb) + this->stop_cb(); } void DMAChannel::pause() { diff --git a/devices/common/hwcomponent.h b/devices/common/hwcomponent.h index 6543b1b..195d79c 100644 --- a/devices/common/hwcomponent.h +++ b/devices/common/hwcomponent.h @@ -43,6 +43,7 @@ enum HWCompType { SCSI_BUS = 1ULL << 20, /* SCSI bus */ SCSI_HOST = 1ULL << 21, /* SCSI host adapter */ SCSI_DEV = 1ULL << 22, /* SCSI device */ + SND_CODEC = 1ULL << 30, /* Sound codec */ SND_SERVER = 1ULL << 31, /* host sound server */ FLOPPY_CTRL = 1ULL << 32, /* floppy disk controller */ FLOPPY_DRV = 1ULL << 33, /* floppy disk drive */ diff --git a/devices/ioctrl/amic.cpp b/devices/ioctrl/amic.cpp index 558f1d4..62edbc6 100644 --- a/devices/ioctrl/amic.cpp +++ b/devices/ioctrl/amic.cpp @@ -229,7 +229,7 @@ void AMIC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) this->snd_buf_size); this->snd_out_dma->enable(); this->awacs->set_sample_rate((this->snd_out_ctrl >> 1) & 3); - this->awacs->start_output_dma(); + this->awacs->dma_out_start(); } else { LOG_F(9, "AMIC Sound Out DMA disabled!"); this->snd_out_dma->disable(); diff --git a/devices/ioctrl/grandcentral.cpp b/devices/ioctrl/grandcentral.cpp index a129d8a..79008f3 100644 --- a/devices/ioctrl/grandcentral.cpp +++ b/devices/ioctrl/grandcentral.cpp @@ -60,8 +60,8 @@ GrandCentral::GrandCentral() : PCIDevice("mac-io/grandcentral"), InterruptCtrl() this->snd_out_dma = std::unique_ptr (new DMAChannel()); this->awacs->set_dma_out(this->snd_out_dma.get()); this->snd_out_dma->set_callbacks( - std::bind(&AwacsScreamer::dma_start, this->awacs.get()), - std::bind(&AwacsScreamer::dma_end, this->awacs.get()) + std::bind(&AwacsScreamer::dma_out_start, this->awacs.get()), + std::bind(&AwacsScreamer::dma_out_stop, this->awacs.get()) ); // connect serial HW diff --git a/devices/ioctrl/heathrow.cpp b/devices/ioctrl/heathrow.cpp index 22fe65f..8cd7bbb 100644 --- a/devices/ioctrl/heathrow.cpp +++ b/devices/ioctrl/heathrow.cpp @@ -71,8 +71,8 @@ HeathrowIC::HeathrowIC() : PCIDevice("mac-io/heathrow"), InterruptCtrl() this->snd_out_dma = std::unique_ptr (new DMAChannel()); this->screamer->set_dma_out(this->snd_out_dma.get()); this->snd_out_dma->set_callbacks( - std::bind(&AwacsScreamer::dma_start, this->screamer.get()), - std::bind(&AwacsScreamer::dma_end, this->screamer.get()) + std::bind(&AwacsScreamer::dma_out_start, this->screamer.get()), + std::bind(&AwacsScreamer::dma_out_stop, this->screamer.get()) ); // connect SCSI HW diff --git a/devices/ioctrl/ohare.cpp b/devices/ioctrl/ohare.cpp index 8433ef9..abc1687 100644 --- a/devices/ioctrl/ohare.cpp +++ b/devices/ioctrl/ohare.cpp @@ -54,8 +54,8 @@ OHare::OHare() : PCIDevice("mac-io/ohare"), InterruptCtrl() this->snd_out_dma = std::unique_ptr (new DMAChannel()); this->awacs->set_dma_out(this->snd_out_dma.get()); this->snd_out_dma->set_callbacks( - std::bind(&AwacsScreamer::dma_start, this->awacs.get()), - std::bind(&AwacsScreamer::dma_end, this->awacs.get()) + std::bind(&AwacsScreamer::dma_out_start, this->awacs.get()), + std::bind(&AwacsScreamer::dma_out_stop, this->awacs.get()) ); // connect serial HW diff --git a/devices/sound/awacs.cpp b/devices/sound/awacs.cpp index 095a252..be38ea3 100644 --- a/devices/sound/awacs.cpp +++ b/devices/sound/awacs.cpp @@ -1,6 +1,6 @@ /* DingusPPC - The Experimental PowerPC Macintosh emulator -Copyright (C) 2018-21 divingkatae and maximum +Copyright (C) 2018-23 divingkatae and maximum (theweirdo) spatium (Contact divingkatae#1017 or powermax#2286 on Discord for more info) @@ -24,11 +24,9 @@ along with this program. If not, see . Currently supported audio codecs: - PDM AWACs in Nubus Power Macintosh models - Screamer AWACs in Beige G3 - - Author: Max Poliakovski 2019-21 */ -#include +#include #include #include #include @@ -38,8 +36,12 @@ along with this program. If not, see . #include #include -AwacsBase::AwacsBase() +AwacsBase::AwacsBase(std::string name) { + supports_types(HWCompType::SND_CODEC); + + this->name = name; + // connect to SoundServer this->snd_server = dynamic_cast (gMachineObj->get_comp_by_name("SoundServer")); @@ -49,31 +51,43 @@ AwacsBase::AwacsBase() void AwacsBase::set_sample_rate(int sr_id) { if (sr_id > this->max_sr_id) { - LOG_F(ERROR, "AWACs: invalid sample rate ID %d!", sr_id); + LOG_F(ERROR, "%s: invalid sample rate ID %d!", this->name.c_str(), sr_id); } else { this->cur_sample_rate = this->sr_table[sr_id]; } }; -void AwacsBase::start_output_dma() +void AwacsBase::dma_out_start() { - int err; + int err; + bool reopen = false; - if ((err = this->snd_server->open_out_stream(this->cur_sample_rate, - (void *)this->dma_out_ch))) { - LOG_F(ERROR, "AWACs: unable to open sound output stream: %d", err); + if (this->out_stream_ready && this->out_sample_rate != this->cur_sample_rate) + reopen = true; + + if (reopen) { + snd_server->close_out_stream(); this->out_stream_ready = false; - return; } - this->out_stream_ready = true; + if (!this->out_stream_ready || reopen) { + if ((err = this->snd_server->open_out_stream(this->cur_sample_rate, + (void *)this->dma_out_ch))) { + LOG_F(ERROR, "%s: unable to open sound output stream: %d", + this->name.c_str(), err); + return; + } - if ((err = snd_server->start_out_stream())) { - LOG_F(ERROR, "AWACs: could not start sound output stream"); + if ((err = snd_server->start_out_stream())) { + LOG_F(ERROR, "%s: could not start sound output stream", this->name.c_str()); + } + + this->out_sample_rate = this->cur_sample_rate; + this->out_stream_ready = true; } } -void AwacsBase::stop_output_dma() +void AwacsBase::dma_out_stop() { if (this->out_stream_ready) { snd_server->close_out_stream(); @@ -82,7 +96,7 @@ void AwacsBase::stop_output_dma() } //=========================== PDM-style AWACs ================================= -AwacDevicePdm::AwacDevicePdm() : AwacsBase() +AwacDevicePdm::AwacDevicePdm() : AwacsBase("AWAC-PDM") { static int pdm_awac_freqs[3] = {22050, 29400, 44100}; @@ -93,31 +107,24 @@ AwacDevicePdm::AwacDevicePdm() : AwacsBase() uint32_t AwacDevicePdm::read_stat() { - LOG_F(INFO, "AWACs-PDM: status requested!"); + LOG_F(INFO, "%s: status requested!", this->name.c_str()); // TODO: return valid status including manufacturer & device IDs return 0; } void AwacDevicePdm::write_ctrl(uint32_t addr, uint16_t value) { - LOG_F(9, "AWACs-PDM: control updated, addr=0x%X, val=0x%X", addr, value); - if (addr <= 4) { this->ctrl_regs[addr] = value; } else { - LOG_F(ERROR, "AWACs-PDM: invalid control register address %d!", addr); + LOG_F(ERROR, "%s: invalid control register address %d!", this->name.c_str(), + addr); } } //============================= Screamer AWACs ================================ -AwacsScreamer::AwacsScreamer() : AwacsBase() +AwacsScreamer::AwacsScreamer(std::string name) : AwacsBase(name) { - this->audio_proc = std::unique_ptr (new AudioProcessor()); - - /* register audio processor chip with the I2C bus */ - I2CBus* i2c_bus = dynamic_cast(gMachineObj->get_comp_by_type(HWCompType::I2C_HOST)); - i2c_bus->register_device(0x45, this->audio_proc.get()); - static int screamer_freqs[8] = { 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 }; @@ -126,6 +133,16 @@ AwacsScreamer::AwacsScreamer() : AwacsBase() this->max_sr_id = 7; } +int AwacsScreamer::device_postinit() { + this->audio_proc = std::unique_ptr (new AudioProcessor()); + + /* register audio processor chip with the I2C bus */ + I2CBus* i2c_bus = dynamic_cast(gMachineObj->get_comp_by_type(HWCompType::I2C_HOST)); + i2c_bus->register_device(0x45, this->audio_proc.get()); + + return 0; +} + uint32_t AwacsScreamer::snd_ctrl_read(uint32_t offset, int size) { switch (offset) { @@ -136,7 +153,7 @@ uint32_t AwacsScreamer::snd_ctrl_read(uint32_t offset, int size) case AWAC_CODEC_STATUS_REG: return (AWAC_AVAILABLE << 8) | (AWAC_MAKER_CRYSTAL << 16) | (AWAC_REV_SCREAMER << 20); default: - LOG_F(ERROR, "Screamer: unsupported register at offset 0x%X", offset); + LOG_F(ERROR, "%s: unsupported register at offset 0x%X", this->name.c_str(), offset); } return 0; @@ -150,55 +167,25 @@ void AwacsScreamer::snd_ctrl_write(uint32_t offset, uint32_t value, int size) switch (offset) { case AWAC_SOUND_CTRL_REG: this->snd_ctrl_reg = BYTESWAP_32(value); - LOG_F(9, "Screamer: new sound control value = 0x%X", this->snd_ctrl_reg); + this->set_sample_rate((this->snd_ctrl_reg >> 8) & 7); break; case AWAC_CODEC_CTRL_REG: subframe = (value >> 14) & 3; reg_num = (value >> 20) & 7; data = ((value >> 8) & 0xF00) | ((value >> 24) & 0xFF); - LOG_F(9, "Screamer subframe = %d, reg = %d, data = %08X", + LOG_F(9, "%s subframe = %d, reg = %d, data = %08X", this->name.c_str(), subframe, reg_num, data); if (!subframe) this->control_regs[reg_num] = data; break; default: - LOG_F(ERROR, "Screamer: unsupported register at offset 0x%X", offset); + LOG_F(ERROR, "%s: unsupported register at offset 0x%X", this->name.c_str(), + offset); } } -void AwacsScreamer::open_stream(int sample_rate) -{ - int err; +static const DeviceDescription Screamer_Descriptor = { + AwacsScreamer::create, {}, {} +}; - if ((err = this->snd_server->open_out_stream(sample_rate, (void *)this->dma_out_ch))) { - LOG_F(ERROR, "Screamer: unable to open sound output stream: %d", err); - this->out_stream_ready = false; - } else { - this->out_sample_rate = sample_rate; - this->out_stream_ready = true; - } -} - -void AwacsScreamer::dma_start() -{ - int err; - - if (!this->out_stream_ready) { - this->open_stream(this->sr_table[(this->snd_ctrl_reg >> 8) & 7]); - } else if (this->out_sample_rate != this->sr_table[(this->snd_ctrl_reg >> 8) & 7]) { - snd_server->close_out_stream(); - this->open_stream(this->sr_table[(this->snd_ctrl_reg >> 8) & 7]); - } - - if (!this->out_stream_ready) { - return; - } - - if ((err = snd_server->start_out_stream())) { - LOG_F(ERROR, "Screamer: could not start sound output stream"); - } -} - -void AwacsScreamer::dma_end() -{ -} +REGISTER_DEVICE(ScreamerSnd, Screamer_Descriptor); diff --git a/devices/sound/awacs.h b/devices/sound/awacs.h index c2c51c2..e0de8b7 100644 --- a/devices/sound/awacs.h +++ b/devices/sound/awacs.h @@ -1,6 +1,6 @@ /* DingusPPC - The Experimental PowerPC Macintosh emulator -Copyright (C) 2018-21 divingkatae and maximum +Copyright (C) 2018-23 divingkatae and maximum (theweirdo) spatium (Contact divingkatae#1017 or powermax#2286 on Discord for more info) @@ -28,18 +28,19 @@ along with this program. If not, see . #ifndef AWAC_H #define AWAC_H -#include #include +#include #include #include #include #include +#include /** Base class for the AWACs codecs. */ -class AwacsBase { +class AwacsBase : public HWComponent { public: - AwacsBase(); + AwacsBase(std::string name); ~AwacsBase() = default; void set_dma_out(DmaOutChannel *dma_out_ch) { @@ -47,8 +48,8 @@ public: }; void set_sample_rate(int sr_id); - void start_output_dma(); - void stop_output_dma(); + void dma_out_start(); + void dma_out_stop(); protected: SoundServer *snd_server; // SoundServer instance pointer @@ -57,8 +58,9 @@ protected: int *sr_table; // pointer to the table of supported sample rates int max_sr_id; // maximum value for sample rate ID - bool out_stream_ready; - int cur_sample_rate; + bool out_stream_ready = false; + int cur_sample_rate = -1; + int out_sample_rate = -1; }; /** AWACs PDM-style sound codec. */ @@ -70,24 +72,17 @@ public: uint32_t read_stat(void); void write_ctrl(uint32_t addr, uint16_t value); - void dma_out_start(uint8_t sample_rate_id); - void dma_out_stop(); - private: uint16_t ctrl_regs[5]; // 12-bit wide control registers }; -/** AWACs Screamer registers offsets. */ +/** Offsets to MacIO sound codec registers. */ enum { AWAC_SOUND_CTRL_REG = 0x00, AWAC_CODEC_CTRL_REG = 0x10, AWAC_CODEC_STATUS_REG = 0x20, }; -/** AWACs Screamer manufacturer and revision. */ -#define AWAC_MAKER_CRYSTAL 1 -#define AWAC_REV_SCREAMER 3 - /** Apple source calls this kValidData but doesn't explain what it actually means. It seems like it's used to check if the sound codec is available. @@ -145,27 +140,36 @@ private: int auto_inc; }; -/** AWACs Screamer sound codec. */ -class AwacsScreamer : public AwacsBase { +/** Sound codec interface with the typical MacIO access. */ +class MacioSndCtrl : public virtual AwacsBase { public: - AwacsScreamer(); + virtual uint32_t snd_ctrl_read(uint32_t offset, int size) = 0; + virtual void snd_ctrl_write(uint32_t offset, uint32_t value, int size) = 0; +}; + +/** AWACs Screamer manufacturer and revision. */ +#define AWAC_MAKER_CRYSTAL 1 +#define AWAC_REV_SCREAMER 3 + +/** Screamer sound codec. */ +class AwacsScreamer : public MacioSndCtrl { +public: + AwacsScreamer(std::string name = "Screamer"); ~AwacsScreamer() = default; uint32_t snd_ctrl_read(uint32_t offset, int size); void snd_ctrl_write(uint32_t offset, uint32_t value, int size); - void dma_start(); - void dma_end(); + int device_postinit(); -protected: - void open_stream(int sample_rate); + static std::unique_ptr create() { + return std::unique_ptr(new AwacsScreamer("Screamer")); + } private: uint32_t snd_ctrl_reg = { 0 }; - uint16_t control_regs[8] = { 0 }; /* control registers, each 12-bits wide */ - uint8_t is_busy = 0; - - int out_sample_rate; + uint16_t control_regs[8] = { 0 }; // control registers, each 12-bits wide + uint8_t is_busy = 0; std::unique_ptr audio_proc; };