From 945e63bdb2349ba18c575ea1912027e89a16aa89 Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Thu, 19 Mar 2020 15:09:24 +0100 Subject: [PATCH] Implement DMA push method for sound. --- CMakeLists.txt | 10 +++++- devices/awacs.cpp | 86 +++++++++++++++++++++++++++++++++++++++++++- devices/awacs.h | 19 +++++++++- devices/dbdma.cpp | 13 ++++--- devices/dbdma.h | 11 +++++- devices/heathrow.cpp | 2 +- main.cpp | 6 ++++ 7 files changed, 138 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ac96cb4..2a64578 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,9 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) +find_package(SDL2 REQUIRED) +include_directories(${SDL2_INCLUDE_DIRS}) + add_subdirectory("${PROJECT_SOURCE_DIR}/cpu/ppc/") add_subdirectory("${PROJECT_SOURCE_DIR}/devices/") add_subdirectory("${PROJECT_SOURCE_DIR}/machines/") @@ -15,7 +18,8 @@ add_subdirectory("${PROJECT_SOURCE_DIR}/thirdparty/") include_directories("${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/devices" "${PROJECT_SOURCE_DIR}/cpu/ppc" "${PROJECT_SOURCE_DIR}/debugger" - "${PROJECT_SOURCE_DIR}/thirdparty") + "${PROJECT_SOURCE_DIR}/thirdparty" + ${SDL2_LIBRARIES}) file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/*.cpp" "${PROJECT_SOURCE_DIR}/debugger/*.cpp") @@ -27,11 +31,15 @@ add_executable(dingusppc ${SOURCES} $ $ $) +target_link_libraries(dingusppc ${SDL2_LIBRARIES}) + add_executable(testppc ${TEST_SOURCES} $ $ $ $) +target_link_libraries(testppc ${SDL2_LIBRARIES}) + add_custom_command( TARGET testppc POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy diff --git a/devices/awacs.cpp b/devices/awacs.cpp index 3272a27..5d5f74a 100644 --- a/devices/awacs.cpp +++ b/devices/awacs.cpp @@ -25,8 +25,12 @@ along with this program. If not, see . */ #include +#include "endianswap.h" #include "awacs.h" #include "machines/machinebase.h" +#include "SDL.h" + +static int awac_freqs[8] = {44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350}; AWACDevice::AWACDevice() { @@ -40,6 +44,12 @@ AWACDevice::AWACDevice() AWACDevice::~AWACDevice() { delete this->audio_proc; + + if (this->snd_buf) + delete[] this->snd_buf; + + if (this->snd_out_dev) + SDL_CloseAudioDevice(snd_out_dev); } uint32_t AWACDevice::snd_ctrl_read(uint32_t offset, int size) @@ -67,7 +77,7 @@ void AWACDevice::snd_ctrl_write(uint32_t offset, uint32_t value, int size) switch(offset) { case AWAC_SOUND_CTRL_REG: - this->snd_ctrl_reg = value; + this->snd_ctrl_reg = BYTESWAP_32(value); LOG_F(INFO, "New sound control value = 0x%X", this->snd_ctrl_reg); break; case AWAC_CODEC_CTRL_REG: @@ -82,3 +92,77 @@ void AWACDevice::snd_ctrl_write(uint32_t offset, uint32_t value, int size) LOG_F(ERROR, "AWAC: unsupported register at offset 0x%X", offset); } } + +uint32_t AWACDevice::convert_data(const uint8_t *data, int len) +{ + int i; + uint16_t *p_in, *p_out; + + if (len > this->buf_len) { + if (this->snd_buf) + delete this->snd_buf; + this->snd_buf = new uint8_t[len]; + this->buf_len = len; + } + + p_in = (uint16_t *)data; + p_out = (uint16_t *)this->snd_buf; + + for (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]; + } + + return i; +} + +void AWACDevice::dma_start() +{ + SDL_AudioSpec snd_spec, snd_settings; + + SDL_zero(snd_spec); + snd_spec.freq = awac_freqs[(this->snd_ctrl_reg >> 8) & 7]; + 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; + + this->snd_out_dev = SDL_OpenAudioDevice(NULL, 0, &snd_spec, &snd_settings, 0); + if (!this->snd_out_dev) { + LOG_F(ERROR, "Could not open sound output device, error %s", SDL_GetError()); + } else { + LOG_F(INFO, "Created audio output channel, sample rate = %d", snd_spec.freq); + this->wake_up = true; + } +} + +void AWACDevice::dma_end() +{ + if (this->snd_out_dev) { + SDL_CloseAudioDevice(this->snd_out_dev); + this->snd_out_dev = 0; + } +} + +void AWACDevice::dma_push(uint8_t *buf, int size) +{ + uint32_t dst_len; + + dst_len = this->convert_data(buf, size); + if (dst_len) { + if (SDL_QueueAudio(this->snd_out_dev, this->snd_buf, dst_len)) { + LOG_F(ERROR, "SDL_QueueAudio error: %s", SDL_GetError()); + } + } + + if (this->wake_up) { + SDL_PauseAudioDevice(this->snd_out_dev, 0); /* start audio playing */ + this->wake_up = false; + } +} + +void AWACDevice::dma_pull(uint8_t *buf, int size) +{ +} diff --git a/devices/awacs.h b/devices/awacs.h index 6e2986a..29f7b74 100644 --- a/devices/awacs.h +++ b/devices/awacs.h @@ -30,6 +30,8 @@ along with this program. If not, see . #include #include "i2c.h" +#include "dbdma.h" +#include "SDL.h" /** AWAC registers offsets. */ enum { @@ -98,7 +100,7 @@ private: }; -class AWACDevice { +class AWACDevice : public DMACallback { public: AWACDevice(); ~AWACDevice(); @@ -106,11 +108,26 @@ public: uint32_t snd_ctrl_read(uint32_t offset, int size); void snd_ctrl_write(uint32_t offset, uint32_t value, int size); + /* DMACallback methods */ + void dma_start(); + void dma_end(); + void dma_push(uint8_t *buf, int size); + void dma_pull(uint8_t *buf, int size); + +protected: + uint32_t convert_data(const uint8_t *data, int len); + private: uint32_t snd_ctrl_reg = {0}; uint16_t control_regs[8] = {0}; /* control registers, each 12-bits wide */ uint8_t is_busy = 0; AudioProcessor *audio_proc; + + SDL_AudioDeviceID snd_out_dev = 0; + bool wake_up = false; + + uint8_t* snd_buf = 0; + uint32_t buf_len = 0; }; diff --git a/devices/dbdma.cpp b/devices/dbdma.cpp index abd7807..58b28ad 100644 --- a/devices/dbdma.cpp +++ b/devices/dbdma.cpp @@ -52,8 +52,9 @@ uint8_t DMAChannel::interpret_cmd() LOG_F(ERROR, "non-zero i/b/w not implemented"); break; } - LOG_F(INFO, "Transfer data, addr = 0x%X, length = 0x%X", - cmd_struct.address, 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->cmd_ptr += 16; break; case 1: @@ -66,8 +67,9 @@ uint8_t DMAChannel::interpret_cmd() LOG_F(ERROR, "non-zero i/b/w not implemented"); break; } - LOG_F(INFO, "Transfer data, addr = 0x%X, length = 0x%X", - cmd_struct.address, 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->cmd_ptr += 16; break; case 2: @@ -190,6 +192,8 @@ void DMAChannel::start() LOG_F(INFO, "Starting DMA channel, stat = 0x%X", this->ch_stat); + this->dma_cb->dma_start(); + while (this->interpret_cmd() != 7) { } } @@ -212,4 +216,5 @@ void DMAChannel::abort() void DMAChannel::pause() { LOG_F(INFO, "Pausing DMA channel"); + this->dma_cb->dma_end(); } diff --git a/devices/dbdma.h b/devices/dbdma.h index cc5f1f9..a10e1e2 100644 --- a/devices/dbdma.h +++ b/devices/dbdma.h @@ -59,9 +59,17 @@ typedef struct DMACmd { uint16_t xfer_stat; } DMACmd; +class DMACallback { +public: + virtual void dma_start(void) = 0; + virtual void dma_end(void) = 0; + virtual void dma_push(uint8_t *buf, int size) = 0; + virtual void dma_pull(uint8_t *buf, int size) = 0; +}; + class DMAChannel { public: - DMAChannel() = default; + DMAChannel(DMACallback *cb) { this->dma_cb = cb; }; ~DMAChannel() = default; uint32_t reg_read(uint32_t offset, int size); @@ -77,6 +85,7 @@ protected: void pause(void); private: + DMACallback *dma_cb = 0; uint16_t ch_stat = 0; uint32_t cmd_ptr = 0; }; diff --git a/devices/heathrow.cpp b/devices/heathrow.cpp index 4c1a14a..bbdc34c 100644 --- a/devices/heathrow.cpp +++ b/devices/heathrow.cpp @@ -43,7 +43,7 @@ HeathrowIC::HeathrowIC() : PCIDevice("mac-io/heathrow") gMachineObj->add_subdevice("ViaCuda", this->viacuda); this->screamer = new AWACDevice(); - this->snd_out_dma = new DMAChannel(); + this->snd_out_dma = new DMAChannel(this->screamer); } HeathrowIC::~HeathrowIC() diff --git a/main.cpp b/main.cpp index 6bb1a07..3b2417c 100644 --- a/main.cpp +++ b/main.cpp @@ -30,6 +30,7 @@ along with this program. If not, see . #include "ppcemu.h" #include "debugger/debugger.h" #include "machines/machinefactory.h" +#include "SDL.h" using namespace std; @@ -71,6 +72,11 @@ int main(int argc, char **argv) goto bail; } + if (SDL_Init(SDL_INIT_AUDIO)){ + LOG_F(ERROR, "SDL_Init error: %s", SDL_GetError()); + goto bail; + } + if ((checker == "1") || (checker == "realtime") || \ (checker == "-realtime") || (checker == "/realtime")) { ppc_exec();