mirror of
https://github.com/dingusdev/dingusppc.git
synced 2025-08-15 09:27:27 +00:00
Implement DMA pull method for sound output.
This commit is contained in:
@@ -27,6 +27,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
#include <thirdparty/loguru/loguru.hpp>
|
#include <thirdparty/loguru/loguru.hpp>
|
||||||
#include "endianswap.h"
|
#include "endianswap.h"
|
||||||
#include "awacs.h"
|
#include "awacs.h"
|
||||||
|
#include "dbdma.h"
|
||||||
#include "machines/machinebase.h"
|
#include "machines/machinebase.h"
|
||||||
#include <thirdparty/SDL2/include/SDL.h>
|
#include <thirdparty/SDL2/include/SDL.h>
|
||||||
|
|
||||||
@@ -52,6 +53,11 @@ AWACDevice::~AWACDevice()
|
|||||||
SDL_CloseAudioDevice(snd_out_dev);
|
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)
|
uint32_t AWACDevice::snd_ctrl_read(uint32_t offset, int size)
|
||||||
{
|
{
|
||||||
switch(offset) {
|
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)
|
uint32_t AWACDevice::convert_data(const uint8_t *data, int len)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -127,7 +175,9 @@ void AWACDevice::dma_start()
|
|||||||
snd_spec.format = AUDIO_S16MSB; /* yes, AWAC accepts big-endian data */
|
snd_spec.format = AUDIO_S16MSB; /* yes, AWAC accepts big-endian data */
|
||||||
snd_spec.channels = 2;
|
snd_spec.channels = 2;
|
||||||
snd_spec.samples = 4096; /* buffer size, chosen empirically */
|
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);
|
this->snd_out_dev = SDL_OpenAudioDevice(NULL, 0, &snd_spec, &snd_settings, 0);
|
||||||
if (!this->snd_out_dev) {
|
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);
|
LOG_F(INFO, "Created audio output channel, sample rate = %d", snd_spec.freq);
|
||||||
this->wake_up = true;
|
this->wake_up = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_PauseAudioDevice(this->snd_out_dev, 0); /* start audio playing */
|
||||||
}
|
}
|
||||||
|
|
||||||
void AWACDevice::dma_end()
|
void AWACDevice::dma_end()
|
||||||
|
@@ -105,6 +105,8 @@ public:
|
|||||||
AWACDevice();
|
AWACDevice();
|
||||||
~AWACDevice();
|
~AWACDevice();
|
||||||
|
|
||||||
|
void set_dma_out(DMAChannel *dma_out_ch);
|
||||||
|
|
||||||
uint32_t snd_ctrl_read(uint32_t offset, int size);
|
uint32_t snd_ctrl_read(uint32_t offset, int size);
|
||||||
void snd_ctrl_write(uint32_t offset, uint32_t value, 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;
|
SDL_AudioDeviceID snd_out_dev = 0;
|
||||||
bool wake_up = false;
|
bool wake_up = false;
|
||||||
|
|
||||||
|
DMAChannel *dma_out_ch;
|
||||||
|
|
||||||
uint8_t* snd_buf = 0;
|
uint8_t* snd_buf = 0;
|
||||||
uint32_t buf_len = 0;
|
uint32_t buf_len = 0;
|
||||||
};
|
};
|
||||||
|
@@ -52,9 +52,11 @@ uint8_t DMAChannel::interpret_cmd()
|
|||||||
LOG_F(ERROR, "non-zero i/b/w not implemented");
|
LOG_F(ERROR, "non-zero i/b/w not implemented");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this->dma_cb->dma_push(
|
//this->dma_cb->dma_push(
|
||||||
mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count),
|
// mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count),
|
||||||
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;
|
this->cmd_ptr += 16;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
@@ -67,9 +69,11 @@ uint8_t DMAChannel::interpret_cmd()
|
|||||||
LOG_F(ERROR, "non-zero i/b/w not implemented");
|
LOG_F(ERROR, "non-zero i/b/w not implemented");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this->dma_cb->dma_push(
|
//this->dma_cb->dma_push(
|
||||||
mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count),
|
// mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count),
|
||||||
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;
|
this->cmd_ptr += 16;
|
||||||
break;
|
break;
|
||||||
case 2:
|
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()
|
void DMAChannel::start()
|
||||||
{
|
{
|
||||||
if (this->ch_stat & CH_STAT_PAUSE) {
|
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);
|
LOG_F(INFO, "Starting DMA channel, stat = 0x%X", this->ch_stat);
|
||||||
|
|
||||||
|
this->queue_len = 0;
|
||||||
|
|
||||||
this->dma_cb->dma_start();
|
this->dma_cb->dma_start();
|
||||||
|
|
||||||
while (this->interpret_cmd() != 7) {
|
//while (this->interpret_cmd() != 7) {
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMAChannel::resume()
|
void DMAChannel::resume()
|
||||||
|
@@ -75,6 +75,8 @@ public:
|
|||||||
uint32_t reg_read(uint32_t offset, int size);
|
uint32_t reg_read(uint32_t offset, int size);
|
||||||
void reg_write(uint32_t offset, uint32_t value, 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:
|
protected:
|
||||||
void get_next_cmd(uint32_t cmd_addr, DMACmd *p_cmd);
|
void get_next_cmd(uint32_t cmd_addr, DMACmd *p_cmd);
|
||||||
uint8_t interpret_cmd(void);
|
uint8_t interpret_cmd(void);
|
||||||
@@ -88,6 +90,9 @@ private:
|
|||||||
DMACallback *dma_cb = 0;
|
DMACallback *dma_cb = 0;
|
||||||
uint16_t ch_stat = 0;
|
uint16_t ch_stat = 0;
|
||||||
uint32_t cmd_ptr = 0;
|
uint32_t cmd_ptr = 0;
|
||||||
|
|
||||||
|
uint32_t queue_len;
|
||||||
|
uint8_t* queue_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DB_DMA_H */
|
#endif /* DB_DMA_H */
|
||||||
|
@@ -44,6 +44,7 @@ HeathrowIC::HeathrowIC() : PCIDevice("mac-io/heathrow")
|
|||||||
|
|
||||||
this->screamer = new AWACDevice();
|
this->screamer = new AWACDevice();
|
||||||
this->snd_out_dma = new DMAChannel(this->screamer);
|
this->snd_out_dma = new DMAChannel(this->screamer);
|
||||||
|
this->screamer->set_dma_out(this->snd_out_dma);
|
||||||
}
|
}
|
||||||
|
|
||||||
HeathrowIC::~HeathrowIC()
|
HeathrowIC::~HeathrowIC()
|
||||||
|
Reference in New Issue
Block a user