Skip to content

Commit

Permalink
[FEATURE] Input level calibration (#525)
Browse files Browse the repository at this point in the history
* Rearrange about info

* Define parameters for input calibration

* No click through ModelInfoControl--just use the 'close' button in the corner

* Settings control: Clean up OnAttached() and set rect for background immediately

* Input calibration controls

* Input calibration controls

* Input calibration
* Disable both the switch and knob when model doesn't have input calibration metadata
* Display I/O calibration levels in model info
* Add tooltip to input calibraiton knob.
* Don't disable mouse events when input calibraiton controls are disabled.

* Set disable state correctly on UI open for input calibration controls

* Input level as text control, still needs cleaning up

* Improve input level graphics, still iffy layout
  • Loading branch information
sdatkinson authored Nov 17, 2024
1 parent dd3ad7a commit 420f438
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 56 deletions.
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

0 comments on commit 420f438

Please sign in to comment.