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
+197
View File
@@ -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]);
}
+110
View File
@@ -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
+213
View File
@@ -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;
}
+61
View File
@@ -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
+109
View File
@@ -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);
}
+99
View File
@@ -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
+158
View File
@@ -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;
}
+191
View File
@@ -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
+124
View File
@@ -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);
}
+102
View File
@@ -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