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:
parent
c00bc19c0d
commit
90295d3733
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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++;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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, ¶m.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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue