|
|
@@ -36,26 +36,15 @@ struct Scope : Module { |
|
|
|
}; |
|
|
|
|
|
|
|
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; |
|
|
|
} |
|
|
|
} |
|
|
|
float min = INFINITY; |
|
|
|
float max = -INFINITY; |
|
|
|
}; |
|
|
|
Point pointBuffer[BUFFER_SIZE]; |
|
|
|
Point pointBuffer[BUFFER_SIZE][2][PORT_MAX_CHANNELS]; |
|
|
|
Point currentPoint[2][PORT_MAX_CHANNELS]; |
|
|
|
int channelsX = 0; |
|
|
|
int channelsY = 0; |
|
|
|
int bufferIndex = 0; |
|
|
|
int frameIndex = 0; |
|
|
|
Point currentPoint; |
|
|
|
|
|
|
|
dsp::SchmittTrigger triggers[16]; |
|
|
|
|
|
|
@@ -85,7 +74,11 @@ struct Scope : Module { |
|
|
|
|
|
|
|
void onReset() override { |
|
|
|
for (int i = 0; i < BUFFER_SIZE; i++) { |
|
|
|
pointBuffer[i] = Point(); |
|
|
|
for (int w = 0; w < 2; w++) { |
|
|
|
for (int c = 0; c < 16; c++) { |
|
|
|
pointBuffer[i][w][c] = Point(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -158,21 +151,29 @@ struct Scope : Module { |
|
|
|
// 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); |
|
|
|
currentPoint[0][c].min = std::min(currentPoint[0][c].min, x); |
|
|
|
currentPoint[0][c].max = std::max(currentPoint[0][c].max, 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); |
|
|
|
currentPoint[1][c].min = std::min(currentPoint[1][c].min, y); |
|
|
|
currentPoint[1][c].max = std::max(currentPoint[1][c].max, y); |
|
|
|
} |
|
|
|
|
|
|
|
if (++frameIndex >= frameCount) { |
|
|
|
frameIndex = 0; |
|
|
|
// Push current point |
|
|
|
pointBuffer[bufferIndex] = currentPoint; |
|
|
|
for (int w = 0; w < 2; w++) { |
|
|
|
for (int c = 0; c < 16; c++) { |
|
|
|
pointBuffer[bufferIndex][w][c] = currentPoint[w][c]; |
|
|
|
} |
|
|
|
} |
|
|
|
// Reset current point |
|
|
|
currentPoint = Point(); |
|
|
|
for (int w = 0; w < 2; w++) { |
|
|
|
for (int c = 0; c < 16; c++) { |
|
|
|
currentPoint[w][c] = Point(); |
|
|
|
} |
|
|
|
} |
|
|
|
bufferIndex++; |
|
|
|
} |
|
|
|
} |
|
|
@@ -199,6 +200,24 @@ struct Scope : Module { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Scope::Point DEMO_POINT_BUFFER[BUFFER_SIZE]; |
|
|
|
|
|
|
|
void demoPointBufferInit() { |
|
|
|
static bool init = false; |
|
|
|
if (init) |
|
|
|
return; |
|
|
|
init = true; |
|
|
|
|
|
|
|
// Calculate demo point buffer |
|
|
|
for (size_t i = 0; i < BUFFER_SIZE; i++) { |
|
|
|
float phase = float(i) / BUFFER_SIZE; |
|
|
|
Scope::Point point; |
|
|
|
point.min = point.max = 4.f * std::sin(2 * M_PI * phase * 2.f); |
|
|
|
DEMO_POINT_BUFFER[i] = point; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct ScopeDisplay : LedDisplay { |
|
|
|
Scope* module; |
|
|
|
ModuleWidget* moduleWidget; |
|
|
@@ -214,6 +233,8 @@ struct ScopeDisplay : LedDisplay { |
|
|
|
|
|
|
|
ScopeDisplay() { |
|
|
|
fontPath = asset::system("res/fonts/ShareTechMono-Regular.ttf"); |
|
|
|
|
|
|
|
demoPointBufferInit(); |
|
|
|
} |
|
|
|
|
|
|
|
void calculateStats(Stats& stats, int wave, int channels) { |
|
|
@@ -225,32 +246,18 @@ struct ScopeDisplay : LedDisplay { |
|
|
|
|
|
|
|
stats = Stats(); |
|
|
|
for (int i = 0; i < BUFFER_SIZE; i++) { |
|
|
|
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); |
|
|
|
Scope::Point point = module->pointBuffer[i][wave][c]; |
|
|
|
stats.max = std::fmax(stats.max, point.max); |
|
|
|
stats.min = std::fmin(stats.min, point.min); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void drawWave(const DrawArgs& args, int wave, int channel, float offset, float gain) { |
|
|
|
// Copy point buffer to stack to prevent min/max values being different phase. |
|
|
|
// This is currently only 256*4*16*4 = 64 kiB. |
|
|
|
Scope::Point pointBuffer[BUFFER_SIZE]; |
|
|
|
if (module) { |
|
|
|
std::copy(std::begin(module->pointBuffer), std::end(module->pointBuffer), std::begin(pointBuffer)); |
|
|
|
} |
|
|
|
else { |
|
|
|
for (size_t i = 0; i < BUFFER_SIZE; i++) { |
|
|
|
float phase = float(i) / BUFFER_SIZE; |
|
|
|
Scope::Point point; |
|
|
|
point.minX[0] = point.maxX[0] = 4.f * std::sin(2 * M_PI * phase * 2.f) + 5.f; |
|
|
|
point.minY[0] = point.maxY[0] = 4.f * std::sin(2 * M_PI * phase * 2.f) - 5.f; |
|
|
|
pointBuffer[i] = point; |
|
|
|
} |
|
|
|
for (int i = 0; i < BUFFER_SIZE; i++) { |
|
|
|
pointBuffer[i] = module ? module->pointBuffer[i][wave][channel] : DEMO_POINT_BUFFER[i]; |
|
|
|
} |
|
|
|
|
|
|
|
nvgSave(args.vg); |
|
|
@@ -260,7 +267,7 @@ struct ScopeDisplay : LedDisplay { |
|
|
|
// Draw max points on top |
|
|
|
for (int i = 0; i < BUFFER_SIZE; i++) { |
|
|
|
const Scope::Point& point = pointBuffer[i]; |
|
|
|
float max = (wave == 0) ? point.maxX[channel] : point.maxY[channel]; |
|
|
|
float max = point.max; |
|
|
|
if (!std::isfinite(max)) |
|
|
|
max = 0.f; |
|
|
|
|
|
|
@@ -277,7 +284,7 @@ struct ScopeDisplay : LedDisplay { |
|
|
|
// Draw min points on bottom |
|
|
|
for (int i = BUFFER_SIZE - 1; i >= 0; i--) { |
|
|
|
const Scope::Point& point = pointBuffer[i]; |
|
|
|
float min = (wave == 0) ? point.minX[channel] : point.minY[channel]; |
|
|
|
float min = point.min; |
|
|
|
if (!std::isfinite(min)) |
|
|
|
min = 0.f; |
|
|
|
|
|
|
@@ -301,8 +308,12 @@ struct ScopeDisplay : LedDisplay { |
|
|
|
if (!module) |
|
|
|
return; |
|
|
|
|
|
|
|
Scope::Point pointBuffer[BUFFER_SIZE]; |
|
|
|
std::copy(std::begin(module->pointBuffer), std::end(module->pointBuffer), std::begin(pointBuffer)); |
|
|
|
Scope::Point pointBufferX[BUFFER_SIZE]; |
|
|
|
Scope::Point pointBufferY[BUFFER_SIZE]; |
|
|
|
for (int i = 0; i < BUFFER_SIZE; i++) { |
|
|
|
pointBufferX[i] = module->pointBuffer[i][0][channel]; |
|
|
|
pointBufferY[i] = module->pointBuffer[i][1][channel]; |
|
|
|
} |
|
|
|
|
|
|
|
nvgSave(args.vg); |
|
|
|
Rect b = box.zeroPos().shrink(Vec(0, 15)); |
|
|
@@ -311,9 +322,10 @@ struct ScopeDisplay : LedDisplay { |
|
|
|
int bufferIndex = module->bufferIndex; |
|
|
|
for (int i = 0; i < BUFFER_SIZE; i++) { |
|
|
|
// Get average point |
|
|
|
const Scope::Point& point = pointBuffer[(i + bufferIndex) % BUFFER_SIZE]; |
|
|
|
float avgX = (point.minX[channel] + point.maxX[channel]) / 2; |
|
|
|
float avgY = (point.minY[channel] + point.maxY[channel]) / 2; |
|
|
|
const Scope::Point& pointX = pointBufferX[(i + bufferIndex) % BUFFER_SIZE]; |
|
|
|
const Scope::Point& pointY = pointBufferY[(i + bufferIndex) % BUFFER_SIZE]; |
|
|
|
float avgX = (pointX.min + pointX.max) / 2; |
|
|
|
float avgY = (pointY.min + pointY.max) / 2; |
|
|
|
if (!std::isfinite(avgX) || !std::isfinite(avgY)) |
|
|
|
continue; |
|
|
|
|
|
|
@@ -430,8 +442,8 @@ struct ScopeDisplay : LedDisplay { |
|
|
|
gainX = std::pow(2.f, std::round(gainX)) / 10.f; |
|
|
|
float gainY = module ? module->params[Scope::Y_SCALE_PARAM].getValue() : 0.f; |
|
|
|
gainY = std::pow(2.f, std::round(gainY)) / 10.f; |
|
|
|
float offsetX = module ? module->params[Scope::X_POS_PARAM].getValue() : 0.f; |
|
|
|
float offsetY = module ? module->params[Scope::Y_POS_PARAM].getValue() : 0.f; |
|
|
|
float offsetX = module ? module->params[Scope::X_POS_PARAM].getValue() : 5.f; |
|
|
|
float offsetY = module ? module->params[Scope::Y_POS_PARAM].getValue() : -5.f; |
|
|
|
|
|
|
|
// Get input colors |
|
|
|
PortWidget* inputX = moduleWidget->getInput(Scope::X_INPUT); |
|
|
|