[CLAP] Sidechain input is declared but not forwarded to the DSP
Summary
The current CLAP wrapper exposes secondary audio input ports correctly, but sidechain audio is not forwarded to the YUP processor.
A host can provide a secondary CLAP audio input, but the wrapper only processes input buses that have a matching output bus. In a standard sidechain layout with two inputs and one output, the sidechain bus is ignored.
The current AudioProcessContext also exposes only one in-place audio buffer, so processors have no API for accessing auxiliary input buses separately.
Severity
P0 / Blocking
This prevents the implementation of functional sidechain-based processors, including:
- compressors;
- gates;
- dynamic equalizers;
- envelope followers;
- external-key detectors.
Typical affected layout
Input 0 Main stereo input
Input 1 Stereo sidechain input
Output 0 Main stereo output
Current behavior
Port declaration is mostly correct
The CLAP wrapper marks only the first input port as the main port:
uint32_t flags = (index == 0)
? CLAP_AUDIO_PORT_IS_MAIN
: 0;
CLAP does not define a dedicated sidechain flag. A secondary input port without CLAP_AUDIO_PORT_IS_MAIN is therefore a valid auxiliary input that may be used as a sidechain.
However, this role is inferred from the port index rather than represented explicitly in YUP's bus model.
The sidechain bus is ignored during processing
The wrapper processes only input buses with a corresponding output bus:
for (uint32_t busIdx = 0;
busIdx < std::min (
process->audio_inputs_count,
process->audio_outputs_count);
++busIdx)
{
const auto& inBus = process->audio_inputs[busIdx];
const auto& outBus = process->audio_outputs[busIdx];
// Copy input to matching output.
}
For the standard sidechain layout:
audio_inputs_count = 2
audio_outputs_count = 1
the loop executes only once.
audio_inputs[0] is processed, while audio_inputs[1], containing the sidechain signal, is never read.
This affects both the 32-bit and 64-bit processing paths.
The processor receives only output channel pointers
The processing buffer is built exclusively from output buses:
wrapper->outputChannelsFloat.clear();
for (uint32_t busIdx = 0;
busIdx < process->audio_outputs_count;
++busIdx)
{
for (uint32_t channel = 0;
channel < process->audio_outputs[busIdx].channel_count;
++channel)
{
wrapper->outputChannelsFloat.push_back (
process->audio_outputs[busIdx].data32[channel]);
}
}
AudioSampleBuffer audioBuffer (
wrapper->outputChannelsFloat.data(),
static_cast<int> (wrapper->outputChannelsFloat.size()),
0,
static_cast<int> (process->frames_count));
Secondary input buffers are therefore not included in the context passed to the DSP.
AudioProcessContext is not bus-aware
The current processing API exposes a single audio buffer:
template <typename FloatType>
struct AudioProcessContext
{
AudioBuffer<FloatType>& audio;
MidiBuffer& midi;
ParameterChangeBuffer& params;
AudioPlayHead* playHead = nullptr;
};
There is no API for accessing:
- the main input bus;
- auxiliary input buses;
- the sidechain input;
- separate input and output buses;
- per-bus channel layouts.
Even if the CLAP wrapper preserved the sidechain samples, a processor could not access them independently.
Expected behavior
The wrapper should support a layout such as:
Input 0 Main Input CLAP_AUDIO_PORT_IS_MAIN
Input 1 Sidechain No main flag
Output 0 Main Output CLAP_AUDIO_PORT_IS_MAIN
During processing, the DSP should receive separate views for:
- the main input;
- the sidechain input;
- the main output.
The sidechain input must remain available even though there is no corresponding output bus.
Proposed solution
1. Add an explicit bus role
Extend AudioBus with a role:
class AudioBus
{
public:
enum class Role
{
Main,
Auxiliary
};
AudioBus (
Type type,
Direction direction,
String name,
int numChannels,
Role role = Role::Main);
Role getRole() const noexcept;
};
The CLAP wrapper can then map the role explicitly:
info->flags =
audioBus->getRole() == AudioBus::Role::Main
? CLAP_AUDIO_PORT_IS_MAIN
: 0;
This avoids relying on the convention that the first port is main and every following port is auxiliary.
2. Make AudioProcessContext bus-aware
Replace the single-buffer model with separate input and output bus views:
template <typename SampleType>
struct AudioProcessContext
{
Span<AudioBusBufferView<const SampleType>> inputs;
Span<AudioBusBufferView<SampleType>> outputs;
MidiBuffer& midi;
ParameterChangeBuffer& params;
AudioPlayHead* playHead = nullptr;
auto getMainInput() const;
auto getMainOutput() const;
auto getAuxiliaryInput (int index) const;
};
A processor could then access the sidechain explicitly:
const auto mainInput = context.getMainInput();
const auto sidechain = context.getAuxiliaryInput (0);
const auto mainOutput = context.getMainOutput();
compressor.process (
mainInput,
sidechain,
mainOutput);
3. Preserve all CLAP input ports
The wrapper should build input views directly from:
process->audio_inputs
process->audio_inputs_count
and output views directly from:
process->audio_outputs
process->audio_outputs_count
Input and output buses must not be paired globally by matching indices.
Only the main input should be copied to the main output when required for non-in-place processing.
4. Handle missing and inactive sidechain buffers safely
The implementation must support:
- missing channel pointers;
- inactive auxiliary ports;
- mono and stereo sidechains;
- 32-bit processing;
- 64-bit processing;
- in-place main processing;
- separate main input and output buffers;
- silent sidechain inputs.
An unavailable sidechain should be exposed as silence and must not cause a crash.
Steps to reproduce
- Create a YUP processor with:
Main input: Stereo
Sidechain input: Stereo
Main output: Stereo
-
Load the CLAP plugin in a compatible host.
-
Route a second track to the plugin's secondary input.
-
Read the auxiliary input from the processor.
Actual result
- The secondary input may be exposed by the host.
- The host can provide sidechain samples through
audio_inputs[1].
- The wrapper does not process or forward
audio_inputs[1].
- The processor receives only a buffer based on the output channels.
- The sidechain signal is unavailable to the DSP.
Expected result
- The host exposes the secondary input as an auxiliary audio port.
audio_inputs[1] is preserved independently from the main input.
- The DSP can read the sidechain through the processing context.
- Sidechain-driven gain reduction works correctly.
Acceptance criteria
Regression tests
Add an automated sidechain processing test with:
Main input: constant signal
Sidechain: silence, followed by an impulse or sine wave
Expected: gain reduction occurs only while the sidechain is active
The test should verify:
- the main input reaches the processor;
- the auxiliary input reaches the processor independently;
- changing the sidechain signal changes the DSP result;
- disabling the sidechain restores unkeyed behavior;
- the main output does not accidentally contain copied sidechain samples.
Additional note
The CLAP port declaration alone is not the primary issue.
A secondary port without CLAP_AUDIO_PORT_IS_MAIN is a valid auxiliary input. The blocking issue is that the wrapper discards auxiliary input data before invoking the processor.
The complete fix therefore requires changes to both:
- the CLAP audio-buffer transport;
- YUP's internal multibus processing API.
[CLAP] Sidechain input is declared but not forwarded to the DSP
Summary
The current CLAP wrapper exposes secondary audio input ports correctly, but sidechain audio is not forwarded to the YUP processor.
A host can provide a secondary CLAP audio input, but the wrapper only processes input buses that have a matching output bus. In a standard sidechain layout with two inputs and one output, the sidechain bus is ignored.
The current
AudioProcessContextalso exposes only one in-place audio buffer, so processors have no API for accessing auxiliary input buses separately.Severity
P0 / Blocking
This prevents the implementation of functional sidechain-based processors, including:
Typical affected layout
Current behavior
Port declaration is mostly correct
The CLAP wrapper marks only the first input port as the main port:
CLAP does not define a dedicated sidechain flag. A secondary input port without
CLAP_AUDIO_PORT_IS_MAINis therefore a valid auxiliary input that may be used as a sidechain.However, this role is inferred from the port index rather than represented explicitly in YUP's bus model.
The sidechain bus is ignored during processing
The wrapper processes only input buses with a corresponding output bus:
For the standard sidechain layout:
the loop executes only once.
audio_inputs[0]is processed, whileaudio_inputs[1], containing the sidechain signal, is never read.This affects both the 32-bit and 64-bit processing paths.
The processor receives only output channel pointers
The processing buffer is built exclusively from output buses:
Secondary input buffers are therefore not included in the context passed to the DSP.
AudioProcessContextis not bus-awareThe current processing API exposes a single audio buffer:
There is no API for accessing:
Even if the CLAP wrapper preserved the sidechain samples, a processor could not access them independently.
Expected behavior
The wrapper should support a layout such as:
During processing, the DSP should receive separate views for:
The sidechain input must remain available even though there is no corresponding output bus.
Proposed solution
1. Add an explicit bus role
Extend
AudioBuswith a role:The CLAP wrapper can then map the role explicitly:
info->flags = audioBus->getRole() == AudioBus::Role::Main ? CLAP_AUDIO_PORT_IS_MAIN : 0;This avoids relying on the convention that the first port is main and every following port is auxiliary.
2. Make
AudioProcessContextbus-awareReplace the single-buffer model with separate input and output bus views:
A processor could then access the sidechain explicitly:
3. Preserve all CLAP input ports
The wrapper should build input views directly from:
and output views directly from:
Input and output buses must not be paired globally by matching indices.
Only the main input should be copied to the main output when required for non-in-place processing.
4. Handle missing and inactive sidechain buffers safely
The implementation must support:
An unavailable sidechain should be exposed as silence and must not cause a crash.
Steps to reproduce
Load the CLAP plugin in a compatible host.
Route a second track to the plugin's secondary input.
Read the auxiliary input from the processor.
Actual result
audio_inputs[1].audio_inputs[1].Expected result
audio_inputs[1]is preserved independently from the main input.Acceptance criteria
MainorAuxiliary.CLAP_AUDIO_PORT_IS_MAIN.AudioProcessContextexposes individual input and output buses.clap-validator.Regression tests
Add an automated sidechain processing test with:
The test should verify:
Additional note
The CLAP port declaration alone is not the primary issue.
A secondary port without
CLAP_AUDIO_PORT_IS_MAINis a valid auxiliary input. The blocking issue is that the wrapper discards auxiliary input data before invoking the processor.The complete fix therefore requires changes to both: