Browse Source

Make Scope polyphonic.

tags/v1.0.1
Andrew Belt 6 years ago
parent
commit
708e27ac74
1 changed files with 83 additions and 67 deletions
  1. +83
    -67
      src/Scope.cpp

+ 83
- 67
src/Scope.cpp View File

@@ -34,8 +34,10 @@ struct Scope : Module {
NUM_LIGHTS NUM_LIGHTS
}; };


float bufferX[BUFFER_SIZE] = {};
float bufferY[BUFFER_SIZE] = {};
float bufferX[16][BUFFER_SIZE] = {};
float bufferY[16][BUFFER_SIZE] = {};
int channelsX = 0;
int channelsY = 0;
int bufferIndex = 0; int bufferIndex = 0;
float frameIndex = 0; float frameIndex = 0;


@@ -60,6 +62,8 @@ struct Scope : Module {
void onReset() override { void onReset() override {
lissajous = false; lissajous = false;
external = false; external = false;
std::memset(bufferX, 0, sizeof(bufferX));
std::memset(bufferY, 0, sizeof(bufferY));
} }


void process(const ProcessArgs &args) override { void process(const ProcessArgs &args) override {
@@ -67,25 +71,42 @@ struct Scope : Module {
if (sumTrigger.process(params[LISSAJOUS_PARAM].getValue() > 0.f)) { if (sumTrigger.process(params[LISSAJOUS_PARAM].getValue() > 0.f)) {
lissajous = !lissajous; lissajous = !lissajous;
} }
lights[PLOT_LIGHT].value = lissajous ? 0.f : 1.f;
lights[LISSAJOUS_LIGHT].value = lissajous ? 1.f : 0.f;
lights[PLOT_LIGHT].setBrightness(!lissajous);
lights[LISSAJOUS_LIGHT].setBrightness(lissajous);


if (extTrigger.process(params[EXTERNAL_PARAM].getValue() > 0.f)) { if (extTrigger.process(params[EXTERNAL_PARAM].getValue() > 0.f)) {
external = !external; external = !external;
} }
lights[INTERNAL_LIGHT].value = external ? 0.f : 1.f;
lights[EXTERNAL_LIGHT].value = external ? 1.f : 0.f;
lights[INTERNAL_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());
int frameCount = (int) std::ceil(deltaTime * args.sampleRate); int frameCount = (int) std::ceil(deltaTime * args.sampleRate);


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

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

// Add frame to buffer // Add frame to buffer
if (bufferIndex < BUFFER_SIZE) { if (bufferIndex < BUFFER_SIZE) {
if (++frameIndex > frameCount) { if (++frameIndex > frameCount) {
frameIndex = 0; frameIndex = 0;
bufferX[bufferIndex] = inputs[X_INPUT].getVoltage();
bufferY[bufferIndex] = inputs[Y_INPUT].getVoltage();
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);
}
bufferIndex++; bufferIndex++;
} }
} }
@@ -105,18 +126,16 @@ struct Scope : Module {
} }
frameIndex++; frameIndex++;


// Must go below 0.1fV to trigger
// Must go below 0.1V to trigger
float gate = external ? inputs[TRIG_INPUT].getVoltage() : inputs[X_INPUT].getVoltage(); float gate = external ? inputs[TRIG_INPUT].getVoltage() : inputs[X_INPUT].getVoltage();


// Reset if triggered // Reset if triggered
float holdTime = 0.1f; float holdTime = 0.1f;
if (resetTrigger.process(rescale(gate, params[TRIG_PARAM].getValue() - 0.1f, params[TRIG_PARAM].getValue(), 0.f, 1.f)) || (frameIndex >= args.sampleRate * holdTime)) {
bufferIndex = 0; frameIndex = 0; return;
}

// Reset if we've waited too long
if (frameIndex >= args.sampleRate * holdTime) {
bufferIndex = 0; frameIndex = 0; return;
float trigValue = params[TRIG_PARAM].getValue();
if (resetTrigger.process(rescale(gate, trigValue - 0.1f, trigValue, 0.f, 1.f)) || (frameIndex >= args.sampleRate * holdTime)) {
bufferIndex = 0;
frameIndex = 0;
return;
} }
} }
} }
@@ -142,55 +161,52 @@ struct Scope : Module {


struct ScopeDisplay : TransparentWidget { struct ScopeDisplay : TransparentWidget {
Scope *module; Scope *module;
int frame = 0;
int statsFrame = 0;
std::shared_ptr<Font> font; std::shared_ptr<Font> font;


struct Stats { struct Stats {
float vrms = 0.f;
// float vrms = 0.f;
float vpp = 0.f; float vpp = 0.f;
float vmin = 0.f; float vmin = 0.f;
float vmax = 0.f; float vmax = 0.f;
void calculate(float *values) {
vrms = 0.f;

void calculate(float *buffer, int channels) {
// vrms = 0.f;
vmax = -INFINITY; vmax = -INFINITY;
vmin = INFINITY; vmin = INFINITY;
for (int i = 0; i < BUFFER_SIZE; i++) {
float v = values[i];
vrms += v*v;
for (int i = 0; i < BUFFER_SIZE * channels; i++) {
float v = buffer[i];
// vrms += v*v;
vmax = std::fmax(vmax, v); vmax = std::fmax(vmax, v);
vmin = std::fmin(vmin, v); vmin = std::fmin(vmin, v);
} }
vrms = std::sqrt(vrms / BUFFER_SIZE);
// vrms = std::sqrt(vrms / BUFFER_SIZE);
vpp = vmax - vmin; vpp = vmax - vmin;
} }
}; };

Stats statsX, statsY; Stats statsX, statsY;


ScopeDisplay() { ScopeDisplay() {
font = APP->window->loadFont(asset::plugin(pluginInstance, "res/sudo/Sudo.ttf")); font = APP->window->loadFont(asset::plugin(pluginInstance, "res/sudo/Sudo.ttf"));
} }


void drawWaveform(const DrawArgs &args, float *valuesX, float *valuesY) {
if (!valuesX)
return;
void drawWaveform(const DrawArgs &args, float *bufferX, float offsetX, float gainX, float *bufferY, float offsetY, float gainY) {
assert(bufferY);
nvgSave(args.vg); nvgSave(args.vg);
Rect b = Rect(Vec(0, 15), box.size.minus(Vec(0, 15*2))); 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); nvgScissor(args.vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
nvgBeginPath(args.vg); nvgBeginPath(args.vg);
// Draw maximum display left to right
for (int i = 0; i < BUFFER_SIZE; i++) { for (int i = 0; i < BUFFER_SIZE; i++) {
float x, y;
if (valuesY) {
x = valuesX[i] / 2.f + 0.5f;
y = valuesY[i] / 2.f + 0.5f;
}
else {
x = (float)i / (BUFFER_SIZE - 1);
y = valuesX[i] / 2.f + 0.5f;
}
Vec v;
if (bufferX)
v.x = (bufferX[i] + offsetX) * gainX / 2.f + 0.5f;
else
v.x = (float) i / (BUFFER_SIZE - 1);
v.y = (bufferY[i] + offsetY) * gainY / 2.f + 0.5f;
Vec p; Vec p;
p.x = b.pos.x + b.size.x * x;
p.y = b.pos.y + b.size.y * (1.f - y);
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);
if (i == 0) if (i == 0)
nvgMoveTo(args.vg, p.x, p.y); nvgMoveTo(args.vg, p.x, p.y);
else else
@@ -251,61 +267,61 @@ struct ScopeDisplay : TransparentWidget {
nvgText(args.vg, pos.x + 6, pos.y + 11, title, NULL); nvgText(args.vg, pos.x + 6, pos.y + 11, title, NULL);


nvgFillColor(args.vg, nvgRGBA(0xff, 0xff, 0xff, 0x80)); nvgFillColor(args.vg, nvgRGBA(0xff, 0xff, 0xff, 0x80));
char text[128];
snprintf(text, sizeof(text), "pp % 06.2f max % 06.2f min % 06.2f", stats->vpp, stats->vmax, stats->vmin);
nvgText(args.vg, pos.x + 22, pos.y + 11, text, NULL);
pos = pos.plus(Vec(22, 11));

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


void draw(const DrawArgs &args) override { void draw(const DrawArgs &args) override {
if (!module) if (!module)
return; return;


float gainX = std::pow(2.f, std::round(module->params[Scope::X_SCALE_PARAM].getValue()));
float gainY = std::pow(2.f, std::round(module->params[Scope::Y_SCALE_PARAM].getValue()));
float gainX = std::pow(2.f, std::round(module->params[Scope::X_SCALE_PARAM].getValue())) / 10.f;
float gainY = std::pow(2.f, std::round(module->params[Scope::Y_SCALE_PARAM].getValue())) / 10.f;
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();


float valuesX[BUFFER_SIZE];
float valuesY[BUFFER_SIZE];
for (int i = 0; i < BUFFER_SIZE; i++) {
int j = i;
// Lock display to buffer if buffer update deltaTime <= 2^-11
if (module->lissajous)
j = (i + module->bufferIndex) % BUFFER_SIZE;
valuesX[i] = (module->bufferX[j] + offsetX) * gainX / 10.f;
valuesY[i] = (module->bufferY[j] + offsetY) * gainY / 10.f;
}

// Draw waveforms // Draw waveforms
if (module->lissajous) { if (module->lissajous) {
// X x Y // X x Y
if (module->inputs[Scope::X_INPUT].isConnected() || module->inputs[Scope::Y_INPUT].isConnected()) {
int lissajousChannels = std::max(module->channelsX, module->channelsY);
for (int c = 0; c < lissajousChannels; c++) {
nvgStrokeColor(args.vg, nvgRGBA(0x9f, 0xe4, 0x36, 0xc0)); nvgStrokeColor(args.vg, nvgRGBA(0x9f, 0xe4, 0x36, 0xc0));
drawWaveform(args, valuesX, valuesY);
drawWaveform(args, module->bufferX[c], offsetX, gainX, module->bufferY[c], offsetY, gainY);
} }
} }
else { else {
// Y // Y
if (module->inputs[Scope::Y_INPUT].isConnected()) {
for (int c = 0; c < module->channelsY; c++) {
nvgStrokeColor(args.vg, nvgRGBA(0xe1, 0x02, 0x78, 0xc0)); nvgStrokeColor(args.vg, nvgRGBA(0xe1, 0x02, 0x78, 0xc0));
drawWaveform(args, valuesY, NULL);
drawWaveform(args, NULL, 0, 0, module->bufferY[c], offsetY, gainY);
} }


// X // X
if (module->inputs[Scope::X_INPUT].isConnected()) {
for (int c = 0; c < module->channelsX; c++) {
nvgStrokeColor(args.vg, nvgRGBA(0x28, 0xb0, 0xf3, 0xc0)); nvgStrokeColor(args.vg, nvgRGBA(0x28, 0xb0, 0xf3, 0xc0));
drawWaveform(args, valuesX, NULL);
drawWaveform(args, NULL, 0, 0, module->bufferX[c], offsetX, gainX);
} }


float valueTrig = (module->params[Scope::TRIG_PARAM].getValue() + offsetX) * gainX / 10.f;
drawTrig(args, valueTrig);
float trigValue = module->params[Scope::TRIG_PARAM].getValue();
trigValue = (trigValue + offsetX) * gainX;
drawTrig(args, trigValue);
} }


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


Loading…
Cancel
Save