Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions core/libs/prx/libSceNgs2.native/include/Ngs2Types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,18 @@ struct Ngs2CustomRackModuleInfo {
};
static_assert(sizeof(Ngs2CustomRackModuleInfo) == 40);

struct Ngs2CustomModuleInfo {
std::uint32_t module_id;
std::uint32_t source_buffer_id;
std::uint32_t extra_buffer_id;
std::uint32_t dest_buffer_id;
std::uint32_t state_offset;
std::uint32_t state_size;
std::uint32_t reserved;
std::uint32_t reserved2;
};
static_assert(sizeof(Ngs2CustomModuleInfo) == 32);

struct Ngs2CustomRackPortInfo {
std::uint32_t source_buffer_id;
std::uint32_t reserved;
Expand Down Expand Up @@ -635,6 +647,12 @@ struct Ngs2VoicePortInfo {
};
static_assert(sizeof(Ngs2VoicePortInfo) == 24);

struct Ngs2VoiceMatrixInfo {
std::uint32_t num_levels;
float levels[64];
};
static_assert(sizeof(Ngs2VoiceMatrixInfo) == 260);

struct Ngs2VoiceState {
std::uint32_t state_flags;
std::int32_t error_code;
Expand Down
3 changes: 3 additions & 0 deletions core/libs/prx/libSceNgs2.native/src/Custom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ static Ngs2UserFx2SetupContext UserFxContext(Ngs2Rack& rack, std::size_t module,

void Ngs2SetupUserFx(Ngs2Rack& rack, const Ngs2CustomRackOption& option) {
rack.customModuleIds.resize(option.num_modules);
rack.customModules.resize(option.num_modules);
rack.userFx.resize(option.num_modules);
rack.userFxCommon.resize(option.num_modules);
for (auto& voice : rack.voices) {
Expand All @@ -100,6 +101,8 @@ void Ngs2SetupUserFx(Ngs2Rack& rack, const Ngs2CustomRackOption& option) {
}
for (std::uint32_t m = 0; m < option.num_modules; m++) {
rack.customModuleIds[m] = option.module[m].module_id;
const auto& info = option.module[m];
rack.customModules[m] = {info.module_id, info.source_buffer_id, info.extra_buffer_id, info.dest_buffer_id, info.state_offset, info.state_size, 0, 0};
if (option.module[m].module_id == SCE_NGS2_CUSTOM_MODULE_ID_FILTER) continue;
if (option.module[m].module_id == SCE_NGS2_CUSTOM_MODULE_ID_USER_FX2) {
const auto& module = *reinterpret_cast<const Ngs2CustomUserFx2ModuleOption*>(option.module[m].option);
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libSceNgs2.native/src/Ngs2Internal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ struct Ngs2Rack {
std::vector<Ngs2CustomUserFx2ModuleOption> userFx;
std::vector<std::vector<std::uint8_t>> userFxCommon;
std::vector<std::uint32_t> customModuleIds;
std::vector<Ngs2CustomModuleInfo> customModules;
std::thread::id lockOwner{};
std::uint32_t lockDepth = 0;
};
Expand Down
11 changes: 11 additions & 0 deletions core/libs/prx/libSceNgs2.native/src/Rack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,17 @@ int APS5_VABI sceNgs2RackGetInfo(uintptr_t rack_handle, Ngs2RackInfo* info, size
return SCE_NGS2_OK;
}

int APS5_VABI sceNgs2CustomRackGetModuleInfo(uintptr_t rack_handle, uint32_t module_index, Ngs2CustomModuleInfo* info, size_t info_size) {
if (info == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
const Ngs2Lock lock;
const auto* rack = Ngs2FindRack(rack_handle);
if (rack == nullptr) return SCE_NGS2_ERROR_INVALID_RACK_HANDLE;
if (module_index >= rack->customModules.size() || info_size < sizeof(Ngs2CustomModuleInfo)) APS5_INVALID_ARG_EX;
if (info_size != sizeof(Ngs2CustomModuleInfo)) throw std::runtime_error("NGS2: custom module info of " + std::to_string(info_size) + " bytes is not implemented");
*info = rack->customModules[module_index];
return SCE_NGS2_OK;
}

int APS5_VABI sceNgs2RackSetUserData(uintptr_t rack_handle, uintptr_t user_data) {
std::lock_guard lock(Ngs2Mutex());
auto* rack = Ngs2FindRack(rack_handle);
Expand Down
14 changes: 14 additions & 0 deletions core/libs/prx/libSceNgs2.native/src/Voice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,20 @@ int APS5_VABI sceNgs2VoiceGetOwner(uintptr_t voice_handle, uintptr_t* owner_rack
return SCE_NGS2_OK;
}

int APS5_VABI sceNgs2VoiceGetMatrixInfo(uintptr_t voice_handle, uint32_t matrix_id, Ngs2VoiceMatrixInfo* info, size_t info_size) {
if (info == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
if (info_size != sizeof(Ngs2VoiceMatrixInfo)) return SCE_NGS2_ERROR_INVALID_OUT_SIZE;
const Ngs2Lock lock;
const auto* found = Ngs2FindVoice(voice_handle);
if (found == nullptr) return SCE_NGS2_ERROR_INVALID_VOICE_HANDLE;
if (matrix_id >= found->matrices.size()) APS5_INVALID_ARG_EX;
const auto& levels = found->matrices[matrix_id];
*info = {};
info->num_levels = static_cast<std::uint32_t>(levels.size());
std::copy(levels.begin(), levels.end(), info->levels);
return SCE_NGS2_OK;
}

int APS5_VABI sceNgs2PanInit(Ngs2PanWork* work, const float* speaker_angles, float unit_angle, uint32_t num_speakers) {
return Ngs2PanInit(work, speaker_angles, unit_angle, num_speakers);
}
Expand Down
57 changes: 57 additions & 0 deletions core/libs/prx/libSceNgs2.native/tests/Ngs2Custom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,62 @@ static void TestCustomGain() {
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
}

template <typename TException>
static bool ModuleInfoThrows(uintptr_t rack, std::uint32_t moduleIndex, std::size_t infoSize) {
Ngs2CustomModuleInfo info;
std::memset(&info, 0xa5, sizeof(info));
bool threw = false;
try {
sceNgs2CustomRackGetModuleInfo(rack, moduleIndex, &info, infoSize);
} catch (const TException&) {
threw = true;
}
const auto* bytes = reinterpret_cast<const unsigned char*>(&info);
for (std::size_t i = 0; i < sizeof(info); i++) Require(bytes[i] == 0xa5);
return threw;
}

static void TestCustomModuleInfo() {
const auto system = CreateSystem();
const Ngs2CustomUserFx2ModuleOption userFx2{{sizeof(Ngs2CustomUserFx2ModuleOption)}, nullptr, nullptr, nullptr, Gain, sizeof(float), sizeof(float), 8, 0x77};
const Ngs2CustomUserFx2ModuleOption modules[2] = {userFx2, userFx2};
auto option = RackOption(modules, 2);
auto& first = option.custom_rack_option.module[0];
first.state_offset = 0x40;
first.reserved = 0x11;
first.reserved2 = 0x22;
auto& filter = option.custom_rack_option.module[1];
filter.option = nullptr;
filter.module_id = SCE_NGS2_CUSTOM_MODULE_ID_FILTER;
filter.state_size = 0;
const auto rack = CustomRack(system, option);

Ngs2CustomModuleInfo info;
std::memset(&info, 0xa5, sizeof(info));
Require(sceNgs2CustomRackGetModuleInfo(rack, 0, &info, sizeof(info)) == SCE_NGS2_OK);
Require(info.module_id == SCE_NGS2_CUSTOM_MODULE_ID_USER_FX2 && info.source_buffer_id == 0 && info.extra_buffer_id == 0 && info.dest_buffer_id == 0);
Require(info.state_offset == 0x40 && info.state_size == 16 && info.reserved == 0 && info.reserved2 == 0);
std::memset(&info, 0xa5, sizeof(info));
Require(sceNgs2CustomRackGetModuleInfo(rack, 1, &info, sizeof(info)) == SCE_NGS2_OK);
Require(info.module_id == SCE_NGS2_CUSTOM_MODULE_ID_FILTER && info.state_offset == 0 && info.state_size == 0 && info.reserved == 0 && info.reserved2 == 0);

Require(sceNgs2CustomRackGetModuleInfo(rack, 0, nullptr, sizeof(info)) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2CustomRackGetModuleInfo(0x1234, 0, nullptr, sizeof(info)) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
std::memset(&info, 0xa5, sizeof(info));
Require(sceNgs2CustomRackGetModuleInfo(0x1234, 0, &info, sizeof(info)) == SCE_NGS2_ERROR_INVALID_RACK_HANDLE);
Require(sceNgs2CustomRackGetModuleInfo(system, 0, &info, sizeof(info)) == SCE_NGS2_ERROR_INVALID_RACK_HANDLE);
const auto* bytes = reinterpret_cast<const unsigned char*>(&info);
for (std::size_t i = 0; i < sizeof(info); i++) Require(bytes[i] == 0xa5);
Require(ModuleInfoThrows<std::invalid_argument>(rack, 2, sizeof(Ngs2CustomModuleInfo)));
Require(ModuleInfoThrows<std::invalid_argument>(rack, 0, sizeof(Ngs2CustomModuleInfo) - 1));
Require(ModuleInfoThrows<std::runtime_error>(rack, 0, sizeof(Ngs2CustomModuleInfo) + 8));
Require(ModuleInfoThrows<std::invalid_argument>(CreateRack(system, SCE_NGS2_RACK_ID_SUBMIXER), 0, sizeof(Ngs2CustomModuleInfo)));

Require(sceNgs2RackDestroy(rack, nullptr) == SCE_NGS2_OK);
Require(sceNgs2CustomRackGetModuleInfo(rack, 0, &info, sizeof(info)) == SCE_NGS2_ERROR_INVALID_RACK_HANDLE);
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
}

int main() {
TestUserFxChain();
TestRackOptions();
Expand All @@ -671,5 +727,6 @@ int main() {
TestCustomFilter();
TestCustomLegacyUserFx();
TestCustomGain();
TestCustomModuleInfo();
return 0;
}
2 changes: 2 additions & 0 deletions core/libs/prx/libSceNgs2.native/tests/Ngs2Test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ int APS5_VABI sceNgs2VoiceRunCommands(uintptr_t, const Ngs2VoiceCommand*, size_t
int APS5_VABI sceNgs2VoiceGetState(uintptr_t, Ngs2VoiceState*, size_t);
int APS5_VABI sceNgs2VoiceGetStateFlags(uintptr_t, uint32_t*);
int APS5_VABI sceNgs2VoiceGetOwner(uintptr_t, uintptr_t*, uint32_t*);
int APS5_VABI sceNgs2VoiceGetMatrixInfo(uintptr_t, uint32_t, Ngs2VoiceMatrixInfo*, size_t);
int APS5_VABI sceNgs2CustomRackGetModuleInfo(uintptr_t, uint32_t, Ngs2CustomModuleInfo*, size_t);
int APS5_VABI sceNgs2VoiceGetPortInfo(uintptr_t, uint32_t, Ngs2VoicePortInfo*, size_t);
int APS5_VABI sceNgs2VoiceQueryInfo(uintptr_t, uint32_t, void*, size_t);
int APS5_VABI sceNgs2GeomResetListenerParam(Ngs2GeomListenerParam*);
Expand Down
49 changes: 49 additions & 0 deletions core/libs/prx/libSceNgs2.native/tests/Ngs2VoiceHandles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,54 @@ static void Invalid(std::string_view api, uintptr_t handle) {
Require(std::all_of(output.begin(), output.end(), [](auto byte) { return byte == 0xa5; }));
}

template <typename TException>
static bool MatrixInfoThrows(uintptr_t voice, std::uint32_t matrixId) {
Ngs2VoiceMatrixInfo info;
std::memset(&info, 0xa5, sizeof(info));
bool threw = false;
try {
sceNgs2VoiceGetMatrixInfo(voice, matrixId, &info, sizeof(info));
} catch (const TException&) {
threw = true;
}
const auto* bytes = reinterpret_cast<const unsigned char*>(&info);
Require(std::all_of(bytes, bytes + sizeof(info), [](auto byte) { return byte == 0xa5; }));
return threw;
}

static void MatrixInfo() {
Ngs2VoiceMatrixInfo info;
std::memset(&info, 0xa5, sizeof(info));
Status(sceNgs2VoiceGetMatrixInfo(0x1234, 0, &info, sizeof(info)), InvalidVoice, "GetMatrixInfo accepted an invalid voice");
const auto* bytes = reinterpret_cast<const unsigned char*>(&info);
Require(std::all_of(bytes, bytes + sizeof(info), [](auto byte) { return byte == 0xa5; }));
Status(sceNgs2VoiceGetMatrixInfo(0x1234, 0, nullptr, sizeof(info)), SCE_NGS2_ERROR_INVALID_OUT_ADDRESS, "GetMatrixInfo checked the voice before null output");
Status(sceNgs2VoiceGetMatrixInfo(0x1234, 0, &info, sizeof(info) - 1), SCE_NGS2_ERROR_INVALID_OUT_SIZE, "GetMatrixInfo checked the voice before the output size");
const auto system = CreateSystem();
const auto rack = CreateRack(system, SCE_NGS2_RACK_ID_SAMPLER);
const auto voice = Voice(rack);
Status(sceNgs2VoiceGetMatrixInfo(voice, 0, &info, sizeof(info)), SCE_NGS2_OK, "GetMatrixInfo failed for a matrix without levels");
Require(info.num_levels == 0 && std::all_of(std::begin(info.levels), std::end(info.levels), [](float level) { return level == 0.0f; }));
const float levels[4] = {1.0f, 0.5f, -5.0f, 6.0f};
Control(voice, SCE_NGS2_VOICE_PARAM_MATRIX_LEVELS, Ngs2VoiceMatrixLevelsParam{{}, 0, 4, levels});
std::memset(&info, 0xa5, sizeof(info));
Status(sceNgs2VoiceGetMatrixInfo(voice, 0, &info, sizeof(info)), SCE_NGS2_OK, "GetMatrixInfo failed for a matrix with levels");
Require(info.num_levels == 4 && info.levels[0] == 1.0f && info.levels[1] == 0.5f && info.levels[2] == -4.0f && info.levels[3] == 4.0f);
Require(std::all_of(std::begin(info.levels) + 4, std::end(info.levels), [](float level) { return level == 0.0f; }));
std::array<float, 64> full{};
for (std::size_t i = 0; i < full.size(); i++) full[i] = static_cast<float>(i) / 64.0f;
Control(voice, SCE_NGS2_VOICE_PARAM_MATRIX_LEVELS, Ngs2VoiceMatrixLevelsParam{{}, 0, 64, full.data()});
Status(sceNgs2VoiceGetMatrixInfo(voice, 0, &info, sizeof(info)), SCE_NGS2_OK, "GetMatrixInfo failed for 64 levels");
Require(info.num_levels == 64 && std::equal(full.begin(), full.end(), info.levels));
Status(sceNgs2VoiceGetMatrixInfo(voice, 0, &info, sizeof(info) + 1), SCE_NGS2_ERROR_INVALID_OUT_SIZE, "GetMatrixInfo accepted a larger output");
Require(MatrixInfoThrows<std::invalid_argument>(voice, 1));
const auto master = Voice(CreateRack(system, SCE_NGS2_RACK_ID_MASTERING));
Require(MatrixInfoThrows<std::invalid_argument>(master, 0));
Require(sceNgs2RackDestroy(rack, nullptr) == SCE_NGS2_OK);
Status(sceNgs2VoiceGetMatrixInfo(voice, 0, &info, sizeof(info)), InvalidVoice, "GetMatrixInfo accepted a voice of a destroyed rack");
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
}

int main(int argc, char** argv) {
try {
if (argc > 2 || (argc == 2 && std::find(Names.begin(), Names.end(), std::string_view(argv[1])) == Names.end())) throw std::runtime_error("unknown voice API test selection");
Expand Down Expand Up @@ -80,6 +128,7 @@ int main(int argc, char** argv) {
Require(sceNgs2RackDestroy(rack, nullptr) == SCE_NGS2_OK);
for (auto name : Names) if (selected(name)) Invalid(name, voice);
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
if (argc == 1) MatrixInfo();
std::cout << "NGS2 invalid voice errors, outputs and precedence checked\n";
return 0;
} catch (const std::exception& error) {
Expand Down
8 changes: 0 additions & 8 deletions core/libs/prx/libSceNgs2/PendingExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@

extern "C" {

int APS5_VABI sceNgs2CustomRackGetModuleInfo(std::uintptr_t rackHandle, std::uint32_t moduleIndex, void* outInfo, std::size_t infoSize) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2FftInit() {
NotImplemented_nid_no_patch(__func__);
return 0;
Expand All @@ -33,9 +29,5 @@ int APS5_VABI sceNgs2SystemEnumRackHandles(std::uintptr_t systemHandle, std::uin
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2VoiceGetMatrixInfo(std::uintptr_t voiceHandle, std::uint32_t matrixId, void* outInfo, std::size_t infoSize) {
NotImplemented_nid_no_patch(__func__);
return 0;
}

}
2 changes: 1 addition & 1 deletion docs/dev/debt/libSceNgs2.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
### Unknown function info

- [sceNgs2FftInit, sceNgs2FftProcess, sceNgs2FftQuerySize](../../../core/libs/prx/libSceNgs2/PendingExports.cpp) (libSceNgs2) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNgs2.c), but no credible prototypes were found in the cited sources. They are declared without parameters solely to resolve known imports and make no ABI claim.
- [sceNgs2CustomRackGetModuleInfo, sceNgs2ParseWaveformUser, sceNgs2SystemEnumHandles, sceNgs2SystemEnumRackHandles, sceNgs2VoiceGetMatrixInfo](../../../core/libs/prx/libSceNgs2/PendingExports.cpp) (libSceNgs2) - names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNgs2.c); signatures follow [SharpProspero's Ngs2 bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/Ngs2.cs), not measured on a console. Opaque structure pointers use `void*`; these stubs throw for import resolution.
- [sceNgs2ParseWaveformUser, sceNgs2SystemEnumHandles, sceNgs2SystemEnumRackHandles](../../../core/libs/prx/libSceNgs2/PendingExports.cpp) (libSceNgs2) - names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNgs2.c); signatures follow [SharpProspero's Ngs2 bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/Ngs2.cs), not measured on a console. Opaque structure pointers use `void*`; these stubs throw for import resolution.
Loading
Loading