audio: replaceIR(slotId, path, gain) for in-place cab/IR swap (#83)

* audio: add replaceIR(slotId, path, gain) for in-place cab/IR swap

Swap an existing convolution slot's IR without a full loadPreset, so the rest
of the chain — the amp VST above all — is not torn down and rebuilt (that
teardown is the ~1-2 s wait when changing cabs / mic position). Mirrors the
existing loadIR worker but calls SignalChain::replaceProcessor(slotId, ...);
optional gain updates the slot post-gain (the cab makeup).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* audio: expose replaceIR over the IPC bridge

Wire the native replaceIR(slotId, path, gain) through audio-bridge (ipcMain
handle) + preload, so renderers get feedBackDesktop.audio.replaceIR. Lets the
rig-builder cab room swap a cab's IRs in place instead of a full loadPreset +
param re-apply (that re-apply was the brief 'can't move the mic yet' lag).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* audio: replaceIR updates slot name/path so getChainState reflects the swap

replaceProcessor deliberately preserves the target slot's name/path during
the prepare/fault window (so a fault in prepareToPlay is blocklisted against
the right plugin path). It kept them even on success, so after a successful
replaceIR the slot's audio was the new IR but getChainState()/preset-save
still reported the OLD IR name+path — a footgun for any consumer that
persists a chain read back from getChainState().

Add optional newName/newPath to replaceProcessor, applied under the swap lock
ONLY on success (empty = keep, so the sandbox-promotion caller is unchanged).
ReplaceIRWorker passes "IR: <name>" + the new path, mirroring LoadIRWorker.

Built (npm run build:audio) clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Jafz2001 <ignacio.fritis@mundotelecomunicaciones.cl>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: byrongamatos <xasiklas@gmail.com>
This commit is contained in:
Jorge Fritis
2026-07-08 22:20:06 +02:00
committed by GitHub
co-authored by Claude Opus 4.8 Jafz2001 byrongamatos
parent a53fd38732
commit 56e929da4e
5 changed files with 85 additions and 2 deletions
+7 -1
View File
@@ -432,7 +432,8 @@ void SignalChain::removeProcessor(int slotId)
if (idx >= 0) slots.remove(idx);
}
bool SignalChain::replaceProcessor(int slotId, std::unique_ptr<juce::AudioProcessor> processor)
bool SignalChain::replaceProcessor(int slotId, std::unique_ptr<juce::AudioProcessor> processor,
const juce::String& newName, const juce::String& newPath)
{
if (!processor) return false;
@@ -469,6 +470,11 @@ bool SignalChain::replaceProcessor(int slotId, std::unique_ptr<juce::AudioProces
auto* slot = slots[idx];
old = std::move(slot->processor);
slot->processor = std::move(staging.processor);
// Swap succeeded: adopt the new identity so getChainState()/preset save
// report the swapped-in processor, not the one it replaced. Only when
// provided — the sandbox-promotion caller passes none and keeps identity.
if (newName.isNotEmpty()) slot->name = newName;
if (newPath.isNotEmpty()) slot->path = newPath;
}
// Tear the old processor down OUTSIDE the audio lock: releaseResources() (and
// a VST3 destructor) can block, and must never stall process() on it.