Repository navigation
Fix crash loading Audio Units with side-chain input buses - #521
Open
RustamKuramshin wants to merge 1 commit into
Open
RustamKuramshin wants to merge 1 commit into
RustamKuramshin wants to merge 1 commit into
Conversation
ExternalPlugin::process() built the AudioBuffer handed to processBlock() with getTotalNumOutputChannels() channels. Plugins with extra input buses (e.g. a side-chain input) have more total input channels than that, and JUCE's AudioUnitPluginInstance::renderGetInput() locates each input bus by its channel offset inside this very buffer. For a stereo effect with a stereo side-chain the AU asks for bus 1 at offset 2, the buffer has only two channels, and the host reads past its end: null channel pointers lead to a memmove on address 0 and a SIGSEGV inside the plugin constructor (detectReloadType). Every AudioUnit with a side-chain bus crashed on load (FabFilter Pro-C 2, TDR Kotelnikov GE, Softube FET Compressor, UAD 1176, Pulsar P11 ...). Allocate max(total inputs, total outputs) channels instead; the extra channels are zero-filled scratch, exactly like the extra output channels already were. VST3 hosting is unaffected (the VST3 wrapper checks bus bounds itself).
RustamKuramshin
force-pushed
the
fix/au-sidechain-process-buffer
branch
from
October 17, 2026 22:28
a34d97f to
dad135a
Compare
Manishram-ai
approved these changes
Nov 1, 2026
Manishram-ai
left a comment
Contributor
There was a problem hiding this comment.
This matches JUCE's docs for processBlock, which say the buffer needs at least as many channels as the larger of total inputs and total outputs, including side-chain buses. Before this, a plugin with a side-chain input got a buffer sized to its outputs only. The extra channels now get the zeroed dummy buffers from the loop below, so the side-chain just sees silence. I couldn't test it without one of those plugins, but the change looks right to me.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Loading any Audio Unit that exposes a side-chain (or any extra) input bus crashed with
SIGSEGVinside the plugin constructor on macOS. Reproduced with FabFilter Pro-C 2, TDR Kotelnikov GE, Softube FET Compressor Mk II, UAD 1176LN and Pulsar P11 Abyss (.component), all with pedalboard 0.9.26 from PyPI; the same plugins load fine as VST3.Cause
ExternalPlugin::process()builds theAudioBufferhanded toprocessBlock()withgetTotalNumOutputChannels()channels. Plugins with extra input buses have more total input channels than that, and JUCE'sAudioUnitPluginInstance::renderGetInput()locates every input bus by its channel offset inside this very buffer (getBusBuffer(*currentBuffer, true, busIndex)). For a stereo effect with a stereo side-chain the AU asks for bus 1 at offset 2, the buffer has only two channels,AudioBuffer::allocateChannelsreads null pointers past its end and the plugin's render callback doesmemmoveto address 0.Backtrace (debug build):
The VST3 wrapper in JUCE checks bus bounds itself, which is why only AU crashed.
Fix
Allocate
max(getTotalNumInputChannels(), getTotalNumOutputChannels())channels. The extra channels are zero-filled scratch, exactly like the extra output channels already were. Behaviour for plugins without extra buses is unchanged.Verification
Debug build on macOS 26.7 / arm64 / Python 3.13: the five AUs above and their VST3 counterparts all load and process audio, with identical reported latency and output level between the AU and VST3 build of each plugin. A guard inside JUCE's
renderGetInputwas tried as well and turned out to be unnecessary once the buffer has the right size, so the JUCE submodule is untouched.