Browse Source

Move module stepping code from Engine to Module. Change meter to a time plot.

tags/v2.0.0
Andrew Belt 3 years ago
parent
commit
7003adffe2
6 changed files with 155 additions and 91 deletions
  1. +1
    -0
      include/common.hpp
  2. +7
    -4
      include/engine/Module.hpp
  3. +1
    -1
      src/app/MenuBar.cpp
  4. +39
    -13
      src/app/ModuleWidget.cpp
  5. +6
    -65
      src/engine/Engine.cpp
  6. +101
    -8
      src/engine/Module.cpp

+ 1
- 0
include/common.hpp View File

@@ -1,5 +1,6 @@
#pragma once
// Include most of the C stdlib for convenience
#include <cstddef>
#include <cstdlib>
#include <cstdio>
#include <cstdint>


+ 7
- 4
include/engine/Module.hpp View File

@@ -225,6 +225,8 @@ struct Module {
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.
@@ -342,10 +344,11 @@ struct Module {
/** DEPRECATED. Override `onSampleRateChange(e)` instead. */
virtual void onSampleRateChange() {}

/** Unstable API, do not use in plugins. */
float& cpuTime();
/** Unstable API, do not use in plugins. */
bool& bypass();
PRIVATE bool& bypass();
PRIVATE const float* meterBuffer();
PRIVATE int meterLength();
PRIVATE int meterIndex();
PRIVATE void step(const ProcessArgs& args);
};




+ 1
- 1
src/app/MenuBar.cpp View File

@@ -553,7 +553,7 @@ struct EngineButton : MenuButton {
menu->box.size.x = box.size.x;

CpuMeterItem* cpuMeterItem = new CpuMeterItem;
cpuMeterItem->text = "CPU meter";
cpuMeterItem->text = "Performance meters";
cpuMeterItem->rightText = "F3 ";
cpuMeterItem->rightText += CHECKMARK(settings::cpuMeter);
menu->addChild(cpuMeterItem);


+ 39
- 13
src/app/ModuleWidget.cpp View File

@@ -394,27 +394,53 @@ void ModuleWidget::draw(const DrawArgs& args) {

Widget::draw(args);

// Power meter
// Meter
if (module && settings::cpuMeter) {
float sampleRate = APP->engine->getSampleRate();
const float* meterBuffer = module->meterBuffer();
int meterLength = module->meterLength();
int meterIndex = module->meterIndex();

float meterAvg = 0.f;
for (int i = 0; i < meterLength; i++) {
meterAvg += meterBuffer[i];
}
meterAvg /= meterLength;

// Text background
nvgBeginPath(args.vg);
nvgRect(args.vg,
0, box.size.y - 35,
65, 35);
nvgRect(args.vg, 0, box.size.y - 20, box.size.x, 20);
nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75));
nvgFill(args.vg);

float percent = module->cpuTime() * APP->engine->getSampleRate() * 100;
float microseconds = module->cpuTime() * 1e6f;
std::string cpuText = string::f("%.1f%%\n%.2f μs", percent, microseconds);
bndLabel(args.vg, 2.0, box.size.y - 34.0, INFINITY, INFINITY, -1, cpuText.c_str());
// Text
float percent = meterAvg * sampleRate * 100.f;
float microseconds = meterAvg * 1e6f;
std::string meterText = string::f("%.1f%% %.2f μs", percent, microseconds);
bndLabel(args.vg, 0.0, box.size.y - 20.0, INFINITY, INFINITY, -1, meterText.c_str());

float p = math::clamp(module->cpuTime() / APP->engine->getSampleTime(), 0.f, 1.f);
// Draw time plot
nvgBeginPath(args.vg);
nvgRect(args.vg,
0, (1.f - p) * box.size.y,
5, p * box.size.y);
nvgFillColor(args.vg, nvgRGBAf(1, 0, 0, 1.0));
nvgMoveTo(args.vg, box.size.x, box.size.y);
for (int i = 0; i < meterLength; i++) {
int index = (meterIndex - i + meterLength) % meterLength;
math::Vec p;
p.x = (1.f - (float) i / (meterLength - 1)) * box.size.x;
p.y = (1.f - meterBuffer[index] * sampleRate * 1.f) * (box.size.y - 20);
nvgLineTo(args.vg, p.x, p.y);
}
NVGcolor color = nvgRGBAf(0.6, 0, 0, 0.6);
nvgLineTo(args.vg, 0.0, box.size.y);
nvgClosePath(args.vg);
nvgFillColor(args.vg, color);
nvgFill(args.vg);

// 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);
nvgStroke(args.vg);
}

// if (module) {


+ 6
- 65
src/engine/Engine.cpp View File

@@ -289,43 +289,17 @@ static void Engine_relaunchWorkers(Engine* that, int threadCount) {
}


static void Port_step(Port* that, float deltaTime) {
// Set plug lights
if (that->channels == 0) {
that->plugLights[0].setBrightness(0.f);
that->plugLights[1].setBrightness(0.f);
that->plugLights[2].setBrightness(0.f);
}
else if (that->channels == 1) {
float v = that->getVoltage() / 10.f;
that->plugLights[0].setSmoothBrightness(v, deltaTime);
that->plugLights[1].setSmoothBrightness(-v, deltaTime);
that->plugLights[2].setBrightness(0.f);
}
else {
float v2 = 0.f;
for (int c = 0; c < that->channels; c++) {
v2 += std::pow(that->getVoltage(c), 2);
}
float v = std::sqrt(v2) / 10.f;
that->plugLights[0].setBrightness(0.f);
that->plugLights[1].setBrightness(0.f);
that->plugLights[2].setSmoothBrightness(v, deltaTime);
}
}


static void Engine_stepModulesWorker(Engine* that, int threadId) {
static void Engine_stepWorker(Engine* that, int threadId) {
Engine::Internal* internal = that->internal;

// int threadCount = internal->threadCount;
int modulesLen = internal->modules.size();

// Build ProcessArgs
Module::ProcessArgs processArgs;
processArgs.sampleRate = internal->sampleRate;
processArgs.sampleTime = internal->sampleTime;

bool cpuMeter = settings::cpuMeter;
processArgs.frame = internal->frame;

// Step each module
while (true) {
@@ -336,40 +310,7 @@ static void Engine_stepModulesWorker(Engine* that, int threadId) {
break;

Module* module = internal->modules[i];

// Start CPU timer
double startTime;
if (cpuMeter) {
startTime = system::getRuntime();
}

// Step module
if (!module->bypass())
module->process(processArgs);
else
module->processBypass(processArgs);

// Stop CPU timer
if (cpuMeter) {
double endTime = system::getRuntime();
float duration = endTime - startTime;

// Smooth CPU time
const float cpuTau = 2.f /* seconds */;
module->cpuTime() += (duration - module->cpuTime()) * processArgs.sampleTime / cpuTau;
}

// Iterate ports to step plug lights
const int portDivider = 8;
if (internal->frame % portDivider == 0) {
float portTime = processArgs.sampleTime * portDivider;
for (Input& input : module->inputs) {
Port_step(&input, portTime);
}
for (Output& output : module->outputs) {
Port_step(&output, portTime);
}
}
module->step(processArgs);
}
}

@@ -441,7 +382,7 @@ static void Engine_stepFrame(Engine* that) {
// Step modules along with workers
internal->workerModuleIndex = 0;
internal->engineBarrier.wait();
Engine_stepModulesWorker(that, 0);
Engine_stepWorker(that, 0);
internal->workerBarrier.wait();

internal->frame++;
@@ -1156,7 +1097,7 @@ void EngineWorker::run() {
engine->internal->engineBarrier.wait();
if (!running)
return;
Engine_stepModulesWorker(engine, id);
Engine_stepWorker(engine, id);
engine->internal->workerBarrier.wait();
}
}


+ 101
- 8
src/engine/Module.cpp View File

@@ -1,5 +1,7 @@
#include <engine/Module.hpp>
#include <plugin.hpp>
#include <system.hpp>
#include <settings.hpp>
#include <asset.hpp>


@@ -7,12 +9,21 @@ namespace rack {
namespace engine {



// Arbitrary prime number so it doesn't over- or under-estimate time of buffered processors.
static const int meterDivider = 23;
static const int samplesCount = 64;
static const int meterBufferLength = 128;



struct Module::Internal {
/** Seconds spent in the process() method, with exponential smoothing.
Only written when CPU timing is enabled, since time measurement is expensive.
*/
float cpuTime = 0.f;
bool bypass = false;

float meterTimeTotal = 0.f;
int meterSamples = 0;
int meterIndex = 0;
float meterBuffer[meterBufferLength] = {};
};


@@ -269,13 +280,95 @@ void Module::onRandomize(const RandomizeEvent& e) {
}


float& Module::cpuTime() {
return internal->cpuTime;
bool& Module::bypass() {
return internal->bypass;
}


bool& Module::bypass() {
return internal->bypass;
const float* Module::meterBuffer() {
return internal->meterBuffer;
}


int Module::meterLength() {
return meterBufferLength;
}


int Module::meterIndex() {
return internal->meterIndex;
}


static void Port_step(Port* that, float deltaTime) {
// Set plug lights
if (that->channels == 0) {
that->plugLights[0].setBrightness(0.f);
that->plugLights[1].setBrightness(0.f);
that->plugLights[2].setBrightness(0.f);
}
else if (that->channels == 1) {
float v = that->getVoltage() / 10.f;
that->plugLights[0].setSmoothBrightness(v, deltaTime);
that->plugLights[1].setSmoothBrightness(-v, deltaTime);
that->plugLights[2].setBrightness(0.f);
}
else {
float v2 = 0.f;
for (int c = 0; c < that->channels; c++) {
v2 += std::pow(that->getVoltage(c), 2);
}
float v = std::sqrt(v2) / 10.f;
that->plugLights[0].setBrightness(0.f);
that->plugLights[1].setBrightness(0.f);
that->plugLights[2].setSmoothBrightness(v, deltaTime);
}
}


void Module::step(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);

// Start CPU timer
double startTime;
if (meterEnabled) {
startTime = system::getTime();
}

// Step module
if (!internal->bypass)
process(args);
else
processBypass(args);

// Stop CPU timer
if (meterEnabled) {
double endTime = system::getTime();
float duration = endTime - startTime;

internal->meterTimeTotal += duration;
if (++internal->meterSamples >= samplesCount) {
// Push time to buffer
internal->meterBuffer[internal->meterIndex++] = internal->meterTimeTotal / samplesCount;
internal->meterIndex %= meterBufferLength;
// Reset total
internal->meterSamples = 0;
internal->meterTimeTotal = 0.f;
}
}

// Iterate ports to step plug lights
const int portDivider = 8;
if (args.frame % portDivider == 0) {
float portTime = args.sampleTime * portDivider;
for (Input& input : inputs) {
Port_step(&input, portTime);
}
for (Output& output : outputs) {
Port_step(&output, portTime);
}
}
}




Loading…
Cancel
Save