Browse Source

Implement Scope design. Rewrite most of its DSP.

tags/v2.0.1
Andrew Belt 3 years ago
parent
commit
5f607ce15a
1 changed files with 247 additions and 139 deletions
  1. +247
    -139
      src/Scope.cpp

+ 247
- 139
src/Scope.cpp View File

@@ -2,7 +2,7 @@
#include "plugin.hpp" #include "plugin.hpp"




static const int BUFFER_SIZE = 512;
static const int BUFFER_SIZE = 256;




struct Scope : Module { struct Scope : Module {
@@ -30,200 +30,277 @@ struct Scope : Module {
NUM_OUTPUTS NUM_OUTPUTS
}; };
enum LightIds { enum LightIds {
PLOT_LIGHT,
LISSAJOUS_LIGHT, LISSAJOUS_LIGHT,
INTERNAL_LIGHT,
EXTERNAL_LIGHT, EXTERNAL_LIGHT,
NUM_LIGHTS NUM_LIGHTS
}; };


float bufferX[16][BUFFER_SIZE] = {};
float bufferY[16][BUFFER_SIZE] = {};
struct Point {
float minX[16] = {};
float maxX[16] = {};
float minY[16] = {};
float maxY[16] = {};

Point() {
for (int c = 0; c < 16; c++) {
minX[c] = INFINITY;
maxX[c] = -INFINITY;
minY[c] = INFINITY;
maxY[c] = -INFINITY;
}
}
};
Point pointBuffer[BUFFER_SIZE];
int channelsX = 0; int channelsX = 0;
int channelsY = 0; int channelsY = 0;
int bufferIndex = 0; int bufferIndex = 0;
int frameIndex = 0; int frameIndex = 0;
Point currentPoint;


dsp::BooleanTrigger sumTrigger;
dsp::BooleanTrigger extTrigger;
bool lissajous = false;
bool external = false;
dsp::SchmittTrigger triggers[16]; dsp::SchmittTrigger triggers[16];


Scope() { Scope() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
configParam(X_SCALE_PARAM, -2.f, 8.f, 0.f, "X scale", " V/div", 1 / 2.f, 5);
configParam(X_POS_PARAM, -10.f, 10.f, 0.f, "X position", " V");
configParam(Y_SCALE_PARAM, -2.f, 8.f, 0.f, "Y scale", " V/div", 1 / 2.f, 5);
configParam(Y_POS_PARAM, -10.f, 10.f, 0.f, "Y position", " V");
const float timeBase = (float) BUFFER_SIZE / 6;
configParam(TIME_PARAM, 6.f, 16.f, 14.f, "Time", " ms/div", 1 / 2.f, 1000 * timeBase);
configButton(LISSAJOUS_PARAM, "Separate/Lissajous mode");
configParam(TRIG_PARAM, -10.f, 10.f, 0.f, "Trigger position", " V");
configButton(EXTERNAL_PARAM, "Internal/external trigger mode");
configInput(X_INPUT, "X");
configInput(Y_INPUT, "Y");
configParam(X_SCALE_PARAM, 0.f, 8.f, 0.f, "Gain 1", " V/screen", 1 / 2.f, 20);
getParamQuantity(X_SCALE_PARAM)->snapEnabled = true;
configParam(X_POS_PARAM, -10.f, 10.f, 0.f, "Offset 1", " V");
configParam(Y_SCALE_PARAM, 0.f, 8.f, 0.f, "Gain 2", " V/screen", 1 / 2.f, 20);
getParamQuantity(Y_SCALE_PARAM)->snapEnabled = true;
configParam(Y_POS_PARAM, -10.f, 10.f, 0.f, "Offset 2", " V");
const float maxTime = -std::log2(5e1f);
const float minTime = -std::log2(5e-3f);
const float defaultTime = -std::log2(5e-1f);
configParam(TIME_PARAM, maxTime, minTime, defaultTime, "Time", " ms/screen", 1 / 2.f, 1000);
configSwitch(LISSAJOUS_PARAM, 0.f, 1.f, 0.f, "Scope mode", {"1 & 2", "1 x 2"});
configParam(TRIG_PARAM, -10.f, 10.f, 0.f, "Trigger threshold", " V");
configSwitch(EXTERNAL_PARAM, 0.f, 1.f, 1.f, "Trigger mode", {"Internal", "External"});

configInput(X_INPUT, "Ch 1");
configInput(Y_INPUT, "Ch 2");
configInput(TRIG_INPUT, "External trigger"); configInput(TRIG_INPUT, "External trigger");

configOutput(X_OUTPUT, "Ch 1");
configOutput(Y_OUTPUT, "Ch 2");
} }


