Skip to content

Commit

Permalink
Move to SC-style envelope (#4)
Browse files Browse the repository at this point in the history
1. Add delay stage to SC-style envelope
2. Move to 25 second with zero style
3. Add shape and shape knobs
4. Do patch migration so old patches load and convert
  • Loading branch information
baconpaul authored Dec 24, 2024
1 parent bf58bcc commit dabfe09
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 38 deletions.
4 changes: 1 addition & 3 deletions doc/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ GENERAL AND CLEANUPS
- AM is somehow not quiet right. Signal to zero seems 'no modulation' not 'no output'
- Don't send VU etc when editor not attached
- Edit area says "click a knbo to edit on startup"
- LFO in Pulse and S&H need a tiny little LPF to avoid super-clicka
- LFO in Pulse and S&H need a tiny little LPF to avoid super-clicka on modulation nodes

INFRASTRUCTURE:
- mac signing


2 changes: 1 addition & 1 deletion libs/sst/sst-basic-blocks
1 change: 1 addition & 0 deletions src/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@ inline std::string fileTrunc(const std::string &f)

#define SXSNLOG(...) \
std::cout << fileTrunc(__FILE__) << ":" << __LINE__ << " " << __VA_ARGS__ << std::endl;
#define SXSNV(x) " " << #x << "=" << x

#endif // CONFIGURATION_H
20 changes: 13 additions & 7 deletions src/dsp/node_support.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#include <cstring>
#include <string.h>

#include "sst/basic-blocks/modulators/DAHDSREnvelope.h"
#include "sst/basic-blocks/modulators/AHDSRShapedSC.h"
#include "sst/basic-blocks/modulators/SimpleLFO.h"

#include "dsp/sr_provider.h"
Expand All @@ -34,21 +34,26 @@ template <typename T> struct EnvelopeSupport
SRProvider sr;

const float &delay, &attackv, &hold, &decay, &sustain, &release, &powerV;
const float &ash, &dsh, &rsh;
EnvelopeSupport(const T &mn, const MonoValues &mv)
: sr(mv), env(&sr), delay(mn.delay), attackv(mn.attack), hold(mn.hold), decay(mn.decay),
sustain(mn.sustain), release(mn.release), powerV(mn.envPower)
sustain(mn.sustain), release(mn.release), powerV(mn.envPower), ash(mn.aShape),
dsh(mn.dShape), rsh(mn.rShape)
{
}

bool active{true}, constantEnv{false};
sst::basic_blocks::modulators::DAHDSREnvelope<SRProvider, blockSize> env;
using range_t = sst::basic_blocks::modulators::TwentyFiveSecondExp;
using env_t = sst::basic_blocks::modulators::AHDSRShapedSC<SRProvider, blockSize, range_t>;
env_t env;

void envAttack()
{
env.initializeLuts();
active = powerV > 0.5;

auto mn = sst::basic_blocks::modulators::TenSecondRange::etMin + 0.001;
auto mx = sst::basic_blocks::modulators::TenSecondRange::etMax - 0.001;
auto mn = 0.0001;
auto mx = 1 - mn;

if (decay < mn && attackv < mn && hold < mn && delay < mn && release > mx)
{
Expand All @@ -60,7 +65,7 @@ template <typename T> struct EnvelopeSupport
}

if (active && !constantEnv)
env.attack(delay);
env.attackFromWithDelay(0.f, delay, attackv);
else if (constantEnv)
{
for (int i = 0; i < blockSize; ++i)
Expand All @@ -74,7 +79,8 @@ template <typename T> struct EnvelopeSupport
if (!active || constantEnv)
return;

env.processBlockScaledAD(delay, attackv, hold, decay, sustain, release, gated);
env.processBlockWithDelay(delay, attackv, hold, decay, sustain, release, ash, dsh, rsh,
gated, true);
}
};

Expand Down
7 changes: 4 additions & 3 deletions src/dsp/op_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,11 @@ struct alignas(16) OpSource : public EnvelopeSupport<Patch::SourceNode>,
}
envProcess(gated);
lfoProcess();
auto lfoFac = lfoByEnv > 0.5 ? env.output : 1.f;
auto lfoFac = lfoByEnv > 0.5 ? env.outputCache[blockSize - 1] : 1.f;

auto rf = monoValues.twoToTheX.twoToThe(ratio + envToRatio * env.output +
lfoFac * lfoToRatio * lfo.outputBlock[0]);
auto rf =
monoValues.twoToTheX.twoToThe(ratio + envToRatio * env.outputCache[blockSize - 1] +
lfoFac * lfoToRatio * lfo.outputBlock[0]);
if (priorRF < -10000)
priorRF = rf;
auto dRF = (priorRF - rf) / blockSize;
Expand Down
4 changes: 3 additions & 1 deletion src/dsp/sr_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ struct SRProvider
{
return (blockSize / gSampleRate) * monoValues.twoToTheX.twoToThe(-f);
}
static constexpr float samplerate{gSampleRate};
static constexpr double samplerate{gSampleRate};
static constexpr double sampleRate{gSampleRate};
static constexpr double sampleRateInv{1.0 / gSampleRate};
};
} // namespace baconpaul::six_sines
#endif // SR_PROVIDER_H
48 changes: 46 additions & 2 deletions src/synth/patch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ std::string Patch::toState() const
TiXmlDocument doc;
TiXmlElement rootNode("patch");
rootNode.SetAttribute("id", "org.baconpaul.six-sines");
rootNode.SetAttribute("version", "2");
rootNode.SetAttribute("version", Patch::patchVersion);

TiXmlElement paramsNode("params");

Expand Down Expand Up @@ -80,7 +80,7 @@ bool Patch::fromState(const std::string &idata)
SXSNLOG("No Version");
return false;
}
if (ver != 2)
if (ver < 2 || ver > Patch::patchVersion)
{
SXSNLOG("Unknown version " << ver);
return false;
Expand Down Expand Up @@ -108,6 +108,8 @@ bool Patch::fromState(const std::string &idata)
}
else
{
auto *param = it->second;
value = migrateParamValueFromVersion(param, value, ver);
it->second->value = value;
}
}
Expand Down Expand Up @@ -154,6 +156,8 @@ bool Patch::fromStateV1(const std::string &idata)
}
else
{
auto *param = it->second;
vv = migrateParamValueFromVersion(param, vv, 1);
it->second->value = vv;
}
}
Expand All @@ -164,4 +168,44 @@ bool Patch::fromStateV1(const std::string &idata)
return true;
}

float Patch::migrateParamValueFromVersion(Param *p, float value, uint32_t version)
{
if ((p->adhocFeatures & Param::AdHocFeatureValues::ENVTIME) && version <= 2)
{
/*
* This is a gross way to do this but really its just to not break
* Jacky's patches from the very first weekend, so...
*/
static auto oldStyle = md_t().asFloat().asLog2SecondsRange(
sst::basic_blocks::modulators::TenSecondRange::etMin,
sst::basic_blocks::modulators::TenSecondRange::etMax);
;
if (value < oldStyle.minVal + 0.0001)
return 0.f;
if (value > oldStyle.maxVal - 0.0001)
return 1.f;
auto osv = oldStyle.valueToString(value);
if (osv.has_value())
{
std::string em;
auto nsv = p->meta.valueFromString(*osv, em);
if (nsv.has_value())
{
SXSNLOG("Converting version " << version << " node '" << p->meta.name
<< "' val=" << value << " -> " << *nsv);
value = *nsv;
}
else
{
value = 0.f;
}
}
else
{
value = 0.f;
}
}
return value;
}

} // namespace baconpaul::six_sines
60 changes: 46 additions & 14 deletions src/synth/patch.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,17 @@ struct Param
}

operator const float &() const { return value; }

uint64_t adhocFeatures{0};
enum AdHocFeatureValues
{
ENVTIME = 1 << 0 // tag for ADSR envs we changed version 2-3
};
};

