Skip to content

Commit

Permalink
Hihat module (electro-smith#117)
Browse files Browse the repository at this point in the history
* setup

* building

* sample based

* default template

* init sr

* setters

* fix setter bugs

* init vals

* tuned decay param

* trig and comments

* two consts

* ratio and rand

* stdlib
  • Loading branch information
beserge authored Jan 18, 2021
1 parent 7f8d752 commit bacd432
Show file tree
Hide file tree
Showing 4 changed files with 367 additions and 0 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ fm2 \
fold \
formantosc \
grainlet \
hihat \
jitter \
line \
limiter \
Expand Down
1 change: 1 addition & 0 deletions daisysp.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "modules/fractal_noise.h"
#include "modules/grainlet.h"
#include "modules/harmonic_osc.h"
#include "modules/hihat.h"
#include "modules/jitter.h"
#include "modules/limiter.h"
#include "modules/line.h"
Expand Down
96 changes: 96 additions & 0 deletions modules/hihat.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
269 changes: 269 additions & 0 deletions modules/hihat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
#pragma once
#ifndef DSY_HIHAT_H
#define DSY_HIHAT_H

#include "modules/svf.h"
#include "modules/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 comments on commit bacd432

Please sign in to comment.