mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 22:55:40 +12:00
Add HLE DSP files
This commit is contained in:
parent
428401870b
commit
2e696deccf
5 changed files with 220 additions and 6 deletions
|
@ -197,7 +197,7 @@ set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selecto
|
||||||
src/core/applets/error_applet.cpp
|
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
|
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)
|
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/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/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/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(
|
cmrc_add_resource_library(
|
||||||
|
|
|
@ -37,7 +37,7 @@ namespace Audio {
|
||||||
MAKE_LOG_FUNCTION(log, dspLogger)
|
MAKE_LOG_FUNCTION(log, dspLogger)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class Type { Null, Teakra };
|
enum class Type { Null, Teakra, HLE };
|
||||||
DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService)
|
DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService)
|
||||||
: mem(mem), scheduler(scheduler), dspService(dspService) {}
|
: mem(mem), scheduler(scheduler), dspService(dspService) {}
|
||||||
virtual ~DSPCore() {}
|
virtual ~DSPCore() {}
|
||||||
|
|
46
include/audio/hle_core.hpp
Normal file
46
include/audio/hle_core.hpp
Normal 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
|
|
@ -1,5 +1,6 @@
|
||||||
#include "audio/dsp_core.hpp"
|
#include "audio/dsp_core.hpp"
|
||||||
|
|
||||||
|
#include "audio/hle_core.hpp"
|
||||||
#include "audio/null_core.hpp"
|
#include "audio/null_core.hpp"
|
||||||
#include "audio/teakra_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) {
|
switch (type) {
|
||||||
case DSPCore::Type::Null: core = std::make_unique<NullDSP>(mem, scheduler, dspService); break;
|
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::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:
|
default:
|
||||||
Helpers::warn("Invalid DSP core selected!");
|
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); });
|
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 = {
|
static const std::unordered_map<std::string, Audio::DSPCore::Type> map = {
|
||||||
{"null", Audio::DSPCore::Type::Null},
|
{"null", Audio::DSPCore::Type::Null}, {"none", Audio::DSPCore::Type::Null}, {"lle", Audio::DSPCore::Type::Teakra},
|
||||||
{"none", Audio::DSPCore::Type::Null},
|
{"teakra", Audio::DSPCore::Type::Teakra}, {"hle", Audio::DSPCore::Type::HLE}, {"fast", Audio::DSPCore::Type::HLE},
|
||||||
{"lle", Audio::DSPCore::Type::Teakra},
|
|
||||||
{"teakra", Audio::DSPCore::Type::Teakra},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (auto search = map.find(inString); search != map.end()) {
|
if (auto search = map.find(inString); search != map.end()) {
|
||||||
|
@ -47,6 +47,7 @@ const char* Audio::DSPCore::typeToString(Audio::DSPCore::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Audio::DSPCore::Type::Null: return "null";
|
case Audio::DSPCore::Type::Null: return "null";
|
||||||
case Audio::DSPCore::Type::Teakra: return "teakra";
|
case Audio::DSPCore::Type::Teakra: return "teakra";
|
||||||
|
case Audio::DSPCore::Type::HLE: return "hle";
|
||||||
default: return "invalid";
|
default: return "invalid";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
166
src/core/audio/hle_core.cpp
Normal file
166
src/core/audio/hle_core.cpp
Normal 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
|
Loading…
Add table
Reference in a new issue