void onReset() override { void onReset() override {
lissajous = false;
external = false;
std::memset(bufferX, 0, sizeof(bufferX));
std::memset(bufferY, 0, sizeof(bufferY));
for (int i = 0; i < BUFFER_SIZE; i++) {
pointBuffer[i] = Point();
}
} }


void process(const ProcessArgs& args) override { void process(const ProcessArgs& args) override {
// Modes
if (sumTrigger.process(params[LISSAJOUS_PARAM].getValue() > 0.f)) {
lissajous = !lissajous;
}
lights[PLOT_LIGHT].setBrightness(!lissajous);
bool lissajous = params[LISSAJOUS_PARAM].getValue() > 0.f;
lights[LISSAJOUS_LIGHT].setBrightness(lissajous); lights[LISSAJOUS_LIGHT].setBrightness(lissajous);


if (extTrigger.process(params[EXTERNAL_PARAM].getValue() > 0.f)) {
external = !external;
}
lights[INTERNAL_LIGHT].setBrightness(!external);
bool external = params[EXTERNAL_PARAM].getValue() > 0.f;
lights[EXTERNAL_LIGHT].setBrightness(external); lights[EXTERNAL_LIGHT].setBrightness(external);


// Compute time // Compute time
float deltaTime = std::pow(2.f, -params[TIME_PARAM].getValue());
float deltaTime = std::pow(2.f, -params[TIME_PARAM].getValue()) / BUFFER_SIZE;
int frameCount = (int) std::ceil(deltaTime * args.sampleRate); int frameCount = (int) std::ceil(deltaTime * args.sampleRate);


// Set channels // Set channels
int channelsX = inputs[X_INPUT].getChannels(); int channelsX = inputs[X_INPUT].getChannels();
if (channelsX != this->channelsX) { if (channelsX != this->channelsX) {
std::memset(bufferX, 0, sizeof(bufferX));
// TODO
// std::memset(bufferX, 0, sizeof(bufferX));
this->channelsX = channelsX; this->channelsX = channelsX;
} }


int channelsY = inputs[Y_INPUT].getChannels(); int channelsY = inputs[Y_INPUT].getChannels();
if (channelsY != this->channelsY) { if (channelsY != this->channelsY) {
std::memset(bufferY, 0, sizeof(bufferY));
// TODO
// std::memset(bufferY, 0, sizeof(bufferY));
this->channelsY = channelsY; this->channelsY = channelsY;
} }


// Add frame to buffer
// Copy inputs to outputs
outputs[X_OUTPUT].setChannels(channelsX);
outputs[X_OUTPUT].writeVoltages(inputs[X_INPUT].getVoltages());
outputs[Y_OUTPUT].setChannels(channelsY);
outputs[Y_OUTPUT].writeVoltages(inputs[Y_INPUT].getVoltages());

// Add point to buffer if recording
if (bufferIndex < BUFFER_SIZE) { if (bufferIndex < BUFFER_SIZE) {
if (++frameIndex > frameCount) {
// Get input
for (int c = 0; c < channelsX; c++) {
float x = inputs[X_INPUT].getVoltage(c);
currentPoint.minX[c] = std::min(currentPoint.minX[c], x);
currentPoint.maxX[c] = std::max(currentPoint.maxX[c], x);
}
for (int c = 0; c < channelsY; c++) {
float y = inputs[Y_INPUT].getVoltage(c);
currentPoint.minY[c] = std::min(currentPoint.minY[c], y);
currentPoint.maxY[c] = std::max(currentPoint.maxY[c], y);
}

if (++frameIndex >= frameCount) {
frameIndex = 0; frameIndex = 0;
for (int c = 0; c < channelsX; c++) {
bufferX[c][bufferIndex] = inputs[X_INPUT].getVoltage(c);
}
for (int c = 0; c < channelsY; c++) {
bufferY[c][bufferIndex] = inputs[Y_INPUT].getVoltage(c);
}
// Push current point
pointBuffer[bufferIndex] = currentPoint;
// Reset current point
currentPoint = Point();
bufferIndex++; bufferIndex++;
} }
} }


// Don't wait for trigger if still filling buffer
if (bufferIndex < BUFFER_SIZE) {
return;
}

// Trigger immediately if external but nothing plugged in, or in Lissajous mode
if (lissajous || (external && !inputs[TRIG_INPUT].isConnected())) {
trigger();
return;
}

frameIndex++;

// Reset if triggered
float trigThreshold = params[TRIG_PARAM].getValue();
Input& trigInput = external ? inputs[TRIG_INPUT] : inputs[X_INPUT];
// Wait for trigger if no longer recording
if (bufferIndex >= BUFFER_SIZE) {
auto trigger = [&]() {
for (int c = 0; c < 16; c++) {
triggers[c].reset();
}
bufferIndex = 0;
frameIndex = 0;
};


// This may be 0
int trigChannels = trigInput.getChannels();
for (int c = 0; c < trigChannels; c++) {
float trigVoltage = trigInput.getVoltage(c);
if (triggers[c].process(rescale(trigVoltage, trigThreshold, trigThreshold + 0.001f, 0.f, 1.f))) {
// Trigger immediately if external but nothing plugged in, or in Lissajous mode
if (lissajous || (external && !inputs[TRIG_INPUT].isConnected())) {
trigger(); trigger();
return; return;
} }
}


// Reset if we've been waiting for `holdTime`
const float holdTime = 0.5f;
if (frameIndex * args.sampleTime >= holdTime) {
trigger();
return;
}
}

void trigger() {
for (int c = 0; c < 16; c++) {
triggers[c].reset();
// Reset if triggered
float trigThreshold = params[TRIG_PARAM].getValue();
Input& trigInput = external ? inputs[TRIG_INPUT] : inputs[X_INPUT];

// This may be 0
int trigChannels = trigInput.getChannels();
for (int c = 0; c < trigChannels; c++) {
float trigVoltage = trigInput.getVoltage(c);
if (triggers[c].process(rescale(trigVoltage, trigThreshold, trigThreshold + 0.001f, 0.f, 1.f))) {
trigger();
return;
}
}
} }
bufferIndex = 0;
frameIndex = 0;
} }


