Skip to content

Commit

Permalink
A mod matrix on the 'main' panel
Browse files Browse the repository at this point in the history
  • Loading branch information
baconpaul committed Dec 30, 2024
1 parent 0de0e08 commit bd11c4a
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 37 deletions.
6 changes: 4 additions & 2 deletions doc/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ PATCH SELECTOR:
- Jog Buttons for next/prev

MAIN/PLAY MODE:
- Global Transpose and Global in-semitone-tuning with LFO routable to tuning
- Global Transpose

CLAP
- An output per OP wher each output is just the solo OP * Main ADSR (and zero if the OP is off)
Expand All @@ -25,4 +25,6 @@ THINGS I DIDNT GET TO FOR 1.0
- 90%-100% of internal nyquist mute fades (maybe ship without this tho)
- Main DAHDSR at DAHD=0 SR=1 means voice lifetime is longest active op mixer DAHDSR and main DAHDSR bypassed
- Note Expressions
- CLAP PolyMod
- CLAP PolyMod
- Global tuning other than octave transpose
- A main LFO
80 changes: 73 additions & 7 deletions src/dsp/matrix_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -421,12 +421,14 @@ struct MixerNode : EnvelopeSupport<Patch::MixerNode>,
}
};

struct OutputNode : EnvelopeSupport<Patch::OutputNode>
struct OutputNode : EnvelopeSupport<Patch::OutputNode>, ModulationSupport<Patch::OutputNode>
{
float output alignas(16)[2][blockSize];
std::array<MixerNode, numOps> &fromArr;
SRProvider sr;

const Patch::OutputNode &outputNode;

const MonoValues &monoValues;
const VoiceValues &voiceValues;

Expand All @@ -436,9 +438,9 @@ struct OutputNode : EnvelopeSupport<Patch::OutputNode>

OutputNode(const Patch::OutputNode &on, std::array<MixerNode, numOps> &f, MonoValues &mv,
const VoiceValues &vv)
: monoValues(mv), voiceValues(vv), sr(mv), fromArr(f), level(on.level), bendUp(on.bendUp),
bendDown(on.bendDown), velSen(on.velSensitivity), EnvelopeSupport(on, mv, vv),
defTrigV(on.defaultTrigger)
: outputNode(on), ModulationSupport(on, mv, vv), monoValues(mv), voiceValues(vv), sr(mv),
fromArr(f), level(on.level), bendUp(on.bendUp), bendDown(on.bendDown),
velSen(on.velSensitivity), EnvelopeSupport(on, mv, vv), defTrigV(on.defaultTrigger)
{
memset(output, 0, sizeof(output));
allowVoiceTrigger = false;
Expand All @@ -447,25 +449,49 @@ struct OutputNode : EnvelopeSupport<Patch::OutputNode>
void attack()
{
defaultTrigger = (TriggerMode)std::round(defTrigV);
bindModulation();
calculateModulation();
envAttack();
}

float finalEnvLevel alignas(16)[blockSize];
void renderBlock()
{
calculateModulation();
for (const auto &from : fromArr)
{
mech::accumulate_from_to<blockSize>(from.output[0], output[0]);
mech::accumulate_from_to<blockSize>(from.output[1], output[1]);
}

envProcess(false);
mech::scale_by<blockSize>(env.outputCache, output[0], output[1]);
mech::copy_from_to<blockSize>(env.outputCache, finalEnvLevel);
mech::scale_by<blockSize>(depthAtten, finalEnvLevel);
mech::scale_by<blockSize>(finalEnvLevel, output[0], output[1]);

// Apply main output
auto lv = level;
auto v = 1.0 - velSen * (1.0 - voiceValues.velocity);
lv = 0.15 * v * lv * lv * lv;
auto v = 1.f - velSen * (1.f - voiceValues.velocity);
lv = 0.15 * std::clamp(v * lv * lv * lv, 0.f, 1.f);
mech::scale_by<blockSize>(lv, output[0], output[1]);

auto pn = panMod;
if (pn != 0.f)
{
pn = (pn + 1) * 0.5;
sdsp::pan_laws::panmatrix_t pmat;
sdsp::pan_laws::stereoEqualPower(pn, pmat);

for (int i = 0; i < blockSize; ++i)
{
auto oL = output[0][i];
auto oR = output[1][i];

output[0][i] = pmat[0] * oL + pmat[2] * oR;
output[1][i] = pmat[3] * oL + pmat[1] * oR;
}
}

#if DEBUG_LEVELS
for (int i = 0; i < blockSize; ++i)
{
Expand All @@ -476,6 +502,46 @@ struct OutputNode : EnvelopeSupport<Patch::OutputNode>
}
#endif
}

float panMod{0.f};
float depthAtten{1.0};

void calculateModulation()
{
depthAtten = 1.f;
attackMod = 0.f;
panMod = 0.f;

if (!anySources)
return;

for (int i = 0; i < numModsPer; ++i)
{
if (sourcePointers[i] &&
(int)outputNode.modtarget[i].value != Patch::MixerNode::TargetID::NONE)
{
// targets: env depth atten, lfo dept atten, direct adjust, env attack, lfo rate
auto dp = depthPointers[i];
if (!dp)
continue;
auto d = *dp;
switch ((Patch::OutputNode::TargetID)outputNode.modtarget[i].value)
{
case Patch::OutputNode::PAN:
panMod += d * *sourcePointers[i];
break;
case Patch::OutputNode::DEPTH_ATTEN:
depthAtten *= 1.0 - d * (1.0 - std::clamp(*sourcePointers[i], 0.f, 1.f));
break;
case Patch::OutputNode::ENV_ATTACK:
attackMod += d * *sourcePointers[i];
break;
default:
break;
}
}
}
}
};
} // namespace baconpaul::six_sines

