mirror of
https://github.com/dingusdev/dingusppc.git
synced 2025-01-11 05:29:43 +00:00
Overhaul audio codec classes.
This commit is contained in:
parent
2fb23e210e
commit
3234f21cab
@ -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() {
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
|
@ -60,8 +60,8 @@ GrandCentral::GrandCentral() : PCIDevice("mac-io/grandcentral"), InterruptCtrl()
|
||||
this->snd_out_dma = std::unique_ptr<DMAChannel> (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
|
||||
|
@ -71,8 +71,8 @@ HeathrowIC::HeathrowIC() : PCIDevice("mac-io/heathrow"), InterruptCtrl()
|
||||
this->snd_out_dma = std::unique_ptr<DMAChannel> (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
|
||||
|
@ -54,8 +54,8 @@ OHare::OHare() : PCIDevice("mac-io/ohare"), InterruptCtrl()
|
||||
this->snd_out_dma = std::unique_ptr<DMAChannel> (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
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
Currently supported audio codecs:
|
||||
- PDM AWACs in Nubus Power Macintosh models
|
||||
- Screamer AWACs in Beige G3
|
||||
|
||||
Author: Max Poliakovski 2019-21
|
||||
*/
|
||||
|
||||
#include <devices/common/dbdma.h>
|
||||
#include <devices/deviceregistry.h>
|
||||
#include <devices/sound/awacs.h>
|
||||
#include <devices/sound/soundserver.h>
|
||||
#include <endianswap.h>
|
||||
@ -38,8 +36,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <loguru.hpp>
|
||||
#include <memory>
|
||||
|
||||
AwacsBase::AwacsBase()
|
||||
AwacsBase::AwacsBase(std::string name)
|
||||
{
|
||||
supports_types(HWCompType::SND_CODEC);
|
||||
|
||||
this->name = name;
|
||||
|
||||
// connect to SoundServer
|
||||
this->snd_server = dynamic_cast<SoundServer *>
|
||||
(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<AudioProcessor> (new AudioProcessor());
|
||||
|
||||
/* register audio processor chip with the I2C bus */
|
||||
I2CBus* i2c_bus = dynamic_cast<I2CBus*>(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<AudioProcessor> (new AudioProcessor());
|
||||
|
||||
/* register audio processor chip with the I2C bus */
|
||||
I2CBus* i2c_bus = dynamic_cast<I2CBus*>(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);
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
#ifndef AWAC_H
|
||||
#define AWAC_H
|
||||
|
||||
#include <devices/common/dbdma.h>
|
||||
#include <devices/common/dmacore.h>
|
||||
#include <devices/common/hwcomponent.h>
|
||||
#include <devices/common/i2c/i2c.h>
|
||||
#include <devices/sound/soundserver.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
/** 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<HWComponent> create() {
|
||||
return std::unique_ptr<AwacsScreamer>(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<AudioProcessor> audio_proc;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user