tests: Port merry's audio tests (#7354)

This commit is contained in:
SachinVin 2024-01-21 09:46:00 +05:30 committed by GitHub
parent 789654d7da
commit 228f26d1e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 705 additions and 1 deletions

View File

@ -200,7 +200,7 @@ void DSP_DSP::UnloadComponent(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultSuccess);
LOG_INFO(Service_DSP, "(STUBBED)");
LOG_INFO(Service_DSP, "called");
}
void DSP_DSP::FlushDataCache(Kernel::HLERequestContext& ctx) {

View File

@ -13,6 +13,11 @@ add_executable(tests
audio_core/audio_fixures.h
audio_core/decoder_tests.cpp
video_core/shader/shader_jit_compiler.cpp
audio_core/merryhime_3ds_audio/merry_audio/merry_audio.cpp
audio_core/merryhime_3ds_audio/merry_audio/merry_audio.h
audio_core/merryhime_3ds_audio/merry_audio/service_fixture.cpp
audio_core/merryhime_3ds_audio/merry_audio/service_fixture.h
audio_core/merryhime_3ds_audio/audio_test_biquad_filter.cpp
)
create_target_directory_groups(tests)

View File

@ -0,0 +1 @@
Port of HW tests from https://github.com/merryhime/3ds-audio

View File

@ -0,0 +1,152 @@
#include <cstdio>
#include <catch2/catch_template_test_macros.hpp>
#include "audio_core/hle/shared_memory.h"
#include "common/settings.h"
#include "merry_audio/merry_audio.h"
TEST_CASE_METHOD(MerryAudio::MerryAudioFixture, "AudioTest-BiquadFilter",
"[audio_core][merryhime_3ds_audio]") {
// High frequency square wave, PCM16
auto fillBuffer = [this](u32* audio_buffer, size_t size) {
for (size_t i = 0; i < size; i++) {
u32 data = (i % 2 == 0 ? 0x1000 : 0x2000);
audio_buffer[i] = (data << 16) | (data & 0xFFFF);
}
DSP_FlushDataCache(audio_buffer, size);
};
constexpr size_t NUM_SAMPLES = 160 * 200;
u32* audio_buffer = (u32*)linearAlloc(NUM_SAMPLES * sizeof(u32));
fillBuffer(audio_buffer, NUM_SAMPLES);
MerryAudio::AudioState state;
{
std::vector<u8> dspfirm;
SECTION("HLE") {
// The test case assumes HLE AudioCore doesn't require a valid firmware
InitDspCore(Settings::AudioEmulation::HLE);
dspfirm = {0};
}
SECTION("LLE") {
InitDspCore(Settings::AudioEmulation::LLE);
dspfirm = loadDspFirmFromFile();
}
if (!dspfirm.size()) {
SKIP("Couldn't load firmware\n");
return;
}
auto ret = audioInit(dspfirm);
if (!ret) {
INFO("Couldn't init audio\n");
goto end;
}
state = *ret;
}
{
/*
const s16 b0 = 0.057200221035302035 * (1 << 14);
const s16 b1 = 0.11440044207060407 * (1 << 14);
const s16 b2 = 0.0238274928983472 * (1 << 14);
const s16 a1 = -1.2188761083637 * (1 << 14);
const s16 a2 = 0.44767699250490806 * (1 << 14);
*/
srand((u32)time(nullptr));
const s16 b0 = rand();
const s16 b1 = rand();
const s16 b2 = rand();
const s16 a1 = rand();
const s16 a2 = rand();
std::array<s32, 160> expected_output;
{
s32 x1 = 0;
s32 x2 = 0;
s32 y1 = 0;
s32 y2 = 0;
for (int i = 0; i < 160; i++) {
const s32 x0 = (i % 4 == 0 || i % 4 == 1 ? 0x1000 : 0x2000);
s32 y0 = ((s32)x0 * (s32)b0 + (s32)x1 * b1 + (s32)x2 * b2 + (s32)a1 * y1 +
(s32)a2 * y2) >>
14;
y0 = std::clamp(y0, -32768, 32767);
expected_output[i] = y2;
x2 = x1;
x1 = x0;
y2 = y1;
y1 = y0;
}
}
state.waitForSync();
initSharedMem(state);
state.write().dsp_configuration->aux_bus_enable_0_dirty.Assign(true);
state.write().dsp_configuration->aux_bus_enable[0] = true;
state.write().source_configurations->config[0].gain[1][0] = 1.0;
state.write().source_configurations->config[0].gain_1_dirty.Assign(true);
state.notifyDsp();
state.waitForSync();
{
u16 buffer_id = 0;
state.write().source_configurations->config[0].play_position = 0;
state.write().source_configurations->config[0].physical_address =
osConvertVirtToPhys(audio_buffer);
state.write().source_configurations->config[0].length = NUM_SAMPLES;
state.write().source_configurations->config[0].mono_or_stereo.Assign(
AudioCore::HLE::SourceConfiguration::Configuration::MonoOrStereo::Mono);
state.write().source_configurations->config[0].format.Assign(
AudioCore::HLE::SourceConfiguration::Configuration::Format::PCM16);
state.write().source_configurations->config[0].fade_in.Assign(false);
state.write().source_configurations->config[0].adpcm_dirty.Assign(false);
state.write().source_configurations->config[0].is_looping.Assign(false);
state.write().source_configurations->config[0].buffer_id = ++buffer_id;
state.write().source_configurations->config[0].partial_reset_flag.Assign(true);
state.write().source_configurations->config[0].play_position_dirty.Assign(true);
state.write().source_configurations->config[0].embedded_buffer_dirty.Assign(true);
state.write().source_configurations->config[0].enable = true;
state.write().source_configurations->config[0].enable_dirty.Assign(true);
state.write().source_configurations->config[0].simple_filter.b0 = 0;
state.write().source_configurations->config[0].simple_filter.a1 = 0;
state.write().source_configurations->config[0].simple_filter_enabled.Assign(false);
state.write().source_configurations->config[0].biquad_filter_enabled.Assign(true);
state.write().source_configurations->config[0].biquad_filter.b0 = b0;
state.write().source_configurations->config[0].biquad_filter.b1 = b1;
state.write().source_configurations->config[0].biquad_filter.b2 = b2;
state.write().source_configurations->config[0].biquad_filter.a1 = a1;
state.write().source_configurations->config[0].biquad_filter.a2 = a2;
state.write().source_configurations->config[0].filters_enabled_dirty.Assign(true);
state.write().source_configurations->config[0].biquad_filter_dirty.Assign(true);
state.write().source_configurations->config[0].simple_filter_dirty.Assign(true);
state.notifyDsp();
bool continue_reading = true;
for (size_t frame_count = 0; continue_reading && frame_count < 10; frame_count++) {
state.waitForSync();
for (size_t i = 0; i < 160; i++) {
if (state.read().intermediate_mix_samples->mix1.pcm32[0][i]) {
for (size_t j = 0; j < 60; j++) {
REQUIRE(state.read().intermediate_mix_samples->mix1.pcm32[0][j] ==
expected_output[j]);
}
continue_reading = false;
break;
}
}
state.notifyDsp();
}
REQUIRE(continue_reading == false);
}
}
end:
audioExit(state);
}

View File

@ -0,0 +1,264 @@
#include <array>
#include <climits>
#include <cstdio>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include "audio_core/hle/shared_memory.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "merry_audio.h"
#define VERIFY(call) call
namespace MerryAudio {
std::vector<u8> MerryAudioFixture::loadDspFirmFromFile() {
std::string firm_filepath =
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "3ds" DIR_SEP "dspfirm.cdc";
FILE* f = fopen(firm_filepath.c_str(), "rb");
if (!f) {
printf("Couldn't find dspfirm\n");
return {};
}
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
std::vector<u8> dspfirm_binary(size);
[[maybe_unused]] std::size_t count = fread(dspfirm_binary.data(), dspfirm_binary.size(), 1, f);
fclose(f);
return dspfirm_binary;
}
std::optional<AudioState> MerryAudioFixture::audioInit(const std::vector<u8>& dspfirm) {
AudioState ret;
ret.service_fixture = this;
if (!dspfirm.size())
return std::nullopt;
if (R_FAILED(dspInit())) {
printf("dspInit() failed\n");
return std::nullopt;
}
VERIFY(DSP_UnloadComponent());
{
bool dspfirm_loaded = false;
VERIFY(DSP_LoadComponent(dspfirm.data(), dspfirm.size(), /*progmask=*/0xFF,
/*datamask=*/0xFF, &dspfirm_loaded));
if (!dspfirm_loaded) {
printf("Failed to load firmware\n");
return std::nullopt;
}
}
VERIFY(svcCreateEvent(&ret.pipe2_irq, 1));
// interrupt type == 2 (pipe related)
// pipe channel == 2 (audio pipe)
VERIFY(DSP_RegisterInterruptEvents(ret.pipe2_irq, 2, 2));
VERIFY(DSP_GetSemaphoreHandle(&ret.dsp_semaphore));
VERIFY(DSP_SetSemaphoreMask(0x2000));
{
// dsp_mode == 0 (request initialisation of DSP)
const u32 dsp_mode = 0;
VERIFY(DSP_WriteProcessPipe(2, &dsp_mode, 4));
}
// Inform the DSP that we have data for her.
VERIFY(DSP_SetSemaphore(0x4000));
// Wait for the DSP to tell us data is available.
VERIFY(svcWaitSynchronization(ret.pipe2_irq, UINT64_MAX));
VERIFY(svcClearEvent(ret.pipe2_irq));
{
u16 len_read = 0;
u16 num_structs = 0;
VERIFY(DSP_ReadPipeIfPossible(2, 0, &num_structs, 2, &len_read));
if (len_read != 2) {
printf("Reading struct addrs header: Could only read %i bytes!\n", len_read);
return std::nullopt;
}
if (num_structs != 15) {
printf("num_structs == %i (!= 15): Are you sure you have the right firmware version?\n",
num_structs);
return std::nullopt;
}
std::array<u16, 15> dsp_addrs;
VERIFY(DSP_ReadPipeIfPossible(2, 0, dsp_addrs.data(), 30, &len_read));
if (len_read != 30) {
printf("Reading struct addrs body: Could only read %i bytes!\n", len_read);
return std::nullopt;
}
for (int i = 0; i < 15; i++) {
const u32 addr0 = static_cast<u32>(dsp_addrs[i]);
const u32 addr1 = static_cast<u32>(dsp_addrs[i]) | 0x10000;
u16* vaddr0;
u16* vaddr1;
VERIFY(DSP_ConvertProcessAddressFromDspDram(addr0, &vaddr0));
VERIFY(DSP_ConvertProcessAddressFromDspDram(addr1, &vaddr1));
ret.dsp_structs[i][0] = reinterpret_cast<u16*>(vaddr0);
ret.dsp_structs[i][1] = reinterpret_cast<u16*>(vaddr1);
}
for (int i = 0; i < 2; i++) {
ret.shared_mem[i].frame_counter = reinterpret_cast<u16*>(ret.dsp_structs[0][i]);
ret.shared_mem[i].source_configurations =
reinterpret_cast<AudioCore::HLE::SourceConfiguration*>(ret.dsp_structs[1][i]);
ret.shared_mem[i].source_statuses =
reinterpret_cast<AudioCore::HLE::SourceStatus*>(ret.dsp_structs[2][i]);
ret.shared_mem[i].adpcm_coefficients =
reinterpret_cast<AudioCore::HLE::AdpcmCoefficients*>(ret.dsp_structs[3][i]);
ret.shared_mem[i].dsp_configuration =
reinterpret_cast<AudioCore::HLE::DspConfiguration*>(ret.dsp_structs[4][i]);
ret.shared_mem[i].dsp_status =
reinterpret_cast<AudioCore::HLE::DspStatus*>(ret.dsp_structs[5][i]);
ret.shared_mem[i].final_samples =
reinterpret_cast<AudioCore::HLE::FinalMixSamples*>(ret.dsp_structs[6][i]);
ret.shared_mem[i].intermediate_mix_samples =
reinterpret_cast<AudioCore::HLE::IntermediateMixSamples*>(ret.dsp_structs[7][i]);
ret.shared_mem[i].compressor =
reinterpret_cast<AudioCore::HLE::Compressor*>(ret.dsp_structs[8][i]);
ret.shared_mem[i].dsp_debug =
reinterpret_cast<AudioCore::HLE::DspDebug*>(ret.dsp_structs[9][i]);
}
}
// Poke the DSP again.
VERIFY(DSP_SetSemaphore(0x4000));
ret.dsp_structs[0][0][0] = ret.frame_id;
ret.frame_id++;
VERIFY(svcSignalEvent(ret.dsp_semaphore));
return {ret};
}
void MerryAudioFixture::audioExit(const AudioState& state) {
{
// dsp_mode == 1 (request shutdown of DSP)
const u32 dsp_mode = 1;
DSP_WriteProcessPipe(2, &dsp_mode, 4);
}
DSP_RegisterInterruptEvents(0, 2, 2);
svcCloseHandle(state.pipe2_irq);
svcCloseHandle(state.dsp_semaphore);
DSP_UnloadComponent();
dspExit();
}
void MerryAudioFixture::initSharedMem(AudioState& state) {
for (auto& config : state.write().source_configurations->config) {
{
config.enable = 0;
config.enable_dirty.Assign(true);
}
{
config.interpolation_mode =
AudioCore::HLE::SourceConfiguration::Configuration::InterpolationMode::None;
// config.interpolation_related = 0;
config.interpolation_dirty.Assign(true);
}
{
config.rate_multiplier = 1.0;
config.rate_multiplier_dirty.Assign(true);
}
{
config.simple_filter_enabled.Assign(false);
config.biquad_filter_enabled.Assign(false);
config.filters_enabled_dirty.Assign(true);
}
{
for (auto& gain : config.gain) {
for (auto& g : gain) {
g = 0.0;
}
}
config.gain[0][0] = 1.0;
config.gain[0][1] = 1.0;
config.gain_0_dirty.Assign(true);
config.gain_1_dirty.Assign(true);
config.gain_2_dirty.Assign(true);
}
{
config.sync_count = 1;
config.sync_count_dirty.Assign(true);
}
{ config.reset_flag.Assign(true); }
}
{
state.write().dsp_configuration->master_volume = 1.0;
state.write().dsp_configuration->aux_return_volume[0] = 0.0;
state.write().dsp_configuration->aux_return_volume[1] = 0.0;
state.write().dsp_configuration->master_volume_dirty.Assign(true);
state.write().dsp_configuration->aux_return_volume_0_dirty.Assign(true);
state.write().dsp_configuration->aux_return_volume_1_dirty.Assign(true);
}
{
state.write().dsp_configuration->output_format =
AudioCore::HLE::DspConfiguration::OutputFormat::Stereo;
state.write().dsp_configuration->output_format_dirty.Assign(true);
}
{
state.write().dsp_configuration->clipping_mode = 0;
state.write().dsp_configuration->clipping_mode_dirty.Assign(true);
}
{
// https://www.3dbrew.org/wiki/Configuration_Memory
state.write().dsp_configuration->headphones_connected = 0;
state.write().dsp_configuration->headphones_connected_dirty.Assign(true);
}
}
const SharedMem& AudioState::write() const {
return shared_mem[frame_id % 2 == 1 ? 1 : 0];
}
const SharedMem& AudioState::read() const {
return shared_mem[frame_id % 2 == 1 ? 1 : 0];
}
void AudioState::waitForSync() {
if (!service_fixture) {
return;
}
service_fixture->svcWaitSynchronization(pipe2_irq, UINT64_MAX);
service_fixture->svcClearEvent(pipe2_irq);
}
void AudioState::notifyDsp() {
write().frame_counter[0] = frame_id;
frame_id++;
if (service_fixture) {
service_fixture->svcSignalEvent(dsp_semaphore);
}
}
} // namespace MerryAudio

View File

@ -0,0 +1,65 @@
#include <array>
#include <optional>
#include <vector>
#include "service_fixture.h"
namespace AudioCore::HLE {
struct SourceConfiguration;
struct SourceStatus;
struct AdpcmCoefficients;
struct DspConfiguration;
struct DspStatus;
struct FinalMixSamples;
struct IntermediateMixSamples;
struct Compressor;
struct DspDebug;
} // namespace AudioCore::HLE
namespace Memory {
class MemorySystem;
}
namespace MerryAudio {
struct SharedMem {
u16* frame_counter;
AudioCore::HLE::SourceConfiguration* source_configurations; // access through write()
AudioCore::HLE::SourceStatus* source_statuses; // access through read()
AudioCore::HLE::AdpcmCoefficients* adpcm_coefficients; // access through write()
AudioCore::HLE::DspConfiguration* dsp_configuration; // access through write()
AudioCore::HLE::DspStatus* dsp_status; // access through read()
AudioCore::HLE::FinalMixSamples* final_samples; // access through read()
AudioCore::HLE::IntermediateMixSamples* intermediate_mix_samples; // access through write()
AudioCore::HLE::Compressor* compressor; // access through write()
AudioCore::HLE::DspDebug* dsp_debug; // access through read()
};
struct AudioState {
ServiceFixture::Handle pipe2_irq = ServiceFixture::PIPE2_IRQ_HANDLE;
ServiceFixture::Handle dsp_semaphore = ServiceFixture::DSP_SEMAPHORE_HANDLE;
std::array<std::array<u16*, 2>, 16> dsp_structs{};
std::array<SharedMem, 2> shared_mem{};
u16 frame_id = 4;
const SharedMem& read() const;
const SharedMem& write() const;
void waitForSync();
void notifyDsp();
ServiceFixture* service_fixture{};
};
class MerryAudioFixture : public ServiceFixture {
public:
std::vector<u8> loadDspFirmFromFile();
std::optional<AudioState> audioInit(const std::vector<u8>& dspfirm);
void audioExit(const AudioState& state);
void initSharedMem(AudioState& state);
};
} // namespace MerryAudio

View File

@ -0,0 +1,133 @@
#include "audio_core/hle/hle.h"
#include "audio_core/lle/lle.h"
#include "common/settings.h"
#include "service_fixture.h"
// SVC
Result ServiceFixture::svcWaitSynchronization(Handle handle, s64 nanoseconds) {
ASSERT(handle == PIPE2_IRQ_HANDLE);
using AudioCore::DspPipe::Audio;
using Service::DSP::InterruptType::Pipe;
const bool& audio_pipe_interrupted =
interrupts_fired[static_cast<u32>(Pipe)][static_cast<u32>(Audio)];
while (!audio_pipe_interrupted) {
core_timing.GetTimer(0)->AddTicks(core_timing.GetTimer(0)->GetDowncount());
core_timing.GetTimer(0)->Advance();
core_timing.GetTimer(0)->SetNextSlice();
}
return ResultSuccess;
}
Result ServiceFixture::svcClearEvent(Handle handle) {
ASSERT(handle == PIPE2_IRQ_HANDLE);
using AudioCore::DspPipe::Audio;
using Service::DSP::InterruptType::Pipe;
interrupts_fired[static_cast<u32>(Pipe)][static_cast<u32>(Audio)] = 0;
return ResultSuccess;
}
Result ServiceFixture::svcSignalEvent(Handle handle) {
ASSERT(handle == DSP_SEMAPHORE_HANDLE);
dsp->SetSemaphore(0x2000);
// TODO: Add relevent amount of ticks
return ResultSuccess;
}
// dsp::DSP
Result ServiceFixture::DSP_LoadComponent(const u8* dspfirm_data, size_t size, u8 progmask,
u8 datamask, bool* dspfirm_loaded) {
dsp->LoadComponent({dspfirm_data, size});
*dspfirm_loaded = true;
return ResultSuccess;
}
Result ServiceFixture::DSP_UnloadComponent() {
dsp->UnloadComponent();
return ResultSuccess;
}
Result ServiceFixture::DSP_GetSemaphoreHandle(Handle* handle) {
*handle = DSP_SEMAPHORE_HANDLE;
return ResultSuccess;
};
Result ServiceFixture::DSP_SetSemaphore(u32 semaphore) {
dsp->SetSemaphore(semaphore);
return ResultSuccess;
}
Result ServiceFixture::DSP_ReadPipeIfPossible(u32 channel, u32 /*peer*/, void* out_buffer, u32 size,
u16* out_size) {
const AudioCore::DspPipe pipe = static_cast<AudioCore::DspPipe>(channel);
const u16 pipe_readable_size = static_cast<u16>(dsp->GetPipeReadableSize(pipe));
std::vector<u8> pipe_buffer;
if (pipe_readable_size >= size) {
pipe_buffer = dsp->PipeRead(pipe, size);
}
*out_size = static_cast<u16>(pipe_buffer.size());
std::memcpy(out_buffer, pipe_buffer.data(), *out_size);
return ResultSuccess;
}
Result ServiceFixture::ServiceFixture::DSP_ConvertProcessAddressFromDspDram(u32 dsp_address,
u16** host_address) {
*host_address = reinterpret_cast<u16*>(
(dsp_address << 1) + (reinterpret_cast<uintptr_t>(dsp->GetDspMemory().data()) + 0x40000u));
return ResultSuccess;
}
Result ServiceFixture::DSP_WriteProcessPipe(u32 channel, const void* buffer, u32 length) {
const AudioCore::DspPipe pipe = static_cast<AudioCore::DspPipe>(channel);
dsp->PipeWrite(pipe, {reinterpret_cast<const u8*>(buffer), length});
return ResultSuccess;
};
// libctru
u32 ServiceFixture::osConvertVirtToPhys(void* vaddr) {
return static_cast<u32>(Memory::FCRAM_PADDR +
(reinterpret_cast<u8*>(vaddr) - memory.GetFCRAMPointer(0)));
}
void* ServiceFixture::linearAlloc(size_t size) {
void* ret = memory.GetFCRAMPointer(0) + linear_alloc_offset;
linear_alloc_offset += size;
return ret;
}
Result ServiceFixture::dspInit() {
if (!dsp) {
dsp = std::make_unique<AudioCore::DspHle>(system, memory, core_timing);
dsp->SetInterruptHandler([this](Service::DSP::InterruptType type, AudioCore::DspPipe pipe) {
interrupts_fired[static_cast<u32>(type)][static_cast<u32>(pipe)] = 1;
});
}
return ResultSuccess;
}
// Fixture
void ServiceFixture::InitDspCore(Settings::AudioEmulation dsp_core) {
if (dsp_core == Settings::AudioEmulation::HLE) {
dsp = std::make_unique<AudioCore::DspHle>(system, memory, core_timing);
} else {
dsp = std::make_unique<AudioCore::DspLle>(
system, memory, core_timing, dsp_core == Settings::AudioEmulation::LLEMultithreaded);
}
dsp->SetInterruptHandler([this](Service::DSP::InterruptType type, AudioCore::DspPipe pipe) {
interrupts_fired[static_cast<u32>(type)][static_cast<u32>(pipe)] = 1;
});
}

View File

@ -0,0 +1,84 @@
#include "audio_core/dsp_interface.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Settings {
enum class AudioEmulation : u32;
}
class ServiceFixture {
public:
typedef int Handle;
static constexpr int PIPE2_IRQ_HANDLE = 0x919E;
static constexpr int DSP_SEMAPHORE_HANDLE = 0xD59;
// SVC
Result svcCreateEvent(Handle* event, u32 reset_type) {
return ResultSuccess;
}
Result svcWaitSynchronization(Handle handle, s64 nanoseconds);
Result svcClearEvent(Handle handle);
Result svcSignalEvent(Handle handle);
Result svcCloseHandle(Handle handle) {
return ResultSuccess;
}
// dsp::DSP
Result DSP_LoadComponent(const u8* dspfirm_data, size_t size, u8 progmask, u8 datamask,
bool* dspfirm_loaded);
Result DSP_UnloadComponent();
Result DSP_RegisterInterruptEvents(Handle /*handle*/, u32 /*interrupt*/, u32 /*channel*/) {
return ResultSuccess;
};
Result DSP_GetSemaphoreHandle(Handle* handle);
Result DSP_SetSemaphoreMask(u32 /*mask*/) {
return ResultSuccess;
}
Result DSP_SetSemaphore(u32 semaphore);
Result DSP_ReadPipeIfPossible(u32 channel, u32 peer, void* out_buffer, u32 size, u16* out_size);
Result DSP_ConvertProcessAddressFromDspDram(u32 dsp_address, u16** host_address);
Result DSP_WriteProcessPipe(u32 channel, const void* buffer, u32 length);
Result DSP_FlushDataCache(void*, size_t) {
return ResultSuccess;
}
// libctru
u32 osConvertVirtToPhys(void* vaddr);
void* linearAlloc(size_t size);
Result dspInit();
Result dspExit() {
return ResultSuccess;
}
// Selects the DPS Emulation to use with the fixture
void InitDspCore(Settings::AudioEmulation dsp_core);
private:
Core::System system{};
Memory::MemorySystem memory{system};
Core::Timing core_timing{1, 100};
std::unique_ptr<AudioCore::DspInterface> dsp{};
std::array<std::array<bool, 3>, 4> interrupts_fired = {};
std::size_t linear_alloc_offset = 0;
};