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
+65
View File
@@ -0,0 +1,65 @@
#include "fm2.h"
using namespace daisysp;
void Fm2::Init(float samplerate)
{
//init oscillators
car_.Init(samplerate);
mod_.Init(samplerate);
//set some reasonable values
lfreq_ = 440.f;
lratio_ = 2.f;
SetFrequency(lfreq_);
SetRatio(lratio_);
car_.SetAmp(1.f);
mod_.SetAmp(1.f);
car_.SetWaveform(Oscillator::WAVE_SIN);
mod_.SetWaveform(Oscillator::WAVE_SIN);
idx_ = 1.f;
}
float Fm2::Process()
{
if(lratio_ != ratio_ || lfreq_ != freq_)
{
lratio_ = ratio_;
lfreq_ = freq_;
car_.SetFreq(lfreq_);
mod_.SetFreq(lfreq_ * lratio_);
}
float modval = mod_.Process();
car_.PhaseAdd(modval * idx_);
return car_.Process();
}
void Fm2::SetFrequency(float freq)
{
freq_ = fabsf(freq);
}
void Fm2::SetRatio(float ratio)
{
ratio_ = fabsf(ratio);
}
void Fm2::SetIndex(float index)
{
idx_ = index * kIdxScalar;
}
float Fm2::GetIndex()
{
return idx_ * kIdxScalarRecip;
}
void Fm2::Reset()
{
car_.Reset();
mod_.Reset();
}
+73
View File
@@ -0,0 +1,73 @@
/*
Copyright (c) 2020 Electrosmith, Corp
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_FM2_H
#define DSY_FM2_H
#include <stdint.h>
#include "Synthesis/oscillator.h"
#ifdef __cplusplus
namespace daisysp
{
/** Simple 2 operator FM synth voice.
Date: November, 2020
Author: Ben Sergentanis
*/
class Fm2
{
public:
Fm2() {}
~Fm2() {}
/** Initializes the FM2 module.
\param samplerate - The sample rate of the audio engine being run.
*/
void Init(float samplerate);
/** Returns the next sample
*/
float Process();
/** Carrier freq. setter
\param freq Carrier frequency in Hz
*/
void SetFrequency(float freq);
/** Set modulator freq. relative to carrier
\param ratio New modulator freq = carrier freq. * ratio
*/
void SetRatio(float ratio);
/** Index setter
\param FM depth, 5 = 2PI rads
*/
void SetIndex(float index);
/** Returns the current FM index. */
float GetIndex();
/** Resets both oscillators */
void Reset();
private:
static constexpr float kIdxScalar = 0.2f;
static constexpr float kIdxScalarRecip = 1.f / kIdxScalar;
Oscillator mod_, car_;
float idx_;
float freq_, lfreq_, ratio_, lratio_;
};
} // namespace daisysp
#endif
#endif
+96
View File
@@ -0,0 +1,96 @@
#include "dsp.h"
#include "formantosc.h"
#include <math.h>
using namespace daisysp;
void FormantOscillator::Init(float sample_rate)
{
carrier_phase_ = 0.0f;
formant_phase_ = 0.0f;
next_sample_ = 0.0f;
carrier_frequency_ = 0.0f;
formant_frequency_ = 100.f;
phase_shift_ = 0.0f;
sample_rate_ = sample_rate;
}
float FormantOscillator::Process()
{
float this_sample = next_sample_;
float next_sample = 0.0f;
carrier_phase_ += carrier_frequency_;
if(carrier_phase_ >= 1.0f)
{
carrier_phase_ -= 1.0f;
float reset_time = carrier_phase_ / carrier_frequency_;
float formant_phase_at_reset
= formant_phase_ + (1.0f - reset_time) * formant_frequency_;
float before = Sine(formant_phase_at_reset + phase_shift_
+ (ps_inc_ * (1.0f - reset_time)));
float after = Sine(0.0f + phase_shift_ + ps_inc_);
float discontinuity = after - before;
this_sample += discontinuity * ThisBlepSample(reset_time);
next_sample += discontinuity * NextBlepSample(reset_time);
formant_phase_ = reset_time * formant_frequency_;
}
else
{
formant_phase_ += formant_frequency_;
if(formant_phase_ >= 1.0f)
{
formant_phase_ -= 1.0f;
}
}
phase_shift_ += ps_inc_;
ps_inc_ = 0.f;
next_sample += Sine(formant_phase_ + phase_shift_);
next_sample_ = next_sample;
return this_sample;
}
void FormantOscillator::SetFormantFreq(float freq)
{
//convert from Hz to phase_inc / sample
formant_frequency_ = freq / sample_rate_;
formant_frequency_ = formant_frequency_ >= .25f ? .25f : formant_frequency_;
formant_frequency_
= formant_frequency_ <= -.25f ? -.25f : formant_frequency_;
}
void FormantOscillator::SetCarrierFreq(float freq)
{
//convert from Hz to phase_inc / sample
carrier_frequency_ = freq / sample_rate_;
carrier_frequency_ = carrier_frequency_ >= .25f ? .25f : carrier_frequency_;
carrier_frequency_
= carrier_frequency_ <= -.25f ? -.25f : carrier_frequency_;
}
void FormantOscillator::SetPhaseShift(float ps)
{
ps_inc_ = ps - phase_shift_;
}
inline float FormantOscillator::Sine(float phase)
{
return sinf(phase * TWOPI_F);
}
inline float FormantOscillator::ThisBlepSample(float t)
{
return 0.5f * t * t;
}
inline float FormantOscillator::NextBlepSample(float t)
{
t = 1.0f - t;
return -0.5f * t * t;
}
+81
View File
@@ -0,0 +1,81 @@
/*
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_FORMANTOSCILLATOR_H
#define DSY_FORMANTOSCILLATOR_H
#include <stdint.h>
#ifdef __cplusplus
/** @file formantosc.h */
namespace daisysp
{
/**
@brief Formant Oscillator Module.
@author Ben Sergentanis
@date Dec 2020
Sinewave with aliasing-free phase reset. \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 FormantOscillator
{
public:
FormantOscillator() {}
~FormantOscillator() {}
/** Initializes the FormantOscillator module.
\param sample_rate - The sample rate of the audio engine being run.
*/
void Init(float sample_rate);
/** Get the next sample
*/
float Process();
/** Set the formant frequency.
\param freq Frequency in Hz
*/
void SetFormantFreq(float freq);
/** Set the carrier frequency. This is the "main" frequency.
\param freq Frequency in Hz
*/
void SetCarrierFreq(float freq);
/** Set the amount of phase shift
\param ps Typically 0-1. Works with other values though, including negative.
*/
void SetPhaseShift(float ps);
private:
inline float Sine(float phase);
inline float ThisBlepSample(float t);
inline float NextBlepSample(float t);
// Oscillator state.
float carrier_phase_;
float formant_phase_;
float next_sample_;
// For interpolation of parameters.
float carrier_frequency_;
float formant_frequency_;
float phase_shift_;
float ps_inc_;
float sample_rate_;
//DISALLOW_COPY_AND_ASSIGN(FormantOscillator);
};
} //namespace daisysp
#endif
#endif
+174
View File
@@ -0,0 +1,174 @@
/*
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_HARMONIC_H
#define DSY_HARMONIC_H
#include <stdint.h>
#include "Utility/dsp.h"
#ifdef __cplusplus
/** @file harmonic_osc.h */
namespace daisysp
{
/**
@brief Harmonic Oscillator Module based on Chebyshev polynomials.
@author Ben Sergentanis
@date Dec 2020
Harmonic Oscillator Module based on Chebyshev polynomials \n
Works well for a small number of harmonics. For the higher order harmonics. \n
We need to reinitialize the recurrence by computing two high harmonics. \n \n
Ported from pichenettes/eurorack/plaits/dsp/oscillator/harmonic_oscillator.h \n
to an independent module. \n
Original code written by Emilie Gillet in 2016. \n
*/
template <int num_harmonics = 16>
class HarmonicOscillator
{
public:
HarmonicOscillator() {}
~HarmonicOscillator() {}
/** Initialize harmonic oscillator
\param sample_rate Audio engine samplerate
*/
void Init(float sample_rate)
{
sample_rate_ = sample_rate;
phase_ = 0.0f;
for(int i = 0; i < num_harmonics; ++i)
{
amplitude_[i] = 0.0f;
newamplitude_[i] = 0.f;
}
amplitude_[0] = 1.f;
newamplitude_[0] = 1.f;
SetFirstHarmIdx(1);
SetFreq(440.f);
recalc_ = false;
}
/** Get the next floating point sample */
float Process()
{
if(recalc_)
{
recalc_ = false;
for(int i = 0; i < num_harmonics; ++i)
{
float f = frequency_
* static_cast<float>(first_harmonic_index_ + i);
if(f >= 0.5f)
{
f = 0.5f;
}
amplitude_[i] = newamplitude_[i] * (1.0f - f * 2.0f);
}
}
phase_ += frequency_;
if(phase_ >= 1.0f)
{
phase_ -= 1.0f;
}
const float two_x = 2.0f * sinf(phase_ * TWOPI_F);
float previous, current;
if(first_harmonic_index_ == 1)
{
previous = 1.0f;
current = two_x * 0.5f;
}
else
{
const float k = first_harmonic_index_;
previous = sinf((phase_ * (k - 1.0f) + 0.25f) * TWOPI_F);
current = sinf((phase_ * k) * TWOPI_F);
}
float sum = 0.0f;
for(int i = 0; i < num_harmonics; ++i)
{
sum += amplitude_[i] * current;
float temp = current;
current = two_x * current - previous;
previous = temp;
}
return sum;
}
/** Set the main frequency
\param freq Freq to be set in Hz.
*/
void SetFreq(float freq)
{
//convert from Hz to phase_inc / sample
freq = freq / sample_rate_;
freq = freq >= .5f ? .5f : freq;
freq = freq <= -.5f ? -.5f : freq;
recalc_ = cmp(freq, frequency_) || recalc_;
frequency_ = freq;
}
/** Offset the set of harmonics. Passing in 3 means "harmonic 0" is the 3rd harm., 1 is the 4th, etc.
\param idx Default behavior is 1. Values < 0 default to 1.
*/
void SetFirstHarmIdx(int idx)
{
idx = idx < 1 ? 1 : idx;
recalc_ = cmp(idx, first_harmonic_index_) || recalc_;
first_harmonic_index_ = idx;
}
/** Set the amplitudes of each harmonic of the root.
\param amplitudes Amplitudes to set. Sum of all amplitudes must be < 1. The array referenced must be at least as large as num_harmonics.
*/
void SetAmplitudes(const float* amplitudes)
{
for(int i = 0; i < num_harmonics; i++)
{
recalc_ = cmp(newamplitude_[i], amplitudes[i]) || recalc_;
newamplitude_[i] = amplitudes[i];
}
}
/** Sets one amplitude. Does nothing if idx out of range.
\param amp Amplitude to set
\param idx Which harmonic to set.
*/
void SetSingleAmp(const float amp, int idx)
{
if(idx < 0 || idx >= num_harmonics)
{
return;
}
recalc_ = cmp(amplitude_[idx], amp) || recalc_;
newamplitude_[idx] = amp;
}
private:
bool cmp(float a, float b) { return fabsf(a - b) > .000001f; }
float sample_rate_;
float phase_;
float frequency_;
float amplitude_[num_harmonics];
float newamplitude_[num_harmonics];
bool recalc_;
int first_harmonic_index_;
};
} // namespace daisysp
#endif
#endif
+83
View File
@@ -0,0 +1,83 @@
#include "dsp.h"
#include "oscillator.h"
using namespace daisysp;
static inline float Polyblep(float phase_inc, float t);
float Oscillator::Process()
{
float out, t;
switch(waveform_)
{
case WAVE_SIN: out = sinf(phase_ * TWOPI_F); break;
case WAVE_TRI:
t = -1.0f + (2.0f * phase_);
out = 2.0f * (fabsf(t) - 0.5f);
break;
case WAVE_SAW: out = -1.0f * (((phase_ * 2.0f)) - 1.0f); break;
case WAVE_RAMP: out = ((phase_ * 2.0f)) - 1.0f; break;
case WAVE_SQUARE: out = phase_ < pw_ ? (1.0f) : -1.0f; break;
case WAVE_POLYBLEP_TRI:
t = phase_;
out = phase_ < 0.5f ? 1.0f : -1.0f;
out += Polyblep(phase_inc_, t);
out -= Polyblep(phase_inc_, fastmod1f(t + 0.5f));
// Leaky Integrator:
// y[n] = A + x[n] + (1 - A) * y[n-1]
out = phase_inc_ * out + (1.0f - phase_inc_) * last_out_;
last_out_ = out;
out *= 4.f; // normalize amplitude after leaky integration
break;
case WAVE_POLYBLEP_SAW:
t = phase_;
out = (2.0f * t) - 1.0f;
out -= Polyblep(phase_inc_, t);
out *= -1.0f;
break;
case WAVE_POLYBLEP_SQUARE:
t = phase_;
out = phase_ < pw_ ? 1.0f : -1.0f;
out += Polyblep(phase_inc_, t);
out -= Polyblep(phase_inc_, fastmod1f(t + (1.0f - pw_)));
out *= 0.707f; // ?
break;
default: out = 0.0f; break;
}
phase_ += phase_inc_;
if(phase_ > 1.0f)
{
phase_ -= 1.0f;
eoc_ = true;
}
else
{
eoc_ = false;
}
eor_ = (phase_ - phase_inc_ < 0.5f && phase_ >= 0.5f);
return out * amp_;
}
float Oscillator::CalcPhaseInc(float f)
{
return f * sr_recip_;
}
static float Polyblep(float phase_inc, float t)
{
float dt = phase_inc;
if(t < dt)
{
t /= dt;
return t + t - t * t - 1.0f;
}
else if(t > 1.0f - dt)
{
t = (t - 1.0f) / dt;
return t * t + t + t + 1.0f;
}
else
{
return 0.0f;
}
}
+125
View File
@@ -0,0 +1,125 @@
/*
Copyright (c) 2020 Electrosmith, Corp
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_OSCILLATOR_H
#define DSY_OSCILLATOR_H
#include <stdint.h>
#include "Utility/dsp.h"
#ifdef __cplusplus
namespace daisysp
{
/** Synthesis of several waveforms, including polyBLEP bandlimited waveforms.
*/
class Oscillator
{
public:
Oscillator() {}
~Oscillator() {}
/** Choices for output waveforms, POLYBLEP are appropriately labeled. Others are naive forms.
*/
enum
{
WAVE_SIN,
WAVE_TRI,
WAVE_SAW,
WAVE_RAMP,
WAVE_SQUARE,
WAVE_POLYBLEP_TRI,
WAVE_POLYBLEP_SAW,
WAVE_POLYBLEP_SQUARE,
WAVE_LAST,
};
/** Initializes the Oscillator
\param sample_rate - sample rate of the audio engine being run, and the frequency that the Process function will be called.
Defaults:
- freq_ = 100 Hz
- amp_ = 0.5
- waveform_ = sine wave.
*/
void Init(float sample_rate)
{
sr_ = sample_rate;
sr_recip_ = 1.0f / sample_rate;
freq_ = 100.0f;
amp_ = 0.5f;
pw_ = 0.5f;
phase_ = 0.0f;
phase_inc_ = CalcPhaseInc(freq_);
waveform_ = WAVE_SIN;
eoc_ = true;
eor_ = true;
}
/** Changes the frequency of the Oscillator, and recalculates phase increment.
*/
inline void SetFreq(const float f)
{
freq_ = f;
phase_inc_ = CalcPhaseInc(f);
}
/** Sets the amplitude of the waveform.
*/
inline void SetAmp(const float a) { amp_ = a; }
/** Sets the waveform to be synthesized by the Process() function.
*/
inline void SetWaveform(const uint8_t wf)
{
waveform_ = wf < WAVE_LAST ? wf : WAVE_SIN;
}
/** Sets the pulse width for WAVE_SQUARE and WAVE_POLYBLEP_SQUARE (range 0 - 1)
*/
inline void SetPw(const float pw) { pw_ = fclamp(pw, 0.0f, 1.0f); }
/** Returns true if cycle is at end of rise. Set during call to Process.
*/
inline bool IsEOR() { return eor_; }
/** Returns true if cycle is at end of cycle. Set during call to Process.
*/
inline bool IsEOC() { return eoc_; }
/** Returns true if cycle rising.
*/
inline bool IsRising() { return phase_ < 0.5f; }
/** Returns true if cycle falling.
*/
inline bool IsFalling() { return phase_ >= 0.5f; }
/** Processes the waveform to be generated, returning one sample. This should be called once per sample period.
*/
float Process();
/** Adds a value 0.0-1.0 (equivalent to 0.0-TWO_PI) to the current phase. Useful for PM and "FM" synthesis.
*/
void PhaseAdd(float _phase) { phase_ += _phase; }
/** Resets the phase to the input argument. If no argumeNt is present, it will reset phase to 0.0;
*/
void Reset(float _phase = 0.0f) { phase_ = _phase; }
private:
float CalcPhaseInc(float f);
uint8_t waveform_;
float amp_, freq_, pw_;
float sr_, sr_recip_, phase_, phase_inc_;
float last_out_, last_freq_;
bool eor_, eoc_;
};
} // namespace daisysp
#endif
#endif
+147
View File
@@ -0,0 +1,147 @@
#include "dsp.h"
#include "oscillatorbank.h"
#include <math.h>
using namespace daisysp;
void OscillatorBank::Init(float sample_rate)
{
sample_rate_ = sample_rate;
phase_ = 0.0f;
next_sample_ = 0.0f;
segment_ = 0.0f;
frequency_ = 0.f;
saw_8_gain_ = 0.0f;
saw_4_gain_ = 0.0f;
saw_2_gain_ = 0.0f;
saw_1_gain_ = 0.0f;
recalc_ = recalc_gain_ = true;
SetGain(1.f);
for(int i = 0; i < 7; i++)
{
registration_[i] = 0.f;
unshifted_registration_[i] = 0.f;
}
SetSingleAmp(1.f, 0);
SetFreq(440.f);
}
float OscillatorBank::Process()
{
if(recalc_)
{
recalc_ = false;
frequency_ *= 8.0f;
// Deal with very high frequencies by shifting everything 1 or 2 octave
// down: Instead of playing the 1st harmonic of a 8kHz wave, we play the
// second harmonic of a 4kHz wave.
size_t shift = 0;
while(frequency_ > 0.5f)
{
shift += 2;
frequency_ *= 0.5f;
}
for(int i = 0; i < 7; i++)
{
registration_[i] = 0.f;
}
for(size_t i = 0; i < 7 - shift; i++)
{
registration_[i + shift] = unshifted_registration_[i];
}
}
if(recalc_gain_ || recalc_)
{
saw_8_gain_ = (registration_[0] + 2.0f * registration_[1]) * gain_;
saw_4_gain_
= (registration_[2] - registration_[1] + 2.0f * registration_[3])
* gain_;
saw_2_gain_
= (registration_[4] - registration_[3] + 2.0f * registration_[5])
* gain_;
saw_1_gain_ = (registration_[6] - registration_[5]) * gain_;
}
float this_sample_ = next_sample_;
next_sample_ = 0.0f;
phase_ += frequency_;
int next_segment_ = static_cast<int>(phase_);
if(next_segment_ != segment_)
{
float discontinuity = 0.0f;
if(next_segment_ == 8)
{
phase_ -= 8.0f;
next_segment_ -= 8;
discontinuity -= saw_8_gain_;
}
if((next_segment_ & 3) == 0)
{
discontinuity -= saw_4_gain_;
}
if((next_segment_ & 1) == 0)
{
discontinuity -= saw_2_gain_;
}
discontinuity -= saw_1_gain_;
if(discontinuity != 0.0f)
{
float fraction = phase_ - static_cast<float>(next_segment_);
float t = fraction / frequency_;
this_sample_ += ThisBlepSample(t) * discontinuity;
next_sample_ += NextBlepSample(t) * discontinuity;
}
}
segment_ = next_segment_;
next_sample_ += (phase_ - 4.0f) * saw_8_gain_ * 0.125f;
next_sample_ += (phase_ - float(segment_ & 4) - 2.0f) * saw_4_gain_ * 0.25f;
next_sample_ += (phase_ - float(segment_ & 6) - 1.0f) * saw_2_gain_ * 0.5f;
next_sample_ += (phase_ - float(segment_ & 7) - 0.5f) * saw_1_gain_;
return 2.0f * this_sample_;
}
void OscillatorBank::SetFreq(float freq)
{
freq = freq / sample_rate_;
freq = freq > 0.5f ? 0.5f : freq;
recalc_ = cmp(freq, frequency_) || recalc_;
frequency_ = freq;
}
void OscillatorBank::SetAmplitudes(const float* amplitudes)
{
for(int i = 0; i < 7; i++)
{
recalc_ = cmp(unshifted_registration_[i], amplitudes[i]) || recalc_;
unshifted_registration_[i] = amplitudes[i];
}
}
void OscillatorBank::SetSingleAmp(float amp, int idx)
{
if(idx < 0 || idx > 6)
{
return;
}
recalc_ = cmp(unshifted_registration_[idx], amp) || recalc_;
unshifted_registration_[idx] = amp;
}
void OscillatorBank::SetGain(float gain)
{
gain = gain > 1.f ? 1.f : gain;
gain = gain < 0.f ? 0.f : gain;
recalc_gain_ = cmp(gain, gain_) || recalc_gain_;
gain_ = gain;
}
+88
View File
@@ -0,0 +1,88 @@
/*
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_OSCILLATORBANK_H
#define DSY_OSCILLATORBANK_H
#include <stdint.h>
#ifdef __cplusplus
/** @file oscillatorbank.h */
namespace daisysp
{
/**
@brief Oscillator Bank module.
@author Ben Sergentanis
@date Dec 2020
A mixture of 7 sawtooth and square waveforms in the style of divide-down organs \n \n
Ported from pichenettes/eurorack/plaits/dsp/oscillator/string_synth_oscillator.h \n
\n to an independent module. \n
Original code written by Emilie Gillet in 2016. \n
*/
class OscillatorBank
{
public:
OscillatorBank() {}
~OscillatorBank() {}
/** Init string synth module
\param sample_rate Audio engine sample rate
*/
void Init(float sample_rate);
/** Get next floating point sample
*/
float Process();
/** Set oscillator frequency (8' oscillator)
\param freq Frequency in Hz
*/
void SetFreq(float freq);
/** Set amplitudes of 7 oscillators. 0-6 are Saw 8', Square 8', Saw 4', Square 4', Saw 2', Square 2', Saw 1'
\param amplitudes array of 7 floating point amplitudes. Must sum to 1.
*/
void SetAmplitudes(const float* amplitudes);
/** Set a single amplitude
\param amp Amplitude to set.
\param idx Which wave's amp to set
*/
void SetSingleAmp(float amp, int idx);
/** Set overall gain.
\param gain Gain to set. 0-1.
*/
void SetGain(float gain);
private:
// Oscillator state.
float phase_;
float next_sample_;
int segment_;
float gain_;
float registration_[7];
float unshifted_registration_[7];
float frequency_;
float saw_8_gain_;
float saw_4_gain_;
float saw_2_gain_;
float saw_1_gain_;
float sample_rate_;
bool recalc_, recalc_gain_;
bool cmp(float a, float b) { return fabsf(a - b) > .0000001; }
};
} // namespace daisysp
#endif
#endif
+106
View File
@@ -0,0 +1,106 @@
#include "dsp.h"
#include "variablesawosc.h"
#include <math.h>
using namespace daisysp;
void VariableSawOscillator::Init(float sample_rate)
{
sample_rate_ = sample_rate;
phase_ = 0.0f;
next_sample_ = 0.0f;
previous_pw_ = 0.5f;
high_ = false;
SetFreq(220.f);
SetPW(0.f);
SetWaveshape(1.f);
}
float VariableSawOscillator::Process()
{
float next_sample = next_sample_;
float this_sample = next_sample;
next_sample = 0.0f;
const float triangle_amount = waveshape_;
const float notch_amount = 1.0f - waveshape_;
const float slope_up = 1.0f / (pw_);
const float slope_down = 1.0f / (1.0f - pw_);
phase_ += frequency_;
if(!high_ && phase_ >= pw_)
{
const float triangle_step
= (slope_up + slope_down) * frequency_ * triangle_amount;
const float notch
= (kVariableSawNotchDepth + 1.0f - pw_) * notch_amount;
const float t = (phase_ - pw_) / (previous_pw_ - pw_ + frequency_);
this_sample += notch * ThisBlepSample(t);
next_sample += notch * NextBlepSample(t);
this_sample -= triangle_step * ThisIntegratedBlepSample(t);
next_sample -= triangle_step * NextIntegratedBlepSample(t);
high_ = true;
}
else if(phase_ >= 1.0f)
{
phase_ -= 1.0f;
const float triangle_step
= (slope_up + slope_down) * frequency_ * triangle_amount;
const float notch = (kVariableSawNotchDepth + 1.0f) * notch_amount;
const float t = phase_ / frequency_;
this_sample -= notch * ThisBlepSample(t);
next_sample -= notch * NextBlepSample(t);
this_sample += triangle_step * ThisIntegratedBlepSample(t);
next_sample += triangle_step * NextIntegratedBlepSample(t);
high_ = false;
}
next_sample += ComputeNaiveSample(
phase_, pw_, slope_up, slope_down, triangle_amount, notch_amount);
previous_pw_ = pw_;
next_sample_ = next_sample;
return (2.0f * this_sample - 1.0f) / (1.0f + kVariableSawNotchDepth);
}
void VariableSawOscillator::SetFreq(float frequency)
{
frequency = frequency / sample_rate_;
frequency = frequency >= .25f ? .25f : frequency;
pw_ = frequency >= .25f ? .5f : pw_;
frequency_ = frequency;
}
void VariableSawOscillator::SetPW(float pw)
{
if(frequency_ >= .25f)
{
pw_ = .5f;
}
else
{
pw_ = fclamp(pw, frequency_ * 2.0f, 1.0f - 2.0f * frequency_);
}
}
void VariableSawOscillator::SetWaveshape(float waveshape)
{
waveshape_ = waveshape;
}
float VariableSawOscillator::ComputeNaiveSample(float phase,
float pw,
float slope_up,
float slope_down,
float triangle_amount,
float notch_amount)
{
float notch_saw = phase < pw ? phase : 1.0f + kVariableSawNotchDepth;
float triangle
= phase < pw ? phase * slope_up : 1.0f - (phase - pw) * slope_down;
return notch_saw * notch_amount + triangle * triangle_amount;
}
+80
View File
@@ -0,0 +1,80 @@
/*
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_VARISAWOSCILLATOR_H
#define DSY_VARISAWOSCILLATOR_H
#include <stdint.h>
#ifdef __cplusplus
/** @file variablesawosc.h */
namespace daisysp
{
/**
@brief Variable Saw Oscillator.
@author Ben Sergentanis
@date Dec 2020
Saw with variable slope or notch. \n \n
Ported from pichenettes/eurorack/plaits/dsp/oscillator/variable_saw_oscillator.h \n
\n to an independent module. \n
Original code written by Emilie Gillet in 2016. \n
*/
class VariableSawOscillator
{
public:
VariableSawOscillator() {}
~VariableSawOscillator() {}
void Init(float sample_rate);
/** Get the next sample */
float Process();
/** Set master freq.
\param frequency Freq in Hz.
*/
void SetFreq(float frequency);
/** Adjust the wave depending on the shape
\param pw Notch or slope. Works best -1 to 1.
*/
void SetPW(float pw);
/** Slope or notch
\param waveshape 0 = notch, 1 = slope
*/
void SetWaveshape(float waveshape);
private:
float ComputeNaiveSample(float phase,
float pw,
float slope_up,
float slope_down,
float triangle_amount,
float notch_amount);
float sample_rate_;
// Oscillator state.
float phase_;
float next_sample_;
float previous_pw_;
bool high_;
const float kVariableSawNotchDepth = 0.2f;
// For interpolation of parameters.
float frequency_;
float pw_;
float waveshape_;
};
} // namespace daisysp
#endif
#endif
+182
View File
@@ -0,0 +1,182 @@
#include "dsp.h"
#include "variableshapeosc.h"
#include <math.h>
using namespace daisysp;
void VariableShapeOscillator::Init(float sample_rate)
{
sample_rate_ = sample_rate;
master_phase_ = 0.0f;
slave_phase_ = 0.0f;
next_sample_ = 0.0f;
previous_pw_ = 0.5f;
high_ = false;
SetFreq(440.f);
SetWaveshape(0.f);
SetPW(0.f);
SetSync(false);
SetSyncFreq(220.f);
}
float VariableShapeOscillator::Process()
{
float next_sample = next_sample_;
bool reset = false;
bool transition_during_reset = false;
float reset_time = 0.0f;
float this_sample = next_sample;
next_sample = 0.0f;
const float square_amount = fmax(waveshape_ - 0.5f, 0.0f) * 2.0f;
const float triangle_amount = fmax(1.0f - waveshape_ * 2.0f, 0.0f);
const float slope_up = 1.0f / (pw_);
const float slope_down = 1.0f / (1.0f - pw_);
if(enable_sync_)
{
master_phase_ += master_frequency_;
if(master_phase_ >= 1.0f)
{
master_phase_ -= 1.0f;
reset_time = master_phase_ / master_frequency_;
float slave_phase_at_reset
= slave_phase_ + (1.0f - reset_time) * slave_frequency_;
reset = true;
if(slave_phase_at_reset >= 1.0f)
{
slave_phase_at_reset -= 1.0f;
transition_during_reset = true;
}
if(!high_ && slave_phase_at_reset >= pw_)
{
transition_during_reset = true;
}
float value = ComputeNaiveSample(slave_phase_at_reset,
pw_,
slope_up,
slope_down,
triangle_amount,
square_amount);
this_sample -= value * ThisBlepSample(reset_time);
next_sample -= value * NextBlepSample(reset_time);
}
}
slave_phase_ += slave_frequency_;
while(transition_during_reset || !reset)
{
if(!high_)
{
if(slave_phase_ < pw_)
{
break;
}
float t = (slave_phase_ - pw_)
/ (previous_pw_ - pw_ + slave_frequency_);
float triangle_step = (slope_up + slope_down) * slave_frequency_;
triangle_step *= triangle_amount;
this_sample += square_amount * ThisBlepSample(t);
next_sample += square_amount * NextBlepSample(t);
this_sample -= triangle_step * ThisIntegratedBlepSample(t);
next_sample -= triangle_step * NextIntegratedBlepSample(t);
high_ = true;
}
if(high_)
{
if(slave_phase_ < 1.0f)
{
break;
}
slave_phase_ -= 1.0f;
float t = slave_phase_ / slave_frequency_;
float triangle_step = (slope_up + slope_down) * slave_frequency_;
triangle_step *= triangle_amount;
this_sample -= (1.0f - triangle_amount) * ThisBlepSample(t);
next_sample -= (1.0f - triangle_amount) * NextBlepSample(t);
this_sample += triangle_step * ThisIntegratedBlepSample(t);
next_sample += triangle_step * NextIntegratedBlepSample(t);
high_ = false;
}
}
if(enable_sync_ && reset)
{
slave_phase_ = reset_time * slave_frequency_;
high_ = false;
}
next_sample += ComputeNaiveSample(slave_phase_,
pw_,
slope_up,
slope_down,
triangle_amount,
square_amount);
previous_pw_ = pw_;
next_sample_ = next_sample;
return (2.0f * this_sample - 1.0f);
}
void VariableShapeOscillator::SetFreq(float frequency)
{
frequency = frequency / sample_rate_;
frequency = frequency >= .25f ? .25f : frequency;
master_frequency_ = frequency;
}
void VariableShapeOscillator::SetPW(float pw)
{
if(slave_frequency_ >= .25f)
{
pw_ = .5f;
}
else
{
pw_ = fclamp(
pw, slave_frequency_ * 2.0f, 1.0f - 2.0f * slave_frequency_);
}
}
void VariableShapeOscillator::SetWaveshape(float waveshape)
{
waveshape_ = waveshape;
}
void VariableShapeOscillator::SetSync(bool enable_sync)
{
enable_sync_ = enable_sync;
}
void VariableShapeOscillator::SetSyncFreq(float frequency)
{
frequency = frequency / sample_rate_;
pw_ = frequency >= .25f ? .5f : pw_;
frequency = frequency >= .25f ? .25f : frequency;
slave_frequency_ = frequency;
}
float VariableShapeOscillator::ComputeNaiveSample(float phase,
float pw,
float slope_up,
float slope_down,
float triangle_amount,
float square_amount)
{
float saw = phase;
float square = phase < pw ? 0.0f : 1.0f;
float triangle
= phase < pw ? phase * slope_up : 1.0f - (phase - pw) * slope_down;
saw += (square - saw) * square_amount;
saw += (triangle - saw) * triangle_amount;
return saw;
}
+95
View File
@@ -0,0 +1,95 @@
/*
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_VARIABLESHAPEOSCILLATOR_H
#define DSY_VARIABLESHAPEOSCILLATOR_H
#include <stdint.h>
#ifdef __cplusplus
/** @file variableshapeosc.h */
namespace daisysp
{
/**
@brief Variable Waveshape Oscillator.
@author Ben Sergentanis
@date Dec 2020
Continuously variable waveform. \n \n
Ported from pichenettes/eurorack/plaits/dsp/oscillator/variable_shape_oscillator.h \n
\n to an independent module. \n
Original code written by Emilie Gillet in 2016. \n
*/
class VariableShapeOscillator
{
public:
VariableShapeOscillator() {}
~VariableShapeOscillator() {}
/** Initialize the oscillator
\param sample_rate Audio engine sample rate
*/
void Init(float sample_rate);
/** Get next sample
*/
float Process();
/** Set master freq.
\param frequency Freq in Hz.
*/
void SetFreq(float frequency);
/** Set pulse width / saw, ramp, tri.
\param pw PW when shape is square. Saw, ramp, tri otherwise.
*/
void SetPW(float pw);
/** Switch from saw/ramp/tri to square.
\param waveshape 0 is saw/ramp/tri, 1 is square.
*/
void SetWaveshape(float waveshape);
/** Whether or not to sync to the sync oscillator
\param enable_sync True to turn sync on.
*/
void SetSync(bool enable_sync);
/** Set sync oscillator freq.
\param frequency Freq in Hz.
*/
void SetSyncFreq(float frequency);
private:
float ComputeNaiveSample(float phase,
float pw,
float slope_up,
float slope_down,
float triangle_amount,
float square_amount);
float sample_rate_;
bool enable_sync_;
// Oscillator state.
float master_phase_;
float slave_phase_;
float next_sample_;
float previous_pw_;
bool high_;
// For interpolation of parameters.
float master_frequency_;
float slave_frequency_;
float pw_;
float waveshape_;
};
} // namespace daisysp
#endif
#endif
+81
View File
@@ -0,0 +1,81 @@
#include "dsp.h"
#include "vosim.h"
#include <math.h>
using namespace daisysp;
void VosimOscillator::Init(float sample_rate)
{
sample_rate_ = sample_rate;
carrier_phase_ = 0.0f;
formant_1_phase_ = 0.0f;
formant_2_phase_ = 0.0f;
SetFreq(105.f);
SetForm1Freq(1390.f);
SetForm2Freq(817.f);
SetShape(.5f);
}
float VosimOscillator::Process()
{
carrier_phase_ += carrier_frequency_;
if(carrier_phase_ >= 1.0f)
{
carrier_phase_ -= 1.0f;
float reset_time = carrier_phase_ / carrier_frequency_;
formant_1_phase_ = reset_time * formant_1_frequency_;
formant_2_phase_ = reset_time * formant_2_frequency_;
}
else
{
formant_1_phase_ += formant_1_frequency_;
if(formant_1_phase_ >= 1.0f)
{
formant_1_phase_ -= 1.0f;
}
formant_2_phase_ += formant_2_frequency_;
if(formant_2_phase_ >= 1.0f)
{
formant_2_phase_ -= 1.0f;
}
}
float carrier = Sine(carrier_phase_ * 0.5f + 0.25f) + 1.0f;
float reset_phase = 0.75f - 0.25f * carrier_shape_;
float reset_amplitude = Sine(reset_phase);
float formant_0 = Sine(formant_1_phase_ + reset_phase) - reset_amplitude;
float formant_1 = Sine(formant_2_phase_ + reset_phase) - reset_amplitude;
return carrier * (formant_0 + formant_1) * 0.25f + reset_amplitude;
}
void VosimOscillator::SetFreq(float freq)
{
carrier_frequency_ = freq / sample_rate_;
carrier_frequency_ = carrier_frequency_ > .25f ? .25f : carrier_frequency_;
}
void VosimOscillator::SetForm1Freq(float freq)
{
formant_1_frequency_ = freq / sample_rate_;
formant_1_frequency_
= formant_1_frequency_ > .25f ? .25f : formant_1_frequency_;
}
void VosimOscillator::SetForm2Freq(float freq)
{
formant_2_frequency_ = freq / sample_rate_;
formant_2_frequency_
= formant_2_frequency_ > .25f ? .25f : formant_2_frequency_;
}
void VosimOscillator::SetShape(float shape)
{
carrier_shape_ = shape;
}
float VosimOscillator::Sine(float phase)
{
return sinf(TWOPI_F * phase);
}
+82
View File
@@ -0,0 +1,82 @@
/*
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_VOSIM_H
#define DSY_VOSIM_H
#include <stdint.h>
#ifdef __cplusplus
/** @file vosim.h */
namespace daisysp
{
/**
@brief Vosim Oscillator Module \n
@author Ben Sergentanis
@date Dec 2020
Two sinewaves multiplied by and sync'ed to a carrier. \n \n
Ported from pichenettes/eurorack/plaits/dsp/oscillator/vosim_oscillator.h \n
\n to an independent module. \n
Original code written by Emilie Gillet in 2016. \n
*/
class VosimOscillator
{
public:
VosimOscillator() {}
~VosimOscillator() {}
/** Initializes the FormantOscillator module.
\param sample_rate - The sample rate of the audio engine being run.
*/
void Init(float sample_rate);
/** Get the next sample
*/
float Process();
/** Set carrier frequency.
\param freq Frequency in Hz.
*/
void SetFreq(float freq);
/** Set formant 1 frequency.
\param freq Frequency in Hz.
*/
void SetForm1Freq(float freq);
/** Set formant 2 frequency.
\param freq Frequency in Hz.
*/
void SetForm2Freq(float freq);
/** Waveshaping
\param shape Shape to set. Works -1 to 1
*/
void SetShape(float shape);
private:
float Sine(float phase);
float sample_rate_;
// Oscillator state.
float carrier_phase_;
float formant_1_phase_;
float formant_2_phase_;
// For interpolation of parameters.
float carrier_frequency_;
float formant_1_frequency_;
float formant_2_frequency_;
float carrier_shape_;
};
} // namespace daisysp
#endif
#endif
+159
View File
@@ -0,0 +1,159 @@
#include "dsp.h"
#include "zoscillator.h"
#include <math.h>
using namespace daisysp;
void ZOscillator::Init(float sample_rate)
{
sample_rate_ = sample_rate;
carrier_phase_ = 0.0f;
discontinuity_phase_ = 0.0f;
formant_phase_ = 0.0f;
next_sample_ = 0.0f;
SetFreq(220.f);
SetFormantFreq(550.f);
SetMode(0.f);
SetShape(1.f);
}
float ZOscillator::Process()
{
float next_sample = next_sample_;
bool reset = false;
float reset_time = 0.0f;
float this_sample = next_sample;
next_sample = 0.0f;
discontinuity_phase_ += 2.0f * carrier_frequency_;
carrier_phase_ += carrier_frequency_;
reset = discontinuity_phase_ >= 1.0f;
if(reset)
{
discontinuity_phase_ -= 1.0f;
reset_time = discontinuity_phase_ / (2.0f * carrier_frequency_);
float carrier_phase_before = carrier_phase_ >= 1.0f ? 1.0f : 0.5f;
float carrier_phase_after = carrier_phase_ >= 1.0f ? 0.0f : 0.5f;
float mode_sub = mode_ + (1.f - reset_time) * (mode_ - mode_new_);
float shape_sub = carrier_shape_
+ (1.0f - reset_time) * (carrier_shape_ - shape_new_);
float before
= Z(carrier_phase_before,
1.0f,
formant_phase_ + (1.0f - reset_time) * formant_frequency_,
shape_sub,
mode_sub);
float after = Z(carrier_phase_after, 0.0f, 0.0f, shape_new_, mode_new_);
float discontinuity = after - before;
this_sample += discontinuity * ThisBlepSample(reset_time);
next_sample += discontinuity * NextBlepSample(reset_time);
formant_phase_ = reset_time * formant_frequency_;
if(carrier_phase_ > 1.0f)
{
carrier_phase_ = discontinuity_phase_ * 0.5f;
}
}
else
{
formant_phase_ += formant_frequency_;
if(formant_phase_ >= 1.0f)
{
formant_phase_ -= 1.0f;
}
}
if(carrier_phase_ >= 1.0f)
{
carrier_phase_ -= 1.0f;
}
carrier_shape_ = shape_new_;
mode_ = mode_new_;
next_sample += Z(carrier_phase_,
discontinuity_phase_,
formant_phase_,
carrier_shape_,
mode_);
next_sample_ = next_sample;
return this_sample;
}
inline float ZOscillator::Sine(float phase)
{
return sinf(phase * TWOPI_F);
}
void ZOscillator::SetFreq(float freq)
{
//convert from Hz to phase_inc / sample
carrier_frequency_ = freq / sample_rate_;
carrier_frequency_ = carrier_frequency_ >= .25f ? .25f : carrier_frequency_;
}
void ZOscillator::SetFormantFreq(float freq)
{
//convert from Hz to phase_inc / sample
formant_frequency_ = freq / sample_rate_;
formant_frequency_ = formant_frequency_ >= .25f ? .25f : formant_frequency_;
}
void ZOscillator::SetShape(float shape)
{
shape_new_ = shape;
}
void ZOscillator::SetMode(float mode)
{
mode_new_ = mode;
}
inline float ZOscillator::Z(float c, float d, float f, float shape, float mode)
{
float ramp_down = 0.5f * (1.0f + Sine(0.5f * d + 0.25f));
float offset;
float phase_shift;
if(mode < 0.333f)
{
offset = 1.0f;
phase_shift = 0.25f + mode * 1.50f;
}
else if(mode < 0.666f)
{
phase_shift = 0.7495f - (mode - 0.33f) * 0.75f;
offset = -Sine(phase_shift);
}
else
{
phase_shift = 0.7495f - (mode - 0.33f) * 0.75f;
offset = 0.001f;
}
float discontinuity = Sine(f + phase_shift);
float contour;
if(shape < 0.5f)
{
shape *= 2.0f;
if(c >= 0.5f)
{
ramp_down *= shape;
}
contour = 1.0f + (Sine(c + 0.25f) - 1.0f) * shape;
}
else
{
contour = Sine(c + shape * 0.5f);
}
return (ramp_down * (offset + discontinuity) - offset) * contour;
}
+84
View File
@@ -0,0 +1,84 @@
/*
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_ZOSCILLATOR_H
#define DSY_ZOSCILLATOR_H
#include <stdint.h>
#ifdef __cplusplus
/** @file zoscillator.h */
namespace daisysp
{
/**
@brief ZOscillator Module \n
@author Ben Sergentanis
@date Dec 2020
Sinewave multiplied by and sync'ed to a carrier. \n \n
Ported from pichenettes/eurorack/plaits/dsp/oscillator/z_oscillator.h \n
\n to an independent module. \n
Original code written by Emilie Gillet in 2016. \n
*/
class ZOscillator
{
public:
ZOscillator() {}
~ZOscillator() {}
/** Init ZOscillator module
\param sample_rate Audio engine sample rate.
*/
void Init(float sample_rate);
/** Get next sample
*/
float Process();
/** Set the carrier frequency
\param freq Frequency in Hz.
*/
void SetFreq(float freq);
/** Set the formant osc. freq
\param freq Frequency in Hz.
*/
void SetFormantFreq(float freq);
/** Adjust the contour of the waveform.
\param shape Waveshape to set. Works best 0-1.
*/
void SetShape(float shape);
/** Set the offset amount and phase shift. \n
< 1/3 is just phase shift, > 2/3 is just offset, and between them is both. \n
\param mode Mode to set. Works best -1 to 1
*/
void SetMode(float mode);
private:
inline float Sine(float phase);
inline float Z(float c, float d, float f, float shape, float mode);
float sample_rate_;
// Oscillator state.
float carrier_phase_;
float discontinuity_phase_;
float formant_phase_;
float next_sample_;
// For interpolation of parameters.
float carrier_frequency_;
float formant_frequency_;
float carrier_shape_, shape_new_;
float mode_, mode_new_;
};
} // namespace daisysp
#endif
#endif