Skip to content

Commit ea2beec

Browse files
authored
Show the transform and response in waveshaper (#886)
with an option to turn it off Addresses #883. Still thinking a bit though
1 parent cb983a9 commit ea2beec

File tree

2 files changed

+157
-32
lines changed

2 files changed

+157
-32
lines changed

src/Waveshaper.cpp

Lines changed: 149 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ struct WaveshaperWidget : widgets::XTModuleWidget
4848

4949
menu->addChild(rack::createMenuItem("Apply DC Blocker", CHECKMARK(m->doDCBlock),
5050
[m]() { m->doDCBlock = !m->doDCBlock; }));
51+
52+
menu->addChild(rack::createMenuItem(
53+
"Show Transform and Response", CHECKMARK(m->showTransformCurve),
54+
[m]() { m->showTransformCurve = !m->showTransformCurve; }));
5155
}
5256
}
5357
};
@@ -57,9 +61,11 @@ struct WaveshaperPlotWidget : public rack::widget::TransparentWidget, style::Sty
5761
typename WaveshaperWidget::M *module{nullptr};
5862
widgets::BufferedDrawFunctionWidget *bdw{nullptr};
5963
widgets::BufferedDrawFunctionWidget *bdwPlot{nullptr};
64+
widgets::BufferedDrawFunctionWidget *bdwResponse{nullptr};
6065

6166
std::vector<std::pair<float, float>> inputSignal;
6267
std::vector<std::pair<float, float>> outputSignal;
68+
std::vector<std::pair<float, float>> responseSignal;
6369
void setup(typename WaveshaperWidget::M *m)
6470
{
6571
module = m;
@@ -75,13 +81,24 @@ struct WaveshaperPlotWidget : public rack::widget::TransparentWidget, style::Sty
7581
rack::Vec(0, 0), box.size, [this](auto *vg) { drawPlot(vg); });
7682
addChild(bdwPlot);
7783

84+
bdwResponse = new widgets::BufferedDrawFunctionWidgetOnLayer(
85+
rack::Vec(box.size.x * 0.666 - rack::mm2px(0), rack::mm2px(0)),
86+
rack::Vec(box.size.x * 0.333, box.size.y - rack::mm2px(0)),
87+
[this](auto *vg) { drawResponse(vg); });
88+
addChild(bdwResponse);
89+
calculateInputSignal();
90+
}
91+
void calculateInputSignal()
92+
{
93+
inputSignal.clear();
7894
auto fac = 2.0;
7995
auto inputRes = (int)box.size.x * fac;
8096
auto dx = 1.0 / inputRes;
97+
auto cmul = module ? (module->showTransformCurve ? 6.0 : 4.0) : 4.0;
8198
for (int i = 0; i < inputRes; ++i)
8299
{
83100
auto x = dx * i;
84-
auto y = std::sin(x * 4.0 * M_PI);
101+
auto y = std::sin(x * cmul * M_PI);
85102
inputSignal.emplace_back(x * box.size.x, y);
86103
}
87104
}
@@ -99,6 +116,7 @@ struct WaveshaperPlotWidget : public rack::widget::TransparentWidget, style::Sty
99116
{
100117
recalcPath();
101118
bdwPlot->dirty = true;
119+
bdwResponse->dirty = true;
102120
}
103121

104122
rack::widget::Widget::step();
@@ -108,6 +126,7 @@ struct WaveshaperPlotWidget : public rack::widget::TransparentWidget, style::Sty
108126
{
109127
bdw->dirty = true;
110128
bdwPlot->dirty = true;
129+
bdwResponse->dirty = true;
111130
}
112131

113132
static WaveshaperPlotWidget *create(rack::Vec pos, rack::Vec size,
@@ -127,6 +146,7 @@ struct WaveshaperPlotWidget : public rack::widget::TransparentWidget, style::Sty
127146
int dirtyCount{0};
128147
int sumDeact{-1};
129148
int sumAbs{-1};
149+
bool stc{false};
130150
uint32_t wtloadCompare{842932918};
131151

132152
bool isDirty()
@@ -156,6 +176,14 @@ struct WaveshaperPlotWidget : public rack::widget::TransparentWidget, style::Sty
156176
}
157177

