| @@ -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 | |||