@@ -231,17 +231,6 @@ struct Module { | |||||
std::string createPatchStorageDirectory(); | std::string createPatchStorageDirectory(); | ||||
std::string getPatchStorageDirectory(); | 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 */ | /** Getters for members */ | ||||
plugin::Model* getModel() { | plugin::Model* getModel() { | ||||
return model; | return model; | ||||
@@ -285,14 +274,26 @@ struct Module { | |||||
// Virtual methods | // 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. | /** Advances the module by one audio sample. | ||||
Override this method to read Inputs and Params and to write Outputs and Lights. | Override this method to read Inputs and Params and to write Outputs and Lights. | ||||
*/ | */ | ||||
virtual void process(const ProcessArgs& args) { | virtual void process(const ProcessArgs& args) { | ||||
step(); | step(); | ||||
} | } | ||||
/** DEPRECATED. Override `process(const ProcessArgs& args)` instead. */ | /** DEPRECATED. Override `process(const ProcessArgs& args)` instead. */ | ||||
virtual void step() {} | virtual void step() {} | ||||
/** Called instead of process() when Module is bypassed. | /** Called instead of process() when Module is bypassed. | ||||
Typically you do not need to override this. Use configBypass() instead. | 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. | If you do override it, avoid reading param values, since the state of the module should have no effect on routing. | ||||
@@ -221,17 +221,6 @@ void ModuleWidget::draw(const DrawArgs& args) { | |||||
int meterLength = module->meterLength(); | int meterLength = module->meterLength(); | ||||
int meterIndex = module->meterIndex(); | 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 | // // Text background | ||||
// nvgBeginPath(args.vg); | // nvgBeginPath(args.vg); | ||||
// nvgRect(args.vg, 0.0, box.size.y - infoHeight, box.size.x, infoHeight); | // 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 | // Draw time plot | ||||
nvgBeginPath(args.vg); | 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++) { | 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; | 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); | 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); | 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); | nvgStrokeWidth(args.vg, 2.0); | ||||
nvgStrokeColor(args.vg, color); | |||||
nvgStroke(args.vg); | 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 | // Selection | ||||
if (APP->scene->rack->isSelected(this)) { | if (APP->scene->rack->isSelected(this)) { | ||||
@@ -14,18 +14,18 @@ namespace engine { | |||||
// Arbitrary prime number so it doesn't over- or under-estimate time of buffered processors. | // 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 { | struct Module::Internal { | ||||
bool bypassed = false; | bool bypassed = false; | ||||
int64_t meterLastBlock = 0; | |||||
int meterSamples = 0; | int meterSamples = 0; | ||||
float meterTimeTotal = 0.f; | |||||
float meterDurationTotal = 0.f; | |||||
float meterBuffer[meterBufferLength] = {}; | |||||
float meterBuffer[METER_BUFFER_LEN] = {}; | |||||
int meterIndex = 0; | int meterIndex = 0; | ||||
}; | }; | ||||
@@ -299,7 +299,7 @@ const float* Module::meterBuffer() { | |||||
int Module::meterLength() { | 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) { | void Module::doProcess(const ProcessArgs& args) { | ||||
// This global setting can change while the function is running, so use a local variable. | // 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 | // Start CPU timer | ||||
double startTime; | double startTime; | ||||
@@ -351,21 +351,23 @@ void Module::doProcess(const ProcessArgs& args) { | |||||
double endTime = system::getTime(); | double endTime = system::getTime(); | ||||
float duration = endTime - startTime; | 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 | // Push time to buffer | ||||
if (internal->meterSamples > 0) { | 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 | // Reset total | ||||
internal->meterSamples = 0; | 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 | // Iterate ports to step plug lights | ||||