mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	tests/audio_core: add sanity test cases for LLE vs HLE
This commit is contained in:
		
							parent
							
								
									8cada619b3
								
							
						
					
					
						commit
						5311c939a2
					
				
					 6 changed files with 266 additions and 1 deletions
				
			
		|  | @ -11,6 +11,8 @@ add_executable(tests | |||
|     core/memory/memory.cpp | ||||
|     core/memory/vm_manager.cpp | ||||
|     precompiled_headers.h | ||||
|     audio_core/hle/hle.cpp | ||||
|     audio_core/lle/lle.cpp | ||||
|     audio_core/audio_fixures.h | ||||
|     audio_core/decoder_tests.cpp | ||||
|     video_core/shader/shader_jit_x64_compiler.cpp | ||||
|  |  | |||
							
								
								
									
										143
									
								
								src/tests/audio_core/hle/hle.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/tests/audio_core/hle/hle.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,143 @@ | |||
| // Copyright 2023 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <catch2/catch_test_macros.hpp> | ||||
| #include "audio_core/hle/decoder.h" | ||||
| #include "audio_core/hle/hle.h" | ||||
| #include "audio_core/lle/lle.h" | ||||
| #include "common/common_paths.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| TEST_CASE("DSP LLE vs HLE", "[audio_core][hle]") { | ||||
|     Memory::MemorySystem hle_memory; | ||||
|     Core::Timing hle_core_timing(1, 100); | ||||
| 
 | ||||
|     Memory::MemorySystem lle_memory; | ||||
|     Core::Timing lle_core_timing(1, 100); | ||||
| 
 | ||||
|     AudioCore::DspHle hle(hle_memory, hle_core_timing); | ||||
|     AudioCore::DspLle lle(lle_memory, lle_core_timing, true); | ||||
| 
 | ||||
|     // Initialiase LLE
 | ||||
|     { | ||||
|         FileUtil::SetUserPath(); | ||||
|         // see tests/audio_core/lle/lle.cpp for details on dspaudio.cdc
 | ||||
|         std::string firm_filepath = | ||||
|             FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "3ds" DIR_SEP "dspaudio.cdc"; | ||||
| 
 | ||||
|         if (!FileUtil::Exists(firm_filepath)) { | ||||
|             SKIP("Test requires dspaudio.cdc"); | ||||
|         } | ||||
| 
 | ||||
|         FileUtil::IOFile firm_file(firm_filepath, "rb"); | ||||
| 
 | ||||
|         std::vector<u8> firm_file_buf(firm_file.GetSize()); | ||||
|         firm_file.ReadArray(firm_file_buf.data(), firm_file_buf.size()); | ||||
|         lle.LoadComponent(firm_file_buf); | ||||
|         lle.SetSemaphoreHandler([&lle]() { | ||||
|             u16 slot = lle.RecvData(2); | ||||
|             u16 side = slot % 2; | ||||
|             u16 pipe = slot / 2; | ||||
|             fmt::print("SetSemaphoreHandler slot={}\n", slot); | ||||
|             if (pipe > 15) | ||||
|                 return; | ||||
|             if (side != 0) | ||||
|                 return; | ||||
|             if (pipe == 0) { | ||||
|                 // pipe 0 is for debug. 3DS automatically drains this pipe and discards the
 | ||||
|                 // data
 | ||||
|                 lle.PipeRead(static_cast<AudioCore::DspPipe>(pipe), | ||||
|                              lle.GetPipeReadableSize(static_cast<AudioCore::DspPipe>(pipe))); | ||||
|             } | ||||
|         }); | ||||
|         lle.SetRecvDataHandler(0, []() { fmt::print("SetRecvDataHandler 0\n"); }); | ||||
|         lle.SetRecvDataHandler(1, []() { fmt::print("SetRecvDataHandler 1\n"); }); | ||||
|         lle.SetRecvDataHandler(2, []() { fmt::print("SetRecvDataHandler 2\n"); }); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("Initialise Audio Pipe") { | ||||
|         std::vector<u8> buffer(4, 0); | ||||
|         buffer[0] = 0; | ||||
| 
 | ||||
|         // LLE
 | ||||
|         { | ||||
|             lle.PipeWrite(AudioCore::DspPipe::Audio, buffer); | ||||
|             lle.SetSemaphore(0x4000); | ||||
| 
 | ||||
|             // todo: wait for interrupt
 | ||||
|             do { | ||||
|                 lle_core_timing.GetTimer(0)->AddTicks(lle_core_timing.GetTimer(0)->GetDowncount()); | ||||
|                 lle_core_timing.GetTimer(0)->Advance(); | ||||
|                 lle_core_timing.GetTimer(0)->SetNextSlice(); | ||||
|             } while (lle.GetPipeReadableSize(AudioCore::DspPipe::Audio) == 0); | ||||
| 
 | ||||
|             REQUIRE(lle.GetPipeReadableSize(AudioCore::DspPipe::Audio) >= 32); | ||||
|         } | ||||
|         std::vector<u8> lle_read_buffer; | ||||
|         lle_read_buffer = lle.PipeRead(AudioCore::DspPipe::Audio, 2); | ||||
|         u16 lle_size; | ||||
|         memcpy(&lle_size, lle_read_buffer.data(), sizeof(lle_size)); | ||||
|         lle_read_buffer = lle.PipeRead(AudioCore::DspPipe::Audio, lle_size * 2); | ||||
| 
 | ||||
|         // HLE
 | ||||
|         { | ||||
|             hle.PipeWrite(AudioCore::DspPipe::Audio, buffer); | ||||
|             REQUIRE(hle.GetPipeReadableSize(AudioCore::DspPipe::Audio) >= 32); | ||||
|         } | ||||
|         std::vector<u8> hle_read_buffer(32); | ||||
|         hle_read_buffer = hle.PipeRead(AudioCore::DspPipe::Audio, 2); | ||||
|         u16 hle_size; | ||||
|         memcpy(&hle_size, hle_read_buffer.data(), sizeof(hle_size)); | ||||
|         hle_read_buffer = hle.PipeRead(AudioCore::DspPipe::Audio, hle_size * 2); | ||||
| 
 | ||||
|         REQUIRE(hle_size == lle_size); | ||||
|         REQUIRE(hle_read_buffer == lle_read_buffer); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("Initialise Binary Pipe") { | ||||
|         std::vector<u8> buffer(32, 0); | ||||
|         AudioCore::HLE::BinaryMessage& request = | ||||
|             *reinterpret_cast<AudioCore::HLE::BinaryMessage*>(buffer.data()); | ||||
| 
 | ||||
|         request.header.codec = AudioCore::HLE::DecoderCodec::DecodeAAC; | ||||
|         request.header.cmd = AudioCore::HLE::DecoderCommand::Init; | ||||
| 
 | ||||
|         // Values used by Pokemon X
 | ||||
|         request.header.result = static_cast<AudioCore::HLE::ResultStatus>(3); | ||||
|         request.decode_aac_init.unknown1 = 1; | ||||
|         request.decode_aac_init.unknown2 = 0xFFFF'FFFF; | ||||
|         request.decode_aac_init.unknown3 = 1; | ||||
|         request.decode_aac_init.unknown4 = 0; | ||||
|         request.decode_aac_init.unknown5 = 1; | ||||
|         request.decode_aac_init.unknown6 = 0x20; | ||||
| 
 | ||||
|         // LLE
 | ||||
|         lle.PipeWrite(AudioCore::DspPipe::Binary, buffer); | ||||
|         lle.SetSemaphore(0x4000); | ||||
| 
 | ||||
|         // todo: wait for interrupt
 | ||||
|         do { | ||||
|             lle_core_timing.GetTimer(0)->AddTicks(lle_core_timing.GetTimer(0)->GetDowncount()); | ||||
|             lle_core_timing.GetTimer(0)->Advance(); | ||||
|             lle_core_timing.GetTimer(0)->SetNextSlice(); | ||||
|         } while (lle.GetPipeReadableSize(AudioCore::DspPipe::Binary) == 0); | ||||
| 
 | ||||
|         REQUIRE(lle.GetPipeReadableSize(AudioCore::DspPipe::Binary) >= 32); | ||||
| 
 | ||||
|         std::vector<u8> lle_read_buffer = lle.PipeRead(AudioCore::DspPipe::Binary, 32); | ||||
|         AudioCore::HLE::BinaryMessage& resp = | ||||
|             *reinterpret_cast<AudioCore::HLE::BinaryMessage*>(lle_read_buffer.data()); | ||||
|         CHECK(resp.header.result == AudioCore::HLE::ResultStatus::Success); | ||||
| 
 | ||||
|         // HLE
 | ||||
|         { | ||||
|             hle.PipeWrite(AudioCore::DspPipe::Binary, buffer); | ||||
|             REQUIRE(hle.GetPipeReadableSize(AudioCore::DspPipe::Binary) >= 32); | ||||
|         } | ||||
|         std::vector<u8> hle_read_buffer = hle.PipeRead(AudioCore::DspPipe::Binary, 32); | ||||
| 
 | ||||
|         REQUIRE(hle_read_buffer == lle_read_buffer); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										110
									
								
								src/tests/audio_core/lle/lle.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/tests/audio_core/lle/lle.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,110 @@ | |||
| // Copyright 2023 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <catch2/catch_test_macros.hpp> | ||||
| #include "audio_core/hle/decoder.h" | ||||
| #include "audio_core/lle/lle.h" | ||||
| #include "common/common_paths.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| TEST_CASE("DSP LLE Sanity", "[audio_core][lle]") { | ||||
|     Memory::MemorySystem memory; | ||||
|     Core::Timing core_timing(1, 100); | ||||
| 
 | ||||
|     AudioCore::DspLle lle(memory, core_timing, true); | ||||
|     { | ||||
|         FileUtil::SetUserPath(); | ||||
|         // dspaudio.cdc can be dumped from Pokemon X & Y, It can be found in the romfs at
 | ||||
|         // "rom:/sound/dspaudio.cdc".
 | ||||
|         // One could also extract the firmware from the 3DS sound app using a modified version of
 | ||||
|         // https://github.com/zoogie/DSP1.
 | ||||
|         std::string firm_filepath = | ||||
|             FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "3ds" DIR_SEP "dspaudio.cdc"; | ||||
| 
 | ||||
|         if (!FileUtil::Exists(firm_filepath)) { | ||||
|             SKIP("Test requires dspaudio.cdc"); | ||||
|         } | ||||
| 
 | ||||
|         FileUtil::IOFile firm_file(firm_filepath, "rb"); | ||||
| 
 | ||||
|         std::vector<u8> firm_file_buf(firm_file.GetSize()); | ||||
|         firm_file.ReadArray(firm_file_buf.data(), firm_file_buf.size()); | ||||
|         lle.LoadComponent(firm_file_buf); | ||||
|     } | ||||
|     lle.SetSemaphoreHandler([&lle]() { | ||||
|         u16 slot = lle.RecvData(2); | ||||
|         u16 side = slot % 2; | ||||
|         u16 pipe = slot / 2; | ||||
|         fmt::print("SetSemaphoreHandler slot={}\n", slot); | ||||
|         if (pipe > 15) | ||||
|             return; | ||||
|         if (side != 0) | ||||
|             return; | ||||
|         if (pipe == 0) { | ||||
|             // pipe 0 is for debug. 3DS automatically drains this pipe and discards the
 | ||||
|             // data
 | ||||
|             lle.PipeRead(static_cast<AudioCore::DspPipe>(pipe), | ||||
|                          lle.GetPipeReadableSize(static_cast<AudioCore::DspPipe>(pipe))); | ||||
|         } | ||||
|     }); | ||||
|     lle.SetRecvDataHandler(0, []() { fmt::print("SetRecvDataHandler 0\n"); }); | ||||
|     lle.SetRecvDataHandler(1, []() { fmt::print("SetRecvDataHandler 1\n"); }); | ||||
|     lle.SetRecvDataHandler(2, []() { fmt::print("SetRecvDataHandler 2\n"); }); | ||||
|     SECTION("Initialise Audio Pipe") { | ||||
|         std::vector<u8> buffer(4, 0); | ||||
|         buffer[0] = 0; | ||||
| 
 | ||||
|         lle.PipeWrite(AudioCore::DspPipe::Audio, buffer); | ||||
|         lle.SetSemaphore(0x4000); | ||||
| 
 | ||||
|         // todo: wait for interrupt
 | ||||
|         do { | ||||
|             core_timing.GetTimer(0)->AddTicks(core_timing.GetTimer(0)->GetDowncount()); | ||||
|             core_timing.GetTimer(0)->Advance(); | ||||
|             core_timing.GetTimer(0)->SetNextSlice(); | ||||
|         } while (lle.GetPipeReadableSize(AudioCore::DspPipe::Audio) == 0); | ||||
| 
 | ||||
|         REQUIRE(lle.GetPipeReadableSize(AudioCore::DspPipe::Audio) >= 32); | ||||
| 
 | ||||
|         buffer = lle.PipeRead(AudioCore::DspPipe::Audio, 2); | ||||
|         u16 size; | ||||
|         memcpy(&size, buffer.data(), sizeof(size)); | ||||
|         // see AudioCore::DspHle::Impl::AudioPipeWriteStructAddresses()
 | ||||
|         REQUIRE(size * 2 == 30); | ||||
|     } | ||||
|     SECTION("Initialise EncodeAAC - Binary Pipe") { | ||||
|         std::vector<u8> buffer(32, 0); | ||||
|         AudioCore::HLE::BinaryMessage& request = | ||||
|             *reinterpret_cast<AudioCore::HLE::BinaryMessage*>(buffer.data()); | ||||
| 
 | ||||
|         request.header.codec = AudioCore::HLE::DecoderCodec::EncodeAAC; | ||||
|         request.header.cmd = AudioCore::HLE::DecoderCommand::Init; | ||||
| 
 | ||||
|         // Values used by the 3DS sound app.
 | ||||
|         request.encode_aac_init.unknown1 = 1; | ||||
|         request.encode_aac_init.sample_rate = static_cast<AudioCore::HLE::DecoderSampleRate>(5); | ||||
|         request.encode_aac_init.unknown3 = 2; | ||||
|         request.encode_aac_init.unknown4 = 0; | ||||
|         request.encode_aac_init.unknown5 = rand(); | ||||
|         request.encode_aac_init.unknown6 = rand(); | ||||
|         lle.PipeWrite(AudioCore::DspPipe::Binary, buffer); | ||||
|         lle.SetSemaphore(0x4000); | ||||
| 
 | ||||
|         // todo: wait for interrupt
 | ||||
|         do { | ||||
|             core_timing.GetTimer(0)->AddTicks(core_timing.GetTimer(0)->GetDowncount()); | ||||
|             core_timing.GetTimer(0)->Advance(); | ||||
|             core_timing.GetTimer(0)->SetNextSlice(); | ||||
|         } while (lle.GetPipeReadableSize(AudioCore::DspPipe::Binary) == 0); | ||||
| 
 | ||||
|         REQUIRE(lle.GetPipeReadableSize(AudioCore::DspPipe::Binary) >= 32); | ||||
| 
 | ||||
|         buffer = lle.PipeRead(AudioCore::DspPipe::Binary, 32); | ||||
|         AudioCore::HLE::BinaryMessage& resp = | ||||
|             *reinterpret_cast<AudioCore::HLE::BinaryMessage*>(buffer.data()); | ||||
|         REQUIRE(resp.header.result == AudioCore::HLE::ResultStatus::Success); | ||||
|         REQUIRE(resp.data == request.data); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue