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
|
kHostTimeCount
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CardinalPluginContext* const pcontext;
|
||||||
|
|
||||||
rack::dsp::PulseGenerator pulseReset, pulseBar, pulseBeat, pulseClock;
|
rack::dsp::PulseGenerator pulseReset, pulseBar, pulseBeat, pulseClock;
|
||||||
float sampleTime = 0.0f;
|
float sampleTime = 0.0f;
|
||||||
int64_t lastBlockFrame = -1;
|
int64_t lastBlockFrame = -1;
|
||||||
|
|
@ -45,141 +47,145 @@ struct HostTime : Module {
|
||||||
int32_t beat = 0;
|
int32_t beat = 0;
|
||||||
double tick = 0.0;
|
double tick = 0.0;
|
||||||
double tickClock = 0.0;
|
double tickClock = 0.0;
|
||||||
|
uint32_t seconds = 0;
|
||||||
} timeInfo;
|
} timeInfo;
|
||||||
|
|
||||||
HostTime()
|
HostTime()
|
||||||
|
: pcontext(static_cast<CardinalPluginContext*>(APP))
|
||||||
{
|
{
|
||||||
config(NUM_PARAMS, NUM_INPUTS, kHostTimeCount, kHostTimeCount);
|
|
||||||
|
|
||||||
const CardinalPluginContext* const pcontext = static_cast<CardinalPluginContext*>(APP);
|
|
||||||
|
|
||||||
if (pcontext == nullptr)
|
if (pcontext == nullptr)
|
||||||
throw rack::Exception("Plugin context is null.");
|
throw rack::Exception("Plugin context is null.");
|
||||||
|
|
||||||
|
config(NUM_PARAMS, NUM_INPUTS, kHostTimeCount, kHostTimeCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void process(const ProcessArgs& args) override
|
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();
|
lastBlockFrame = blockFrame;
|
||||||
|
timeInfo.reset = pcontext->reset;
|
||||||
// Update time position if running a new audio block
|
timeInfo.bar = pcontext->bar;
|
||||||
if (lastBlockFrame != blockFrame)
|
timeInfo.beat = pcontext->beat;
|
||||||
{
|
timeInfo.tick = pcontext->tick;
|
||||||
lastBlockFrame = blockFrame;
|
timeInfo.tickClock = pcontext->tickClock;
|
||||||
timeInfo.reset = pcontext->reset;
|
timeInfo.seconds = pcontext->frame / pcontext->sampleRate;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
struct HostTimeWidget : ModuleWidget {
|
||||||
static constexpr const float startX = 10.0f;
|
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;
|
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")));
|
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(RACK_GRID_WIDTH, 0)));
|
||||||
addChild(createWidget<ScrewBlack>(Vec(box.size.x - 2 * 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(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)));
|
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_cv + 0 * padding), m, HostTime::kHostTimeRolling));
|
||||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 1 * padding), module, HostTime::kHostTimeReset));
|
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 1 * padding), m, HostTime::kHostTimeReset));
|
||||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 2 * padding), module, HostTime::kHostTimeBar));
|
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 2 * padding), m, HostTime::kHostTimeBar));
|
||||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 3 * padding), module, HostTime::kHostTimeBeat));
|
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 3 * padding), m, HostTime::kHostTimeBeat));
|
||||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 4 * padding), module, HostTime::kHostTimeClock));
|
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 4 * padding), m, HostTime::kHostTimeClock));
|
||||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 5 * padding), module, HostTime::kHostTimeBarPhase));
|
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 5 * padding), m, HostTime::kHostTimeBarPhase));
|
||||||
addOutput(createOutput<PJ301MPort>(Vec(startX, startY + 6 * padding), module, HostTime::kHostTimeBeatPhase));
|
addOutput(createOutput<PJ301MPort>(Vec(startX, startY_cv + 6 * padding), m, HostTime::kHostTimeBeatPhase));
|
||||||
|
|
||||||
const float x = startX + 28;
|
const float x = startX + 28;
|
||||||
addChild(createLightCentered<SmallLight<GreenLight>> (Vec(x, startY + 0 * padding + 12), module, HostTime::kHostTimeRolling));
|
addChild(createLightCentered<SmallLight<GreenLight>> (Vec(x, startY_cv + 0 * padding + 12), m, HostTime::kHostTimeRolling));
|
||||||
addChild(createLightCentered<SmallLight<WhiteLight>> (Vec(x, startY + 1 * padding + 12), module, HostTime::kHostTimeReset));
|
addChild(createLightCentered<SmallLight<WhiteLight>> (Vec(x, startY_cv + 1 * padding + 12), m, HostTime::kHostTimeReset));
|
||||||
addChild(createLightCentered<SmallLight<RedLight>> (Vec(x, startY + 2 * padding + 12), module, HostTime::kHostTimeBar));
|
addChild(createLightCentered<SmallLight<RedLight>> (Vec(x, startY_cv + 2 * padding + 12), m, HostTime::kHostTimeBar));
|
||||||
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY + 3 * padding + 12), module, HostTime::kHostTimeBeat));
|
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY_cv + 3 * padding + 12), m, HostTime::kHostTimeBeat));
|
||||||
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY + 4 * padding + 12), module, HostTime::kHostTimeClock));
|
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY_cv + 4 * padding + 12), m, HostTime::kHostTimeClock));
|
||||||
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY + 5 * padding + 12), module, HostTime::kHostTimeBarPhase));
|
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY_cv + 5 * padding + 12), m, HostTime::kHostTimeBarPhase));
|
||||||
addChild(createLightCentered<SmallLight<YellowLight>>(Vec(x, startY + 6 * padding + 12), module, HostTime::kHostTimeBeatPhase));
|
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)
|
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);
|
nvgBeginPath(vg);
|
||||||
nvgRoundedRect(vg, startX - 1.0f, y - 2.0f, box.size.x - (startX + 1) * 2, 28.0f, 4);
|
nvgRoundedRect(vg, startX - 1.0f, y - 2.0f, box.size.x - (startX + 1) * 2, 28.0f, 4);
|
||||||
nvgFillColor(vg, nvgRGB(0xd0, 0xd0, 0xd0));
|
nvgFillColor(vg, nvgRGB(0xd0, 0xd0, 0xd0));
|
||||||
|
|
@ -204,14 +210,55 @@ struct HostTimeWidget : ModuleWidget {
|
||||||
drawOutputLine(args.vg, 1, "Reset");
|
drawOutputLine(args.vg, 1, "Reset");
|
||||||
drawOutputLine(args.vg, 2, "Bar");
|
drawOutputLine(args.vg, 2, "Bar");
|
||||||
drawOutputLine(args.vg, 3, "Beat");
|
drawOutputLine(args.vg, 3, "Beat");
|
||||||
drawOutputLine(args.vg, 4, "Clock");
|
drawOutputLine(args.vg, 4, "Step");
|
||||||
|
|
||||||
nvgFontSize(args.vg, 11);
|
nvgFontSize(args.vg, 11);
|
||||||
drawOutputLine(args.vg, 5, "Bar Phase");
|
drawOutputLine(args.vg, 5, "Bar Phase");
|
||||||
drawOutputLine(args.vg, 6, "Beat 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);
|
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");
|
Model* modelHostTime = createModel<HostTime, HostTimeWidget>("HostTime");
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ struct ImGuiWidget::PrivateData {
|
||||||
|
|
||||||
if (useMonospacedFont)
|
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;
|
ImFontConfig fc;
|
||||||
fc.OversampleH = 1;
|
fc.OversampleH = 1;
|
||||||
fc.OversampleV = 1;
|
fc.OversampleV = 1;
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"plugin": "Cardinal",
|
"plugin": "Cardinal",
|
||||||
"model": "HostParameters",
|
"model": "HostTime",
|
||||||
"version": "2.0",
|
"version": "2.0",
|
||||||
"params": [],
|
"params": [],
|
||||||
"leftModuleId": 2,
|
"leftModuleId": 2,
|
||||||
|
|
@ -51,12 +51,12 @@
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
"plugin": "Cardinal",
|
"plugin": "Cardinal",
|
||||||
"model": "HostTime",
|
"model": "HostParameters",
|
||||||
"version": "2.0",
|
"version": "2.0",
|
||||||
"params": [],
|
"params": [],
|
||||||
"leftModuleId": 3,
|
"leftModuleId": 3,
|
||||||
"pos": [
|
"pos": [
|
||||||
27,
|
26,
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue