Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Input level calibration #525

Merged
merged 11 commits into from
Nov 17, 2024
69 changes: 55 additions & 14 deletions NeuralAmpModeler/NeuralAmpModeler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo& info)
GetParam(kEQActive)->InitBool("ToneStack", true);
GetParam(kOutNorm)->InitBool("OutNorm", true);
GetParam(kIRToggle)->InitBool("IRToggle", true);
GetParam(kCalibrateInput)->InitBool("CalibrateInput", false);
// TODO Double, label "dBu"
GetParam(kInputCalibrationLevel)->InitDouble("InputCalibrationLevel", 12.5, -30.0, 30.0, 0.1, "dBu");

mNoiseGateTrigger.AddListener(&mNoiseGateGain);

Expand Down Expand Up @@ -116,6 +119,7 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo& info)

const auto backgroundBitmap = pGraphics->LoadBitmap(BACKGROUND_FN);
const auto fileBackgroundBitmap = pGraphics->LoadBitmap(FILEBACKGROUND_FN);
const auto inputLevelBackgroundBitmap = pGraphics->LoadBitmap(INPUTLEVELBACKGROUND_FN);
const auto linesBitmap = pGraphics->LoadBitmap(LINES_FN);
const auto knobBackgroundBitmap = pGraphics->LoadBitmap(KNOBBACKGROUND_FN);
const auto switchHandleBitmap = pGraphics->LoadBitmap(SLIDESWITCHHANDLE_FN);
Expand All @@ -130,9 +134,8 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo& info)
// Areas for knobs
const auto knobsPad = 20.0f;
const auto knobsExtraSpaceBelowTitle = 25.0f;
const auto knobHeight = 120.f;
const auto singleKnobPad = -2.0f;
const auto knobsArea = contentArea.GetFromTop(knobHeight)
const auto knobsArea = contentArea.GetFromTop(NAM_KNOB_HEIGHT)
.GetReducedFromLeft(knobsPad)
.GetReducedFromRight(knobsPad)
.GetVShifted(titleHeight + knobsExtraSpaceBelowTitle);
Expand Down Expand Up @@ -250,15 +253,19 @@ NeuralAmpModeler::NeuralAmpModeler(const InstanceInfo& info)
},
gearSVG));

pGraphics->AttachControl(new NAMSettingsPageControl(b, backgroundBitmap, crossSVG, style), kCtrlTagSettingsBox)
pGraphics
->AttachControl(new NAMSettingsPageControl(
b, backgroundBitmap, inputLevelBackgroundBitmap, switchHandleBitmap, crossSVG, style),
kCtrlTagSettingsBox)
->Hide(true);

pGraphics->ForAllControlsFunc([](IControl* pControl) {
pControl->SetMouseEventsWhenDisabled(true);
pControl->SetMouseOverWhenDisabled(true);
});

pGraphics->GetControlWithTag(kCtrlTagOutNorm)->SetMouseEventsWhenDisabled(false);
// pGraphics->GetControlWithTag(kCtrlTagOutNorm)->SetMouseEventsWhenDisabled(false);
// pGraphics->GetControlWithTag(kCtrlTagCalibrateInput)->SetMouseEventsWhenDisabled(false);
};
}

Expand Down Expand Up @@ -381,9 +388,19 @@ void NeuralAmpModeler::OnIdle()
{
pGraphics->GetControlWithTag(kCtrlTagOutNorm)->SetDisabled(!mModel->HasLoudness());
ModelInfo modelInfo;
modelInfo.sampleRate = mModel->GetEncapsulatedSampleRate();
modelInfo.knownSampleRate = true;
modelInfo.sampleRate.known = true;
modelInfo.sampleRate.value = mModel->GetEncapsulatedSampleRate();
modelInfo.inputCalibrationLevel.known = mModel->HasInputLevel();
modelInfo.inputCalibrationLevel.value = mModel->HasInputLevel() ? mModel->GetInputLevel() : 0.0;
modelInfo.outputCalibrationLevel.known = mModel->HasOutputLevel();
modelInfo.outputCalibrationLevel.value = mModel->HasOutputLevel() ? mModel->GetOutputLevel() : 0.0;

static_cast<NAMSettingsPageControl*>(pGraphics->GetControlWithTag(kCtrlTagSettingsBox))->SetModelInfo(modelInfo);

const bool disableInputCalibrationControls = !mModel->HasInputLevel();
pGraphics->GetControlWithTag(kCtrlTagCalibrateInput)->SetDisabled(disableInputCalibrationControls);
pGraphics->GetControlWithTag(kCtrlTagInputCalibrationLevel)->SetDisabled(disableInputCalibrationControls);

mNewModelLoadedInDSP = false;
}
}
Expand Down Expand Up @@ -461,13 +478,25 @@ void NeuralAmpModeler::OnUIOpen()
}

if (mModel != nullptr)
GetUI()->GetControlWithTag(kCtrlTagOutNorm)->SetDisabled(!mModel->HasLoudness());
{
auto* pGraphics = GetUI();
assert(pGraphics != nullptr);
pGraphics->GetControlWithTag(kCtrlTagOutNorm)->SetDisabled(!mModel->HasLoudness());
const bool disableInputCalibrationControls = !mModel->HasInputLevel();
pGraphics->GetControlWithTag(kCtrlTagCalibrateInput)->SetDisabled(disableInputCalibrationControls);
pGraphics->GetControlWithTag(kCtrlTagInputCalibrationLevel)->SetDisabled(disableInputCalibrationControls);
}
}

void NeuralAmpModeler::OnParamChange(int paramIdx)
{
switch (paramIdx)
{
// Changes to the input gain
case kCalibrateInput:
case kInputCalibrationLevel:
case kInputLevel: _SetInputGain(); break;
// Tone stack:
case kToneBass: mToneStack->SetParam("bass", GetParam(paramIdx)->Value()); break;
case kToneMid: mToneStack->SetParam("middle", GetParam(paramIdx)->Value()); break;
case kToneTreble: mToneStack->SetParam("treble", GetParam(paramIdx)->Value()); break;
Expand All @@ -487,7 +516,7 @@ void NeuralAmpModeler::OnParamChangeUI(int paramIdx, EParamSource source)
case kEQActive:
pGraphics->ForControlInGroup("EQ_KNOBS", [active](IControl* pControl) { pControl->SetDisabled(!active); });
break;
case kIRToggle: pGraphics->GetControlWithTag(kCtrlTagIRFileBrowser)->SetDisabled(!active);
case kIRToggle: pGraphics->GetControlWithTag(kCtrlTagIRFileBrowser)->SetDisabled(!active); break;
default: break;
}
}
Expand Down Expand Up @@ -551,6 +580,7 @@ void NeuralAmpModeler::_ApplyDSPStaging()
mShouldRemoveModel = false;
mModelCleared = true;
_UpdateLatency();
_SetInputGain();
}
if (mShouldRemoveIR)
{
Expand All @@ -566,6 +596,7 @@ void NeuralAmpModeler::_ApplyDSPStaging()
mStagedModel = nullptr;
mNewModelLoadedInDSP = true;
_UpdateLatency();
_SetInputGain();
}
if (mStagedIR != nullptr)
{
Expand Down Expand Up @@ -651,6 +682,17 @@ void NeuralAmpModeler::_ResetModelAndIR(const double sampleRate, const int maxBl
}
}

void NeuralAmpModeler::_SetInputGain()
{
iplug::sample inputGainDB = GetParam(kInputLevel)->Value();
// Input calibration
if ((mModel != nullptr) && (mModel->HasInputLevel()) && GetParam(kCalibrateInput)->Bool())
{
inputGainDB += GetParam(kInputCalibrationLevel)->Value() - mModel->GetInputLevel();
}
mInputGain = DBToAmp(inputGainDB);
}

std::string NeuralAmpModeler::_StageModel(const WDL_String& modelPath)
{
WDL_String previousNAMPath = mNAMPath;
Expand Down Expand Up @@ -787,13 +829,12 @@ void NeuralAmpModeler::_ProcessInput(iplug::sample** inputs, const size_t nFrame
}

// On the standalone, we can probably assume that the user has plugged into only one input and they expect it to be
// carried straight through. Don't apply any division over nCahnsIn because we're just "catching anything out there."
// carried straight through. Don't apply any division over nChansIn because we're just "catching anything out there."
// However, in a DAW, it's probably something providing stereo, and we want to take the average in order to avoid
// doubling the loudness.
#ifdef APP_API
const double gain = pow(10.0, GetParam(kInputLevel)->Value() / 20.0);
#else
const double gain = pow(10.0, GetParam(kInputLevel)->Value() / 20.0) / (float)nChansIn;
// doubling the loudness. (This would change w/ double mono processing)
double gain = mInputGain;
#ifndef APP_API
gain /= (float)nChansIn;
#endif
// Assume _PrepareBuffers() was already called
for (size_t c = 0; c < nChansIn; c++)
Expand Down
20 changes: 20 additions & 0 deletions NeuralAmpModeler/NeuralAmpModeler.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ enum EParams
kEQActive,
kOutNorm,
kIRToggle,
// Input calibration
kCalibrateInput,
kInputCalibrationLevel,
kNumParams
};

Expand All @@ -54,6 +57,8 @@ enum ECtrlTags
kCtrlTagOutputMeter,
kCtrlTagSettingsBox,
kCtrlTagOutNorm,
kCtrlTagCalibrateInput,
kCtrlTagInputCalibrationLevel,
kNumCtrlTags
};

Expand Down Expand Up @@ -102,7 +107,17 @@ class ResamplingNAM : public nam::DSP
// Get the other information from the encapsulated NAM so that we can tell the outside world about what we're
// holding.
if (mEncapsulated->HasLoudness())
{
SetLoudness(mEncapsulated->GetLoudness());
}
if (mEncapsulated->HasInputLevel())
{
SetInputLevel(mEncapsulated->GetInputLevel());
}
if (mEncapsulated->HasOutputLevel())
{
SetOutputLevel(mEncapsulated->GetOutputLevel());
}

// NOTE: prewarm samples doesn't mean anything--we can prewarm the encapsulated model as it likes and be good to
// go.
Expand Down Expand Up @@ -228,6 +243,8 @@ class NeuralAmpModeler final : public iplug::Plugin
// Resetting for models and IRs, called by OnReset
void _ResetModelAndIR(const double sampleRate, const int maxBlockSize);

void _SetInputGain();

// Unserialize current-version plug-in data:
int _UnserializeStateCurrent(const iplug::IByteChunk& chunk, int startPos);
// Unserialize v0.7.9 legacy data:
Expand All @@ -253,6 +270,9 @@ class NeuralAmpModeler final : public iplug::Plugin
iplug::sample** mInputPointers = nullptr;
iplug::sample** mOutputPointers = nullptr;

// Input and (soon) output gain
iplug::sample mInputGain = 1.0;

// Noise gates
dsp::noise_gate::Trigger mNoiseGateTrigger;
dsp::noise_gate::Gain mNoiseGateGain;
Expand Down
Loading
Loading