mirror of
https://github.com/got-feedBack/feedBack-desktop.git
synced 2026-08-11 03:09:56 +00:00
audio: per-slot postGain for parallel-branch loudness leveling (#58)
Adds an optional per-slot output gain (ProcessorSlot.postGain, default 1.0 = no-op) applied in SignalChain::runSlot after processBlock + pan, plumbed through setPostGain / IPC / preload / loadPreset / getChainState (mirroring setPan). Gives each parallel branch its own loudness trim. Review hardening (multi-angle + Codex): savePreset() now serializes postGain (it was read back but never written → save/load dropped it); setPostGain() rejects non-finite input (NaN would poison the buffer); new signalchain_postgain_test.cpp asserts gain scaling, NaN rejection, and serialization. Verified locally: build:audio clean; signalchain_postgain_test 5/5 pass. (Org CI runners failed to start — infra/billing, unrelated to the change; changes are platform-neutral C++.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
27e8f56ad8
commit
c8415113c8
@@ -1,5 +1,6 @@
|
||||
#include "SignalChain.h"
|
||||
#include "Sandbox/SandboxedProcessor.h"
|
||||
#include <cmath> // std::isfinite (postGain sanitisation)
|
||||
|
||||
#if ! JUCE_WINDOWS
|
||||
#include <csetjmp>
|
||||
@@ -287,6 +288,8 @@ void SignalChain::process(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& mi
|
||||
slotMidi.addEvent(drained[i].msg, 0);
|
||||
invokePlugin(*slot, [&](juce::AudioProcessor& p) { p.processBlock(buf, slotMidi); });
|
||||
applyPan(buf, numSamples, slot->pan);
|
||||
if (slot->postGain != 1.0f)
|
||||
buf.applyGain(0, numSamples, slot->postGain);
|
||||
};
|
||||
|
||||
// Fast path: no parallel branch → plain serial chain. Behaviour is unchanged
|
||||
@@ -548,6 +551,18 @@ void SignalChain::setPan(int slotId, float pan)
|
||||
if (idx >= 0) slots[idx]->pan = juce::jlimit(-1.0f, 1.0f, pan);
|
||||
}
|
||||
|
||||
void SignalChain::setPostGain(int slotId, float gain)
|
||||
{
|
||||
// Reject non-finite input: juce::jlimit passes NaN through unchanged (both
|
||||
// of its comparisons are false for NaN), and a NaN gain would then multiply
|
||||
// the slot buffer to NaN and poison the whole chain until the slot rebuilds.
|
||||
if (! std::isfinite(gain)) return;
|
||||
const juce::ScopedLock sl(lock);
|
||||
int idx = findSlotIndex(slotId);
|
||||
// Clamp to [0, 16] — a 0..+24 dB linear ceiling for the per-slot trim.
|
||||
if (idx >= 0) slots[idx]->postGain = juce::jlimit(0.0f, 16.0f, gain);
|
||||
}
|
||||
|
||||
void SignalChain::setBranch(int slotId, int branch)
|
||||
{
|
||||
const juce::ScopedLock sl(lock);
|
||||
@@ -674,6 +689,9 @@ juce::String SignalChain::savePreset() const
|
||||
if (slot->pan != 0.0f) slotObj->setProperty("pan", slot->pan);
|
||||
if (slot->branch != 0) slotObj->setProperty("branch", slot->branch);
|
||||
if (slot->branchSrc != 0) slotObj->setProperty("branchSrc", slot->branchSrc);
|
||||
// Per-slot output trim (loudness leveling). LoadPresetWorker reads this
|
||||
// back, so it must be written here or a save/load round-trip drops it.
|
||||
if (slot->postGain != 1.0f) slotObj->setProperty("postGain", slot->postGain);
|
||||
|
||||
// Save processor state as base64
|
||||
auto state = slot->getState();
|
||||
|
||||
Reference in New Issue
Block a user