From e249f825643b3e01e0502ebd6c885ae8050e304b Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 28 Aug 2021 09:27:24 -0400 Subject: [PATCH] Redesign module CPU meter based on Pyer design. --- include/engine/Module.hpp | 23 +++++++------ src/app/ModuleWidget.cpp | 71 ++++++++++++++------------------------- src/engine/Module.cpp | 34 ++++++++++--------- 3 files changed, 56 insertions(+), 72 deletions(-) diff --git a/include/engine/Module.hpp b/include/engine/Module.hpp index e3b21b0a..1bb83db3 100644 --- a/include/engine/Module.hpp +++ b/include/engine/Module.hpp @@ -231,17 +231,6 @@ struct Module { std::string createPatchStorageDirectory(); std::string getPatchStorageDirectory(); - struct ProcessArgs { - /** The current sample rate in Hz. */ - float sampleRate; - /** The timestep of process() in seconds. - Defined by `1 / sampleRate`. - */ - float sampleTime; - /** Number of audio samples since the Engine's first sample. */ - int64_t frame; - }; - /** Getters for members */ plugin::Model* getModel() { return model; @@ -285,14 +274,26 @@ struct Module { // Virtual methods + struct ProcessArgs { + /** The current sample rate in Hz. */ + float sampleRate; + /** The timestep of process() in seconds. + Defined by `1 / sampleRate`. + */ + float sampleTime; + /** Number of audio samples since the Engine's first sample. */ + int64_t frame; + }; /** Advances the module by one audio sample. Override this method to read Inputs and Params and to write Outputs and Lights. */ virtual void process(const ProcessArgs& args) { step(); } + /** DEPRECATED. Override `process(const ProcessArgs& args)` instead. */ virtual void step() {} + /** Called instead of process() when Module is bypassed. Typically you do not need to override this. Use configBypass() instead. If you do override it, avoid reading param values, since the state of the module should have no effect on routing. diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index edfbddc8..9ea69c91 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -221,17 +221,6 @@ void ModuleWidget::draw(const DrawArgs& args) { int meterLength = module->meterLength(); int meterIndex = module->meterIndex(); - float meterMax = 0.f; - float meterAvg = 0.f; - for (int i = 0; i < meterLength; i++) { - float m = meterBuffer[i]; - meterAvg += m; - meterMax = std::max(meterMax, m); - } - meterAvg /= meterLength; - float percentMax = meterMax * sampleRate; - float mult = (percentMax <= 0.1f) ? 10.f : 1.f; - // // Text background // nvgBeginPath(args.vg); // nvgRect(args.vg, 0.0, box.size.y - infoHeight, box.size.x, infoHeight); @@ -240,48 +229,40 @@ void ModuleWidget::draw(const DrawArgs& args) { // Draw time plot nvgBeginPath(args.vg); - nvgMoveTo(args.vg, box.size.x, box.size.y); + nvgMoveTo(args.vg, 0.0, box.size.y); + math::Vec p1; for (int i = 0; i < meterLength; i++) { - int index = (meterIndex - i + meterLength) % meterLength; - float percent = math::clamp(meterBuffer[index] * mult * sampleRate, 0.f, 1.f); + int index = math::eucMod(meterIndex + i + 1, meterLength); + float meter = math::clamp(meterBuffer[index] * sampleRate, 0.f, 1.f); math::Vec p; - p.x = (1.f - (float) i / (meterLength - 1)) * box.size.x; - p.y = (1.f - percent) * (box.size.y); - nvgLineTo(args.vg, p.x, p.y); + p.x = (float) i / (meterLength - 1) * box.size.x; + p.y = (1.f - meter) * box.size.y; + if (i == 0) { + nvgLineTo(args.vg, VEC_ARGS(p)); + } + else { + math::Vec p2 = p; + p2.x -= 0.5f / (meterLength - 1) * box.size.x; + nvgBezierTo(args.vg, VEC_ARGS(p1), VEC_ARGS(p2), VEC_ARGS(p)); + } + p1 = p; + p1.x += 0.5f / (meterLength - 1) * box.size.x; } - NVGcolor color; - if (mult == 1.f) - color = nvgRGBAf(0.5, 0, 0, 0.85); - else if (mult == 10.f) - color = nvgRGBAf(0.85, 0, 0, 0.85); - nvgLineTo(args.vg, 0.0, box.size.y); + nvgLineTo(args.vg, box.size.x, box.size.y); nvgClosePath(args.vg); - nvgFillColor(args.vg, color); + NVGcolor color = nvgRGBAf(0.5, 0.5, 0.5, 1.0); + nvgFillColor(args.vg, color::alpha(color, 0.5)); nvgFill(args.vg); - - // Text - float percent = meterAvg * sampleRate * 100.f; - float microseconds = meterAvg * 1e6f; - std::string meterText = string::f("%.0fx\n%.2f μs\n%.1f%%", mult, microseconds, percent); - bndLabel(args.vg, 0.0, box.size.y - 60, INFINITY, INFINITY, -1, meterText.c_str()); - - // Draw border - nvgStrokeColor(args.vg, color); - nvgBeginPath(args.vg); - nvgRect(args.vg, 0, 0, box.size.x, box.size.y); nvgStrokeWidth(args.vg, 2.0); + nvgStrokeColor(args.vg, color); nvgStroke(args.vg); - } - // if (module) { - // nvgBeginPath(args.vg); - // nvgRect(args.vg, 0, 0, 20, 20); - // nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75)); - // nvgFill(args.vg); - - // std::string debugText = string::f("%d", module->id); - // bndLabel(args.vg, 0, 0, INFINITY, INFINITY, -1, debugText.c_str()); - // } + // Text + float percent = meterBuffer[meterIndex] * sampleRate * 100.f; + float microseconds = meterBuffer[meterIndex] * 1e6f; + std::string meterText = string::f("%.2f μs/sample %.1f%%", microseconds, percent); + bndLabel(args.vg, box.size.x - 130.0, 0.0, INFINITY, INFINITY, -1, meterText.c_str()); + } // Selection if (APP->scene->rack->isSelected(this)) { diff --git a/src/engine/Module.cpp b/src/engine/Module.cpp index e0d3e19f..df6452a5 100644 --- a/src/engine/Module.cpp +++ b/src/engine/Module.cpp @@ -14,18 +14,18 @@ namespace engine { // Arbitrary prime number so it doesn't over- or under-estimate time of buffered processors. -static const int meterDivider = 1; -static const int meterBufferLength = 128; +static const int METER_DIVIDER = 37; +static const int METER_BUFFER_LEN = 32; +static const float METER_TIME = 1.0f; struct Module::Internal { bool bypassed = false; - int64_t meterLastBlock = 0; int meterSamples = 0; - float meterTimeTotal = 0.f; + float meterDurationTotal = 0.f; - float meterBuffer[meterBufferLength] = {}; + float meterBuffer[METER_BUFFER_LEN] = {}; int meterIndex = 0; }; @@ -299,7 +299,7 @@ const float* Module::meterBuffer() { int Module::meterLength() { - return meterBufferLength; + return METER_BUFFER_LEN; } @@ -332,7 +332,7 @@ static void Port_step(Port* that, float deltaTime) { void Module::doProcess(const ProcessArgs& args) { // This global setting can change while the function is running, so use a local variable. - bool meterEnabled = settings::cpuMeter && (args.frame % meterDivider == 0); + bool meterEnabled = settings::cpuMeter && (args.frame % METER_DIVIDER == 0); // Start CPU timer double startTime; @@ -351,21 +351,23 @@ void Module::doProcess(const ProcessArgs& args) { double endTime = system::getTime(); float duration = endTime - startTime; - int64_t block = APP->engine->getBlock(); - if (block > internal->meterLastBlock) { + internal->meterSamples++; + internal->meterDurationTotal += duration; + + // Seconds we've been measuring + float meterTime = internal->meterSamples * METER_DIVIDER * args.sampleTime; + + if (meterTime >= METER_TIME) { // Push time to buffer if (internal->meterSamples > 0) { - internal->meterBuffer[internal->meterIndex++] = internal->meterTimeTotal / internal->meterSamples; - internal->meterIndex %= meterBufferLength; + internal->meterIndex++; + internal->meterIndex %= METER_BUFFER_LEN; + internal->meterBuffer[internal->meterIndex] = internal->meterDurationTotal / internal->meterSamples; } // Reset total internal->meterSamples = 0; - internal->meterTimeTotal = 0.f; + internal->meterDurationTotal = 0.f; } - - internal->meterLastBlock = block; - internal->meterSamples++; - internal->meterTimeTotal += duration; } // Iterate ports to step plug lights