From 228f26d1e4d64d391b37eff16e83750f55048aff Mon Sep 17 00:00:00 2001 From: SachinVin <26602104+SachinVin@users.noreply.github.com> Date: Sun, 21 Jan 2024 09:46:00 +0530 Subject: [PATCH] tests: Port merry's audio tests (#7354) --- src/core/hle/service/dsp/dsp_dsp.cpp | 2 +- src/tests/CMakeLists.txt | 5 + .../audio_core/merryhime_3ds_audio/README.md | 1 + .../audio_test_biquad_filter.cpp | 152 ++++++++++ .../merry_audio/merry_audio.cpp | 264 ++++++++++++++++++ .../merry_audio/merry_audio.h | 65 +++++ .../merry_audio/service_fixture.cpp | 133 +++++++++ .../merry_audio/service_fixture.h | 84 ++++++ 8 files changed, 705 insertions(+), 1 deletion(-) create mode 100644 src/tests/audio_core/merryhime_3ds_audio/README.md create mode 100644 src/tests/audio_core/merryhime_3ds_audio/audio_test_biquad_filter.cpp create mode 100644 src/tests/audio_core/merryhime_3ds_audio/merry_audio/merry_audio.cpp create mode 100644 src/tests/audio_core/merryhime_3ds_audio/merry_audio/merry_audio.h create mode 100644 src/tests/audio_core/merryhime_3ds_audio/merry_audio/service_fixture.cpp create mode 100644 src/tests/audio_core/merryhime_3ds_audio/merry_audio/service_fixture.h diff --git a/src/core/hle/service/dsp/dsp_dsp.cpp b/src/core/hle/service/dsp/dsp_dsp.cpp index 3de9354ef2..9b4a9c27b6 100644 --- a/src/core/hle/service/dsp/dsp_dsp.cpp +++ b/src/core/hle/service/dsp/dsp_dsp.cpp @@ -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) { diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index b1984b3a19..590f07e783 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -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) diff --git a/src/tests/audio_core/merryhime_3ds_audio/README.md b/src/tests/audio_core/merryhime_3ds_audio/README.md new file mode 100644 index 0000000000..060275e9fa --- /dev/null +++ b/src/tests/audio_core/merryhime_3ds_audio/README.md @@ -0,0 +1 @@ +Port of HW tests from https://github.com/merryhime/3ds-audio diff --git a/src/tests/audio_core/merryhime_3ds_audio/audio_test_biquad_filter.cpp b/src/tests/audio_core/merryhime_3ds_audio/audio_test_biquad_filter.cpp new file mode 100644 index 0000000000..908e382235 --- /dev/null +++ b/src/tests/audio_core/merryhime_3ds_audio/audio_test_biquad_filter.cpp @@ -0,0 +1,152 @@ +#include +#include +#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 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 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); +} diff --git a/src/tests/audio_core/merryhime_3ds_audio/merry_audio/merry_audio.cpp b/src/tests/audio_core/merryhime_3ds_audio/merry_audio/merry_audio.cpp new file mode 100644 index 0000000000..7004149f04 --- /dev/null +++ b/src/tests/audio_core/merryhime_3ds_audio/merry_audio/merry_audio.cpp @@ -0,0 +1,264 @@ +#include +#include +#include +#include +#include +#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 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 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 MerryAudioFixture::audioInit(const std::vector& 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 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(dsp_addrs[i]); + const u32 addr1 = static_cast(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(vaddr0); + ret.dsp_structs[i][1] = reinterpret_cast(vaddr1); + } + + for (int i = 0; i < 2; i++) { + ret.shared_mem[i].frame_counter = reinterpret_cast(ret.dsp_structs[0][i]); + + ret.shared_mem[i].source_configurations = + reinterpret_cast(ret.dsp_structs[1][i]); + ret.shared_mem[i].source_statuses = + reinterpret_cast(ret.dsp_structs[2][i]); + ret.shared_mem[i].adpcm_coefficients = + reinterpret_cast(ret.dsp_structs[3][i]); + + ret.shared_mem[i].dsp_configuration = + reinterpret_cast(ret.dsp_structs[4][i]); + ret.shared_mem[i].dsp_status = + reinterpret_cast(ret.dsp_structs[5][i]); + + ret.shared_mem[i].final_samples = + reinterpret_cast(ret.dsp_structs[6][i]); + ret.shared_mem[i].intermediate_mix_samples = + reinterpret_cast(ret.dsp_structs[7][i]); + + ret.shared_mem[i].compressor = + reinterpret_cast(ret.dsp_structs[8][i]); + + ret.shared_mem[i].dsp_debug = + reinterpret_cast(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 diff --git a/src/tests/audio_core/merryhime_3ds_audio/merry_audio/merry_audio.h b/src/tests/audio_core/merryhime_3ds_audio/merry_audio/merry_audio.h new file mode 100644 index 0000000000..c4e67c68b5 --- /dev/null +++ b/src/tests/audio_core/merryhime_3ds_audio/merry_audio/merry_audio.h @@ -0,0 +1,65 @@ +#include +#include +#include +#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, 16> dsp_structs{}; + std::array 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 loadDspFirmFromFile(); + std::optional audioInit(const std::vector& dspfirm); + void audioExit(const AudioState& state); + void initSharedMem(AudioState& state); +}; +} // namespace MerryAudio diff --git a/src/tests/audio_core/merryhime_3ds_audio/merry_audio/service_fixture.cpp b/src/tests/audio_core/merryhime_3ds_audio/merry_audio/service_fixture.cpp new file mode 100644 index 0000000000..9417cea971 --- /dev/null +++ b/src/tests/audio_core/merryhime_3ds_audio/merry_audio/service_fixture.cpp @@ -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(Pipe)][static_cast(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(Pipe)][static_cast(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(channel); + const u16 pipe_readable_size = static_cast(dsp->GetPipeReadableSize(pipe)); + + std::vector pipe_buffer; + if (pipe_readable_size >= size) { + pipe_buffer = dsp->PipeRead(pipe, size); + } + + *out_size = static_cast(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( + (dsp_address << 1) + (reinterpret_cast(dsp->GetDspMemory().data()) + 0x40000u)); + return ResultSuccess; +} + +Result ServiceFixture::DSP_WriteProcessPipe(u32 channel, const void* buffer, u32 length) { + const AudioCore::DspPipe pipe = static_cast(channel); + dsp->PipeWrite(pipe, {reinterpret_cast(buffer), length}); + + return ResultSuccess; +}; + +// libctru +u32 ServiceFixture::osConvertVirtToPhys(void* vaddr) { + return static_cast(Memory::FCRAM_PADDR + + (reinterpret_cast(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(system, memory, core_timing); + dsp->SetInterruptHandler([this](Service::DSP::InterruptType type, AudioCore::DspPipe pipe) { + interrupts_fired[static_cast(type)][static_cast(pipe)] = 1; + }); + } + + return ResultSuccess; +} + +// Fixture +void ServiceFixture::InitDspCore(Settings::AudioEmulation dsp_core) { + if (dsp_core == Settings::AudioEmulation::HLE) { + dsp = std::make_unique(system, memory, core_timing); + } else { + dsp = std::make_unique( + system, memory, core_timing, dsp_core == Settings::AudioEmulation::LLEMultithreaded); + } + dsp->SetInterruptHandler([this](Service::DSP::InterruptType type, AudioCore::DspPipe pipe) { + interrupts_fired[static_cast(type)][static_cast(pipe)] = 1; + }); +} diff --git a/src/tests/audio_core/merryhime_3ds_audio/merry_audio/service_fixture.h b/src/tests/audio_core/merryhime_3ds_audio/merry_audio/service_fixture.h new file mode 100644 index 0000000000..8789f0104f --- /dev/null +++ b/src/tests/audio_core/merryhime_3ds_audio/merry_audio/service_fixture.h @@ -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 dsp{}; + + std::array, 4> interrupts_fired = {}; + + std::size_t linear_alloc_offset = 0; +};