Add HLE DSP files

This commit is contained in:
wheremyfoodat 2024-04-05 00:44:31 +03:00
parent 428401870b
commit 2e696deccf
5 changed files with 220 additions and 6 deletions

View file

@ -197,7 +197,7 @@ set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selecto
src/core/applets/error_applet.cpp
)
set(AUDIO_SOURCE_FILES src/core/audio/dsp_core.cpp src/core/audio/null_core.cpp src/core/audio/teakra_core.cpp
src/core/audio/miniaudio_device.cpp
src/core/audio/miniaudio_device.cpp src/core/audio/hle_core.cpp
)
set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp)
@ -234,6 +234,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
include/PICA/dynapica/shader_rec_emitter_arm64.hpp include/scheduler.hpp include/applets/error_applet.hpp
include/audio/dsp_core.hpp include/audio/null_core.hpp include/audio/teakra_core.hpp
include/audio/miniaudio_device.hpp include/ring_buffer.hpp include/bitfield.hpp include/audio/dsp_shared_mem.hpp
include/audio/hle_core.hpp
)
cmrc_add_resource_library(

View file

@ -37,7 +37,7 @@ namespace Audio {
MAKE_LOG_FUNCTION(log, dspLogger)
public:
enum class Type { Null, Teakra };
enum class Type { Null, Teakra, HLE };
DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService)
: mem(mem), scheduler(scheduler), dspService(dspService) {}
virtual ~DSPCore() {}

View file

@ -0,0 +1,46 @@
#pragma once
#include <array>
#include "audio/dsp_core.hpp"
#include "audio/dsp_shared_mem.hpp"
#include "memory.hpp"
namespace Audio {
class HLE_DSP : public DSPCore {
enum class DSPState : u32 {
Off,
On,
Slep,
};
// Number of DSP pipes
static constexpr size_t pipeCount = 8;
DSPState dspState;
std::array<std::vector<u8>, pipeCount> pipeData; // The data of each pipe
std::array<u8, Memory::DSP_RAM_SIZE> dspRam;
void resetAudioPipe();
bool loaded = false; // Have we loaded a component?
public:
HLE_DSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) {}
~HLE_DSP() override {}
void reset() override;
void runAudioFrame() override;
u8* getDspMemory() override { return dspRam.data(); }
u16 recvData(u32 regId) override;
bool recvDataIsReady(u32 regId) override { return true; } // Treat data as always ready
void writeProcessPipe(u32 channel, u32 size, u32 buffer) override;
std::vector<u8> readPipe(u32 channel, u32 peer, u32 size, u32 buffer) override;
void loadComponent(std::vector<u8>& data, u32 programMask, u32 dataMask) override;
void unloadComponent() override;
void setSemaphore(u16 value) override {}
void setSemaphoreMask(u16 value) override {}
};
} // namespace Audio

View file

