Skip to content

Commit

Permalink
Update GenericDrum
Browse files Browse the repository at this point in the history
  • Loading branch information
ryukau committed Oct 2, 2023
1 parent 96f2f37 commit e8cb0c6
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 54 deletions.
35 changes: 20 additions & 15 deletions GenericDrum/source/dsp/dspcore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ void DSPCore::setup(double sampleRate)
using ID = ParameterID::ID; \
const auto &pv = param.value; \
\
preventBlowUp = pv[ID::preventBlowUp]->getInt(); \
\
pitchSmoothingKp \
= EMAFilter<double>::secondToP(upRate, pv[ID::noteSlideTimeSecond]->getDouble()); \
auto pitchBend \
Expand All @@ -170,7 +172,6 @@ void DSPCore::setup(double sampleRate)
membraneWireMix.METHOD(pv[ID::membraneWireMix]->getDouble()); \
stereoBalance.METHOD(pv[ID::stereoBalance]->getDouble()); \
stereoMerge.METHOD(pv[ID::stereoMerge]->getDouble() / double(2)); \
outputGain.METHOD(pv[ID::outputGain]->getDouble()); \
\
constexpr auto highpassQ = std::numbers::sqrt2_v<double> / double(2); \
const auto highpassCut = pv[ID::safetyHighpassHz]->getDouble() / sampleRate; \
Expand All @@ -186,7 +187,7 @@ void DSPCore::setup(double sampleRate)
} \
feedbackMatrix.constructHouseholder(); \
\
const auto noiseLowpassHz = pv[ID::noiseLowpassHz]->getDouble() / upRate; \
const auto noiseLowpassFreq = pv[ID::noiseLowpassHz]->getDouble() / upRate; \
const auto noiseAllpassMaxTimeHz = pv[ID::noiseAllpassMaxTimeHz]->getDouble(); \
const auto wireFrequencyHz = pv[ID::wireFrequencyHz]->getDouble(); \
const auto secondaryPitchOffset = pv[ID::secondaryPitchOffset]->getDouble(); \
Expand All @@ -198,8 +199,15 @@ void DSPCore::setup(double sampleRate)
const auto bandpassCutSpread = pv[ID::bandpassCutSpread]->getDouble(); \
const auto pitchRandomCent = pv[ID::pitchRandomCent]->getDouble(); \
const size_t pitchType = pv[ID::pitchType]->getInt(); \
\
auto gain = pv[ID::outputGain]->getDouble(); \
if (pv[ID::normalizeGainWrtNoiseLowpassHz]->getInt()) { \
gain *= approxNormalizeGain(noiseLowpassFreq); \
} \
outputGain.METHOD(gain); \
\
for (size_t drm = 0; drm < nDrum; ++drm) { \
noiseLowpass[drm].METHOD(noiseLowpassHz); \
noiseLowpass[drm].METHOD(noiseLowpassFreq); \
noiseAllpass[drm].timeInSamples.METHOD( \
prepareSerialAllpassTime<nAllpass>(upRate, noiseAllpassMaxTimeHz, paramRng)); \
wireAllpass[drm].timeInSamples.METHOD( \
Expand Down Expand Up @@ -338,12 +346,6 @@ double DSPCore::processDrum(
sig += noiseLowpass[index].process(noise);
sig = std::tanh(noiseAllpass[index].process(sig, double(0.95)));

// Wire and membrane processing goes like:
// 1. solve collision,
// 2. update GUI status,
// 3. process delays,
// 4. update collision parameters.

// Wire.
solveCollision(
wirePosition[index], membrane1Position[index], wireVelocity[index],
Expand All @@ -352,8 +354,9 @@ double DSPCore::processDrum(
if (!isWireCollided && wirePosition[index] != 0) isWireCollided = true;

auto wireCollision = std::lerp(
wireEnergyNoise[index].process(wirePosition[index], noiseRng),
wireEnergyDecay[index].process(wirePosition[index]), wireCollisionTypeMix.getValue());
wireEnergyNoise[index].process(wirePosition[index], preventBlowUp, noiseRng),
wireEnergyDecay[index].process(wirePosition[index], preventBlowUp),
wireCollisionTypeMix.getValue());
wireCollision = double(8) * std::tanh(double(0.125) * wireCollision);
const auto wireIn = double(0.995) * (sig + wireCollision);
const auto wirePos = wireAllpass[index].process(wireIn, double(0.5)) * wireGain;
Expand All @@ -375,14 +378,16 @@ double DSPCore::processDrum(
feedbackMatrix.process();

const auto collision1
= membrane1EnergyDecay[index].process(membrane1Position[index]) / double(maxFdnSize);
= membrane1EnergyDecay[index].process(membrane1Position[index], false)
/ double(maxFdnSize);
const auto p1 = membrane1[index].process(
sig + collision1, crossGain, pitch, timeModAmt, feedbackMatrix);
membrane1Velocity[index] = p1 - membrane1Position[index];
membrane1Position[index] = p1;

const auto collision2
= membrane2EnergyDecay[index].process(membrane2Position[index]) / double(maxFdnSize);
= membrane2EnergyDecay[index].process(membrane2Position[index], false)
/ double(maxFdnSize);
const auto p2 = membrane2[index].process(
sig + collision2, crossGain, pitch, timeModAmt, feedbackMatrix);
membrane2Velocity[index] = p2 - membrane2Position[index];
Expand All @@ -407,8 +412,8 @@ double DSPCore::processDrum(
const auto merge = stereoMerge.process(); \
const auto outGain = outputGain.process(); \
\
std::uniform_real_distribution<double> dist{double(-1), double(1)}; \
const auto noise = noiseGain * dist(noiseRng); \
std::uniform_real_distribution<double> dist{double(-0.5), double(0.5)}; \
const auto noise = noiseGain * (dist(noiseRng) + dist(noiseRng)); \
noiseGain *= noiseDecay; \
wireGain *= wireDecay;

Expand Down
1 change: 1 addition & 0 deletions GenericDrum/source/dsp/dspcore.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ class DSPCore {
std::array<ComplexLowpass<double>, nDrum> noiseLowpass;
std::array<SerialAllpass<double, nAllpass>, nDrum> noiseAllpass;

bool preventBlowUp = false;
std::array<SerialAllpass<double, nAllpass>, 2> wireAllpass;
std::array<EnergyStoreDecay<double>, 2> wireEnergyDecay;
std::array<EnergyStoreNoise<double, std::minstd_rand>, 2> wireEnergyNoise;
Expand Down
67 changes: 62 additions & 5 deletions GenericDrum/source/dsp/filter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@

namespace SomeDSP {

// Normalize gain for `ComplexLowpass`.
// `x` is normalzied cutoff in [0, 0.5).
template<typename Sample> inline Sample approxNormalizeGain(Sample x)
{
constexpr std::array<Sample, 4> b{
Sample(812352066809.4705), Sample(3.094123212331795e+16),
Sample(2.3874703361354637e+19), Sample(4.274664722153258e+20)};
constexpr std::array<Sample, 4> a{
Sample(-1650067658.2394962), Sample(1417724332731251.8),
Sample(2.7196907485733147e+19), Sample(1.1852026355744978e+22)};
return x * (b[1] + x * (b[2] + x * (b[3] + x * b[4])))
/ (Sample(1) + x * (a[1] + x * (a[2] + x * (a[3] + x * a[4]))));
}

template<typename Sample> class ComplexLowpass {
private:
Sample x1 = 0;
Expand Down Expand Up @@ -190,7 +204,7 @@ template<typename Sample> class Delay {

void reset() { std::fill(buf.begin(), buf.end(), Sample(0)); }

Sample process(Sample input, Sample timeInSamples)
Sample processLinterp(Sample input, Sample timeInSamples)
{
const int size = int(buf.size());

Expand All @@ -208,6 +222,23 @@ template<typename Sample> class Delay {
if (rptr0 < 0) rptr0 += size;
return std::lerp(buf[rptr0], buf[(rptr0 != 0 ? rptr0 : size) - 1], rFraction);
}

Sample process(Sample input, Sample timeInSamples)
{
const int size = int(buf.size());

// Set delay time. Min delay is set to 1 sample to avoid artifact of feedback.
const int timeInt = int(std::clamp(timeInSamples, Sample(1), Sample(size - 1)));

// Write to buffer.
buf[wptr] = input;
if (++wptr >= size) wptr = 0;

// Read from buffer.
int rptr0 = wptr - timeInt;
if (rptr0 < 0) rptr0 += size;
return buf[rptr0];
}
};

template<typename Sample, size_t length> class ParallelRateLimiter {
Expand Down Expand Up @@ -294,10 +325,11 @@ template<typename Sample> class EnergyStoreDecay {

void reset() { sum = 0; }

Sample process(Sample value)
Sample process(Sample value, bool preventBlowUp)
{
const auto absed = std::abs(value);
if (absed > eps) sum = (sum + value) * decay;
if (preventBlowUp) sum = std::min(Sample(1) / Sample(4), sum);
return sum *= gain;
}
};
Expand All @@ -309,10 +341,11 @@ template<typename Sample, typename Rng> class EnergyStoreNoise {
public:
void reset() { sum = 0; }

Sample process(Sample value, Rng &rng)
Sample process(Sample value, bool preventBlowUp, Rng &rng)
{
sum += std::abs(value);
std::uniform_real_distribution<Sample> dist{Sample(-sum), Sample(sum)};
const auto range = preventBlowUp ? std::min(Sample(1) / Sample(64), sum) : sum;
std::uniform_real_distribution<Sample> dist{Sample(-range), Sample(range)};
const auto out = dist(rng);
sum -= std::abs(out);
return out;
Expand Down Expand Up @@ -344,7 +377,7 @@ template<typename Sample, size_t length> class ParallelDelay {
for (auto &bf : buffer) std::fill(bf.begin(), bf.end(), Sample(0));
}

void process(
void processLinterp(
std::array<Sample, length> &input,
const std::array<Sample, length> &timeInSamples,
Sample timeScaler)
Expand All @@ -369,6 +402,30 @@ template<typename Sample, size_t length> class ParallelDelay {
input[idx] = std::lerp(bf[rptr0], bf[(rptr0 != 0 ? rptr0 : size) - 1], rFraction);
}
}

void process(
std::array<Sample, length> &input,
const std::array<Sample, length> &timeInSamples,
Sample timeScaler)
{
for (size_t idx = 0; idx < length; ++idx) {
auto &bf = buffer[idx];
const int size = int(bf.size());

// Set delay time. Min delay is set to 1 sample to avoid artifact of feedback.
int timeInt
= int(std::clamp(timeInSamples[idx] / timeScaler, Sample(1), Sample(size - 1)));

// Write to buffer.
bf[wptr[idx]] = input[idx];
if (++wptr[idx] >= size) wptr[idx] = 0;

// Read from buffer.
int rptr0 = wptr[idx] - timeInt;
if (rptr0 < 0) rptr0 += size;
input[idx] = bf[rptr0];
}
}
};

template<typename Sample, size_t length> class MembraneDelayRateLimiter {
Expand Down
50 changes: 21 additions & 29 deletions GenericDrum/source/editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,20 +89,6 @@ void Editor::valueChanged(CControl *pControl)
ParamID id = pControl->getTag();

if (id != ID::isWireCollided && id != ID::isSecondaryCollided) {
// controller->setParamNormalized(ID::isWireCollided, 0.0);
// controller->performEdit(ID::isWireCollided, 0.0);
// if (labelWireCollision.get()) {
// labelWireCollision->setText(wireDidntCollidedText);
// labelWireCollision->setDirty();
// }

// controller->setParamNormalized(ID::isSecondaryCollided, 0.0);
// controller->performEdit(ID::isSecondaryCollided, 0.0);
// if (labelMembraneCollision.get()) {
// labelMembraneCollision->setText(membraneDidntCollidedText);
// labelMembraneCollision->setDirty();
// }

resetStatusText(
controller, labelWireCollision, ID::isWireCollided, wireDidntCollidedText);
resetStatusText(
Expand Down Expand Up @@ -180,6 +166,7 @@ bool Editor::prepareUI()
constexpr auto mixTop4 = mixTop0 + 4 * labelY;
constexpr auto mixTop5 = mixTop0 + 5 * labelY;
constexpr auto mixTop6 = mixTop0 + 6 * labelY;
constexpr auto mixTop7 = mixTop0 + 7 * labelY;
constexpr auto mixLeft0 = left0;
constexpr auto mixLeft1 = mixLeft0 + labelWidth + 2 * margin;
addGroupLabel(mixLeft0, mixTop0, groupLabelWidth, labelHeight, uiTextSize, "Mix");
Expand All @@ -195,26 +182,31 @@ bool Editor::prepareUI()
mixLeft1, mixTop2, labelWidth, labelHeight, uiTextSize, ID::safetyHighpassHz,
Scales::safetyHighpassHz, false, 5);
addCheckbox(
mixLeft0, mixTop3, labelWidth, labelHeight, uiTextSize, "Reset Seed at Note-on",
mixLeft0, mixTop3, labelWidth, labelHeight, uiTextSize, "2x Sampling",
ID::overSampling);
addCheckbox(
mixLeft1, mixTop3, labelWidth, labelHeight, uiTextSize, "Normalize Gain",
ID::normalizeGainWrtNoiseLowpassHz);
addCheckbox(
mixLeft0, mixTop4, labelWidth, labelHeight, uiTextSize, "Reset Seed at Note-on",
ID::resetSeedAtNoteOn);
addCheckbox(
mixLeft1, mixTop3, labelWidth, labelHeight, uiTextSize, "2x Sampling",
ID::overSampling);
addLabel(mixLeft0, mixTop4, labelWidth, labelHeight, uiTextSize, "Channel");
addOptionMenu(
mixLeft1, mixTop4, labelWidth, labelHeight, uiTextSize, ID::stereoEnable,
{"Mono", "Stereo"});
addLabel(mixLeft0, mixTop5, labelWidth, labelHeight, uiTextSize, "Stereo Balance");
mixLeft1, mixTop4, labelWidth, labelHeight, uiTextSize, "Prevent Blow Up",
ID::preventBlowUp);
addToggleButton(
mixLeft0, mixTop5, groupLabelWidth, labelHeight, uiTextSize, "Stereo Unison",
ID::stereoEnable);
addLabel(mixLeft0, mixTop6, labelWidth, labelHeight, uiTextSize, "Stereo Balance");
addTextKnob(
mixLeft1, mixTop5, labelWidth, labelHeight, uiTextSize, ID::stereoBalance,
mixLeft1, mixTop6, labelWidth, labelHeight, uiTextSize, ID::stereoBalance,
Scales::bipolarScale, false, 5);
addLabel(mixLeft0, mixTop6, labelWidth, labelHeight, uiTextSize, "Stereo Merge");
addLabel(mixLeft0, mixTop7, labelWidth, labelHeight, uiTextSize, "Stereo Merge");
addTextKnob(
mixLeft1, mixTop6, labelWidth, labelHeight, uiTextSize, ID::stereoMerge,
mixLeft1, mixTop7, labelWidth, labelHeight, uiTextSize, ID::stereoMerge,
Scales::defaultScale, false, 5);

// Tuning.
constexpr auto tuningTop0 = top0 + 7 * labelY;
constexpr auto tuningTop0 = top0 + 8 * labelY;
constexpr auto tuningTop1 = tuningTop0 + 1 * labelY;
constexpr auto tuningTop2 = tuningTop0 + 2 * labelY;
constexpr auto tuningTop3 = tuningTop0 + 3 * labelY;
Expand Down Expand Up @@ -322,7 +314,7 @@ bool Editor::prepareUI()
wireLeft1, wireTop6, labelWidth, labelHeight, uiTextSize, ID::wireCollisionTypeMix,
Scales::defaultScale, false, 5);
labelWireCollision = addLabel(
wireLeft0, wireTop7, 2 * labelWidth, labelHeight, uiTextSize,
wireLeft0, wireTop7, groupLabelWidth, labelHeight, uiTextSize,
"Wire collision status.");

// Primary Membrane.
Expand Down Expand Up @@ -466,12 +458,12 @@ bool Editor::prepareUI()
secondaryLeft1, secondaryTop4, labelWidth, labelHeight, uiTextSize,
ID::secondaryDistance, Scales::collisionDistance, false, 5);
labelMembraneCollision = addLabel(
secondaryLeft0, secondaryTop5, 2 * labelWidth, labelHeight, uiTextSize,
secondaryLeft0, secondaryTop5, groupLabelWidth, labelHeight, uiTextSize,
"Membrane collision status.");

// Plugin name.
constexpr auto splashMargin = uiMargin;
constexpr auto splashTop = top0 + 13 * labelY + int(labelHeight / 4) + 2 * margin;
constexpr auto splashTop = top0 + 18 * labelY + int(labelHeight / 4) + 2 * margin;
constexpr auto splashLeft = left0 + int(labelWidth / 4);
addSplashScreen(
splashLeft, splashTop, splashWidth, splashHeight, splashMargin, splashMargin,
Expand Down
2 changes: 1 addition & 1 deletion GenericDrum/source/parameter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ DecibelScale<double> Scales::safetyHighpassHz(ampToDB(0.1), ampToDB(100.0), fals
UIntScale<double> Scales::semitone(semitoneOffset + 48);
LinearScale<double> Scales::cent(-100.0, 100.0);
LinearScale<double> Scales::pitchBendRange(0.0, 120.0);
DecibelScale<double> Scales::noteSlideTimeSecond(-100.0, 40.0, true);
DecibelScale<double> Scales::noteSlideTimeSecond(-40.0, 40.0, false);

DecibelScale<double> Scales::noiseDecaySeconds(-40, ampToDB(0.5), false);

Expand Down
14 changes: 10 additions & 4 deletions GenericDrum/source/parameter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ enum ID {
outputGain,
safetyHighpassEnable,
safetyHighpassHz,
resetSeedAtNoteOn,
overSampling,
normalizeGainWrtNoiseLowpassHz,
resetSeedAtNoteOn,
preventBlowUp,

stereoEnable,
stereoBalance,
Expand Down Expand Up @@ -158,10 +160,14 @@ struct GlobalParameter : public ParameterInterface {
value[ID::safetyHighpassHz] = std::make_unique<DecibelValue>(
Scales::safetyHighpassHz.invmap(15.0), Scales::safetyHighpassHz, "safetyHighpassHz",
Info::kCanAutomate);
value[ID::resetSeedAtNoteOn] = std::make_unique<UIntValue>(
1, Scales::boolScale, "resetSeedAtNoteOn", Info::kCanAutomate);
value[ID::overSampling] = std::make_unique<UIntValue>(
1, Scales::boolScale, "overSampling", Info::kCanAutomate);
value[ID::normalizeGainWrtNoiseLowpassHz] = std::make_unique<UIntValue>(
1, Scales::boolScale, "normalizeGainWrtNoiseLowpassHz", Info::kCanAutomate);
value[ID::resetSeedAtNoteOn] = std::make_unique<UIntValue>(
1, Scales::boolScale, "resetSeedAtNoteOn", Info::kCanAutomate);
value[ID::preventBlowUp] = std::make_unique<UIntValue>(
0, Scales::boolScale, "preventBlowUp", Info::kCanAutomate);

value[ID::stereoEnable] = std::make_unique<UIntValue>(
1, Scales::boolScale, "stereoEnable", Info::kCanAutomate);
Expand All @@ -185,7 +191,7 @@ struct GlobalParameter : public ParameterInterface {
Scales::pitchBendRange.invmap(2.0), Scales::pitchBendRange, "pitchBendRange",
Info::kCanAutomate);
value[ID::noteSlideTimeSecond] = std::make_unique<DecibelValue>(
Scales::noteSlideTimeSecond.invmap(0.01), Scales::noteSlideTimeSecond,
Scales::noteSlideTimeSecond.invmap(0.1), Scales::noteSlideTimeSecond,
"noteSlideTimeSecond", Info::kCanAutomate);

value[ID::seed]
Expand Down

0 comments on commit e8cb0c6

Please sign in to comment.