feat(libSceNgs2.native): implement system, racks, voices and PCM rendering

libSceNgs2 keeps its stubs; its types now come from the .native header.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Adrià Franch 2026-10-02 06:21:07 +02:00
parent c00bc19c0d
commit 90295d3733
12 changed files with 1734 additions and 544 deletions

View File

@ -511,138 +511,6 @@ struct AudioPropagationSystemMemory {
std::size_t size_gpu_mem;
};
using Ngs2Handle = std::uintptr_t;
using Ngs2BufferAllocHandler = std::int32_t (*)(void*);
using Ngs2BufferFreeHandler = std::int32_t (*)(void*);
struct Ngs2ContextBufferInfo {
void* host_buffer;
std::size_t host_buffer_size;
std::uintptr_t reserved[5];
std::uintptr_t user_data;
};
struct Ngs2BufferAllocator {
Ngs2BufferAllocHandler alloc_handler;
Ngs2BufferFreeHandler free_handler;
std::uintptr_t user_data;
};
struct Ngs2SystemOption {
std::size_t size;
char name[16];
std::uint32_t flags;
std::uint32_t max_grain_samples;
std::uint32_t num_grain_samples;
std::uint32_t sample_rate;
std::uint32_t reserved[6];
};
struct Ngs2SystemInfo {
char name[16];
Ngs2Handle system_handle;
Ngs2ContextBufferInfo buffer_info;
std::uint32_t uid;
std::uint32_t min_grain_samples;
std::uint32_t max_grain_samples;
std::uint32_t state_flags;
std::uint32_t rack_count;
float last_render_ratio;
std::int64_t last_render_tick;
std::int64_t render_count;
std::uint32_t sample_rate;
std::uint32_t num_grain_samples;
};
struct Ngs2RackOption {
std::size_t size;
char name[16];
std::uint32_t flags;
std::uint32_t max_grain_samples;
std::uint32_t max_voices;
std::uint32_t max_input_delay_blocks;
std::uint32_t max_matrices;
std::uint32_t max_ports;
std::uint32_t reserved[20];
};
struct Ngs2VoiceParamHeader {
std::uint16_t size;
std::int16_t next;
std::uint32_t id;
};
struct Ngs2RenderBufferInfo {
void* buffer;
std::size_t buffer_size;
std::uint32_t waveform_type;
std::uint32_t num_channels;
};
struct Ngs2VoiceState {
std::uint32_t state_flags;
};
struct Ngs2WaveformFormat {
std::uint32_t waveform_type;
std::uint32_t num_channels;
std::uint32_t sample_rate;
std::uint32_t config_data;
std::uint32_t frame_offset;
std::uint32_t frame_margin;
};
struct Ngs2WaveformBlock {
std::uint64_t data_offset;
std::uint64_t data_size;
std::uint32_t num_repeats;
std::uint32_t num_skip_samples;
std::uint32_t num_samples;
std::uint32_t reserved;
std::uintptr_t user_data;
};
struct Ngs2WaveformInfo {
Ngs2WaveformFormat format;
std::uint32_t data_offset;
std::uint32_t data_size;
std::uint32_t loop_begin_position;
std::uint32_t loop_end_position;
std::uint32_t num_samples;
std::uint32_t audio_unit_size;
std::uint32_t num_audio_unit_samples;
std::uint32_t num_audio_unit_per_frame;
std::uint32_t audio_frame_size;
std::uint32_t num_audio_frame_samples;
std::uint32_t num_delay_samples;
std::uint32_t num_blocks;
Ngs2WaveformBlock block[4];
};
struct Ngs2PanParam {
std::uint32_t reserved[16];
};
struct Ngs2PanWork {
std::uint32_t reserved[64];
};
struct Ngs2GeomListenerParam {
std::uint32_t reserved[32];
};
struct Ngs2GeomListenerWork {
std::uint32_t reserved[64];
};
struct Ngs2GeomSourceParam {
std::uint32_t reserved[32];
};
struct Ngs2GeomAttribute {
std::uint32_t reserved[32];
};
struct AvPlayerAudio {
std::uint16_t channel_count;
std::uint8_t reserved1[2];

View File

@ -0,0 +1,32 @@
add_library(libSceNgs2.native SHARED EXCLUDE_FROM_ALL
${CMAKE_CURRENT_SOURCE_DIR}/src/Rack.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Render.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/System.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Unimplemented.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Voice.cpp
)
target_include_directories(libSceNgs2.native PRIVATE ${LIBS_INCLUDE_DIR})
set_target_properties(libSceNgs2.native PROPERTIES
CXX_EXTENSIONS OFF
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
)
configure_windows_unwind(libSceNgs2.native)
if(MINGW)
foreach(runtimeLinkFlag IN ITEMS -static-libgcc -static-libstdc++ -static)
string(REPLACE "${runtimeLinkFlag}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
endforeach()
endif()
if(BUILD_TESTING)
add_test_executable(ngs2_tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/Ngs2Render.cpp)
target_include_directories(ngs2_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(ngs2_tests PRIVATE libSceNgs2.native libkernel libc)
configure_windows_unwind(ngs2_tests)
add_test(NAME ngs2_render COMMAND ngs2_tests)
if(WIN32)
set_tests_properties(ngs2_render PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNgs2.native>")
endif()
endif()

View File

@ -1,218 +0,0 @@
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
extern "C" {
int APS5_VABI sceNgs2CalcWaveformBlock(const Ngs2WaveformFormat* format, uint32_t sample_pos, uint32_t num_samples, Ngs2WaveformBlock* block) {
(void)format;
(void)sample_pos;
(void)num_samples;
(void)block;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GeomApply(const Ngs2GeomListenerWork* listener, const Ngs2GeomSourceParam* source, Ngs2GeomAttribute* out_attrib, uint32_t flags) {
(void)listener;
(void)source;
(void)out_attrib;
(void)flags;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GeomCalcListener(const Ngs2GeomListenerParam* param, Ngs2GeomListenerWork* out_work, uint32_t flags) {
(void)param;
(void)out_work;
(void)flags;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GeomResetListenerParam(Ngs2GeomListenerParam* out_listener_param) {
(void)out_listener_param;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GeomResetSourceParam(Ngs2GeomSourceParam* out_source_param) {
(void)out_source_param;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2PanGetVolumeMatrix(Ngs2PanWork* work, const Ngs2PanParam* params, uint32_t num_params, uint32_t matrix_format, float* out_volume_matrix) {
(void)work;
(void)params;
(void)num_params;
(void)matrix_format;
(void)out_volume_matrix;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2PanInit(Ngs2PanWork* work, const float* speaker_angles, float unit_angle, uint32_t num_speakers) {
(void)work;
(void)speaker_angles;
(void)unit_angle;
(void)num_speakers;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2ParseWaveformData(const void* data, size_t data_size, Ngs2WaveformInfo* info) {
(void)data;
(void)data_size;
(void)info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackCreate(uintptr_t system_handle, uint32_t rack_id, const Ngs2RackOption* option, const Ngs2ContextBufferInfo* buffer_info, uintptr_t* handle) {
(void)system_handle;
(void)rack_id;
(void)option;
(void)buffer_info;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackCreateWithAllocator(uintptr_t system_handle, uint32_t rack_id, const Ngs2RackOption* option, const Ngs2BufferAllocator* allocator, uintptr_t* handle) {
(void)system_handle;
(void)rack_id;
(void)option;
(void)allocator;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackDestroy(uintptr_t rack_handle, Ngs2ContextBufferInfo* buffer_info) {
(void)rack_handle;
(void)buffer_info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackGetVoiceHandle(uintptr_t rack_handle, uint32_t voice_id, uintptr_t* handle) {
(void)rack_handle;
(void)voice_id;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackLock(uintptr_t rack_handle) {
(void)rack_handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackQueryBufferSize(uint32_t rack_id, const Ngs2RackOption* option, Ngs2ContextBufferInfo* buffer_info) {
(void)rack_id;
(void)option;
(void)buffer_info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackUnlock(uintptr_t rack_handle) {
(void)rack_handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemCreate(const Ngs2SystemOption* option, const Ngs2ContextBufferInfo* buffer_info, uintptr_t* handle) {
(void)option;
(void)buffer_info;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemCreateWithAllocator(const Ngs2SystemOption* option, const Ngs2BufferAllocator* allocator, uintptr_t* handle) {
(void)option;
(void)allocator;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemDestroy(uintptr_t system_handle, Ngs2ContextBufferInfo* buffer_info) {
(void)system_handle;
(void)buffer_info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemGetInfo(uintptr_t system_handle, Ngs2SystemInfo* info, size_t info_size) {
(void)system_handle;
(void)info;
(void)info_size;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemQueryBufferSize(const Ngs2SystemOption* option, Ngs2ContextBufferInfo* buffer_info) {
(void)option;
(void)buffer_info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemRender(uintptr_t system_handle, const Ngs2RenderBufferInfo* buffer_info, uint32_t num_buffer_info) {
(void)system_handle;
(void)buffer_info;
(void)num_buffer_info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemResetOption(Ngs2SystemOption* option) {
(void)option;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemSetGrainSamples(uintptr_t system_handle, uint32_t num_samples) {
(void)system_handle;
(void)num_samples;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamHeader* param_list) {
(void)voice_handle;
(void)param_list;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* state, size_t state_size) {
(void)voice_handle;
(void)state;
(void)state_size;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2VoiceGetStateFlags(uintptr_t voice_handle, uint32_t* state_flags) {
(void)voice_handle;
(void)state_flags;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2VoiceRunCommands(uintptr_t voice_handle, const void* commands, uint32_t num_commands, uint32_t flags) {
(void)voice_handle;
(void)commands;
(void)num_commands;
(void)flags;
NotImplemented_nid_no_patch(__func__);
return 0;
}
}

View File

@ -0,0 +1,361 @@
#ifndef CORE_LIBS_PRX_LIBSCENGS2_INCLUDE_NGS2TYPES_HPP
#define CORE_LIBS_PRX_LIBSCENGS2_INCLUDE_NGS2TYPES_HPP
#include <cstddef>
#include <cstdint>
#include "prx/libc/include/general/VabiMacros.hpp"
static constexpr int SCE_NGS2_OK = 0;
static constexpr int SCE_NGS2_ERROR_INVALID_OUT_ADDRESS = static_cast<int>(0x804A8010);
static constexpr int SCE_NGS2_ERROR_INVALID_OUT_SIZE = static_cast<int>(0x804A8011);
static constexpr int SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE = static_cast<int>(0x804A8201);
static constexpr int SCE_NGS2_ERROR_INVALID_RACK_HANDLE = static_cast<int>(0x804A8202);
static constexpr std::uint32_t SCE_NGS2_RACK_ID_SAMPLER = 0x1000;
static constexpr std::uint32_t SCE_NGS2_RACK_ID_SUBMIXER = 0x2000;
static constexpr std::uint32_t SCE_NGS2_RACK_ID_MASTERING = 0x3000;
static constexpr std::uint32_t SCE_NGS2_WAVEFORM_TYPE_PCM_I16L = 0x12;
static constexpr std::uint32_t SCE_NGS2_WAVEFORM_TYPE_PCM_F32L = 0x18;
static constexpr std::uint32_t SCE_NGS2_VOICE_EVENT_PLAY = 1;
static constexpr std::uint32_t SCE_NGS2_VOICE_EVENT_STOP = 2;
static constexpr std::uint32_t SCE_NGS2_VOICE_EVENT_STOP_IMM = 4;
static constexpr std::uint32_t SCE_NGS2_VOICE_EVENT_KILL = 8;
static constexpr std::uint32_t SCE_NGS2_VOICE_EVENT_PAUSE = 16;
static constexpr std::uint32_t SCE_NGS2_VOICE_EVENT_RESUME = 32;
static constexpr std::uint32_t SCE_NGS2_VOICE_STATE_FLAG_INUSE = 1;
static constexpr std::uint32_t SCE_NGS2_VOICE_STATE_FLAG_PLAYING = 2;
static constexpr std::uint32_t SCE_NGS2_VOICE_STATE_FLAG_PAUSED = 4;
static constexpr std::uint32_t SCE_NGS2_VOICE_STATE_FLAG_STOPPED = 8;
static constexpr std::uint32_t SCE_NGS2_VOICE_PARAM_MATRIX_LEVELS = 0x0001;
static constexpr std::uint32_t SCE_NGS2_VOICE_PARAM_PORT_VOLUME = 0x0002;
static constexpr std::uint32_t SCE_NGS2_VOICE_PARAM_PORT_MATRIX = 0x0003;
static constexpr std::uint32_t SCE_NGS2_VOICE_PARAM_PORT_DELAY = 0x0004;
static constexpr std::uint32_t SCE_NGS2_VOICE_PARAM_PATCH = 0x0005;
static constexpr std::uint32_t SCE_NGS2_VOICE_PARAM_EVENT = 0x0006;
static constexpr std::uint32_t SCE_NGS2_VOICE_PARAM_CALLBACK = 0x0007;
static constexpr std::uint32_t SCE_NGS2_SAMPLER_VOICE_PARAM_SETUP = 0x10000000;
static constexpr std::uint32_t SCE_NGS2_SAMPLER_VOICE_PARAM_ADD_WAVEFORM_BLOCKS = 0x10000001;
static constexpr std::uint32_t SCE_NGS2_SAMPLER_VOICE_PARAM_EXIT_LOOP = 0x10000004;
static constexpr std::uint32_t SCE_NGS2_SAMPLER_VOICE_PARAM_PITCH = 0x10000005;
static constexpr std::uint32_t SCE_NGS2_SUBMIXER_VOICE_PARAM_SETUP = 0x20000000;
static constexpr std::uint32_t SCE_NGS2_MASTERING_VOICE_PARAM_SETUP = 0x30000000;
static constexpr std::uint32_t SCE_NGS2_MASTERING_VOICE_PARAM_OUTPUT = 0x30000005;
static constexpr std::uint32_t SCE_NGS2_WAVEFORM_BLOCKS_FLAG_CONTINUE = 1;
static constexpr std::uint32_t SCE_NGS2_WAVEFORM_BLOCKS_FLAG_RESET = 4;
static constexpr std::uint32_t SCE_NGS2_VOICE_CALLBACK_FLAG_BLOCK_END = 1;
static constexpr std::uint32_t SCE_NGS2_VOICE_CALLBACK_FLAG_BLOCK_REPEAT = 2;
using Ngs2Handle = std::uintptr_t;
struct Ngs2ContextBufferInfo {
void* host_buffer;
std::size_t host_buffer_size;
std::uintptr_t reserved[5];
std::uintptr_t user_data;
};
static_assert(sizeof(Ngs2ContextBufferInfo) == 64);
using Ngs2BufferAllocHandler = std::int32_t (APS5_VABI *)(Ngs2ContextBufferInfo*);
using Ngs2BufferFreeHandler = std::int32_t (APS5_VABI *)(Ngs2ContextBufferInfo*);
struct Ngs2BufferAllocator {
Ngs2BufferAllocHandler alloc_handler;
Ngs2BufferFreeHandler free_handler;
std::uintptr_t user_data;
};
struct Ngs2SystemOption {
std::size_t size;
char name[64];
std::uintptr_t job_scheduler_options[4];
std::uint32_t flags;
std::uint32_t max_grain_samples;
std::uint32_t num_grain_samples;
std::uint32_t sample_rate;
std::uint32_t max_voice_channels;
std::uint32_t reserved[5];
};
static_assert(sizeof(Ngs2SystemOption) == 144);
struct Ngs2SystemInfo {
char name[64];
Ngs2Handle system_handle;
Ngs2ContextBufferInfo buffer_info;
std::uint32_t uid;
std::uint32_t min_grain_samples;
std::uint32_t max_grain_samples;
std::uint32_t state_flags;
std::uint32_t rack_count;
float last_render_ratio;
std::int64_t last_render_tick;
std::int64_t render_count;
std::uint32_t sample_rate;
std::uint32_t num_grain_samples;
};
static_assert(sizeof(Ngs2SystemInfo) == 184);
struct Ngs2RackOption {
std::size_t size;
char name[64];
std::uint32_t flags;
std::uint32_t max_grain_samples;
std::uint32_t max_voices;
std::uint32_t max_input_delay_blocks;
std::uint32_t max_matrices;
std::uint32_t max_ports;
std::uint32_t max_voice_channels;
std::uint32_t max_output_channels;
std::uint32_t reserved[18];
};
static_assert(sizeof(Ngs2RackOption) == 176);
struct Ngs2SamplerRackOption {
Ngs2RackOption rack_option;
std::uint32_t max_channel_works;
std::uint32_t max_codec_caches;
std::uint32_t max_waveform_blocks;
std::uint32_t max_envelope_points;
std::uint32_t max_filters;
std::uint32_t max_atrac9_decoders;
std::uint32_t max_atrac9_channel_works;
std::uint32_t max_ajm_atrac9_decoders;
std::uint32_t num_peak_meter_blocks;
};
struct Ngs2SubmixerRackOption {
Ngs2RackOption rack_option;
std::uint32_t max_channels;
std::uint32_t max_envelope_points;
std::uint32_t max_filters;
std::uint32_t max_inputs;
std::uint32_t num_peak_meter_blocks;
};
struct Ngs2MasteringRackOption {
Ngs2RackOption rack_option;
std::uint32_t max_channels;
std::uint32_t num_peak_meter_blocks;
};
struct Ngs2VoiceParamHeader {
std::uint16_t size;
std::int16_t next;
std::uint32_t id;
};
struct Ngs2VoiceMatrixLevelsParam {
Ngs2VoiceParamHeader header;
std::uint32_t matrix_id;
std::uint32_t num_levels;
const float* levels;
};
struct Ngs2VoicePortVolumeParam {
Ngs2VoiceParamHeader header;
std::uint32_t port;
float level;
};
struct Ngs2VoicePortMatrixParam {
Ngs2VoiceParamHeader header;
std::uint32_t port;
std::int32_t matrix_id;
};
struct Ngs2VoicePortDelayParam {
Ngs2VoiceParamHeader header;
std::uint32_t port;
std::uint32_t num_samples;
};
struct Ngs2VoicePatchParam {
Ngs2VoiceParamHeader header;
std::uint32_t port;
std::uint32_t dest_input_id;
Ngs2Handle dest_handle;
};
struct Ngs2VoiceEventParam {
Ngs2VoiceParamHeader header;
std::uint32_t event_id;
};
struct Ngs2VoiceCallbackInfo {
std::uintptr_t callback_data;
Ngs2Handle voice_handle;
std::uint32_t flag;
std::uint32_t reserved;
std::uintptr_t user_data;
const void* block_data;
std::uint32_t block_size;
std::uint32_t num_repeated;
std::uint32_t attributes;
std::uint32_t reserved2;
};
static_assert(sizeof(Ngs2VoiceCallbackInfo) == 56);
using Ngs2VoiceCallbackHandler = void (APS5_VABI *)(const Ngs2VoiceCallbackInfo*);
struct Ngs2VoiceCallbackParam {
Ngs2VoiceParamHeader header;
Ngs2VoiceCallbackHandler callback;
std::uintptr_t callback_data;
std::uint32_t flags;
std::uint32_t reserved;
};
struct Ngs2WaveformFormat {
std::uint32_t waveform_type;
std::uint32_t num_channels;
std::uint32_t sample_rate;
std::uint32_t config_data;
std::uint32_t frame_offset;
std::uint32_t frame_margin;
};
static_assert(sizeof(Ngs2WaveformFormat) == 24);
struct Ngs2WaveformBlock {
std::uint64_t data_offset;
std::uint64_t data_size;
std::uint32_t num_repeats;
std::uint32_t num_skip_samples;
std::uint32_t num_samples;
std::uint32_t reserved;
std::uintptr_t user_data;
};
static_assert(sizeof(Ngs2WaveformBlock) == 40);
struct Ngs2SamplerVoiceSetupParam {
Ngs2VoiceParamHeader header;
Ngs2WaveformFormat format;
};
struct Ngs2SamplerVoiceWaveformBlocksParam {
Ngs2VoiceParamHeader header;
const void* data;
std::uint32_t flags;
std::uint32_t num_blocks;
const Ngs2WaveformBlock* blocks;
};
struct Ngs2SamplerVoicePitchParam {
Ngs2VoiceParamHeader header;
float ratio;
};
struct Ngs2SubmixerVoiceSetupParam {
Ngs2VoiceParamHeader header;
std::uint32_t num_io_channels;
std::uint32_t flags;
};
static_assert(sizeof(Ngs2SubmixerVoiceSetupParam) == 16);
struct Ngs2MasteringVoiceSetupParam {
Ngs2VoiceParamHeader header;
std::uint32_t num_io_channels;
std::uint32_t flags;
};
struct Ngs2MasteringVoiceOutputParam {
Ngs2VoiceParamHeader header;
std::uint32_t output_id;
std::uint32_t reserved;
};
struct Ngs2VoiceCommand {
std::uint32_t id;
std::uint8_t flags;
std::uint8_t type;
std::uint16_t count;
union {
std::uint32_t u;
std::int32_t i;
float f;
const float* levels;
} value;
};
static_assert(sizeof(Ngs2VoiceCommand) == 16);
struct Ngs2RenderBufferInfo {
void* buffer;
std::size_t buffer_size;
std::uint32_t waveform_type;
std::uint32_t num_channels;
};
struct Ngs2VoiceState {
std::uint32_t state_flags;
std::int32_t error_code;
};
static_assert(sizeof(Ngs2VoiceState) == 8);
struct Ngs2SubmixerVoiceState {
Ngs2VoiceState voice_state;
float envelope_height;
float peak_height;
float compressor_height;
};
static_assert(sizeof(Ngs2SubmixerVoiceState) == 20);
struct Ngs2SamplerVoiceState {
Ngs2VoiceState voice_state;
float envelope_height;
float peak_height;
std::uint32_t reserved;
std::uint64_t num_decoded_samples;
std::uint64_t decoded_data_size;
std::uintptr_t user_data;
const void* waveform_data;
};
static_assert(sizeof(Ngs2SamplerVoiceState) == 56);
struct Ngs2WaveformInfo {
Ngs2WaveformFormat format;
std::uint32_t data_offset;
std::uint32_t data_size;
std::uint32_t loop_begin_position;
std::uint32_t loop_end_position;
std::uint32_t num_samples;
std::uint32_t audio_unit_size;
std::uint32_t num_audio_unit_samples;
std::uint32_t num_audio_unit_per_frame;
std::uint32_t audio_frame_size;
std::uint32_t num_audio_frame_samples;
std::uint32_t num_delay_samples;
std::uint32_t num_blocks;
Ngs2WaveformBlock block[4];
};
static_assert(sizeof(Ngs2WaveformInfo) == 232);
struct Ngs2PanParam {
std::uint32_t reserved[16];
};
struct Ngs2PanWork {
std::uint32_t reserved[64];
};
struct Ngs2GeomListenerParam {
std::uint32_t reserved[32];
};
struct Ngs2GeomListenerWork {
std::uint32_t reserved[64];
};
struct Ngs2GeomSourceParam {
std::uint32_t reserved[32];
};
struct Ngs2GeomAttribute {
std::uint32_t reserved[32];
};
#endif

View File

@ -0,0 +1,96 @@
#ifndef CORE_LIBS_PRX_LIBSCENGS2_SRC_NGS2INTERNAL_HPP
#define CORE_LIBS_PRX_LIBSCENGS2_SRC_NGS2INTERNAL_HPP
#include <cstddef>
#include <cstdint>
#include <deque>
#include <mutex>
#include <string>
#include <vector>
#include "prx/libSceNgs2.native/include/Ngs2Types.hpp"
static constexpr std::uint32_t NGS2_MAX_CHANNELS = 8;
struct Ngs2System;
struct Ngs2Rack;
enum class Ngs2PlayState : std::uint32_t {
Empty = 0,
Playing = SCE_NGS2_VOICE_STATE_FLAG_INUSE | SCE_NGS2_VOICE_STATE_FLAG_PLAYING,
Paused = SCE_NGS2_VOICE_STATE_FLAG_INUSE | SCE_NGS2_VOICE_STATE_FLAG_PAUSED,
Stopped = SCE_NGS2_VOICE_STATE_FLAG_INUSE | SCE_NGS2_VOICE_STATE_FLAG_PLAYING | SCE_NGS2_VOICE_STATE_FLAG_STOPPED,
};
struct Ngs2Block {
const std::uint8_t* data;
Ngs2WaveformBlock info;
std::uint32_t cursor = 0;
std::uint32_t numRepeated = 0;
};
struct Ngs2Voice;
struct Ngs2Port {
Ngs2Voice* dest = nullptr;
float volume = 1.0f;
std::int32_t matrix = -1;
};
struct Ngs2Voice {
Ngs2Rack* rack = nullptr;
Ngs2PlayState state = Ngs2PlayState::Empty;
std::uint32_t stateFlags = 0;
std::uint32_t channels = 0;
std::uint32_t sampleRate = 0;
float pitch = 1.0f;
std::uint64_t phase = 0;
std::deque<Ngs2Block> blocks;
bool acceptsBlocks = true;
std::uint64_t decodedSamples = 0;
const std::uint8_t* waveformEnd = nullptr;
Ngs2VoiceCallbackHandler callback = nullptr;
std::uintptr_t callbackData = 0;
std::uint32_t callbackFlags = 0;
std::vector<Ngs2Port> ports;
std::vector<std::vector<float>> matrices;
std::uint32_t outputId = 0;
std::vector<float> samples;
bool rendering = false;
bool rendered = false;
bool hasSamples = false;
void SetEvent(std::uint32_t eventId);
void ResetSetup();
const std::uint8_t* WaveformData() const;
};
struct Ngs2Rack {
Ngs2System* system = nullptr;
std::uint32_t rackId = 0;
std::uint32_t maxChannels = 0;
Ngs2ContextBufferInfo bufferInfo{};
Ngs2BufferAllocator allocator{};
std::vector<Ngs2Voice> voices;
};
struct Ngs2System {
Ngs2SystemOption option{};
Ngs2ContextBufferInfo bufferInfo{};
Ngs2BufferAllocator allocator{};
std::uint32_t uid = 0;
std::int64_t renderCount = 0;
std::vector<Ngs2Rack*> racks;
};
std::string Ngs2Hex(std::uint32_t value);
std::recursive_mutex& Ngs2Mutex();
Ngs2System* Ngs2FindSystem(Ngs2Handle handle);
Ngs2Rack* Ngs2FindRack(Ngs2Handle handle);
Ngs2Voice* Ngs2FindVoice(Ngs2Handle handle);
void* Ngs2Place(const Ngs2ContextBufferInfo* bufferInfo, std::size_t size, std::size_t alignment);
int Ngs2DestroyRack(Ngs2Rack& rack, Ngs2ContextBufferInfo* outBufferInfo);
int Ngs2ReleaseBuffer(const Ngs2BufferAllocator& allocator, Ngs2ContextBufferInfo bufferInfo, Ngs2ContextBufferInfo* outBufferInfo);
void Ngs2RenderSystem(Ngs2System& system, const Ngs2RenderBufferInfo* bufferInfo, std::uint32_t numBufferInfo);
#endif

View File

@ -0,0 +1,193 @@
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <mutex>
#include <new>
#include <stdexcept>
#include <string>
#include "prx/libc/include/General.hpp"
#include "Ngs2Internal.hpp"
union RackOptions {
Ngs2RackOption common;
Ngs2SamplerRackOption sampler;
Ngs2SubmixerRackOption submixer;
Ngs2MasteringRackOption mastering;
};
static std::size_t RackOptionSize(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: throw std::runtime_error("NGS2: rack id " + Ngs2Hex(rackId) + " is not implemented");
}
}
static RackOptions DefaultRackOption(std::uint32_t rackId) {
RackOptions options{};
auto& common = options.common;
common.size = RackOptionSize(rackId);
common.max_grain_samples = 512;
common.max_voices = 1;
common.max_input_delay_blocks = 1;
common.max_matrices = 1;
common.max_ports = 8;
switch (rackId) {
case SCE_NGS2_RACK_ID_SAMPLER:
common.max_voices = 256;
common.max_input_delay_blocks = 0;
options.sampler.max_channel_works = 256;
options.sampler.max_codec_caches = 32;
options.sampler.max_waveform_blocks = 4;
options.sampler.max_envelope_points = 4;
options.sampler.max_filters = 8;
options.sampler.max_atrac9_decoders = 256;
options.sampler.max_atrac9_channel_works = 256;
options.sampler.num_peak_meter_blocks = 8;
break;
case SCE_NGS2_RACK_ID_SUBMIXER:
options.submixer.max_channels = 8;
options.submixer.max_envelope_points = 4;
options.submixer.max_filters = 8;
options.submixer.max_inputs = 1;
options.submixer.num_peak_meter_blocks = 8;
break;
default:
common.max_matrices = 0;
common.max_ports = 0;
options.mastering.max_channels = 8;
options.mastering.num_peak_meter_blocks = 8;
break;
}
return options;
}
static std::uint32_t RackMaxChannels(std::uint32_t rackId, const RackOptions& options) {
if (rackId == SCE_NGS2_RACK_ID_SUBMIXER) return options.submixer.max_channels;
if (rackId == SCE_NGS2_RACK_ID_MASTERING) return options.mastering.max_channels;
return NGS2_MAX_CHANNELS;
}
static RackOptions CheckedRackOption(std::uint32_t rackId, const Ngs2RackOption* option) {
const auto size = RackOptionSize(rackId);
if (option == nullptr) return DefaultRackOption(rackId);
if (option->size != size) throw std::invalid_argument("NGS2: unexpected rack option size " + std::to_string(option->size));
RackOptions options{};
std::memcpy(&options, option, size);
const auto maxChannels = RackMaxChannels(rackId, options);
if (options.common.max_voices == 0 || maxChannels == 0 || maxChannels > NGS2_MAX_CHANNELS) {
throw std::invalid_argument("NGS2: invalid rack voice or channel count");
}
return options;
}
static int CreateRack(Ngs2Handle systemHandle, std::uint32_t rackId, const RackOptions& options, const Ngs2ContextBufferInfo& bufferInfo,
const Ngs2BufferAllocator& allocator, Ngs2Handle* handle) {
auto* system = Ngs2FindSystem(systemHandle);
if (system == nullptr) return SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
auto* rack = new (Ngs2Place(&bufferInfo, sizeof(Ngs2Rack), alignof(Ngs2Rack))) Ngs2Rack{};
rack->system = system;
rack->rackId = rackId;
rack->maxChannels = RackMaxChannels(rackId, options);
rack->bufferInfo = bufferInfo;
rack->allocator = allocator;
rack->voices.resize(options.common.max_voices);
for (auto& voice : rack->voices) {
voice.rack = rack;
voice.ports.resize(options.common.max_ports);
voice.matrices.resize(options.common.max_matrices);
}
system->racks.push_back(rack);
*handle = reinterpret_cast<Ngs2Handle>(rack);
return SCE_NGS2_OK;
}
int Ngs2DestroyRack(Ngs2Rack& rack, Ngs2ContextBufferInfo* outBufferInfo) {
auto& racks = rack.system->racks;
std::erase(racks, &rack);
for (auto* other : racks) {
for (auto& voice : other->voices) {
for (auto& port : voice.ports) {
if (port.dest != nullptr && port.dest->rack == &rack) port.dest = nullptr;
}
}
}
const auto bufferInfo = rack.bufferInfo;
const auto allocator = rack.allocator;
std::destroy_at(&rack);
return Ngs2ReleaseBuffer(allocator, bufferInfo, outBufferInfo);
}
#pragma GCC visibility push(default)
extern "C" {
int APS5_VABI sceNgs2RackQueryBufferSize(uint32_t rack_id, const Ngs2RackOption* option, Ngs2ContextBufferInfo* buffer_info) {
if (buffer_info == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
CheckedRackOption(rack_id, option);
*buffer_info = {};
buffer_info->host_buffer_size = sizeof(Ngs2Rack);
return SCE_NGS2_OK;
}
int APS5_VABI sceNgs2RackCreate(uintptr_t system_handle, uint32_t rack_id, const Ngs2RackOption* option, const Ngs2ContextBufferInfo* buffer_info, uintptr_t* handle) {
if (handle == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
const auto options = CheckedRackOption(rack_id, option);
if (buffer_info == nullptr) APS5_INVALID_ARG_EX;
std::lock_guard lock(Ngs2Mutex());
return CreateRack(system_handle, rack_id, options, *buffer_info, {}, handle);
}
int APS5_VABI sceNgs2RackCreateWithAllocator(uintptr_t system_handle, uint32_t rack_id, const Ngs2RackOption* option, const Ngs2BufferAllocator* allocator, uintptr_t* handle) {
if (handle == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
const auto options = CheckedRackOption(rack_id, option);
if (allocator == nullptr || allocator->alloc_handler == nullptr || allocator->free_handler == nullptr) APS5_INVALID_ARG_EX;
std::lock_guard lock(Ngs2Mutex());
if (Ngs2FindSystem(system_handle) == nullptr) return SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
Ngs2ContextBufferInfo bufferInfo{};
bufferInfo.host_buffer_size = sizeof(Ngs2Rack);
bufferInfo.user_data = allocator->user_data;
const int result = allocator->alloc_handler(&bufferInfo);
if (result != SCE_NGS2_OK) throw std::runtime_error("NGS2: the allocator handler failed with " + std::to_string(result));
return CreateRack(system_handle, rack_id, options, bufferInfo, *allocator, handle);
}
int APS5_VABI sceNgs2RackDestroy(uintptr_t rack_handle, Ngs2ContextBufferInfo* buffer_info) {
std::lock_guard lock(Ngs2Mutex());
if (buffer_info != nullptr) *buffer_info = {};
auto* rack = Ngs2FindRack(rack_handle);
if (rack == nullptr) return SCE_NGS2_ERROR_INVALID_RACK_HANDLE;
return Ngs2DestroyRack(*rack, buffer_info);
}
int APS5_VABI sceNgs2RackGetVoiceHandle(uintptr_t rack_handle, uint32_t voice_id, uintptr_t* handle) {
if (handle == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
std::lock_guard lock(Ngs2Mutex());
auto* rack = Ngs2FindRack(rack_handle);
if (rack == nullptr) return SCE_NGS2_ERROR_INVALID_RACK_HANDLE;
if (voice_id >= rack->voices.size()) APS5_INVALID_ARG_EX;
*handle = reinterpret_cast<Ngs2Handle>(&rack->voices[voice_id]);
return SCE_NGS2_OK;
}
int APS5_VABI sceNgs2RackLock(uintptr_t rack_handle) {
Ngs2Mutex().lock();
if (Ngs2FindRack(rack_handle) != nullptr) return SCE_NGS2_OK;
Ngs2Mutex().unlock();
return SCE_NGS2_ERROR_INVALID_RACK_HANDLE;
}
int APS5_VABI sceNgs2RackUnlock(uintptr_t rack_handle) {
std::lock_guard lock(Ngs2Mutex());
if (Ngs2FindRack(rack_handle) == nullptr) return SCE_NGS2_ERROR_INVALID_RACK_HANDLE;
Ngs2Mutex().unlock();
return SCE_NGS2_OK;
}
}
#pragma GCC visibility pop

View File

@ -0,0 +1,189 @@
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <stdexcept>
#include <string>
#include <vector>
#include "prx/libc/include/General.hpp"
#include "Ngs2Internal.hpp"
static constexpr double PHASE_ONE = 4294967296.0;
static constexpr std::uint64_t PHASE_FRACTION = 0xffffffff;
static const std::uint8_t* FrameAt(const Ngs2Voice& voice, const Ngs2Block& block, std::uint32_t frame) {
return block.data + (static_cast<std::size_t>(block.info.num_skip_samples) + frame) * voice.channels * sizeof(std::int16_t);
}
static const std::uint8_t* NextFrame(const Ngs2Voice& voice) {
const auto& block = voice.blocks.front();
if (block.cursor + 1 < block.info.num_samples) return FrameAt(voice, block, block.cursor + 1);
if (block.info.num_repeats != 0) return FrameAt(voice, block, 0);
if (voice.blocks.size() > 1) return FrameAt(voice, voice.blocks[1], 0);
return FrameAt(voice, block, block.cursor);
}
static float ReadSample(const std::uint8_t* frame, std::uint32_t channel) {
std::int16_t sample;
std::memcpy(&sample, frame + channel * sizeof(sample), sizeof(sample));
return sample;
}
static void FinishBlock(Ngs2Voice& voice) {
auto& block = voice.blocks.front();
const bool repeat = block.info.num_repeats != 0;
if (repeat) {
if (block.info.num_repeats != UINT32_MAX) block.info.num_repeats--;
block.numRepeated++;
block.cursor = 0;
}
const Ngs2VoiceCallbackInfo info{
voice.callbackData,
reinterpret_cast<Ngs2Handle>(&voice),
repeat ? SCE_NGS2_VOICE_CALLBACK_FLAG_BLOCK_REPEAT : SCE_NGS2_VOICE_CALLBACK_FLAG_BLOCK_END,
0,
block.info.user_data,
block.data,
static_cast<std::uint32_t>(block.info.data_size),
block.numRepeated,
0,
0,
};
if (!repeat) {
voice.waveformEnd = FrameAt(voice, block, block.info.num_samples);
voice.blocks.pop_front();
}
if (voice.callback != nullptr && (voice.callbackFlags & info.flag) != 0) voice.callback(&info);
}
static void Advance(Ngs2Voice& voice, std::uint64_t frames) {
while (frames != 0 && voice.state == Ngs2PlayState::Playing && !voice.blocks.empty()) {
auto& block = voice.blocks.front();
const auto step = std::min<std::uint64_t>(frames, block.info.num_samples - block.cursor);
block.cursor += static_cast<std::uint32_t>(step);
voice.decodedSamples += step;
frames -= step;
if (block.cursor == block.info.num_samples) FinishBlock(voice);
}
}
static void ConsumeSamples(Ngs2Voice& voice, std::uint32_t grain, std::uint32_t systemRate) {
const auto step = static_cast<std::uint64_t>(std::llround(voice.sampleRate * static_cast<double>(voice.pitch) / systemRate * PHASE_ONE));
std::uint32_t written = 0;
for (; written < grain && voice.state == Ngs2PlayState::Playing && !voice.blocks.empty(); written++) {
const auto& block = voice.blocks.front();
const auto* current = FrameAt(voice, block, block.cursor);
const auto* next = NextFrame(voice);
const auto fraction = static_cast<float>(static_cast<double>(voice.phase) / PHASE_ONE);
for (std::uint32_t channel = 0; channel < voice.channels; channel++) {
voice.samples[channel * grain + written] = std::lerp(ReadSample(current, channel), ReadSample(next, channel), fraction) / 32768.0f;
}
voice.phase += step;
Advance(voice, voice.phase >> 32);
voice.phase &= PHASE_FRACTION;
}
voice.hasSamples = written != 0;
if (voice.state == Ngs2PlayState::Playing && voice.blocks.empty() && !voice.acceptsBlocks) voice.state = Ngs2PlayState::Empty;
}
static void RenderVoice(Ngs2Voice& voice, const std::vector<Ngs2Voice*>& voices, std::uint32_t grain, std::uint32_t systemRate);
static void MixPort(Ngs2Voice& voice, const Ngs2Voice& source, const Ngs2Port& port, std::uint32_t grain) {
const auto* matrix = port.matrix < 0 ? nullptr : &source.matrices[port.matrix];
const std::size_t outputs = matrix == nullptr ? voice.channels : std::min<std::size_t>(voice.channels, matrix->size() / source.channels);
for (std::size_t dst = 0; dst < outputs; dst++) {
for (std::uint32_t src = 0; src < source.channels; src++) {
const float level = port.volume * (matrix == nullptr ? (src == dst ? 1.0f : 0.0f) : (*matrix)[dst * source.channels + src]);
if (level == 0.0f) continue;
for (std::uint32_t i = 0; i < grain; i++) voice.samples[dst * grain + i] += source.samples[src * grain + i] * level;
}
}
}
static void MixInputs(Ngs2Voice& voice, const std::vector<Ngs2Voice*>& voices, std::uint32_t grain, std::uint32_t systemRate) {
for (auto* source : voices) {
for (const auto& port : source->ports) {
if (port.dest != &voice || port.volume == 0.0f) continue;
RenderVoice(*source, voices, grain, systemRate);
if (!source->hasSamples) continue;
MixPort(voice, *source, port, grain);
voice.hasSamples = true;
}
}
}
static void RenderVoice(Ngs2Voice& voice, const std::vector<Ngs2Voice*>& voices, std::uint32_t grain, std::uint32_t systemRate) {
if (voice.rendered) return;
if (voice.rendering) throw std::invalid_argument("NGS2: the voice patches form a cycle");
voice.rendering = true;
voice.samples.assign(static_cast<std::size_t>(grain) * voice.channels, 0.0f);
voice.hasSamples = false;
if (voice.state == Ngs2PlayState::Playing && voice.channels != 0) {
if (voice.rack->rackId == SCE_NGS2_RACK_ID_SAMPLER) ConsumeSamples(voice, grain, systemRate);
else MixInputs(voice, voices, grain, systemRate);
}
voice.rendering = false;
voice.rendered = true;
}
static std::size_t SampleBytes(std::uint32_t waveformType) {
if (waveformType == SCE_NGS2_WAVEFORM_TYPE_PCM_I16L) return sizeof(std::int16_t);
if (waveformType == SCE_NGS2_WAVEFORM_TYPE_PCM_F32L) return sizeof(float);
throw std::runtime_error("NGS2: render waveform type " + Ngs2Hex(waveformType) + " is not implemented");
}
static std::vector<float>& OutputMix(std::vector<std::vector<float>>& mixes, const Ngs2RenderBufferInfo* bufferInfo, std::uint32_t numBufferInfo,
const Ngs2Voice& voice, std::uint32_t grain) {
if (voice.outputId >= numBufferInfo) throw std::invalid_argument("NGS2: mastering output " + std::to_string(voice.outputId) + " has no render buffer");
const auto& output = bufferInfo[voice.outputId];
const auto sampleBytes = SampleBytes(output.waveform_type);
if (output.num_channels != voice.channels) throw std::runtime_error("NGS2: mixing " + std::to_string(voice.channels) + " mastering channels into " + std::to_string(output.num_channels) + " is not implemented");
if (output.buffer == nullptr || output.buffer_size < static_cast<std::size_t>(grain) * output.num_channels * sampleBytes) {
throw std::invalid_argument("NGS2: render buffer " + std::to_string(voice.outputId) + " is missing or too small");
}
auto& mix = mixes[voice.outputId];
if (mix.empty()) mix.resize(static_cast<std::size_t>(grain) * output.num_channels);
return mix;
}
static void WriteOutput(const Ngs2RenderBufferInfo& output, const std::vector<float>& mix) {
if (output.waveform_type == SCE_NGS2_WAVEFORM_TYPE_PCM_F32L) {
std::memcpy(output.buffer, mix.data(), mix.size() * sizeof(float));
return;
}
auto* pcm = static_cast<std::int16_t*>(output.buffer);
for (std::size_t i = 0; i < mix.size(); i++) {
pcm[i] = static_cast<std::int16_t>(std::min(std::lrint(std::clamp(mix[i], -1.0f, 1.0f) * 32768.0f), 32767L));
}
}
void Ngs2RenderSystem(Ngs2System& system, const Ngs2RenderBufferInfo* bufferInfo, std::uint32_t numBufferInfo) {
const auto grain = system.option.num_grain_samples;
std::vector<Ngs2Voice*> voices;
for (auto* rack : system.racks) {
for (auto& voice : rack->voices) {
voice.rendered = false;
voices.push_back(&voice);
}
}
std::vector<std::vector<float>> mixes(numBufferInfo);
for (auto* voice : voices) {
RenderVoice(*voice, voices, grain, system.option.sample_rate);
if (voice->rack->rackId != SCE_NGS2_RACK_ID_MASTERING || !voice->hasSamples) continue;
auto& mix = OutputMix(mixes, bufferInfo, numBufferInfo, *voice, grain);
for (std::uint32_t channel = 0; channel < voice->channels; channel++) {
for (std::uint32_t i = 0; i < grain; i++) mix[i * voice->channels + channel] += voice->samples[channel * grain + i];
}
}
for (std::uint32_t i = 0; i < numBufferInfo; i++) {
if (bufferInfo[i].buffer != nullptr) std::memset(bufferInfo[i].buffer, 0, bufferInfo[i].buffer_size);
if (!mixes[i].empty()) WriteOutput(bufferInfo[i], mixes[i]);
}
for (auto* voice : voices) {
if (voice->state == Ngs2PlayState::Stopped) voice->state = Ngs2PlayState::Empty;
voice->stateFlags = static_cast<std::uint32_t>(voice->state);
}
system.renderCount++;
}

View File

@ -0,0 +1,208 @@
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <memory>
#include <mutex>
#include <new>
#include <stdexcept>
#include <string>
#include <vector>
#include "prx/libc/include/General.hpp"
#include "Ngs2Internal.hpp"
static constexpr std::uint32_t MIN_GRAIN_SAMPLES = 64;
static std::vector<Ngs2System*>& Systems() {
static std::vector<Ngs2System*> systems;
return systems;
}
std::string Ngs2Hex(std::uint32_t value) {
char text[16];
std::snprintf(text, sizeof(text), "0x%x", value);
return text;
}
std::recursive_mutex& Ngs2Mutex() {
static std::recursive_mutex mutex;
return mutex;
}
Ngs2System* Ngs2FindSystem(Ngs2Handle handle) {
auto& systems = Systems();
const auto found = std::find(systems.begin(), systems.end(), reinterpret_cast<Ngs2System*>(handle));
return found == systems.end() ? nullptr : *found;
}
Ngs2Rack* Ngs2FindRack(Ngs2Handle handle) {
for (auto* system : Systems()) {
for (auto* rack : system->racks) {
if (reinterpret_cast<Ngs2Handle>(rack) == handle) return rack;
}
}
return nullptr;
}
Ngs2Voice* Ngs2FindVoice(Ngs2Handle handle) {
for (auto* system : Systems()) {
for (auto* rack : system->racks) {
for (auto& voice : rack->voices) {
if (reinterpret_cast<Ngs2Handle>(&voice) == handle) return &voice;
}
}
}
return nullptr;
}
void* Ngs2Place(const Ngs2ContextBufferInfo* bufferInfo, std::size_t size, std::size_t alignment) {
if (bufferInfo == nullptr || bufferInfo->host_buffer == nullptr || bufferInfo->host_buffer_size < size) {
throw std::invalid_argument("NGS2: the context buffer is missing or smaller than the queried size");
}
if (reinterpret_cast<std::uintptr_t>(bufferInfo->host_buffer) % alignment != 0) {
throw std::invalid_argument("NGS2: the context buffer is not aligned to " + std::to_string(alignment) + " bytes");
}
return bufferInfo->host_buffer;
}
int Ngs2ReleaseBuffer(const Ngs2BufferAllocator& allocator, Ngs2ContextBufferInfo bufferInfo, Ngs2ContextBufferInfo* outBufferInfo) {
if (outBufferInfo != nullptr) *outBufferInfo = allocator.free_handler == nullptr ? bufferInfo : Ngs2ContextBufferInfo{};
return allocator.free_handler == nullptr ? SCE_NGS2_OK : allocator.free_handler(&bufferInfo);
}
static Ngs2ContextBufferInfo Allocate(const Ngs2BufferAllocator* allocator, std::size_t size) {
if (allocator == nullptr || allocator->alloc_handler == nullptr || allocator->free_handler == nullptr) APS5_INVALID_ARG_EX;
Ngs2ContextBufferInfo bufferInfo{};
bufferInfo.host_buffer_size = size;
bufferInfo.user_data = allocator->user_data;
const int result = allocator->alloc_handler(&bufferInfo);
if (result != SCE_NGS2_OK) throw std::runtime_error("NGS2: the allocator handler failed with " + std::to_string(result));
return bufferInfo;
}
static Ngs2SystemOption DefaultSystemOption() {
Ngs2SystemOption option{};
option.size = sizeof(Ngs2SystemOption);
option.max_grain_samples = 512;
option.num_grain_samples = 256;
option.sample_rate = 48000;
return option;
}
static Ngs2SystemOption CheckedSystemOption(const Ngs2SystemOption* option) {
if (option == nullptr) return DefaultSystemOption();
if (option->size != sizeof(Ngs2SystemOption)) throw std::invalid_argument("NGS2: unexpected system option size " + std::to_string(option->size));
if (option->flags != 0) throw std::runtime_error("NGS2: system option flags are not implemented");
if (option->sample_rate == 0 || option->max_grain_samples < MIN_GRAIN_SAMPLES || option->num_grain_samples == 0 ||
option->num_grain_samples > option->max_grain_samples) {
throw std::invalid_argument("NGS2: invalid system grain or sample rate");
}
return *option;
}
static int CreateSystem(const Ngs2SystemOption& option, const Ngs2ContextBufferInfo& bufferInfo, const Ngs2BufferAllocator& allocator, Ngs2Handle* handle) {
static std::uint32_t nextUid = 1;
auto* system = new (Ngs2Place(&bufferInfo, sizeof(Ngs2System), alignof(Ngs2System))) Ngs2System{};
system->option = option;
system->bufferInfo = bufferInfo;
system->allocator = allocator;
system->uid = nextUid++;
Systems().push_back(system);
*handle = reinterpret_cast<Ngs2Handle>(system);
return SCE_NGS2_OK;
}
#pragma GCC visibility push(default)
extern "C" {
int APS5_VABI sceNgs2SystemQueryBufferSize(const Ngs2SystemOption* option, Ngs2ContextBufferInfo* buffer_info) {
if (buffer_info == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
CheckedSystemOption(option);
*buffer_info = {};
buffer_info->host_buffer_size = sizeof(Ngs2System);
return SCE_NGS2_OK;
}
int APS5_VABI sceNgs2SystemCreate(const Ngs2SystemOption* option, const Ngs2ContextBufferInfo* buffer_info, uintptr_t* handle) {
if (handle == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
const auto checked = CheckedSystemOption(option);
if (buffer_info == nullptr) APS5_INVALID_ARG_EX;
std::lock_guard lock(Ngs2Mutex());
return CreateSystem(checked, *buffer_info, {}, handle);
}
int APS5_VABI sceNgs2SystemCreateWithAllocator(const Ngs2SystemOption* option, const Ngs2BufferAllocator* allocator, uintptr_t* handle) {
if (handle == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
const auto checked = CheckedSystemOption(option);
const auto bufferInfo = Allocate(allocator, sizeof(Ngs2System));
std::lock_guard lock(Ngs2Mutex());
return CreateSystem(checked, bufferInfo, *allocator, handle);
}
int APS5_VABI sceNgs2SystemDestroy(uintptr_t system_handle, Ngs2ContextBufferInfo* buffer_info) {
std::lock_guard lock(Ngs2Mutex());
if (buffer_info != nullptr) *buffer_info = {};
auto* system = Ngs2FindSystem(system_handle);
if (system == nullptr) return SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
while (!system->racks.empty()) {
const int result = Ngs2DestroyRack(*system->racks.back(), nullptr);
if (result != SCE_NGS2_OK) return result;
}
std::erase(Systems(), system);
const auto bufferInfo = system->bufferInfo;
const auto allocator = system->allocator;
std::destroy_at(system);
return Ngs2ReleaseBuffer(allocator, bufferInfo, buffer_info);
}
int APS5_VABI sceNgs2SystemGetInfo(uintptr_t system_handle, Ngs2SystemInfo* info, size_t info_size) {
if (info == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
if (info_size != sizeof(Ngs2SystemInfo)) return SCE_NGS2_ERROR_INVALID_OUT_SIZE;
std::lock_guard lock(Ngs2Mutex());
const auto* system = Ngs2FindSystem(system_handle);
if (system == nullptr) return SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
*info = {};
std::memcpy(info->name, system->option.name, sizeof(info->name));
info->system_handle = system_handle;
info->buffer_info = system->bufferInfo;
info->uid = system->uid;
info->min_grain_samples = MIN_GRAIN_SAMPLES;
info->max_grain_samples = system->option.max_grain_samples;
info->state_flags = 1;
info->rack_count = static_cast<std::uint32_t>(system->racks.size());
info->render_count = system->renderCount;
info->sample_rate = system->option.sample_rate;
info->num_grain_samples = system->option.num_grain_samples;
return SCE_NGS2_OK;
}
int APS5_VABI sceNgs2SystemSetGrainSamples(uintptr_t system_handle, uint32_t num_samples) {
std::lock_guard lock(Ngs2Mutex());
auto* system = Ngs2FindSystem(system_handle);
if (system == nullptr) return SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
if (num_samples == 0 || num_samples > system->option.max_grain_samples) APS5_INVALID_ARG_EX;
system->option.num_grain_samples = num_samples;
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);
if (system == nullptr) return SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
if (buffer_info == nullptr || num_buffer_info == 0) APS5_INVALID_ARG_EX;
Ngs2RenderSystem(*system, buffer_info, num_buffer_info);
return SCE_NGS2_OK;
}
int APS5_VABI sceNgs2SystemResetOption(Ngs2SystemOption* option) {
if (option == nullptr) APS5_INVALID_ARG_EX;
*option = DefaultSystemOption();
return SCE_NGS2_OK;
}
}
#pragma GCC visibility pop

View File

@ -0,0 +1,78 @@
#include <cstddef>
#include <cstdint>
#include "prx/libc/include/General.hpp"
#include "prx/libSceNgs2.native/include/Ngs2Types.hpp"
#pragma GCC visibility push(default)
extern "C" {
int APS5_VABI sceNgs2CalcWaveformBlock(const Ngs2WaveformFormat* format, uint32_t sample_pos, uint32_t num_samples, Ngs2WaveformBlock* block) {
(void)format;
(void)sample_pos;
(void)num_samples;
(void)block;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GeomApply(const Ngs2GeomListenerWork* listener, const Ngs2GeomSourceParam* source, Ngs2GeomAttribute* out_attrib, uint32_t flags) {
(void)listener;
(void)source;
(void)out_attrib;
(void)flags;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GeomCalcListener(const Ngs2GeomListenerParam* param, Ngs2GeomListenerWork* out_work, uint32_t flags) {
(void)param;
(void)out_work;
(void)flags;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GeomResetListenerParam(Ngs2GeomListenerParam* out_listener_param) {
(void)out_listener_param;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GeomResetSourceParam(Ngs2GeomSourceParam* out_source_param) {
(void)out_source_param;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2PanGetVolumeMatrix(Ngs2PanWork* work, const Ngs2PanParam* params, uint32_t num_params, uint32_t matrix_format, float* out_volume_matrix) {
(void)work;
(void)params;
(void)num_params;
(void)matrix_format;
(void)out_volume_matrix;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2PanInit(Ngs2PanWork* work, const float* speaker_angles, float unit_angle, uint32_t num_speakers) {
(void)work;
(void)speaker_angles;
(void)unit_angle;
(void)num_speakers;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2ParseWaveformData(const void* data, size_t data_size, Ngs2WaveformInfo* info) {
(void)data;
(void)data_size;
(void)info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
#pragma GCC visibility pop

View File

@ -0,0 +1,304 @@
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <mutex>
#include <stdexcept>
#include <string>
#include "prx/libc/include/General.hpp"
#include "Ngs2Internal.hpp"
static constexpr std::uint32_t COMMAND_EVENT = 2;
static constexpr std::uint32_t COMMAND_MATRIX_LEVELS = 5;
static constexpr std::uint32_t COMMAND_PORT_VOLUME = 6;
static constexpr std::uint32_t COMMAND_PORT_MATRIX = 7;
static constexpr std::uint8_t COMMAND_TYPE_FLOAT = 1;
static constexpr std::uint8_t COMMAND_TYPE_INT = 3;
static constexpr std::uint8_t COMMAND_TYPE_UINT = 4;
static constexpr std::uint8_t COMMAND_TYPE_FLOAT_ARRAY = 0x11;
void Ngs2Voice::SetEvent(std::uint32_t eventId) {
switch (eventId) {
case SCE_NGS2_VOICE_EVENT_PLAY:
if (state == Ngs2PlayState::Empty || state == Ngs2PlayState::Stopped) {
state = Ngs2PlayState::Playing;
stateFlags |= SCE_NGS2_VOICE_STATE_FLAG_INUSE;
}
break;
case SCE_NGS2_VOICE_EVENT_STOP:
if (state == Ngs2PlayState::Playing || state == Ngs2PlayState::Paused) state = Ngs2PlayState::Stopped;
break;
case SCE_NGS2_VOICE_EVENT_STOP_IMM:
case SCE_NGS2_VOICE_EVENT_KILL:
state = Ngs2PlayState::Empty;
break;
case SCE_NGS2_VOICE_EVENT_PAUSE:
if (state == Ngs2PlayState::Playing) state = Ngs2PlayState::Paused;
break;
case SCE_NGS2_VOICE_EVENT_RESUME:
if (state == Ngs2PlayState::Paused) state = Ngs2PlayState::Playing;
break;
default: throw std::invalid_argument("NGS2: unknown voice event " + Ngs2Hex(eventId));
}
}
void Ngs2Voice::ResetSetup() {
SetEvent(SCE_NGS2_VOICE_EVENT_STOP_IMM);
std::fill(ports.begin(), ports.end(), Ngs2Port{});
for (auto& matrix : matrices) matrix.clear();
channels = 0;
sampleRate = 0;
pitch = 1.0f;
phase = 0;
blocks.clear();
acceptsBlocks = true;
decodedSamples = 0;
waveformEnd = nullptr;
}
const std::uint8_t* Ngs2Voice::WaveformData() const {
if (blocks.empty()) return waveformEnd;
const auto& block = blocks.front();
return block.data + (static_cast<std::size_t>(block.info.num_skip_samples) + block.cursor) * channels * sizeof(std::int16_t);
}
template <typename TParam>
static const TParam& ParamAs(const Ngs2VoiceParamHeader& param) {
if (param.size < sizeof(TParam)) throw std::invalid_argument("NGS2: voice param " + Ngs2Hex(param.id) + " is too small");
return reinterpret_cast<const TParam&>(param);
}
static Ngs2Port& PortAt(Ngs2Voice& voice, std::uint32_t port) {
if (port >= voice.ports.size()) throw std::invalid_argument("NGS2: voice port " + std::to_string(port) + " is out of range");
return voice.ports[port];
}
static void SetMatrixLevels(Ngs2Voice& voice, std::uint32_t matrixId, const float* levels, std::uint32_t numLevels) {
if (matrixId >= voice.matrices.size() || numLevels > NGS2_MAX_CHANNELS * NGS2_MAX_CHANNELS || (levels == nullptr && numLevels != 0)) {
APS5_INVALID_ARG_EX;
}
voice.matrices[matrixId].assign(levels, levels + numLevels);
}
static void SetPortMatrix(Ngs2Voice& voice, std::uint32_t port, std::int32_t matrixId) {
if (matrixId < -1 || matrixId >= static_cast<std::int32_t>(voice.matrices.size())) APS5_INVALID_ARG_EX;
PortAt(voice, port).matrix = matrixId;
}
static void Patch(Ngs2Voice& voice, const Ngs2VoicePatchParam& patch) {
auto& port = PortAt(voice, patch.port);
if (patch.dest_input_id != 0) throw std::runtime_error("NGS2: voice input " + std::to_string(patch.dest_input_id) + " is not implemented");
if (patch.dest_handle == 0) {
port.dest = nullptr;
return;
}
auto* dest = Ngs2FindVoice(patch.dest_handle);
if (dest == nullptr || dest->rack->system != voice.rack->system || dest->rack->rackId == SCE_NGS2_RACK_ID_SAMPLER) APS5_INVALID_ARG_EX;
port.dest = dest;
}
static void ApplyCommonParam(Ngs2Voice& voice, const Ngs2VoiceParamHeader& param) {
switch (param.id) {
case SCE_NGS2_VOICE_PARAM_MATRIX_LEVELS: {
const auto& levels = ParamAs<Ngs2VoiceMatrixLevelsParam>(param);
SetMatrixLevels(voice, levels.matrix_id, levels.levels, levels.num_levels);
break;
}
case SCE_NGS2_VOICE_PARAM_PORT_VOLUME: {
const auto& volume = ParamAs<Ngs2VoicePortVolumeParam>(param);
PortAt(voice, volume.port).volume = volume.level;
break;
}
case SCE_NGS2_VOICE_PARAM_PORT_MATRIX: {
const auto& matrix = ParamAs<Ngs2VoicePortMatrixParam>(param);
SetPortMatrix(voice, matrix.port, matrix.matrix_id);
break;
}
case SCE_NGS2_VOICE_PARAM_PORT_DELAY: {
const auto& delay = ParamAs<Ngs2VoicePortDelayParam>(param);
PortAt(voice, delay.port);
if (delay.num_samples != 0) throw std::runtime_error("NGS2: port delay is not implemented");
break;
}
case SCE_NGS2_VOICE_PARAM_PATCH: Patch(voice, ParamAs<Ngs2VoicePatchParam>(param)); break;
case SCE_NGS2_VOICE_PARAM_EVENT: voice.SetEvent(ParamAs<Ngs2VoiceEventParam>(param).event_id); break;
case SCE_NGS2_VOICE_PARAM_CALLBACK: {
const auto& callback = ParamAs<Ngs2VoiceCallbackParam>(param);
voice.callback = callback.callback;
voice.callbackData = callback.callback_data;
voice.callbackFlags = callback.flags;
break;
}
default: throw std::runtime_error("NGS2: voice param " + Ngs2Hex(param.id) + " is not implemented");
}
}
static void SetupSampler(Ngs2Voice& voice, const Ngs2WaveformFormat& format) {
voice.ResetSetup();
if (format.waveform_type == 0 && format.num_channels == 0 && format.sample_rate == 0) return;
if (format.waveform_type != SCE_NGS2_WAVEFORM_TYPE_PCM_I16L) throw std::runtime_error("NGS2: waveform type " + Ngs2Hex(format.waveform_type) + " is not implemented");
if (format.frame_offset != 0 || format.frame_margin != 0) throw std::runtime_error("NGS2: waveform frame offset and margin are not implemented");
if (format.num_channels == 0 || format.num_channels > NGS2_MAX_CHANNELS || format.sample_rate == 0) APS5_INVALID_ARG_EX;
voice.channels = format.num_channels;
voice.sampleRate = format.sample_rate;
}
static void AddWaveformBlocks(Ngs2Voice& voice, const Ngs2SamplerVoiceWaveformBlocksParam& param) {
constexpr std::uint32_t knownFlags = SCE_NGS2_WAVEFORM_BLOCKS_FLAG_CONTINUE | SCE_NGS2_WAVEFORM_BLOCKS_FLAG_RESET;
if ((param.flags & ~knownFlags) != 0) throw std::runtime_error("NGS2: waveform block flags " + Ngs2Hex(param.flags) + " are not implemented");
if (voice.channels == 0 || (param.num_blocks != 0 && (param.blocks == nullptr || param.data == nullptr))) APS5_INVALID_ARG_EX;
const bool reset = (param.flags & SCE_NGS2_WAVEFORM_BLOCKS_FLAG_RESET) != 0;
if (!voice.acceptsBlocks && !reset) throw std::invalid_argument("NGS2: the voice waveform was already closed");
if (reset) {
voice.blocks.clear();
voice.phase = 0;
voice.waveformEnd = nullptr;
}
voice.acceptsBlocks = (param.flags & SCE_NGS2_WAVEFORM_BLOCKS_FLAG_CONTINUE) != 0;
const std::size_t frameBytes = voice.channels * sizeof(std::int16_t);
for (std::uint32_t i = 0; i < param.num_blocks; i++) {
const auto& block = param.blocks[i];
if (block.num_samples == 0 && block.data_size == 0) continue;
if (block.num_samples == 0 || (static_cast<std::uint64_t>(block.num_skip_samples) + block.num_samples) * frameBytes > block.data_size) {
throw std::invalid_argument("NGS2: waveform block " + std::to_string(i) + " does not fit its data");
}
voice.blocks.push_back({static_cast<const std::uint8_t*>(param.data) + block.data_offset, block});
}
}
static void ApplySamplerParam(Ngs2Voice& voice, const Ngs2VoiceParamHeader& param) {
switch (param.id) {
case SCE_NGS2_SAMPLER_VOICE_PARAM_SETUP: SetupSampler(voice, ParamAs<Ngs2SamplerVoiceSetupParam>(param).format); break;
case SCE_NGS2_SAMPLER_VOICE_PARAM_ADD_WAVEFORM_BLOCKS: AddWaveformBlocks(voice, ParamAs<Ngs2SamplerVoiceWaveformBlocksParam>(param)); break;
case SCE_NGS2_SAMPLER_VOICE_PARAM_EXIT_LOOP: {
const auto looping = std::find_if(voice.blocks.begin(), voice.blocks.end(), [](const Ngs2Block& block) { return block.info.num_repeats != 0; });
if (looping != voice.blocks.end()) looping->info.num_repeats = 0;
break;
}
case SCE_NGS2_SAMPLER_VOICE_PARAM_PITCH: {
const float ratio = ParamAs<Ngs2SamplerVoicePitchParam>(param).ratio;
if (!std::isfinite(ratio) || ratio < 0.0f) APS5_INVALID_ARG_EX;
voice.pitch = ratio;
break;
}
default: throw std::runtime_error("NGS2: sampler voice param " + Ngs2Hex(param.id) + " is not implemented");
}
}
static void SetupMixer(Ngs2Voice& voice, std::uint32_t numChannels) {
if (numChannels == 0 || numChannels > voice.rack->maxChannels) APS5_INVALID_ARG_EX;
voice.ResetSetup();
voice.channels = numChannels;
}
static void ApplyParam(Ngs2Voice& voice, const Ngs2VoiceParamHeader& param) {
const std::uint32_t rackId = param.id >> 16;
if (rackId == 0) {
ApplyCommonParam(voice, param);
return;
}
if (rackId != voice.rack->rackId) throw std::invalid_argument("NGS2: voice param " + Ngs2Hex(param.id) + " belongs to another rack");
switch (rackId) {
case SCE_NGS2_RACK_ID_SAMPLER: ApplySamplerParam(voice, param); return;
case SCE_NGS2_RACK_ID_SUBMIXER:
if (param.id != SCE_NGS2_SUBMIXER_VOICE_PARAM_SETUP) break;
if (ParamAs<Ngs2SubmixerVoiceSetupParam>(param).flags != 0) throw std::runtime_error("NGS2: submixer setup flags are not implemented");
SetupMixer(voice, ParamAs<Ngs2SubmixerVoiceSetupParam>(param).num_io_channels);
return;
case SCE_NGS2_RACK_ID_MASTERING:
if (param.id == SCE_NGS2_MASTERING_VOICE_PARAM_SETUP) {
SetupMixer(voice, ParamAs<Ngs2MasteringVoiceSetupParam>(param).num_io_channels);
return;
}
if (param.id == SCE_NGS2_MASTERING_VOICE_PARAM_OUTPUT) {
voice.outputId = ParamAs<Ngs2MasteringVoiceOutputParam>(param).output_id;
return;
}
break;
default: break;
}
throw std::runtime_error("NGS2: voice param " + Ngs2Hex(param.id) + " is not implemented");
}
static Ngs2Voice& CheckedVoice(Ngs2Handle handle) {
auto* voice = Ngs2FindVoice(handle);
if (voice == nullptr) throw std::invalid_argument("NGS2: invalid voice handle");
return *voice;
}
#pragma GCC visibility push(default)
extern "C" {
int APS5_VABI sceNgs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamHeader* param_list) {
std::lock_guard lock(Ngs2Mutex());
auto& voice = CheckedVoice(voice_handle);
if (param_list == nullptr) APS5_INVALID_ARG_EX;
for (const auto* param = param_list;; param = reinterpret_cast<const Ngs2VoiceParamHeader*>(reinterpret_cast<const std::uint8_t*>(param) + param->next)) {
ApplyParam(voice, *param);
if (param->next == 0) break;
}
return SCE_NGS2_OK;
}
int APS5_VABI sceNgs2VoiceRunCommands(uintptr_t voice_handle, const Ngs2VoiceCommand* commands, size_t num_commands) {
std::lock_guard lock(Ngs2Mutex());
auto& voice = CheckedVoice(voice_handle);
if (commands == nullptr && num_commands != 0) APS5_INVALID_ARG_EX;
for (std::size_t i = 0; i < num_commands; i++) {
const auto& command = commands[i];
const std::uint32_t index = command.id >> 24;
const std::uint32_t id = command.id & 0xffffff;
const std::uint8_t expectedType = id == COMMAND_EVENT ? COMMAND_TYPE_UINT
: id == COMMAND_MATRIX_LEVELS ? COMMAND_TYPE_FLOAT_ARRAY
: id == COMMAND_PORT_VOLUME ? COMMAND_TYPE_FLOAT
: id == COMMAND_PORT_MATRIX ? COMMAND_TYPE_INT : 0;
if (expectedType == 0) throw std::runtime_error("NGS2: voice command " + Ngs2Hex(command.id) + " is not implemented");
if (command.type != expectedType) throw std::invalid_argument("NGS2: voice command " + Ngs2Hex(command.id) + " has value type " + Ngs2Hex(command.type));
if (id == COMMAND_EVENT) voice.SetEvent(command.value.u);
else if (id == COMMAND_MATRIX_LEVELS) SetMatrixLevels(voice, index, command.value.levels, command.count);
else if (id == COMMAND_PORT_VOLUME) PortAt(voice, index).volume = command.value.f;
else SetPortMatrix(voice, index, command.value.i);
}
return SCE_NGS2_OK;
}
int APS5_VABI sceNgs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* state, size_t state_size) {
if (state == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
std::lock_guard lock(Ngs2Mutex());
const auto& voice = CheckedVoice(voice_handle);
switch (voice.rack->rackId) {
case SCE_NGS2_RACK_ID_SAMPLER: {
if (state_size != sizeof(Ngs2SamplerVoiceState)) return SCE_NGS2_ERROR_INVALID_OUT_SIZE;
auto& sampler = *reinterpret_cast<Ngs2SamplerVoiceState*>(state);
sampler = {};
sampler.voice_state.state_flags = voice.stateFlags;
sampler.envelope_height = 1.0f;
sampler.num_decoded_samples = voice.decodedSamples;
sampler.decoded_data_size = voice.decodedSamples * voice.channels * sizeof(std::int16_t);
sampler.user_data = voice.blocks.empty() ? 0 : voice.blocks.front().info.user_data;
sampler.waveform_data = voice.WaveformData();
return SCE_NGS2_OK;
}
case SCE_NGS2_RACK_ID_SUBMIXER: {
if (state_size != sizeof(Ngs2SubmixerVoiceState)) return SCE_NGS2_ERROR_INVALID_OUT_SIZE;
auto& submixer = *reinterpret_cast<Ngs2SubmixerVoiceState*>(state);
submixer = {};
submixer.voice_state.state_flags = voice.stateFlags;
return SCE_NGS2_OK;
}
default: throw std::runtime_error("NGS2: voice state of rack " + Ngs2Hex(voice.rack->rackId) + " is not implemented");
}
}
int APS5_VABI sceNgs2VoiceGetStateFlags(uintptr_t voice_handle, uint32_t* state_flags) {
if (state_flags == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS;
std::lock_guard lock(Ngs2Mutex());
*state_flags = CheckedVoice(voice_handle).stateFlags;
return SCE_NGS2_OK;
}
}
#pragma GCC visibility pop

View File

@ -0,0 +1,272 @@
#include "prx/libSceNgs2.native/include/Ngs2Types.hpp"
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <vector>
extern "C" {
int APS5_VABI sceNgs2SystemResetOption(Ngs2SystemOption*);
int APS5_VABI sceNgs2SystemQueryBufferSize(const Ngs2SystemOption*, Ngs2ContextBufferInfo*);
int APS5_VABI sceNgs2SystemCreate(const Ngs2SystemOption*, const Ngs2ContextBufferInfo*, uintptr_t*);
int APS5_VABI sceNgs2SystemCreateWithAllocator(const Ngs2SystemOption*, const Ngs2BufferAllocator*, uintptr_t*);
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 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*);
int APS5_VABI sceNgs2RackCreateWithAllocator(uintptr_t, uint32_t, const Ngs2RackOption*, const Ngs2BufferAllocator*, uintptr_t*);
int APS5_VABI sceNgs2RackDestroy(uintptr_t, Ngs2ContextBufferInfo*);
int APS5_VABI sceNgs2RackGetVoiceHandle(uintptr_t, uint32_t, uintptr_t*);
int APS5_VABI sceNgs2VoiceControl(uintptr_t, const Ngs2VoiceParamHeader*);
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*);
}
static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "NGS2 check failed at line %d\n", line);
std::abort();
}
}
#define Require(value) Check((value), __LINE__)
static constexpr std::uint32_t Grain = 8;
static std::vector<std::uint64_t> buffers[16];
static std::size_t usedBuffers = 0;
static Ngs2ContextBufferInfo Buffer(const Ngs2ContextBufferInfo& query) {
auto& storage = buffers[usedBuffers++];
storage.resize(query.host_buffer_size / sizeof(std::uint64_t) + 1);
Ngs2ContextBufferInfo info{};
info.host_buffer = storage.data();
info.host_buffer_size = query.host_buffer_size;
return info;
}
static uintptr_t CreateSystem() {
Ngs2ContextBufferInfo query{};
Require(sceNgs2SystemQueryBufferSize(nullptr, &query) == SCE_NGS2_OK && query.host_buffer_size != 0);
const auto info = Buffer(query);
uintptr_t system = 0;
Require(sceNgs2SystemCreate(nullptr, &info, &system) == SCE_NGS2_OK && system != 0);
Require(sceNgs2SystemSetGrainSamples(system, Grain) == SCE_NGS2_OK);
return system;
}
static uintptr_t CreateRack(uintptr_t system, std::uint32_t rackId) {
Ngs2ContextBufferInfo query{};
Require(sceNgs2RackQueryBufferSize(rackId, nullptr, &query) == SCE_NGS2_OK);
const auto info = Buffer(query);
uintptr_t rack = 0;
Require(sceNgs2RackCreate(system, rackId, nullptr, &info, &rack) == SCE_NGS2_OK && rack != 0);
return rack;
}
static uintptr_t Voice(uintptr_t rack) {
uintptr_t voice = 0;
Require(sceNgs2RackGetVoiceHandle(rack, 0, &voice) == SCE_NGS2_OK && voice != 0);
return voice;
}
template <typename TParam>
static void Control(uintptr_t voice, std::uint32_t id, TParam param) {
param.header = {static_cast<std::uint16_t>(sizeof(TParam)), 0, id};
Require(sceNgs2VoiceControl(voice, &param.header) == SCE_NGS2_OK);
}
static void Event(uintptr_t voice, std::uint32_t eventId) {
Control(voice, SCE_NGS2_VOICE_PARAM_EVENT, Ngs2VoiceEventParam{{}, eventId});
}
static void Patch(uintptr_t source, uintptr_t dest) {
Control(source, SCE_NGS2_VOICE_PARAM_PATCH, Ngs2VoicePatchParam{{}, 0, 0, dest});
}
static uintptr_t Mastering(uintptr_t system, std::uint32_t channels) {
const auto voice = Voice(CreateRack(system, SCE_NGS2_RACK_ID_MASTERING));
Control(voice, SCE_NGS2_MASTERING_VOICE_PARAM_SETUP, Ngs2MasteringVoiceSetupParam{{}, channels, 0});
Control(voice, SCE_NGS2_MASTERING_VOICE_PARAM_OUTPUT, Ngs2MasteringVoiceOutputParam{{}, 0, 0});
Event(voice, SCE_NGS2_VOICE_EVENT_PLAY);
return voice;
}
static uintptr_t Sampler(uintptr_t system, const std::vector<std::int16_t>& pcm, std::uint32_t repeats) {
const auto voice = Voice(CreateRack(system, SCE_NGS2_RACK_ID_SAMPLER));
Control(voice, SCE_NGS2_SAMPLER_VOICE_PARAM_SETUP, Ngs2SamplerVoiceSetupParam{{}, {SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 1, 48000, 0, 0, 0}});
const Ngs2WaveformBlock block{0, pcm.size() * sizeof(std::int16_t), repeats, 0, static_cast<std::uint32_t>(pcm.size()), 0, 0x55};
Control(voice, SCE_NGS2_SAMPLER_VOICE_PARAM_ADD_WAVEFORM_BLOCKS, Ngs2SamplerVoiceWaveformBlocksParam{{}, pcm.data(), 0, 1, &block});
return voice;
}
static std::vector<std::uint32_t> callbackFlags;
static void APS5_VABI OnBlock(const Ngs2VoiceCallbackInfo* info) {
Require(info->callback_data == 7 && info->user_data == 0x55);
callbackFlags.push_back(info->flag);
}
static std::uint32_t Flags(uintptr_t voice) {
std::uint32_t flags = 0xffffffff;
Require(sceNgs2VoiceGetStateFlags(voice, &flags) == SCE_NGS2_OK);
return flags;
}
static std::vector<std::int16_t> RenderI16(uintptr_t system) {
std::vector<std::int16_t> out(Grain, -1);
const Ngs2RenderBufferInfo info{out.data(), out.size() * sizeof(std::int16_t), SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 1};
Require(sceNgs2SystemRender(system, &info, 1) == SCE_NGS2_OK);
return out;
}
static void TestErrorsAndInfo() {
Ngs2SystemOption option{};
Require(sceNgs2SystemResetOption(&option) == SCE_NGS2_OK);
Require(option.size == sizeof(option) && option.max_grain_samples == 512 && option.num_grain_samples == 256 && option.sample_rate == 48000);
Require(sceNgs2SystemQueryBufferSize(&option, nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2RackQueryBufferSize(SCE_NGS2_RACK_ID_SAMPLER, nullptr, nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Ngs2SystemInfo info{};
Require(sceNgs2SystemGetInfo(0x1234, &info, sizeof(info)) == SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE);
Require(sceNgs2RackDestroy(0x1234, nullptr) == SCE_NGS2_ERROR_INVALID_RACK_HANDLE);
const auto system = CreateSystem();
CreateRack(system, SCE_NGS2_RACK_ID_SAMPLER);
Require(sceNgs2SystemGetInfo(system, nullptr, sizeof(info)) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2SystemGetInfo(system, &info, sizeof(info) - 1) == SCE_NGS2_ERROR_INVALID_OUT_SIZE);
Require(sceNgs2SystemGetInfo(system, &info, sizeof(info)) == SCE_NGS2_OK);
Require(info.system_handle == system && info.uid != 0 && info.rack_count == 1 && info.sample_rate == 48000);
Require(info.num_grain_samples == Grain && info.max_grain_samples == 512 && info.render_count == 0);
Ngs2ContextBufferInfo released{};
Require(sceNgs2SystemDestroy(system, &released) == SCE_NGS2_OK && released.host_buffer != nullptr);
Require(sceNgs2SystemGetInfo(system, &info, sizeof(info)) == SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE);
}
static void TestPcmBlockEnd() {
const auto system = CreateSystem();
const auto master = Mastering(system, 1);
std::vector<std::int16_t> pcm;
for (int i = 0; i < 12; i++) pcm.push_back(static_cast<std::int16_t>(i * 1000 - 4000));
const auto sampler = Sampler(system, pcm, 0);
Patch(sampler, master);
Control(sampler, SCE_NGS2_VOICE_PARAM_CALLBACK, Ngs2VoiceCallbackParam{{}, OnBlock, 7, SCE_NGS2_VOICE_CALLBACK_FLAG_BLOCK_END, 0});
Require(Flags(sampler) == 0);
Event(sampler, SCE_NGS2_VOICE_EVENT_PLAY);
Require(Flags(sampler) == SCE_NGS2_VOICE_STATE_FLAG_INUSE);
callbackFlags.clear();
auto out = RenderI16(system);
for (std::uint32_t i = 0; i < Grain; i++) Require(out[i] == pcm[i]);
Require(Flags(sampler) == (SCE_NGS2_VOICE_STATE_FLAG_INUSE | SCE_NGS2_VOICE_STATE_FLAG_PLAYING) && callbackFlags.empty());
Ngs2SamplerVoiceState state{};
Require(sceNgs2VoiceGetState(sampler, &state.voice_state, sizeof(state) - 8) == SCE_NGS2_ERROR_INVALID_OUT_SIZE);
Require(sceNgs2VoiceGetState(sampler, &state.voice_state, sizeof(state)) == SCE_NGS2_OK);
Require(state.num_decoded_samples == Grain && state.decoded_data_size == Grain * 2 && state.user_data == 0x55);
Require(state.waveform_data == pcm.data() + Grain);
out = RenderI16(system);
for (std::uint32_t i = 0; i < Grain; i++) Require(out[i] == (i < 4 ? pcm[Grain + i] : 0));
Require(callbackFlags.size() == 1 && callbackFlags[0] == SCE_NGS2_VOICE_CALLBACK_FLAG_BLOCK_END);
Require(Flags(sampler) == 0);
Require(sceNgs2VoiceGetState(sampler, &state.voice_state, sizeof(state)) == SCE_NGS2_OK);
Require(state.num_decoded_samples == pcm.size() && state.waveform_data == pcm.data() + pcm.size());
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
}
static void TestPitchAndRepeat() {
const auto system = CreateSystem();
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);
Control(sampler, SCE_NGS2_SAMPLER_VOICE_PARAM_PITCH, Ngs2SamplerVoicePitchParam{{}, 0.5f});
Control(sampler, SCE_NGS2_VOICE_PARAM_CALLBACK,
Ngs2VoiceCallbackParam{{}, OnBlock, 7, SCE_NGS2_VOICE_CALLBACK_FLAG_BLOCK_END | SCE_NGS2_VOICE_CALLBACK_FLAG_BLOCK_REPEAT, 0});
Event(sampler, SCE_NGS2_VOICE_EVENT_PLAY);
callbackFlags.clear();
const std::int16_t first[Grain] = {0, 500, 1000, 1500, 2000, 2500, 3000, 1500};
auto out = RenderI16(system);
for (std::uint32_t i = 0; i < Grain; i++) Require(out[i] == first[i]);
Require(callbackFlags.size() == 1 && callbackFlags[0] == SCE_NGS2_VOICE_CALLBACK_FLAG_BLOCK_REPEAT);
const std::int16_t second[Grain] = {0, 500, 1000, 1500, 2000, 2500, 3000, 3000};
out = RenderI16(system);
for (std::uint32_t i = 0; i < Grain; i++) Require(out[i] == second[i]);
Require(callbackFlags.size() == 2 && callbackFlags[1] == SCE_NGS2_VOICE_CALLBACK_FLAG_BLOCK_END);
Require(Flags(sampler) == 0);
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
}
static void TestSubmixerMatrix() {
const auto system = CreateSystem();
const auto master = Mastering(system, 2);
const auto submixer = Voice(CreateRack(system, SCE_NGS2_RACK_ID_SUBMIXER));
Control(submixer, SCE_NGS2_SUBMIXER_VOICE_PARAM_SETUP, Ngs2SubmixerVoiceSetupParam{{}, 2, 0});
Patch(submixer, master);
const Ngs2VoiceCommand play{2, 0, 4, 0, {.u = SCE_NGS2_VOICE_EVENT_PLAY}};
Require(sceNgs2VoiceRunCommands(submixer, &play, 1) == SCE_NGS2_OK);
const std::vector<std::int16_t> pcm(Grain, 16384);
const auto sampler = Sampler(system, pcm, 0);
Patch(sampler, submixer);
const float levels[2] = {1.0f, 0.5f};
Control(sampler, SCE_NGS2_VOICE_PARAM_MATRIX_LEVELS, Ngs2VoiceMatrixLevelsParam{{}, 0, 2, levels});
Control(sampler, SCE_NGS2_VOICE_PARAM_PORT_MATRIX, Ngs2VoicePortMatrixParam{{}, 0, 0});
Control(sampler, SCE_NGS2_VOICE_PARAM_PORT_VOLUME, Ngs2VoicePortVolumeParam{{}, 0, 0.5f});
Require(sceNgs2VoiceRunCommands(sampler, &play, 1) == SCE_NGS2_OK);
std::vector<float> out(Grain * 2, -1.0f);
const Ngs2RenderBufferInfo info{out.data(), out.size() * sizeof(float), SCE_NGS2_WAVEFORM_TYPE_PCM_F32L, 2};
Require(sceNgs2SystemRender(system, &info, 1) == SCE_NGS2_OK);
for (std::uint32_t i = 0; i < Grain; i++) Require(out[i * 2] == 0.25f && out[i * 2 + 1] == 0.125f);
Ngs2SubmixerVoiceState state{};
Require(sceNgs2VoiceGetState(submixer, &state.voice_state, sizeof(state)) == SCE_NGS2_OK);
Require(state.voice_state.state_flags == (SCE_NGS2_VOICE_STATE_FLAG_INUSE | SCE_NGS2_VOICE_STATE_FLAG_PLAYING));
Require(sceNgs2SystemRender(system, &info, 1) == SCE_NGS2_OK);
for (float sample : out) Require(sample == 0.0f);
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
}
static int allocations = 0;
static std::int32_t APS5_VABI Allocate(Ngs2ContextBufferInfo* info) {
Require(info->host_buffer == nullptr && info->host_buffer_size != 0 && info->user_data == 9);
info->host_buffer = std::calloc(1, info->host_buffer_size);
allocations++;
return SCE_NGS2_OK;
}
static std::int32_t APS5_VABI Release(Ngs2ContextBufferInfo* info) {
Require(info->host_buffer != nullptr && info->user_data == 9);
std::free(info->host_buffer);
allocations--;
return SCE_NGS2_OK;
}
static void TestAllocator() {
const Ngs2BufferAllocator allocator{Allocate, Release, 9};
uintptr_t system = 0;
Require(sceNgs2SystemCreateWithAllocator(nullptr, &allocator, &system) == SCE_NGS2_OK && allocations == 1);
uintptr_t sampler = 0;
uintptr_t master = 0;
Require(sceNgs2RackCreateWithAllocator(system, SCE_NGS2_RACK_ID_SAMPLER, nullptr, &allocator, &sampler) == SCE_NGS2_OK);
Require(sceNgs2RackCreateWithAllocator(system, SCE_NGS2_RACK_ID_MASTERING, nullptr, &allocator, &master) == SCE_NGS2_OK && allocations == 3);
Ngs2ContextBufferInfo released{};
Require(sceNgs2RackDestroy(sampler, &released) == SCE_NGS2_OK && allocations == 2 && released.host_buffer == nullptr);
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK && allocations == 0);
Require(sceNgs2RackDestroy(master, nullptr) == SCE_NGS2_ERROR_INVALID_RACK_HANDLE);
}
int main() {
TestErrorsAndInfo();
TestPcmBlockEnd();
TestPitchAndRepeat();
TestSubmixerMatrix();
TestAllocator();
return 0;
}

View File

@ -5,199 +5,6 @@
extern "C" {
int APS5_VABI sceNgs2CalcWaveformBlock(const Ngs2WaveformFormat* format, uint32_t sample_pos, uint32_t num_samples, Ngs2WaveformBlock* block) {
(void)format;
(void)sample_pos;
(void)num_samples;
(void)block;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GeomApply(const Ngs2GeomListenerWork* listener, const Ngs2GeomSourceParam* source, Ngs2GeomAttribute* out_attrib, uint32_t flags) {
(void)listener;
(void)source;
(void)out_attrib;
(void)flags;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GeomCalcListener(const Ngs2GeomListenerParam* param, Ngs2GeomListenerWork* out_work, uint32_t flags) {
(void)param;
(void)out_work;
(void)flags;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GeomResetListenerParam(Ngs2GeomListenerParam* out_listener_param) {
(void)out_listener_param;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2GeomResetSourceParam(Ngs2GeomSourceParam* out_source_param) {
(void)out_source_param;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2PanGetVolumeMatrix(Ngs2PanWork* work, const Ngs2PanParam* params, uint32_t num_params, uint32_t matrix_format, float* out_volume_matrix) {
(void)work;
(void)params;
(void)num_params;
(void)matrix_format;
(void)out_volume_matrix;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2PanInit(Ngs2PanWork* work, const float* speaker_angles, float unit_angle, uint32_t num_speakers) {
(void)work;
(void)speaker_angles;
(void)unit_angle;
(void)num_speakers;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2ParseWaveformData(const void* data, size_t data_size, Ngs2WaveformInfo* info) {
(void)data;
(void)data_size;
(void)info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackCreate(uintptr_t system_handle, uint32_t rack_id, const Ngs2RackOption* option, const Ngs2ContextBufferInfo* buffer_info, uintptr_t* handle) {
(void)system_handle;
(void)rack_id;
(void)option;
(void)buffer_info;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackCreateWithAllocator(uintptr_t system_handle, uint32_t rack_id, const Ngs2RackOption* option, const Ngs2BufferAllocator* allocator, uintptr_t* handle) {
(void)system_handle;
(void)rack_id;
(void)option;
(void)allocator;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackDestroy(uintptr_t rack_handle, Ngs2ContextBufferInfo* buffer_info) {
(void)rack_handle;
(void)buffer_info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackGetVoiceHandle(uintptr_t rack_handle, uint32_t voice_id, uintptr_t* handle) {
(void)rack_handle;
(void)voice_id;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackLock(uintptr_t rack_handle) {
(void)rack_handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackQueryBufferSize(uint32_t rack_id, const Ngs2RackOption* option, Ngs2ContextBufferInfo* buffer_info) {
(void)rack_id;
(void)option;
(void)buffer_info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2RackUnlock(uintptr_t rack_handle) {
(void)rack_handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemCreate(const Ngs2SystemOption* option, const Ngs2ContextBufferInfo* buffer_info, uintptr_t* handle) {
(void)option;
(void)buffer_info;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemCreateWithAllocator(const Ngs2SystemOption* option, const Ngs2BufferAllocator* allocator, uintptr_t* handle) {
(void)option;
(void)allocator;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemDestroy(uintptr_t system_handle, Ngs2ContextBufferInfo* buffer_info) {
(void)system_handle;
(void)buffer_info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemGetInfo(uintptr_t system_handle, Ngs2SystemInfo* info, size_t info_size) {
(void)system_handle;
(void)info;
(void)info_size;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemQueryBufferSize(const Ngs2SystemOption* option, Ngs2ContextBufferInfo* buffer_info) {
(void)option;
(void)buffer_info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemRender(uintptr_t system_handle, const Ngs2RenderBufferInfo* buffer_info, uint32_t num_buffer_info) {
(void)system_handle;
(void)buffer_info;
(void)num_buffer_info;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2SystemSetGrainSamples(uintptr_t system_handle, uint32_t num_samples) {
(void)system_handle;
(void)num_samples;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2VoiceControl(uintptr_t voice_handle, const Ngs2VoiceParamHeader* param_list) {
(void)voice_handle;
(void)param_list;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* state, size_t state_size) {
(void)voice_handle;
(void)state;
(void)state_size;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNgs2VoiceGetStateFlags(uintptr_t voice_handle, uint32_t* state_flags) {
(void)voice_handle;
(void)state_flags;
NotImplemented_nid_no_patch(__func__);
return 0;
}
APS5_DUMMY_FUN
}