158178
dval = wstype != lastType || ddb != lastDrive || bias != lastBias;
179+
180+
if (module->showTransformCurve != stc)
181+
{
182+
dval = true;
183+
calculateInputSignal();
184+
stc = module->showTransformCurve;
185+
bdw->dirty = true; // special - gotta redo the background
186+
}
159187
}
160188
return dval;
161189
}
@@ -168,55 +196,88 @@ struct WaveshaperPlotWidget : public rack::widget::TransparentWidget, style::Sty
168196
if (!module)
169197
return;
170198

199+
responseSignal.clear();
171200
outputSignal.clear();
172201
auto wstype = (sst::waveshapers::WaveshaperType)std::round(
173202
module->paramQuantities[Waveshaper::WSHP_TYPE]->getValue());
174203
sst::waveshapers::QuadWaveshaperState wss;
175204
float R[4];
176205

177-
initializeWaveshaperRegister(wstype, R);
178-
179-
for (int i = 0; i < sst::waveshapers::n_waveshaper_registers; ++i)
180206
{
181-
wss.R[i] = _mm_set1_ps(R[i]);
182-
}
207+
initializeWaveshaperRegister(wstype, R);
183208

184-
wss.init = _mm_cmpeq_ps(_mm_setzero_ps(), _mm_setzero_ps()); // better way?
209+
for (int i = 0; i < sst::waveshapers::n_waveshaper_registers; ++i)
210+
{
211+
wss.R[i] = _mm_set1_ps(R[i]);
212+
}
185213

186-
float ddb{0.f}, bias{0.f};
187-
if (style::XTStyle::getShowModulationAnimationOnDisplay())
188-
{
189-
ddb = module->modulationAssistant.values[Waveshaper::DRIVE][0];
190-
bias = module->modulationAssistant.values[Waveshaper::BIAS][0];
191-
}
192-
else
193-
{
194-
ddb = module->modulationAssistant.basevalues[Waveshaper::DRIVE];
195-
bias = module->modulationAssistant.basevalues[Waveshaper::BIAS];
196-
}
214+
wss.init = _mm_cmpeq_ps(_mm_setzero_ps(), _mm_setzero_ps()); // better way?
197215

198-
auto wsop = sst::waveshapers::GetQuadWaveshaper(wstype);
199-
auto damp = pow(10, 0.05 * ddb);
200-
auto d1 = _mm_set1_ps(damp);
216+
float ddb{0.f}, bias{0.f};
217+
if (style::XTStyle::getShowModulationAnimationOnDisplay())
218+
{
219+
ddb = module->modulationAssistant.values[Waveshaper::DRIVE][0];
220+
bias = module->modulationAssistant.values[Waveshaper::BIAS][0];
221+
}
222+
else
223+
{
224+
ddb = module->modulationAssistant.basevalues[Waveshaper::DRIVE];
225+
bias = module->modulationAssistant.basevalues[Waveshaper::BIAS];
226+
}
227+
228+
auto wsop = sst::waveshapers::GetQuadWaveshaper(wstype);
229+
auto damp = pow(10, 0.05 * ddb);
230+
auto d1 = _mm_set1_ps(damp);
231+
232+
lastType = wstype;
233+
lastBias = bias;
234+
lastDrive = ddb;
201235

202-
lastType = wstype;
203-
lastBias = bias;
204-
lastDrive = ddb;
236+
for (const auto &[x, y] : inputSignal)
237+
{
238+
auto ivs = _mm_set1_ps(y + bias);
239+
auto ov1 = ivs;
240+
241+
if (wsop)
242+
{
243+
ov1 = wsop(&wss, ivs, d1);
244+
}
245+
246+
float r alignas(16)[8];
247+
_mm_store_ps(r, ov1);
248+
249+
outputSignal.emplace_back(x, r[0]);
250+
}
251+
}
205252

