From 63a542a4e50b6608cdae92d366f9aef36df66bc0 Mon Sep 17 00:00:00 2001 From: Paul Walker Date: Wed, 25 Dec 2024 18:07:46 -0500 Subject: [PATCH] Add infrastructure for waveforms; just add sin(x)^5 for now --- src/dsp/op_source.h | 5 ++- src/dsp/sintable.h | 72 ++++++++++++++++++++++++++++-------- src/synth/patch.h | 19 ++++++++-- src/ui/patch-data-bindings.h | 5 +++ src/ui/source-sub-panel.cpp | 57 ++++++++++++++++++++++++++-- src/ui/source-sub-panel.h | 8 +++- 6 files changed, 141 insertions(+), 25 deletions(-) diff --git a/src/dsp/op_source.h b/src/dsp/op_source.h index 2e269a9..50ef97e 100644 --- a/src/dsp/op_source.h +++ b/src/dsp/op_source.h @@ -43,6 +43,7 @@ struct alignas(16) OpSource : public EnvelopeSupport, bool keytrack{true}; const float &ratio, &activeV, &envToRatio, &lfoToRatio, &lfoByEnv; // in frequency multiple + const float &waveForm; bool active{false}; // todo waveshape @@ -53,7 +54,7 @@ struct alignas(16) OpSource : public EnvelopeSupport, OpSource(const Patch::SourceNode &sn, const MonoValues &mv, const VoiceValues &vv) : monoValues(mv), voiceValues(vv), EnvelopeSupport(sn, mv, vv), LFOSupport(sn, mv), ratio(sn.ratio), activeV(sn.active), envToRatio(sn.envToRatio), lfoToRatio(sn.lfoToRatio), - lfoByEnv(sn.envLfoSum) + lfoByEnv(sn.envLfoSum), waveForm(sn.waveForm) { reset(); } @@ -67,6 +68,8 @@ struct alignas(16) OpSource : public EnvelopeSupport, phase = 4 << 27; fbVal[0] = 0.f; fbVal[1] = 0.f; + auto wf = (SinTable::WaveForm)std::round(waveForm); + st.setWaveForm(wf); } void zeroInputs() diff --git a/src/dsp/sintable.h b/src/dsp/sintable.h index 4a443ff..1e81079 100644 --- a/src/dsp/sintable.h +++ b/src/dsp/sintable.h @@ -16,6 +16,7 @@ #ifndef BACONPAUL_SIX_SINES_DSP_SINTABLE_H #define BACONPAUL_SIX_SINES_DSP_SINTABLE_H +#include #include "configuration.h" #include @@ -23,26 +24,53 @@ namespace baconpaul::six_sines { struct SinTable { + enum WaveForm + { + SIN = 0, // these stream so you know.... + SIN_FIFTH, + + NUM_WAVEFORMS + }; + static constexpr size_t nPoints{1 << 12}; - float quadrantTable[4][nPoints + 1]; - float dQuadrantTable[4][nPoints + 1]; + float quadrantTable[NUM_WAVEFORMS][4][nPoints + 1]; + float dQuadrantTable[NUM_WAVEFORMS][4][nPoints + 1]; float cubicHermiteCoefficients[4][nPoints]; float linterpCoefficients[2][nPoints]; - SIMD_M128 simdQuad alignas(16)[4 * nPoints]; // for each quad it is q, q+1, dq + 1 - SIMD_M128 simdCubic alignas(16)[nPoints]; // it is cq, cq+1, cdq, cd1+1 + SIMD_M128 + simdFullQuad alignas(16)[NUM_WAVEFORMS][4 * nPoints]; // for each quad it is q, q+1, dq + 1 + SIMD_M128 *simdQuad; + SIMD_M128 simdCubic alignas(16)[nPoints]; // it is cq, cq+1, cdq, cd1+1 SinTable() { + simdQuad = simdFullQuad[0]; + memset(quadrantTable, 0, sizeof(quadrantTable)); + memset(dQuadrantTable, 0, sizeof(dQuadrantTable)); + + // Waveform 0: Pure sine + for (int i = 0; i < nPoints + 1; ++i) + { + for (int Q = 0; Q < 4; ++Q) + { + auto s = sin((1.0 * i / (nPoints - 1) + Q) * M_PI / 2.0); + auto c = cos((1.0 * i / (nPoints - 1) + Q) * M_PI / 2.0); + quadrantTable[0][Q][i] = s; + dQuadrantTable[0][Q][i] = c / (nPoints - 1); + } + } + + // Waveform 1: sin(x) ^ 5. Derivative is 5 sin(x)^4 cos(x) for (int i = 0; i < nPoints + 1; ++i) { - for (int j = 0; j < 4; ++j) + for (int Q = 0; Q < 4; ++Q) { - auto s = sin((1.0 * i / (nPoints - 1) + j) * M_PI / 2.0); - auto c = cos((1.0 * i / (nPoints - 1) + j) * M_PI / 2.0); - quadrantTable[j][i] = s; - dQuadrantTable[j][i] = c / (nPoints - 1); + auto s = sin((1.0 * i / (nPoints - 1) + Q) * M_PI / 2.0); + auto c = cos((1.0 * i / (nPoints - 1) + Q) * M_PI / 2.0); + quadrantTable[1][Q][i] = s * s * s * s * s; + dQuadrantTable[1][Q][i] = 5 * s * s * s * s * c / (nPoints - 1); } } @@ -64,16 +92,20 @@ struct SinTable linterpCoefficients[1][i] = t; } + // Load the SIMD buffers for (int i = 0; i < nPoints; ++i) { - for (int j = 0; j < 4; ++j) + for (int WF = 0; WF < NUM_WAVEFORMS; ++WF) { - float r alignas(16)[4]; - r[0] = quadrantTable[j][i]; - r[1] = dQuadrantTable[j][i]; - r[2] = quadrantTable[j][i + 1]; - r[3] = dQuadrantTable[j][i + 1]; - simdQuad[nPoints * j + i] = SIMD_MM(load_ps)(r); + for (int Q = 0; Q < 4; ++Q) + { + float r alignas(16)[4]; + r[0] = quadrantTable[WF][Q][i]; + r[1] = dQuadrantTable[WF][Q][i]; + r[2] = quadrantTable[WF][Q][i + 1]; + r[3] = dQuadrantTable[WF][Q][i + 1]; + simdFullQuad[WF][nPoints * Q + i] = SIMD_MM(load_ps)(r); + } } { @@ -87,6 +119,14 @@ struct SinTable } } + void setWaveForm(WaveForm wf) + { + auto stwf = size_t(wf); + if (stwf >= NUM_WAVEFORMS) // mostly remove ine during dev + stwf = 0; + simdQuad = simdFullQuad[stwf]; + } + static constexpr double frToPhase{(1 << 26) / gSampleRate}; uint32_t dPhase(float fr) { diff --git a/src/synth/patch.h b/src/synth/patch.h index 52a84c6..854f841 100644 --- a/src/synth/patch.h +++ b/src/synth/patch.h @@ -26,6 +26,7 @@ #include "sst/basic-blocks/params/ParamMetadata.h" #include "sst/basic-blocks/modulators/DAHDSREnvelope.h" #include "synth/matrix_index.h" +#include "dsp/sintable.h" namespace baconpaul::six_sines { @@ -46,7 +47,7 @@ struct Param operator const float &() const { return value; } uint64_t adhocFeatures{0}; - enum AdHocFeatureValues + enum AdHocFeatureValues : uint64_t { ENVTIME = 1 << 0 // tag for ADSR envs we changed version 2-3 }; @@ -310,7 +311,16 @@ struct Patch .withRange(0, 1) .withDefault(0) .withUnorderedMapFormatting({{0, "+"}, {1, "x"}})), - + waveForm(intMd() + .withName(name(idx) + " Waveform") + .withGroupName(name(idx)) + .withID(id(5, idx)) + .withRange(0, SinTable::WaveForm::NUM_WAVEFORMS - 1) + .withDefault(0) + .withUnorderedMapFormatting({ + {SinTable::WaveForm::SIN, "Sin"}, + {SinTable::WaveForm::SIN_FIFTH, "Sin^5"}, + })), DAHDSRMixin(name(idx), id(100, idx), false), LFOMixin(name(idx), id(45, idx)) { } @@ -325,9 +335,12 @@ struct Patch Param lfoToRatio; Param envLfoSum; + Param waveForm; + std::vector params() { - std::vector res{&ratio, &active, &envToRatio, &lfoToRatio, &envLfoSum}; + std::vector res{&ratio, &active, &envToRatio, + &lfoToRatio, &envLfoSum, &waveForm}; appendDAHDSRParams(res); appendLFOParams(res); return res; diff --git a/src/ui/patch-data-bindings.h b/src/ui/patch-data-bindings.h index 6d0fb9a..4bdb081 100644 --- a/src/ui/patch-data-bindings.h +++ b/src/ui/patch-data-bindings.h @@ -95,6 +95,8 @@ struct PatchDiscrete : jdat::Discrete SixSinesEditor &editor; uint32_t pid; Param *p{nullptr}; + std::function onGuiSetValue{nullptr}; + PatchDiscrete(SixSinesEditor &e, uint32_t id) : editor(e), pid(id) { if (e.patchCopy.paramMap.find(id) == e.patchCopy.paramMap.end()) @@ -126,6 +128,9 @@ struct PatchDiscrete : jdat::Discrete p->value = f; editor.uiToAudio.push({Synth::UIToAudioMsg::Action::SET_PARAM, pid, static_cast(f)}); editor.flushOperator(); + + if (onGuiSetValue) + onGuiSetValue(); } void setValueFromModel(const int &f) override { p->value = f; } int getDefaultValue() const override diff --git a/src/ui/source-sub-panel.cpp b/src/ui/source-sub-panel.cpp index 6bf60a2..4a7806b 100644 --- a/src/ui/source-sub-panel.cpp +++ b/src/ui/source-sub-panel.cpp @@ -16,12 +16,42 @@ #include "source-sub-panel.h" #include "patch-data-bindings.h" #include "ui-constants.h" +#include "dsp/sintable.h" // for drawing namespace baconpaul::six_sines::ui { SourceSubPanel::SourceSubPanel(SixSinesEditor &e) : HasEditor(e) { setSelectedIndex(0); }; SourceSubPanel::~SourceSubPanel() {} +struct WavPainter : juce::Component +{ + const Param &wf; + SinTable st; + WavPainter(const Param &w) : wf(w) {} + + void paint(juce::Graphics &g) + { + st.setWaveForm((SinTable::WaveForm)std::round(wf.value)); + uint32_t phase{0}; + int nPixels{getWidth()}; + auto dPhase = (1 << 27) / (nPixels - 1); + auto p = juce::Path(); + for (int i = 0; i < nPixels; ++i) + { + auto sv = st.at(phase); + auto x = i; + auto y = (1 - (sv + 1) * 0.5) * getHeight(); + if (i == 0) + p.startNewSubPath(x, y); + else + p.lineTo(x, y); + phase += dPhase; + } + g.setColour(juce::Colours::white); + g.strokePath(p, juce::PathStrokeType(1)); + } +}; + void SourceSubPanel::setSelectedIndex(size_t idx) { index = idx; @@ -53,6 +83,17 @@ void SourceSubPanel::setSelectedIndex(size_t idx) modTitle->setText("Depth"); addAndMakeVisible(*modTitle); + wavTitle = std::make_unique(); + wavTitle->setText("Wave"); + addAndMakeVisible(*wavTitle); + + createComponent(editor, *this, sn.waveForm, wavButton, wavButtonD); + addAndMakeVisible(*wavButton); + wavButtonD->onGuiSetValue = [this]() { wavPainter->repaint(); }; + + wavPainter = std::make_unique(sn.waveForm); + addAndMakeVisible(*wavPainter); + resized(); } @@ -68,13 +109,21 @@ void SourceSubPanel::resized() auto depx = r.getX() + 2 * uicMargin; auto depy = r.getY(); - auto xtraW = 4; - positionTitleLabelAt(depx, depy, uicKnobSize + 2 * xtraW, modTitle); + positionTitleLabelAt(depx, depy, uicKnobSize * 2 + uicMargin, modTitle); - depx += xtraW; depy += uicTitleLabelHeight; positionKnobAndLabel(depx, depy, envToRatio, envToRatioL); - depy += uicLabeledKnobHeight + uicMargin; + depx += uicKnobSize + uicMargin; positionKnobAndLabel(depx, depy, lfoToRatio, lfoToRatioL); + + depx = r.getX() + 2 * uicMargin; + depy += uicLabeledKnobHeight + uicMargin; + + positionTitleLabelAt(depx, depy, uicKnobSize * 2 + uicMargin, wavTitle); + depy += uicTitleLabelHeight; + wavButton->setBounds(depx, depy, uicKnobSize * 2 + uicMargin, uicLabelHeight); + + depy += uicLabelHeight + uicMargin; + wavPainter->setBounds(depx, depy, uicKnobSize * 2 + uicMargin, uicLabelHeight * 2.5); } } // namespace baconpaul::six_sines::ui \ No newline at end of file diff --git a/src/ui/source-sub-panel.h b/src/ui/source-sub-panel.h index 33d4a6e..aa525f4 100644 --- a/src/ui/source-sub-panel.h +++ b/src/ui/source-sub-panel.h @@ -17,6 +17,7 @@ #define BACONPAUL_SIX_SINES_UI_SOURCE_SUB_PANEL_H #include +#include "sst/jucegui/components/JogUpDownButton.h" #include "six-sines-editor.h" #include "dahdsr-components.h" #include "lfo-components.h" @@ -50,7 +51,12 @@ struct SourceSubPanel : juce::Component, std::unique_ptr lfoMul; std::unique_ptr lfoMulD; - std::unique_ptr modTitle; + std::unique_ptr wavButton; + std::unique_ptr wavButtonD; + + std::unique_ptr modTitle, wavTitle; + + std::unique_ptr wavPainter; }; } // namespace baconpaul::six_sines::ui #endif // MAIN_SUB_PANEL_H