Added DaisySP
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
#include "dsp.h"
|
||||
#include "analogbassdrum.h"
|
||||
#include <cmath>
|
||||
|
||||
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 = static_cast<int>(1.0e-3f * sample_rate_);
|
||||
const int kFMPulseDuration = static_cast<int>(6.0e-3f * sample_rate_);
|
||||
const float kPulseDecayTime = 0.2e-3f * sample_rate_;
|
||||
const float kPulseFilterTime = 0.1e-3f * 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(.4f * 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;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright (c) 2020 Electrosmith, Corp, Emilie Gillet
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef DSY_ANALOG_BD_H
|
||||
#define DSY_ANALOG_BD_H
|
||||
|
||||
#include <stdint.h>
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "Synthesis/oscillator.h"
|
||||
#include "Filters/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,0 +1,219 @@
|
||||
#include "dsp.h"
|
||||
#include "analogsnaredrum.h"
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace daisysp;
|
||||
|
||||
static const int kNumModes = 5;
|
||||
|
||||
void AnalogSnareDrum::Init(float sample_rate)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
|
||||
trig_ = false;
|
||||
|
||||
pulse_remaining_samples_ = 0;
|
||||
pulse_ = 0.0f;
|
||||
pulse_height_ = 0.0f;
|
||||
pulse_lp_ = 0.0f;
|
||||
noise_envelope_ = 0.0f;
|
||||
sustain_gain_ = 0.0f;
|
||||
|
||||
SetSustain(false);
|
||||
SetAccent(.6f);
|
||||
SetFreq(200.f);
|
||||
SetDecay(.3f);
|
||||
SetSnappy(.7f);
|
||||
SetTone(.5f);
|
||||
|
||||
for(int i = 0; i < kNumModes; ++i)
|
||||
{
|
||||
resonator_[i].Init(sample_rate_);
|
||||
phase_[i] = 0.f;
|
||||
}
|
||||
noise_filter_.Init(sample_rate_);
|
||||
}
|
||||
|
||||
/** Trigger the drum */
|
||||
void AnalogSnareDrum::Trig()
|
||||
{
|
||||
trig_ = true;
|
||||
}
|
||||
|
||||
void AnalogSnareDrum::SetSustain(bool sustain)
|
||||
{
|
||||
sustain_ = sustain;
|
||||
}
|
||||
|
||||
void AnalogSnareDrum::SetAccent(float accent)
|
||||
{
|
||||
accent_ = fclamp(accent, 0.f, 1.f);
|
||||
}
|
||||
|
||||
void AnalogSnareDrum::SetFreq(float f0)
|
||||
{
|
||||
f0 = f0 / sample_rate_;
|
||||
f0_ = fclamp(f0, 0.f, .4f);
|
||||
}
|
||||
|
||||
void AnalogSnareDrum::SetTone(float tone)
|
||||
{
|
||||
tone_ = fclamp(tone, 0.f, 1.f);
|
||||
tone_ *= 2.f;
|
||||
}
|
||||
|
||||
void AnalogSnareDrum::SetDecay(float decay)
|
||||
{
|
||||
decay_ = decay;
|
||||
return;
|
||||
decay_ = fmax(decay, 0.f);
|
||||
}
|
||||
|
||||
void AnalogSnareDrum::SetSnappy(float snappy)
|
||||
{
|
||||
snappy_ = fclamp(snappy, 0.f, 1.f);
|
||||
}
|
||||
|
||||
float AnalogSnareDrum::Process(bool trigger)
|
||||
{
|
||||
const float decay_xt = decay_ * (1.0f + decay_ * (decay_ - 1.0f));
|
||||
const int kTriggerPulseDuration = 1.0e-3 * sample_rate_;
|
||||
const float kPulseDecayTime = 0.1e-3 * sample_rate_;
|
||||
const float q = 2000.0f * powf(2.f, kOneTwelfth * decay_xt * 84.0f);
|
||||
const float noise_envelope_decay
|
||||
= 1.0f
|
||||
- 0.0017f
|
||||
* powf(2.f,
|
||||
kOneTwelfth * (-decay_ * (50.0f + snappy_ * 10.0f)));
|
||||
const float exciter_leak = snappy_ * (2.0f - snappy_) * 0.1f;
|
||||
|
||||
float snappy = snappy_ * 1.1f - 0.05f;
|
||||
snappy = fclamp(snappy, 0.0f, 1.0f);
|
||||
|
||||
float tone = tone_;
|
||||
|
||||
if(trigger || trig_)
|
||||
{
|
||||
trig_ = false;
|
||||
pulse_remaining_samples_ = kTriggerPulseDuration;
|
||||
pulse_height_ = 3.0f + 7.0f * accent_;
|
||||
noise_envelope_ = 2.0f;
|
||||
}
|
||||
|
||||
static const float kModeFrequencies[kNumModes]
|
||||
= {1.00f, 2.00f, 3.18f, 4.16f, 5.62f};
|
||||
|
||||
float f[kNumModes];
|
||||
float gain[kNumModes];
|
||||
|
||||
for(int i = 0; i < kNumModes; ++i)
|
||||
{
|
||||
f[i] = fmin(f0_ * kModeFrequencies[i], 0.499f);
|
||||
resonator_[i].SetFreq(f[i] * sample_rate_);
|
||||
// resonator_[i].SetRes(1.0f + f[i] * (i == 0 ? q : q * 0.25f));
|
||||
resonator_[i].SetRes((f[i] * (i == 0 ? q : q * 0.25f)) * .2);
|
||||
}
|
||||
|
||||
if(tone < 0.666667f)
|
||||
{
|
||||
// 808-style (2 modes)
|
||||
tone *= 1.5f;
|
||||
gain[0] = 1.5f + (1.0f - tone) * (1.0f - tone) * 4.5f;
|
||||
gain[1] = 2.0f * tone + 0.15f;
|
||||
for(int i = 2; i < kNumModes; i++)
|
||||
{
|
||||
gain[i] = 0.f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// What the 808 could have been if there were extra modes!
|
||||
tone = (tone - 0.666667f) * 3.0f;
|
||||
gain[0] = 1.5f - tone * 0.5f;
|
||||
gain[1] = 2.15f - tone * 0.7f;
|
||||
for(int i = 2; i < kNumModes; ++i)
|
||||
{
|
||||
gain[i] = tone;
|
||||
tone *= tone;
|
||||
}
|
||||
}
|
||||
|
||||
float f_noise = f0_ * 16.0f;
|
||||
fclamp(f_noise, 0.0f, 0.499f);
|
||||
noise_filter_.SetFreq(f_noise * sample_rate_);
|
||||
//noise_filter_.SetRes(1.0f + f_noise * 1.5f);
|
||||
noise_filter_.SetRes(f_noise * 1.5f);
|
||||
|
||||
// Q45 / Q46
|
||||
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_;
|
||||
}
|
||||
|
||||
float sustain_gain_value = sustain_gain_ = accent_ * decay_;
|
||||
|
||||
// R189 / C57 / R190 + C58 / C59 / R197 / R196 / IC14
|
||||
pulse_lp_ = fclamp(pulse_lp_, pulse, 0.75f);
|
||||
|
||||
float shell = 0.0f;
|
||||
for(int i = 0; i < kNumModes; ++i)
|
||||
{
|
||||
float excitation
|
||||
= i == 0 ? (pulse - pulse_lp_) + 0.006f * pulse : 0.026f * pulse;
|
||||
|
||||
phase_[i] += f[i];
|
||||
phase_[i] = phase_[i] >= 1.f ? phase_[i] - 1.f : phase_[i];
|
||||
|
||||
resonator_[i].Process(excitation);
|
||||
|
||||
shell += gain[i]
|
||||
* (sustain_
|
||||
? sin(phase_[i] * TWOPI_F) * sustain_gain_value * 0.25f
|
||||
: resonator_[i].Band() + excitation * exciter_leak);
|
||||
}
|
||||
shell = SoftClip(shell);
|
||||
|
||||
// C56 / R194 / Q48 / C54 / R188 / D54
|
||||
float noise = 2.0f * rand() * kRandFrac - 1.0f;
|
||||
if(noise < 0.0f)
|
||||
noise = 0.0f;
|
||||
noise_envelope_ *= noise_envelope_decay;
|
||||
noise *= (sustain_ ? sustain_gain_value : noise_envelope_) * snappy * 2.0f;
|
||||
|
||||
// C66 / R201 / C67 / R202 / R203 / Q49
|
||||
noise_filter_.Process(noise);
|
||||
noise = noise_filter_.Band();
|
||||
|
||||
// IC13
|
||||
return noise + shell * (1.0f - snappy);
|
||||
}
|
||||
|
||||
inline float AnalogSnareDrum::SoftLimit(float x)
|
||||
{
|
||||
return x * (27.0f + x * x) / (27.0f + 9.0f * x * x);
|
||||
}
|
||||
|
||||
inline float AnalogSnareDrum::SoftClip(float x)
|
||||
{
|
||||
if(x < -3.0f)
|
||||
{
|
||||
return -1.0f;
|
||||
}
|
||||
else if(x > 3.0f)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SoftLimit(x);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright (c) 2020 Electrosmith, Corp, Emilie Gillet
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef DSY_ANALOG_SNARE_H
|
||||
#define DSY_ANALOG_SNARE_H
|
||||
|
||||
#include "Filters/svf.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#ifdef __cplusplus
|
||||
|
||||
/** @file analogsnaredrum.h */
|
||||
|
||||
namespace daisysp
|
||||
{
|
||||
/**
|
||||
@brief 808 snare drum model, revisited.
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
Ported from pichenettes/eurorack/plaits/dsp/drums/analog_snare_drum.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class AnalogSnareDrum
|
||||
{
|
||||
public:
|
||||
AnalogSnareDrum() {}
|
||||
~AnalogSnareDrum() {}
|
||||
|
||||
static const int kNumModes = 5;
|
||||
|
||||
/** Init the module
|
||||
\param sample_rate Audio engine sample rate
|
||||
*/
|
||||
void Init(float sample_rate);
|
||||
|
||||
/** Get the next sample
|
||||
\param trigger Hit the drum with true. Defaults to false.
|
||||
*/
|
||||
float Process(bool trigger = false);
|
||||
|
||||
/** Trigger the drum */
|
||||
void Trig();
|
||||
|
||||
/** Init the module
|
||||
\param sample_rate Audio engine sample rate
|
||||
*/
|
||||
void SetSustain(bool sustain);
|
||||
|
||||
/** Set how much accent to use
|
||||
\param accent Works 0-1.
|
||||
*/
|
||||
void SetAccent(float accent);
|
||||
|
||||
/** Set the drum's root frequency
|
||||
\param f0 Freq in Hz
|
||||
*/
|
||||
void SetFreq(float f0);
|
||||
|
||||
/** Set the brightness of the drum tone.
|
||||
\param tone Works 0-1. 1 = bright, 0 = dark.
|
||||
*/
|
||||
void SetTone(float tone);
|
||||
|
||||
/** Set the length of the drum decay
|
||||
\param decay Works with positive numbers
|
||||
*/
|
||||
void SetDecay(float decay);
|
||||
|
||||
/** Sets the mix between snare and drum.
|
||||
\param snappy 1 = just snare. 0 = just drum.
|
||||
*/
|
||||
void SetSnappy(float snappy);
|
||||
|
||||
private:
|
||||
float sample_rate_;
|
||||
|
||||
float f0_, tone_, accent_, snappy_, decay_;
|
||||
bool sustain_;
|
||||
bool trig_;
|
||||
|
||||
inline float SoftLimit(float x);
|
||||
inline float SoftClip(float x);
|
||||
|
||||
int pulse_remaining_samples_;
|
||||
float pulse_;
|
||||
float pulse_height_;
|
||||
float pulse_lp_;
|
||||
float noise_envelope_;
|
||||
float sustain_gain_;
|
||||
|
||||
Svf resonator_[kNumModes];
|
||||
Svf noise_filter_;
|
||||
|
||||
// Replace the resonators in "free running" (sustain) mode.
|
||||
float phase_[kNumModes];
|
||||
};
|
||||
} // namespace daisysp
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "dsp.h"
|
||||
#include "hihat.h"
|
||||
#include <math.h>
|
||||
|
||||
using namespace daisysp;
|
||||
|
||||
void SquareNoise::Init(float sample_rate)
|
||||
{
|
||||
for(int i = 0; i < 6; i++)
|
||||
{
|
||||
phase_[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
float SquareNoise::Process(float f0)
|
||||
{
|
||||
const float ratios[6] = {// Nominal f0: 414 Hz
|
||||
1.0f,
|
||||
1.304f,
|
||||
1.466f,
|
||||
1.787f,
|
||||
1.932f,
|
||||
2.536f};
|
||||
|
||||
uint32_t increment[6];
|
||||
uint32_t phase[6];
|
||||
for(int i = 0; i < 6; ++i)
|
||||
{
|
||||
float f = f0 * ratios[i];
|
||||
if(f >= 0.499f)
|
||||
f = 0.499f;
|
||||
increment[i] = static_cast<uint32_t>(f * 4294967296.0f);
|
||||
phase[i] = phase_[i];
|
||||
}
|
||||
|
||||
phase[0] += increment[0];
|
||||
phase[1] += increment[1];
|
||||
phase[2] += increment[2];
|
||||
phase[3] += increment[3];
|
||||
phase[4] += increment[4];
|
||||
phase[5] += increment[5];
|
||||
uint32_t noise = 0;
|
||||
noise += (phase[0] >> 31);
|
||||
noise += (phase[1] >> 31);
|
||||
noise += (phase[2] >> 31);
|
||||
noise += (phase[3] >> 31);
|
||||
noise += (phase[4] >> 31);
|
||||
noise += (phase[5] >> 31);
|
||||
|
||||
for(int i = 0; i < 6; ++i)
|
||||
{
|
||||
phase_[i] = phase[i];
|
||||
}
|
||||
|
||||
return 0.33f * static_cast<float>(noise) - 1.0f;
|
||||
}
|
||||
|
||||
void RingModNoise::Init(float sample_rate)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
|
||||
for(int i = 0; i < 6; ++i)
|
||||
{
|
||||
oscillator_[i].Init(sample_rate_);
|
||||
}
|
||||
}
|
||||
|
||||
float RingModNoise::Process(float f0)
|
||||
{
|
||||
const float ratio = f0 / (0.01f + f0);
|
||||
const float f1a = 200.0f / sample_rate_ * ratio;
|
||||
const float f1b = 7530.0f / sample_rate_ * ratio;
|
||||
const float f2a = 510.0f / sample_rate_ * ratio;
|
||||
const float f2b = 8075.0f / sample_rate_ * ratio;
|
||||
const float f3a = 730.0f / sample_rate_ * ratio;
|
||||
const float f3b = 10500.0f / sample_rate_ * ratio;
|
||||
|
||||
float out = ProcessPair(&oscillator_[0], f1a, f1b);
|
||||
out += ProcessPair(&oscillator_[2], f2a, f2b);
|
||||
out += ProcessPair(&oscillator_[4], f3a, f3b);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
float RingModNoise::ProcessPair(Oscillator* osc, float f1, float f2)
|
||||
{
|
||||
osc[0].SetWaveform(Oscillator::WAVE_SQUARE);
|
||||
osc[0].SetFreq(f1 * sample_rate_);
|
||||
float temp_1 = osc[0].Process();
|
||||
|
||||
osc[1].SetWaveform(Oscillator::WAVE_SAW);
|
||||
osc[1].SetFreq(f2 * sample_rate_);
|
||||
float temp_2 = osc[1].Process();
|
||||
|
||||
return temp_1 * temp_2;
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
Copyright (c) 2020 Electrosmith, Corp, Emilie Gillet
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef DSY_HIHAT_H
|
||||
#define DSY_HIHAT_H
|
||||
|
||||
#include "Filters/svf.h"
|
||||
#include "Synthesis/oscillator.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef __cplusplus
|
||||
|
||||
/** @file hihat.h */
|
||||
|
||||
namespace daisysp
|
||||
{
|
||||
/**
|
||||
@brief 808 style "metallic noise" with 6 square oscillators.
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
Ported from pichenettes/eurorack/plaits/dsp/drums/hihat.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class SquareNoise
|
||||
{
|
||||
public:
|
||||
SquareNoise() {}
|
||||
~SquareNoise() {}
|
||||
|
||||
void Init(float sample_rate);
|
||||
|
||||
float Process(float f0);
|
||||
|
||||
private:
|
||||
uint32_t phase_[6];
|
||||
};
|
||||
|
||||
/**
|
||||
@brief Ring mod style metallic noise generator.
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
Ported from pichenettes/eurorack/plaits/dsp/drums/hihat.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class RingModNoise
|
||||
{
|
||||
public:
|
||||
RingModNoise() {}
|
||||
~RingModNoise() {}
|
||||
|
||||
void Init(float sample_rate);
|
||||
|
||||
float Process(float f0);
|
||||
|
||||
private:
|
||||
float ProcessPair(Oscillator* osc, float f1, float f2);
|
||||
Oscillator oscillator_[6];
|
||||
|
||||
float sample_rate_;
|
||||
};
|
||||
|
||||
/**
|
||||
@brief Swing type VCA
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
Ported from pichenettes/eurorack/plaits/dsp/drums/hihat.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class SwingVCA
|
||||
{
|
||||
public:
|
||||
float operator()(float s, float gain)
|
||||
{
|
||||
s *= s > 0.0f ? 10.0f : 0.1f;
|
||||
s = s / (1.0f + fabsf(s));
|
||||
return (s + 1.0f) * gain;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@brief Linear type VCA
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
Ported from pichenettes/eurorack/plaits/dsp/drums/hihat.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class LinearVCA
|
||||
{
|
||||
public:
|
||||
float operator()(float s, float gain) { return s * gain; }
|
||||
};
|
||||
|
||||
/**
|
||||
@brief 808 HH, with a few extra parameters to push things to the CY territory...
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
The template parameter MetallicNoiseSource allows another kind of "metallic \n
|
||||
noise" to be used, for results which are more similar to KR-55 or FM hi-hats. \n \n
|
||||
Ported from pichenettes/eurorack/plaits/dsp/drums/hihat.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
template <typename MetallicNoiseSource = SquareNoise,
|
||||
typename VCA = LinearVCA,
|
||||
bool resonance = true>
|
||||
class HiHat
|
||||
{
|
||||
public:
|
||||
HiHat() {}
|
||||
~HiHat() {}
|
||||
|
||||
/** Initialize the module
|
||||
\param sample_rate Audio engine sample rate
|
||||
*/
|
||||
void Init(float sample_rate)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
|
||||
trig_ = false;
|
||||
|
||||
envelope_ = 0.0f;
|
||||
noise_clock_ = 0.0f;
|
||||
noise_sample_ = 0.0f;
|
||||
sustain_gain_ = 0.0f;
|
||||
|
||||
SetFreq(3000.f);
|
||||
SetTone(.5f);
|
||||
SetDecay(.2f);
|
||||
SetNoisiness(.8f);
|
||||
SetAccent(.8f);
|
||||
SetSustain(false);
|
||||
|
||||
metallic_noise_.Init(sample_rate_);
|
||||
noise_coloration_svf_.Init(sample_rate_);
|
||||
hpf_.Init(sample_rate_);
|
||||
}
|
||||
|
||||
/** Get the next sample
|
||||
\param trigger Hit the hihat with true. Defaults to false.
|
||||
*/
|
||||
float Process(bool trigger = false)
|
||||
{
|
||||
const float envelope_decay
|
||||
= 1.0f - 0.003f * SemitonesToRatio(-decay_ * 84.0f);
|
||||
const float cut_decay
|
||||
= 1.0f - 0.0025f * SemitonesToRatio(-decay_ * 36.0f);
|
||||
|
||||
if(trigger || trig_)
|
||||
{
|
||||
trig_ = false;
|
||||
|
||||
envelope_
|
||||
= (1.5f + 0.5f * (1.0f - decay_)) * (0.3f + 0.7f * accent_);
|
||||
}
|
||||
|
||||
// Process the metallic noise.
|
||||
float out = metallic_noise_.Process(2.0f * f0_);
|
||||
|
||||
// Apply BPF on the metallic noise.
|
||||
float cutoff = 150.0f / sample_rate_ * SemitonesToRatio(tone_ * 72.0f);
|
||||
|
||||
cutoff = fclamp(cutoff, 0.0f, 16000.0f / sample_rate_);
|
||||
|
||||
|
||||
noise_coloration_svf_.SetFreq(cutoff * sample_rate_);
|
||||
noise_coloration_svf_.SetRes(resonance ? 3.0f + 6.0f * tone_ : 1.0f);
|
||||
|
||||
noise_coloration_svf_.Process(out);
|
||||
out = noise_coloration_svf_.Band();
|
||||
|
||||
// This is not at all part of the 808 circuit! But to add more variety, we
|
||||
// add a variable amount of clocked noise to the output of the 6 schmitt
|
||||
// trigger oscillators.
|
||||
float noise_f = f0_ * (16.0f + 16.0f * (1.0f - noisiness_));
|
||||
noise_f = fclamp(noise_f, 0.0f, 0.5f);
|
||||
|
||||
noise_clock_ += noise_f;
|
||||
if(noise_clock_ >= 1.0f)
|
||||
{
|
||||
noise_clock_ -= 1.0f;
|
||||
noise_sample_ = rand() * kRandFrac - 0.5f;
|
||||
}
|
||||
out += noisiness_ * (noise_sample_ - out);
|
||||
|
||||
// Apply VCA.
|
||||
sustain_gain_ = accent_ * decay_;
|
||||
VCA vca;
|
||||
envelope_ *= envelope_ > 0.5f ? envelope_decay : cut_decay;
|
||||
out = vca(out, sustain_ ? sustain_gain_ : envelope_);
|
||||
|
||||
hpf_.SetFreq(cutoff * sample_rate_);
|
||||
hpf_.SetRes(.5f);
|
||||
hpf_.Process(out);
|
||||
out = hpf_.High();
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Trigger the hihat */
|
||||
void Trig() { trig_ = true; }
|
||||
|
||||
/** Make the hihat ring out infinitely.
|
||||
\param sustain True = infinite sustain.
|
||||
*/
|
||||
void SetSustain(bool sustain) { sustain_ = sustain; }
|
||||
|
||||
/** Set how much accent to use
|
||||
\param accent Works 0-1.
|
||||
*/
|
||||
void SetAccent(float accent) { accent_ = fclamp(accent, 0.f, 1.f); }
|
||||
|
||||
/** Set the hihat tone's root frequency
|
||||
\param f0 Freq in Hz
|
||||
*/
|
||||
void SetFreq(float f0)
|
||||
{
|
||||
f0 /= sample_rate_;
|
||||
f0_ = fclamp(f0, 0.f, 1.f);
|
||||
}
|
||||
|
||||
/** Set the overall brightness / darkness of the hihat.
|
||||
\param tone Works from 0-1.
|
||||
*/
|
||||
void SetTone(float tone) { tone_ = fclamp(tone, 0.f, 1.f); }
|
||||
|
||||
/** Set the length of the hihat decay
|
||||
\param decay Works > 0. Tuned for 0-1.
|
||||
*/
|
||||
void SetDecay(float decay)
|
||||
{
|
||||
decay_ = fmax(decay, 0.f);
|
||||
decay_ *= 1.7;
|
||||
decay_ -= 1.2;
|
||||
}
|
||||
|
||||
/** Sets the mix between tone and noise
|
||||
\param snappy 1 = just noise. 0 = just tone.
|
||||
*/
|
||||
void SetNoisiness(float noisiness)
|
||||
{
|
||||
noisiness_ = fclamp(noisiness, 0.f, 1.f);
|
||||
noisiness_ *= noisiness_;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
float sample_rate_;
|
||||
|
||||
float accent_, f0_, tone_, decay_, noisiness_;
|
||||
bool sustain_;
|
||||
bool trig_;
|
||||
|
||||
float SemitonesToRatio(float in) { return powf(2.f, in * kOneTwelfth); }
|
||||
|
||||
float envelope_;
|
||||
float noise_clock_;
|
||||
float noise_sample_;
|
||||
float sustain_gain_;
|
||||
|
||||
MetallicNoiseSource metallic_noise_;
|
||||
Svf noise_coloration_svf_;
|
||||
Svf hpf_;
|
||||
};
|
||||
} // namespace daisysp
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,231 @@
|
||||
#include "synthbassdrum.h"
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace daisysp;
|
||||
|
||||
void SyntheticBassDrumClick::Init(float sample_rate)
|
||||
{
|
||||
lp_ = 0.0f;
|
||||
hp_ = 0.0f;
|
||||
filter_.Init(sample_rate);
|
||||
filter_.SetFreq(5000.0f);
|
||||
filter_.SetRes(1.f); //2.f
|
||||
}
|
||||
|
||||
float SyntheticBassDrumClick::Process(float in)
|
||||
{
|
||||
//SLOPE(lp_, in, 0.5f, 0.1f);
|
||||
float error = in - lp_;
|
||||
lp_ += (error > 0 ? .5f : .1f) * error;
|
||||
|
||||
fonepole(hp_, lp_, 0.04f);
|
||||
filter_.Process(lp_ - hp_);
|
||||
return filter_.Low();
|
||||
}
|
||||
|
||||
void SyntheticBassDrumAttackNoise::Init()
|
||||
{
|
||||
lp_ = 0.0f;
|
||||
hp_ = 0.0f;
|
||||
}
|
||||
|
||||
float SyntheticBassDrumAttackNoise::Process()
|
||||
{
|
||||
float sample = rand() * kRandFrac;
|
||||
fonepole(lp_, sample, 0.05f);
|
||||
fonepole(hp_, lp_, 0.005f);
|
||||
return lp_ - hp_;
|
||||
}
|
||||
|
||||
void SyntheticBassDrum::Init(float sample_rate)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
|
||||
trig_ = false;
|
||||
|
||||
phase_ = 0.0f;
|
||||
phase_noise_ = 0.0f;
|
||||
f0_ = 0.0f;
|
||||
fm_ = 0.0f;
|
||||
fm_lp_ = 0.0f;
|
||||
body_env_lp_ = 0.0f;
|
||||
body_env_ = 0.0f;
|
||||
body_env_pulse_width_ = 0;
|
||||
fm_pulse_width_ = 0;
|
||||
tone_lp_ = 0.0f;
|
||||
sustain_gain_ = 0.0f;
|
||||
|
||||
SetFreq(100.f);
|
||||
SetSustain(false);
|
||||
SetAccent(.2f);
|
||||
SetTone(.6f);
|
||||
SetDecay(.7f);
|
||||
SetDirtiness(.3f);
|
||||
SetFmEnvelopeAmount(.6);
|
||||
SetFmEnvelopeDecay(.3);
|
||||
|
||||
click_.Init(sample_rate);
|
||||
noise_.Init();
|
||||
}
|
||||
|
||||
inline float SyntheticBassDrum::DistortedSine(float phase,
|
||||
float phase_noise,
|
||||
float dirtiness)
|
||||
{
|
||||
phase += phase_noise * dirtiness;
|
||||
|
||||
//MAKE_INTEGRAL_FRACTIONAL(phase);
|
||||
int32_t phase_integral = static_cast<int32_t>(phase);
|
||||
float phase_fractional = phase - static_cast<float>(phase_integral);
|
||||
|
||||
phase = phase_fractional;
|
||||
float triangle = (phase < 0.5f ? phase : 1.0f - phase) * 4.0f - 1.0f;
|
||||
float sine = 2.0f * triangle / (1.0f + fabsf(triangle));
|
||||
float clean_sine = sinf(TWOPI_F * (phase + 0.75f));
|
||||
return sine + (1.0f - dirtiness) * (clean_sine - sine);
|
||||
}
|
||||
|
||||
inline float SyntheticBassDrum::TransistorVCA(float s, float gain)
|
||||
{
|
||||
s = (s - 0.6f) * gain;
|
||||
return 3.0f * s / (2.0f + fabsf(s)) + gain * 0.3f;
|
||||
}
|
||||
|
||||
float SyntheticBassDrum::Process(bool trigger)
|
||||
{
|
||||
float dirtiness = dirtiness_;
|
||||
dirtiness *= fmax(1.0f - 8.0f * new_f0_, 0.0f);
|
||||
|
||||
const float fm_decay
|
||||
= 1.0f
|
||||
- 1.0f / (0.008f * (1.0f + fm_envelope_decay_ * 4.0f) * sample_rate_);
|
||||
|
||||
const float body_env_decay
|
||||
= 1.0f
|
||||
- 1.0f / (0.02f * sample_rate_)
|
||||
* powf(2.f, (-decay_ * 60.0f) * kOneTwelfth);
|
||||
const float transient_env_decay = 1.0f - 1.0f / (0.005f * sample_rate_);
|
||||
const float tone_f = fmin(
|
||||
4.0f * new_f0_ * powf(2.f, (tone_ * 108.0f) * kOneTwelfth), 1.0f);
|
||||
const float transient_level = tone_;
|
||||
|
||||
if(trigger || trig_)
|
||||
{
|
||||
trig_ = false;
|
||||
fm_ = 1.0f;
|
||||
body_env_ = transient_env_ = 0.3f + 0.7f * accent_;
|
||||
body_env_pulse_width_ = sample_rate_ * 0.001f;
|
||||
fm_pulse_width_ = sample_rate_ * 0.0013f;
|
||||
}
|
||||
|
||||
sustain_gain_ = accent_ * decay_;
|
||||
|
||||
fonepole(phase_noise_, rand() * kRandFrac - 0.5f, 0.002f);
|
||||
|
||||
float mix = 0.0f;
|
||||
|
||||
if(sustain_)
|
||||
{
|
||||
f0_ = new_f0_;
|
||||
phase_ += f0_;
|
||||
if(phase_ >= 1.0f)
|
||||
{
|
||||
phase_ -= 1.0f;
|
||||
}
|
||||
float body = DistortedSine(phase_, phase_noise_, dirtiness);
|
||||
mix -= TransistorVCA(body, sustain_gain_);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(fm_pulse_width_)
|
||||
{
|
||||
--fm_pulse_width_;
|
||||
phase_ = 0.25f;
|
||||
}
|
||||
else
|
||||
{
|
||||
fm_ *= fm_decay;
|
||||
float fm = 1.0f + fm_envelope_amount_ * 3.5f * fm_lp_;
|
||||
f0_ = new_f0_;
|
||||
phase_ += fmin(f0_ * fm, 0.5f);
|
||||
if(phase_ >= 1.0f)
|
||||
{
|
||||
phase_ -= 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if(body_env_pulse_width_)
|
||||
{
|
||||
--body_env_pulse_width_;
|
||||
}
|
||||
else
|
||||
{
|
||||
body_env_ *= body_env_decay;
|
||||
transient_env_ *= transient_env_decay;
|
||||
}
|
||||
|
||||
const float envelope_lp_f = 0.1f;
|
||||
fonepole(body_env_lp_, body_env_, envelope_lp_f);
|
||||
fonepole(transient_env_lp_, transient_env_, envelope_lp_f);
|
||||
fonepole(fm_lp_, fm_, envelope_lp_f);
|
||||
|
||||
float body = DistortedSine(phase_, phase_noise_, dirtiness);
|
||||
float transient = click_.Process(body_env_pulse_width_ ? 0.0f : 1.0f)
|
||||
+ noise_.Process();
|
||||
|
||||
mix -= TransistorVCA(body, body_env_lp_);
|
||||
mix -= transient * transient_env_lp_ * transient_level;
|
||||
}
|
||||
|
||||
fonepole(tone_lp_, mix, tone_f);
|
||||
return tone_lp_;
|
||||
}
|
||||
|
||||
void SyntheticBassDrum::Trig()
|
||||
{
|
||||
trig_ = true;
|
||||
}
|
||||
|
||||
void SyntheticBassDrum::SetSustain(bool sustain)
|
||||
{
|
||||
sustain_ = sustain;
|
||||
}
|
||||
|
||||
void SyntheticBassDrum::SetAccent(float accent)
|
||||
{
|
||||
accent_ = fclamp(accent, 0.f, 1.f);
|
||||
}
|
||||
|
||||
void SyntheticBassDrum::SetFreq(float freq)
|
||||
{
|
||||
freq /= sample_rate_;
|
||||
new_f0_ = fclamp(freq, 0.f, 1.f);
|
||||
}
|
||||
|
||||
void SyntheticBassDrum::SetTone(float tone)
|
||||
{
|
||||
tone_ = fclamp(tone, 0.f, 1.f);
|
||||
}
|
||||
|
||||
void SyntheticBassDrum::SetDecay(float decay)
|
||||
{
|
||||
decay = fclamp(decay, 0.f, 1.f);
|
||||
decay_ = decay * decay;
|
||||
}
|
||||
|
||||
void SyntheticBassDrum::SetDirtiness(float dirtiness)
|
||||
{
|
||||
dirtiness_ = fclamp(dirtiness, 0.f, 1.f);
|
||||
}
|
||||
|
||||
void SyntheticBassDrum::SetFmEnvelopeAmount(float fm_envelope_amount)
|
||||
{
|
||||
fm_envelope_amount_ = fclamp(fm_envelope_amount, 0.f, 1.f);
|
||||
}
|
||||
|
||||
void SyntheticBassDrum::SetFmEnvelopeDecay(float fm_envelope_decay)
|
||||
{
|
||||
fm_envelope_decay = fclamp(fm_envelope_decay, 0.f, 1.f);
|
||||
fm_envelope_decay_ = fm_envelope_decay * fm_envelope_decay;
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
Copyright (c) 2020 Electrosmith, Corp, Emilie Gillet
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef DSY_SYNTHBD_H
|
||||
#define DSY_SYNTHBD_H
|
||||
|
||||
#include "Filters/svf.h"
|
||||
#include "Utility/dsp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#ifdef __cplusplus
|
||||
|
||||
/** @file synthbassdrum.h */
|
||||
|
||||
namespace daisysp
|
||||
{
|
||||
/**
|
||||
@brief Click noise for SyntheticBassDrum
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
Ported from pichenettes/eurorack/plaits/dsp/drums/synthetic_bass_drum.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class SyntheticBassDrumClick
|
||||
{
|
||||
public:
|
||||
SyntheticBassDrumClick() {}
|
||||
~SyntheticBassDrumClick() {}
|
||||
|
||||
/** Init the module
|
||||
\param sample_rate Audio engine sample rate.
|
||||
*/
|
||||
void Init(float sample_rate);
|
||||
|
||||
/** Get the next sample.
|
||||
\param in Trigger the click.
|
||||
*/
|
||||
float Process(float in);
|
||||
|
||||
private:
|
||||
float lp_;
|
||||
float hp_;
|
||||
Svf filter_;
|
||||
};
|
||||
|
||||
/**
|
||||
@brief Attack Noise generator for SyntheticBassDrum.
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
Ported from pichenettes/eurorack/plaits/dsp/drums/synthetic_bass_drum.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class SyntheticBassDrumAttackNoise
|
||||
{
|
||||
public:
|
||||
SyntheticBassDrumAttackNoise() {}
|
||||
~SyntheticBassDrumAttackNoise() {}
|
||||
|
||||
/** Init the module */
|
||||
void Init();
|
||||
|
||||
/** Get the next sample. */
|
||||
float Process();
|
||||
|
||||
private:
|
||||
float lp_;
|
||||
float hp_;
|
||||
};
|
||||
|
||||
/**
|
||||
@brief Naive bass drum model (modulated oscillator with FM + envelope).
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
Inadvertently 909-ish. \n \n
|
||||
Ported from pichenettes/eurorack/plaits/dsp/drums/synthetic_bass_drum.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class SyntheticBassDrum
|
||||
{
|
||||
public:
|
||||
SyntheticBassDrum() {}
|
||||
~SyntheticBassDrum() {}
|
||||
|
||||
/** Init the module
|
||||
\param sample_rate Audio engine sample rate.
|
||||
*/
|
||||
void Init(float sample_rate);
|
||||
|
||||
/** Generates a distorted sine wave */
|
||||
inline float DistortedSine(float phase, float phase_noise, float dirtiness);
|
||||
|
||||
/** Transistor VCA simulation.
|
||||
\param s Input sample.
|
||||
\param gain VCA gain.
|
||||
*/
|
||||
inline float TransistorVCA(float s, float gain);
|
||||
|
||||
/** Get the next sample.
|
||||
\param trigger True triggers the BD. This is optional.
|
||||
*/
|
||||
float Process(bool trigger = false);
|
||||
|
||||
/** Trigger the drum */
|
||||
void Trig();
|
||||
|
||||
/** Allows the drum to play continuously
|
||||
\param sustain True sets the drum on infinite sustain.
|
||||
*/
|
||||
void SetSustain(bool sustain);
|
||||
|
||||
/** Sets the amount of accent.
|
||||
\param accent Works 0-1.
|
||||
*/
|
||||
void SetAccent(float accent);
|
||||
|
||||
/** Set the bass drum's root frequency.
|
||||
\param Frequency in Hz.
|
||||
*/
|
||||
void SetFreq(float freq);
|
||||
|
||||
/** Sets the overall bright / darkness of the drum.
|
||||
\param tone Works 0-1.
|
||||
*/
|
||||
void SetTone(float tone);
|
||||
|
||||
/** Sets how long the drum's volume takes to decay.
|
||||
\param Works 0-1.
|
||||
*/
|
||||
void SetDecay(float decay);
|
||||
|
||||
/** Makes things grimy
|
||||
\param dirtiness Works 0-1.
|
||||
*/
|
||||
void SetDirtiness(float dirtiness);
|
||||
|
||||
/** Sets how much of a pitch sweep the drum experiences when triggered.
|
||||
\param fm_envelope_amount Works 0-1.
|
||||
*/
|
||||
void SetFmEnvelopeAmount(float fm_envelope_amount);
|
||||
|
||||
/** Sets how long the initial pitch sweep takes.
|
||||
\param fm_envelope_decay Works 0-1.
|
||||
*/
|
||||
void SetFmEnvelopeDecay(float fm_envelope_decay);
|
||||
|
||||
private:
|
||||
float sample_rate_;
|
||||
|
||||
bool trig_;
|
||||
bool sustain_;
|
||||
float accent_, new_f0_, tone_, decay_;
|
||||
float dirtiness_, fm_envelope_amount_, fm_envelope_decay_;
|
||||
|
||||
float f0_;
|
||||
float phase_;
|
||||
float phase_noise_;
|
||||
|
||||
float fm_;
|
||||
float fm_lp_;
|
||||
float body_env_;
|
||||
float body_env_lp_;
|
||||
float transient_env_;
|
||||
float transient_env_lp_;
|
||||
|
||||
float sustain_gain_;
|
||||
|
||||
float tone_lp_;
|
||||
|
||||
SyntheticBassDrumClick click_;
|
||||
SyntheticBassDrumAttackNoise noise_;
|
||||
|
||||
int body_env_pulse_width_;
|
||||
int fm_pulse_width_;
|
||||
};
|
||||
|
||||
} // namespace daisysp
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,201 @@
|
||||
#include "dsp.h"
|
||||
#include "synthsnaredrum.h"
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace daisysp;
|
||||
|
||||
void SyntheticSnareDrum::Init(float sample_rate)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
|
||||
phase_[0] = 0.0f;
|
||||
phase_[1] = 0.0f;
|
||||
drum_amplitude_ = 0.0f;
|
||||
snare_amplitude_ = 0.0f;
|
||||
fm_ = 0.0f;
|
||||
hold_counter_ = 0;
|
||||
sustain_gain_ = 0.0f;
|
||||
|
||||
SetSustain(false);
|
||||
SetAccent(.6f);
|
||||
SetFreq(200.f);
|
||||
SetFmAmount(.1f);
|
||||
SetDecay(.3f);
|
||||
SetSnappy(.7f);
|
||||
|
||||
trig_ = false;
|
||||
|
||||
drum_lp_.Init(sample_rate_);
|
||||
snare_hp_.Init(sample_rate_);
|
||||
snare_lp_.Init(sample_rate_);
|
||||
}
|
||||
|
||||
inline float SyntheticSnareDrum::DistortedSine(float phase)
|
||||
{
|
||||
float triangle = (phase < 0.5f ? phase : 1.0f - phase) * 4.0f - 1.3f;
|
||||
return 2.0f * triangle / (1.0f + fabsf(triangle));
|
||||
}
|
||||
|
||||
bool even = true;
|
||||
|
||||
float SyntheticSnareDrum::Process(bool trigger)
|
||||
{
|
||||
const float decay_xt = decay_ * (1.0f + decay_ * (decay_ - 1.0f));
|
||||
const float drum_decay
|
||||
= 1.0f
|
||||
- 1.0f / (0.015f * sample_rate_)
|
||||
* powf(2.f,
|
||||
kOneTwelfth
|
||||
* (-decay_xt * 72.0f - fm_amount_ * 12.0f
|
||||
+ snappy_ * 7.0f));
|
||||
|
||||
const float snare_decay
|
||||
= 1.0f
|
||||
- 1.0f / (0.01f * sample_rate_)
|
||||
* powf(2.f, kOneTwelfth * (-decay_ * 60.0f - snappy_ * 7.0f));
|
||||
const float fm_decay = 1.0f - 1.0f / (0.007f * sample_rate_);
|
||||
|
||||
float snappy = snappy_ * 1.1f - 0.05f;
|
||||
snappy = fclamp(snappy, 0.0f, 1.0f);
|
||||
|
||||
const float drum_level = sqrtf(1.0f - snappy);
|
||||
const float snare_level = sqrtf(snappy);
|
||||
|
||||
const float snare_f_min = fmin(10.0f * f0_, 0.5f);
|
||||
const float snare_f_max = fmin(35.0f * f0_, 0.5f);
|
||||
|
||||
snare_hp_.SetFreq(snare_f_min * sample_rate_);
|
||||
snare_lp_.SetFreq(snare_f_max * sample_rate_);
|
||||
snare_lp_.SetRes(0.5f + 2.0f * snappy);
|
||||
|
||||
drum_lp_.SetFreq(3.0f * f0_ * sample_rate_);
|
||||
|
||||
if(trigger || trig_)
|
||||
{
|
||||
trig_ = false;
|
||||
snare_amplitude_ = drum_amplitude_ = 0.3f + 0.7f * accent_;
|
||||
fm_ = 1.0f;
|
||||
phase_[0] = phase_[1] = 0.0f;
|
||||
hold_counter_
|
||||
= static_cast<int>((0.04f + decay_ * 0.03f) * sample_rate_);
|
||||
}
|
||||
|
||||
even = !even;
|
||||
if(sustain_)
|
||||
{
|
||||
sustain_gain_ = snare_amplitude_ = accent_ * decay_;
|
||||
drum_amplitude_ = snare_amplitude_;
|
||||
fm_ = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compute all D envelopes.
|
||||
// The envelope for the drum has a very long tail.
|
||||
// The envelope for the snare has a "hold" stage which lasts between
|
||||
// 40 and 70 ms
|
||||
drum_amplitude_
|
||||
*= (drum_amplitude_ > 0.03f || even) ? drum_decay : 1.0f;
|
||||
if(hold_counter_)
|
||||
{
|
||||
--hold_counter_;
|
||||
}
|
||||
else
|
||||
{
|
||||
snare_amplitude_ *= snare_decay;
|
||||
}
|
||||
fm_ *= fm_decay;
|
||||
}
|
||||
|
||||
// The 909 circuit has a funny kind of oscillator coupling - the signal
|
||||
// leaving Q40's collector and resetting all oscillators allow some
|
||||
// intermodulation.
|
||||
float reset_noise = 0.0f;
|
||||
float reset_noise_amount = (0.125f - f0_) * 8.0f;
|
||||
reset_noise_amount = fclamp(reset_noise_amount, 0.0f, 1.0f);
|
||||
reset_noise_amount *= reset_noise_amount;
|
||||
reset_noise_amount *= fm_amount_;
|
||||
reset_noise += phase_[0] > 0.5f ? -1.0f : 1.0f;
|
||||
reset_noise += phase_[1] > 0.5f ? -1.0f : 1.0f;
|
||||
reset_noise *= reset_noise_amount * 0.025f;
|
||||
|
||||
float f = f0_ * (1.0f + fm_amount_ * (4.0f * fm_));
|
||||
phase_[0] += f;
|
||||
phase_[1] += f * 1.47f;
|
||||
if(reset_noise_amount > 0.1f)
|
||||
{
|
||||
if(phase_[0] >= 1.0f + reset_noise)
|
||||
{
|
||||
phase_[0] = 1.0f - phase_[0];
|
||||
}
|
||||
if(phase_[1] >= 1.0f + reset_noise)
|
||||
{
|
||||
phase_[1] = 1.0f - phase_[1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(phase_[0] >= 1.0f)
|
||||
{
|
||||
phase_[0] -= 1.0f;
|
||||
}
|
||||
if(phase_[1] >= 1.0f)
|
||||
{
|
||||
phase_[1] -= 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float drum = -0.1f;
|
||||
drum += DistortedSine(phase_[0]) * 0.60f;
|
||||
drum += DistortedSine(phase_[1]) * 0.25f;
|
||||
drum *= drum_amplitude_ * drum_level;
|
||||
|
||||
drum_lp_.Process(drum);
|
||||
drum = drum_lp_.Low();
|
||||
|
||||
float noise = rand() * kRandFrac;
|
||||
snare_lp_.Process(noise);
|
||||
float snare = snare_lp_.Low();
|
||||
snare_hp_.Process(snare);
|
||||
snare = snare_hp_.High();
|
||||
snare = (snare + 0.1f) * (snare_amplitude_ + fm_) * snare_level;
|
||||
|
||||
return snare + drum; // It's a snare, it's a drum, it's a snare drum.
|
||||
}
|
||||
|
||||
void SyntheticSnareDrum::Trig()
|
||||
{
|
||||
trig_ = true;
|
||||
}
|
||||
|
||||
void SyntheticSnareDrum::SetSustain(bool sustain)
|
||||
{
|
||||
sustain_ = sustain;
|
||||
}
|
||||
|
||||
void SyntheticSnareDrum::SetAccent(float accent)
|
||||
{
|
||||
accent_ = fclamp(accent, 0.f, 1.f);
|
||||
}
|
||||
|
||||
void SyntheticSnareDrum::SetFreq(float f0)
|
||||
{
|
||||
f0 /= sample_rate_;
|
||||
f0_ = fclamp(f0, 0.f, 1.f);
|
||||
}
|
||||
|
||||
void SyntheticSnareDrum::SetFmAmount(float fm_amount)
|
||||
{
|
||||
fm_amount = fclamp(fm_amount, 0.f, 1.f);
|
||||
fm_amount_ = fm_amount * fm_amount;
|
||||
}
|
||||
|
||||
void SyntheticSnareDrum::SetDecay(float decay)
|
||||
{
|
||||
decay_ = fmax(decay, 0.f);
|
||||
}
|
||||
|
||||
void SyntheticSnareDrum::SetSnappy(float snappy)
|
||||
{
|
||||
snappy_ = fclamp(snappy, 0.f, 1.f);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright (c) 2020 Electrosmith, Corp, Emilie Gillet
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef DSY_SYNTHSD_H
|
||||
#define DSY_SYNTHSD_H
|
||||
|
||||
#include "Filters/svf.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#ifdef __cplusplus
|
||||
|
||||
/** @file synthsnaredrum.h */
|
||||
|
||||
namespace daisysp
|
||||
{
|
||||
/**
|
||||
@brief Naive snare drum model (two modulated oscillators + filtered noise).
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
Uses a few magic numbers taken from the 909 schematics: \n
|
||||
- Ratio between the two modes of the drum set to 1.47. \n
|
||||
- Funky coupling between the two modes. \n
|
||||
- Noise coloration filters and envelope shapes for the snare. \n \n
|
||||
Ported from pichenettes/eurorack/plaits/dsp/drums/synthetic_snare_drum.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class SyntheticSnareDrum
|
||||
{
|
||||
public:
|
||||
SyntheticSnareDrum() {}
|
||||
~SyntheticSnareDrum() {}
|
||||
|
||||
/** Init the module
|
||||
\param sample_rate Audio engine sample rate
|
||||
*/
|
||||
void Init(float sample_rate);
|
||||
|
||||
/** Get the next sample.
|
||||
\param trigger True = hit the drum. This argument is optional.
|
||||
*/
|
||||
float Process(bool trigger = false);
|
||||
|
||||
/** Trigger the drum */
|
||||
void Trig();
|
||||
|
||||
/** Make the drum ring out infinitely.
|
||||
\param sustain True = infinite sustain.
|
||||
*/
|
||||
void SetSustain(bool sustain);
|
||||
|
||||
/** Set how much accent to use
|
||||
\param accent Works 0-1.
|
||||
*/
|
||||
void SetAccent(float accent);
|
||||
|
||||
/** Set the drum's root frequency
|
||||
\param f0 Freq in Hz
|
||||
*/
|
||||
void SetFreq(float f0);
|
||||
|
||||
/** Set the amount of fm sweep.
|
||||
\param fm_amount Works from 0 - 1.
|
||||
*/
|
||||
void SetFmAmount(float fm_amount);
|
||||
|
||||
/** Set the length of the drum decay
|
||||
\param decay Works with positive numbers
|
||||
*/
|
||||
void SetDecay(float decay);
|
||||
|
||||
/** Sets the mix between snare and drum.
|
||||
\param snappy 1 = just snare. 0 = just drum.
|
||||
*/
|
||||
void SetSnappy(float snappy);
|
||||
|
||||
private:
|
||||
inline float DistortedSine(float phase);
|
||||
|
||||
float sample_rate_;
|
||||
|
||||
bool trig_;
|
||||
bool sustain_;
|
||||
float accent_, f0_, fm_amount_, decay_, snappy_;
|
||||
|
||||
float phase_[2];
|
||||
float drum_amplitude_;
|
||||
float snare_amplitude_;
|
||||
float fm_;
|
||||
float sustain_gain_;
|
||||
int hold_counter_;
|
||||
|
||||
Svf drum_lp_;
|
||||
Svf snare_hp_;
|
||||
Svf snare_lp_;
|
||||
};
|
||||
} // namespace daisysp
|
||||
#endif
|
||||
#endif
|
||||
Reference in New Issue
Block a user