206-
for (const auto &[x, y] : inputSignal)
207253
{
208-
auto ivs = _mm_set1_ps(y + bias);
209-
auto ov1 = ivs;
254+
initializeWaveshaperRegister(wstype, R);
210255

211-
if (wsop)
256+
for (int i = 0; i < sst::waveshapers::n_waveshaper_registers; ++i)
212257
{
213-
ov1 = wsop(&wss, ivs, d1);
258+
wss.R[i] = _mm_set1_ps(R[i]);
214259
}
215260

216-
float r alignas(16)[8];
217-
_mm_store_ps(r, ov1);
261+
wss.init = _mm_cmpeq_ps(_mm_setzero_ps(), _mm_setzero_ps()); // better way?
218262

219-
outputSignal.emplace_back(x, r[0]);
263+
auto wsop = sst::waveshapers::GetQuadWaveshaper(wstype);
264+
265+
for (float x = -2.0; x < 2.0; x += 2.0 * 0.01)
266+
{
267+
auto ivs = _mm_set1_ps(x);
268+
auto d1 = _mm_set1_ps(1.f);
269+
auto ov1 = ivs;
270+
271+
if (wsop)
272+
{
273+
ov1 = wsop(&wss, ivs, d1);
274+
}
275+
276+
float r alignas(16)[8];
277+
_mm_store_ps(r, ov1);
278+
279+
responseSignal.emplace_back(x, r[0]);
280+
}
220281
}
221282
}
222283

@@ -230,6 +291,62 @@ struct WaveshaperPlotWidget : public rack::widget::TransparentWidget, style::Sty
230291
return ypx;
231292
}
232293

294+
void drawResponse(NVGcontext *vg)
295+
{
296+
if (!module)
297+
return;
298+
299+
if (!module->showTransformCurve)
300+
return;
301+
302+
auto bx = bdwResponse->box;
303+
nvgBeginPath(vg);
304+
auto markCol = style()->getColor(style::XTStyle::PLOT_MARKS);
305+
auto bCol = style()->getColor(style::XTStyle::LED_PANEL);
306+
// bCol.a = 0.95;
307+
nvgStrokeColor(vg, markCol);
308+
nvgFillColor(vg, bCol);
309+
nvgRect(vg, 0, 0, bx.getWidth(), bx.getHeight());
310+
nvgStrokeWidth(vg, 1);
311+
nvgFill(vg);
312+
nvgStroke(vg);
313+
314+
auto xs = 2.0f, ys = 3.8f;
315+
316+
nvgBeginPath(vg);
317+
bool start = true;
318+
for (float x = -xs; x <= xs; x += xs * 0.01)
319+
{
320+
auto px = (x + xs) / (2 * xs) * bx.getWidth();
321+
322+
auto ly = -x;
323+
auto py = (ly + ys) / (2 * ys) * bx.getHeight();
324+
if (start)
325+
nvgMoveTo(vg, px, py);
326+
else
327+
nvgLineTo(vg, px, py);
328+
start = false;
329+
}
330+
nvgStroke(vg);
331+
332+
auto crvCol = style()->getColor(style::XTStyle::PLOT_CURVE);
333+
nvgBeginPath(vg);
334+
nvgStrokeColor(vg, crvCol);
335+
start = true;
336+
for (auto &[x, y] : responseSignal)
337+
{
338+
auto px = (x + xs) / (2 * xs) * bx.getWidth();
339+
340+
auto ly = -y;
341+
auto py = (ly + ys) / (2 * ys) * bx.getHeight();
342+
if (start)
343+
nvgMoveTo(vg, px, py);
344+
else
345+
nvgLineTo(vg, px, py);
346+
start = false;
347+
}
348+
nvgStroke(vg);
349+
}
233350
void drawPlotBackground(NVGcontext *vg)
234351
{
235352
// This will go in layer 0

src/Waveshaper.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ struct Waveshaper : public modules::XTModule
195195
int monoChannelOffset{0};
196196

197197
std::atomic<bool> doDCBlock{true};
198+
std::atomic<bool> showTransformCurve{true};
198199
bool wasDoDCBlock{true};
199200
/*
200201
* This is a bit annoying - i don't want to break 2.0.3.0 patches by turning on
@@ -570,6 +571,7 @@ struct Waveshaper : public modules::XTModule
570571
{
571572
auto ws = json_object();
572573
json_object_set_new(ws, "doDCBlock", json_boolean(doDCBlock));
574+
json_object_set_new(ws, "showTransformCurve", json_boolean(showTransformCurve));
573575
return ws;
574576
}
575577

@@ -585,6 +587,12 @@ struct Waveshaper : public modules::XTModule
585587
{
586588
doDCBlock = true;
587589
}
590+
591+
auto stc = json_object_get(modJ, "showTransformCurve");
592+
if (stc)
593+
{
594+
showTransformCurve = json_boolean_value(stc);
595+
}
588596
}
589597
};
590598

0 commit comments

Comments
 (0)