Skip to content

Commit

Permalink
AnalogBassDrum module (electro-smith#118)
Browse files Browse the repository at this point in the history
* building

* sample based

* setters

* comments and init

* no bd.

* double comment

* trig

* two consts

* kOneTwelfth
  • Loading branch information
beserge authored Jan 18, 2021
1 parent bacd432 commit cb07ddd
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 0 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ MODULES = \
adenv \
adsr \
allpass \
analogbassdrum \
autowah \
atone \
balance \
Expand Down
1 change: 1 addition & 0 deletions daisysp.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "modules/adenv.h"
#include "modules/adsr.h"
#include "modules/allpass.h"
#include "modules/analogbassdrum.h"
#include "modules/atone.h"
#include "modules/autowah.h"
#include "modules/balance.h"
Expand Down
192 changes: 192 additions & 0 deletions modules/analogbassdrum.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#include "dsp.h"
#include "analogbassdrum.h"
#include <math.h>

using namespace daisysp;

void AnalogBassDrum::Init(float sample_rate)
{
sample_rate_ = sample_rate;

trig_ = false;

pulse_remaining_samples_ = 0;
fm_pulse_remaining_samples_ = 0;
pulse_ = 0.0f;
pulse_height_ = 0.0f;
pulse_lp_ = 0.0f;
fm_pulse_lp_ = 0.0f;
retrig_pulse_ = 0.0f;
lp_out_ = 0.0f;
tone_lp_ = 0.0f;
sustain_gain_ = 0.0f;
phase_ = 0.f;


SetSustain(false);
SetAccent(.1f);
SetFreq(50.f);
SetTone(.1f);
SetDecay(.3f);
SetSelfFmAmount(1.f);
SetAttackFmAmount(.5f);

resonator_.Init(sample_rate_);
}

inline float AnalogBassDrum::Diode(float x)
{
if(x >= 0.0f)
{
return x;
}
else
{
x *= 2.0f;
return 0.7f * x / (1.0f + fabsf(x));
}
}

float AnalogBassDrum::Process(bool trigger)
{
const int kTriggerPulseDuration = 1.0e-3 * sample_rate_;
const int kFMPulseDuration = 6.0e-3 * sample_rate_;
const float kPulseDecayTime = 0.2e-3 * sample_rate_;
const float kPulseFilterTime = 0.1e-3 * sample_rate_;
const float kRetrigPulseDuration = 0.05f * sample_rate_;

const float scale = 0.001f / f0_;
const float q = 1500.0f * powf(2.f, kOneTwelfth * decay_ * 80.0f);
const float tone_f
= fmin(4.0f * f0_ * powf(2.f, kOneTwelfth * tone_ * 108.0f), 1.0f);
const float exciter_leak = 0.08f * (tone_ + 0.25f);


if(trigger || trig_)
{
trig_ = false;

pulse_remaining_samples_ = kTriggerPulseDuration;
fm_pulse_remaining_samples_ = kFMPulseDuration;
pulse_height_ = 3.0f + 7.0f * accent_;
lp_out_ = 0.0f;
}

// Q39 / Q40
float pulse = 0.0f;
if(pulse_remaining_samples_)
{
--pulse_remaining_samples_;
pulse = pulse_remaining_samples_ ? pulse_height_ : pulse_height_ - 1.0f;
pulse_ = pulse;
}
else
{
pulse_ *= 1.0f - 1.0f / kPulseDecayTime;
pulse = pulse_;
}
if(sustain_)
{
pulse = 0.0f;
}

// C40 / R163 / R162 / D83
fonepole(pulse_lp_, pulse, 1.0f / kPulseFilterTime);
pulse = Diode((pulse - pulse_lp_) + pulse * 0.044f);

// Q41 / Q42
float fm_pulse = 0.0f;
if(fm_pulse_remaining_samples_)
{
--fm_pulse_remaining_samples_;
fm_pulse = 1.0f;
// C39 / C52
retrig_pulse_ = fm_pulse_remaining_samples_ ? 0.0f : -0.8f;
}
else
{
// C39 / R161
retrig_pulse_ *= 1.0f - 1.0f / kRetrigPulseDuration;
}
if(sustain_)
{
fm_pulse = 0.0f;
}
fonepole(fm_pulse_lp_, fm_pulse, 1.0f / kPulseFilterTime);

// Q43 and R170 leakage
float punch = 0.7f + Diode(10.0f * lp_out_ - 1.0f);

// Q43 / R165
float attack_fm = fm_pulse_lp_ * 1.7f * attack_fm_amount_;
float self_fm = punch * 0.08f * self_fm_amount_;
float f = f0_ * (1.0f + attack_fm + self_fm);
f = fclamp(f, 0.0f, 0.4f);

float resonator_out;
if(sustain_)
{
sustain_gain_ = accent_ * decay_;
phase_ += f;
phase_ = phase_ >= 1.f ? phase_ - 1.f : phase_;

resonator_out = sin(TWOPI_F * phase_) * sustain_gain_;
lp_out_ = cos(TWOPI_F * phase_) * sustain_gain_;
}
else
{
resonator_.SetFreq(f * sample_rate_);
//resonator_.SetRes(1.0f + q * f);
resonator_.SetRes(.4 * q * f);

resonator_.Process((pulse - retrig_pulse_ * 0.2f) * scale);
resonator_out = resonator_.Band();
lp_out_ = resonator_.Low();
}

fonepole(tone_lp_, pulse * exciter_leak + resonator_out, tone_f);

return tone_lp_;
}

void AnalogBassDrum::Trig()
{
trig_ = true;
}

void AnalogBassDrum::SetSustain(bool sustain)
{
sustain_ = sustain;
}

void AnalogBassDrum::SetAccent(float accent)
{
accent_ = fclamp(accent, 0.f, 1.f);
}

void AnalogBassDrum::SetFreq(float f0)
{
f0 /= sample_rate_;
f0_ = fclamp(f0, 0.f, .5f);
}

void AnalogBassDrum::SetTone(float tone)
{
tone_ = fclamp(tone, 0.f, 1.f);
}

void AnalogBassDrum::SetDecay(float decay)
{
decay_ = decay * .1f;
decay_ -= .1f;
}

void AnalogBassDrum::SetAttackFmAmount(float attack_fm_amount)
{
attack_fm_amount_ = attack_fm_amount * 50.f;
}

void AnalogBassDrum::SetSelfFmAmount(float self_fm_amount)
{
self_fm_amount_ = self_fm_amount * 50.f;
}
105 changes: 105 additions & 0 deletions modules/analogbassdrum.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#pragma once
#ifndef DSY_ANALOG_BD_H
#define DSY_ANALOG_BD_H

#include <stdint.h>
#ifdef __cplusplus

#include "modules/oscillator.h"
#include "modules/svf.h"

/** @file analogbassdrum.h */

namespace daisysp
{
/**
@brief 808 bass drum model, revisited.
@author Ben Sergentanis
@date Jan 2021
Ported from pichenettes/eurorack/plaits/dsp/drums/analog_bass_drum.h \n
to an independent module. \n
Original code written by Emilie Gillet in 2016. \n
*/
class AnalogBassDrum
{
public:
AnalogBassDrum() {}
~AnalogBassDrum() {}

/** Initialize the module
\param sample_rate Audio engine sample rate
*/
void Init(float sample_rate);

/** Get the next sample.
\param trigger True strikes the drum. Defaults to false.
*/
float Process(bool trigger = false);

/** Strikes the drum. */
void Trig();

/** Set the bassdrum to play infinitely
\param sustain True = infinite length
*/
void SetSustain(bool sustain);

/** Set a small accent.
\param accent Works 0-1
*/
void SetAccent(float accent);

/** Set the drum's root frequency
\param f0 Frequency in Hz
*/
void SetFreq(float f0);

/** Set the amount of click.
\param tone Works 0-1.
*/
void SetTone(float tone);

/** Set the decay length of the drum.
\param decay Works best 0-1.
*/
void SetDecay(float decay);

/** Set the amount of fm attack. Works together with self fm.
\param attack_fm_amount Works best 0-1.
*/
void SetAttackFmAmount(float attack_fm_amount);

/**Set the amount of self fm. Also affects fm attack, and volume decay.
\param self_fm_amount Works best 0-1.
*/
void SetSelfFmAmount(float self_fm_amount);

private:
inline float Diode(float x);

float sample_rate_;

float accent_, f0_, tone_, decay_;
float attack_fm_amount_, self_fm_amount_;

bool trig_, sustain_;

int pulse_remaining_samples_;
int fm_pulse_remaining_samples_;
float pulse_;
float pulse_height_;
float pulse_lp_;
float fm_pulse_lp_;
float retrig_pulse_;
float lp_out_;
float tone_lp_;
float sustain_gain_;

Svf resonator_;

//for use in sin + cos osc. in sustain mode
float phase_;
};
} // namespace daisysp
#endif
#endif

0 comments on commit cb07ddd

Please sign in to comment.