Merge pull request #812 from adriadam10/feat/ngs2-system-state
feat(ngs2): system sample rate, user data and lock
This commit is contained in:
commit
2f7c9e5914
|
|
@ -130,6 +130,7 @@ struct Ngs2System {
|
|||
Ngs2BufferAllocator allocator{};
|
||||
std::uint32_t uid = 0;
|
||||
std::int64_t renderCount = 0;
|
||||
std::uintptr_t userData = 0;
|
||||
std::vector<Ngs2Rack*> racks;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,55 @@ int APS5_VABI sceNgs2SystemSetGrainSamples(uintptr_t system_handle, uint32_t num
|
|||
return SCE_NGS2_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNgs2SystemSetSampleRate(uintptr_t system_handle, uint32_t sample_rate) {
|
||||
std::lock_guard lock(Ngs2Mutex());
|
||||
auto* system = Ngs2FindSystem(system_handle);
|
||||
if (system == nullptr) return SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
|
||||
if (sample_rate == 0) APS5_INVALID_ARG_EX;
|
||||
for (const auto* rack : system->racks) {
|
||||
for (const auto& voice : rack->voices) {
|
||||
for (const auto& filter : voice.filters) {
|
||||
if (filter.enabled && sample_rate != system->option.sample_rate) {
|
||||
throw std::runtime_error("NGS2: changing the sample rate under an enabled sampler filter is not implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
system->option.sample_rate = sample_rate;
|
||||
return SCE_NGS2_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNgs2SystemSetUserData(uintptr_t system_handle, uintptr_t user_data) {
|
||||
std::lock_guard lock(Ngs2Mutex());
|
||||
auto* system = Ngs2FindSystem(system_handle);
|
||||
if (system == nullptr) return SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
|
||||
system->userData = user_data;
|
||||
return SCE_NGS2_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNgs2SystemGetUserData(uintptr_t system_handle, uintptr_t* user_data) {
|
||||
std::lock_guard lock(Ngs2Mutex());
|
||||
const auto* system = Ngs2FindSystem(system_handle);
|
||||
if (system == nullptr) return SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
|
||||
if (user_data == nullptr) APS5_INVALID_ARG_EX;
|
||||
*user_data = system->userData;
|
||||
return SCE_NGS2_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNgs2SystemLock(uintptr_t system_handle) {
|
||||
Ngs2Mutex().lock();
|
||||
if (Ngs2FindSystem(system_handle) != nullptr) return SCE_NGS2_OK;
|
||||
Ngs2Mutex().unlock();
|
||||
return SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNgs2SystemUnlock(uintptr_t system_handle) {
|
||||
std::lock_guard lock(Ngs2Mutex());
|
||||
if (Ngs2FindSystem(system_handle) == nullptr) return SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
|
||||
Ngs2Mutex().unlock();
|
||||
return SCE_NGS2_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNgs2SystemRender(uintptr_t system_handle, const Ngs2RenderBufferInfo* buffer_info, uint32_t num_buffer_info) {
|
||||
std::lock_guard lock(Ngs2Mutex());
|
||||
auto* system = Ngs2FindSystem(system_handle);
|
||||
|
|
|
|||
|
|
@ -173,10 +173,28 @@ static void TestRejectedParams() {
|
|||
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
|
||||
}
|
||||
|
||||
static void TestRateChangeUnderFilter() {
|
||||
const auto system = CreateSystem();
|
||||
const auto sampler = Sampler(system, std::vector<std::int16_t>(Grain, 0), 1, 48000);
|
||||
SetFilter(sampler, Filter(0, 1, 1, 0, 1000.0f, 1.0f, 1.0f));
|
||||
Require(sceNgs2SystemSetSampleRate(system, 48000) == SCE_NGS2_OK);
|
||||
bool rejected = false;
|
||||
try {
|
||||
sceNgs2SystemSetSampleRate(system, 96000);
|
||||
} catch (const std::runtime_error&) {
|
||||
rejected = true;
|
||||
}
|
||||
Require(rejected);
|
||||
SetFilter(sampler, Filter(0, 1, 0, 0, 0.0f, 1.0f, 1.0f));
|
||||
Require(sceNgs2SystemSetSampleRate(system, 96000) == SCE_NGS2_OK);
|
||||
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
|
||||
}
|
||||
|
||||
int main() {
|
||||
TestLowPassAndTail();
|
||||
TestBypassMaskAndGain();
|
||||
TestSystemRateAndZeroCutoff();
|
||||
TestRejectedParams();
|
||||
TestRateChangeUnderFilter();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
#include "Ngs2Test.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
static uintptr_t Sampler(uintptr_t system, const std::vector<std::int16_t>& pcm, std::uint32_t repeats) {
|
||||
|
|
@ -152,6 +156,71 @@ static std::int32_t APS5_VABI Release(Ngs2ContextBufferInfo* info) {
|
|||
return SCE_NGS2_OK;
|
||||
}
|
||||
|
||||
static void TestSampleRate() {
|
||||
const auto system = CreateSystem();
|
||||
Require(sceNgs2SystemSetSampleRate(0x1234, 96000) == SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE);
|
||||
bool threw = false;
|
||||
try {
|
||||
sceNgs2SystemSetSampleRate(system, 0);
|
||||
} catch (const std::invalid_argument&) {
|
||||
threw = true;
|
||||
}
|
||||
Require(threw);
|
||||
Require(sceNgs2SystemSetSampleRate(system, 96000) == SCE_NGS2_OK);
|
||||
Ngs2SystemInfo info{};
|
||||
Require(sceNgs2SystemGetInfo(system, &info, sizeof(info)) == SCE_NGS2_OK && info.sample_rate == 96000);
|
||||
|
||||
const auto master = Mastering(system, 1);
|
||||
const std::vector<std::int16_t> pcm{0, 1000, 2000, 3000};
|
||||
const auto sampler = Sampler(system, pcm, 1);
|
||||
Patch(sampler, master);
|
||||
Event(sampler, SCE_NGS2_VOICE_EVENT_PLAY);
|
||||
const std::int16_t expected[Grain] = {0, 500, 1000, 1500, 2000, 2500, 3000, 1500};
|
||||
const auto out = RenderI16(system);
|
||||
for (std::uint32_t i = 0; i < Grain; i++) Require(out[i] == expected[i]);
|
||||
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
|
||||
}
|
||||
|
||||
static void TestUserData() {
|
||||
uintptr_t value = 1;
|
||||
Require(sceNgs2SystemSetUserData(0x1234, 5) == SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE);
|
||||
Require(sceNgs2SystemGetUserData(0x1234, &value) == SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE && value == 1);
|
||||
const auto system = CreateSystem();
|
||||
Require(sceNgs2SystemGetUserData(system, &value) == SCE_NGS2_OK && value == 0);
|
||||
Require(sceNgs2SystemSetUserData(system, 0xfedcba9876543210) == SCE_NGS2_OK);
|
||||
Require(sceNgs2SystemGetUserData(system, &value) == SCE_NGS2_OK && value == 0xfedcba9876543210);
|
||||
bool threw = false;
|
||||
try {
|
||||
sceNgs2SystemGetUserData(system, nullptr);
|
||||
} catch (const std::invalid_argument&) {
|
||||
threw = true;
|
||||
}
|
||||
Require(threw);
|
||||
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
|
||||
}
|
||||
|
||||
static void TestLock() {
|
||||
const auto system = CreateSystem();
|
||||
Require(sceNgs2SystemLock(0x1234) == SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE);
|
||||
Require(sceNgs2SystemUnlock(0x1234) == SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE);
|
||||
Ngs2SystemInfo info{};
|
||||
std::thread free([&] { Require(sceNgs2SystemGetInfo(system, &info, sizeof(info)) == SCE_NGS2_OK); });
|
||||
free.join();
|
||||
|
||||
Require(sceNgs2SystemLock(system) == SCE_NGS2_OK);
|
||||
std::atomic<bool> done = false;
|
||||
std::thread blocked([&] {
|
||||
Require(sceNgs2SystemGetInfo(system, &info, sizeof(info)) == SCE_NGS2_OK);
|
||||
done = true;
|
||||
});
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
Require(!done);
|
||||
Require(sceNgs2SystemUnlock(system) == SCE_NGS2_OK);
|
||||
blocked.join();
|
||||
Require(done);
|
||||
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
|
||||
}
|
||||
|
||||
static void TestAllocator() {
|
||||
const Ngs2BufferAllocator allocator{Allocate, Release, 9};
|
||||
uintptr_t system = 0;
|
||||
|
|
@ -171,6 +240,9 @@ int main() {
|
|||
TestPcmBlockEnd();
|
||||
TestPitchAndRepeat();
|
||||
TestSubmixerMatrix();
|
||||
TestSampleRate();
|
||||
TestUserData();
|
||||
TestLock();
|
||||
TestAllocator();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ int APS5_VABI sceNgs2SystemCreateWithAllocator(const Ngs2SystemOption*, const Ng
|
|||
int APS5_VABI sceNgs2SystemDestroy(uintptr_t, Ngs2ContextBufferInfo*);
|
||||
int APS5_VABI sceNgs2SystemGetInfo(uintptr_t, Ngs2SystemInfo*, size_t);
|
||||
int APS5_VABI sceNgs2SystemSetGrainSamples(uintptr_t, uint32_t);
|
||||
int APS5_VABI sceNgs2SystemSetSampleRate(uintptr_t, uint32_t);
|
||||
int APS5_VABI sceNgs2SystemSetUserData(uintptr_t, uintptr_t);
|
||||
int APS5_VABI sceNgs2SystemGetUserData(uintptr_t, uintptr_t*);
|
||||
int APS5_VABI sceNgs2SystemLock(uintptr_t);
|
||||
int APS5_VABI sceNgs2SystemUnlock(uintptr_t);
|
||||
int APS5_VABI sceNgs2SystemRender(uintptr_t, const Ngs2RenderBufferInfo*, uint32_t);
|
||||
int APS5_VABI sceNgs2RackQueryBufferSize(uint32_t, const Ngs2RackOption*, Ngs2ContextBufferInfo*);
|
||||
int APS5_VABI sceNgs2RackCreate(uintptr_t, uint32_t, const Ngs2RackOption*, const Ngs2ContextBufferInfo*, uintptr_t*);
|
||||
|
|
@ -40,7 +45,7 @@ inline void Check(bool value, int line) {
|
|||
|
||||
inline constexpr std::uint32_t Grain = 8;
|
||||
|
||||
inline std::vector<std::uint64_t> buffers[16];
|
||||
inline std::vector<std::uint64_t> buffers[32];
|
||||
inline std::size_t usedBuffers = 0;
|
||||
|
||||
inline Ngs2ContextBufferInfo Buffer(const Ngs2ContextBufferInfo& query) {
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [sceSystemServiceDisableMediaPlay](../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - unknown signature
|
||||
- [sceSystemServiceReenableMediaPlay](../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - unknown signature
|
||||
- [sceSystemServiceParamGetString](../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the system name (parameter 6) is the fixed `PS5`, not the console's name; other parameters and buffers of 1 to 64 bytes throw
|
||||
- [sceNgs2SystemSetSampleRate, sceNgs2SystemSetUserData, sceNgs2SystemGetUserData, sceNgs2SystemLock, sceNgs2SystemUnlock](../../core/libs/prx/libSceNgs2.native/src/System.cpp) (libSceNgs2.native) - shadPS4 only checks the system handle (`INVALID_SYSTEM_HANDLE`) and SharpProspero names what each call does; neither gives the other limits. A sample rate of 0 and a null user data pointer throw, any other rate is accepted, and changing the rate while a sampler filter is enabled throws because the filter coefficients were computed for the old rate. Lock and unlock take the same lock as `sceNgs2RackLock`
|
||||
- [sceVoiceSetMuteFlag](../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - unknown signature
|
||||
- [sceRtcFormatRFC2822, sceRtcFormatRFC2822LocalTime, sceRtcFormatRFC3339LocalTime](../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - only shadPS4 (PS4) has bodies. A null tick returns `INVALID_POINTER` like the existing `sceRtcFormatRFC3339`, where shadPS4 formats the current time instead; offsets outside ±1439 minutes return `INVALID_VALUE` like `sceRtcFormatRFC3339`; the local-time variants use the host's offset at the given instant (shadPS4 uses `sceKernelGettimezone`, the current offset)
|
||||
- [snwprintf_s](../../core/libs/prx/libc/src/FormattingWide.cpp) (libc) - follows C11 K.3.9.1.3 with `RSIZE_MAX` assumed to be `SIZE_MAX >> 1`. A runtime-constraint violation only returns a negative value: `set_constraint_handler_s` is not exported and the console's default handler is unknown. An invalid multibyte `%s` argument is copied byte by byte instead of being reported as an encoding error, as in `vswprintf`
|
||||
|
|
|
|||
Loading…
Reference in New Issue