| @@ -36,26 +36,15 @@ struct Scope : Module { | |||||
| }; | }; | ||||
| struct Point { | 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 channelsX = 0; | ||||
| int channelsY = 0; | int channelsY = 0; | ||||
| int bufferIndex = 0; | int bufferIndex = 0; | ||||
| int frameIndex = 0; | int frameIndex = 0; | ||||
| Point currentPoint; | |||||
| dsp::SchmittTrigger triggers[16]; | dsp::SchmittTrigger triggers[16]; | ||||
| @@ -85,7 +74,11 @@ struct Scope : Module { | |||||
| void onReset() override { | void onReset() override { | ||||
| for (int i = 0; i < BUFFER_SIZE; i++) { | 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 | // Get input | ||||
| for (int c = 0; c < channelsX; c++) { | for (int c = 0; c < channelsX; c++) { | ||||
| float x = inputs[X_INPUT].getVoltage(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++) { | for (int c = 0; c < channelsY; c++) { | ||||
| float y = inputs[Y_INPUT].getVoltage(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) { | if (++frameIndex >= frameCount) { | ||||
| frameIndex = 0; | frameIndex = 0; | ||||
| // Push current point | // 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 | // Reset current point | ||||
| currentPoint = Point(); | |||||
| for (int w = 0; w < 2; w++) { | |||||
| for (int c = 0; c < 16; c++) { | |||||
| currentPoint[w][c] = Point(); | |||||
| } | |||||
| } | |||||
| bufferIndex++; | 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 { | struct ScopeDisplay : LedDisplay { | ||||
| Scope* module; | Scope* module; | ||||
| ModuleWidget* moduleWidget; | ModuleWidget* moduleWidget; | ||||
| @@ -214,6 +233,8 @@ struct ScopeDisplay : LedDisplay { | |||||
| ScopeDisplay() { | ScopeDisplay() { | ||||
| fontPath = asset::system("res/fonts/ShareTechMono-Regular.ttf"); | fontPath = asset::system("res/fonts/ShareTechMono-Regular.ttf"); | ||||
| demoPointBufferInit(); | |||||
| } | } | ||||
| void calculateStats(Stats& stats, int wave, int channels) { | void calculateStats(Stats& stats, int wave, int channels) { | ||||
| @@ -225,32 +246,18 @@ struct ScopeDisplay : LedDisplay { | |||||
| stats = Stats(); | stats = Stats(); | ||||
| for (int i = 0; i < BUFFER_SIZE; i++) { | for (int i = 0; i < BUFFER_SIZE; i++) { | ||||
| Scope::Point point = module->pointBuffer[i]; | |||||
| for (int c = 0; c < channels; c++) { | 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) { | 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]; | 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); | nvgSave(args.vg); | ||||
| @@ -260,7 +267,7 @@ struct ScopeDisplay : LedDisplay { | |||||
| // Draw max points on top | // Draw max points on top | ||||
| for (int i = 0; i < BUFFER_SIZE; i++) { | for (int i = 0; i < BUFFER_SIZE; i++) { | ||||
| const Scope::Point& point = pointBuffer[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)) | if (!std::isfinite(max)) | ||||
| max = 0.f; | max = 0.f; | ||||
| @@ -277,7 +284,7 @@ struct ScopeDisplay : LedDisplay { | |||||
| // Draw min points on bottom | // Draw min points on bottom | ||||
| for (int i = BUFFER_SIZE - 1; i >= 0; i--) { | for (int i = BUFFER_SIZE - 1; i >= 0; i--) { | ||||
| const Scope::Point& point = pointBuffer[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)) | if (!std::isfinite(min)) | ||||
| min = 0.f; | min = 0.f; | ||||
| @@ -301,8 +308,12 @@ struct ScopeDisplay : LedDisplay { | |||||
| if (!module) | if (!module) | ||||
| return; | 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); | nvgSave(args.vg); | ||||
| Rect b = box.zeroPos().shrink(Vec(0, 15)); | Rect b = box.zeroPos().shrink(Vec(0, 15)); | ||||
| @@ -311,9 +322,10 @@ struct ScopeDisplay : LedDisplay { | |||||
| int bufferIndex = module->bufferIndex; | int bufferIndex = module->bufferIndex; | ||||
| for (int i = 0; i < BUFFER_SIZE; i++) { | for (int i = 0; i < BUFFER_SIZE; i++) { | ||||
| // Get average point | // 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)) | if (!std::isfinite(avgX) || !std::isfinite(avgY)) | ||||
| continue; | continue; | ||||
| @@ -430,8 +442,8 @@ struct ScopeDisplay : LedDisplay { | |||||
| gainX = std::pow(2.f, std::round(gainX)) / 10.f; | gainX = std::pow(2.f, std::round(gainX)) / 10.f; | ||||
| float gainY = module ? module->params[Scope::Y_SCALE_PARAM].getValue() : 0.f; | float gainY = module ? module->params[Scope::Y_SCALE_PARAM].getValue() : 0.f; | ||||
| gainY = std::pow(2.f, std::round(gainY)) / 10.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 | // Get input colors | ||||
| PortWidget* inputX = moduleWidget->getInput(Scope::X_INPUT); | PortWidget* inputX = moduleWidget->getInput(Scope::X_INPUT); | ||||