struct Patch
{
static constexpr uint32_t patchVersion{3};
std::vector<const Param *> params;
std::unordered_map<uint32_t, Param *> paramMap;

Expand All @@ -57,11 +64,7 @@ struct Patch
static md_t floatMd() { return md_t().asFloat().withFlags(floatFlags); }
static md_t floatEnvRateMd()
{
return md_t()
.asFloat()
.withFlags(floatFlags)
.asLog2SecondsRange(sst::basic_blocks::modulators::TenSecondRange::etMin,
sst::basic_blocks::modulators::TenSecondRange::etMax);
return md_t().asFloat().withFlags(floatFlags).as25SecondExpTime();
}
static md_t boolMd() { return md_t().asBool().withFlags(boolFlags); }
static md_t intMd() { return md_t().asInt().withFlags(boolFlags); }
Expand All @@ -81,8 +84,9 @@ struct Patch
{
if (paramMap.find(p->meta.id) != paramMap.end())
{
SXSNLOG("Duplicate param id " << p->meta.id << " at " << p->meta.name);
SXSNLOG("Collision with " << paramMap[p->meta.id]->meta.name);
SXSNLOG("Duplicate param id " << p->meta.id);
SXSNLOG(" - New Param : '" << p->meta.name << "'");
SXSNLOG(" - Other Param : '" << paramMap[p->meta.id]->meta.name << "'");
std::terminate();
}
paramMap.emplace(p->meta.id, p);
Expand Down Expand Up @@ -185,27 +189,26 @@ struct Patch

struct DAHDSRMixin
{
using tsr = sst::basic_blocks::modulators::TenSecondRange;
DAHDSRMixin(const std::string name, int id0, bool longAdsr)
: delay(floatEnvRateMd()
.withName(name + " Env Delay")
.withGroupName(name)
.withDefault(tsr::etMin)
.withDefault(0.f)
.withID(id0 + 0)),
attack(floatEnvRateMd()
.withName(name + " Env Attack")
.withGroupName(name)
.withDefault(longAdsr ? -7.f : tsr::etMin)
.withDefault(longAdsr ? 0.05 : 0.f)
.withID(id0 + 1)),
hold(floatEnvRateMd()
.withName(name + " Env Hold")
.withGroupName(name)
.withDefault(tsr::etMin)
.withDefault(0.f)
.withID(id0 + 2)),
decay(floatEnvRateMd()
.withName(name + " Env Decay")
.withGroupName(name)
.withDefault(longAdsr ? 0.f : tsr::etMin)
.withDefault(longAdsr ? 0.3 : 0.f)
.withID(id0 + 3)),
sustain(floatMd()
.asPercent()
Expand All @@ -216,17 +219,41 @@ struct Patch
release(floatEnvRateMd()
.withName(name + " Env Release")
.withGroupName(name)
.withDefault(longAdsr ? -2.f : tsr::etMax)
.withDefault(longAdsr ? 0.4 : 1.f)
.withID(id0 + 5)),
envPower(boolMd()
.withName(name + " Env Power")
.withGroupName(name)
.withDefault(true)
.withID(id0 + 6))
.withID(id0 + 6)),
aShape(floatMd()
.asPercentBipolar()
.withName(name + " Attack Shape")
.withGroupName(name)
.withDefault(0)
.withID(id0 + 7)),
dShape(floatMd()
.asPercentBipolar()
.withName(name + " Decay Shape")
.withGroupName(name)
.withDefault(0)
.withID(id0 + 8)),
rShape(floatMd()
.asPercentBipolar()
.withName(name + " Release Shape")
.withGroupName(name)
.withDefault(0)
.withID(id0 + 9))
{
delay.adhocFeatures = Param::AdHocFeatureValues::ENVTIME;
attack.adhocFeatures = Param::AdHocFeatureValues::ENVTIME;
hold.adhocFeatures = Param::AdHocFeatureValues::ENVTIME;
decay.adhocFeatures = Param::AdHocFeatureValues::ENVTIME;
release.adhocFeatures = Param::AdHocFeatureValues::ENVTIME;
}

Param delay, attack, hold, decay, sustain, release, envPower;
Param aShape, dShape, rShape;

void appendDAHDSRParams(std::vector<Param *> &res)
{
Expand All @@ -237,6 +264,9 @@ struct Patch
res.push_back(&sustain);
res.push_back(&release);
res.push_back(&envPower);
res.push_back(&aShape);
res.push_back(&dShape);
res.push_back(&rShape);
}
};

Expand Down Expand Up @@ -546,6 +576,8 @@ struct Patch

private:
bool fromStateV1(const std::string &);

float migrateParamValueFromVersion(Param *p, float value, uint32_t version);
};
} // namespace baconpaul::six_sines
#endif // PATCH_H
5 changes: 1 addition & 4 deletions src/synth/synth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,7 @@ void Synth::process(const clap_output_events_t *outq)
mech::accumulate_from_to<blockSize>(cvoice->output[0], lOutput[0]);
mech::accumulate_from_to<blockSize>(cvoice->output[1], lOutput[1]);