@ -1,5 +1,6 @@
#include "audio/dsp_core.hpp"
#include "audio/hle_core.hpp"
#include "audio/null_core.hpp"
#include "audio/teakra_core.hpp"
@ -13,6 +14,7 @@ std::unique_ptr<Audio::DSPCore> Audio::makeDSPCore(DSPCore::Type type, Memory& m
switch (type) {
case DSPCore::Type::Null: core = std::make_unique<NullDSP>(mem, scheduler, dspService); break;
case DSPCore::Type::Teakra: core = std::make_unique<TeakraDSP>(mem, scheduler, dspService); break;
case DSPCore::Type::HLE: core = std::make_unique<HLE_DSP>(mem, scheduler, dspService); break;
default:
Helpers::warn("Invalid DSP core selected!");
@ -29,10 +31,8 @@ Audio::DSPCore::Type Audio::DSPCore::typeFromString(std::string inString) {
std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); });
static const std::unordered_map<std::string, Audio::DSPCore::Type> map = {
{"null", Audio::DSPCore::Type::Null},
{"none", Audio::DSPCore::Type::Null},
{"lle", Audio::DSPCore::Type::Teakra},
{"teakra", Audio::DSPCore::Type::Teakra},
{"null", Audio::DSPCore::Type::Null}, {"none", Audio::DSPCore::Type::Null}, {"lle", Audio::DSPCore::Type::Teakra},
{"teakra", Audio::DSPCore::Type::Teakra}, {"hle", Audio::DSPCore::Type::HLE}, {"fast", Audio::DSPCore::Type::HLE},
};
if (auto search = map.find(inString); search != map.end()) {
@ -47,6 +47,7 @@ const char* Audio::DSPCore::typeToString(Audio::DSPCore::Type type) {
switch (type) {
case Audio::DSPCore::Type::Null: return "null";
case Audio::DSPCore::Type::Teakra: return "teakra";
case Audio::DSPCore::Type::HLE: return "hle";
default: return "invalid";
}
}

166
src/core/audio/hle_core.cpp Normal file
View file

@ -0,0 +1,166 @@
#include "audio/hle_core.hpp"
#include "services/dsp.hpp"
namespace Audio {
namespace DSPPipeType {
enum : u32 {
Debug = 0,
DMA = 1,
Audio = 2,
Binary = 3,
};
}
void HLE_DSP::resetAudioPipe() {
// Hardcoded responses for now
// These are DSP DRAM offsets for various variables
// https://www.3dbrew.org/wiki/DSP_Memory_Region
static constexpr std::array<u16, 16> responses = {
0x000F, // Number of responses
0xBFFF, // Frame counter
0x9E92, // Source configs
0x8680, // Source statuses
0xA792, // ADPCM coefficients
0x9430, // DSP configs
0x8400, // DSP status
0x8540, // Final samples
0x9492, // Intermediate mix samples
0x8710, // Compressor
0x8410, // Debug
0xA912, // ??
0xAA12, // ??
0xAAD2, // ??
0xAC52, // Surround sound biquad filter 1
0xAC5C // Surround sound biquad filter 2
};
std::vector<u8>& audioPipe = pipeData[DSPPipeType::Audio];
audioPipe.resize(responses.size() * sizeof(u16));
// Push back every response to the audio pipe
size_t index = 0;
for (auto e : responses) {
audioPipe[index++] = e & 0xff;
audioPipe[index++] = e >> 8;
}
}
void HLE_DSP::reset() {
loaded = false;
for (auto& e : pipeData) {
e.clear();
}
// Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted
resetAudioPipe();
}
void HLE_DSP::loadComponent(std::vector<u8>& data, u32 programMask, u32 dataMask) {
if (loaded) {
Helpers::warn("Loading DSP component when already loaded");
}
loaded = true;
scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::cyclesPerFrame);
}
void HLE_DSP::unloadComponent() {
if (!loaded) {
Helpers::warn("Audio: unloadComponent called without a running program");
}
loaded = false;
scheduler.removeEvent(Scheduler::EventType::RunDSP);
}
void HLE_DSP::runAudioFrame() {
// Signal audio pipe when an audio frame is done
if (dspState == DSPState::On) [[likely]] {
dspService.triggerPipeEvent(DSPPipeType::Audio);
}
scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::cyclesPerFrame);
}
u16 HLE_DSP::recvData(u32 regId) {
if (regId != 0) {
Helpers::panic("Audio: invalid register in null frontend");
}
return dspState == DSPState::On;
}
void HLE_DSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) {
enum class StateChange : u8 {
Initialize = 0,
Shutdown = 1,
Wakeup = 2,
Sleep = 3,
};
switch (channel) {
case DSPPipeType::Audio: {
if (size != 4) {
printf("Invalid size written to DSP Audio Pipe\n");
break;
}
// Get new state
const u8 state = mem.read8(buffer);
if (state > 3) {
log("WriteProcessPipe::Audio: Unknown state change type");
} else {
switch (static_cast<StateChange>(state)) {
case StateChange::Initialize:
// TODO: Other initialization stuff here
dspState = DSPState::On;
resetAudioPipe();
dspService.triggerPipeEvent(DSPPipeType::Audio);
break;
case StateChange::Shutdown:
dspState = DSPState::Off;
break;
default: Helpers::panic("Unimplemented DSP audio pipe state change %d", state);
}
}
break;
}
case DSPPipeType::Binary:
Helpers::warn("Unimplemented write to binary pipe! Size: %d\n", size);
// This pipe and interrupt are normally used for requests like AAC decode
dspService.triggerPipeEvent(DSPPipeType::Binary);
break;
default: log("Audio::HLE_DSP: Wrote to unimplemented pipe %d\n", channel); break;
}
}
std::vector<u8> HLE_DSP::readPipe(u32 pipe, u32 peer, u32 size, u32 buffer) {
if (size & 1) Helpers::panic("Tried to read odd amount of bytes from DSP pipe");
if (pipe >= pipeCount || size > 0xffff) {
return {};
}
if (pipe != DSPPipeType::Audio) {
log("Reading from non-audio pipe! This might be broken, might need to check what pipe is being read from and implement writing to it\n");
}
std::vector<u8>& data = pipeData[pipe];
size = std::min<u32>(size, data.size()); // Clamp size to the maximum available data size
if (size == 0) {
return {};
}
// Return "size" bytes from the audio pipe and erase them
std::vector<u8> out(data.begin(), data.begin() + size);
data.erase(data.begin(), data.begin() + size);
return out;
}
} // namespace Audio