json_t* dataToJson() override {
json_t* rootJ = json_object();
json_object_set_new(rootJ, "lissajous", json_integer((int) lissajous));
json_object_set_new(rootJ, "external", json_integer((int) external));
return rootJ;
bool isLissajous() {
return params[LISSAJOUS_PARAM].getValue() > 0.f;
} }


void dataFromJson(json_t* rootJ) override { void dataFromJson(json_t* rootJ) override {
json_t* sumJ = json_object_get(rootJ, "lissajous");
if (sumJ)
lissajous = json_integer_value(sumJ);
// In <2.0, lissajous and external were class variables
json_t* lissajousJ = json_object_get(rootJ, "lissajous");
if (lissajousJ) {
if (json_integer_value(lissajousJ))
params[LISSAJOUS_PARAM].setValue(1.f);
}


json_t* extJ = json_object_get(rootJ, "external");
if (extJ)
external = json_integer_value(extJ);
json_t* externalJ = json_object_get(rootJ, "external");
if (externalJ) {
if (json_integer_value(externalJ))
params[EXTERNAL_PARAM].setValue(1.f);
}
} }
}; };




struct ScopeDisplay : LedDisplay { struct ScopeDisplay : LedDisplay {
Scope* module; Scope* module;
ModuleWidget* moduleWidget;
int statsFrame = 0; int statsFrame = 0;
std::string fontPath; std::string fontPath;


struct Stats { struct Stats {
float vpp = 0.f;
float vmin = 0.f;
float vmax = 0.f;

void calculate(float* buffer, int channels) {
vmax = -INFINITY;
vmin = INFINITY;
for (int i = 0; i < BUFFER_SIZE * channels; i++) {
float v = buffer[i];
vmax = std::fmax(vmax, v);
vmin = std::fmin(vmin, v);
}
vpp = vmax - vmin;
}
float min = INFINITY;
float max = -INFINITY;
}; };
Stats statsX, statsY;
Stats statsX;
Stats statsY;


ScopeDisplay() { ScopeDisplay() {
fontPath = asset::system("res/fonts/ShareTechMono-Regular.ttf"); fontPath = asset::system("res/fonts/ShareTechMono-Regular.ttf");
} }


void drawWaveform(const DrawArgs& args, float* bufferX, float offsetX, float gainX, float* bufferY, float offsetY, float gainY) {
assert(bufferY);
void calculateStats(Stats& stats, int wave, int channels) {
if (!module)
return;

stats = Stats();
for (int i = 0; i < BUFFER_SIZE; i++) {
const Scope::Point& point = module->pointBuffer[i];
for (int c = 0; c < channels; c++) {
float max = (wave == 0) ? point.maxX[c] : point.maxY[c];
float min = (wave == 0) ? point.minX[c] : point.minY[c];

stats.max = std::fmax(stats.max, max);
stats.min = std::fmin(stats.min, min);
}
}
}

void drawWave(const DrawArgs& args, int wave, int channel, float offset, float gain) {
if (!module)
return;

nvgSave(args.vg); nvgSave(args.vg);
Rect b = Rect(Vec(0, 15), box.size.minus(Vec(0, 15 * 2)));
nvgScissor(args.vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
Rect b = box.zeroPos().shrink(Vec(0, 15));
nvgScissor(args.vg, RECT_ARGS(b));
nvgBeginPath(args.vg); nvgBeginPath(args.vg);
// Draw max points on top
for (int i = 0; i < BUFFER_SIZE; i++) { for (int i = 0; i < BUFFER_SIZE; i++) {
Vec v;
if (bufferX)
v.x = (bufferX[i] + offsetX) * gainX / 2.f + 0.5f;
const Scope::Point& point = module->pointBuffer[i];
float max = (wave == 0) ? point.maxX[channel] : point.maxY[channel];
if (!std::isfinite(max))
continue;

Vec p;
p.x = (float) i / (BUFFER_SIZE - 1);
p.y = (max + offset) * gain * -0.5f + 0.5f;
p = b.interpolate(p);
p.y += 1.0;
if (i == 0)
nvgMoveTo(args.vg, p.x, p.y);
else else
v.x = (float) i / (BUFFER_SIZE - 1);
v.y = (bufferY[i] + offsetY) * gainY / 2.f + 0.5f;
nvgLineTo(args.vg, p.x, p.y);
}
// Draw min points on bottom
for (int i = BUFFER_SIZE - 1; i >= 0; i--) {
const Scope::Point& point = module->pointBuffer[i];
float min = (wave == 0) ? point.minX[channel] : point.minY[channel];
if (!std::isfinite(min))
continue;

Vec p;
p.x = (float) i / (BUFFER_SIZE - 1);
p.y = (min + offset) * gain * -0.5f + 0.5f;
p = b.interpolate(p);
p.y -= 1.0;
nvgLineTo(args.vg, p.x, p.y);
}
nvgClosePath(args.vg);
// nvgLineCap(args.vg, NVG_ROUND);
// nvgMiterLimit(args.vg, 2.f);
nvgGlobalCompositeOperation(args.vg, NVG_LIGHTER);
nvgFill(args.vg);
nvgResetScissor(args.vg);
nvgRestore(args.vg);
}

void drawLissajous(const DrawArgs& args, int channel, float offsetX, float gainX, float offsetY, float gainY) {
if (!module)
return;

nvgSave(args.vg);
Rect b = box.zeroPos().shrink(Vec(0, 15));
nvgScissor(args.vg, RECT_ARGS(b));
nvgBeginPath(args.vg);
int bufferIndex = module->bufferIndex;
for (int i = 0; i < BUFFER_SIZE; i++) {
// Get average point
const Scope::Point& point = module->pointBuffer[(i + bufferIndex) % BUFFER_SIZE];
float avgX = (point.minX[channel] + point.maxX[channel]) / 2;
float avgY = (point.minY[channel] + point.maxY[channel]) / 2;
if (!std::isfinite(avgX) || !std::isfinite(avgY))
continue;

Vec p; Vec p;
p.x = rescale(v.x, 0.f, 1.f, b.pos.x, b.pos.x + b.size.x);
p.y = rescale(v.y, 0.f, 1.f, b.pos.y + b.size.y, b.pos.y);
p.x = (avgX + offsetX) * gainX * -0.5f + 0.5f;
p.y = (avgY + offsetY) * gainY * -0.5f + 0.5f;
p = b.interpolate(p);
if (i == 0) if (i == 0)
nvgMoveTo(args.vg, p.x, p.y); nvgMoveTo(args.vg, p.x, p.y);
else else
@@ -251,7 +328,6 @@ struct ScopeDisplay : LedDisplay {
nvgBeginPath(args.vg); nvgBeginPath(args.vg);
nvgMoveTo(args.vg, p.x - 13, p.y); nvgMoveTo(args.vg, p.x - 13, p.y);
nvgLineTo(args.vg, 0, p.y); nvgLineTo(args.vg, 0, p.y);
nvgClosePath(args.vg);
} }
nvgStroke(args.vg); nvgStroke(args.vg);


@@ -278,7 +354,7 @@ struct ScopeDisplay : LedDisplay {
nvgResetScissor(args.vg); nvgResetScissor(args.vg);
} }


void drawStats(const DrawArgs& args, Vec pos, const char* title, Stats* stats) {
void drawStats(const DrawArgs& args, Vec pos, const char* title, const Stats& stats) {
std::shared_ptr<Font> font = APP->window->loadFont(fontPath); std::shared_ptr<Font> font = APP->window->loadFont(fontPath);
if (!font) if (!font)
return; return;
@@ -294,16 +370,35 @@ struct ScopeDisplay : LedDisplay {


std::string text; std::string text;
text = "pp "; text = "pp ";
text += isNear(stats->vpp, 0.f, 100.f) ? string::f("% 6.2f", stats->vpp) : " ---";
float pp = stats.max - stats.min;
text += isNear(pp, 0.f, 100.f) ? string::f("% 6.2f", pp) : " ---";
nvgText(args.vg, pos.x, pos.y, text.c_str(), NULL); nvgText(args.vg, pos.x, pos.y, text.c_str(), NULL);
text = "max "; text = "max ";
text += isNear(stats->vmax, 0.f, 100.f) ? string::f("% 6.2f", stats->vmax) : " ---";
text += isNear(stats.max, 0.f, 100.f) ? string::f("% 6.2f", stats.max) : " ---";
nvgText(args.vg, pos.x + 58 * 1, pos.y, text.c_str(), NULL); nvgText(args.vg, pos.x + 58 * 1, pos.y, text.c_str(), NULL);
text = "min "; text = "min ";
text += isNear(stats->vmin, 0.f, 100.f) ? string::f("% 6.2f", stats->vmin) : " ---";
text += isNear(stats.min, 0.f, 100.f) ? string::f("% 6.2f", stats.min) : " ---";
nvgText(args.vg, pos.x + 58 * 2, pos.y, text.c_str(), NULL); nvgText(args.vg, pos.x + 58 * 2, pos.y, text.c_str(), NULL);
} }


void drawBackground(const DrawArgs& args) {
Rect b = box.zeroPos().shrink(Vec(0, 15));

nvgStrokeColor(args.vg, nvgRGBA(0xff, 0xff, 0xff, 0x10));
for (int i = 0; i < 5; i++) {
nvgBeginPath(args.vg);

Vec p;
p.x = 0.0;
p.y = float(i) / (5 - 1);
nvgMoveTo(args.vg, VEC_ARGS(b.interpolate(p)));

p.x = 1.0;
nvgLineTo(args.vg, VEC_ARGS(b.interpolate(p)));
nvgStroke(args.vg);
}
}

void drawLayer(const DrawArgs& args, int layer) override { void drawLayer(const DrawArgs& args, int layer) override {
if (layer != 1) if (layer != 1)
return; return;
@@ -316,41 +411,53 @@ struct ScopeDisplay : LedDisplay {
float offsetX = module->params[Scope::X_POS_PARAM].getValue(); float offsetX = module->params[Scope::X_POS_PARAM].getValue();
float offsetY = module->params[Scope::Y_POS_PARAM].getValue(); float offsetY = module->params[Scope::Y_POS_PARAM].getValue();


// Get input colors
PortWidget* inputX = moduleWidget->getInput(Scope::X_INPUT);
PortWidget* inputY = moduleWidget->getInput(Scope::Y_INPUT);
CableWidget* inputXCable = APP->scene->rack->getTopCable(inputX);
CableWidget* inputYCable = APP->scene->rack->getTopCable(inputY);
NVGcolor inputXColor = inputXCable ? inputXCable->color : color::WHITE;
NVGcolor inputYColor = inputYCable ? inputYCable->color : color::WHITE;

// Draw waveforms // Draw waveforms
if (module->lissajous) {
if (module->isLissajous()) {
// X x Y // X x Y
int lissajousChannels = std::max(module->channelsX, module->channelsY);
int lissajousChannels = std::min(module->channelsX, module->channelsY);
for (int c = 0; c < lissajousChannels; c++) { for (int c = 0; c < lissajousChannels; c++) {
nvgStrokeColor(args.vg, nvgRGBA(0x9f, 0xe4, 0x36, 0xc0));
drawWaveform(args, module->bufferX[c], offsetX, gainX, module->bufferY[c], offsetY, gainY);
nvgStrokeColor(args.vg, SCHEME_YELLOW);
drawLissajous(args, c, offsetX, gainX, offsetY, gainY);
} }
} }
else { else {
// Y // Y
for (int c = 0; c < module->channelsY; c++) { for (int c = 0; c < module->channelsY; c++) {
nvgStrokeColor(args.vg, nvgRGBA(0xe1, 0x02, 0x78, 0xc0));
drawWaveform(args, NULL, 0, 0, module->bufferY[c], offsetY, gainY);
nvgFillColor(args.vg, inputYColor);
drawWave(args, 1, c, offsetY, gainY);
} }


// X // X
for (int c = 0; c < module->channelsX; c++) { for (int c = 0; c < module->channelsX; c++) {
nvgStrokeColor(args.vg, nvgRGBA(0x28, 0xb0, 0xf3, 0xc0));
drawWaveform(args, NULL, 0, 0, module->bufferX[c], offsetX, gainX);
nvgFillColor(args.vg, inputXColor);
drawWave(args, 0, c, offsetX, gainX);
} }


// Trigger
float trigThreshold = module->params[Scope::TRIG_PARAM].getValue(); float trigThreshold = module->params[Scope::TRIG_PARAM].getValue();
trigThreshold = (trigThreshold + offsetX) * gainX; trigThreshold = (trigThreshold + offsetX) * gainX;
drawTrig(args, trigThreshold); drawTrig(args, trigThreshold);

// Background lines
drawBackground(args);
} }


// Calculate and draw stats // Calculate and draw stats
if (++statsFrame >= 4) { if (++statsFrame >= 4) {
statsFrame = 0; statsFrame = 0;
statsX.calculate(module->bufferX[0], module->channelsX);
statsY.calculate(module->bufferY[0], module->channelsY);
calculateStats(statsX, 0, module->channelsX);
calculateStats(statsY, 1, module->channelsY);
} }
drawStats(args, Vec(0, 0), "X", &statsX);
drawStats(args, Vec(0, box.size.y - 15), "Y", &statsY);
drawStats(args, Vec(0, 0 + 1), "X", statsX);
drawStats(args, Vec(0, box.size.y - 15 - 1), "Y", statsY);
} }
}; };


@@ -365,14 +472,14 @@ struct ScopeWidget : ModuleWidget {
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));


// addParam(createParamCentered<VCVButton>(mm2px(Vec(8.643, 80.603)), module, Scope::_1X2_PARAM));
addParam(createLightParamCentered<VCVLightLatch<MediumSimpleLight<WhiteLight>>>(mm2px(Vec(8.643, 80.603)), module, Scope::LISSAJOUS_PARAM, Scope::LISSAJOUS_LIGHT));
addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(24.897, 80.551)), module, Scope::X_SCALE_PARAM)); addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(24.897, 80.551)), module, Scope::X_SCALE_PARAM));
addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(41.147, 80.551)), module, Scope::Y_SCALE_PARAM)); addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(41.147, 80.551)), module, Scope::Y_SCALE_PARAM));
// addParam(createParamCentered<VCVButton>(mm2px(Vec(57.397, 80.521)), module, Scope::TRIG_PARAM));
addParam(createLightParamCentered<VCVLightLatch<MediumSimpleLight<WhiteLight>>>(mm2px(Vec(57.397, 80.521)), module, Scope::EXTERNAL_PARAM, Scope::EXTERNAL_LIGHT));
addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(8.643, 96.819)), module, Scope::TIME_PARAM)); addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(8.643, 96.819)), module, Scope::TIME_PARAM));
addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(24.897, 96.789)), module, Scope::X_POS_PARAM)); addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(24.897, 96.789)), module, Scope::X_POS_PARAM));
addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(41.147, 96.815)), module, Scope::Y_POS_PARAM)); addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(41.147, 96.815)), module, Scope::Y_POS_PARAM));
// addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(57.397, 96.815)), module, Scope::THERS_PARAM));
addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(57.397, 96.815)), module, Scope::TRIG_PARAM));


addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.643, 113.115)), module, Scope::X_INPUT)); addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.643, 113.115)), module, Scope::X_INPUT));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(33.023, 113.115)), module, Scope::Y_INPUT)); addInput(createInputCentered<PJ301MPort>(mm2px(Vec(33.023, 113.115)), module, Scope::Y_INPUT));
@@ -381,9 +488,10 @@ struct ScopeWidget : ModuleWidget {
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(20.833, 113.115)), module, Scope::X_OUTPUT)); addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(20.833, 113.115)), module, Scope::X_OUTPUT));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(45.212, 113.115)), module, Scope::Y_OUTPUT)); addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(45.212, 113.115)), module, Scope::Y_OUTPUT));


ScopeDisplay* display = createWidget<ScopeDisplay>(Vec(0, 44));
display->box.size = Vec(box.size.x, 140);
ScopeDisplay* display = createWidget<ScopeDisplay>(mm2px(Vec(0.0, 13.039)));
display->box.size = mm2px(Vec(66.04, 55.88));
display->module = module; display->module = module;
display->moduleWidget = this;
addChild(display); addChild(display);
} }
}; };


Loading…
Cancel
Save