Added DaisySP
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
#include <cmath>
|
||||
#include "dsp.h"
|
||||
#include "KarplusString.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace daisysp;
|
||||
|
||||
void String::Init(float sample_rate)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
|
||||
SetFreq(440.f);
|
||||
non_linearity_amount_ = .5f;
|
||||
brightness_ = .5f;
|
||||
damping_ = .5f;
|
||||
|
||||
string_.Init();
|
||||
stretch_.Init();
|
||||
Reset();
|
||||
|
||||
SetFreq(440.f);
|
||||
SetDamping(.8f);
|
||||
SetNonLinearity(.1f);
|
||||
SetBrightness(.5f);
|
||||
|
||||
crossfade_.Init();
|
||||
}
|
||||
|
||||
void String::Reset()
|
||||
{
|
||||
string_.Reset();
|
||||
stretch_.Reset();
|
||||
iir_damping_filter_.Init();
|
||||
|
||||
dc_blocker_.Init(sample_rate_);
|
||||
|
||||
dispersion_noise_ = 0.0f;
|
||||
curved_bridge_ = 0.0f;
|
||||
out_sample_[0] = out_sample_[1] = 0.0f;
|
||||
src_phase_ = 0.0f;
|
||||
}
|
||||
|
||||
float String::Process(const float in)
|
||||
{
|
||||
if(non_linearity_amount_ <= 0.0f)
|
||||
{
|
||||
non_linearity_amount_ *= -1;
|
||||
float ret = ProcessInternal<NON_LINEARITY_CURVED_BRIDGE>(in);
|
||||
non_linearity_amount_ *= -1;
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ProcessInternal<NON_LINEARITY_DISPERSION>(in);
|
||||
}
|
||||
}
|
||||
|
||||
void String::SetFreq(float freq)
|
||||
{
|
||||
freq /= sample_rate_;
|
||||
frequency_ = fclamp(freq, 0.f, .25f);
|
||||
}
|
||||
|
||||
void String::SetNonLinearity(float non_linearity_amount)
|
||||
{
|
||||
non_linearity_amount_ = fclamp(non_linearity_amount, 0.f, 1.f);
|
||||
}
|
||||
|
||||
void String::SetBrightness(float brightness)
|
||||
{
|
||||
brightness_ = fclamp(brightness, 0.f, 1.f);
|
||||
}
|
||||
|
||||
void String::SetDamping(float damping)
|
||||
{
|
||||
damping_ = fclamp(damping, 0.f, 1.f);
|
||||
}
|
||||
|
||||
template <String::StringNonLinearity non_linearity>
|
||||
float String::ProcessInternal(const float in)
|
||||
{
|
||||
float brightness = brightness_;
|
||||
|
||||
float delay = 1.0f / frequency_;
|
||||
delay = fclamp(delay, 4.f, kDelayLineSize - 4.0f);
|
||||
|
||||
// If there is not enough delay time in the delay line, we play at the
|
||||
// lowest possible note and we upsample on the fly with a shitty linear
|
||||
// interpolator. We don't care because it's a corner case (frequency_ < 11.7Hz)
|
||||
float src_ratio = delay * frequency_;
|
||||
if(src_ratio >= 0.9999f)
|
||||
{
|
||||
// When we are above 11.7 Hz, we make sure that the linear interpolator
|
||||
// does not get in the way.
|
||||
src_phase_ = 1.0f;
|
||||
src_ratio = 1.0f;
|
||||
}
|
||||
|
||||
float damping_cutoff
|
||||
= fmin(12.0f + damping_ * damping_ * 60.0f + brightness * 24.0f, 84.0f);
|
||||
float damping_f
|
||||
= fmin(frequency_ * powf(2.f, damping_cutoff * kOneTwelfth), 0.499f);
|
||||
|
||||
// Crossfade to infinite decay.
|
||||
if(damping_ >= 0.95f)
|
||||
{
|
||||
float to_infinite = 20.0f * (damping_ - 0.95f);
|
||||
brightness += to_infinite * (1.0f - brightness);
|
||||
damping_f += to_infinite * (0.4999f - damping_f);
|
||||
damping_cutoff += to_infinite * (128.0f - damping_cutoff);
|
||||
}
|
||||
|
||||
float temp_f = damping_f;
|
||||
iir_damping_filter_.SetFrequency(temp_f);
|
||||
|
||||
float ratio = powf(2.f, damping_cutoff * kOneTwelfth);
|
||||
float damping_compensation = 1.f - 2.f * atanf(1.f / ratio) / (TWOPI_F);
|
||||
|
||||
float stretch_point
|
||||
= non_linearity_amount_ * (2.0f - non_linearity_amount_) * 0.225f;
|
||||
float stretch_correction = (160.0f / sample_rate_) * delay;
|
||||
stretch_correction = fclamp(stretch_correction, 1.f, 2.1f);
|
||||
|
||||
float noise_amount_sqrt = non_linearity_amount_ > 0.75f
|
||||
? 4.0f * (non_linearity_amount_ - 0.75f)
|
||||
: 0.0f;
|
||||
float noise_amount = noise_amount_sqrt * noise_amount_sqrt * 0.1f;
|
||||
float noise_filter = 0.06f + 0.94f * brightness * brightness;
|
||||
|
||||
float bridge_curving_sqrt = non_linearity_amount_;
|
||||
float bridge_curving = bridge_curving_sqrt * bridge_curving_sqrt * 0.01f;
|
||||
|
||||
float ap_gain = -0.618f * non_linearity_amount_
|
||||
/ (0.15f + fabsf(non_linearity_amount_));
|
||||
|
||||
src_phase_ += src_ratio;
|
||||
if(src_phase_ > 1.0f)
|
||||
{
|
||||
src_phase_ -= 1.0f;
|
||||
|
||||
delay = delay * damping_compensation;
|
||||
float s = 0.0f;
|
||||
|
||||
if(non_linearity == NON_LINEARITY_DISPERSION)
|
||||
{
|
||||
float noise = rand() * kRandFrac - 0.5f;
|
||||
fonepole(dispersion_noise_, noise, noise_filter);
|
||||
delay *= 1.0f + dispersion_noise_ * noise_amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
delay *= 1.0f - curved_bridge_ * bridge_curving;
|
||||
}
|
||||
|
||||
if(non_linearity == NON_LINEARITY_DISPERSION)
|
||||
{
|
||||
float ap_delay = delay * stretch_point;
|
||||
float main_delay = delay
|
||||
- ap_delay * (0.408f - stretch_point * 0.308f)
|
||||
* stretch_correction;
|
||||
if(ap_delay >= 4.0f && main_delay >= 4.0f)
|
||||
{
|
||||
s = string_.Read(main_delay);
|
||||
s = stretch_.Allpass(s, ap_delay, ap_gain);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = string_.ReadHermite(delay);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s = string_.ReadHermite(delay);
|
||||
}
|
||||
|
||||
if(non_linearity == NON_LINEARITY_CURVED_BRIDGE)
|
||||
{
|
||||
float value = fabsf(s) - 0.025f;
|
||||
float sign = s > 0.0f ? 1.0f : -1.5f;
|
||||
curved_bridge_ = (fabsf(value) + value) * sign;
|
||||
}
|
||||
|
||||
s += in;
|
||||
s = fclamp(s, -20.f, +20.f);
|
||||
|
||||
s = dc_blocker_.Process(s);
|
||||
|
||||
s = iir_damping_filter_.Process(s);
|
||||
string_.Write(s);
|
||||
|
||||
out_sample_[1] = out_sample_[0];
|
||||
out_sample_[0] = s;
|
||||
}
|
||||
|
||||
crossfade_.SetPos(src_phase_);
|
||||
return crossfade_.Process(out_sample_[1], out_sample_[0]);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
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_STRING_H
|
||||
#define DSY_STRING_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Dynamics/crossfade.h"
|
||||
#include "Utility/dcblock.h"
|
||||
#include "Utility/delayline.h"
|
||||
#include "Filters/onepole.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/** @file KarplusString.h */
|
||||
|
||||
namespace daisysp
|
||||
{
|
||||
/**
|
||||
@brief Comb filter / KS string.
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
"Lite" version of the implementation used in Rings \n \n
|
||||
Ported from pichenettes/eurorack/plaits/dsp/oscillator/formant_oscillator.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class String
|
||||
{
|
||||
public:
|
||||
String() {}
|
||||
~String() {}
|
||||
|
||||
/** Initialize the module.
|
||||
\param sample_rate Audio engine sample rate
|
||||
*/
|
||||
void Init(float sample_rate);
|
||||
|
||||
/** Clear the delay line */
|
||||
void Reset();
|
||||
|
||||
/** Get the next floating point sample
|
||||
\param in Signal to excite the string.
|
||||
*/
|
||||
float Process(const float in);
|
||||
|
||||
/** Set the string frequency.
|
||||
\param freq Frequency in Hz
|
||||
*/
|
||||
void SetFreq(float freq);
|
||||
|
||||
/** Set the string's behavior.
|
||||
\param -1 to 0 is curved bridge, 0 to 1 is dispersion.
|
||||
*/
|
||||
void SetNonLinearity(float non_linearity_amount);
|
||||
|
||||
/** Set the string's overall brightness
|
||||
\param Works 0-1.
|
||||
*/
|
||||
void SetBrightness(float brightness);
|
||||
|
||||
/** Set the string's decay time.
|
||||
\param damping Works 0-1.
|
||||
*/
|
||||
void SetDamping(float damping);
|
||||
|
||||
|
||||
private:
|
||||
static constexpr size_t kDelayLineSize = 1024;
|
||||
|
||||
enum StringNonLinearity
|
||||
{
|
||||
NON_LINEARITY_CURVED_BRIDGE,
|
||||
NON_LINEARITY_DISPERSION
|
||||
};
|
||||
|
||||
template <String::StringNonLinearity non_linearity>
|
||||
float ProcessInternal(const float in);
|
||||
|
||||
DelayLine<float, kDelayLineSize> string_;
|
||||
DelayLine<float, kDelayLineSize / 4> stretch_;
|
||||
|
||||
float frequency_, non_linearity_amount_, brightness_, damping_;
|
||||
|
||||
float sample_rate_;
|
||||
|
||||
OnePole iir_damping_filter_;
|
||||
|
||||
DcBlock dc_blocker_;
|
||||
|
||||
CrossFade crossfade_;
|
||||
|
||||
float dispersion_noise_;
|
||||
float curved_bridge_;
|
||||
|
||||
// Very crappy linear interpolation upsampler used for low pitches that
|
||||
// do not fit the delay line. Rarely used.
|
||||
float src_phase_;
|
||||
float out_sample_[2];
|
||||
};
|
||||
} // namespace daisysp
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,213 @@
|
||||
#include "drip.h"
|
||||
#include <math.h>
|
||||
#include <cstdlib>
|
||||
#include "dsp.h"
|
||||
|
||||
using namespace daisysp;
|
||||
|
||||
#define WUTR_SOUND_DECAY 0.95f
|
||||
#define WUTR_SYSTEM_DECAY 0.996f
|
||||
#define WUTR_GAIN 1.0f
|
||||
#define WUTR_NUM_SOURCES 10.0f
|
||||
#define WUTR_CENTER_FREQ0 450.0f
|
||||
#define WUTR_CENTER_FREQ1 600.0f
|
||||
#define WUTR_CENTER_FREQ2 750.0f
|
||||
#define WUTR_RESON 0.9985f
|
||||
#define WUTR_FREQ_SWEEP 1.0001f
|
||||
#define MAX_SHAKE 2000.0f
|
||||
|
||||
int Drip::my_random(int max)
|
||||
{
|
||||
return (rand() % (max + 1));
|
||||
}
|
||||
|
||||
float Drip::noise_tick()
|
||||
{
|
||||
float temp;
|
||||
temp = 1.0f * rand() - 1073741823.5f;
|
||||
return temp * (1.0f / 1073741823.0f);
|
||||
}
|
||||
|
||||
void Drip::Init(float sample_rate, float dettack)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
float temp;
|
||||
dettack_ = dettack;
|
||||
num_tubes_ = 10;
|
||||
damp_ = 0.2f;
|
||||
shake_max_ = 0.0f;
|
||||
freq_ = 450.0f;
|
||||
freq1_ = 600.0f;
|
||||
freq2_ = 720.0f;
|
||||
amp_ = 0.3f;
|
||||
|
||||
snd_level_ = 0.0;
|
||||
float tpidsr = 2.0 * PI_F / sample_rate_;
|
||||
|
||||
kloop_ = (sample_rate_ * dettack_);
|
||||
outputs00_ = 0.0f;
|
||||
outputs01_ = 0.0f;
|
||||
outputs10_ = 0.0f;
|
||||
outputs11_ = 0.0f;
|
||||
outputs20_ = 0.0f;
|
||||
outputs21_ = 0.0f;
|
||||
|
||||
total_energy_ = 0.0f;
|
||||
|
||||
center_freqs0_ = res_freq0_ = WUTR_CENTER_FREQ0;
|
||||
center_freqs1_ = res_freq1_ = WUTR_CENTER_FREQ1;
|
||||
center_freqs2_ = res_freq2_ = WUTR_CENTER_FREQ2;
|
||||
num_objects_save_ = num_objects_ = WUTR_NUM_SOURCES;
|
||||
sound_decay_ = WUTR_SOUND_DECAY;
|
||||
system_decay_ = WUTR_SYSTEM_DECAY;
|
||||
temp = logf(WUTR_NUM_SOURCES) * WUTR_GAIN / WUTR_NUM_SOURCES;
|
||||
gains0_ = gains1_ = gains2_ = temp;
|
||||
coeffs01_ = WUTR_RESON * WUTR_RESON;
|
||||
coeffs00_ = -WUTR_RESON * 2.0f * cosf(WUTR_CENTER_FREQ0 * tpidsr);
|
||||
coeffs11_ = WUTR_RESON * WUTR_RESON;
|
||||
coeffs10_ = -WUTR_RESON * 2.0f * cosf(WUTR_CENTER_FREQ1 * tpidsr);
|
||||
coeffs21_ = WUTR_RESON * WUTR_RESON;
|
||||
coeffs20_ = -WUTR_RESON * 2.0f * cosf(WUTR_CENTER_FREQ2 * tpidsr);
|
||||
|
||||
shake_energy_ = amp_ * 1.0f * MAX_SHAKE * 0.1f;
|
||||
shake_damp_ = 0.0f;
|
||||
if(shake_energy_ > MAX_SHAKE)
|
||||
shake_energy_ = MAX_SHAKE;
|
||||
shake_max_save_ = 0.0f;
|
||||
num_objects_ = 10.0f;
|
||||
finalZ0_ = finalZ1_ = finalZ2_ = 0.0f;
|
||||
}
|
||||
|
||||
float Drip::Process(bool trig)
|
||||
{
|
||||
float data;
|
||||
float lastOutput;
|
||||
|
||||
float tpidsr = 2.0f * PI_F / sample_rate_;
|
||||
|
||||
if(trig)
|
||||
{
|
||||
Init(sample_rate_, dettack_);
|
||||
}
|
||||
if(num_tubes_ != 0.0f && num_tubes_ != num_objects_)
|
||||
{
|
||||
num_objects_ = num_tubes_;
|
||||
if(num_objects_ < 1.0f)
|
||||
{
|
||||
num_objects_ = 1.0f;
|
||||
}
|
||||
}
|
||||
if(freq_ != 0.0f && freq_ != res_freq0_)
|
||||
{
|
||||
res_freq0_ = freq_;
|
||||
coeffs00_ = -WUTR_RESON * 2.0f * cosf(res_freq0_ * tpidsr);
|
||||
}
|
||||
if(damp_ != 0.0f && damp_ != shake_damp_)
|
||||
{
|
||||
shake_damp_ = damp_;
|
||||
system_decay_ = WUTR_SYSTEM_DECAY + (shake_damp_ * 0.002f);
|
||||
}
|
||||
if(shake_max_ != 0.0f && shake_max_ != shake_max_save_)
|
||||
{
|
||||
shake_max_save_ = shake_max_;
|
||||
shake_energy_ += shake_max_save_ * MAX_SHAKE * 0.1f;
|
||||
if(shake_energy_ > MAX_SHAKE)
|
||||
shake_energy_ = MAX_SHAKE;
|
||||
}
|
||||
if(freq1_ != 0.0f && freq1_ != res_freq1_)
|
||||
{
|
||||
res_freq1_ = freq1_;
|
||||
coeffs10_ = -WUTR_RESON * 2.0f * cosf(res_freq1_ * tpidsr);
|
||||
}
|
||||
if(freq2_ != 0.0f && freq2_ != res_freq2_)
|
||||
{
|
||||
res_freq2_ = freq2_;
|
||||
coeffs20_ = -WUTR_RESON * 2.0f * cosf(res_freq2_ * tpidsr);
|
||||
}
|
||||
if((--kloop_) == 0.0f)
|
||||
{
|
||||
shake_energy_ = 0.0f;
|
||||
}
|
||||
|
||||
float shakeEnergy = shake_energy_;
|
||||
float systemDecay = system_decay_;
|
||||
float sndLevel = snd_level_;
|
||||
float num_objects = num_objects_;
|
||||
float soundDecay = sound_decay_;
|
||||
float inputs0, inputs1, inputs2;
|
||||
|
||||
shakeEnergy *= systemDecay; /* Exponential system decay */
|
||||
|
||||
sndLevel = shakeEnergy;
|
||||
if(my_random(32767) < num_objects)
|
||||
{
|
||||
int j;
|
||||
j = my_random(3);
|
||||
if(j == 0)
|
||||
{
|
||||
center_freqs0_ = res_freq1_ * (0.75f + (0.25f * noise_tick()));
|
||||
gains0_ = fabsf(noise_tick());
|
||||
}
|
||||
else if(j == 1)
|
||||
{
|
||||
center_freqs1_ = res_freq1_ * (1.0f + (0.25f * noise_tick()));
|
||||
gains1_ = fabsf(noise_tick());
|
||||
}
|
||||
else
|
||||
{
|
||||
center_freqs2_ = res_freq1_ * (1.25f + (0.25f * noise_tick()));
|
||||
gains2_ = fabsf(noise_tick());
|
||||
}
|
||||
}
|
||||
|
||||
gains0_ *= WUTR_RESON;
|
||||
if(gains0_ > 0.001f)
|
||||
{
|
||||
center_freqs0_ *= WUTR_FREQ_SWEEP;
|
||||
coeffs00_ = -WUTR_RESON * 2.0f * cosf(center_freqs0_ * tpidsr);
|
||||
}
|
||||
gains1_ *= WUTR_RESON;
|
||||
if(gains1_ > 0.00f)
|
||||
{
|
||||
center_freqs1_ *= WUTR_FREQ_SWEEP;
|
||||
coeffs10_ = -WUTR_RESON * 2.0f * cosf(center_freqs1_ * tpidsr);
|
||||
}
|
||||
gains2_ *= WUTR_RESON;
|
||||
if(gains2_ > 0.001f)
|
||||
{
|
||||
center_freqs2_ *= WUTR_FREQ_SWEEP;
|
||||
coeffs20_ = -WUTR_RESON * 2.0f * cosf(center_freqs2_ * tpidsr);
|
||||
}
|
||||
|
||||
sndLevel *= soundDecay;
|
||||
inputs0 = sndLevel;
|
||||
inputs0 *= noise_tick();
|
||||
inputs1 = inputs0 * gains1_;
|
||||
inputs2 = inputs0 * gains2_;
|
||||
inputs0 *= gains0_;
|
||||
inputs0 -= outputs00_ * coeffs00_;
|
||||
inputs0 -= outputs01_ * coeffs01_;
|
||||
outputs01_ = outputs00_;
|
||||
outputs00_ = inputs0;
|
||||
data = gains0_ * outputs00_;
|
||||
inputs1 -= outputs10_ * coeffs10_;
|
||||
inputs1 -= outputs11_ * coeffs11_;
|
||||
outputs11_ = outputs10_;
|
||||
outputs10_ = inputs1_;
|
||||
data += gains1_ * outputs10_;
|
||||
inputs2 -= outputs20_ * coeffs20_;
|
||||
inputs2 -= outputs21_ * coeffs21_;
|
||||
outputs21_ = outputs20_;
|
||||
outputs20_ = inputs2_;
|
||||
data += gains2_ * outputs20_;
|
||||
|
||||
finalZ2_ = finalZ1_;
|
||||
finalZ1_ = finalZ0_;
|
||||
finalZ0_ = data * 4.0f;
|
||||
|
||||
lastOutput = finalZ2_ - finalZ0_;
|
||||
lastOutput *= 0.005f;
|
||||
shake_energy_ = shakeEnergy;
|
||||
snd_level_ = sndLevel;
|
||||
return lastOutput;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright (c) 2020 Electrosmith, Corp, Paul Batchelor
|
||||
|
||||
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_DRIP_H
|
||||
#define DSY_DRIP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#ifdef __cplusplus
|
||||
|
||||
/** @file drip.h */
|
||||
|
||||
namespace daisysp
|
||||
{
|
||||
/**
|
||||
Imitates the sound of dripping water via Physical Modeling Synthesis. \n
|
||||
Ported from soundpipe by Ben Sergentanis, May 2020
|
||||
@author Perry Cook
|
||||
@date 2000
|
||||
*/
|
||||
class Drip
|
||||
{
|
||||
public:
|
||||
Drip() {}
|
||||
~Drip() {}
|
||||
|
||||
/**
|
||||
Initializes the Drip module.
|
||||
\param sample_rate The sample rate of the audio engine being run.
|
||||
\param dettack The period of time over which all sound is stopped.
|
||||
*/
|
||||
void Init(float sample_rate, float dettack);
|
||||
|
||||
/**
|
||||
Process the next floating point sample.
|
||||
\param trig If true, begins a new drip.
|
||||
\return Next sample.
|
||||
*/
|
||||
float Process(bool trig);
|
||||
|
||||
private:
|
||||
float gains0_, gains1_, gains2_, kloop_, dettack_, num_tubes_, damp_,
|
||||
shake_max_, freq_, freq1_, freq2_, amp_, snd_level_, outputs00_,
|
||||
outputs01_, outputs10_, outputs11_, outputs20_, outputs21_,
|
||||
total_energy_, center_freqs0_, center_freqs1_, center_freqs2_,
|
||||
num_objects_save_, sound_decay_, system_decay_, finalZ0_, finalZ1_,
|
||||
finalZ2_, coeffs01_, coeffs00_, coeffs11_, coeffs10_, coeffs21_,
|
||||
coeffs20_, shake_energy_, shake_damp_, shake_max_save_, num_objects_,
|
||||
sample_rate_, res_freq0_, res_freq1_, res_freq2_, inputs1_, inputs2_;
|
||||
|
||||
int my_random(int max);
|
||||
float noise_tick();
|
||||
};
|
||||
} // namespace daisysp
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,109 @@
|
||||
#include "modalvoice.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace daisysp;
|
||||
|
||||
void ModalVoice::Init(float sample_rate)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
aux_ = 0.f;
|
||||
|
||||
excitation_filter_.Init();
|
||||
resonator_.Init(0.015f, 24, sample_rate_);
|
||||
excitation_filter_.Init();
|
||||
dust_.Init();
|
||||
|
||||
SetSustain(false);
|
||||
SetFreq(440.f);
|
||||
SetAccent(.3f);
|
||||
SetStructure(.6f);
|
||||
SetBrightness(.8f);
|
||||
SetDamping(.6f);
|
||||
}
|
||||
|
||||
void ModalVoice::SetSustain(bool sustain)
|
||||
{
|
||||
sustain_ = sustain;
|
||||
}
|
||||
|
||||
void ModalVoice::Trig()
|
||||
{
|
||||
trig_ = true;
|
||||
}
|
||||
|
||||
void ModalVoice::SetFreq(float freq)
|
||||
{
|
||||
resonator_.SetFreq(freq);
|
||||
f0_ = freq / sample_rate_;
|
||||
f0_ = fclamp(f0_, 0.f, .25f);
|
||||
}
|
||||
|
||||
void ModalVoice::SetAccent(float accent)
|
||||
{
|
||||
accent_ = fclamp(accent, 0.f, 1.f);
|
||||
}
|
||||
|
||||
void ModalVoice::SetStructure(float structure)
|
||||
{
|
||||
resonator_.SetStructure(structure);
|
||||
}
|
||||
|
||||
void ModalVoice::SetBrightness(float brightness)
|
||||
{
|
||||
brightness_ = fclamp(brightness, 0.f, 1.f);
|
||||
density_ = brightness_ * brightness_;
|
||||
}
|
||||
|
||||
void ModalVoice::SetDamping(float damping)
|
||||
{
|
||||
damping_ = fclamp(damping, 0.f, 1.f);
|
||||
}
|
||||
|
||||
float ModalVoice::GetAux()
|
||||
{
|
||||
return aux_;
|
||||
}
|
||||
|
||||
float ModalVoice::Process(bool trigger)
|
||||
{
|
||||
float brightness = brightness_ + 0.25f * accent_ * (1.0f - brightness_);
|
||||
float damping = damping_ + 0.25f * accent_ * (1.0f - damping_);
|
||||
|
||||
const float range = sustain_ ? 36.0f : 60.0f;
|
||||
const float f = sustain_ ? 4.0f * f0_ : 2.0f * f0_;
|
||||
const float cutoff = fmin(
|
||||
f
|
||||
* powf(2.f,
|
||||
kOneTwelfth
|
||||
* ((brightness * (2.0f - brightness) - 0.5f) * range)),
|
||||
0.499f);
|
||||
const float q = sustain_ ? 0.7f : 1.5f;
|
||||
|
||||
float temp = 0.f;
|
||||
// Synthesize excitation signal.
|
||||
if(sustain_)
|
||||
{
|
||||
const float dust_f = 0.00005f + 0.99995f * density_ * density_;
|
||||
dust_.SetDensity(dust_f);
|
||||
temp = dust_.Process() * (4.0f - dust_f * 3.0f) * accent_;
|
||||
}
|
||||
else if(trigger || trig_)
|
||||
{
|
||||
const float attenuation = 1.0f - damping * 0.5f;
|
||||
const float amplitude = (0.12f + 0.08f * accent_) * attenuation;
|
||||
temp = amplitude * powf(2.f, kOneTwelfth * (cutoff * cutoff * 24.0f))
|
||||
/ cutoff;
|
||||
trig_ = false;
|
||||
}
|
||||
|
||||
const float one = 1.0f;
|
||||
excitation_filter_.Process<ResonatorSvf<1>::LOW_PASS, false>(
|
||||
&cutoff, &q, &one, temp, &temp);
|
||||
|
||||
aux_ = temp;
|
||||
|
||||
resonator_.SetBrightness(brightness);
|
||||
resonator_.SetDamping(damping);
|
||||
|
||||
return resonator_.Process(temp);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
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_MODAL_H
|
||||
#define DSY_MODAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "Filters/svf.h"
|
||||
#include "PhysicalModeling/resonator.h"
|
||||
#include "Noise/dust.h"
|
||||
#ifdef __cplusplus
|
||||
|
||||
/** @file modalvoice.h */
|
||||
|
||||
namespace daisysp
|
||||
{
|
||||
/**
|
||||
@brief Simple modal synthesis voice with a mallet exciter: click -> LPF -> resonator.
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
The click can be replaced by continuous white noise. \n \n
|
||||
Ported from pichenettes/eurorack/plaits/dsp/physical_modelling/modal_voice.h \n
|
||||
and pichenettes/eurorack/plaits/dsp/physical_modelling/modal_voice.cc \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class ModalVoice
|
||||
{
|
||||
public:
|
||||
ModalVoice() {}
|
||||
~ModalVoice() {}
|
||||
|
||||
/** Initialize the module
|
||||
\param sample_rate Audio engine sample rate
|
||||
*/
|
||||
void Init(float sample_rate);
|
||||
|
||||
/** Get the next sample
|
||||
\param trigger Strike the resonator. Defaults to false.
|
||||
*/
|
||||
float Process(bool trigger = false);
|
||||
|
||||
/** Continually excite the resonator with noise.
|
||||
\param sustain True turns on the noise.
|
||||
*/
|
||||
void SetSustain(bool sustain);
|
||||
|
||||
/** Strike the resonator. */
|
||||
void Trig();
|
||||
|
||||
/** Set the resonator root frequency.
|
||||
\param freq Frequency in Hz.
|
||||
*/
|
||||
void SetFreq(float freq);
|
||||
|
||||
/** Hit the resonator a bit harder.
|
||||
\param accent Works 0-1.
|
||||
*/
|
||||
void SetAccent(float accent);
|
||||
|
||||
/** Changes the general charater of the resonator (stiffness, brightness)
|
||||
\param structure Works best from 0-1
|
||||
*/
|
||||
void SetStructure(float structure);
|
||||
|
||||
/** Set the brighness of the resonator, and the noise density.
|
||||
\param brightness Works best 0-1
|
||||
*/
|
||||
void SetBrightness(float brightness);
|
||||
|
||||
/** How long the resonant body takes to decay.
|
||||
\param damping Works best 0-1
|
||||
*/
|
||||
void SetDamping(float damping);
|
||||
|
||||
/** Get the raw excitation signal. Must call Process() first. */
|
||||
float GetAux();
|
||||
|
||||
private:
|
||||
float sample_rate_;
|
||||
|
||||
bool sustain_, trig_;
|
||||
float f0_, structure_, brightness_, damping_;
|
||||
float density_, accent_;
|
||||
float aux_;
|
||||
|
||||
ResonatorSvf<1> excitation_filter_;
|
||||
Resonator resonator_;
|
||||
Dust dust_;
|
||||
};
|
||||
} // namespace daisysp
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "resonator.h"
|
||||
#include <math.h>
|
||||
|
||||
using namespace daisysp;
|
||||
|
||||
void Resonator::Init(float position, int resolution, float sample_rate)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
|
||||
SetFreq(440.f);
|
||||
SetStructure(.5f);
|
||||
SetBrightness(.5f);
|
||||
SetDamping(.5f);
|
||||
|
||||
resolution_ = fmin(resolution, kMaxNumModes);
|
||||
|
||||
for(int i = 0; i < resolution; ++i)
|
||||
{
|
||||
mode_amplitude_[i] = cos(position * TWOPI_F) * 0.25f;
|
||||
}
|
||||
|
||||
for(int i = 0; i < kMaxNumModes / kModeBatchSize; ++i)
|
||||
{
|
||||
mode_filters_[i].Init();
|
||||
}
|
||||
}
|
||||
|
||||
inline float NthHarmonicCompensation(int n, float stiffness)
|
||||
{
|
||||
float stretch_factor = 1.0f;
|
||||
for(int i = 0; i < n - 1; ++i)
|
||||
{
|
||||
stretch_factor += stiffness;
|
||||
if(stiffness < 0.0f)
|
||||
{
|
||||
stiffness *= 0.93f;
|
||||
}
|
||||
else
|
||||
{
|
||||
stiffness *= 0.98f;
|
||||
}
|
||||
}
|
||||
return 1.0f / stretch_factor;
|
||||
}
|
||||
|
||||
float Resonator::Process(const float in)
|
||||
{
|
||||
//convert Hz to cycles / sample
|
||||
float out = 0.f;
|
||||
|
||||
float stiffness = CalcStiff(structure_);
|
||||
float f0 = frequency_ * NthHarmonicCompensation(3, stiffness);
|
||||
float brightness = brightness_;
|
||||
|
||||
float harmonic = f0;
|
||||
float stretch_factor = 1.0f;
|
||||
|
||||
float input = damping_ * 79.7f;
|
||||
float q_sqrt = powf(2.f, input * ratiofrac_);
|
||||
|
||||
float q = 500.0f * q_sqrt * q_sqrt;
|
||||
brightness *= 1.0f - structure_ * 0.3f;
|
||||
brightness *= 1.0f - damping_ * 0.3f;
|
||||
float q_loss = brightness * (2.0f - brightness) * 0.85f + 0.15f;
|
||||
|
||||
float mode_q[kModeBatchSize];
|
||||
float mode_f[kModeBatchSize];
|
||||
float mode_a[kModeBatchSize];
|
||||
int batch_counter = 0;
|
||||
|
||||
ResonatorSvf<kModeBatchSize>* batch_processor = &mode_filters_[0];
|
||||
|
||||
for(int i = 0; i < resolution_; ++i)
|
||||
{
|
||||
float mode_frequency = harmonic * stretch_factor;
|
||||
if(mode_frequency >= 0.499f)
|
||||
{
|
||||
mode_frequency = 0.499f;
|
||||
}
|
||||
const float mode_attenuation = 1.0f - mode_frequency * 2.0f;
|
||||
|
||||
mode_f[batch_counter] = mode_frequency;
|
||||
mode_q[batch_counter] = 1.0f + mode_frequency * q;
|
||||
mode_a[batch_counter] = mode_amplitude_[i] * mode_attenuation;
|
||||
++batch_counter;
|
||||
|
||||
if(batch_counter == kModeBatchSize)
|
||||
{
|
||||
batch_counter = 0;
|
||||
batch_processor
|
||||
->Process<ResonatorSvf<kModeBatchSize>::BAND_PASS, true>(
|
||||
mode_f, mode_q, mode_a, in, &out);
|
||||
++batch_processor;
|
||||
}
|
||||
|
||||
stretch_factor += stiffness;
|
||||
if(stiffness < 0.0f)
|
||||
{
|
||||
// Make sure that the partials do not fold back into negative frequencies.
|
||||
stiffness *= 0.93f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This helps adding a few extra partials in the highest frequencies.
|
||||
stiffness *= 0.98f;
|
||||
}
|
||||
harmonic += f0;
|
||||
q *= q_loss;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void Resonator::SetFreq(float freq)
|
||||
{
|
||||
frequency_ = freq / sample_rate_;
|
||||
}
|
||||
|
||||
void Resonator::SetStructure(float structure)
|
||||
{
|
||||
structure_ = fmax(fmin(structure, 1.f), 0.f);
|
||||
}
|
||||
|
||||
void Resonator::SetBrightness(float brightness)
|
||||
{
|
||||
brightness_ = fmax(fmin(brightness, 1.f), 0.f);
|
||||
}
|
||||
|
||||
void Resonator::SetDamping(float damping)
|
||||
{
|
||||
damping_ = fmax(fmin(damping, 1.f), 0.f);
|
||||
}
|
||||
|
||||
float Resonator::CalcStiff(float sig)
|
||||
{
|
||||
if(sig < .25f)
|
||||
{
|
||||
sig = .25 - sig;
|
||||
sig = -sig * .25;
|
||||
}
|
||||
else if(sig < .3f)
|
||||
{
|
||||
sig = 0.f;
|
||||
}
|
||||
else if(sig < .9f)
|
||||
{
|
||||
sig -= .3f;
|
||||
sig *= stiff_frac_2;
|
||||
}
|
||||
else
|
||||
{
|
||||
sig -= .9f;
|
||||
sig *= 10; // div by .1
|
||||
sig *= sig;
|
||||
sig = 1.5 - cos(sig * PI_F) * .5f;
|
||||
}
|
||||
return sig;
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
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_RESONATOR_H
|
||||
#define DSY_RESONATOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "Utility/dsp.h"
|
||||
#ifdef __cplusplus
|
||||
|
||||
|
||||
/** @file resonator.h */
|
||||
|
||||
namespace daisysp
|
||||
{
|
||||
// We render 4 modes simultaneously since there are enough registers to hold
|
||||
// all state variables.
|
||||
/**
|
||||
@brief SVF for use in the Resonator Class \n
|
||||
@author Ported by Ben Sergentanis
|
||||
@date Jan 2021
|
||||
Ported from pichenettes/eurorack/plaits/dsp/physical_modelling/resonator.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
template <int batch_size>
|
||||
class ResonatorSvf
|
||||
{
|
||||
public:
|
||||
enum FilterMode
|
||||
{
|
||||
LOW_PASS,
|
||||
BAND_PASS,
|
||||
BAND_PASS_NORMALIZED,
|
||||
HIGH_PASS
|
||||
};
|
||||
|
||||
ResonatorSvf() {}
|
||||
~ResonatorSvf() {}
|
||||
|
||||
void Init()
|
||||
{
|
||||
for(int i = 0; i < batch_size; ++i)
|
||||
{
|
||||
state_1_[i] = state_2_[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
template <FilterMode mode, bool add>
|
||||
void Process(const float* f,
|
||||
const float* q,
|
||||
const float* gain,
|
||||
const float in,
|
||||
float* out)
|
||||
{
|
||||
float g[batch_size];
|
||||
float r[batch_size];
|
||||
float r_plus_g[batch_size];
|
||||
float h[batch_size];
|
||||
float state_1[batch_size];
|
||||
float state_2[batch_size];
|
||||
float gains[batch_size];
|
||||
for(int i = 0; i < batch_size; ++i)
|
||||
{
|
||||
g[i] = fasttan(f[i]);
|
||||
r[i] = 1.0f / q[i];
|
||||
h[i] = 1.0f / (1.0f + r[i] * g[i] + g[i] * g[i]);
|
||||
r_plus_g[i] = r[i] + g[i];
|
||||
state_1[i] = state_1_[i];
|
||||
state_2[i] = state_2_[i];
|
||||
gains[i] = gain[i];
|
||||
}
|
||||
|
||||
float s_in = in;
|
||||
float s_out = 0.0f;
|
||||
for(int i = 0; i < batch_size; ++i)
|
||||
{
|
||||
const float hp
|
||||
= (s_in - r_plus_g[i] * state_1[i] - state_2[i]) * h[i];
|
||||
const float bp = g[i] * hp + state_1[i];
|
||||
state_1[i] = g[i] * hp + bp;
|
||||
const float lp = g[i] * bp + state_2[i];
|
||||
state_2[i] = g[i] * bp + lp;
|
||||
s_out += gains[i] * ((mode == LOW_PASS) ? lp : bp);
|
||||
}
|
||||
if(add)
|
||||
{
|
||||
*out++ += s_out;
|
||||
}
|
||||
else
|
||||
{
|
||||
*out++ = s_out;
|
||||
}
|
||||
|
||||
for(int i = 0; i < batch_size; ++i)
|
||||
{
|
||||
state_1_[i] = state_1[i];
|
||||
state_2_[i] = state_2[i];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr float kPiPow3 = PI_F * PI_F * PI_F;
|
||||
static constexpr float kPiPow5 = kPiPow3 * PI_F * PI_F;
|
||||
static inline float fasttan(float f)
|
||||
{
|
||||
const float a = 3.260e-01 * kPiPow3;
|
||||
const float b = 1.823e-01 * kPiPow5;
|
||||
float f2 = f * f;
|
||||
return f * (PI_F + f2 * (a + b * f2));
|
||||
}
|
||||
|
||||
float state_1_[batch_size];
|
||||
float state_2_[batch_size];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Resonant Body Simulation
|
||||
@author Ported by Ben Sergentanis
|
||||
@date Jan 2021
|
||||
Ported from pichenettes/eurorack/plaits/dsp/physical_modelling/resonator.h \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class Resonator
|
||||
{
|
||||
public:
|
||||
Resonator() {}
|
||||
~Resonator() {}
|
||||
|
||||
/** Initialize the module
|
||||
\param position Offset the phase of the amplitudes. 0-1
|
||||
\param resolution Quality vs speed scalar
|
||||
\param sample_rate Samplerate of the audio engine being run.
|
||||
*/
|
||||
void Init(float position, int resolution, float sample_rate);
|
||||
|
||||
/** Get the next sample_rate
|
||||
\param in The signal to excited the resonant body
|
||||
*/
|
||||
float Process(const float in);
|
||||
|
||||
/** Resonator frequency.
|
||||
\param freq Frequency in Hz.
|
||||
*/
|
||||
void SetFreq(float freq);
|
||||
|
||||
/** Changes the general charater of the resonator (stiffness, brightness)
|
||||
\param structure Works best from 0-1
|
||||
*/
|
||||
void SetStructure(float structure);
|
||||
|
||||
/** Set the brighness of the resonator
|
||||
\param brightness Works best 0-1
|
||||
*/
|
||||
void SetBrightness(float brightness);
|
||||
|
||||
/** How long the resonant body takes to decay.
|
||||
\param damping Works best 0-1
|
||||
*/
|
||||
void SetDamping(float damping);
|
||||
|
||||
private:
|
||||
int resolution_;
|
||||
float frequency_, brightness_, structure_, damping_;
|
||||
|
||||
static constexpr int kMaxNumModes = 24;
|
||||
static constexpr int kModeBatchSize = 4;
|
||||
static constexpr float ratiofrac_ = 1.f / 12.f;
|
||||
static constexpr float stiff_frac_ = 1.f / 64.f;
|
||||
static constexpr float stiff_frac_2 = 1.f / .6f;
|
||||
|
||||
float sample_rate_;
|
||||
|
||||
float CalcStiff(float sig);
|
||||
|
||||
float mode_amplitude_[kMaxNumModes];
|
||||
ResonatorSvf<kModeBatchSize> mode_filters_[kMaxNumModes / kModeBatchSize];
|
||||
};
|
||||
|
||||
} // namespace daisysp
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,124 @@
|
||||
#include "stringvoice.h"
|
||||
#include <algorithm>
|
||||
#include "dsp.h"
|
||||
|
||||
using namespace daisysp;
|
||||
|
||||
void StringVoice::Init(float sample_rate)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
|
||||
excitation_filter_.Init(sample_rate);
|
||||
string_.Init(sample_rate_);
|
||||
dust_.Init();
|
||||
remaining_noise_samples_ = 0;
|
||||
|
||||
SetSustain(false);
|
||||
SetFreq(440.f);
|
||||
SetAccent(.8f);
|
||||
SetStructure(.7f);
|
||||
SetBrightness(.2f);
|
||||
SetDamping(.7f);
|
||||
}
|
||||
|
||||
void StringVoice::Reset()
|
||||
{
|
||||
string_.Reset();
|
||||
}
|
||||
|
||||
void StringVoice::SetSustain(bool sustain)
|
||||
{
|
||||
sustain_ = sustain;
|
||||
}
|
||||
|
||||
void StringVoice::Trig()
|
||||
{
|
||||
trig_ = true;
|
||||
}
|
||||
|
||||
void StringVoice::SetFreq(float freq)
|
||||
{
|
||||
string_.SetFreq(freq);
|
||||
f0_ = freq / sample_rate_;
|
||||
f0_ = fclamp(f0_, 0.f, .25f);
|
||||
}
|
||||
|
||||
void StringVoice::SetAccent(float accent)
|
||||
{
|
||||
accent_ = fclamp(accent, 0.f, 1.f);
|
||||
}
|
||||
|
||||
void StringVoice::SetStructure(float structure)
|
||||
{
|
||||
structure = fclamp(structure, 0.f, 1.f);
|
||||
const float non_linearity
|
||||
= structure < 0.24f
|
||||
? (structure - 0.24f) * 4.166f
|
||||
: (structure > 0.26f ? (structure - 0.26f) * 1.35135f : 0.0f);
|
||||
string_.SetNonLinearity(non_linearity);
|
||||
}
|
||||
|
||||
void StringVoice::SetBrightness(float brightness)
|
||||
{
|
||||
brightness_ = fclamp(brightness, 0.f, 1.f);
|
||||
density_ = brightness_ * brightness_;
|
||||
}
|
||||
|
||||
void StringVoice::SetDamping(float damping)
|
||||
{
|
||||
damping_ = fclamp(damping, 0.f, 1.f);
|
||||
}
|
||||
|
||||
float StringVoice::GetAux()
|
||||
{
|
||||
return aux_;
|
||||
}
|
||||
|
||||
float StringVoice::Process(bool trigger)
|
||||
{
|
||||
const float brightness = brightness_ + .25 * accent_ * (1.f - brightness_);
|
||||
const float damping = damping_ + .25 * accent_ * (1.f - damping_);
|
||||
|
||||
// Synthesize excitation signal.
|
||||
if(trigger || trig_ || sustain_)
|
||||
{
|
||||
trig_ = false;
|
||||
const float range = 72.0f;
|
||||
const float f = 4.0f * f0_;
|
||||
const float cutoff = fmin(
|
||||
f
|
||||
* powf(2.f,
|
||||
kOneTwelfth * (brightness * (2.0f - brightness) - 0.5f)
|
||||
* range),
|
||||
0.499f);
|
||||
const float q = sustain_ ? 1.0f : 0.5f;
|
||||
remaining_noise_samples_ = static_cast<size_t>(1.0f / f0_);
|
||||
excitation_filter_.SetFreq(cutoff * sample_rate_);
|
||||
excitation_filter_.SetRes(q);
|
||||
}
|
||||
|
||||
float temp = 0.f;
|
||||
|
||||
if(sustain_)
|
||||
{
|
||||
const float dust_f = 0.00005f + 0.99995f * density_ * density_;
|
||||
dust_.SetDensity(dust_f);
|
||||
temp = dust_.Process() * (8.0f - dust_f * 6.0f) * accent_;
|
||||
}
|
||||
else if(remaining_noise_samples_)
|
||||
{
|
||||
temp = 2.0f * rand() * kRandFrac - 1.0f;
|
||||
remaining_noise_samples_--;
|
||||
remaining_noise_samples_ = DSY_MAX(remaining_noise_samples_, 0.f);
|
||||
}
|
||||
|
||||
excitation_filter_.Process(temp);
|
||||
temp = excitation_filter_.Low();
|
||||
|
||||
aux_ = temp;
|
||||
|
||||
string_.SetBrightness(brightness);
|
||||
string_.SetDamping(damping);
|
||||
|
||||
return string_.Process(temp);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
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_STRINGVOICE_H
|
||||
#define DSY_STRINGVOICE_H
|
||||
|
||||
#include "Filters/svf.h"
|
||||
#include "PhysicalModeling/KarplusString.h"
|
||||
#include "Noise/dust.h"
|
||||
#include <stdint.h>
|
||||
#ifdef __cplusplus
|
||||
|
||||
/** @file stringvoice.h */
|
||||
|
||||
namespace daisysp
|
||||
{
|
||||
/**
|
||||
@brief Extended Karplus-Strong, with all the niceties from Rings
|
||||
@author Ben Sergentanis
|
||||
@date Jan 2021
|
||||
Ported from pichenettes/eurorack/plaits/dsp/physical_modelling/string_voice.h \n
|
||||
and pichenettes/eurorack/plaits/dsp/physical_modelling/string_voice.cc \n
|
||||
to an independent module. \n
|
||||
Original code written by Emilie Gillet in 2016. \n
|
||||
*/
|
||||
class StringVoice
|
||||
{
|
||||
public:
|
||||
StringVoice() {}
|
||||
~StringVoice() {}
|
||||
|
||||
/** Initialize the module
|
||||
\param sample_rate Audio engine sample rate
|
||||
*/
|
||||
void Init(float sample_rate);
|
||||
|
||||
/** Reset the string oscillator */
|
||||
void Reset();
|
||||
|
||||
/** Get the next sample
|
||||
\param trigger Strike the string. Defaults to false.
|
||||
*/
|
||||
float Process(bool trigger = false);
|
||||
|
||||
/** Continually excite the string with noise.
|
||||
\param sustain True turns on the noise.
|
||||
*/
|
||||
void SetSustain(bool sustain);
|
||||
|
||||
/** Strike the string. */
|
||||
void Trig();
|
||||
|
||||
/** Set the string root frequency.
|
||||
\param freq Frequency in Hz.
|
||||
*/
|
||||
void SetFreq(float freq);
|
||||
|
||||
/** Hit the string a bit harder. Influences brightness and decay.
|
||||
\param accent Works 0-1.
|
||||
*/
|
||||
void SetAccent(float accent);
|
||||
|
||||
/** Changes the string's nonlinearity (string type).
|
||||
\param structure Works 0-1. 0-.26 is curved bridge, .26-1 is dispersion.
|
||||
*/
|
||||
void SetStructure(float structure);
|
||||
|
||||
/** Set the brighness of the string, and the noise density.
|
||||
\param brightness Works best 0-1
|
||||
*/
|
||||
void SetBrightness(float brightness);
|
||||
|
||||
/** How long the resonant body takes to decay relative to the accent level.
|
||||
\param damping Works best 0-1. Full damp is only achieved with full accent.
|
||||
*/
|
||||
void SetDamping(float damping);
|
||||
|
||||
/** Get the raw excitation signal. Must call Process() first. */
|
||||
float GetAux();
|
||||
|
||||
private:
|
||||
float sample_rate_;
|
||||
|
||||
bool sustain_, trig_;
|
||||
float f0_, brightness_, damping_;
|
||||
float density_, accent_;
|
||||
float aux_;
|
||||
|
||||
Dust dust_;
|
||||
Svf excitation_filter_;
|
||||
String string_;
|
||||
size_t remaining_noise_samples_;
|
||||
};
|
||||
} // namespace daisysp
|
||||
#endif
|
||||
#endif
|
||||
Reference in New Issue
Block a user