This repository has been archived by the owner on Mar 5, 2024. It is now read-only.
forked from neobrain/citra
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tests\audio_core\hle\source.cpp: Add test cases to verify last_buffer_id
- Loading branch information
Showing
2 changed files
with
380 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,379 @@ | ||
#include <cstdio> | ||
#include <catch2/catch_template_test_macros.hpp> | ||
#include "audio_core/hle/shared_memory.h" | ||
#include "common/settings.h" | ||
#include "tests/audio_core/merryhime_3ds_audio/merry_audio/merry_audio.h" | ||
|
||
TEST_CASE_METHOD(MerryAudio::MerryAudioFixture, "Verify SourceStatus::Status::last_buffer_id 1", | ||
"[audio_core][hle]") { | ||
// World's worst triangle wave generator. | ||
// Generates PCM16. | ||
auto fillBuffer = [this](u32* audio_buffer, size_t size, unsigned freq) { | ||
for (size_t i = 0; i < size; i++) { | ||
u32 data = (i % freq) * 256; | ||
audio_buffer[i] = (data << 16) | (data & 0xFFFF); | ||
} | ||
|
||
DSP_FlushDataCache(audio_buffer, size); | ||
}; | ||
|
||
constexpr size_t NUM_SAMPLES = 160 * 1; | ||
u32* audio_buffer = (u32*)linearAlloc(NUM_SAMPLES * sizeof(u32)); | ||
fillBuffer(audio_buffer, NUM_SAMPLES, 160); | ||
u32* audio_buffer2 = (u32*)linearAlloc(NUM_SAMPLES * sizeof(u32)); | ||
fillBuffer(audio_buffer2, NUM_SAMPLES, 80); | ||
u32* audio_buffer3 = (u32*)linearAlloc(NUM_SAMPLES * sizeof(u32)); | ||
fillBuffer(audio_buffer3, NUM_SAMPLES, 40); | ||
|
||
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 Sanity") { | ||
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; | ||
} | ||
|
||
state.waitForSync(); | ||
initSharedMem(state); | ||
state.notifyDsp(); | ||
|
||
state.waitForSync(); | ||
state.notifyDsp(); | ||
state.waitForSync(); | ||
state.notifyDsp(); | ||
state.waitForSync(); | ||
state.notifyDsp(); | ||
state.waitForSync(); | ||
state.notifyDsp(); | ||
|
||
{ | ||
u16 buffer_id = 0; | ||
size_t next_queue_position = 0; | ||
|
||
state.write().source_configurations->config[0].play_position = 0; | ||
state.write().source_configurations->config[0].physical_address = | ||
osConvertVirtToPhys(audio_buffer3); | ||
state.write().source_configurations->config[0].length = NUM_SAMPLES; | ||
state.write().source_configurations->config[0].mono_or_stereo.Assign( | ||
AudioCore::HLE::SourceConfiguration::Configuration::MonoOrStereo::Stereo); | ||
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] | ||
.buffers[next_queue_position] | ||
.physical_address = osConvertVirtToPhys(buffer_id % 2 ? audio_buffer2 : audio_buffer); | ||
state.write().source_configurations->config[0].buffers[next_queue_position].length = | ||
NUM_SAMPLES; | ||
state.write().source_configurations->config[0].buffers[next_queue_position].adpcm_dirty = | ||
false; | ||
state.write().source_configurations->config[0].buffers[next_queue_position].is_looping = | ||
false; | ||
state.write().source_configurations->config[0].buffers[next_queue_position].buffer_id = | ||
++buffer_id; | ||
state.write().source_configurations->config[0].buffers_dirty |= 1 << next_queue_position; | ||
next_queue_position = (next_queue_position + 1) % 4; | ||
state.write().source_configurations->config[0].buffer_queue_dirty.Assign(true); | ||
state.write().source_configurations->config[0].enable = true; | ||
state.write().source_configurations->config[0].enable_dirty.Assign(true); | ||
|
||
state.notifyDsp(); | ||
|
||
for (size_t frame_count = 0; frame_count < 10; frame_count++) { | ||
state.waitForSync(); | ||
if (!state.read().source_statuses->status[0].is_enabled) { | ||
state.write().source_configurations->config[0].enable = true; | ||
state.write().source_configurations->config[0].enable_dirty.Assign(true); | ||
} | ||
|
||
if (state.read().source_statuses->status[0].current_buffer_id_dirty) { | ||
if (state.read().source_statuses->status[0].current_buffer_id == buffer_id || | ||
state.read().source_statuses->status[0].current_buffer_id == 0) { | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.physical_address = | ||
osConvertVirtToPhys(buffer_id % 2 ? audio_buffer2 : audio_buffer); | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.length = NUM_SAMPLES; | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.adpcm_dirty = false; | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.is_looping = false; | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.buffer_id = ++buffer_id; | ||
state.write().source_configurations->config[0].buffers_dirty |= | ||
1 << next_queue_position; | ||
next_queue_position = (next_queue_position + 1) % 4; | ||
state.write().source_configurations->config[0].buffer_queue_dirty.Assign(true); | ||
} | ||
} | ||
|
||
state.notifyDsp(); | ||
} | ||
|
||
// current_buffer_id should be 0 if the queue is not empty | ||
REQUIRE(state.read().source_statuses->status[0].last_buffer_id == 0); | ||
|
||
// Let the queue finish playing | ||
for (size_t frame_count = 0; frame_count < 10; frame_count++) { | ||
state.waitForSync(); | ||
state.notifyDsp(); | ||
} | ||
|
||
// TODO: There seems to be some nuances with how the LLE firmware runs the buffer queue, | ||
// that differs from the HLE implementation | ||
// REQUIRE(state.read().source_statuses->status[0].last_buffer_id == 5); | ||
|
||
// current_buffer_id should be equal to buffer_id once the queue is empty | ||
REQUIRE(state.read().source_statuses->status[0].last_buffer_id == buffer_id); | ||
} | ||
|
||
end: | ||
audioExit(state); | ||
} | ||
|
||
TEST_CASE_METHOD(MerryAudio::MerryAudioFixture, "Verify SourceStatus::Status::last_buffer_id 2", | ||
"[audio_core][hle]") { | ||
// World's worst triangle wave generator. | ||
// Generates PCM16. | ||
auto fillBuffer = [this](u32* audio_buffer, size_t size, unsigned freq) { | ||
for (size_t i = 0; i < size; i++) { | ||
u32 data = (i % freq) * 256; | ||
audio_buffer[i] = (data << 16) | (data & 0xFFFF); | ||
} | ||
|
||
DSP_FlushDataCache(audio_buffer, size); | ||
}; | ||
|
||
constexpr size_t NUM_SAMPLES = 160 * 1; | ||
u32* audio_buffer = (u32*)linearAlloc(NUM_SAMPLES * sizeof(u32)); | ||
fillBuffer(audio_buffer, NUM_SAMPLES, 160); | ||
u32* audio_buffer2 = (u32*)linearAlloc(NUM_SAMPLES * sizeof(u32)); | ||
fillBuffer(audio_buffer2, NUM_SAMPLES, 80); | ||
u32* audio_buffer3 = (u32*)linearAlloc(NUM_SAMPLES * sizeof(u32)); | ||
fillBuffer(audio_buffer3, NUM_SAMPLES, 40); | ||
|
||
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 Sanity") { | ||
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; | ||
} | ||
|
||
state.waitForSync(); | ||
initSharedMem(state); | ||
state.notifyDsp(); | ||
|
||
state.waitForSync(); | ||
state.notifyDsp(); | ||
state.waitForSync(); | ||
state.notifyDsp(); | ||
state.waitForSync(); | ||
state.notifyDsp(); | ||
state.waitForSync(); | ||
state.notifyDsp(); | ||
|
||
{ | ||
u16 buffer_id = 0; | ||
size_t next_queue_position = 0; | ||
|
||
state.write().source_configurations->config[0].play_position = 0; | ||
state.write().source_configurations->config[0].physical_address = | ||
osConvertVirtToPhys(audio_buffer3); | ||
state.write().source_configurations->config[0].length = NUM_SAMPLES; | ||
state.write().source_configurations->config[0].mono_or_stereo.Assign( | ||
AudioCore::HLE::SourceConfiguration::Configuration::MonoOrStereo::Stereo); | ||
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] | ||
.buffers[next_queue_position] | ||
.physical_address = osConvertVirtToPhys(buffer_id % 2 ? audio_buffer2 : audio_buffer); | ||
state.write().source_configurations->config[0].buffers[next_queue_position].length = | ||
NUM_SAMPLES; | ||
state.write().source_configurations->config[0].buffers[next_queue_position].adpcm_dirty = | ||
false; | ||
state.write().source_configurations->config[0].buffers[next_queue_position].is_looping = | ||
false; | ||
state.write().source_configurations->config[0].buffers[next_queue_position].buffer_id = | ||
++buffer_id; | ||
state.write().source_configurations->config[0].buffers_dirty |= 1 << next_queue_position; | ||
next_queue_position = (next_queue_position + 1) % 4; | ||
state.write().source_configurations->config[0].buffer_queue_dirty.Assign(true); | ||
state.write().source_configurations->config[0].enable = true; | ||
state.write().source_configurations->config[0].enable_dirty.Assign(true); | ||
|
||
state.notifyDsp(); | ||
|
||
for (size_t frame_count = 0; frame_count < 10; frame_count++) { | ||
state.waitForSync(); | ||
if (!state.read().source_statuses->status[0].is_enabled) { | ||
state.write().source_configurations->config[0].enable = true; | ||
state.write().source_configurations->config[0].enable_dirty.Assign(true); | ||
} | ||
|
||
if (state.read().source_statuses->status[0].current_buffer_id_dirty) { | ||
if (state.read().source_statuses->status[0].current_buffer_id == buffer_id || | ||
state.read().source_statuses->status[0].current_buffer_id == 0) { | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.physical_address = | ||
osConvertVirtToPhys(buffer_id % 2 ? audio_buffer2 : audio_buffer); | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.length = NUM_SAMPLES; | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.adpcm_dirty = false; | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.is_looping = false; | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.buffer_id = ++buffer_id; | ||
state.write().source_configurations->config[0].buffers_dirty |= | ||
1 << next_queue_position; | ||
next_queue_position = (next_queue_position + 1) % 4; | ||
state.write().source_configurations->config[0].buffer_queue_dirty.Assign(true); | ||
} | ||
} | ||
|
||
state.notifyDsp(); | ||
} | ||
|
||
// current_buffer_id should be 0 if the queue is not empty | ||
REQUIRE(state.read().source_statuses->status[0].last_buffer_id == 0); | ||
|
||
// Let the queue finish playing | ||
for (size_t frame_count = 0; frame_count < 10; frame_count++) { | ||
state.waitForSync(); | ||
state.notifyDsp(); | ||
} | ||
|
||
// TODO: There seems to be some nuances with how the LLE firmware runs the buffer queue, | ||
// that differs from the HLE implementation | ||
// REQUIRE(state.read().source_statuses->status[0].last_buffer_id == 5); | ||
|
||
// current_buffer_id should be equal to buffer_id once the queue is empty | ||
REQUIRE(state.read().source_statuses->status[0].last_buffer_id == buffer_id); | ||
|
||
// Restart Playing | ||
for (size_t frame_count = 0; frame_count < 10; frame_count++) { | ||
state.waitForSync(); | ||
if (!state.read().source_statuses->status[0].is_enabled) { | ||
state.write().source_configurations->config[0].enable = true; | ||
state.write().source_configurations->config[0].enable_dirty.Assign(true); | ||
} | ||
|
||
if (state.read().source_statuses->status[0].current_buffer_id_dirty) { | ||
if (state.read().source_statuses->status[0].current_buffer_id == buffer_id || | ||
state.read().source_statuses->status[0].current_buffer_id == 0) { | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.physical_address = | ||
osConvertVirtToPhys(buffer_id % 2 ? audio_buffer2 : audio_buffer); | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.length = NUM_SAMPLES; | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.adpcm_dirty = false; | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.is_looping = false; | ||
state.write() | ||
.source_configurations->config[0] | ||
.buffers[next_queue_position] | ||
.buffer_id = ++buffer_id; | ||
state.write().source_configurations->config[0].buffers_dirty |= | ||
1 << next_queue_position; | ||
next_queue_position = (next_queue_position + 1) % 4; | ||
state.write().source_configurations->config[0].buffer_queue_dirty.Assign(true); | ||
} | ||
} | ||
|
||
state.notifyDsp(); | ||
} | ||
|
||
// current_buffer_id should be 0 if the queue is not empty | ||
REQUIRE(state.read().source_statuses->status[0].last_buffer_id == 0); | ||
|
||
// Let the queue finish playing | ||
for (size_t frame_count = 0; frame_count < 10; frame_count++) { | ||
state.waitForSync(); | ||
state.notifyDsp(); | ||
} | ||
|
||
// current_buffer_id should be equal to buffer_id once the queue is empty | ||
REQUIRE(state.read().source_statuses->status[0].last_buffer_id == buffer_id); | ||
} | ||
|
||
end: | ||
audioExit(state); | ||
} |