@@ -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. | |||
@@ -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)) { | |||
@@ -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 | |||