[Bug] Enabling NUM_HARDCODED_FX_MODS breaks custom C++ nodes.
-
Hardcoded Master FX parameter modslots can force c++ Third-party node parameters to max
*I used ChatGPT to help format this bug report so that it's written clearly and thoroughly.
I apologize for the "Ai aesthetic" residue that leaves behind.Branch: I'm on the latest HISE develop branch, pulled and compiled today
OS: Windows 11
Juce: I'm using Juce 6 (version: 6.1.3)
Project flags:NUM_HARDCODED_FX_MODS=8 NUM_HARDCODED_POLY_FX_MODS=8Summary
I think there is a bug in the hardcoded Master FX "parameter-modslot" path.
I didn't want to post a report until I was sure it wasn't my own mistake, but I've been reading the Hise source all day and testing different C+ nodes and in the end I concluded this might be a bug:If a third-party node exposes a parameter modslot with
ConnectionMode::Parameter, loading that node into a hardcoded Master FX forces the modslot parameter values to get set to the top of their ranges (and stuck there).This is not a small issue. For DSP nodes it completely breaks effects, and means you can't use any c++ nodes that have modslots inside monophonic hardcoded master FX.
I like almost all of my c++ nodes to have modslots so that they can work easily with hise modulation and the matrix modulator. This bug means I have to make modslot and non-modslot versions of every effect, so that I can load the modslot version into poly contexts and the non-modslot one into mono contexts. And then for the mono nodes I have to use a different modulation scheme. Messy.
(unless I'm misunderstanding something).How I found it
I was testing a compiled third-party DSP node.
The same node behaved normally in a ScriptFX context, but in a hardcoded Master FX it produced broken / glitchy / extremely wrong output.
When I removed these project flags and rebuilt, the hardcoded Master FX version started behaving normally again:
NUM_HARDCODED_FX_MODS=8 NUM_HARDCODED_POLY_FX_MODS=8So the issue appears to be tied to hardcoded FX modslots.
Disabling those flags is not a usable workaround for me, because then hardcoded modslots would be unavailable project-wide.
Minimal test
I made four tiny diagnostic third-party nodes.
Each node has one parameter:
Parameter_name: ProbeValue Range: 0.0 to 1.0 Default: 0.25Each node outputs a tone whose gain follows
ProbeValue.Expected result: quiet tone.
Bug result if the parameter is forced to max: much louder tone.Each node exposes the same parameter slot:
modulation::ConnectionInfo slot; slot.connectedParameterIndex = ProbeValueParameter; slot.connectionMode = modulation::ConnectionMode::Parameter; slot.modulationMode = modulation::ParameterMode::ScaleAdd;I tested four variants:
Griffin_ModSlotProbe_NoHandle Griffin_ModSlotProbe_WithHandle Griffin_ModSlotProbe_FrameHandle Griffin_ModSlotProbe_ModNodeHandleThese check whether adding the following details would fix or change the behavior:
- no
handleModulation()function defined handleModulation(double&) { return 0; }- block processing forwarded through
processFrame()from process() isModNode() == true(yeah, I know that's not going to do anything)
Result
In hardcoded Master FX:
NoHandle left loud, right silent WithHandle left loud, right silent FrameHandle left loud, right silent ModNodeHandle left loud, right silentThe left channel becoming loud proves
ProbeValuewas driven from0.25to1.0.
And the right channel staying silent meanshandleModulation(double&)callback was not called.Expected behaviour
Ideally, a hardcoded Master FX parameter modslot should not push a node parameter to its maximum value simply because the slot exists.
If no meaningful modulation value is active, the parameter should keep its current/default value, or the slot should not be treated as connected until there is an actual usable modulation connection.
Current behaviour
Currently, with hardcoded FX modslots enabled, a third-party C++ node exposing
ConnectionMode::Parametercan get seemingly spammed with max-range parameter values during rendering / stuck at max value (moving the parameter doesn't unstick us, the parameters are stuck to max).Why this matters
I'm assuming that HISE nodes are intended to be modular.
If that's true, then a node that is poly-capable, or a node that exposes modslots, should be able to function in a mono hardcoded Master FX context too.This is also inconvenient for my shipped products. A HISE user can load one of my nodes into HISE and get broken DSP because the hardcoded Master FX modulation path corrupts parameter values.
The result of all this is that
ConnectionMode::Parameterbecomes unsafe for third-party hardcoded Master FX products.Suspected source bug
The Hise algorithm seems to do something like this:
HardcodedMasterFX::applyEffect() -> extraMods.processChunkedWithModulation(rd) -> ExtraModulatorRuntimeTargetSource::handleModulation(...) -> ModChainWithBuffer::getOneModulationValue(startSample) -> rd.handleModulation(pIndex, mv) -> parameter range convertFrom0to1(mv) -> p->callback.call(value)The important part is in the hardcoded Master FX context, with no active voice state,
getOneModulationValue()can return an inactive/default modulation value of1.0f.That normalized
1.0is then converted through the parameter range, so the node receives the parameter maximum.Request
Could the hardcoded Master FX parameter-modslot path be changed so inactive / unconnected / not-yet-valid modulation does not force parameter-mode slots to max?
post script: a similar / related bug exists in the Hise synth group, I will write a report on that after a bit more investigation.
Christoph, Thanks for all your hard workš«”
- no