Expand Down
70 changes: 59 additions & 11 deletions src/synth/patch.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ struct Patch
// These stream
enum TargetID
{
SKIP = -1,
NONE = 0,
DIRECT = 10,
ENV_DEPTH_ATTEN = 20,
Expand All @@ -342,9 +343,11 @@ struct Patch

std::vector<std::pair<TargetID, std::string>> targetList{
{TargetID::NONE, "Off"},
{TargetID::SKIP, ""},
{TargetID::DIRECT, "Ratio"},
{TargetID::ENV_DEPTH_ATTEN, "Env Depth"},
{TargetID::LFO_DEPTH_ATTEN, "LFO Depth"},
{TargetID::ENV_DEPTH_ATTEN, "Env Sens"},
{TargetID::LFO_DEPTH_ATTEN, "LFO Sens"},
{TargetID::SKIP, ""},
{TargetID::ENV_ATTACK, "Env Attack"},
{TargetID::LFO_RATE, "LFO Rate"},
};
Expand Down Expand Up @@ -466,6 +469,7 @@ struct Patch
// These stream
enum TargetID
{
SKIP = -1,
NONE = 0,
DIRECT = 10,
DEPTH_ATTEN = 20,
Expand All @@ -477,9 +481,11 @@ struct Patch

std::vector<std::pair<TargetID, std::string>> targetList{
{TargetID::NONE, "Off"},
{TargetID::SKIP, ""},
{TargetID::DIRECT, "Feedback Level"},
{TargetID::DEPTH_ATTEN, "Mod Depth"},
{TargetID::LFO_DEPTH_ATTEN, "LFO Depth"},
{TargetID::DEPTH_ATTEN, "Mod Sens"},
{TargetID::LFO_DEPTH_ATTEN, "LFO Sens"},
{TargetID::SKIP, ""},
{TargetID::ENV_ATTACK, "Env Attack"},
{TargetID::LFO_RATE, "LFO Rate"},
};
Expand Down Expand Up @@ -554,6 +560,7 @@ struct Patch

enum TargetID
{
SKIP = -1,
NONE = 0,
DIRECT = 10,
DEPTH_ATTEN = 20,
Expand All @@ -565,9 +572,11 @@ struct Patch

std::vector<std::pair<TargetID, std::string>> targetList{
{TargetID::NONE, "Off"},
{TargetID::SKIP, ""},
{TargetID::DIRECT, "Modulation Level"},
{TargetID::DEPTH_ATTEN, "Mod Depth"},
{TargetID::LFO_DEPTH_ATTEN, "LFO Depth"},
{TargetID::DEPTH_ATTEN, "Mod Sens"},
{TargetID::LFO_DEPTH_ATTEN, "LFO Sens"},
{TargetID::SKIP, ""},
{TargetID::ENV_ATTACK, "Env Attack"},
{TargetID::LFO_RATE, "LFO Rate"},
};
Expand Down Expand Up @@ -656,6 +665,7 @@ struct Patch

enum TargetID
{
SKIP = -1,
NONE = 0,
DIRECT = 10,
PAN = 15,
Expand All @@ -669,11 +679,14 @@ struct Patch

std::vector<std::pair<TargetID, std::string>> targetList{
{TargetID::NONE, "Off"},
{TargetID::SKIP, ""},
{TargetID::DIRECT, "Amplitude"},
{TargetID::PAN, "Pan"},
{TargetID::DEPTH_ATTEN, "Env Depth"},
{TargetID::LFO_DEPTH_ATTEN, "LFOENV Depth"},
{TargetID::LFO_DEPTH_ATTEN, "LFOPan Depth"},
{TargetID::SKIP, ""},
{TargetID::DEPTH_ATTEN, "Env Sens"},
{TargetID::LFO_DEPTH_ATTEN, "LFOENV Sens"},
{TargetID::LFO_DEPTH_ATTEN, "LFOPan Sens"},
{TargetID::SKIP, ""},
{TargetID::ENV_ATTACK, "Env Attack"},
{TargetID::LFO_RATE, "LFO Rate"},
};
Expand Down Expand Up @@ -779,9 +792,25 @@ struct Patch
}
};

struct OutputNode : public DAHDSRMixin
struct OutputNode : public DAHDSRMixin, public ModulationMixin
{
static constexpr uint32_t idBase{500};

enum TargetID
{
SKIP = -1,
NONE = 0,
PAN = 15,
DEPTH_ATTEN = 20,

ENV_ATTACK = 40,
};

std::vector<std::pair<TargetID, std::string>> targetList{
{TargetID::NONE, "Off"}, {TargetID::SKIP, ""}, {TargetID::DEPTH_ATTEN, "Env Sens"},
{TargetID::PAN, "Pan"}, {TargetID::SKIP, ""}, {TargetID::ENV_ATTACK, "Env Attack"},
};

OutputNode()
: DAHDSRMixin(name(), id(2), true), level(floatMd()
.asPercent()
Expand Down Expand Up @@ -879,7 +908,19 @@ struct Patch
.withGroupName(name())
.withRange(1, 96)
.withDefault(24)
.withID(id(34)))
.withID(id(34))),

ModulationMixin(name(), id(120)),
modtarget(scpu::make_array_lambda<Param, numModsPer>(
[this](int i)
{
return md_t()
.withName(name() + " Mod Target " + std::to_string(i))
.withGroupName(name())
.withRange(0, 2000)
.withDefault(TargetID::NONE)
.withID(id(150 + i));
}))
{
defaultTrigger.adhocFeatures = Param::AdHocFeatureValues::TRIGGERMODE;
}
Expand All @@ -892,13 +933,20 @@ struct Patch
Param unisonCount, unisonSpread, uniPhaseRand;
Param mpeActive, mpeBendRange;

std::array<Param, numModsPer> modtarget;

std::vector<Param *> params()
{
std::vector<Param *> res{
&level, &velSensitivity, &playMode, &bendUp, &bendDown,
&polyLimit, &defaultTrigger, &portaTime, &pianoModeActive, &unisonCount,
&unisonSpread, &uniPhaseRand, &mpeActive, &mpeBendRange};
appendDAHDSRParams(res);

for (int i = 0; i < numModsPer; ++i)
res.push_back(&modtarget[i]);
appendModulationParams(res);

return res;
}
};
Expand Down
10 changes: 7 additions & 3 deletions src/ui/main-sub-panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@

namespace baconpaul::six_sines::ui
{
MainSubPanel::MainSubPanel(SixSinesEditor &e) : HasEditor(e), DAHDSRComponents()
MainSubPanel::MainSubPanel(SixSinesEditor &e)
: HasEditor(e), DAHDSRComponents(), ModulationComponents()
{
auto &on = editor.patchCopy.output;
voiceTrigerAllowed = false;
setupDAHDSR(e, on);
setupModulation(e, on);

createComponent(editor, *this, on.velSensitivity, velSen, velSenD);
addAndMakeVisible(*velSen);
Expand Down Expand Up @@ -201,6 +203,8 @@ void MainSubPanel::resized()
mpeRange->setBounds(bbx.withHeight(uicLabelHeight));
bbx = bbx.translated(0, uicMargin + uicLabelHeight);
mpeRangeL->setBounds(bbx.withHeight(uicLabelHeight));

layoutModulation(p);
}

void MainSubPanel::setTriggerButtonLabel()
Expand All @@ -225,7 +229,7 @@ void MainSubPanel::showTriggerButtonMenu()
{
auto tmv = (int)std::round(editor.patchCopy.output.defaultTrigger.value);

auto genSet = [w = juce::Component::SafePointer(asComp())](int nv)
auto genSet = [w = juce::Component::SafePointer(this)](int nv)
{
auto that = w;
return [nv, that]()
Expand All @@ -246,7 +250,7 @@ void MainSubPanel::showTriggerButtonMenu()
p.addItem(TriggerModeName[g], true, tmv == g, genSet(g));
}

p.showMenuAsync(juce::PopupMenu::Options().withParentComponent(&asComp()->editor));
p.showMenuAsync(juce::PopupMenu::Options().withParentComponent(&this->editor));
}

void MainSubPanel::setEnabledState()
Expand Down
6 changes: 5 additions & 1 deletion src/ui/main-sub-panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@
#include "patch-data-bindings.h"
#include "six-sines-editor.h"
#include "dahdsr-components.h"
#include "modulation-components.h"
#include "ruled-label.h"

namespace baconpaul::six_sines::ui
{
struct MainSubPanel : juce::Component, HasEditor, DAHDSRComponents<MainSubPanel, Patch::OutputNode>
struct MainSubPanel : juce::Component,
HasEditor,
DAHDSRComponents<MainSubPanel, Patch::OutputNode>,
ModulationComponents<MainSubPanel, Patch::OutputNode>
{
MainSubPanel(SixSinesEditor &);
~MainSubPanel();
Expand Down
Loading

0 comments on commit bd11c4a

Please sign in to comment.