mirror of
https://github.com/Vita3K/Vita3K.git
synced 2025-04-02 11:02:10 -04:00
1008 lines
30 KiB
C++
1008 lines
30 KiB
C++
// Vita3K emulator project
|
|
// Copyright (C) 2022 Vita3K team
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along
|
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
#include <ngs/modules/atrac9.h>
|
|
#include <ngs/system.h>
|
|
#include <util/log.h>
|
|
|
|
#include "SceNgs.h"
|
|
|
|
#define SCE_NGS_MAX_SYSTEM_CHANNELS 2
|
|
|
|
using SceNgsSynthSystemHandle = Ptr<ngs::System>;
|
|
using SceNgsRackHandle = Ptr<ngs::Rack>;
|
|
using SceNgsVoiceHandle = Ptr<ngs::Voice>;
|
|
using SceNgsPatchHandle = Ptr<ngs::Patch>;
|
|
|
|
struct SceNgsVolumeMatrix {
|
|
SceFloat32 matrix[SCE_NGS_MAX_SYSTEM_CHANNELS][SCE_NGS_MAX_SYSTEM_CHANNELS];
|
|
};
|
|
|
|
struct SceNgsPatchAudioPropInfo {
|
|
SceInt32 out_channels;
|
|
SceInt32 in_channels;
|
|
SceNgsVolumeMatrix volume_matrix;
|
|
};
|
|
|
|
struct SceNgsVoiceInfo {
|
|
SceUInt32 voice_state;
|
|
SceUInt32 num_modules;
|
|
SceUInt32 num_inputs;
|
|
SceUInt32 num_outputs;
|
|
SceUInt32 num_patches_per_output;
|
|
SceUInt32 update_passed;
|
|
};
|
|
|
|
static_assert(sizeof(SceNgsPatchAudioPropInfo) == 24);
|
|
|
|
struct SceNgsPatchDeliveryInfo {
|
|
SceNgsVoiceHandle source_voice_handle;
|
|
SceInt32 output_index;
|
|
SceInt32 output_subindex;
|
|
SceNgsVoiceHandle dest_voice_handle;
|
|
SceInt32 input_index;
|
|
};
|
|
|
|
static_assert(sizeof(SceNgsPatchDeliveryInfo) == 20);
|
|
|
|
static constexpr SceUInt32 SCE_NGS_OK = 0;
|
|
static constexpr SceUInt32 SCE_NGS_ERROR = 0x804A0001;
|
|
static constexpr SceUInt32 SCE_NGS_ERROR_INVALID_ARG = 0x804A0002;
|
|
static constexpr SceUInt32 SCE_NGS_ERROR_INVALID_STATE = 0x804A0010;
|
|
static constexpr SceUInt32 SCE_NGS_ERROR_PARAM_OUT_OF_RANGE = 0x804A0009;
|
|
static constexpr SceUInt32 SCE_NGS_SIZE_MISMATCH = 0x804A000D;
|
|
|
|
static constexpr SceUInt32 SCE_NGS_VOICE_STATE_AVAILABLE = 0;
|
|
static constexpr SceUInt32 SCE_NGS_VOICE_STATE_ACTIVE = 1 << 0;
|
|
static constexpr SceUInt32 SCE_NGS_VOICE_STATE_FINALIZE = 1 << 2;
|
|
static constexpr SceUInt32 SCE_NGS_VOICE_STATE_UNLOADING = 1 << 3;
|
|
static constexpr SceUInt32 SCE_NGS_VOICE_STATE_PENDING = 1 << 4;
|
|
static constexpr SceUInt32 SCE_NGS_VOICE_STATE_PAUSED = 1 << 5;
|
|
static constexpr SceUInt32 SCE_NGS_VOICE_STATE_KEY_OFF = 1 << 6;
|
|
|
|
static constexpr SceUInt32 SCE_NGS_MODULE_FLAG_NOT_BYPASSED = 0;
|
|
static constexpr SceUInt32 SCE_NGS_MODULE_FLAG_BYPASSED = 2;
|
|
|
|
static constexpr SceUInt32 SCE_NGS_VOICE_INIT_BASE = 0;
|
|
static constexpr SceUInt32 SCE_NGS_VOICE_INIT_ROUTING = 1;
|
|
static constexpr SceUInt32 SCE_NGS_VOICE_INIT_PRESET = 2;
|
|
static constexpr SceUInt32 SCE_NGS_VOICE_INIT_CALLBACKS = 4;
|
|
static constexpr SceUInt32 SCE_NGS_VOICE_INIT_ALL = 7;
|
|
|
|
static constexpr SceInt32 SCE_NGS_SAMPLE_OFFSET_FROM_AT9_HEADER = 1 << 31;
|
|
|
|
EXPORT(int, sceNgsAT9GetSectionDetails, std::uint32_t samples_start, const std::uint32_t num_samples, const std::uint32_t config_data, ngs::atrac9::SkipBufferInfo *info) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return -1;
|
|
}
|
|
if (!info) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
// Check magic!
|
|
if ((config_data & 0xFF) != 0xFE && info) {
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
}
|
|
samples_start &= ~SCE_NGS_SAMPLE_OFFSET_FROM_AT9_HEADER;
|
|
|
|
get_buffer_parameter(samples_start, num_samples, config_data, *info);
|
|
return 0;
|
|
}
|
|
|
|
EXPORT(int, sceNgsModuleGetNumPresets) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceNgsModuleGetPreset) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceNgsPatchCreateRouting, ngs::PatchSetupInfo *patch_info, SceNgsPatchHandle *handle) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
if (!patch_info || !handle)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
// Make the scheduler order this right based on dependencies request
|
|
ngs::Voice *source = patch_info->source.get(host.mem);
|
|
|
|
if (!source)
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
|
|
LOG_TRACE("Patching 0x{:X}:{}:{} to 0x{:X}:{}", patch_info->source.address(), patch_info->source_output_index,
|
|
patch_info->source_output_index, patch_info->dest.address(), patch_info->dest_input_index);
|
|
|
|
*handle = source->rack->system->voice_scheduler.patch(host.mem, patch_info);
|
|
|
|
if (!*handle) {
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
}
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsPatchGetInfo, SceNgsPatchHandle patch_handle, SceNgsPatchAudioPropInfo *prop_info, SceNgsPatchDeliveryInfo *deli_info) {
|
|
// Always stereo
|
|
if (prop_info) {
|
|
prop_info->in_channels = 2;
|
|
prop_info->out_channels = 2;
|
|
}
|
|
|
|
if (host.cfg.current_config.disable_ngs)
|
|
return SCE_NGS_OK;
|
|
|
|
ngs::Patch *patch = patch_handle.get(host.mem);
|
|
if (!patch) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
if (prop_info) {
|
|
prop_info->volume_matrix.matrix[0][0] = patch->volume_matrix[0][0];
|
|
prop_info->volume_matrix.matrix[0][1] = patch->volume_matrix[0][1];
|
|
prop_info->volume_matrix.matrix[1][0] = patch->volume_matrix[1][0];
|
|
prop_info->volume_matrix.matrix[1][1] = patch->volume_matrix[1][1];
|
|
}
|
|
|
|
if (deli_info) {
|
|
deli_info->input_index = patch->dest_index;
|
|
deli_info->output_index = patch->output_index;
|
|
deli_info->output_subindex = patch->output_sub_index;
|
|
deli_info->source_voice_handle = Ptr<ngs::Voice>(patch->source, host.mem);
|
|
deli_info->dest_voice_handle = Ptr<ngs::Voice>(patch->dest, host.mem);
|
|
}
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(int, sceNgsPatchRemoveRouting, SceNgsPatchHandle patch_handle) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::Patch *patch = patch_handle.get(host.mem);
|
|
|
|
if (!patch_handle.valid(host.mem) || !patch) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
if (!patch->source->remove_patch(host.mem, patch_handle)) {
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
EXPORT(int, sceNgsRackGetRequiredMemorySize, SceNgsSynthSystemHandle sys_handle, ngs::RackDescription *description, uint32_t *size) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
*size = 1;
|
|
return 0;
|
|
}
|
|
|
|
*size = ngs::Rack::get_required_memspace_size(host.mem, description);
|
|
return 0;
|
|
}
|
|
|
|
EXPORT(SceUInt32, sceNgsRackGetVoiceHandle, SceNgsRackHandle rack_handle, const std::uint32_t index, SceNgsVoiceHandle *voice_handle) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
ngs::Rack *rack = rack_handle.get(host.mem);
|
|
|
|
if (!rack || !voice_handle) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
if (index >= rack->voices.size()) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
*voice_handle = rack->voices[index];
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(SceUInt32, sceNgsRackInit, SceNgsSynthSystemHandle sys_handle, ngs::BufferParamsInfo *info, const ngs::RackDescription *description, SceNgsRackHandle *handle) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
assert(sys_handle);
|
|
assert(info);
|
|
assert(description);
|
|
|
|
ngs::System *system = sys_handle.get(host.mem);
|
|
|
|
if (!ngs::init_rack(host.ngs, host.mem, system, info, description)) {
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
}
|
|
|
|
*handle = info->data.cast<ngs::Rack>();
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(int, sceNgsRackRelease) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceNgsRackSetParamErrorCallback) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceNgsSystemGetRequiredMemorySize, ngs::SystemInitParameters *params, uint32_t *size) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
*size = 1;
|
|
return 0;
|
|
}
|
|
|
|
*size = ngs::System::get_required_memspace_size(params); // System struct size
|
|
return 0;
|
|
}
|
|
|
|
EXPORT(SceUInt32, sceNgsSystemInit, Ptr<void> memspace, const std::uint32_t memspace_size, ngs::SystemInitParameters *params,
|
|
SceNgsSynthSystemHandle *handle) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
if (!ngs::init_system(host.ngs, host.mem, params, memspace, memspace_size)) {
|
|
return RET_ERROR(SCE_NGS_ERROR); // TODO: Better error code
|
|
}
|
|
|
|
*handle = memspace.cast<ngs::System>();
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(int, sceNgsSystemLock) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceNgsSystemRelease) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceNgsSystemSetFlags) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceNgsSystemSetParamErrorCallback) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceNgsSystemUnlock) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(SceUInt32, sceNgsSystemUpdate, SceNgsSynthSystemHandle handle) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::System *sys = handle.get(host.mem);
|
|
sys->voice_scheduler.update(host.kernel, host.mem, thread_id);
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoiceBypassModule, SceNgsVoiceHandle voice_handle, const SceUInt32 module, const SceUInt32 bypass_flag) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
if (!voice_handle)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
ngs::Voice *voice = voice_handle.get(host.mem);
|
|
|
|
ngs::ModuleData *storage = voice->module_storage(module);
|
|
|
|
if (!storage)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
// no need to lock a mutex for this
|
|
storage->is_bypassed = (bypass_flag & SCE_NGS_MODULE_FLAG_BYPASSED);
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetAtrac9Voice) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_ATRAC9);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetCompressorBuss) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_COMPRESSOR);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetCompressorSideChainBuss) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_SIDE_CHAIN_COMPRESSOR);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetDelayBuss) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_DELAY);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetDistortionBuss) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_DISTORTION);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetEnvelopeBuss) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_ENVELOPE);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetEqBuss) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_EQUALIZATION);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetMasterBuss) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_MASTER);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetMixerBuss) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_MIXER);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetPauserBuss) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_PAUSER);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetPitchShiftBuss) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_PITCH_SHIFT);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetReverbBuss) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_REVERB);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetSasEmuVoice) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_SAS_EMULATION);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetScreamAtrac9Voice) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_SCREAM_ATRAC9);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetScreamVoice) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_SCREAM);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetSimpleAtrac9Voice) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_SIMPLE_ATRAC9);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetSimpleVoice) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_SIMPLE);
|
|
}
|
|
|
|
EXPORT(Ptr<ngs::VoiceDefinition>, sceNgsVoiceDefGetTemplate1) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return Ptr<ngs::VoiceDefinition>(0);
|
|
}
|
|
|
|
return get_voice_definition(host.ngs, host.mem, ngs::BussType::BUSS_NORMAL_PLAYER);
|
|
}
|
|
|
|
static SceUInt32 ngsVoiceStateFromHLEState(const ngs::Voice *voice) {
|
|
SceUInt32 state;
|
|
switch (voice->state) {
|
|
case ngs::VoiceState::VOICE_STATE_AVAILABLE:
|
|
state = SCE_NGS_VOICE_STATE_AVAILABLE;
|
|
break;
|
|
|
|
case ngs::VoiceState::VOICE_STATE_ACTIVE:
|
|
state = SCE_NGS_VOICE_STATE_ACTIVE;
|
|
break;
|
|
|
|
case ngs::VoiceState::VOICE_STATE_FINALIZING:
|
|
state = SCE_NGS_VOICE_STATE_FINALIZE;
|
|
break;
|
|
|
|
case ngs::VoiceState::VOICE_STATE_UNLOADING:
|
|
state = SCE_NGS_VOICE_STATE_UNLOADING;
|
|
break;
|
|
|
|
default:
|
|
assert(false && "Invalid voice state to translate");
|
|
return 0;
|
|
}
|
|
|
|
if (voice->is_pending)
|
|
state |= SCE_NGS_VOICE_STATE_PENDING;
|
|
|
|
if (voice->is_paused)
|
|
state |= SCE_NGS_VOICE_STATE_PAUSED;
|
|
|
|
if (voice->is_keyed_off)
|
|
state |= SCE_NGS_VOICE_STATE_KEY_OFF;
|
|
|
|
return state;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoiceGetInfo, SceNgsVoiceHandle handle, SceNgsVoiceInfo *info) {
|
|
if (host.cfg.current_config.disable_ngs)
|
|
return SCE_NGS_OK;
|
|
|
|
ngs::Voice *voice = handle.get(host.mem);
|
|
if (!voice || !info) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
const std::lock_guard<std::mutex> guard(*voice->voice_mutex);
|
|
|
|
info->voice_state = ngsVoiceStateFromHLEState(voice);
|
|
info->num_modules = static_cast<SceUInt32>(voice->datas.size());
|
|
info->num_inputs = static_cast<SceUInt32>(voice->inputs.inputs.size());
|
|
info->num_outputs = voice->rack->vdef->output_count();
|
|
info->num_patches_per_output = static_cast<SceUInt32>(voice->rack->patches_per_output);
|
|
info->update_passed = voice->frame_count;
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(int, sceNgsVoiceGetModuleBypass, SceNgsVoiceHandle voice_handle, const SceUInt32 module, SceUInt32 *bypass_flag) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::Voice *voice = voice_handle.get(host.mem);
|
|
|
|
ngs::ModuleData *storage = voice->module_storage(module);
|
|
|
|
if (!storage)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
if (storage->is_bypassed)
|
|
*bypass_flag = SCE_NGS_MODULE_FLAG_BYPASSED;
|
|
else
|
|
*bypass_flag = SCE_NGS_MODULE_FLAG_NOT_BYPASSED;
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(int, sceNgsVoiceGetModuleType) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoiceGetOutputPatch, SceNgsVoiceHandle voice_handle, const SceInt32 output_index, const SceInt32 output_subindex, SceNgsPatchHandle *handle) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::Voice *voice = voice_handle.get(host.mem);
|
|
|
|
if (!voice || !handle) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
if ((output_subindex < 0) || (output_index < 0)) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
if ((output_index >= static_cast<SceInt32>(voice->rack->vdef->output_count())) || (output_subindex >= voice->rack->patches_per_output)) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
*handle = voice->patches[output_index][output_subindex];
|
|
if (!(*handle) || (handle->get(host.mem))->output_sub_index == -1) {
|
|
LOG_WARN("Getting non-existen output patch port {}:{}", output_index, output_subindex);
|
|
*handle = nullptr;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
EXPORT(int, sceNgsVoiceGetParamsOutOfRange) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoiceGetStateData, SceNgsVoiceHandle voice_handle, const SceUInt32 module, void *mem, const SceUInt32 mem_size) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::Voice *voice = voice_handle.get(host.mem);
|
|
ngs::ModuleData *storage = voice->module_storage(module);
|
|
|
|
if (!storage) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
std::memcpy(mem, storage->voice_state_data.data(), std::min<std::size_t>(mem_size, storage->voice_state_data.size()));
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoiceInit, SceNgsVoiceHandle voice_handle, const ngs::VoicePreset *preset, const SceUInt32 init_flags) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::Voice *voice = voice_handle.get(host.mem);
|
|
|
|
if (!voice)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
if (voice->state == ngs::VoiceState::VOICE_STATE_ACTIVE)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_STATE);
|
|
|
|
std::lock_guard<std::mutex> guard(*voice->voice_mutex);
|
|
|
|
if (init_flags == SCE_NGS_VOICE_INIT_BASE || init_flags == SCE_NGS_VOICE_INIT_ALL) {
|
|
voice->state = ngs::VoiceState::VOICE_STATE_AVAILABLE;
|
|
}
|
|
|
|
if (init_flags & SCE_NGS_VOICE_INIT_ROUTING) {
|
|
// reset all patches
|
|
for (auto &patches : voice->patches) {
|
|
for (auto &patch : patches) {
|
|
if (patch) {
|
|
patch.get(host.mem)->output_sub_index = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (init_flags & SCE_NGS_VOICE_INIT_PRESET) {
|
|
if (!preset) {
|
|
STUBBED("Default preset not implemented");
|
|
} else if (!voice->set_preset(host.mem, preset)) {
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
}
|
|
}
|
|
|
|
if (init_flags & SCE_NGS_VOICE_INIT_CALLBACKS) {
|
|
for (auto &module_data : voice->datas) {
|
|
module_data.callback = Ptr<ngs::NgsCallback>();
|
|
}
|
|
}
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoiceKeyOff, SceNgsVoiceHandle voice_handle) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
ngs::Voice *voice = voice_handle.get(host.mem);
|
|
|
|
if (!voice) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
voice->is_keyed_off = true;
|
|
voice->rack->system->voice_scheduler.off(voice);
|
|
|
|
// call the finish callback, I got no idea what the module id should be in this case
|
|
voice->invoke_callback(host.kernel, host.mem, thread_id, voice->finished_callback, voice->finished_callback_user_data, 0);
|
|
|
|
voice->is_keyed_off = false;
|
|
voice->rack->system->voice_scheduler.stop(voice);
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(int, sceNgsVoiceKill, SceNgsVoiceHandle voice_handle) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::Voice *voice = voice_handle.get(host.mem);
|
|
|
|
if (!voice) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
voice->rack->system->voice_scheduler.stop(voice);
|
|
|
|
return 0;
|
|
}
|
|
|
|
EXPORT(SceUInt32, sceNgsVoiceLockParams, SceNgsVoiceHandle voice_handle, SceUInt32 module, SceUInt32 unk2, Ptr<ngs::BufferParamsInfo> buf) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
auto *buffer_info = buf.get(host.mem);
|
|
|
|
buffer_info->data = alloc(host.mem, 10, "SceNgs buffer stub");
|
|
buffer_info->size = 10;
|
|
return 0;
|
|
}
|
|
|
|
ngs::Voice *voice = voice_handle.get(host.mem);
|
|
if (!voice) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
ngs::ModuleData *data = voice->module_storage(module);
|
|
if (!data) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
ngs::BufferParamsInfo *info = data->lock_params(host.mem);
|
|
if (!info) {
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
}
|
|
|
|
*(buf.get(host.mem)) = *info;
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoicePatchSetVolume, SceNgsPatchHandle patch_handle, const SceInt32 output_channel, const SceInt32 input_channel, const SceFloat32 vol) {
|
|
if (host.cfg.current_config.disable_ngs)
|
|
return SCE_NGS_OK;
|
|
|
|
ngs::Patch *patch = patch_handle.get(host.mem);
|
|
if (!patch || patch->output_sub_index == -1)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
patch->volume_matrix[output_channel][input_channel] = vol;
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoicePatchSetVolumes, SceNgsPatchHandle patch_handle, const SceInt32 output_channel, const SceFloat32 *volumes, const SceInt32 vols) {
|
|
if (host.cfg.current_config.disable_ngs)
|
|
return SCE_NGS_OK;
|
|
|
|
ngs::Patch *patch = patch_handle.get(host.mem);
|
|
if (!patch || patch->output_sub_index == -1)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
for (int i = 0; i < std::min(vols, 2); i++)
|
|
patch->volume_matrix[output_channel][i] = volumes[i];
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoicePatchSetVolumesMatrix, SceNgsPatchHandle patch_handle, const SceNgsVolumeMatrix *matrix) {
|
|
if (host.cfg.current_config.disable_ngs)
|
|
return 0;
|
|
|
|
ngs::Patch *patch = patch_handle.get(host.mem);
|
|
if (!patch || patch->output_sub_index == -1)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
patch->volume_matrix[0][0] = matrix->matrix[0][0];
|
|
patch->volume_matrix[0][1] = matrix->matrix[0][1];
|
|
patch->volume_matrix[1][0] = matrix->matrix[1][0];
|
|
patch->volume_matrix[1][1] = matrix->matrix[1][1];
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(int, sceNgsVoicePause, SceNgsVoiceHandle handle) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::Voice *voice = handle.get(host.mem);
|
|
|
|
if (!voice) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
if (voice->is_paused)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_STATE);
|
|
|
|
if (!voice->rack->system->voice_scheduler.pause(voice)) {
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
}
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(SceUInt32, sceNgsVoicePlay, SceNgsVoiceHandle handle) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::Voice *voice = handle.get(host.mem);
|
|
if (!voice) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
voice->is_pending = true;
|
|
if (!voice->rack->system->voice_scheduler.play(host.mem, voice)) {
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
}
|
|
voice->is_pending = false;
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(int, sceNgsVoiceResume, SceNgsVoiceHandle handle) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::Voice *voice = handle.get(host.mem);
|
|
if (!voice) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
if (!voice->is_paused)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_STATE);
|
|
|
|
if (!voice->rack->system->voice_scheduler.resume(host.mem, voice)) {
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
}
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoiceSetFinishedCallback, SceNgsVoiceHandle voice_handle, Ptr<ngs::NgsCallback> callback, Ptr<void> user_data) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::Voice *voice = voice_handle.get(host.mem);
|
|
|
|
if (!voice)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
voice->finished_callback = callback;
|
|
voice->finished_callback_user_data = user_data;
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoiceSetModuleCallback, SceNgsVoiceHandle voice_handle, const SceUInt32 module, Ptr<ngs::ModuleCallback> callback, Ptr<void> user_data) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::Voice *voice = voice_handle.get(host.mem);
|
|
|
|
if (!voice)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
ngs::ModuleData *storage = voice->module_storage(module);
|
|
if (!storage) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
storage->callback = callback;
|
|
storage->user_data = user_data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoiceSetParamsBlock, SceNgsVoiceHandle voice_handle, const ngs::ModuleParameterHeader *header,
|
|
const SceUInt32 size, SceInt32 *num_error) {
|
|
if (host.cfg.current_config.disable_ngs)
|
|
return SCE_NGS_OK;
|
|
|
|
ngs::Voice *voice = voice_handle.get(host.mem);
|
|
if (!voice)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
const std::lock_guard<std::mutex> guard(*voice->voice_mutex);
|
|
|
|
*num_error = voice->parse_params_block(host.mem, header, size);
|
|
|
|
if (*num_error == 0)
|
|
return SCE_NGS_OK;
|
|
else
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoiceSetPreset, SceNgsVoiceHandle voice_handle, const ngs::VoicePreset *preset) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
if (!voice_handle || !preset)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
ngs::Voice *voice = voice_handle.get(host.mem);
|
|
|
|
const std::lock_guard<std::mutex> guard(*voice->voice_mutex);
|
|
if (!voice->set_preset(host.mem, preset))
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(SceInt32, sceNgsVoiceUnlockParams, SceNgsVoiceHandle handle, const SceUInt32 module_index) {
|
|
if (host.cfg.current_config.disable_ngs) {
|
|
return 0;
|
|
}
|
|
|
|
ngs::Voice *voice = handle.get(host.mem);
|
|
|
|
if (!voice)
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
|
|
ngs::ModuleData *data = voice->module_storage(module_index);
|
|
|
|
if (!data) {
|
|
return RET_ERROR(SCE_NGS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
if (!data->unlock_params()) {
|
|
return RET_ERROR(SCE_NGS_ERROR);
|
|
}
|
|
|
|
return SCE_NGS_OK;
|
|
}
|
|
|
|
EXPORT(int, sceSulphaNgsGetDefaultConfig) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceSulphaNgsGetNeededMemory) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceSulphaNgsInit) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceSulphaNgsSetRackName) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceSulphaNgsSetSampleName) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceSulphaNgsSetSynthName) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceSulphaNgsSetVoiceName) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceSulphaNgsShutdown) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
EXPORT(int, sceSulphaNgsTrace) {
|
|
return UNIMPLEMENTED();
|
|
}
|
|
|
|
BRIDGE_IMPL(sceNgsAT9GetSectionDetails)
|
|
BRIDGE_IMPL(sceNgsModuleGetNumPresets)
|
|
BRIDGE_IMPL(sceNgsModuleGetPreset)
|
|
BRIDGE_IMPL(sceNgsPatchCreateRouting)
|
|
BRIDGE_IMPL(sceNgsPatchGetInfo)
|
|
BRIDGE_IMPL(sceNgsPatchRemoveRouting)
|
|
BRIDGE_IMPL(sceNgsRackGetRequiredMemorySize)
|
|
BRIDGE_IMPL(sceNgsRackGetVoiceHandle)
|
|
BRIDGE_IMPL(sceNgsRackInit)
|
|
BRIDGE_IMPL(sceNgsRackRelease)
|
|
BRIDGE_IMPL(sceNgsRackSetParamErrorCallback)
|
|
BRIDGE_IMPL(sceNgsSystemGetRequiredMemorySize)
|
|
BRIDGE_IMPL(sceNgsSystemInit)
|
|
BRIDGE_IMPL(sceNgsSystemLock)
|
|
BRIDGE_IMPL(sceNgsSystemRelease)
|
|
BRIDGE_IMPL(sceNgsSystemSetFlags)
|
|
BRIDGE_IMPL(sceNgsSystemSetParamErrorCallback)
|
|
BRIDGE_IMPL(sceNgsSystemUnlock)
|
|
BRIDGE_IMPL(sceNgsSystemUpdate)
|
|
BRIDGE_IMPL(sceNgsVoiceBypassModule)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetAtrac9Voice)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetCompressorBuss)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetCompressorSideChainBuss)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetDelayBuss)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetDistortionBuss)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetEnvelopeBuss)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetEqBuss)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetMasterBuss)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetMixerBuss)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetPauserBuss)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetPitchShiftBuss)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetReverbBuss)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetSasEmuVoice)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetScreamAtrac9Voice)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetScreamVoice)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetSimpleAtrac9Voice)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetSimpleVoice)
|
|
BRIDGE_IMPL(sceNgsVoiceDefGetTemplate1)
|
|
BRIDGE_IMPL(sceNgsVoiceGetInfo)
|
|
BRIDGE_IMPL(sceNgsVoiceGetModuleBypass)
|
|
BRIDGE_IMPL(sceNgsVoiceGetModuleType)
|
|
BRIDGE_IMPL(sceNgsVoiceGetOutputPatch)
|
|
BRIDGE_IMPL(sceNgsVoiceGetParamsOutOfRange)
|
|
BRIDGE_IMPL(sceNgsVoiceGetStateData)
|
|
BRIDGE_IMPL(sceNgsVoiceInit)
|
|
BRIDGE_IMPL(sceNgsVoiceKeyOff)
|
|
BRIDGE_IMPL(sceNgsVoiceKill)
|
|
BRIDGE_IMPL(sceNgsVoiceLockParams)
|
|
BRIDGE_IMPL(sceNgsVoicePatchSetVolume)
|
|
BRIDGE_IMPL(sceNgsVoicePatchSetVolumes)
|
|
BRIDGE_IMPL(sceNgsVoicePatchSetVolumesMatrix)
|
|
BRIDGE_IMPL(sceNgsVoicePause)
|
|
BRIDGE_IMPL(sceNgsVoicePlay)
|
|
BRIDGE_IMPL(sceNgsVoiceResume)
|
|
BRIDGE_IMPL(sceNgsVoiceSetFinishedCallback)
|
|
BRIDGE_IMPL(sceNgsVoiceSetModuleCallback)
|
|
BRIDGE_IMPL(sceNgsVoiceSetParamsBlock)
|
|
BRIDGE_IMPL(sceNgsVoiceSetPreset)
|
|
BRIDGE_IMPL(sceNgsVoiceUnlockParams)
|
|
BRIDGE_IMPL(sceSulphaNgsGetDefaultConfig)
|
|
BRIDGE_IMPL(sceSulphaNgsGetNeededMemory)
|
|
BRIDGE_IMPL(sceSulphaNgsInit)
|
|
BRIDGE_IMPL(sceSulphaNgsSetRackName)
|
|
BRIDGE_IMPL(sceSulphaNgsSetSampleName)
|
|
BRIDGE_IMPL(sceSulphaNgsSetSynthName)
|
|
BRIDGE_IMPL(sceSulphaNgsSetVoiceName)
|
|
BRIDGE_IMPL(sceSulphaNgsShutdown)
|
|
BRIDGE_IMPL(sceSulphaNgsTrace)
|