mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 19:07:01 +02:00
audio: rewind compressed sampler blocks at loop boundaries
This commit is contained in:
@@ -712,6 +712,74 @@ void TestLoopCallbacksAndExit() {
|
||||
Ngs2VoiceControl(reinterpret_cast<uintptr_t>(&pending.voice), &exit_loop);
|
||||
}
|
||||
|
||||
void TestAtrac9Looping() {
|
||||
// Four encoded mono frames with one nonzero spectral coefficient, then superframe padding.
|
||||
// The block skips one frame plus one sample and ends inside the third frame.
|
||||
constexpr std::array<uint8_t, 36> frame {0, 0, 0x04, 0x20, 0x04, 0xc0, 0, 0, 0x1f, 0xc0};
|
||||
std::array<uint8_t, 192> encoded {};
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
std::copy(std::begin(frame), std::end(frame), encoded.begin() + i * sizeof(frame));
|
||||
if (i != 0) encoded[i * sizeof(frame)] |= 0x80;
|
||||
}
|
||||
const auto make_voice = [&](uint32_t repeats, LoopCallbacks& log) {
|
||||
auto f = std::make_unique<Fixture>(24000);
|
||||
f->voice.SetupSampler({NGS2_WAVEFORM_TYPE_ATRAC9, 1, 24000, 0xfe4005f0});
|
||||
f->voice.state = Ngs2VoicePlayState::Playing;
|
||||
f->voice.callback = reinterpret_cast<uintptr_t>(&RecordLoopCallback);
|
||||
f->voice.callback_data = reinterpret_cast<uintptr_t>(&log);
|
||||
f->voice.callback_flags = 3;
|
||||
const Ngs2WaveformBlock block {0, encoded.size(), repeats, 129, 137, 0, 123};
|
||||
struct Blocks {
|
||||
Ngs2VoiceParamHeader header {32, 0, 0x10000001};
|
||||
const uint8_t* data;
|
||||
uint32_t flags, count;
|
||||
const Ngs2WaveformBlock* blocks;
|
||||
} param {{32, 0, 0x10000001}, encoded.data(), 0, 1, &block};
|
||||
Check(Ngs2VoiceControl(reinterpret_cast<uintptr_t>(&f->voice), ¶m.header) == OK,
|
||||
"compressed loop queue failed");
|
||||
return f;
|
||||
};
|
||||
LoopCallbacks baseline_log, finite_log, split_log, infinite_log;
|
||||
auto baseline = make_voice(0, baseline_log);
|
||||
baseline->Render(274);
|
||||
const auto reference = baseline->voice.samples;
|
||||
Check(std::ranges::any_of(reference, [](float sample) { return std::abs(sample) > 0.00001f; }),
|
||||
"synthetic compressed fixture produced no decoded signal");
|
||||
auto finite = make_voice(2, finite_log);
|
||||
finite->Render(822);
|
||||
Check(finite->voice.state == Ngs2VoicePlayState::Empty &&
|
||||
finite->voice.decoded_samples == 411 && finite->voice.decoded_bytes == 576 &&
|
||||
finite_log.events == std::vector<std::pair<uint32_t, uint32_t>> {{2, 1}, {2, 2}, {1, 2}},
|
||||
"compressed finite loop lost samples, input progress, or completion callbacks");
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
Check(std::equal(reference.begin(), reference.end() - 1,
|
||||
finite->voice.samples.begin() + i * 274),
|
||||
"compressed loop did not restore decoder history and skip samples");
|
||||
const float tail = i == 2 ? reference.back() : std::lerp(reference[272], reference[0], 0.5f);
|
||||
Check(finite->voice.samples[i * 274 + 273] == tail,
|
||||
"compressed loop dropped the final frame or broke boundary interpolation");
|
||||
}
|
||||
auto split = make_voice(2, split_log);
|
||||
split->Render(273);
|
||||
auto joined = split->voice.samples;
|
||||
split->Render(549);
|
||||
joined.insert(joined.end(), split->voice.samples.begin(), split->voice.samples.end());
|
||||
Check(joined == finite->voice.samples && split_log.events == finite_log.events,
|
||||
"compressed loop changed across render grains");
|
||||
auto infinite = make_voice(UINT32_MAX, infinite_log);
|
||||
infinite->Render(300);
|
||||
Check(infinite->voice.state == Ngs2VoicePlayState::Playing &&
|
||||
infinite->voice.blocks.front().info.num_repeats == UINT32_MAX,
|
||||
"compressed infinite loop did not rewind");
|
||||
Ngs2VoiceParamHeader exit_loop {sizeof(Ngs2VoiceParamHeader), 0, 0x10000004};
|
||||
Ngs2VoiceControl(reinterpret_cast<uintptr_t>(&infinite->voice), &exit_loop);
|
||||
infinite->Render(248);
|
||||
Check(infinite->voice.state == Ngs2VoicePlayState::Empty &&
|
||||
infinite->voice.decoded_samples == 274 &&
|
||||
infinite_log.events == std::vector<std::pair<uint32_t, uint32_t>> {{2, 1}, {1, 1}},
|
||||
"compressed loop exit failed to finish the active iteration");
|
||||
}
|
||||
|
||||
void TestWaveformReadAddress() {
|
||||
Fixture f(48000, 2);
|
||||
const int16_t pcm[] = {-100, -100, 8192, 4096, 16384, 8192};
|
||||
@@ -921,6 +989,7 @@ int main() {
|
||||
TestSamplerReuseResetsRouting();
|
||||
TestDeferredSamplerSetup();
|
||||
TestLoopCallbacksAndExit();
|
||||
TestAtrac9Looping();
|
||||
TestWaveformReadAddress();
|
||||
TestFilterTailDuringStarvation();
|
||||
TestFiniteFilterTail();
|
||||
|
||||
Reference in New Issue
Block a user