Files
AnyPS5/core/libs/prx/libSceNgs2.native/tests/Ngs2VoiceIndex.cpp
T
JosepBove 5fe4780812 fix(ngs2): return the voice-index error from sceNgs2RackGetVoiceHandle
An index at or beyond the rack's voice count returned through
APS5_INVALID_ARG_EX, which throws and ends the process. Heretic + Hexen
(PPSA26525) creates a one-voice reverb rack, takes voice 0 and asks for
voice 1 at start-up, so it stopped at 1.5 s on
"sceNgs2RackGetVoiceHandle: invalid argument". Return 0x804A8305 and
leave the output handle untouched, after the existing null-output and
rack-handle checks. The value follows prosper's analysis of
libSceNgs2.native.sprx; shadPS4 lists 0x804A0302 for the PS4 library.
Neither is a PS5 measurement, recorded in the technical debt.

ngs2_voiceindex creates one-voice sampler, submixer, mastering and
reverb racks and a default sampler rack, and checks the error, the
untouched output, the null-output and invalid-rack codes and that the
last valid index still resolves afterwards. It aborts on main's library
with the throw above and passes with this change.
2026-10-14 17:29:25 +02:00

92 lines
3.5 KiB
C++

#include "Ngs2Test.hpp"
#include <array>
#include <cstring>
#include <limits>
namespace {
union RackOption {
Ngs2RackOption common;
Ngs2SamplerRackOption sampler;
Ngs2SubmixerRackOption submixer;
Ngs2MasteringRackOption mastering;
Ngs2ReverbRackOption reverb;
};
std::size_t OptionSize(std::uint32_t rackId) {
switch (rackId) {
case SCE_NGS2_RACK_ID_SAMPLER: return sizeof(Ngs2SamplerRackOption);
case SCE_NGS2_RACK_ID_SUBMIXER: return sizeof(Ngs2SubmixerRackOption);
case SCE_NGS2_RACK_ID_MASTERING: return sizeof(Ngs2MasteringRackOption);
default: return sizeof(Ngs2ReverbRackOption);
}
}
uintptr_t CreateSingleVoiceRack(uintptr_t system, std::uint32_t rackId) {
RackOption option{};
option.common.size = OptionSize(rackId);
option.common.max_grain_samples = 512;
option.common.max_voices = 1;
option.common.max_input_delay_blocks = 1;
option.common.max_matrices = 1;
option.common.max_ports = 8;
switch (rackId) {
case SCE_NGS2_RACK_ID_SAMPLER:
option.sampler.max_channel_works = 1;
option.sampler.max_waveform_blocks = 1;
break;
case SCE_NGS2_RACK_ID_SUBMIXER:
option.submixer.max_channels = 2;
option.submixer.max_inputs = 1;
break;
case SCE_NGS2_RACK_ID_MASTERING:
option.mastering.max_channels = 2;
break;
default:
option.reverb.max_channels = 2;
option.reverb.reverb_size = 1;
break;
}
Ngs2ContextBufferInfo query{};
Require(sceNgs2RackQueryBufferSize(rackId, &option.common, &query) == SCE_NGS2_OK);
const auto info = Buffer(query);
uintptr_t rack = 0;
Require(sceNgs2RackCreate(system, rackId, &option.common, &info, &rack) == SCE_NGS2_OK && rack != 0);
return rack;
}
void CheckOutOfRange(uintptr_t rack, std::uint32_t maxVoices) {
uintptr_t last = 0;
Require(sceNgs2RackGetVoiceHandle(rack, maxVoices - 1, &last) == SCE_NGS2_OK && last != 0);
for (const auto index : std::array{maxVoices, maxVoices + 1, std::numeric_limits<std::uint32_t>::max()}) {
uintptr_t output = 0x5a5a;
Require(sceNgs2RackGetVoiceHandle(rack, index, &output) == SCE_NGS2_ERROR_INVALID_VOICE_INDEX);
Require(output == 0x5a5a);
}
Require(sceNgs2RackGetVoiceHandle(rack, maxVoices, nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
uintptr_t again = 0;
Require(sceNgs2RackGetVoiceHandle(rack, maxVoices - 1, &again) == SCE_NGS2_OK && again == last);
}
}
int main() {
const auto system = CreateSystem();
for (const auto rackId : std::array{SCE_NGS2_RACK_ID_SAMPLER, SCE_NGS2_RACK_ID_SUBMIXER,
SCE_NGS2_RACK_ID_MASTERING, SCE_NGS2_RACK_ID_REVERB}) {
const auto rack = CreateSingleVoiceRack(system, rackId);
CheckOutOfRange(rack, 1);
Require(sceNgs2RackDestroy(rack, nullptr) == SCE_NGS2_OK);
}
const auto sampler = CreateRack(system, SCE_NGS2_RACK_ID_SAMPLER);
Ngs2RackInfo info{};
Require(sceNgs2RackGetInfo(sampler, &info, sizeof(info)) == SCE_NGS2_OK && info.max_voices == 256);
CheckOutOfRange(sampler, info.max_voices);
uintptr_t output = 0x5a5a;
Require(sceNgs2RackGetVoiceHandle(0x1234, 0, &output) == SCE_NGS2_ERROR_INVALID_RACK_HANDLE && output == 0x5a5a);
Require(sceNgs2RackDestroy(sampler, nullptr) == SCE_NGS2_OK);
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
std::puts("NGS2 voice index: PASS");
}