Skip to content

Commit

Permalink
Update DoubleLoopCymbal (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryukau committed Sep 9, 2024
1 parent f57bf7d commit d973a85
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 122 deletions.
96 changes: 39 additions & 57 deletions DoubleLoopCymbal/source/dsp/delay.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,29 @@ namespace SomeDSP {

template<typename Sample> class ExpDecay {
public:
// static constexpr Sample alpha = Sample(1.1920928955078125e-7); // 2^-23.
// static constexpr Sample normalizeGain = Sample(15.942385152878742); // -log(2^-23).

Sample value = 0;
Sample alpha = 0;

void setTime(Sample decayTimeInSamples, bool sustain = false)
void setTime(Sample decayTimeInSamples)
{
constexpr auto eps = Sample(std::numeric_limits<float>::epsilon());
alpha = std::pow(eps, Sample(1) / decayTimeInSamples);
}

void reset() { value = 0; }
void trigger(Sample gain = Sample(1)) { value = gain; }
Sample process() { return value *= alpha; }
};

template<typename Sample> class ExpSREnvelope {
public:
Sample value = 0;
Sample alpha = 0;

void setTime(Sample decayTimeInSamples, bool sustain)
{
constexpr auto eps = Sample(std::numeric_limits<float>::epsilon());
alpha = sustain ? Sample(1) : std::pow(eps, Sample(1) / decayTimeInSamples);
Expand Down Expand Up @@ -304,55 +323,16 @@ template<typename Sample> class EmaLowShelf {
}
};

template<typename Sample> class AdaptiveNotchCPZ {
public:
static constexpr Sample mu = Sample(2) / Sample(1024);
Sample alpha = Sample(-2);

Sample v1 = 0;
Sample v2 = 0;

void reset()
{
alpha = Sample(-2); // 0 Hz as initial guess.

v1 = 0;
v2 = 0;
}

Sample process(Sample input, Sample narrowness)
{
const auto a1 = narrowness * alpha;
const auto a2 = narrowness * narrowness;
auto gain = alpha >= 0 ? (Sample(1) + a1 + a2) / (Sample(2) + alpha)
: (Sample(1) - a1 + a2) / (Sample(2) - alpha);

constexpr auto clip = Sample(1) / std::numeric_limits<Sample>::epsilon();
const auto x0 = std::clamp(input, -clip, clip);
auto v0 = x0 - a1 * v1 - a2 * v2;
const auto y0 = v0 + alpha * v1 + v2;
const auto s0
= (Sample(1) - narrowness) * v0 - narrowness * (Sample(1) - narrowness) * v2;
constexpr auto bound = Sample(2);
alpha = std::clamp(alpha - y0 * s0 * mu, -bound, bound);

v2 = v1;
v1 = v0;

return y0 * gain;
}
};

template<typename Sample, size_t nAllpass, size_t nAdaptiveNotch> class SerialAllpass {
template<typename Sample, size_t nAllpass> class SerialAllpass {
private:
std::array<Sample, nAllpass> buffer{};
std::array<Delay<Sample>, nAllpass> delay;
std::array<EmaHighShelf<Sample>, nAllpass> lowpass;
std::array<EmaLowShelf<Sample>, nAllpass> highpass;

public:
std::array<AdaptiveNotchCPZ<Sample>, nAdaptiveNotch> notch;
static constexpr size_t size = nAllpass;
size_t nDelay = nAllpass;
std::array<Sample, nAllpass> timeInSamples{};

void setup(Sample maxTimeSamples)
Expand All @@ -366,7 +346,6 @@ template<typename Sample, size_t nAllpass, size_t nAdaptiveNotch> class SerialAl
for (auto &x : delay) x.reset();
for (auto &x : lowpass) x.reset();
for (auto &x : highpass) x.reset();
for (auto &x : notch) x.reset();
}

void applyGain(Sample gain)
Expand All @@ -376,14 +355,25 @@ template<typename Sample, size_t nAllpass, size_t nAdaptiveNotch> class SerialAl

Sample sum(Sample altSignMix)
{
// // TODO
// Sample sumAlt = Sample(0);
// Sample sign = Sample(1);
// for (const auto &x : buffer) {
// sumAlt += x * sign;
// sign = -sign;
// }
// Sample sumDirect = std::accumulate(buffer.begin(), buffer.end(), Sample(0));
// return std::lerp(sumDirect, sumAlt, altSignMix) / (Sample(2) * nAllpass);

Sample sumAlt = Sample(0);
Sample sign = Sample(1);
for (const auto &x : buffer) {
sumAlt += x * sign;
for (size_t i = 0; i < nDelay; ++i) {
sumAlt += buffer[i] * sign;
sign = -sign;
}
Sample sumDirect = std::accumulate(buffer.begin(), buffer.end(), Sample(0));
return std::lerp(sumDirect, sumAlt, altSignMix) / (Sample(2) * nAllpass);
Sample sumDirect
= std::accumulate(buffer.begin(), buffer.begin() + nDelay, Sample(0));
return std::lerp(sumDirect, sumAlt, altSignMix) / (Sample(2) * nDelay);
}

Sample process(
Expand All @@ -394,12 +384,9 @@ template<typename Sample, size_t nAllpass, size_t nAdaptiveNotch> class SerialAl
Sample lowShelfGain,
Sample gain,
Sample pitchRatio,
Sample timeModAmount,
size_t nNotch,
Sample notchMix,
Sample notchNarrowness)
Sample timeModAmount)
{
for (size_t idx = 0; idx < nAllpass; ++idx) {
for (size_t idx = 0; idx < nDelay; ++idx) {
constexpr auto sign = 1;
auto x0 = lowpass[idx].process(sign * input, highShelfCut, highShelfGain);
// auto x0 = sign * input;
Expand All @@ -411,11 +398,6 @@ template<typename Sample, size_t nAllpass, size_t nAdaptiveNotch> class SerialAl
}

// input = lowpass[0].process(input, highShelfCut, highShelfGain);

for (size_t idx = 0; idx < nNotch; ++idx) {
input += notchMix * (notch[idx].process(input, notchNarrowness) - input);
}

return input;
}
};
Expand Down
46 changes: 22 additions & 24 deletions DoubleLoopCymbal/source/dsp/dspcore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,12 @@ void DSPCore::setup(double sampleRate)
std::clamp(pv[ID::allpassFeed2]->getDouble(), double(-0.99999), double(0.99999))); \
allpassMixSpike.METHOD(pv[ID::allpassMixSpike]->getDouble()); \
allpassMixAltSign.METHOD(pv[ID::allpassMixAltSign]->getDouble()); \
highShelfCutoff.METHOD(EMAFilter<double>::cutoffToP(std::clamp( \
pv[ID::highShelfFrequencyHz]->getDouble() / upRate, double(0), double(0.5)))); \
highShelfCutoff.METHOD(EMAFilter<double>::cutoffToP( \
std::min(pv[ID::highShelfFrequencyHz]->getDouble() / upRate, double(0.5)))); \
highShelfGain.METHOD(pv[ID::highShelfGain]->getDouble()); \
lowShelfCutoff.METHOD(EMAFilter<double>::cutoffToP(std::clamp( \
pv[ID::lowShelfFrequencyHz]->getDouble() / upRate, double(0), double(0.5)))); \
lowShelfCutoff.METHOD(EMAFilter<double>::cutoffToP( \
std::min(pv[ID::lowShelfFrequencyHz]->getDouble() / upRate, double(0.5)))); \
lowShelfGain.METHOD(pv[ID::lowShelfGain]->getDouble()); \
notchMix.METHOD(pv[ID::adaptiveNotchMix]->getDouble()); \
notchNarrowness.METHOD(pv[ID::adaptiveNotchNarrowness]->getDouble()); \
stereoBalance.METHOD(pv[ID::stereoBalance]->getDouble()); \
stereoMerge.METHOD(pv[ID::stereoMerge]->getDouble() / double(2)); \
\
Expand All @@ -212,7 +210,7 @@ void DSPCore::setup(double sampleRate)
\
if (!pv[ID::release]->getInt() && noteStack.empty()) { \
envelopeRelease.setTime( \
double(8) * pv[ID::closeAttackSeconds]->getDouble() * upRate); \
double(8) * pv[ID::closeAttackSeconds]->getDouble() * upRate, false); \
} \
\
envelopeClose.update( \
Expand Down Expand Up @@ -240,18 +238,23 @@ void DSPCore::updateDelayTime()
const auto delayTimeBase = pv[ID::delayTimeBaseSecond]->getDouble() * upRate;
const auto delayTimeRandom = pv[ID::delayTimeRandomSecond]->getDouble() * upRate;
const auto shape = pv[ID::delayTimeShape]->getDouble();
std::uniform_real_distribution<double> delayTimeDist{
double(0), double(delayTimeRandom)};
const auto pitchRatio = std::exp2(pv[ID::delayTimeRatio]->getDouble() / double(-12));
std::uniform_real_distribution<double> timeDist{double(0), double(delayTimeRandom)};
for (size_t idx = 0; idx < nAllpass; ++idx) {
static_assert(nAllpass >= 1);
const auto t1 = delayTimeBase / double(idx + 1);
const auto t2 = delayTimeBase * (circularModes[idx] / circularModes[nAllpass - 1]);
const auto timeHarmonics = std::lerp(t1, t2, shape);
serialAllpass1[0].timeInSamples[idx] = timeHarmonics + delayTimeDist(paramRng);
serialAllpass1[1].timeInSamples[idx] = timeHarmonics + delayTimeDist(paramRng);
serialAllpass2[0].timeInSamples[idx] = timeHarmonics + delayTimeDist(paramRng);
serialAllpass2[1].timeInSamples[idx] = timeHarmonics + delayTimeDist(paramRng);
const auto harmonics = std::lerp(t1, t2, shape);
serialAllpass1[0].timeInSamples[idx] = harmonics + timeDist(paramRng);
serialAllpass1[1].timeInSamples[idx] = harmonics + timeDist(paramRng);
serialAllpass2[0].timeInSamples[idx] = pitchRatio * (harmonics + timeDist(paramRng));
serialAllpass2[1].timeInSamples[idx] = pitchRatio * (harmonics + timeDist(paramRng));
}

const size_t nDelay1 = 1 + pv[ID::allpassDelayCount1]->getInt();
const size_t nDelay2 = 1 + pv[ID::allpassDelayCount2]->getInt();
for (auto &x : serialAllpass1) x.nDelay = nDelay1;
for (auto &x : serialAllpass2) x.nDelay = nDelay2;
}

void DSPCore::reset()
Expand Down Expand Up @@ -325,9 +328,6 @@ std::array<double, 2> DSPCore::processFrame(const std::array<double, 2> &externa
const auto hsGain = highShelfGain.process(); //* envRelease;
const auto lsCut = lowShelfCutoff.process();
const auto lsGain = lowShelfGain.process();
const size_t nNotch = param.value[ParameterID::nAdaptiveNotch]->getInt();
const auto ntMix = notchMix.process();
const auto ntNarrowness = notchNarrowness.process();
const auto balance = stereoBalance.process();
const auto merge = stereoMerge.process();
const auto outGain = outputGain.process() * envRelease;
Expand Down Expand Up @@ -373,11 +373,9 @@ std::array<double, 2> DSPCore::processFrame(const std::array<double, 2> &externa
* normalizeGain;

feedbackBuffer1[0] = serialAllpass1[0].process(
excitation[0], hsCut, hsGain, lsCut, lsGain, apGain1, pitchRatio, timeModAmt, nNotch,
ntMix, ntNarrowness);
excitation[0], hsCut, hsGain, lsCut, lsGain, apGain1, pitchRatio, timeModAmt);
feedbackBuffer1[1] = serialAllpass1[1].process(
excitation[1], hsCut, hsGain, lsCut, lsGain, apGain1, pitchRatio, timeModAmt, nNotch,
ntMix, ntNarrowness);
excitation[1], hsCut, hsGain, lsCut, lsGain, apGain1, pitchRatio, timeModAmt);

auto cymbal0
= std::lerp(serialAllpass2[0].sum(apMixSign), feedbackBuffer2[0], apMixSpike)
Expand All @@ -387,10 +385,10 @@ std::array<double, 2> DSPCore::processFrame(const std::array<double, 2> &externa
* normalizeGain;
feedbackBuffer2[0] = serialAllpass2[0].process(
ap1Out0 - apGain2 * feedbackBuffer2[0], hsCut, hsGain, lsCut, lsGain, apGain2,
pitchRatio, timeModAmt, nNotch, ntMix, ntNarrowness);
pitchRatio, timeModAmt);
feedbackBuffer2[1] = serialAllpass2[1].process(
ap1Out1 - apGain2 * feedbackBuffer2[1], hsCut, hsGain, lsCut, lsGain, apGain2,
pitchRatio, timeModAmt, nNotch, ntMix, ntNarrowness);
pitchRatio, timeModAmt);

constexpr auto eps = std::numeric_limits<double>::epsilon();
if (balance < -eps) {
Expand Down Expand Up @@ -508,7 +506,7 @@ void DSPCore::noteOff(int_fast32_t noteId)
velocity = 0;
envelopeHalfClosed.release();
if (!pv[ID::release]->getInt()) {
envelopeRelease.setTime(releaseTimeSecond * upRate);
envelopeRelease.setTime(releaseTimeSecond * upRate, false);
}
}
}
Expand Down
8 changes: 3 additions & 5 deletions DoubleLoopCymbal/source/dsp/dspcore.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,6 @@ class DSPCore {
ExpSmoother<double> highShelfGain;
ExpSmoother<double> lowShelfCutoff;
ExpSmoother<double> lowShelfGain;
ExpSmoother<double> notchMix;
ExpSmoother<double> notchNarrowness;
ExpSmoother<double> stereoBalance;
ExpSmoother<double> stereoMerge;
ExpSmoother<double> outputGain;
Expand All @@ -143,13 +141,13 @@ class DSPCore {
TransitionReleaseSmoother<double> releaseSmoother;
ExpDecay<double> envelopeNoise;
ExpDSREnvelope<double> envelopeHalfClosed;
ExpDecay<double> envelopeRelease;
ExpSREnvelope<double> envelopeRelease;
ExpADEnvelope<double> envelopeClose;
std::array<HalfClosedNoise<double>, 2> halfClosedNoise;
std::array<double, 2> feedbackBuffer1{};
std::array<double, 2> feedbackBuffer2{};
std::array<SerialAllpass<double, nAllpass, nNotch>, 2> serialAllpass1;
std::array<SerialAllpass<double, nAllpass, nNotch>, 2> serialAllpass2;
std::array<SerialAllpass<double, nAllpass>, 2> serialAllpass1;
std::array<SerialAllpass<double, nAllpass>, 2> serialAllpass2;

std::array<std::array<double, 2>, 2> halfbandInput{};
std::array<HalfBandIIR<double, HalfBandCoefficient<double>>, 2> halfbandIir;
Expand Down
27 changes: 13 additions & 14 deletions DoubleLoopCymbal/source/editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,25 +353,24 @@ bool Editor::prepareUI()
thirdLeft1, thirdTop3, labelWidth, labelHeight, uiTextSize, ID::delayTimeRandomSecond,
Scales::delayTimeSecond, false, 5);
addLabel(
thirdLeft0, thirdTop4, labelWidth, labelHeight, uiTextSize, "Modulation [sample]");
thirdLeft0, thirdTop4, labelWidth, labelHeight, uiTextSize, "Pitch Ratio [st.]");
addTextKnob(
thirdLeft1, thirdTop4, labelWidth, labelHeight, uiTextSize, ID::delayTimeModAmount,
thirdLeft1, thirdTop4, labelWidth, labelHeight, uiTextSize, ID::delayTimeRatio,
Scales::semitone, false, 5);
addLabel(
thirdLeft0, thirdTop5, labelWidth, labelHeight, uiTextSize, "Modulation [sample]");
addTextKnob(
thirdLeft1, thirdTop5, labelWidth, labelHeight, uiTextSize, ID::delayTimeModAmount,
Scales::delayTimeModAmount, false, 5);

addLabel(thirdLeft0, thirdTop6, labelWidth, labelHeight, uiTextSize, "nNotch");
addLabel(thirdLeft0, thirdTop8, labelWidth, labelHeight, uiTextSize, "Delay Count 1");
addTextKnob(
thirdLeft1, thirdTop6, labelWidth, labelHeight, uiTextSize, ID::nAdaptiveNotch,
Scales::nAdaptiveNotch, false, 0, 0);
addLabel(thirdLeft0, thirdTop7, labelWidth, labelHeight, uiTextSize, "Notch Mix");
thirdLeft1, thirdTop8, labelWidth, labelHeight, uiTextSize, ID::allpassDelayCount1,
Scales::allpassDelayCount, false, 0, 1);
addLabel(thirdLeft0, thirdTop9, labelWidth, labelHeight, uiTextSize, "Delay Count 2");
addTextKnob(
thirdLeft1, thirdTop7, labelWidth, labelHeight, uiTextSize, ID::adaptiveNotchMix,
Scales::defaultScale, false, 5);
addLabel(
thirdLeft0, thirdTop8, labelWidth, labelHeight, uiTextSize, "Notch Narrowness");
addTextKnob(
thirdLeft1, thirdTop8, labelWidth, labelHeight, uiTextSize,
ID::adaptiveNotchNarrowness, Scales::adaptiveNotchNarrowness, false, 5);

