HostTime: Rename Clock to Step, add full time and BBT display
Signed-off-by: falkTX <falktx@falktx.com>
This commit is contained in:
parent
1311b6a127
commit
c7006e3726
3 changed files with 154 additions and 107 deletions
|
@ -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<CardinalPluginContext*>(APP))
|
||||
{
|
||||
config(NUM_PARAMS, NUM_INPUTS, kHostTimeCount, kHostTimeCount);
|
||||
|
||||
const CardinalPluginContext* const pcontext = static_cast<CardinalPluginContext*>(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<CardinalPluginContext*>(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<Font> 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<ScrewBlack>(Vec(RACK_GRID_WIDTH, 0)));
|
||||
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
|
||||
addChild(createWidget<ScrewBlack>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
|
||||
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
|
||||
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 0 * padding), module, HostTime::kHostTimeRolling));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 1 * padding), module, HostTime::kHostTimeReset));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 2 * padding), module, HostTime::kHostTimeBar));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 3 * padding), module, HostTime::kHostTimeBeat));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 4 * padding), module, HostTime::kHostTimeClock));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 5 * padding), module, HostTime::kHostTimeBarPhase));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 6 * padding), module, HostTime::kHostTimeBeatPhase));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 0 * padding), m, HostTime::kHostTimeRolling));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 1 * padding), m, HostTime::kHostTimeReset));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 2 * padding), m, HostTime::kHostTimeBar));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 3 * padding), m, HostTime::kHostTimeBeat));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 4 * padding), m, HostTime::kHostTimeClock));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 5 * padding), m, HostTime::kHostTimeBarPhase));
|
||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 6 * padding), m, HostTime::kHostTimeBeatPhase));
|
||||
|
||||
const float x = startX + 28;
|
||||
addChild(createLightCentered<SmallLight<GreenLight>> (Vec(x, startY + 0 * padding + 12), module, HostTime::kHostTimeRolling));
|
||||
addChild(createLightCentered<SmallLight<WhiteLight>> (Vec(x, startY + 1 * padding + 12), module, HostTime::kHostTimeReset));
|
||||
addChild(createLightCentered<SmallLight<RedLight>> (Vec(x, startY + 2 * padding + 12), module, HostTime::kHostTimeBar));
|
||||
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY + 3 * padding + 12), module, HostTime::kHostTimeBeat));
|
||||
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY + 4 * padding + 12), module, HostTime::kHostTimeClock));
|
||||
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY + 5 * padding + 12), module, HostTime::kHostTimeBarPhase));
|
||||
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY + 6 * padding + 12), module, HostTime::kHostTimeBeatPhase));
|
||||
addChild(createLightCentered<SmallLight<GreenLight>> (Vec(x, startY_cv + 0 * padding + 12), m, HostTime::kHostTimeRolling));
|
||||
addChild(createLightCentered<SmallLight<WhiteLight>> (Vec(x, startY_cv + 1 * padding + 12), m, HostTime::kHostTimeReset));
|
||||
addChild(createLightCentered<SmallLight<RedLight>> (Vec(x, startY_cv + 2 * padding + 12), m, HostTime::kHostTimeBar));
|
||||
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY_cv + 3 * padding + 12), m, HostTime::kHostTimeBeat));
|
||||
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY_cv + 4 * padding + 12), m, HostTime::kHostTimeClock));
|
||||
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY_cv + 5 * padding + 12), m, HostTime::kHostTimeBarPhase));
|
||||
addChild(createLightCentered<SmallLight<YellowLight>>(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<int>(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, HostTimeWidget>("HostTime");
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue