Add a little level meter to audio2 module
Signed-off-by: falkTX <falktx@falktx.com>
This commit is contained in:
parent
377636fa7a
commit
756271f1d2
1 changed files with 191 additions and 24 deletions
|
@ -44,7 +44,7 @@ struct NanoKnob : Knob {
|
|||
const int knobSize = std::min(w, h - BND_WIDGET_HEIGHT * 2) - ringSize;
|
||||
|
||||
const int knobStartX = w / 2 - knobSize / 2;
|
||||
const int knobStartY = BND_WIDGET_HEIGHT + ringSize;
|
||||
const int knobStartY = ringSize;
|
||||
const int knobCenterX = knobStartX + knobSize / 2;
|
||||
const int knobCenterY = knobStartY + knobSize / 2;
|
||||
|
||||
|
@ -78,6 +78,18 @@ struct NanoKnob : Knob {
|
|||
nvgStrokeColor(args.vg, nvgRGBAf(testing.r, testing.g, testing.b, 0.1f));
|
||||
nvgStroke(args.vg);
|
||||
|
||||
// line indicator
|
||||
nvgStrokeWidth(args.vg, 2);
|
||||
nvgSave(args.vg);
|
||||
nvgTranslate(args.vg, knobCenterX, knobCenterY);
|
||||
nvgRotate(args.vg, nvgDegToRad(45.0f) + normalizedValue * nvgDegToRad(270.0f));
|
||||
nvgBeginPath(args.vg);
|
||||
nvgRoundedRect(args.vg, -2, knobSize / 2 - 9, 2, 6, 1);
|
||||
nvgClosePath(args.vg);
|
||||
nvgFillColor(args.vg, nvgRGBf(1.0f, 1.0f, 1.0f));
|
||||
nvgFill(args.vg);
|
||||
nvgRestore(args.vg);
|
||||
|
||||
// adjusted from VCVRack's LightWidget.cpp
|
||||
if (const float halo = settings::haloBrightness)
|
||||
{
|
||||
|
@ -95,7 +107,7 @@ struct NanoKnob : Knob {
|
|||
}
|
||||
|
||||
// bottom label (value)
|
||||
bndIconLabelValue(args.vg, 0, BND_WIDGET_HEIGHT + knobSize + ringSize, w, BND_WIDGET_HEIGHT, -1,
|
||||
bndIconLabelValue(args.vg, 0, knobSize + ringSize, w, BND_WIDGET_HEIGHT, -1,
|
||||
testing, BND_CENTER,
|
||||
BND_LABEL_FONT_SIZE, displayString.c_str(), nullptr);
|
||||
|
||||
|
@ -113,15 +125,10 @@ struct NanoKnob : Knob {
|
|||
const int knobSize = std::min(w, h - BND_WIDGET_HEIGHT * 2) - ringSize;
|
||||
|
||||
const int knobStartX = w / 2 - knobSize / 2;
|
||||
const int knobStartY = BND_WIDGET_HEIGHT + ringSize;
|
||||
const int knobStartY = ringSize;
|
||||
const int knobCenterX = knobStartX + knobSize / 2;
|
||||
const int knobCenterY = knobStartY + knobSize / 2;
|
||||
|
||||
// top label (name)
|
||||
bndIconLabelValue(args.vg, 0, 0, w, BND_WIDGET_HEIGHT, -1,
|
||||
SCHEME_WHITE, BND_CENTER,
|
||||
BND_LABEL_FONT_SIZE, displayLabel.c_str(), nullptr);
|
||||
|
||||
// knob
|
||||
NVGcolor shade_top;
|
||||
NVGcolor shade_down;
|
||||
|
@ -164,18 +171,6 @@ struct NanoKnob : Knob {
|
|||
|
||||
nvgLineCap(args.vg, NVG_ROUND);
|
||||
|
||||
// line indicator
|
||||
nvgStrokeWidth(args.vg, 2);
|
||||
nvgSave(args.vg);
|
||||
nvgTranslate(args.vg, knobCenterX, knobCenterY);
|
||||
nvgRotate(args.vg, nvgDegToRad(45.0f) + normalizedValue * nvgDegToRad(270.0f));
|
||||
nvgBeginPath(args.vg);
|
||||
nvgRoundedRect(args.vg, -2, knobSize / 2 - 9, 2, 6, 1);
|
||||
nvgClosePath(args.vg);
|
||||
nvgFillColor(args.vg, nvgRGBf(1.0f, 1.0f, 1.0f));
|
||||
nvgFill(args.vg);
|
||||
nvgRestore(args.vg);
|
||||
|
||||
// outer ring background
|
||||
nvgBeginPath(args.vg);
|
||||
nvgArc(args.vg,
|
||||
|
@ -189,6 +184,11 @@ struct NanoKnob : Knob {
|
|||
nvgStrokeColor(args.vg, nvgRGBf(0.5f, 0.5f, 0.5f));
|
||||
nvgStroke(args.vg);
|
||||
|
||||
// bottom label (name)
|
||||
bndIconLabelValue(args.vg, 0, knobStartY + knobSize + BND_WIDGET_HEIGHT * 0.75f, w, BND_WIDGET_HEIGHT, -1,
|
||||
SCHEME_WHITE, BND_CENTER,
|
||||
BND_LABEL_FONT_SIZE, displayLabel.c_str(), nullptr);
|
||||
|
||||
Knob::draw(args);
|
||||
}
|
||||
|
||||
|
@ -202,6 +202,36 @@ struct NanoKnob : Knob {
|
|||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Find the highest absolute and normalized value within a float array.
|
||||
*/
|
||||
static inline
|
||||
float d_findMaxNormalizedFloat(const float floats[], const std::size_t count)
|
||||
{
|
||||
DISTRHO_SAFE_ASSERT_RETURN(floats != nullptr, 0.0f);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(count > 0, 0.0f);
|
||||
|
||||
static const float kEmptyFloats[8192] = {};
|
||||
|
||||
if (count <= 8192 && std::memcmp(floats, kEmptyFloats, count) == 0)
|
||||
return 0.0f;
|
||||
|
||||
float tmp, maxf2 = std::abs(floats[0]);
|
||||
|
||||
for (std::size_t i=1; i<count; ++i)
|
||||
{
|
||||
tmp = std::abs(*floats++);
|
||||
|
||||
if (tmp > maxf2)
|
||||
maxf2 = tmp;
|
||||
}
|
||||
|
||||
if (maxf2 > 1.0f)
|
||||
maxf2 = 1.0f;
|
||||
|
||||
return maxf2;
|
||||
}
|
||||
|
||||
template<int numIO>
|
||||
struct HostAudio : Module {
|
||||
CardinalPluginContext* const pcontext;
|
||||
|
@ -214,6 +244,8 @@ struct HostAudio : Module {
|
|||
// for rack core audio module compatibility
|
||||
dsp::RCFilter dcFilters[numIO];
|
||||
bool dcFilterEnabled = (numIO == 2);
|
||||
float gainMeterL = 0.0f;
|
||||
float gainMeterR = 0.0f;
|
||||
|
||||
HostAudio()
|
||||
: pcontext(static_cast<CardinalPluginContext*>(APP)),
|
||||
|
@ -250,6 +282,7 @@ struct HostAudio : Module {
|
|||
const float* const* const dataIns = pcontext->dataIns;
|
||||
float** const dataOuts = pcontext->dataOuts;
|
||||
|
||||
const int blockFrames = pcontext->engine->getBlockFrames();
|
||||
const int64_t blockFrame = pcontext->engine->getBlockFrame();
|
||||
|
||||
if (lastBlockFrame != blockFrame)
|
||||
|
@ -259,7 +292,7 @@ struct HostAudio : Module {
|
|||
}
|
||||
|
||||
const int k = dataFrame++;
|
||||
DISTRHO_SAFE_ASSERT_RETURN(k < pcontext->engine->getBlockFrames(),);
|
||||
DISTRHO_SAFE_ASSERT_INT2_RETURN(k < blockFrames, k, blockFrames,);
|
||||
|
||||
const float gain = numParams != 0 ? std::pow(params[0].getValue(), 2.f) : 1.0f;
|
||||
|
||||
|
@ -285,8 +318,23 @@ struct HostAudio : Module {
|
|||
dataOuts[i][k] += clamp(v * gain, -1.0f, 1.0f);
|
||||
}
|
||||
|
||||
if (numInputs == 2 && ! inputs[1].isConnected())
|
||||
dataOuts[1][k] += dataOuts[0][k];
|
||||
if (numInputs == 2)
|
||||
{
|
||||
const bool connected = inputs[1].isConnected();
|
||||
|
||||
if (! connected)
|
||||
dataOuts[1][k] += dataOuts[0][k];
|
||||
|
||||
if (dataFrame == blockFrames)
|
||||
{
|
||||
gainMeterL = std::max(gainMeterL, d_findMaxNormalizedFloat(dataOuts[0], blockFrames));
|
||||
|
||||
if (connected)
|
||||
gainMeterR = std::max(gainMeterR, d_findMaxNormalizedFloat(dataOuts[1], blockFrames));
|
||||
else
|
||||
gainMeterR = gainMeterL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_t* dataToJson() override
|
||||
|
@ -307,6 +355,120 @@ struct HostAudio : Module {
|
|||
}
|
||||
};
|
||||
|
||||
template<int numIO>
|
||||
struct NanoMeter : Widget {
|
||||
HostAudio<numIO>* const module;
|
||||
|
||||
NanoMeter(HostAudio<numIO>* const m)
|
||||
: module(m)
|
||||
{
|
||||
box.size = Vec(100, 100);
|
||||
}
|
||||
|
||||
void drawLayer(const DrawArgs& args, int layer) override
|
||||
{
|
||||
if (layer != 1)
|
||||
return;
|
||||
|
||||
const float usableHeight = box.size.y - 10.0f;
|
||||
|
||||
// draw background
|
||||
nvgBeginPath(args.vg);
|
||||
nvgRect(args.vg,
|
||||
0,
|
||||
0,
|
||||
box.size.x,
|
||||
usableHeight);
|
||||
nvgFillColor(args.vg, nvgRGB(26, 26, 26));
|
||||
nvgFill(args.vg);
|
||||
|
||||
nvgFillColor(args.vg, nvgRGBAf(0.76f, 0.11f, 0.22f, 0.5f));
|
||||
nvgStrokeColor(args.vg, nvgRGBf(0.76f, 0.11f, 0.22f));
|
||||
|
||||
if (module != nullptr)
|
||||
{
|
||||
float gainMeterL = 0.0f;
|
||||
float gainMeterR = 0.0f;
|
||||
std::swap(gainMeterL, module->gainMeterL);
|
||||
std::swap(gainMeterR, module->gainMeterR);
|
||||
|
||||
const float heightL = sqrtf(gainMeterL) * usableHeight;
|
||||
nvgBeginPath(args.vg);
|
||||
nvgRect(args.vg, 0.0f, usableHeight - heightL, box.size.x * 0.5f - 1.0f, heightL);
|
||||
nvgFill(args.vg);
|
||||
nvgStroke(args.vg);
|
||||
|
||||
const float heightR = sqrtf(gainMeterR) * usableHeight;
|
||||
nvgBeginPath(args.vg);
|
||||
nvgRect(args.vg, box.size.x * 0.5f + 1.0f, usableHeight - heightR, box.size.x * 0.5f - 2.0f, heightR);
|
||||
nvgFill(args.vg);
|
||||
nvgStroke(args.vg);
|
||||
}
|
||||
|
||||
nvgLineCap(args.vg, NVG_ROUND);
|
||||
|
||||
nvgBeginPath(args.vg);
|
||||
nvgMoveTo(args.vg, 0.0f, usableHeight + 2.0f);
|
||||
nvgLineTo(args.vg, box.size.x * 0.5f - 11.0f, usableHeight + 2.0f);
|
||||
nvgStroke(args.vg);
|
||||
|
||||
nvgBeginPath(args.vg);
|
||||
nvgMoveTo(args.vg, 0.0f, usableHeight + 4.0f);
|
||||
nvgLineTo(args.vg, box.size.x * 0.5f - 16.0f, usableHeight + 4.0f);
|
||||
nvgStroke(args.vg);
|
||||
|
||||
nvgBeginPath(args.vg);
|
||||
nvgMoveTo(args.vg, 0.0f, usableHeight + 6.0f);
|
||||
nvgLineTo(args.vg, box.size.x * 0.5f - 19.0f, usableHeight + 6.0f);
|
||||
nvgStroke(args.vg);
|
||||
|
||||
nvgBeginPath(args.vg);
|
||||
nvgMoveTo(args.vg, 0.0f, usableHeight + 8.0f);
|
||||
nvgLineTo(args.vg, box.size.x * 0.5f - 22.0f, usableHeight + 8.0f);
|
||||
nvgStroke(args.vg);
|
||||
|
||||
nvgBeginPath(args.vg);
|
||||
nvgMoveTo(args.vg, 0.0f, usableHeight + 10.0f);
|
||||
nvgLineTo(args.vg, box.size.x * 0.5f - 24.0f, usableHeight + 10.0f);
|
||||
nvgStroke(args.vg);
|
||||
|
||||
nvgBeginPath(args.vg);
|
||||
nvgMoveTo(args.vg, 0.0f, usableHeight + 12.0f);
|
||||
nvgLineTo(args.vg, box.size.x * 0.5f - 26.0f, usableHeight + 12.0f);
|
||||
nvgStroke(args.vg);
|
||||
|
||||
nvgBeginPath(args.vg);
|
||||
nvgMoveTo(args.vg, box.size.x * 0.5f + 10.0f, usableHeight + 2.0f);
|
||||
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 2.0f);
|
||||
nvgStroke(args.vg);
|
||||
|
||||
nvgBeginPath(args.vg);
|
||||
nvgMoveTo(args.vg, box.size.x * 0.5f + 15.0f, usableHeight + 4.0f);
|
||||
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 4.0f);
|
||||
nvgStroke(args.vg);
|
||||
|
||||
nvgBeginPath(args.vg);
|
||||
nvgMoveTo(args.vg, box.size.x * 0.5f + 18.0f, usableHeight + 6.0f);
|
||||
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 6.0f);
|
||||
nvgStroke(args.vg);
|
||||
|
||||
nvgBeginPath(args.vg);
|
||||
nvgMoveTo(args.vg, box.size.x * 0.5f + 20.0f, usableHeight + 8.0f);
|
||||
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 8.0f);
|
||||
nvgStroke(args.vg);
|
||||
|
||||
nvgBeginPath(args.vg);
|
||||
nvgMoveTo(args.vg, box.size.x * 0.5f + 22.0f, usableHeight + 10.0f);
|
||||
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 10.0f);
|
||||
nvgStroke(args.vg);
|
||||
|
||||
nvgBeginPath(args.vg);
|
||||
nvgMoveTo(args.vg, box.size.x * 0.5f + 24.0f, usableHeight + 12.0f);
|
||||
nvgLineTo(args.vg, box.size.x - 1.0f, usableHeight + 12.0f);
|
||||
nvgStroke(args.vg);
|
||||
}
|
||||
};
|
||||
|
||||
template<int numIO>
|
||||
struct HostAudioWidget : ModuleWidget {
|
||||
static constexpr const float startX_In = 14.0f;
|
||||
|
@ -336,7 +498,12 @@ struct HostAudioWidget : ModuleWidget {
|
|||
|
||||
if (numIO == 2)
|
||||
{
|
||||
addParam(createParamCentered<NanoKnob>(Vec(middleX, 290.0f), m, 0));
|
||||
addParam(createParamCentered<NanoKnob>(Vec(middleX, 310.0f), m, 0));
|
||||
|
||||
NanoMeter<numIO>* const meter = new NanoMeter<numIO>(m);
|
||||
meter->box.pos = Vec(startX_In + padding - 4.0f, startY + padding * 2);
|
||||
meter->box.size = Vec(padding * 2.0f - 4.0f, 136.0f);
|
||||
addChild(meter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue