From 0d2301c0067a554df0ad2fcfff8d25bb0fe8c4e8 Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Thu, 26 Mar 2020 02:07:12 +0100 Subject: [PATCH] Implement DMA pull method for sound output. --- devices/awacs.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++- devices/awacs.h | 4 ++++ devices/dbdma.cpp | 55 +++++++++++++++++++++++++++++++++++++------- devices/dbdma.h | 5 ++++ devices/heathrow.cpp | 1 + 5 files changed, 110 insertions(+), 9 deletions(-) diff --git a/devices/awacs.cpp b/devices/awacs.cpp index 574b73e..081132e 100644 --- a/devices/awacs.cpp +++ b/devices/awacs.cpp @@ -27,6 +27,7 @@ along with this program. If not, see . #include #include "endianswap.h" #include "awacs.h" +#include "dbdma.h" #include "machines/machinebase.h" #include @@ -52,6 +53,11 @@ AWACDevice::~AWACDevice() SDL_CloseAudioDevice(snd_out_dev); } +void AWACDevice::set_dma_out(DMAChannel *dma_out_ch) +{ + this->dma_out_ch = dma_out_ch; +} + uint32_t AWACDevice::snd_ctrl_read(uint32_t offset, int size) { switch(offset) { @@ -93,6 +99,48 @@ void AWACDevice::snd_ctrl_write(uint32_t offset, uint32_t value, int size) } } +static void convert_data(const uint8_t *in, uint8_t *out, uint32_t len) +{ + uint16_t *p_in, *p_out; + + if (len & 7) { + LOG_F(WARNING, "AWAC sound buffer len not a multiply of 8, %d", len); + } + + p_in = (uint16_t *)in; + p_out = (uint16_t *)out; + len >>= 1; + + LOG_F(INFO, "Converting %d samples", len); + + /* AWAC data comes as LLRR -> convert it to LRLR */ + for (int i = 0; i < len; i += 8) { + p_out[i] = p_in[i]; + p_out[i+1] = p_in[i+2]; + p_out[i+2] = p_in[i+1]; + p_out[i+3] = p_in[i+3]; + } +} + +static void audio_out_callback(void *user_data, uint8_t *buf, int buf_len) +{ + uint8_t *p_in; + uint32_t rem_len, got_len; + + DMAChannel *dma_ch = (DMAChannel *)user_data; /* C API baby! */ + + for (rem_len = buf_len; rem_len > 0; rem_len -= got_len, buf += got_len) { + if (!dma_ch->get_data(rem_len, &got_len, &p_in)) { + convert_data(p_in, buf, got_len); + LOG_F(9, "Converted sound data, len = %d", got_len); + } else { /* no more data */ + memset(buf, 0, rem_len); /* fill the buffer with silence */ + LOG_F(9, "Inserted silence, len = %d", rem_len); + break; + } + } +} + uint32_t AWACDevice::convert_data(const uint8_t *data, int len) { int i; @@ -127,7 +175,9 @@ void AWACDevice::dma_start() snd_spec.format = AUDIO_S16MSB; /* yes, AWAC accepts big-endian data */ snd_spec.channels = 2; snd_spec.samples = 4096; /* buffer size, chosen empirically */ - snd_spec.callback = NULL; + snd_spec.callback = audio_out_callback; + snd_spec.userdata = (void *)this->dma_out_ch; + this->snd_out_dev = SDL_OpenAudioDevice(NULL, 0, &snd_spec, &snd_settings, 0); if (!this->snd_out_dev) { @@ -136,6 +186,8 @@ void AWACDevice::dma_start() LOG_F(INFO, "Created audio output channel, sample rate = %d", snd_spec.freq); this->wake_up = true; } + + SDL_PauseAudioDevice(this->snd_out_dev, 0); /* start audio playing */ } void AWACDevice::dma_end() diff --git a/devices/awacs.h b/devices/awacs.h index 417bd4d..9c0e5bd 100644 --- a/devices/awacs.h +++ b/devices/awacs.h @@ -105,6 +105,8 @@ public: AWACDevice(); ~AWACDevice(); + void set_dma_out(DMAChannel *dma_out_ch); + uint32_t snd_ctrl_read(uint32_t offset, int size); void snd_ctrl_write(uint32_t offset, uint32_t value, int size); @@ -126,6 +128,8 @@ private: SDL_AudioDeviceID snd_out_dev = 0; bool wake_up = false; + DMAChannel *dma_out_ch; + uint8_t* snd_buf = 0; uint32_t buf_len = 0; }; diff --git a/devices/dbdma.cpp b/devices/dbdma.cpp index c0a5128..934e0cf 100644 --- a/devices/dbdma.cpp +++ b/devices/dbdma.cpp @@ -52,9 +52,11 @@ uint8_t DMAChannel::interpret_cmd() LOG_F(ERROR, "non-zero i/b/w not implemented"); break; } - this->dma_cb->dma_push( - mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count), - cmd_struct.req_count); + //this->dma_cb->dma_push( + // mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count), + // cmd_struct.req_count); + this->queue_data = mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count); + this->queue_len = cmd_struct.req_count; this->cmd_ptr += 16; break; case 1: @@ -67,9 +69,11 @@ uint8_t DMAChannel::interpret_cmd() LOG_F(ERROR, "non-zero i/b/w not implemented"); break; } - this->dma_cb->dma_push( - mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count), - cmd_struct.req_count); + //this->dma_cb->dma_push( + // mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count), + // cmd_struct.req_count); + this->queue_data = mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count); + this->queue_len = cmd_struct.req_count; this->cmd_ptr += 16; break; case 2: @@ -183,6 +187,39 @@ void DMAChannel::reg_write(uint32_t offset, uint32_t value, int size) } } +int DMAChannel::get_data(uint32_t req_len, uint32_t *avail_len, uint8_t **p_data) +{ + if (this->ch_stat & CH_STAT_DEAD || !(this->ch_stat & CH_STAT_ACTIVE)) { + LOG_F(WARNING, "Dead/idle channel -> no more data"); + *avail_len = 0; + return -1; /* dead or idle channel? -> no more data */ + } + + /* interpret DBDMA program until we get data or become idle */ + while ((this->ch_stat & CH_STAT_ACTIVE) && !this->queue_len) { + this->interpret_cmd(); + } + + /* dequeue data if any */ + if (this->queue_len) { + if (this->queue_len >= req_len) { + LOG_F(9, "Return req_len = %d data", req_len); + *p_data = this->queue_data; + *avail_len = req_len; + this->queue_len -= req_len; + this->queue_data += req_len; + } else { /* return less data than req_len */ + LOG_F(9, "Return queue_len = %d data", this->queue_len); + *p_data = this->queue_data; + *avail_len = this->queue_len; + this->queue_len = 0; + } + return 0; /* tell the caller there is more data */ + } + + return -1; /* tell the caller there is no more data */ +} + void DMAChannel::start() { if (this->ch_stat & CH_STAT_PAUSE) { @@ -192,10 +229,12 @@ void DMAChannel::start() LOG_F(INFO, "Starting DMA channel, stat = 0x%X", this->ch_stat); + this->queue_len = 0; + this->dma_cb->dma_start(); - while (this->interpret_cmd() != 7) { - } + //while (this->interpret_cmd() != 7) { + //} } void DMAChannel::resume() diff --git a/devices/dbdma.h b/devices/dbdma.h index a10e1e2..249e4c4 100644 --- a/devices/dbdma.h +++ b/devices/dbdma.h @@ -75,6 +75,8 @@ public: uint32_t reg_read(uint32_t offset, int size); void reg_write(uint32_t offset, uint32_t value, int size); + int get_data(uint32_t req_len, uint32_t *avail_len, uint8_t **p_data); + protected: void get_next_cmd(uint32_t cmd_addr, DMACmd *p_cmd); uint8_t interpret_cmd(void); @@ -88,6 +90,9 @@ private: DMACallback *dma_cb = 0; uint16_t ch_stat = 0; uint32_t cmd_ptr = 0; + + uint32_t queue_len; + uint8_t* queue_data; }; #endif /* DB_DMA_H */ diff --git a/devices/heathrow.cpp b/devices/heathrow.cpp index a8c411f..059ea59 100644 --- a/devices/heathrow.cpp +++ b/devices/heathrow.cpp @@ -44,6 +44,7 @@ HeathrowIC::HeathrowIC() : PCIDevice("mac-io/heathrow") this->screamer = new AWACDevice(); this->snd_out_dma = new DMAChannel(this->screamer); + this->screamer->set_dma_out(this->snd_out_dma); } HeathrowIC::~HeathrowIC()