Added DaisySP
This commit is contained in:
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user