| @@ -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); | |||