From c7006e372651daf05a4561b2c3146939af0ad853 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 1 Feb 2022 20:53:11 +0000 Subject: [PATCH] HostTime: Rename Clock to Step, add full time and BBT display Signed-off-by: falkTX --- plugins/Cardinal/src/HostTime.cpp | 253 ++++++++++++++++----------- plugins/Cardinal/src/ImGuiWidget.cpp | 2 +- src/template.vcv | 6 +- 3 files changed, 154 insertions(+), 107 deletions(-) diff --git a/plugins/Cardinal/src/HostTime.cpp b/plugins/Cardinal/src/HostTime.cpp index bafcc7a..069913d 100644 --- a/plugins/Cardinal/src/HostTime.cpp +++ b/plugins/Cardinal/src/HostTime.cpp @@ -35,6 +35,8 @@ struct HostTime : Module { kHostTimeCount }; + const CardinalPluginContext* const pcontext; + rack::dsp::PulseGenerator pulseReset, pulseBar, pulseBeat, pulseClock; float sampleTime = 0.0f; int64_t lastBlockFrame = -1; @@ -45,141 +47,145 @@ struct HostTime : Module { int32_t beat = 0; double tick = 0.0; double tickClock = 0.0; + uint32_t seconds = 0; } timeInfo; HostTime() + : pcontext(static_cast(APP)) { - config(NUM_PARAMS, NUM_INPUTS, kHostTimeCount, kHostTimeCount); - - const CardinalPluginContext* const pcontext = static_cast(APP); - if (pcontext == nullptr) throw rack::Exception("Plugin context is null."); + + config(NUM_PARAMS, NUM_INPUTS, kHostTimeCount, kHostTimeCount); } void process(const ProcessArgs& args) override { - if (const CardinalPluginContext* const pcontext = static_cast(APP)) + const int64_t blockFrame = pcontext->engine->getBlockFrame(); + + // Update time position if running a new audio block + if (lastBlockFrame != blockFrame) { - const int64_t blockFrame = pcontext->engine->getBlockFrame(); - - // Update time position if running a new audio block - if (lastBlockFrame != blockFrame) - { - lastBlockFrame = blockFrame; - timeInfo.reset = pcontext->reset; - timeInfo.bar = pcontext->bar; - timeInfo.beat = pcontext->beat; - timeInfo.tick = pcontext->tick; - timeInfo.tickClock = pcontext->tickClock; - } - - const bool playing = pcontext->playing; - const bool playingWithBBT = playing && pcontext->bbtValid; - - if (playingWithBBT) - { - if (timeInfo.tick == 0.0) - { - pulseReset.trigger(); - pulseClock.trigger(); - pulseBeat.trigger(); - if (timeInfo.beat == 1) - pulseBar.trigger(); - } - - if (timeInfo.reset) - { - timeInfo.reset = false; - pulseReset.trigger(); - } - - if ((timeInfo.tick += pcontext->ticksPerFrame) >= pcontext->ticksPerBeat) - { - timeInfo.tick -= pcontext->ticksPerBeat; - pulseBeat.trigger(); - - if (++timeInfo.beat > pcontext->beatsPerBar) - { - timeInfo.beat = 1; - ++timeInfo.bar; - pulseBar.trigger(); - } - } - - if ((timeInfo.tickClock += pcontext->ticksPerFrame) >= pcontext->ticksPerClock) - { - timeInfo.tickClock -= pcontext->ticksPerClock; - pulseClock.trigger(); - } - } - - const bool hasReset = pulseReset.process(args.sampleTime); - const bool hasBar = pulseBar.process(args.sampleTime); - const bool hasBeat = pulseBeat.process(args.sampleTime); - const bool hasClock = pulseClock.process(args.sampleTime); - const float beatPhase = playingWithBBT && pcontext->ticksPerBeat > 0.0 - ? timeInfo.tick / pcontext->ticksPerBeat - : 0.0f; - const float barPhase = playingWithBBT && pcontext->beatsPerBar > 0 - ? ((float) (timeInfo.beat - 1) + beatPhase) / pcontext->beatsPerBar - : 0.0f; - - lights[kHostTimeRolling].setBrightness(playing ? 1.0f : 0.0f); - lights[kHostTimeReset].setBrightnessSmooth(hasReset ? 1.0f : 0.0f, args.sampleTime * 0.5f); - lights[kHostTimeBar].setBrightnessSmooth(hasBar ? 1.0f : 0.0f, args.sampleTime * 0.5f); - lights[kHostTimeBeat].setBrightnessSmooth(hasBeat ? 1.0f : 0.0f, args.sampleTime); - lights[kHostTimeClock].setBrightnessSmooth(hasClock ? 1.0f : 0.0f, args.sampleTime * 2.0f); - lights[kHostTimeBarPhase].setBrightness(barPhase); - lights[kHostTimeBeatPhase].setBrightness(beatPhase); - - outputs[kHostTimeRolling].setVoltage(playing ? 10.0f : 0.0f); - outputs[kHostTimeReset].setVoltage(hasReset ? 10.0f : 0.0f); - outputs[kHostTimeBar].setVoltage(hasBar ? 10.0f : 0.0f); - outputs[kHostTimeBeat].setVoltage(hasBeat ? 10.0f : 0.0f); - outputs[kHostTimeClock].setVoltage(hasClock ? 10.0f : 0.0f); - outputs[kHostTimeBarPhase].setVoltage(barPhase * 10.0f); - outputs[kHostTimeBeatPhase].setVoltage(beatPhase * 10.0f); + lastBlockFrame = blockFrame; + timeInfo.reset = pcontext->reset; + timeInfo.bar = pcontext->bar; + timeInfo.beat = pcontext->beat; + timeInfo.tick = pcontext->tick; + timeInfo.tickClock = pcontext->tickClock; + timeInfo.seconds = pcontext->frame / pcontext->sampleRate; } + + const bool playing = pcontext->playing; + const bool playingWithBBT = playing && pcontext->bbtValid; + + if (playingWithBBT) + { + if (timeInfo.tick == 0.0) + { + pulseReset.trigger(); + pulseClock.trigger(); + pulseBeat.trigger(); + if (timeInfo.beat == 1) + pulseBar.trigger(); + } + + if (timeInfo.reset) + { + timeInfo.reset = false; + pulseReset.trigger(); + } + + if ((timeInfo.tick += pcontext->ticksPerFrame) >= pcontext->ticksPerBeat) + { + timeInfo.tick -= pcontext->ticksPerBeat; + pulseBeat.trigger(); + + if (++timeInfo.beat > pcontext->beatsPerBar) + { + timeInfo.beat = 1; + ++timeInfo.bar; + pulseBar.trigger(); + } + } + + if ((timeInfo.tickClock += pcontext->ticksPerFrame) >= pcontext->ticksPerClock) + { + timeInfo.tickClock -= pcontext->ticksPerClock; + pulseClock.trigger(); + } + } + + const bool hasReset = pulseReset.process(args.sampleTime); + const bool hasBar = pulseBar.process(args.sampleTime); + const bool hasBeat = pulseBeat.process(args.sampleTime); + const bool hasClock = pulseClock.process(args.sampleTime); + const float beatPhase = playingWithBBT && pcontext->ticksPerBeat > 0.0 + ? timeInfo.tick / pcontext->ticksPerBeat + : 0.0f; + const float barPhase = playingWithBBT && pcontext->beatsPerBar > 0 + ? ((float) (timeInfo.beat - 1) + beatPhase) / pcontext->beatsPerBar + : 0.0f; + + lights[kHostTimeRolling].setBrightness(playing ? 1.0f : 0.0f); + lights[kHostTimeReset].setBrightnessSmooth(hasReset ? 1.0f : 0.0f, args.sampleTime * 0.5f); + lights[kHostTimeBar].setBrightnessSmooth(hasBar ? 1.0f : 0.0f, args.sampleTime * 0.5f); + lights[kHostTimeBeat].setBrightnessSmooth(hasBeat ? 1.0f : 0.0f, args.sampleTime); + lights[kHostTimeClock].setBrightnessSmooth(hasClock ? 1.0f : 0.0f, args.sampleTime * 2.0f); + lights[kHostTimeBarPhase].setBrightness(barPhase); + lights[kHostTimeBeatPhase].setBrightness(beatPhase); + + outputs[kHostTimeRolling].setVoltage(playing ? 10.0f : 0.0f); + outputs[kHostTimeReset].setVoltage(hasReset ? 10.0f : 0.0f); + outputs[kHostTimeBar].setVoltage(hasBar ? 10.0f : 0.0f); + outputs[kHostTimeBeat].setVoltage(hasBeat ? 10.0f : 0.0f); + outputs[kHostTimeClock].setVoltage(hasClock ? 10.0f : 0.0f); + outputs[kHostTimeBarPhase].setVoltage(barPhase * 10.0f); + outputs[kHostTimeBeatPhase].setVoltage(beatPhase * 10.0f); } }; struct HostTimeWidget : ModuleWidget { static constexpr const float startX = 10.0f; - static constexpr const float startY = 73.0f; + static constexpr const float startY_top = 71.0f; + static constexpr const float startY_cv = 115.0f; static constexpr const float padding = 32.0f; - HostTimeWidget(HostTime* const module) + HostTime* const module; + std::shared_ptr monoFont; + + HostTimeWidget(HostTime* const m) + : module(m) { - setModule(module); + setModule(m); setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HostTime.svg"))); + monoFont = APP->window->loadFont(asset::system("res/fonts/ShareTechMono-Regular.ttf")); addChild(createWidget(Vec(RACK_GRID_WIDTH, 0))); addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); - addOutput(createOutput(Vec(startX, startY + 0 * padding), module, HostTime::kHostTimeRolling)); - addOutput(createOutput(Vec(startX, startY + 1 * padding), module, HostTime::kHostTimeReset)); - addOutput(createOutput(Vec(startX, startY + 2 * padding), module, HostTime::kHostTimeBar)); - addOutput(createOutput(Vec(startX, startY + 3 * padding), module, HostTime::kHostTimeBeat)); - addOutput(createOutput(Vec(startX, startY + 4 * padding), module, HostTime::kHostTimeClock)); - addOutput(createOutput(Vec(startX, startY + 5 * padding), module, HostTime::kHostTimeBarPhase)); - addOutput(createOutput(Vec(startX, startY + 6 * padding), module, HostTime::kHostTimeBeatPhase)); + addOutput(createOutput(Vec(startX, startY_cv + 0 * padding), m, HostTime::kHostTimeRolling)); + addOutput(createOutput(Vec(startX, startY_cv + 1 * padding), m, HostTime::kHostTimeReset)); + addOutput(createOutput(Vec(startX, startY_cv + 2 * padding), m, HostTime::kHostTimeBar)); + addOutput(createOutput(Vec(startX, startY_cv + 3 * padding), m, HostTime::kHostTimeBeat)); + addOutput(createOutput(Vec(startX, startY_cv + 4 * padding), m, HostTime::kHostTimeClock)); + addOutput(createOutput(Vec(startX, startY_cv + 5 * padding), m, HostTime::kHostTimeBarPhase)); + addOutput(createOutput(Vec(startX, startY_cv + 6 * padding), m, HostTime::kHostTimeBeatPhase)); const float x = startX + 28; - addChild(createLightCentered> (Vec(x, startY + 0 * padding + 12), module, HostTime::kHostTimeRolling)); - addChild(createLightCentered> (Vec(x, startY + 1 * padding + 12), module, HostTime::kHostTimeReset)); - addChild(createLightCentered> (Vec(x, startY + 2 * padding + 12), module, HostTime::kHostTimeBar)); - addChild(createLightCentered>(Vec(x, startY + 3 * padding + 12), module, HostTime::kHostTimeBeat)); - addChild(createLightCentered>(Vec(x, startY + 4 * padding + 12), module, HostTime::kHostTimeClock)); - addChild(createLightCentered>(Vec(x, startY + 5 * padding + 12), module, HostTime::kHostTimeBarPhase)); - addChild(createLightCentered>(Vec(x, startY + 6 * padding + 12), module, HostTime::kHostTimeBeatPhase)); + addChild(createLightCentered> (Vec(x, startY_cv + 0 * padding + 12), m, HostTime::kHostTimeRolling)); + addChild(createLightCentered> (Vec(x, startY_cv + 1 * padding + 12), m, HostTime::kHostTimeReset)); + addChild(createLightCentered> (Vec(x, startY_cv + 2 * padding + 12), m, HostTime::kHostTimeBar)); + addChild(createLightCentered>(Vec(x, startY_cv + 3 * padding + 12), m, HostTime::kHostTimeBeat)); + addChild(createLightCentered>(Vec(x, startY_cv + 4 * padding + 12), m, HostTime::kHostTimeClock)); + addChild(createLightCentered>(Vec(x, startY_cv + 5 * padding + 12), m, HostTime::kHostTimeBarPhase)); + addChild(createLightCentered>(Vec(x, startY_cv + 6 * padding + 12), m, HostTime::kHostTimeBeatPhase)); } void drawOutputLine(NVGcontext* const vg, const uint offset, const char* const text) { - const float y = startY + offset * padding; + const float y = startY_cv + offset * padding; nvgBeginPath(vg); nvgRoundedRect(vg, startX - 1.0f, y - 2.0f, box.size.x - (startX + 1) * 2, 28.0f, 4); nvgFillColor(vg, nvgRGB(0xd0, 0xd0, 0xd0)); @@ -204,14 +210,55 @@ struct HostTimeWidget : ModuleWidget { drawOutputLine(args.vg, 1, "Reset"); drawOutputLine(args.vg, 2, "Bar"); drawOutputLine(args.vg, 3, "Beat"); - drawOutputLine(args.vg, 4, "Clock"); + drawOutputLine(args.vg, 4, "Step"); nvgFontSize(args.vg, 11); drawOutputLine(args.vg, 5, "Bar Phase"); drawOutputLine(args.vg, 6, "Beat Phase"); + nvgBeginPath(args.vg); + nvgRoundedRect(args.vg, startX - 1.0f, startY_top, 98.0f, 38.0f, 4); // 98 + nvgFillColor(args.vg, color::BLACK); + nvgFill(args.vg); + ModuleWidget::draw(args); } + + void drawLayer(const DrawArgs& args, int layer) override + { + if (layer == 1) + { + nvgFontFaceId(args.vg, monoFont->handle); + nvgFontSize(args.vg, 17); + nvgFillColor(args.vg, nvgRGBf(0.76f, 0.11f, 0.22f)); + + char timeString1[24]; + char timeString2[24]; + + if (module != nullptr && monoFont != nullptr) + { + const uint32_t seconds = module->timeInfo.seconds; + std::snprintf(timeString1, sizeof(timeString1), " %02d:%02d:%02d", + (seconds / 3600) % 100, + (seconds / 60) % 60, + seconds % 60); + std::snprintf(timeString2, sizeof(timeString2), "%03d:%02d:%04d", + module->timeInfo.bar % 1000, + module->timeInfo.beat % 100, + static_cast(module->timeInfo.tick + 0.5)); + } + else + { + std::strcpy(timeString1, " 00:00:00"); + std::strcpy(timeString2, "001:01:0000"); + } + + nvgText(args.vg, startX + 3.5f, startY_top + 15.0f, timeString1, nullptr); + nvgText(args.vg, startX + 3.5f, startY_top + 33.0f, timeString2, nullptr); + } + + ModuleWidget::drawLayer(args, layer); + } }; Model* modelHostTime = createModel("HostTime"); diff --git a/plugins/Cardinal/src/ImGuiWidget.cpp b/plugins/Cardinal/src/ImGuiWidget.cpp index d3317dd..f2e1467 100644 --- a/plugins/Cardinal/src/ImGuiWidget.cpp +++ b/plugins/Cardinal/src/ImGuiWidget.cpp @@ -91,7 +91,7 @@ struct ImGuiWidget::PrivateData { if (useMonospacedFont) { - const std::string fontPath = asset::system("fonts/ShareTechMono-Regular.ttf"); + const std::string fontPath = asset::system("res/fonts/ShareTechMono-Regular.ttf"); ImFontConfig fc; fc.OversampleH = 1; fc.OversampleV = 1; diff --git a/src/template.vcv b/src/template.vcv index 589cded..20cc112 100644 --- a/src/template.vcv +++ b/src/template.vcv @@ -38,7 +38,7 @@ { "id": 3, "plugin": "Cardinal", - "model": "HostParameters", + "model": "HostTime", "version": "2.0", "params": [], "leftModuleId": 2, @@ -51,12 +51,12 @@ { "id": 4, "plugin": "Cardinal", - "model": "HostTime", + "model": "HostParameters", "version": "2.0", "params": [], "leftModuleId": 3, "pos": [ - 27, + 26, 0 ] }