thirdLeft1, thirdTop9, labelWidth, labelHeight, uiTextSize, ID::allpassDelayCount2,
Scales::allpassDelayCount, false, 0, 1);
addLabel(thirdLeft0, thirdTop10, labelWidth, labelHeight, uiTextSize, "Feed 1");
addTextKnob(
thirdLeft1, thirdTop10, labelWidth, labelHeight, uiTextSize, ID::allpassFeed1,
Expand Down
5 changes: 2 additions & 3 deletions DoubleLoopCymbal/source/parameter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,10 @@ DecibelScale<double> Scales::halfClosedDensityHz(0.0, 80.0, true);
DecibelScale<double> Scales::delayTimeSecond(-100, -30, false);
DecibelScale<double> Scales::delayTimeModAmount(-40, 60, true);

UIntScale<double> Scales::allpassDelayCount(nAllpass - 1);

DecibelScale<double> Scales::cutoffFrequencyHz(0, 100, false);
DecibelScale<double> Scales::shelvingGain(-60, 0, true);

UIntScale<double> Scales::nAdaptiveNotch(nNotch);
NegativeDecibelScale<double> Scales::adaptiveNotchNarrowness(-60, 0, 1, false);

} // namespace Synth
} // namespace Steinberg
Loading

0 comments on commit d973a85

Please sign in to comment.