Implement DMA push method for sound.

This commit is contained in:
Maxim Poliakovski 2020-03-19 15:09:24 +01:00
parent 8e34c1657c
commit 945e63bdb2
7 changed files with 138 additions and 9 deletions

View File

@ -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_OBJECTS:cpu_ppc>
$<TARGET_OBJECTS:machines>
$<TARGET_OBJECTS:loguru>)
target_link_libraries(dingusppc ${SDL2_LIBRARIES})
add_executable(testppc ${TEST_SOURCES} $<TARGET_OBJECTS:cpu_ppc>
$<TARGET_OBJECTS:devices>
$<TARGET_OBJECTS:machines>
$<TARGET_OBJECTS:loguru>)
target_link_libraries(testppc ${SDL2_LIBRARIES})
add_custom_command(
TARGET testppc POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy

View File

@ -25,8 +25,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <thirdparty/loguru.hpp>
#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)
{
}

View File

@ -30,6 +30,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cinttypes>
#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;
};

View File

@ -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();
}

View File

@ -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;
};

View File

@ -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()

View File

@ -30,6 +30,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#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();