Initial release — InstaShadow mastering compressor v1.0

Dual-stage compressor (optical + VCA) with output transformer saturation.
- Port-Hamiltonian T4B opto-cell model with implicit trapezoidal integration
- Feed-forward VCA with 7 ratios, 6 attack/release presets, Dual release mode
- 3 transformer types (Nickel/Iron/Steel) with 4x oversampled waveshaping
- Analog-style needle VU meters, horizontal GR meters
- Sidechain HPF, stereo link, independent section bypass
- Full state save/restore, CI/CD for Windows/macOS/Linux
This commit is contained in:
hariel1985
2026-03-27 16:03:24 +01:00
commit a587a43ff9
33 fájl változott, egészen pontosan 2417 új sor hozzáadva és 0 régi sor törölve

100
Source/VCACompressor.cpp Normal file
Fájl megtekintése

@@ -0,0 +1,100 @@
#include "VCACompressor.h"
VCACompressor::VCACompressor() {}
void VCACompressor::prepare (double sampleRate)
{
sr = sampleRate;
reset();
}
void VCACompressor::reset()
{
smoothedGrDb = 0.0;
dualFastEnv = 0.0;
dualSlowEnv = 0.0;
wasCompressing = false;
currentGrDb = 0.0f;
}
double VCACompressor::makeCoeff (double timeSec) const
{
if (timeSec <= 0.0) return 0.0;
return std::exp (-1.0 / (sr * timeSec));
}
float VCACompressor::computeGainReduction (float inputDb, float thresholdDb, float ratio) const
{
// Soft-knee gain computer
float halfKnee = kneeWidthDb * 0.5f;
if (inputDb < thresholdDb - halfKnee)
{
return 0.0f; // below threshold — no compression
}
else if (inputDb > thresholdDb + halfKnee)
{
// Above knee — full compression
float compressed = thresholdDb + (inputDb - thresholdDb) / ratio;
return compressed - inputDb; // negative dB value
}
else
{
// In knee region — quadratic interpolation
float x = inputDb - thresholdDb + halfKnee;
return ((1.0f / ratio) - 1.0f) * x * x / (2.0f * kneeWidthDb);
}
}
float VCACompressor::processSample (float sidechainDb, float thresholdDb, float ratio,
float attackSec, float releaseSec, bool dualRelease)
{
// Compute desired gain reduction
float desiredGrDb = computeGainReduction (sidechainDb, thresholdDb, ratio);
if (! dualRelease)
{
// Standard attack/release smoothing
double coeff;
if (desiredGrDb < smoothedGrDb)
coeff = makeCoeff ((double) attackSec); // attacking (GR going more negative)
else
coeff = makeCoeff ((double) releaseSec); // releasing
smoothedGrDb = coeff * smoothedGrDb + (1.0 - coeff) * (double) desiredGrDb;
}
else
{
// Dual release mode: two-stage release mimicking optical behavior
double attackCoeff = makeCoeff ((double) attackSec);
if (desiredGrDb < smoothedGrDb)
{
// Attacking
smoothedGrDb = attackCoeff * smoothedGrDb + (1.0 - attackCoeff) * (double) desiredGrDb;
dualFastEnv = smoothedGrDb;
dualSlowEnv = smoothedGrDb;
wasCompressing = true;
}
else
{
// Releasing: blend fast (~60ms) and slow (~2s) release
double fastCoeff = makeCoeff (0.06); // 60ms — first 50-80% of recovery
double slowCoeff = makeCoeff (2.0); // 2s — remaining recovery
dualFastEnv = fastCoeff * dualFastEnv + (1.0 - fastCoeff) * (double) desiredGrDb;
dualSlowEnv = slowCoeff * dualSlowEnv + (1.0 - slowCoeff) * (double) desiredGrDb;
// Blend: use the DEEPER (more negative) of the two
// This naturally creates the two-stage behavior:
// fast env recovers quickly, slow env holds longer
smoothedGrDb = std::min (dualFastEnv, dualSlowEnv);
wasCompressing = false;
}
}
currentGrDb = (float) smoothedGrDb;
// Convert GR dB to linear gain
return std::pow (10.0f, currentGrDb / 20.0f);
}