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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions core/libs/prx/libSceNgs2.native/include/Ngs2Types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ 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_REVERB = 0x2001;
static constexpr std::uint32_t SCE_NGS2_RACK_ID_MASTERING = 0x3000;
static constexpr std::uint32_t SCE_NGS2_RACK_ID_CUSTOM_SAMPLER = 0x4001;
static constexpr std::uint32_t SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER = 0x4002;

static constexpr std::uint32_t SCE_NGS2_WAVEFORM_TYPE_PCM_I16L = 0x12;
Expand Down Expand Up @@ -57,11 +58,16 @@ static constexpr std::uint32_t SCE_NGS2_REVERB_VOICE_PARAM_I3DL2 = 0x20010001;
static constexpr std::uint32_t SCE_NGS2_MASTERING_VOICE_PARAM_SETUP = 0x30000000;
static constexpr std::uint32_t SCE_NGS2_MASTERING_VOICE_PARAM_GAIN = 0x30000004;
static constexpr std::uint32_t SCE_NGS2_MASTERING_VOICE_PARAM_OUTPUT = 0x30000005;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_SAMPLER_VOICE_PARAM_SETUP = 0x40010000;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_SAMPLER_VOICE_PARAM_ADD_WAVEFORM_BLOCKS = 0x40010001;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_SAMPLER_VOICE_PARAM_PITCH = 0x40010005;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_SUBMIXER_VOICE_PARAM_SETUP = 0x40020000;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_VOICE_PARAM_FILTER = 0x40001300;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_VOICE_PARAM_USER_FX2 = 0x40001f00;

static constexpr std::uint32_t SCE_NGS2_CUSTOM_MAX_MODULES = 24;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MAX_PORTS = 16;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MODULE_ID_FILTER = 0x13;
static constexpr std::uint32_t SCE_NGS2_CUSTOM_MODULE_ID_USER_FX2 = 0x1f;

static constexpr std::uint32_t SCE_NGS2_WAVEFORM_BLOCKS_FLAG_CONTINUE = 1;
Expand Down Expand Up @@ -282,6 +288,17 @@ struct Ngs2CustomSubmixerRackOption {
};
static_assert(sizeof(Ngs2CustomSubmixerRackOption) == 1288);

struct Ngs2CustomSamplerRackOption {
Ngs2CustomRackOption custom_rack_option;
std::uint32_t max_channel_works;
std::uint32_t max_waveform_blocks;
std::uint32_t max_atrac9_decoders;
std::uint32_t max_atrac9_channel_works;
std::uint32_t max_ajm_atrac9_decoders;
std::uint32_t max_codec_caches;
};
static_assert(sizeof(Ngs2CustomSamplerRackOption) == 1304);

