Skip to content

Commit

Permalink
Add a new envelope curve type (exp lerp) (#105)
Browse files Browse the repository at this point in the history
* Add a new envelope curve type (exp lerp)

1. Add a new param metadata setting (exp(A + x(B--A))-C)/D
2. Make the AHDSRShapedSC and DAR envelope work with those shapes
   for SC by adding a new RangeProvider and range provide type
   constexpr
3. Make the modulation depth on cubic decibel work (separately)

* Modulation formatting for Exp curve
  • Loading branch information
baconpaul authored Jun 3, 2024
1 parent 8da120a commit 777e36d
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 31 deletions.
16 changes: 15 additions & 1 deletion include/sst/basic-blocks/modulators/AHDSRShapedSC.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,21 @@ struct AHDSRShapedSC : DiscreteStagesEnvelope<BLOCK_SIZE, RangeProvider>

inline float dPhase(float x)
{
return srProvider->envelope_rate_linear_nowrap(x * base_t::etScale + base_t::etMin);
if constexpr (RangeProvider::phaseStrategy == DPhaseStrategies::ENVTIME_2TWOX)
{
return srProvider->envelope_rate_linear_nowrap(x * base_t::etScale + base_t::etMin);
}

if constexpr (RangeProvider::phaseStrategy == ENVTIME_EXP)
{
auto timeInSeconds =
(std::exp(RangeProvider::A + x * (RangeProvider::B - RangeProvider::A)) -
RangeProvider::C) /
RangeProvider::D;
auto dPhase = BLOCK_SIZE * srProvider->sampleRateInv / timeInSeconds;

return dPhase;
}
}

// from https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp/
Expand Down
26 changes: 23 additions & 3 deletions include/sst/basic-blocks/modulators/DAREnvelope.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,34 @@ struct DAREnvelope : DiscreteStagesEnvelope<BLOCK_SIZE, RangeProvider>
base_t::resetCurrent();
}

// The value here is in natural units by now
inline float dPhase(float x)
{
if constexpr (RangeProvider::phaseStrategy == DPhaseStrategies::ENVTIME_2TWOX)
{
return srProvider->envelope_rate_linear_nowrap(x);
}

if constexpr (RangeProvider::phaseStrategy == ENVTIME_EXP)
{
auto timeInSeconds =
(std::exp(RangeProvider::A + x * (RangeProvider::B - RangeProvider::A)) -
RangeProvider::C) /
RangeProvider::D;
auto dPhase = BLOCK_SIZE * srProvider->sampleRateInv / timeInSeconds;

return dPhase;
}
}

template <bool gated> inline float stepDigital(const float d, const float a, const float r)
{
float target = 0;
switch (this->stage)
{
case base_t::s_delay:
{
phase += srProvider->envelope_rate_linear_nowrap(d);
phase += dPhase(d);
if (phase >= 1)
{
if constexpr (gated)
Expand All @@ -91,7 +111,7 @@ struct DAREnvelope : DiscreteStagesEnvelope<BLOCK_SIZE, RangeProvider>
break;
case base_t::s_attack:
{
phase += srProvider->envelope_rate_linear_nowrap(a);
phase += dPhase(a);
if (phase >= 1)
{
phase = 1;
Expand Down Expand Up @@ -123,7 +143,7 @@ struct DAREnvelope : DiscreteStagesEnvelope<BLOCK_SIZE, RangeProvider>
break;
case base_t::s_release:
{
phase -= srProvider->envelope_rate_linear_nowrap(r);
phase -= dPhase(r);
if (phase <= 0)
{
phase = 0;
Expand Down
47 changes: 43 additions & 4 deletions include/sst/basic-blocks/modulators/DiscreteStagesEnvelope.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,45 @@

namespace sst::basic_blocks::modulators
{

enum DPhaseStrategies
{
/*
* SR Provider provides envelope_rate_linear_nowrap(f) = blockSize * 2^-f / sampleRate
* and we scale into units in the 2^x space
*/
ENVTIME_2TWOX,
/*
* SRProvider isn't consulted. Instead we use an exp table (probably. For now use exp)
*/
ENVTIME_EXP
};
struct TenSecondRange
{
// 0.0039s -> 10s
static constexpr DPhaseStrategies phaseStrategy{ENVTIME_2TWOX};
static constexpr float etMin{-8}, etMax{3.32192809489};
};

struct ThirtyTwoSecondRange
{
// 0.0039s -> 32s
static constexpr DPhaseStrategies phaseStrategy{ENVTIME_2TWOX};
static constexpr float etMin{-8}, etMax{5};
};

struct TwoMinuteRange
{
// 0.0039s -> 120s
static constexpr DPhaseStrategies phaseStrategy{ENVTIME_2TWOX};
static constexpr float etMin{-8}, etMax{6.90689059561};
};

struct TwentyFiveSecondExp
{
static constexpr DPhaseStrategies phaseStrategy{ENVTIME_EXP};
static constexpr double A{0.6931471824646}, B{10.1267113685608}, C{-2.0}, D{1000.0};
};

template <int BLOCK_SIZE, typename RangeProvider> struct DiscreteStagesEnvelope
{
static constexpr float etMin{RangeProvider::etMin}, etMax{RangeProvider::etMax},
Expand Down Expand Up @@ -198,9 +218,28 @@ template <int BLOCK_SIZE, typename RangeProvider> struct DiscreteStagesEnvelope
memset(outputCacheCubed, 0, sizeof(outputCacheCubed));
}

float rateFrom01(float r01) { return r01 * etScale + etMin; }
float rateTo01(float r) { return (r - etMin) / etScale; }
float deltaTo01(float d) { return d / etScale; }
float rateFrom01(float r01)
{
// EXP works entirely in normalized params
if constexpr (RangeProvider::phaseStrategy == DPhaseStrategies::ENVTIME_EXP)
return r01;
else
return r01 * etScale + etMin;
}
float rateTo01(float r)
{
if constexpr (RangeProvider::phaseStrategy == DPhaseStrategies::ENVTIME_EXP)
return r;
else
return (r - etMin) / etScale;
}
float deltaTo01(float d)
{
if constexpr (RangeProvider::phaseStrategy == DPhaseStrategies::ENVTIME_EXP)
return d;
else
return d / etScale;
}
};
} // namespace sst::basic_blocks::modulators

Expand Down
Loading

0 comments on commit 777e36d

Please sign in to comment.