if (cvoice->out.env.stage >
sst::basic_blocks::modulators::DAHDSREnvelope<SRProvider,
blockSize>::s_release ||
cvoice->fadeBlocks == 0)
if (cvoice->out.env.stage > OutputNode::env_t::s_release || cvoice->fadeBlocks == 0)
{
responder.doVoiceEndCallback(cvoice);
cvoice = removeFromVoiceList(cvoice);
Expand Down
20 changes: 18 additions & 2 deletions src/ui/dahdsr-components.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ template <typename Comp, typename Patch> struct DAHDSRComponents
mk(v.sustain, 4, "S");
mk(v.release, 5, "R");

auto c = asComp();
createComponent(e, *c, v.aShape, shapes[0], shapesD[0]);
createComponent(e, *c, v.dShape, shapes[1], shapesD[1]);
createComponent(e, *c, v.rShape, shapes[2], shapesD[2]);

c->addAndMakeVisible(*shapes[0]);
c->addAndMakeVisible(*shapes[1]);
c->addAndMakeVisible(*shapes[2]);

titleLab = std::make_unique<RuledLabel>();
titleLab->setText("Envelope");
asComp()->addAndMakeVisible(*titleLab);
Expand All @@ -71,17 +80,24 @@ template <typename Comp, typename Patch> struct DAHDSRComponents
{
if (!slider[i])
continue;
slider[i]->setBounds(bx.withHeight(q));
slider[i]->setBounds(bx.withHeight(q).withTrimmedTop(uicSliderWidth));
if (i % 2)
{
shapes[i / 2]->setBounds(bx.withHeight(uicSliderWidth));
}
lab[i]->setBounds(bx.withTrimmedTop(q + uicLabelGap));
bx = bx.translated(uicSliderWidth, 0);
}
bx = bx.translated(-uicSliderWidth, 0);
return juce::Rectangle<int>(x, y, 0, 0).withLeft(bx.getRight()).withBottom(bx.getBottom());
}

static constexpr int nels{6}; // dahdsr
static constexpr int nels{6}; // dahdsr
static constexpr int nShape{3}; // no shape for delay jp;d or release
std::array<std::unique_ptr<jcmp::VSlider>, nels> slider;
std::array<std::unique_ptr<PatchContinuous>, nels> sliderD;
std::array<std::unique_ptr<jcmp::Knob>, nShape> shapes;
std::array<std::unique_ptr<PatchContinuous>, nShape> shapesD;
std::array<std::unique_ptr<jcmp::Label>, nels> lab;
std::unique_ptr<RuledLabel> titleLab;
};
Expand Down
2 changes: 1 addition & 1 deletion src/ui/six-sines-editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ struct MenuValueTypein : HasEditor, juce::PopupMenu::CustomComponent, juce::Text

MenuValueTypein(SixSinesEditor &editor,
juce::Component::SafePointer<jcmp::ContinuousParamEditor> under)
: HasEditor(editor), underComp(under)
: juce::PopupMenu::CustomComponent(false), HasEditor(editor), underComp(under)
{
textEditor = std::make_unique<juce::TextEditor>();
textEditor->setWantsKeyboardFocus(true);
Expand Down

0 comments on commit dabfe09

Please sign in to comment.