struct Ngs2RackInfo {
char name[64];
Ngs2Handle rack_handle;
Expand Down Expand Up @@ -491,6 +508,33 @@ struct Ngs2CustomVoiceUserFx2Param {
};
static_assert(sizeof(Ngs2CustomVoiceUserFx2Param) == 24);

struct Ngs2CustomVoiceFilterParam {
Ngs2VoiceParamHeader header;
std::uint32_t type;
std::uint32_t channel_mask;
union {
struct {
float i0;
float i1;
float i2;
float o1;
float o2;
} direct;
struct {
float frequency;
float q;
float level;
std::uint32_t reserved;
std::uint32_t reserved2;
} fcq;
} param;
std::uint32_t reserved3;
};
static_assert(sizeof(Ngs2CustomVoiceFilterParam) == 40);
static_assert(offsetof(Ngs2CustomVoiceFilterParam, type) == 8);
static_assert(offsetof(Ngs2CustomVoiceFilterParam, channel_mask) == 12);
static_assert(offsetof(Ngs2CustomVoiceFilterParam, param) == 16);

struct Ngs2MasteringVoiceSetupParam {
Ngs2VoiceParamHeader header;
std::uint32_t num_io_channels;
Expand Down
46 changes: 40 additions & 6 deletions core/libs/prx/libSceNgs2.native/src/Custom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@ void Ngs2CheckCustomRack(const Ngs2CustomRackOption& option) {
if (option.num_modules > SCE_NGS2_CUSTOM_MAX_MODULES || option.rack_option.max_ports > SCE_NGS2_CUSTOM_MAX_PORTS) APS5_INVALID_ARG_EX;
for (std::uint32_t m = 0; m < option.num_modules; m++) {
const auto& info = option.module[m];
if (info.module_id != SCE_NGS2_CUSTOM_MODULE_ID_USER_FX2) throw std::runtime_error("NGS2: custom module id " + Ngs2Hex(info.module_id) + " is not implemented");
if (info.option == nullptr || info.option->size != sizeof(Ngs2CustomUserFx2ModuleOption)) APS5_INVALID_ARG_EX;
if (info.source_buffer_id != 0 || info.dest_buffer_id != 0) APS5_INVALID_ARG_EX;
if (reinterpret_cast<const Ngs2CustomUserFx2ModuleOption*>(info.option)->process_handler == nullptr) APS5_INVALID_ARG_EX;
if (info.module_id == SCE_NGS2_CUSTOM_MODULE_ID_USER_FX2) {
if (info.option == nullptr || info.option->size != sizeof(Ngs2CustomUserFx2ModuleOption)) APS5_INVALID_ARG_EX;
if (info.source_buffer_id != 0 || info.dest_buffer_id != 0) APS5_INVALID_ARG_EX;
if (reinterpret_cast<const Ngs2CustomUserFx2ModuleOption*>(info.option)->process_handler == nullptr) APS5_INVALID_ARG_EX;
} else if (info.module_id == SCE_NGS2_CUSTOM_MODULE_ID_FILTER) {
if (info.option != nullptr || info.state_size != 0 || info.state_offset != 0) APS5_INVALID_ARG_EX;
if (info.source_buffer_id != 0 || info.extra_buffer_id != 0 || info.dest_buffer_id != 0) APS5_INVALID_ARG_EX;
} else {
throw std::runtime_error("NGS2: custom module id " + Ngs2Hex(info.module_id) + " is not implemented");
}
}
for (std::uint32_t p = 0; p < option.rack_option.max_ports; p++) {
if (option.port[p].source_buffer_id != 0) APS5_INVALID_ARG_EX;
Expand All @@ -34,10 +40,13 @@ static Ngs2UserFx2SetupContext UserFxContext(Ngs2Rack& rack, std::size_t module,
}

void Ngs2SetupUserFx(Ngs2Rack& rack, const Ngs2CustomRackOption& option) {
rack.customModuleIds.resize(option.num_modules);
rack.userFx.resize(option.num_modules);
rack.userFxCommon.resize(option.num_modules);
for (auto& voice : rack.voices) voice.userFx.resize(option.num_modules);
for (std::uint32_t m = 0; m < option.num_modules; m++) {
rack.customModuleIds[m] = option.module[m].module_id;
if (option.module[m].module_id == SCE_NGS2_CUSTOM_MODULE_ID_FILTER) continue;
const auto& module = *reinterpret_cast<const Ngs2CustomUserFx2ModuleOption*>(option.module[m].option);
rack.userFx[m] = module;
rack.userFxCommon[m].resize(module.common_size);
Expand All @@ -58,6 +67,7 @@ void Ngs2SetupUserFx(Ngs2Rack& rack, const Ngs2CustomRackOption& option) {
void Ngs2CleanupUserFx(Ngs2Rack& rack) {
for (std::uint32_t i = 0; i < rack.voices.size(); i++) {
for (std::size_t m = 0; m < rack.userFx.size(); m++) {
if (rack.customModuleIds[m] != SCE_NGS2_CUSTOM_MODULE_ID_USER_FX2) continue;
if (rack.userFx[m].cleanup_handler == nullptr) continue;
auto context = UserFxContext(rack, m, i);
const int result = rack.userFx[m].cleanup_handler(&context);
Expand All @@ -84,11 +94,33 @@ static void SetUserFxParam(Ngs2Voice& voice, std::uint32_t index, const Ngs2Cust
}

void Ngs2ApplyCustomParam(Ngs2Voice& voice, const Ngs2VoiceParamHeader& param) {
if (voice.rack->rackId != SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) throw std::invalid_argument("NGS2: voice param " + Ngs2Hex(param.id) + " belongs to another rack");
if (voice.rack->rackId != SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER && voice.rack->rackId != SCE_NGS2_RACK_ID_CUSTOM_SAMPLER) throw std::invalid_argument("NGS2: voice param " + Ngs2Hex(param.id) + " belongs to another rack");
if (param.id == SCE_NGS2_CUSTOM_SUBMIXER_VOICE_PARAM_SETUP) {
SetupCustomSubmixer(voice, ParamAs<Ngs2CustomSubmixerVoiceSetupParam>(param));
return;
}
if ((param.id & ~0x1fu) == SCE_NGS2_CUSTOM_VOICE_PARAM_FILTER) {
if (voice.rack->rackId != SCE_NGS2_RACK_ID_CUSTOM_SAMPLER && voice.rack->rackId != SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) {
throw std::invalid_argument("NGS2: voice param " + Ngs2Hex(param.id) + " belongs to another rack");
}
const auto index = param.id & 0x1f;
if (index >= voice.rack->customModuleIds.size() || voice.rack->customModuleIds[index] != SCE_NGS2_CUSTOM_MODULE_ID_FILTER) APS5_INVALID_ARG_EX;
const auto& custom = ParamAs<Ngs2CustomVoiceFilterParam>(param);
if (custom.type != 1) throw std::runtime_error("NGS2: custom voice filter type " + std::to_string(custom.type) + " is not implemented");
Ngs2SamplerVoiceFilterParam filter{};
filter.header = custom.header;
filter.index = index;
filter.location = 1;
filter.type = custom.type;
filter.channel_mask = custom.channel_mask;
if (custom.type == 1) {
filter.frequency = custom.param.fcq.frequency;
filter.q = custom.param.fcq.q;
filter.level = custom.param.fcq.level;
}
Ngs2SetFilter(voice, filter);
return;
}
if ((param.id & ~0x1fu) == SCE_NGS2_CUSTOM_VOICE_PARAM_USER_FX2) {
SetUserFxParam(voice, param.id & 0x1f, ParamAs<Ngs2CustomVoiceUserFx2Param>(param));
return;
Expand All @@ -97,10 +129,12 @@ void Ngs2ApplyCustomParam(Ngs2Voice& voice, const Ngs2VoiceParamHeader& param) {
}

void Ngs2ProcessUserFx(Ngs2Voice& voice, std::uint32_t grain, std::uint32_t sampleRate) {
if (voice.userFx.empty()) return;
if (voice.userFx.empty() || std::none_of(voice.rack->customModuleIds.begin(), voice.rack->customModuleIds.end(),
[](std::uint32_t id) { return id == SCE_NGS2_CUSTOM_MODULE_ID_USER_FX2; })) return;
std::vector<float*> channels(voice.channels);
for (std::uint32_t c = 0; c < voice.channels; c++) channels[c] = voice.samples.data() + static_cast<std::size_t>(c) * grain;
for (std::size_t m = 0; m < voice.userFx.size(); m++) {
if (voice.rack->customModuleIds[m] != SCE_NGS2_CUSTOM_MODULE_ID_USER_FX2) continue;
auto& fx = voice.userFx[m];
const auto& module = voice.rack->userFx[m];
Ngs2UserFx2ProcessContext context{channels.data(), voice.rack->userFxCommon[m].data(), fx.param.data(), fx.work.data(), fx.state.data(),
Expand Down
2 changes: 2 additions & 0 deletions core/libs/prx/libSceNgs2.native/src/Ngs2Internal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ struct Ngs2Rack {
std::vector<Ngs2Voice> voices;
std::vector<Ngs2CustomUserFx2ModuleOption> userFx;
std::vector<std::vector<std::uint8_t>> userFxCommon;
std::vector<std::uint32_t> customModuleIds;
};

struct Ngs2System {
Expand Down Expand Up @@ -185,6 +186,7 @@ void* Ngs2Place(const Ngs2ContextBufferInfo* bufferInfo, std::size_t size, std::
int Ngs2DestroyRack(Ngs2Rack& rack, Ngs2ContextBufferInfo* outBufferInfo);
int Ngs2ReleaseBuffer(const Ngs2BufferAllocator& allocator, Ngs2ContextBufferInfo bufferInfo, Ngs2ContextBufferInfo* outBufferInfo);
void Ngs2SetupAtrac9(Ngs2Voice& voice, const Ngs2WaveformFormat& format);
void Ngs2SetFilter(Ngs2Voice& voice, const Ngs2SamplerVoiceFilterParam& param);
std::size_t Ngs2Atrac9BlockBytes(const Ngs2Voice& voice, const Ngs2WaveformBlock& block);
void Ngs2RestartAtrac9(Ngs2Voice& voice);
void Ngs2ConsumeAtrac9(Ngs2Voice& voice, std::uint32_t grain, std::uint32_t systemRate);
Expand Down
11 changes: 9 additions & 2 deletions core/libs/prx/libSceNgs2.native/src/Rack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ union RackOptions {
Ngs2MasteringRackOption mastering;
Ngs2ReverbRackOption reverb;
Ngs2CustomSubmixerRackOption customSubmixer;
Ngs2CustomSamplerRackOption customSampler;
};

static std::size_t RackOptionSize(std::uint32_t rackId) {
Expand All @@ -27,12 +28,13 @@ static std::size_t RackOptionSize(std::uint32_t rackId) {
case SCE_NGS2_RACK_ID_MASTERING: return sizeof(Ngs2MasteringRackOption);
case SCE_NGS2_RACK_ID_REVERB: return sizeof(Ngs2ReverbRackOption);
case SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER: return sizeof(Ngs2CustomSubmixerRackOption);
case SCE_NGS2_RACK_ID_CUSTOM_SAMPLER: return sizeof(Ngs2CustomSamplerRackOption);
default: throw std::runtime_error("NGS2: rack id " + Ngs2Hex(rackId) + " is not implemented");
}
}

static RackOptions DefaultRackOption(std::uint32_t rackId) {
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) throw std::runtime_error("NGS2: default custom rack options are not implemented");
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER || rackId == SCE_NGS2_RACK_ID_CUSTOM_SAMPLER) throw std::runtime_error("NGS2: default custom rack options are not implemented");
RackOptions options{};
auto& common = options.common;
common.size = RackOptionSize(rackId);
Expand Down Expand Up @@ -94,6 +96,7 @@ static RackOptions CheckedRackOption(std::uint32_t rackId, const Ngs2RackOption*
throw std::invalid_argument("NGS2: invalid rack voice or channel count");
}
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) Ngs2CheckCustomRack(options.customSubmixer.custom_rack_option);
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SAMPLER) Ngs2CheckCustomRack(options.customSampler.custom_rack_option);
if (rackId == SCE_NGS2_RACK_ID_REVERB && options.reverb.reverb_size != 1) {
throw std::runtime_error("NGS2: reverb rack size " + std::to_string(options.reverb.reverb_size) + " is not implemented");
}
Expand All @@ -109,12 +112,15 @@ static int CreateRack(Ngs2Handle systemHandle, std::uint32_t rackId, const RackO
rack->system = system;
rack->rackId = rackId;
rack->maxChannels = RackMaxChannels(rackId, options);
rack->maxFilters = rackId == SCE_NGS2_RACK_ID_SAMPLER ? options.sampler.max_filters : 0;
rack->maxFilters = rackId == SCE_NGS2_RACK_ID_SAMPLER ? options.sampler.max_filters
: rackId == SCE_NGS2_RACK_ID_CUSTOM_SAMPLER ? options.customSampler.custom_rack_option.num_modules
: rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER ? options.customSubmixer.custom_rack_option.num_modules : 0;
rack->uid = nextUid++;
std::memcpy(rack->name, options.common.name, sizeof(rack->name));
rack->name[sizeof(rack->name) - 1] = '\0';
rack->maxGrainSamples = options.common.max_grain_samples;
if (rackId == SCE_NGS2_RACK_ID_SAMPLER) rack->maxChannelWorks = options.sampler.max_channel_works;
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SAMPLER) rack->maxChannelWorks = options.customSampler.max_channel_works;
if (rackId == SCE_NGS2_RACK_ID_SUBMIXER) rack->maxInputs = options.submixer.max_inputs;
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) rack->maxInputs = options.customSubmixer.max_inputs;
rack->bufferInfo = bufferInfo;
Expand All @@ -126,6 +132,7 @@ static int CreateRack(Ngs2Handle systemHandle, std::uint32_t rackId, const RackO
voice.matrices.resize(options.common.max_matrices);
}
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) Ngs2SetupUserFx(*rack, options.customSubmixer.custom_rack_option);
if (rackId == SCE_NGS2_RACK_ID_CUSTOM_SAMPLER) Ngs2SetupUserFx(*rack, options.customSampler.custom_rack_option);
system->racks.push_back(rack);
*handle = reinterpret_cast<Ngs2Handle>(rack);
return SCE_NGS2_OK;
Expand Down
2 changes: 1 addition & 1 deletion core/libs/prx/libSceNgs2.native/src/Render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ static void RenderVoice(Ngs2Voice& voice, const std::vector<Ngs2Voice*>& voices,
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) RenderSampler(voice, grain, systemRate);
if (voice.rack->rackId == SCE_NGS2_RACK_ID_SAMPLER || voice.rack->rackId == SCE_NGS2_RACK_ID_CUSTOM_SAMPLER) RenderSampler(voice, grain, systemRate);
else MixInputs(voice, voices, grain, systemRate);
if (voice.rack->rackId == SCE_NGS2_RACK_ID_REVERB && voice.reverb) voice.hasSamples = Ngs2ProcessReverb(voice, grain, systemRate);
Ngs2ProcessLegacyUserFx(voice, grain, systemRate);
Expand Down
40 changes: 30 additions & 10 deletions core/libs/prx/libSceNgs2.native/src/Voice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ static void Patch(Ngs2Voice& voice, const Ngs2VoicePatchParam& patch) {
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;
if (dest == nullptr || dest->rack->system != voice.rack->system || dest->rack->rackId == SCE_NGS2_RACK_ID_SAMPLER || dest->rack->rackId == SCE_NGS2_RACK_ID_CUSTOM_SAMPLER) APS5_INVALID_ARG_EX;
port.dest = dest;
}

Expand Down Expand Up @@ -245,7 +245,7 @@ static void AddWaveformBlocks(Ngs2Voice& voice, const Ngs2SamplerVoiceWaveformBl
}
}

static void SetFilter(Ngs2Voice& voice, const Ngs2SamplerVoiceFilterParam& param) {
void Ngs2SetFilter(Ngs2Voice& voice, const Ngs2SamplerVoiceFilterParam& param) {
if (param.index >= voice.rack->maxFilters) APS5_INVALID_ARG_EX;
if (voice.filters.size() <= param.index) voice.filters.resize(param.index + 1);
auto& filter = voice.filters[param.index];
Expand Down Expand Up @@ -279,6 +279,11 @@ static void SetFilter(Ngs2Voice& voice, const Ngs2SamplerVoiceFilterParam& param
filter.a2 = (1.0 - alpha) * inverseA0;
}

static void SetPitch(Ngs2Voice& voice, float ratio) {
if (!std::isfinite(ratio) || ratio < 0.0f) APS5_INVALID_ARG_EX;
voice.pitch = ratio;
}

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;
Expand All @@ -288,17 +293,30 @@ static void ApplySamplerParam(Ngs2Voice& voice, const Ngs2VoiceParamHeader& para
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;
}
case SCE_NGS2_SAMPLER_VOICE_PARAM_FILTER: SetFilter(voice, ParamAs<Ngs2SamplerVoiceFilterParam>(param)); break;
case SCE_NGS2_SAMPLER_VOICE_PARAM_PITCH: SetPitch(voice, ParamAs<Ngs2SamplerVoicePitchParam>(param).ratio); break;
case SCE_NGS2_SAMPLER_VOICE_PARAM_FILTER: Ngs2SetFilter(voice, ParamAs<Ngs2SamplerVoiceFilterParam>(param)); break;
default: throw std::runtime_error("NGS2: sampler voice param " + Ngs2Hex(param.id) + " is not implemented");
}
}

static void AddCustomSamplerBlocks(Ngs2Voice& voice, const Ngs2SamplerVoiceWaveformBlocksParam& param) {
if ((param.flags & SCE_NGS2_WAVEFORM_BLOCKS_FLAG_APPEND) == 0 && param.blocks != nullptr) {
for (std::uint32_t i = 0; i < param.num_blocks; i++) {
if (param.blocks[i].num_repeats != 0) throw std::runtime_error("NGS2: repeated waveform blocks on a custom sampler voice are not implemented");
}
}
AddWaveformBlocks(voice, param);
}

static void ApplyCustomSamplerParam(Ngs2Voice& voice, const Ngs2VoiceParamHeader& param) {
switch (param.id) {
case SCE_NGS2_CUSTOM_SAMPLER_VOICE_PARAM_SETUP: SetupSampler(voice, ParamAs<Ngs2SamplerVoiceSetupParam>(param).format); break;
case SCE_NGS2_CUSTOM_SAMPLER_VOICE_PARAM_ADD_WAVEFORM_BLOCKS: AddCustomSamplerBlocks(voice, ParamAs<Ngs2SamplerVoiceWaveformBlocksParam>(param)); break;
case SCE_NGS2_CUSTOM_SAMPLER_VOICE_PARAM_PITCH: SetPitch(voice, ParamAs<Ngs2SamplerVoicePitchParam>(param).ratio); break;
default: throw std::runtime_error("NGS2: custom 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();
Expand All @@ -311,13 +329,15 @@ static void ApplyParam(Ngs2Voice& voice, const Ngs2VoiceParamHeader& param) {
ApplyCommonParam(voice, param);
return;
}
if (rackId == (SCE_NGS2_CUSTOM_VOICE_PARAM_USER_FX2 >> 16) || rackId == SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER) {
if (rackId == (SCE_NGS2_CUSTOM_VOICE_PARAM_USER_FX2 >> 16)) {
Ngs2ApplyCustomParam(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_CUSTOM_SAMPLER: ApplyCustomSamplerParam(voice, param); return;
case SCE_NGS2_RACK_ID_CUSTOM_SUBMIXER: Ngs2ApplyCustomParam(voice, param); return;
case SCE_NGS2_RACK_ID_SUBMIXER:
if (param.id == SCE_NGS2_SUBMIXER_VOICE_PARAM_USER_FX) {
const auto& fx = ParamAs<Ngs2SubmixerVoiceUserFxParam>(param);
Expand Down
Loading
Loading