Added DaisySP

This commit is contained in:
2026-04-24 14:46:05 +02:00
parent 82190216c3
commit dd63b3aed4
96 changed files with 10613 additions and 0 deletions
+192
View File
@@ -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;
}
+113
View File
@@ -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
+219
View File
@@ -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);
}
}
+106
View File
@@ -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
+96
View File
@@ -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;
}
+277
View File
@@ -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
+231
View File
@@ -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;
}
+187
View File
@@ -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
+201
View File
@@ -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);
}
+105
View File
@@ -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