parent
ec5515c91a
commit
ee76bf2e32
@ -0,0 +1,157 @@ |
|||||||
|
#include <math.h> |
||||||
|
#include "adenv.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
//#define EXPF expf
|
||||||
|
// This causes with infinity with certain curves,
|
||||||
|
// which then causes NaN erros...
|
||||||
|
#define EXPF expf_fast |
||||||
|
|
||||||
|
// To resolve annoying bugs when using this you can:
|
||||||
|
// if (val != val)
|
||||||
|
// val = 0.0f; // This will un-NaN the value.
|
||||||
|
|
||||||
|
// Fast Exp approximation
|
||||||
|
// 8x multiply version
|
||||||
|
//inline float expf_fast(float x)
|
||||||
|
//{
|
||||||
|
// x = 1.0f + x / 256.0f;
|
||||||
|
// x *= x;
|
||||||
|
// x *= x;
|
||||||
|
// x *= x;
|
||||||
|
// x *= x;
|
||||||
|
// x *= x;
|
||||||
|
// x *= x;
|
||||||
|
// x *= x;
|
||||||
|
// x *= x;
|
||||||
|
// return x;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// 10x multiply version
|
||||||
|
inline float expf_fast(float x) |
||||||
|
{ |
||||||
|
x = 1.0f + x / 1024.0f; |
||||||
|
x *= x; |
||||||
|
x *= x; |
||||||
|
x *= x; |
||||||
|
x *= x; |
||||||
|
x *= x; |
||||||
|
x *= x; |
||||||
|
x *= x; |
||||||
|
x *= x; |
||||||
|
x *= x; |
||||||
|
x *= x; |
||||||
|
return x; |
||||||
|
} |
||||||
|
|
||||||
|
// Private Functions
|
||||||
|
void AdEnv::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
current_segment_ = ADENV_SEG_IDLE; |
||||||
|
curve_scalar_ = 0.0f; // full linear
|
||||||
|
phase_ = 0; |
||||||
|
min_ = 0.0f; |
||||||
|
max_ = 1.0f; |
||||||
|
output_ = 0.0001f; |
||||||
|
for(uint8_t i = 0; i < ADENV_SEG_LAST; i++) |
||||||
|
{ |
||||||
|
segment_time_[i] = 0.05f; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
float AdEnv::Process() |
||||||
|
{ |
||||||
|
uint32_t time_samps; |
||||||
|
float val, out, end, beg, inc; |
||||||
|
|
||||||
|
// Handle Retriggering
|
||||||
|
if(trigger_) |
||||||
|
{ |
||||||
|
trigger_ = 0; |
||||||
|
current_segment_ = ADENV_SEG_ATTACK; |
||||||
|
phase_ = 0; |
||||||
|
curve_x_ = 0.0f; |
||||||
|
retrig_val_ = output_; |
||||||
|
} |
||||||
|
|
||||||
|
time_samps = (uint32_t)(segment_time_[current_segment_] * sample_rate_); |
||||||
|
|
||||||
|
// Fixed for now, but we could always make this a more flexible multi-segment envelope
|
||||||
|
switch(current_segment_) |
||||||
|
{ |
||||||
|
case ADENV_SEG_ATTACK: |
||||||
|
beg = retrig_val_; |
||||||
|
end = 1.0f; |
||||||
|
break; |
||||||
|
case ADENV_SEG_DECAY: |
||||||
|
beg = 1.0f; |
||||||
|
end = 0.0f; |
||||||
|
break; |
||||||
|
case ADENV_SEG_IDLE: |
||||||
|
default: |
||||||
|
beg = 0; |
||||||
|
end = 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if(prev_segment_ != current_segment_) |
||||||
|
{ |
||||||
|
//Reset at segment beginning
|
||||||
|
curve_x_ = 0; |
||||||
|
phase_ = 0; |
||||||
|
} |
||||||
|
|
||||||
|
//recalculate increment value
|
||||||
|
if(curve_scalar_ == 0.0f) |
||||||
|
{ |
||||||
|
c_inc_ = (end - beg) / time_samps; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
c_inc_ = (end - beg) / (1.0f - EXPF(curve_scalar_)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// update output
|
||||||
|
val = output_; |
||||||
|
inc = c_inc_; |
||||||
|
out = val; |
||||||
|
if(curve_scalar_ == 0.0f) |
||||||
|
{ |
||||||
|
val += inc; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
curve_x_ += (curve_scalar_ / time_samps); |
||||||
|
val = beg + inc * (1.0f - EXPF(curve_x_)); |
||||||
|
if(val != val) |
||||||
|
val = 0.0f; // NaN check
|
||||||
|
} |
||||||
|
|
||||||
|
// Update Segment
|
||||||
|
phase_ += 1; |
||||||
|
prev_segment_ = current_segment_; |
||||||
|
if(current_segment_ != ADENV_SEG_IDLE) |
||||||
|
{ |
||||||
|
if((out >= 1.f && current_segment_ == ADENV_SEG_ATTACK) |
||||||
|
|| (out <= 0.f && current_segment_ == ADENV_SEG_DECAY)) |
||||||
|
{ |
||||||
|
// Advance segment
|
||||||
|
current_segment_++; |
||||||
|
// TODO: Add Cycling feature here.
|
||||||
|
if(current_segment_ > ADENV_SEG_DECAY) |
||||||
|
{ |
||||||
|
current_segment_ = ADENV_SEG_IDLE; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if(current_segment_ == ADENV_SEG_IDLE) |
||||||
|
{ |
||||||
|
val = out = 0.0f; |
||||||
|
} |
||||||
|
output_ = val; |
||||||
|
|
||||||
|
return out * (max_ - min_) + min_; |
||||||
|
} |
@ -0,0 +1,92 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef ADENV_H |
||||||
|
#define ADENV_H |
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Distinct stages that the phase of the envelope can be located in.
|
||||||
|
@see AdEnv |
||||||
|
*/ |
||||||
|
enum AdEnvSegment |
||||||
|
{ |
||||||
|
/** located at phase location 0, and not currently running */ |
||||||
|
ADENV_SEG_IDLE, |
||||||
|
/** First segment of envelope where phase moves from MIN value to MAX value */ |
||||||
|
ADENV_SEG_ATTACK, |
||||||
|
/** Second segment of envelope where phase moves from MAX to MIN value */ |
||||||
|
ADENV_SEG_DECAY, |
||||||
|
/** The final segment of the envelope (currently decay) */ |
||||||
|
ADENV_SEG_LAST, |
||||||
|
}; |
||||||
|
|
||||||
|
/** Trigger-able envelope with adjustable min/max, and independent per-segment time control.
|
||||||
|
|
||||||
|
\author shensley |
||||||
|
\todo - Add Cycling |
||||||
|
\todo - Implement Curve (its only linear for now). |
||||||
|
\todo - Maybe make this an ADsr_ that has AD/AR/Asr_ modes. |
||||||
|
*/ |
||||||
|
class AdEnv |
||||||
|
{ |
||||||
|
public: |
||||||
|
AdEnv() {} |
||||||
|
~AdEnv() {} |
||||||
|
/** Initializes the ad envelope.
|
||||||
|
|
||||||
|
Defaults: |
||||||
|
- current segment = idle |
||||||
|
- curve = linear |
||||||
|
- phase = 0 |
||||||
|
- min = 0 |
||||||
|
- max = 1 |
||||||
|
|
||||||
|
\param sample_rate sample rate of the audio engine being run |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Processes the current sample of the envelope. This should be called once
|
||||||
|
per sample period. |
||||||
|
\return the current envelope value. |
||||||
|
*/ |
||||||
|
float Process(); |
||||||
|
|
||||||
|
/** Starts or retriggers the envelope.*/ |
||||||
|
inline void Trigger() { trigger_ = 1; } |
||||||
|
/** Sets the length of time (in seconds) for a specific segment. */ |
||||||
|
inline void SetTime(uint8_t seg, float time) { segment_time_[seg] = time; } |
||||||
|
/** Sets the amount of curve applied. A positve value will create a log
|
||||||
|
curve. Input range: -100 to 100. (or more) |
||||||
|
*/ |
||||||
|
inline void SetCurve(float scalar) { curve_scalar_ = scalar; } |
||||||
|
/** Sets the minimum value of the envelope output.
|
||||||
|
Input range: -FLTmax_, to FLTmax_ |
||||||
|
*/ |
||||||
|
inline void SetMin(float min) { min_ = min; } |
||||||
|
/** Sets the maximum value of the envelope output.
|
||||||
|
Input range: -FLTmax_, to FLTmax_ |
||||||
|
*/ |
||||||
|
inline void SetMax(float max) { max_ = max; } |
||||||
|
/** Returns the current output value without processing the next sample */ |
||||||
|
inline float GetValue() const { return (output_ * (max_ - min_)) + min_; } |
||||||
|
/** Returns the segment of the envelope that the phase is currently located
|
||||||
|
in. |
||||||
|
*/ |
||||||
|
inline uint8_t GetCurrentSegment() { return current_segment_; } |
||||||
|
/** Returns true if the envelope is currently in any stage apart from idle.
|
||||||
|
*/ |
||||||
|
inline bool IsRunning() const { return current_segment_ != ADENV_SEG_IDLE; } |
||||||
|
|
||||||
|
private: |
||||||
|
uint8_t current_segment_, prev_segment_; |
||||||
|
float segment_time_[ADENV_SEG_LAST]; |
||||||
|
float sample_rate_, min_, max_, output_, curve_scalar_; |
||||||
|
float c_inc_, curve_x_, retrig_val_; |
||||||
|
uint32_t phase_; |
||||||
|
uint8_t trigger_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,82 @@ |
|||||||
|
#include "adsr.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
float Adsr::Tau2Pole(float tau) |
||||||
|
{ |
||||||
|
return expf(-1.0f / (tau * sample_rate_)); |
||||||
|
} |
||||||
|
|
||||||
|
float Adsr::AdsrFilter() |
||||||
|
{ |
||||||
|
y_ = b_ * x_ + a_ * y_; |
||||||
|
return y_; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void Adsr::Init(float sample_rate) |
||||||
|
{ |
||||||
|
seg_time_[ADSR_SEG_ATTACK] = 0.1f; |
||||||
|
seg_time_[ADSR_SEG_DECAY] = 0.1f; |
||||||
|
sus_ = 0.7f; |
||||||
|
seg_time_[ADSR_SEG_RELEASE] = 0.1f; |
||||||
|
//timer_ = 0;
|
||||||
|
a_ = 0.0f; |
||||||
|
b_ = 0.0f; |
||||||
|
x_ = 0.0f; |
||||||
|
y_ = 0.0f; |
||||||
|
prev_ = 0.0f; |
||||||
|
atk_time_ = seg_time_[ADSR_SEG_ATTACK] * sample_rate; |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
} |
||||||
|
|
||||||
|
float Adsr::Process(bool gate) |
||||||
|
{ |
||||||
|
float pole, out; |
||||||
|
out = 0.0f; |
||||||
|
|
||||||
|
if(gate && mode_ != ADSR_SEG_DECAY) |
||||||
|
{ |
||||||
|
mode_ = ADSR_SEG_ATTACK; |
||||||
|
//timer_ = 0;
|
||||||
|
pole = Tau2Pole(seg_time_[ADSR_SEG_ATTACK] * 0.6f); |
||||||
|
atk_time_ = seg_time_[ADSR_SEG_ATTACK] * sample_rate_; |
||||||
|
a_ = pole; |
||||||
|
b_ = 1.0f - pole; |
||||||
|
} |
||||||
|
else if(!gate && mode_ != ADSR_SEG_IDLE) |
||||||
|
{ |
||||||
|
mode_ = ADSR_SEG_RELEASE; |
||||||
|
pole = Tau2Pole(seg_time_[ADSR_SEG_RELEASE]); |
||||||
|
a_ = pole; |
||||||
|
b_ = 1.0f - pole; |
||||||
|
} |
||||||
|
|
||||||
|
x_ = (int)gate; |
||||||
|
prev_ = (int)gate; |
||||||
|
|
||||||
|
switch(mode_) |
||||||
|
{ |
||||||
|
case ADSR_SEG_IDLE: out = 0.0f; break; |
||||||
|
case ADSR_SEG_ATTACK: |
||||||
|
out = AdsrFilter(); |
||||||
|
|
||||||
|
if(out > .99f) |
||||||
|
{ |
||||||
|
mode_ = ADSR_SEG_DECAY; |
||||||
|
pole = Tau2Pole(seg_time_[ADSR_SEG_DECAY]); |
||||||
|
a_ = pole; |
||||||
|
b_ = 1.0f - pole; |
||||||
|
} |
||||||
|
break; |
||||||
|
case ADSR_SEG_DECAY: |
||||||
|
case ADSR_SEG_RELEASE: |
||||||
|
x_ *= sus_; |
||||||
|
out = AdsrFilter(); |
||||||
|
if(out <= 0.01f) |
||||||
|
mode_ = ADSR_SEG_IDLE; |
||||||
|
default: break; |
||||||
|
} |
||||||
|
return out; |
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_ADSR_H |
||||||
|
#define DSY_ADSR_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Distinct stages that the phase of the envelope can be located in.
|
||||||
|
- IDLE = located at phase location 0, and not currently running |
||||||
|
- ATTACK = First segment of envelope where phase moves from 0 to 1 |
||||||
|
- DECAY = Second segment of envelope where phase moves from 1 to SUSTAIN value |
||||||
|
- SUSTAIN = Third segment of envelope, stays at SUSTAIN level until GATE is released |
||||||
|
- RELEASE = Fourth segment of envelop where phase moves from SUSTAIN to 0 |
||||||
|
- LAST = Last segment, aka release |
||||||
|
*/ |
||||||
|
enum |
||||||
|
{ |
||||||
|
ADSR_SEG_IDLE, |
||||||
|
ADSR_SEG_ATTACK, |
||||||
|
ADSR_SEG_DECAY, |
||||||
|
ADSR_SEG_SUSTAIN, |
||||||
|
ADSR_SEG_RELEASE, |
||||||
|
ADSR_SEG_LAST, |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
/** adsr envelope module
|
||||||
|
|
||||||
|
Original author(s) : Paul Batchelor |
||||||
|
|
||||||
|
Ported from Soundpipe by Ben Sergentanis, May 2020 |
||||||
|
*/ |
||||||
|
class Adsr |
||||||
|
{ |
||||||
|
public: |
||||||
|
Adsr() {} |
||||||
|
~Adsr() {} |
||||||
|
/** Initializes the Adsr module.
|
||||||
|
\param sample_rate - The sample rate of the audio engine being run.
|
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
|
||||||
|
/** Processes one sample through the filter and returns one sample.
|
||||||
|
\param gate - trigger the envelope, hold it to sustain
|
||||||
|
*/ |
||||||
|
float Process(bool gate); |
||||||
|
|
||||||
|
/** Sets time
|
||||||
|
Set time per segment in seconds |
||||||
|
*/ |
||||||
|
inline void SetTime(int seg, float time) { seg_time_[seg] = time; } |
||||||
|
/** Sustain level
|
||||||
|
\param sus_level - sets sustain level |
||||||
|
*/ |
||||||
|
inline void SetSustainLevel(float sus_level) { sus_ = sus_level; } |
||||||
|
/** get the current envelope segment
|
||||||
|
\return the segment of the envelope that the phase is currently located in. |
||||||
|
*/ |
||||||
|
inline uint8_t GetCurrentSegment() { return mode_; } |
||||||
|
/** Tells whether envelope is active
|
||||||
|
\return true if the envelope is currently in any stage apart from idle. |
||||||
|
*/ |
||||||
|
inline bool IsRunning() const { return mode_ != ADSR_SEG_IDLE; } |
||||||
|
|
||||||
|
private: |
||||||
|
float sus_, seg_time_[ADSR_SEG_LAST], a_, b_, y_, x_, prev_, atk_time_; |
||||||
|
int sample_rate_; |
||||||
|
uint8_t mode_; |
||||||
|
float Tau2Pole(float tau); |
||||||
|
float AdsrFilter(); |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,41 @@ |
|||||||
|
#include "line.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Line::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
dur_ = 0.5f; |
||||||
|
end_ = 0.0f; |
||||||
|
start_ = 1.0f; |
||||||
|
val_ = 1.0f; |
||||||
|
} |
||||||
|
|
||||||
|
void Line::Start(float start, float end, float dur) |
||||||
|
{ |
||||||
|
start_ = start; |
||||||
|
end_ = end; |
||||||
|
dur_ = dur; |
||||||
|
inc_ = (end - start) / ((sample_rate_ * dur_)); |
||||||
|
val_ = start_; |
||||||
|
finished_ = 0; |
||||||
|
} |
||||||
|
|
||||||
|
float Line::Process(uint8_t *finished) |
||||||
|
{ |
||||||
|
float out; |
||||||
|
out = val_; |
||||||
|
|
||||||
|
if((end_ > start_ && out >= end_) || (end_ < start_ && out <= end_)) |
||||||
|
{ |
||||||
|
finished_ = 1; |
||||||
|
val_ = end_; |
||||||
|
out = end_; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
val_ += inc_; |
||||||
|
} |
||||||
|
*finished = finished_; |
||||||
|
return out; |
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef LINE_H |
||||||
|
#define LINE_H |
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** creates a Line segment signal
|
||||||
|
*/ |
||||||
|
class Line |
||||||
|
{ |
||||||
|
public: |
||||||
|
Line() {} |
||||||
|
~Line() {} |
||||||
|
/** Initializes Line module.
|
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Processes Line segment. Returns one sample.
|
||||||
|
value of finished will be updated to a 1, upon completion of the Line's trajectory. |
||||||
|
*/ |
||||||
|
float Process(uint8_t *finished); |
||||||
|
|
||||||
|
/** Begin creation of Line.
|
||||||
|
\param start - beginning value |
||||||
|
\param end - ending value |
||||||
|
\param dur - duration in seconds of Line segment |
||||||
|
*/ |
||||||
|
void Start(float start, float end, float dur); |
||||||
|
|
||||||
|
private: |
||||||
|
float start_, end_, dur_; |
||||||
|
float inc_, val_, sample_rate_; |
||||||
|
uint8_t finished_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,27 @@ |
|||||||
|
#include <math.h> |
||||||
|
#include "phasor.h" |
||||||
|
#include "../utility/dsp.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Phasor::SetFreq(float freq) |
||||||
|
{ |
||||||
|
freq_ = freq; |
||||||
|
inc_ = (TWOPI_F * freq_) / sample_rate_; |
||||||
|
} |
||||||
|
|
||||||
|
float Phasor::Process() |
||||||
|
{ |
||||||
|
float out; |
||||||
|
out = phs_ / TWOPI_F; |
||||||
|
phs_ += inc_; |
||||||
|
if(phs_ > TWOPI_F) |
||||||
|
{ |
||||||
|
phs_ -= TWOPI_F; |
||||||
|
} |
||||||
|
if(phs_ < 0.0f) |
||||||
|
{ |
||||||
|
phs_ = 0.0f; |
||||||
|
} |
||||||
|
return out; |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_PHASOR_H |
||||||
|
#define DSY_PHASOR_H |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Generates a normalized signal moving from 0-1 at the specified frequency.
|
||||||
|
|
||||||
|
\todo Selecting which channels should be initialized/included in the sequence conversion. |
||||||
|
\todo Setup a similar start function for an external mux, but that seems outside the scope of this file. |
||||||
|
|
||||||
|
*/ |
||||||
|
class Phasor |
||||||
|
{ |
||||||
|
public: |
||||||
|
Phasor() {} |
||||||
|
~Phasor() {} |
||||||
|
/** Initializes the Phasor module
|
||||||
|
sample rate, and freq are in Hz |
||||||
|
initial phase is in radians |
||||||
|
Additional Init functions have defaults when arg is not specified: |
||||||
|
- phs = 0.0f |
||||||
|
- freq = 1.0f |
||||||
|
*/ |
||||||
|
inline void Init(float sample_rate, float freq, float initial_phase) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
phs_ = initial_phase; |
||||||
|
SetFreq(freq); |
||||||
|
} |
||||||
|
|
||||||
|
/** Initialize phasor with samplerate and freq
|
||||||
|
*/ |
||||||
|
inline void Init(float sample_rate, float freq) |
||||||
|
{ |
||||||
|
Init(sample_rate, freq, 0.0f); |
||||||
|
} |
||||||
|
|
||||||
|
/** Initialize phasor with samplerate
|
||||||
|
*/ |
||||||
|
inline void Init(float sample_rate) { Init(sample_rate, 1.0f, 0.0f); } |
||||||
|
/** processes Phasor and returns current value
|
||||||
|
*/ |
||||||
|
float Process(); |
||||||
|
|
||||||
|
|
||||||
|
/** Sets frequency of the Phasor in Hz
|
||||||
|
*/ |
||||||
|
void SetFreq(float freq); |
||||||
|
|
||||||
|
|
||||||
|
/** Returns current frequency value in Hz
|
||||||
|
*/ |
||||||
|
inline float GetFreq() { return freq_; } |
||||||
|
|
||||||
|
private: |
||||||
|
float freq_; |
||||||
|
float sample_rate_, inc_, phs_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,192 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "analogbassdrum.h" |
||||||
|
#include <cmath> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void AnalogBassDrum::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
trig_ = false; |
||||||
|
|
||||||
|
pulse_remaining_samples_ = 0; |
||||||
|
fm_pulse_remaining_samples_ = 0; |
||||||
|
pulse_ = 0.0f; |
||||||
|
pulse_height_ = 0.0f; |
||||||
|
pulse_lp_ = 0.0f; |
||||||
|
fm_pulse_lp_ = 0.0f; |
||||||
|
retrig_pulse_ = 0.0f; |
||||||
|
lp_out_ = 0.0f; |
||||||
|
tone_lp_ = 0.0f; |
||||||
|
sustain_gain_ = 0.0f; |
||||||
|
phase_ = 0.f; |
||||||
|
|
||||||
|
|
||||||
|
SetSustain(false); |
||||||
|
SetAccent(.1f); |
||||||
|
SetFreq(50.f); |
||||||
|
SetTone(.1f); |
||||||
|
SetDecay(.3f); |
||||||
|
SetSelfFmAmount(1.f); |
||||||
|
SetAttackFmAmount(.5f); |
||||||
|
|
||||||
|
resonator_.Init(sample_rate_); |
||||||
|
} |
||||||
|
|
||||||
|
inline float AnalogBassDrum::Diode(float x) |
||||||
|
{ |
||||||
|
if(x >= 0.0f) |
||||||
|
{ |
||||||
|
return x; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
x *= 2.0f; |
||||||
|
return 0.7f * x / (1.0f + fabsf(x)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
float AnalogBassDrum::Process(bool trigger) |
||||||
|
{ |
||||||
|
const int kTriggerPulseDuration = static_cast<int>(1.0e-3f * sample_rate_); |
||||||
|
const int kFMPulseDuration = static_cast<int>(6.0e-3f * sample_rate_); |
||||||
|
const float kPulseDecayTime = 0.2e-3f * sample_rate_; |
||||||
|
const float kPulseFilterTime = 0.1e-3f * sample_rate_; |
||||||
|
const float kRetrigPulseDuration = 0.05f * sample_rate_; |
||||||
|
|
||||||
|
const float scale = 0.001f / f0_; |
||||||
|
const float q = 1500.0f * powf(2.f, kOneTwelfth * decay_ * 80.0f); |
||||||
|
const float tone_f |
||||||
|
= fmin(4.0f * f0_ * powf(2.f, kOneTwelfth * tone_ * 108.0f), 1.0f); |
||||||
|
const float exciter_leak = 0.08f * (tone_ + 0.25f); |
||||||
|
|
||||||
|
|
||||||
|
if(trigger || trig_) |
||||||
|
{ |
||||||
|
trig_ = false; |
||||||
|
|
||||||
|
pulse_remaining_samples_ = kTriggerPulseDuration; |
||||||
|
fm_pulse_remaining_samples_ = kFMPulseDuration; |
||||||
|
pulse_height_ = 3.0f + 7.0f * accent_; |
||||||
|
lp_out_ = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
// Q39 / Q40
|
||||||
|
float pulse = 0.0f; |
||||||
|
if(pulse_remaining_samples_) |
||||||
|
{ |
||||||
|
--pulse_remaining_samples_; |
||||||
|
pulse = pulse_remaining_samples_ ? pulse_height_ : pulse_height_ - 1.0f; |
||||||
|
pulse_ = pulse; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
pulse_ *= 1.0f - 1.0f / kPulseDecayTime; |
||||||
|
pulse = pulse_; |
||||||
|
} |
||||||
|
if(sustain_) |
||||||
|
{ |
||||||
|
pulse = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
// C40 / R163 / R162 / D83
|
||||||
|
fonepole(pulse_lp_, pulse, 1.0f / kPulseFilterTime); |
||||||
|
pulse = Diode((pulse - pulse_lp_) + pulse * 0.044f); |
||||||
|
|
||||||
|
// Q41 / Q42
|
||||||
|
float fm_pulse = 0.0f; |
||||||
|
if(fm_pulse_remaining_samples_) |
||||||
|
{ |
||||||
|
--fm_pulse_remaining_samples_; |
||||||
|
fm_pulse = 1.0f; |
||||||
|
// C39 / C52
|
||||||
|
retrig_pulse_ = fm_pulse_remaining_samples_ ? 0.0f : -0.8f; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// C39 / R161
|
||||||
|
retrig_pulse_ *= 1.0f - 1.0f / kRetrigPulseDuration; |
||||||
|
} |
||||||
|
if(sustain_) |
||||||
|
{ |
||||||
|
fm_pulse = 0.0f; |
||||||
|
} |
||||||
|
fonepole(fm_pulse_lp_, fm_pulse, 1.0f / kPulseFilterTime); |
||||||
|
|
||||||
|
// Q43 and R170 leakage
|
||||||
|
float punch = 0.7f + Diode(10.0f * lp_out_ - 1.0f); |
||||||
|
|
||||||
|
// Q43 / R165
|
||||||
|
float attack_fm = fm_pulse_lp_ * 1.7f * attack_fm_amount_; |
||||||
|
float self_fm = punch * 0.08f * self_fm_amount_; |
||||||
|
float f = f0_ * (1.0f + attack_fm + self_fm); |
||||||
|
f = fclamp(f, 0.0f, 0.4f); |
||||||
|
|
||||||
|
float resonator_out; |
||||||
|
if(sustain_) |
||||||
|
{ |
||||||
|
sustain_gain_ = accent_ * decay_; |
||||||
|
phase_ += f; |
||||||
|
phase_ = phase_ >= 1.f ? phase_ - 1.f : phase_; |
||||||
|
|
||||||
|
resonator_out = sin(TWOPI_F * phase_) * sustain_gain_; |
||||||
|
lp_out_ = cos(TWOPI_F * phase_) * sustain_gain_; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
resonator_.SetFreq(f * sample_rate_); |
||||||
|
//resonator_.SetRes(1.0f + q * f);
|
||||||
|
resonator_.SetRes(.4f * q * f); |
||||||
|
|
||||||
|
resonator_.Process((pulse - retrig_pulse_ * 0.2f) * scale); |
||||||
|
resonator_out = resonator_.Band(); |
||||||
|
lp_out_ = resonator_.Low(); |
||||||
|
} |
||||||
|
|
||||||
|
fonepole(tone_lp_, pulse * exciter_leak + resonator_out, tone_f); |
||||||
|
|
||||||
|
return tone_lp_; |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogBassDrum::Trig() |
||||||
|
{ |
||||||
|
trig_ = true; |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogBassDrum::SetSustain(bool sustain) |
||||||
|
{ |
||||||
|
sustain_ = sustain; |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogBassDrum::SetAccent(float accent) |
||||||
|
{ |
||||||
|
accent_ = fclamp(accent, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogBassDrum::SetFreq(float f0) |
||||||
|
{ |
||||||
|
f0 /= sample_rate_; |
||||||
|
f0_ = fclamp(f0, 0.f, .5f); |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogBassDrum::SetTone(float tone) |
||||||
|
{ |
||||||
|
tone_ = fclamp(tone, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogBassDrum::SetDecay(float decay) |
||||||
|
{ |
||||||
|
decay_ = decay * .1f; |
||||||
|
decay_ -= .1f; |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogBassDrum::SetAttackFmAmount(float attack_fm_amount) |
||||||
|
{ |
||||||
|
attack_fm_amount_ = attack_fm_amount * 50.f; |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogBassDrum::SetSelfFmAmount(float self_fm_amount) |
||||||
|
{ |
||||||
|
self_fm_amount_ = self_fm_amount * 50.f; |
||||||
|
} |
@ -0,0 +1,105 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_ANALOG_BD_H |
||||||
|
#define DSY_ANALOG_BD_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
#include "Synthesis/oscillator.h" |
||||||
|
#include "Filters/svf.h" |
||||||
|
|
||||||
|
/** @file analogbassdrum.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief 808 bass drum model, revisited. |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021
|
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/drums/analog_bass_drum.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class AnalogBassDrum |
||||||
|
{ |
||||||
|
public: |
||||||
|
AnalogBassDrum() {} |
||||||
|
~AnalogBassDrum() {} |
||||||
|
|
||||||
|
/** Initialize the module
|
||||||
|
\param sample_rate Audio engine sample rate |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the next sample.
|
||||||
|
\param trigger True strikes the drum. Defaults to false. |
||||||
|
*/ |
||||||
|
float Process(bool trigger = false); |
||||||
|
|
||||||
|
/** Strikes the drum. */ |
||||||
|
void Trig(); |
||||||
|
|
||||||
|
/** Set the bassdrum to play infinitely
|
||||||
|
\param sustain True = infinite length |
||||||
|
*/ |
||||||
|
void SetSustain(bool sustain); |
||||||
|
|
||||||
|
/** Set a small accent.
|
||||||
|
\param accent Works 0-1 |
||||||
|
*/ |
||||||
|
void SetAccent(float accent); |
||||||
|
|
||||||
|
/** Set the drum's root frequency
|
||||||
|
\param f0 Frequency in Hz |
||||||
|
*/ |
||||||
|
void SetFreq(float f0); |
||||||
|
|
||||||
|
/** Set the amount of click.
|
||||||
|
\param tone Works 0-1. |
||||||
|
*/ |
||||||
|
void SetTone(float tone); |
||||||
|
|
||||||
|
/** Set the decay length of the drum.
|
||||||
|
\param decay Works best 0-1. |
||||||
|
*/ |
||||||
|
void SetDecay(float decay); |
||||||
|
|
||||||
|
/** Set the amount of fm attack. Works together with self fm.
|
||||||
|
\param attack_fm_amount Works best 0-1. |
||||||
|
*/ |
||||||
|
void SetAttackFmAmount(float attack_fm_amount); |
||||||
|
|
||||||
|
/**Set the amount of self fm. Also affects fm attack, and volume decay.
|
||||||
|
\param self_fm_amount Works best 0-1. |
||||||
|
*/ |
||||||
|
void SetSelfFmAmount(float self_fm_amount); |
||||||
|
|
||||||
|
private: |
||||||
|
inline float Diode(float x); |
||||||
|
|
||||||
|
float sample_rate_; |
||||||
|
|
||||||
|
float accent_, f0_, tone_, decay_; |
||||||
|
float attack_fm_amount_, self_fm_amount_; |
||||||
|
|
||||||
|
bool trig_, sustain_; |
||||||
|
|
||||||
|
int pulse_remaining_samples_; |
||||||
|
int fm_pulse_remaining_samples_; |
||||||
|
float pulse_; |
||||||
|
float pulse_height_; |
||||||
|
float pulse_lp_; |
||||||
|
float fm_pulse_lp_; |
||||||
|
float retrig_pulse_; |
||||||
|
float lp_out_; |
||||||
|
float tone_lp_; |
||||||
|
float sustain_gain_; |
||||||
|
|
||||||
|
Svf resonator_; |
||||||
|
|
||||||
|
//for use in sin + cos osc. in sustain mode
|
||||||
|
float phase_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,219 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "analogsnaredrum.h" |
||||||
|
#include <math.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
static const int kNumModes = 5; |
||||||
|
|
||||||
|
void AnalogSnareDrum::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
trig_ = false; |
||||||
|
|
||||||
|
pulse_remaining_samples_ = 0; |
||||||
|
pulse_ = 0.0f; |
||||||
|
pulse_height_ = 0.0f; |
||||||
|
pulse_lp_ = 0.0f; |
||||||
|
noise_envelope_ = 0.0f; |
||||||
|
sustain_gain_ = 0.0f; |
||||||
|
|
||||||
|
SetSustain(false); |
||||||
|
SetAccent(.6f); |
||||||
|
SetFreq(200.f); |
||||||
|
SetDecay(.3f); |
||||||
|
SetSnappy(.7f); |
||||||
|
SetTone(.5f); |
||||||
|
|
||||||
|
for(int i = 0; i < kNumModes; ++i) |
||||||
|
{ |
||||||
|
resonator_[i].Init(sample_rate_); |
||||||
|
phase_[i] = 0.f; |
||||||
|
} |
||||||
|
noise_filter_.Init(sample_rate_); |
||||||
|
} |
||||||
|
|
||||||
|
/** Trigger the drum */ |
||||||
|
void AnalogSnareDrum::Trig() |
||||||
|
{ |
||||||
|
trig_ = true; |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogSnareDrum::SetSustain(bool sustain) |
||||||
|
{ |
||||||
|
sustain_ = sustain; |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogSnareDrum::SetAccent(float accent) |
||||||
|
{ |
||||||
|
accent_ = fclamp(accent, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogSnareDrum::SetFreq(float f0) |
||||||
|
{ |
||||||
|
f0 = f0 / sample_rate_; |
||||||
|
f0_ = fclamp(f0, 0.f, .4f); |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogSnareDrum::SetTone(float tone) |
||||||
|
{ |
||||||
|
tone_ = fclamp(tone, 0.f, 1.f); |
||||||
|
tone_ *= 2.f; |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogSnareDrum::SetDecay(float decay) |
||||||
|
{ |
||||||
|
decay_ = decay; |
||||||
|
return; |
||||||
|
decay_ = fmax(decay, 0.f); |
||||||
|
} |
||||||
|
|
||||||
|
void AnalogSnareDrum::SetSnappy(float snappy) |
||||||
|
{ |
||||||
|
snappy_ = fclamp(snappy, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
float AnalogSnareDrum::Process(bool trigger) |
||||||
|
{ |
||||||
|
const float decay_xt = decay_ * (1.0f + decay_ * (decay_ - 1.0f)); |
||||||
|
const int kTriggerPulseDuration = 1.0e-3 * sample_rate_; |
||||||
|
const float kPulseDecayTime = 0.1e-3 * sample_rate_; |
||||||
|
const float q = 2000.0f * powf(2.f, kOneTwelfth * decay_xt * 84.0f); |
||||||
|
const float noise_envelope_decay |
||||||
|
= 1.0f |
||||||
|
- 0.0017f |
||||||
|
* powf(2.f, |
||||||
|
kOneTwelfth * (-decay_ * (50.0f + snappy_ * 10.0f))); |
||||||
|
const float exciter_leak = snappy_ * (2.0f - snappy_) * 0.1f; |
||||||
|
|
||||||
|
float snappy = snappy_ * 1.1f - 0.05f; |
||||||
|
snappy = fclamp(snappy, 0.0f, 1.0f); |
||||||
|
|
||||||
|
float tone = tone_; |
||||||
|
|
||||||
|
if(trigger || trig_) |
||||||
|
{ |
||||||
|
trig_ = false; |
||||||
|
pulse_remaining_samples_ = kTriggerPulseDuration; |
||||||
|
pulse_height_ = 3.0f + 7.0f * accent_; |
||||||
|
noise_envelope_ = 2.0f; |
||||||
|
} |
||||||
|
|
||||||
|
static const float kModeFrequencies[kNumModes] |
||||||
|
= {1.00f, 2.00f, 3.18f, 4.16f, 5.62f}; |
||||||
|
|
||||||
|
float f[kNumModes]; |
||||||
|
float gain[kNumModes]; |
||||||
|
|
||||||
|
for(int i = 0; i < kNumModes; ++i) |
||||||
|
{ |
||||||
|
f[i] = fmin(f0_ * kModeFrequencies[i], 0.499f); |
||||||
|
resonator_[i].SetFreq(f[i] * sample_rate_); |
||||||
|
// resonator_[i].SetRes(1.0f + f[i] * (i == 0 ? q : q * 0.25f));
|
||||||
|
resonator_[i].SetRes((f[i] * (i == 0 ? q : q * 0.25f)) * .2); |
||||||
|
} |
||||||
|
|
||||||
|
if(tone < 0.666667f) |
||||||
|
{ |
||||||
|
// 808-style (2 modes)
|
||||||
|
tone *= 1.5f; |
||||||
|
gain[0] = 1.5f + (1.0f - tone) * (1.0f - tone) * 4.5f; |
||||||
|
gain[1] = 2.0f * tone + 0.15f; |
||||||
|
for(int i = 2; i < kNumModes; i++) |
||||||
|
{ |
||||||
|
gain[i] = 0.f; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// What the 808 could have been if there were extra modes!
|
||||||
|
tone = (tone - 0.666667f) * 3.0f; |
||||||
|
gain[0] = 1.5f - tone * 0.5f; |
||||||
|
gain[1] = 2.15f - tone * 0.7f; |
||||||
|
for(int i = 2; i < kNumModes; ++i) |
||||||
|
{ |
||||||
|
gain[i] = tone; |
||||||
|
tone *= tone; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
float f_noise = f0_ * 16.0f; |
||||||
|
fclamp(f_noise, 0.0f, 0.499f); |
||||||
|
noise_filter_.SetFreq(f_noise * sample_rate_); |
||||||
|
//noise_filter_.SetRes(1.0f + f_noise * 1.5f);
|
||||||
|
noise_filter_.SetRes(f_noise * 1.5f); |
||||||
|
|
||||||
|
// Q45 / Q46
|
||||||
|
float pulse = 0.0f; |
||||||
|
if(pulse_remaining_samples_) |
||||||
|
{ |
||||||
|
--pulse_remaining_samples_; |
||||||
|
pulse = pulse_remaining_samples_ ? pulse_height_ : pulse_height_ - 1.0f; |
||||||
|
pulse_ = pulse; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
pulse_ *= 1.0f - 1.0f / kPulseDecayTime; |
||||||
|
pulse = pulse_; |
||||||
|
} |
||||||
|
|
||||||
|
float sustain_gain_value = sustain_gain_ = accent_ * decay_; |
||||||
|
|
||||||
|
// R189 / C57 / R190 + C58 / C59 / R197 / R196 / IC14
|
||||||
|
pulse_lp_ = fclamp(pulse_lp_, pulse, 0.75f); |
||||||
|
|
||||||
|
float shell = 0.0f; |
||||||
|
for(int i = 0; i < kNumModes; ++i) |
||||||
|
{ |
||||||
|
float excitation |
||||||
|
= i == 0 ? (pulse - pulse_lp_) + 0.006f * pulse : 0.026f * pulse; |
||||||
|
|
||||||
|
phase_[i] += f[i]; |
||||||
|
phase_[i] = phase_[i] >= 1.f ? phase_[i] - 1.f : phase_[i]; |
||||||
|
|
||||||
|
resonator_[i].Process(excitation); |
||||||
|
|
||||||
|
shell += gain[i] |
||||||
|
* (sustain_ |
||||||
|
? sin(phase_[i] * TWOPI_F) * sustain_gain_value * 0.25f |
||||||
|
: resonator_[i].Band() + excitation * exciter_leak); |
||||||
|
} |
||||||
|
shell = SoftClip(shell); |
||||||
|
|
||||||
|
// C56 / R194 / Q48 / C54 / R188 / D54
|
||||||
|
float noise = 2.0f * rand() * kRandFrac - 1.0f; |
||||||
|
if(noise < 0.0f) |
||||||
|
noise = 0.0f; |
||||||
|
noise_envelope_ *= noise_envelope_decay; |
||||||
|
noise *= (sustain_ ? sustain_gain_value : noise_envelope_) * snappy * 2.0f; |
||||||
|
|
||||||
|
// C66 / R201 / C67 / R202 / R203 / Q49
|
||||||
|
noise_filter_.Process(noise); |
||||||
|
noise = noise_filter_.Band(); |
||||||
|
|
||||||
|
// IC13
|
||||||
|
return noise + shell * (1.0f - snappy); |
||||||
|
} |
||||||
|
|
||||||
|
inline float AnalogSnareDrum::SoftLimit(float x) |
||||||
|
{ |
||||||
|
return x * (27.0f + x * x) / (27.0f + 9.0f * x * x); |
||||||
|
} |
||||||
|
|
||||||
|
inline float AnalogSnareDrum::SoftClip(float x) |
||||||
|
{ |
||||||
|
if(x < -3.0f) |
||||||
|
{ |
||||||
|
return -1.0f; |
||||||
|
} |
||||||
|
else if(x > 3.0f) |
||||||
|
{ |
||||||
|
return 1.0f; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return SoftLimit(x); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,98 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_ANALOG_SNARE_H |
||||||
|
#define DSY_ANALOG_SNARE_H |
||||||
|
|
||||||
|
#include "Filters/svf.h" |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file analogsnaredrum.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief 808 snare drum model, revisited. |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/drums/analog_snare_drum.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class AnalogSnareDrum |
||||||
|
{ |
||||||
|
public: |
||||||
|
AnalogSnareDrum() {} |
||||||
|
~AnalogSnareDrum() {} |
||||||
|
|
||||||
|
static const int kNumModes = 5; |
||||||
|
|
||||||
|
/** Init the module
|
||||||
|
\param sample_rate Audio engine sample rate |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the next sample
|
||||||
|
\param trigger Hit the drum with true. Defaults to false. |
||||||
|
*/ |
||||||
|
float Process(bool trigger = false); |
||||||
|
|
||||||
|
/** Trigger the drum */ |
||||||
|
void Trig(); |
||||||
|
|
||||||
|
/** Init the module
|
||||||
|
\param sample_rate Audio engine sample rate |
||||||
|
*/ |
||||||
|
void SetSustain(bool sustain); |
||||||
|
|
||||||
|
/** Set how much accent to use
|
||||||
|
\param accent Works 0-1. |
||||||
|
*/ |
||||||
|
void SetAccent(float accent); |
||||||
|
|
||||||
|
/** Set the drum's root frequency
|
||||||
|
\param f0 Freq in Hz |
||||||
|
*/ |
||||||
|
void SetFreq(float f0); |
||||||
|
|
||||||
|
/** Set the brightness of the drum tone.
|
||||||
|
\param tone Works 0-1. 1 = bright, 0 = dark. |
||||||
|
*/ |
||||||
|
void SetTone(float tone); |
||||||
|
|
||||||
|
/** Set the length of the drum decay
|
||||||
|
\param decay Works with positive numbers |
||||||
|
*/ |
||||||
|
void SetDecay(float decay); |
||||||
|
|
||||||
|
/** Sets the mix between snare and drum.
|
||||||
|
\param snappy 1 = just snare. 0 = just drum. |
||||||
|
*/ |
||||||
|
void SetSnappy(float snappy); |
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_; |
||||||
|
|
||||||
|
float f0_, tone_, accent_, snappy_, decay_; |
||||||
|
bool sustain_; |
||||||
|
bool trig_; |
||||||
|
|
||||||
|
inline float SoftLimit(float x); |
||||||
|
inline float SoftClip(float x); |
||||||
|
|
||||||
|
int pulse_remaining_samples_; |
||||||
|
float pulse_; |
||||||
|
float pulse_height_; |
||||||
|
float pulse_lp_; |
||||||
|
float noise_envelope_; |
||||||
|
float sustain_gain_; |
||||||
|
|
||||||
|
Svf resonator_[kNumModes]; |
||||||
|
Svf noise_filter_; |
||||||
|
|
||||||
|
// Replace the resonators in "free running" (sustain) mode.
|
||||||
|
float phase_[kNumModes]; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,96 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "hihat.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void SquareNoise::Init(float sample_rate) |
||||||
|
{ |
||||||
|
for(int i = 0; i < 6; i++) |
||||||
|
{ |
||||||
|
phase_[i] = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
float SquareNoise::Process(float f0) |
||||||
|
{ |
||||||
|
const float ratios[6] = {// Nominal f0: 414 Hz
|
||||||
|
1.0f, |
||||||
|
1.304f, |
||||||
|
1.466f, |
||||||
|
1.787f, |
||||||
|
1.932f, |
||||||
|
2.536f}; |
||||||
|
|
||||||
|
uint32_t increment[6]; |
||||||
|
uint32_t phase[6]; |
||||||
|
for(int i = 0; i < 6; ++i) |
||||||
|
{ |
||||||
|
float f = f0 * ratios[i]; |
||||||
|
if(f >= 0.499f) |
||||||
|
f = 0.499f; |
||||||
|
increment[i] = static_cast<uint32_t>(f * 4294967296.0f); |
||||||
|
phase[i] = phase_[i]; |
||||||
|
} |
||||||
|
|
||||||
|
phase[0] += increment[0]; |
||||||
|
phase[1] += increment[1]; |
||||||
|
phase[2] += increment[2]; |
||||||
|
phase[3] += increment[3]; |
||||||
|
phase[4] += increment[4]; |
||||||
|
phase[5] += increment[5]; |
||||||
|
uint32_t noise = 0; |
||||||
|
noise += (phase[0] >> 31); |
||||||
|
noise += (phase[1] >> 31); |
||||||
|
noise += (phase[2] >> 31); |
||||||
|
noise += (phase[3] >> 31); |
||||||
|
noise += (phase[4] >> 31); |
||||||
|
noise += (phase[5] >> 31); |
||||||
|
|
||||||
|
for(int i = 0; i < 6; ++i) |
||||||
|
{ |
||||||
|
phase_[i] = phase[i]; |
||||||
|
} |
||||||
|
|
||||||
|
return 0.33f * static_cast<float>(noise) - 1.0f; |
||||||
|
} |
||||||
|
|
||||||
|
void RingModNoise::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
for(int i = 0; i < 6; ++i) |
||||||
|
{ |
||||||
|
oscillator_[i].Init(sample_rate_); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
float RingModNoise::Process(float f0) |
||||||
|
{ |
||||||
|
const float ratio = f0 / (0.01f + f0); |
||||||
|
const float f1a = 200.0f / sample_rate_ * ratio; |
||||||
|
const float f1b = 7530.0f / sample_rate_ * ratio; |
||||||
|
const float f2a = 510.0f / sample_rate_ * ratio; |
||||||
|
const float f2b = 8075.0f / sample_rate_ * ratio; |
||||||
|
const float f3a = 730.0f / sample_rate_ * ratio; |
||||||
|
const float f3b = 10500.0f / sample_rate_ * ratio; |
||||||
|
|
||||||
|
float out = ProcessPair(&oscillator_[0], f1a, f1b); |
||||||
|
out += ProcessPair(&oscillator_[2], f2a, f2b); |
||||||
|
out += ProcessPair(&oscillator_[4], f3a, f3b); |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
float RingModNoise::ProcessPair(Oscillator* osc, float f1, float f2) |
||||||
|
{ |
||||||
|
osc[0].SetWaveform(Oscillator::WAVE_SQUARE); |
||||||
|
osc[0].SetFreq(f1 * sample_rate_); |
||||||
|
float temp_1 = osc[0].Process(); |
||||||
|
|
||||||
|
osc[1].SetWaveform(Oscillator::WAVE_SAW); |
||||||
|
osc[1].SetFreq(f2 * sample_rate_); |
||||||
|
float temp_2 = osc[1].Process(); |
||||||
|
|
||||||
|
return temp_1 * temp_2; |
||||||
|
} |
@ -0,0 +1,269 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_HIHAT_H |
||||||
|
#define DSY_HIHAT_H |
||||||
|
|
||||||
|
#include "Filters/svf.h" |
||||||
|
#include "Synthesis/oscillator.h" |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file hihat.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief 808 style "metallic noise" with 6 square oscillators. |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/drums/hihat.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class SquareNoise |
||||||
|
{ |
||||||
|
public: |
||||||
|
SquareNoise() {} |
||||||
|
~SquareNoise() {} |
||||||
|
|
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
float Process(float f0); |
||||||
|
|
||||||
|
private: |
||||||
|
uint32_t phase_[6]; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Ring mod style metallic noise generator. |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/drums/hihat.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class RingModNoise |
||||||
|
{ |
||||||
|
public: |
||||||
|
RingModNoise() {} |
||||||
|
~RingModNoise() {} |
||||||
|
|
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
float Process(float f0); |
||||||
|
|
||||||
|
private: |
||||||
|
float ProcessPair(Oscillator* osc, float f1, float f2); |
||||||
|
Oscillator oscillator_[6]; |
||||||
|
|
||||||
|
float sample_rate_; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Swing type VCA |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/drums/hihat.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class SwingVCA |
||||||
|
{ |
||||||
|
public: |
||||||
|
float operator()(float s, float gain) |
||||||
|
{ |
||||||
|
s *= s > 0.0f ? 10.0f : 0.1f; |
||||||
|
s = s / (1.0f + fabsf(s)); |
||||||
|
return (s + 1.0f) * gain; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Linear type VCA |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/drums/hihat.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class LinearVCA |
||||||
|
{ |
||||||
|
public: |
||||||
|
float operator()(float s, float gain) { return s * gain; } |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief 808 HH, with a few extra parameters to push things to the CY territory... |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
The template parameter MetallicNoiseSource allows another kind of "metallic \n |
||||||
|
noise" to be used, for results which are more similar to KR-55 or FM hi-hats. \n \n |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/drums/hihat.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
template <typename MetallicNoiseSource = SquareNoise, |
||||||
|
typename VCA = LinearVCA, |
||||||
|
bool resonance = true> |
||||||
|
class HiHat |
||||||
|
{ |
||||||
|
public: |
||||||
|
HiHat() {} |
||||||
|
~HiHat() {} |
||||||
|
|
||||||
|
/** Initialize the module
|
||||||
|
\param sample_rate Audio engine sample rate |
||||||
|
*/ |
||||||
|
void Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
trig_ = false; |
||||||
|
|
||||||
|
envelope_ = 0.0f; |
||||||
|
noise_clock_ = 0.0f; |
||||||
|
noise_sample_ = 0.0f; |
||||||
|
sustain_gain_ = 0.0f; |
||||||
|
|
||||||
|
SetFreq(3000.f); |
||||||
|
SetTone(.5f); |
||||||
|
SetDecay(.2f); |
||||||
|
SetNoisiness(.8f); |
||||||
|
SetAccent(.8f); |
||||||
|
SetSustain(false); |
||||||
|
|
||||||
|
metallic_noise_.Init(sample_rate_); |
||||||
|
noise_coloration_svf_.Init(sample_rate_); |
||||||
|
hpf_.Init(sample_rate_); |
||||||
|
} |
||||||
|
|
||||||
|
/** Get the next sample
|
||||||
|
\param trigger Hit the hihat with true. Defaults to false. |
||||||
|
*/ |
||||||
|
float Process(bool trigger = false) |
||||||
|
{ |
||||||
|
const float envelope_decay |
||||||
|
= 1.0f - 0.003f * SemitonesToRatio(-decay_ * 84.0f); |
||||||
|
const float cut_decay |
||||||
|
= 1.0f - 0.0025f * SemitonesToRatio(-decay_ * 36.0f); |
||||||
|
|
||||||
|
if(trigger || trig_) |
||||||
|
{ |
||||||
|
trig_ = false; |
||||||
|
|
||||||
|
envelope_ |
||||||
|
= (1.5f + 0.5f * (1.0f - decay_)) * (0.3f + 0.7f * accent_); |
||||||
|
} |
||||||
|
|
||||||
|
// Process the metallic noise.
|
||||||
|
float out = metallic_noise_.Process(2.0f * f0_); |
||||||
|
|
||||||
|
// Apply BPF on the metallic noise.
|
||||||
|
float cutoff = 150.0f / sample_rate_ * SemitonesToRatio(tone_ * 72.0f); |
||||||
|
|
||||||
|
cutoff = fclamp(cutoff, 0.0f, 16000.0f / sample_rate_); |
||||||
|
|
||||||
|
|
||||||
|
noise_coloration_svf_.SetFreq(cutoff * sample_rate_); |
||||||
|
noise_coloration_svf_.SetRes(resonance ? 3.0f + 6.0f * tone_ : 1.0f); |
||||||
|
|
||||||
|
noise_coloration_svf_.Process(out); |
||||||
|
out = noise_coloration_svf_.Band(); |
||||||
|
|
||||||
|
// This is not at all part of the 808 circuit! But to add more variety, we
|
||||||
|
// add a variable amount of clocked noise to the output of the 6 schmitt
|
||||||
|
// trigger oscillators.
|
||||||
|
float noise_f = f0_ * (16.0f + 16.0f * (1.0f - noisiness_)); |
||||||
|
noise_f = fclamp(noise_f, 0.0f, 0.5f); |
||||||
|
|
||||||
|
noise_clock_ += noise_f; |
||||||
|
if(noise_clock_ >= 1.0f) |
||||||
|
{ |
||||||
|
noise_clock_ -= 1.0f; |
||||||
|
noise_sample_ = rand() * kRandFrac - 0.5f; |
||||||
|
} |
||||||
|
out += noisiness_ * (noise_sample_ - out); |
||||||
|
|
||||||
|
// Apply VCA.
|
||||||
|
sustain_gain_ = accent_ * decay_; |
||||||
|
VCA vca; |
||||||
|
envelope_ *= envelope_ > 0.5f ? envelope_decay : cut_decay; |
||||||
|
out = vca(out, sustain_ ? sustain_gain_ : envelope_); |
||||||
|
|
||||||
|
hpf_.SetFreq(cutoff * sample_rate_); |
||||||
|
hpf_.SetRes(.5f); |
||||||
|
hpf_.Process(out); |
||||||
|
out = hpf_.High(); |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
/** Trigger the hihat */ |
||||||
|
void Trig() { trig_ = true; } |
||||||
|
|
||||||
|
/** Make the hihat ring out infinitely.
|
||||||
|
\param sustain True = infinite sustain. |
||||||
|
*/ |
||||||
|
void SetSustain(bool sustain) { sustain_ = sustain; } |
||||||
|
|
||||||
|
/** Set how much accent to use
|
||||||
|
\param accent Works 0-1. |
||||||
|
*/ |
||||||
|
void SetAccent(float accent) { accent_ = fclamp(accent, 0.f, 1.f); } |
||||||
|
|
||||||
|
/** Set the hihat tone's root frequency
|
||||||
|
\param f0 Freq in Hz |
||||||
|
*/ |
||||||
|
void SetFreq(float f0) |
||||||
|
{ |
||||||
|
f0 /= sample_rate_; |
||||||
|
f0_ = fclamp(f0, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
/** Set the overall brightness / darkness of the hihat.
|
||||||
|
\param tone Works from 0-1. |
||||||
|
*/ |
||||||
|
void SetTone(float tone) { tone_ = fclamp(tone, 0.f, 1.f); } |
||||||
|
|
||||||
|
/** Set the length of the hihat decay
|
||||||
|
\param decay Works > 0. Tuned for 0-1. |
||||||
|
*/ |
||||||
|
void SetDecay(float decay) |
||||||
|
{ |
||||||
|
decay_ = fmax(decay, 0.f); |
||||||
|
decay_ *= 1.7; |
||||||
|
decay_ -= 1.2; |
||||||
|
} |
||||||
|
|
||||||
|
/** Sets the mix between tone and noise
|
||||||
|
\param snappy 1 = just noise. 0 = just tone. |
||||||
|
*/ |
||||||
|
void SetNoisiness(float noisiness) |
||||||
|
{ |
||||||
|
noisiness_ = fclamp(noisiness, 0.f, 1.f); |
||||||
|
noisiness_ *= noisiness_; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_; |
||||||
|
|
||||||
|
float accent_, f0_, tone_, decay_, noisiness_; |
||||||
|
bool sustain_; |
||||||
|
bool trig_; |
||||||
|
|
||||||
|
float SemitonesToRatio(float in) { return powf(2.f, in * kOneTwelfth); } |
||||||
|
|
||||||
|
float envelope_; |
||||||
|
float noise_clock_; |
||||||
|
float noise_sample_; |
||||||
|
float sustain_gain_; |
||||||
|
|
||||||
|
MetallicNoiseSource metallic_noise_; |
||||||
|
Svf noise_coloration_svf_; |
||||||
|
Svf hpf_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,231 @@ |
|||||||
|
#include "synthbassdrum.h" |
||||||
|
#include <math.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void SyntheticBassDrumClick::Init(float sample_rate) |
||||||
|
{ |
||||||
|
lp_ = 0.0f; |
||||||
|
hp_ = 0.0f; |
||||||
|
filter_.Init(sample_rate); |
||||||
|
filter_.SetFreq(5000.0f); |
||||||
|
filter_.SetRes(1.f); //2.f
|
||||||
|
} |
||||||
|
|
||||||
|
float SyntheticBassDrumClick::Process(float in) |
||||||
|
{ |
||||||
|
//SLOPE(lp_, in, 0.5f, 0.1f);
|
||||||
|
float error = in - lp_; |
||||||
|
lp_ += (error > 0 ? .5f : .1f) * error; |
||||||
|
|
||||||
|
fonepole(hp_, lp_, 0.04f); |
||||||
|
filter_.Process(lp_ - hp_); |
||||||
|
return filter_.Low(); |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticBassDrumAttackNoise::Init() |
||||||
|
{ |
||||||
|
lp_ = 0.0f; |
||||||
|
hp_ = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
float SyntheticBassDrumAttackNoise::Process() |
||||||
|
{ |
||||||
|
float sample = rand() * kRandFrac; |
||||||
|
fonepole(lp_, sample, 0.05f); |
||||||
|
fonepole(hp_, lp_, 0.005f); |
||||||
|
return lp_ - hp_; |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticBassDrum::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
trig_ = false; |
||||||
|
|
||||||
|
phase_ = 0.0f; |
||||||
|
phase_noise_ = 0.0f; |
||||||
|
f0_ = 0.0f; |
||||||
|
fm_ = 0.0f; |
||||||
|
fm_lp_ = 0.0f; |
||||||
|
body_env_lp_ = 0.0f; |
||||||
|
body_env_ = 0.0f; |
||||||
|
body_env_pulse_width_ = 0; |
||||||
|
fm_pulse_width_ = 0; |
||||||
|
tone_lp_ = 0.0f; |
||||||
|
sustain_gain_ = 0.0f; |
||||||
|
|
||||||
|
SetFreq(100.f); |
||||||
|
SetSustain(false); |
||||||
|
SetAccent(.2f); |
||||||
|
SetTone(.6f); |
||||||
|
SetDecay(.7f); |
||||||
|
SetDirtiness(.3f); |
||||||
|
SetFmEnvelopeAmount(.6); |
||||||
|
SetFmEnvelopeDecay(.3); |
||||||
|
|
||||||
|
click_.Init(sample_rate); |
||||||
|
noise_.Init(); |
||||||
|
} |
||||||
|
|
||||||
|
inline float SyntheticBassDrum::DistortedSine(float phase, |
||||||
|
float phase_noise, |
||||||
|
float dirtiness) |
||||||
|
{ |
||||||
|
phase += phase_noise * dirtiness; |
||||||
|
|
||||||
|
//MAKE_INTEGRAL_FRACTIONAL(phase);
|
||||||
|
int32_t phase_integral = static_cast<int32_t>(phase); |
||||||
|
float phase_fractional = phase - static_cast<float>(phase_integral); |
||||||
|
|
||||||
|
phase = phase_fractional; |
||||||
|
float triangle = (phase < 0.5f ? phase : 1.0f - phase) * 4.0f - 1.0f; |
||||||
|
float sine = 2.0f * triangle / (1.0f + fabsf(triangle)); |
||||||
|
float clean_sine = sinf(TWOPI_F * (phase + 0.75f)); |
||||||
|
return sine + (1.0f - dirtiness) * (clean_sine - sine); |
||||||
|
} |
||||||
|
|
||||||
|
inline float SyntheticBassDrum::TransistorVCA(float s, float gain) |
||||||
|
{ |
||||||
|
s = (s - 0.6f) * gain; |
||||||
|
return 3.0f * s / (2.0f + fabsf(s)) + gain * 0.3f; |
||||||
|
} |
||||||
|
|
||||||
|
float SyntheticBassDrum::Process(bool trigger) |
||||||
|
{ |
||||||
|
float dirtiness = dirtiness_; |
||||||
|
dirtiness *= fmax(1.0f - 8.0f * new_f0_, 0.0f); |
||||||
|
|
||||||
|
const float fm_decay |
||||||
|
= 1.0f |
||||||
|
- 1.0f / (0.008f * (1.0f + fm_envelope_decay_ * 4.0f) * sample_rate_); |
||||||
|
|
||||||
|
const float body_env_decay |
||||||
|
= 1.0f |
||||||
|
- 1.0f / (0.02f * sample_rate_) |
||||||
|
* powf(2.f, (-decay_ * 60.0f) * kOneTwelfth); |
||||||
|
const float transient_env_decay = 1.0f - 1.0f / (0.005f * sample_rate_); |
||||||
|
const float tone_f = fmin( |
||||||
|
4.0f * new_f0_ * powf(2.f, (tone_ * 108.0f) * kOneTwelfth), 1.0f); |
||||||
|
const float transient_level = tone_; |
||||||
|
|
||||||
|
if(trigger || trig_) |
||||||
|
{ |
||||||
|
trig_ = false; |
||||||
|
fm_ = 1.0f; |
||||||
|
body_env_ = transient_env_ = 0.3f + 0.7f * accent_; |
||||||
|
body_env_pulse_width_ = sample_rate_ * 0.001f; |
||||||
|
fm_pulse_width_ = sample_rate_ * 0.0013f; |
||||||
|
} |
||||||
|
|
||||||
|
sustain_gain_ = accent_ * decay_; |
||||||
|
|
||||||
|
fonepole(phase_noise_, rand() * kRandFrac - 0.5f, 0.002f); |
||||||
|
|
||||||
|
float mix = 0.0f; |
||||||
|
|
||||||
|
if(sustain_) |
||||||
|
{ |
||||||
|
f0_ = new_f0_; |
||||||
|
phase_ += f0_; |
||||||
|
if(phase_ >= 1.0f) |
||||||
|
{ |
||||||
|
phase_ -= 1.0f; |
||||||
|
} |
||||||
|
float body = DistortedSine(phase_, phase_noise_, dirtiness); |
||||||
|
mix -= TransistorVCA(body, sustain_gain_); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if(fm_pulse_width_) |
||||||
|
{ |
||||||
|
--fm_pulse_width_; |
||||||
|
phase_ = 0.25f; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
fm_ *= fm_decay; |
||||||
|
float fm = 1.0f + fm_envelope_amount_ * 3.5f * fm_lp_; |
||||||
|
f0_ = new_f0_; |
||||||
|
phase_ += fmin(f0_ * fm, 0.5f); |
||||||
|
if(phase_ >= 1.0f) |
||||||
|
{ |
||||||
|
phase_ -= 1.0f; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if(body_env_pulse_width_) |
||||||
|
{ |
||||||
|
--body_env_pulse_width_; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
body_env_ *= body_env_decay; |
||||||
|
transient_env_ *= transient_env_decay; |
||||||
|
} |
||||||
|
|
||||||
|
const float envelope_lp_f = 0.1f; |
||||||
|
fonepole(body_env_lp_, body_env_, envelope_lp_f); |
||||||
|
fonepole(transient_env_lp_, transient_env_, envelope_lp_f); |
||||||
|
fonepole(fm_lp_, fm_, envelope_lp_f); |
||||||
|
|
||||||
|
float body = DistortedSine(phase_, phase_noise_, dirtiness); |
||||||
|
float transient = click_.Process(body_env_pulse_width_ ? 0.0f : 1.0f) |
||||||
|
+ noise_.Process(); |
||||||
|
|
||||||
|
mix -= TransistorVCA(body, body_env_lp_); |
||||||
|
mix -= transient * transient_env_lp_ * transient_level; |
||||||
|
} |
||||||
|
|
||||||
|
fonepole(tone_lp_, mix, tone_f); |
||||||
|
return tone_lp_; |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticBassDrum::Trig() |
||||||
|
{ |
||||||
|
trig_ = true; |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticBassDrum::SetSustain(bool sustain) |
||||||
|
{ |
||||||
|
sustain_ = sustain; |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticBassDrum::SetAccent(float accent) |
||||||
|
{ |
||||||
|
accent_ = fclamp(accent, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticBassDrum::SetFreq(float freq) |
||||||
|
{ |
||||||
|
freq /= sample_rate_; |
||||||
|
new_f0_ = fclamp(freq, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticBassDrum::SetTone(float tone) |
||||||
|
{ |
||||||
|
tone_ = fclamp(tone, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticBassDrum::SetDecay(float decay) |
||||||
|
{ |
||||||
|
decay = fclamp(decay, 0.f, 1.f); |
||||||
|
decay_ = decay * decay; |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticBassDrum::SetDirtiness(float dirtiness) |
||||||
|
{ |
||||||
|
dirtiness_ = fclamp(dirtiness, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticBassDrum::SetFmEnvelopeAmount(float fm_envelope_amount) |
||||||
|
{ |
||||||
|
fm_envelope_amount_ = fclamp(fm_envelope_amount, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticBassDrum::SetFmEnvelopeDecay(float fm_envelope_decay) |
||||||
|
{ |
||||||
|
fm_envelope_decay = fclamp(fm_envelope_decay, 0.f, 1.f); |
||||||
|
fm_envelope_decay_ = fm_envelope_decay * fm_envelope_decay; |
||||||
|
} |
@ -0,0 +1,179 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_SYNTHBD_H |
||||||
|
#define DSY_SYNTHBD_H |
||||||
|
|
||||||
|
#include "Filters/svf.h" |
||||||
|
#include "Utility/dsp.h" |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file synthbassdrum.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Click noise for SyntheticBassDrum |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/drums/synthetic_bass_drum.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class SyntheticBassDrumClick |
||||||
|
{ |
||||||
|
public: |
||||||
|
SyntheticBassDrumClick() {} |
||||||
|
~SyntheticBassDrumClick() {} |
||||||
|
|
||||||
|
/** Init the module
|
||||||
|
\param sample_rate Audio engine sample rate. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the next sample.
|
||||||
|
\param in Trigger the click. |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
private: |
||||||
|
float lp_; |
||||||
|
float hp_; |
||||||
|
Svf filter_; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Attack Noise generator for SyntheticBassDrum.
|
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/drums/synthetic_bass_drum.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class SyntheticBassDrumAttackNoise |
||||||
|
{ |
||||||
|
public: |
||||||
|
SyntheticBassDrumAttackNoise() {} |
||||||
|
~SyntheticBassDrumAttackNoise() {} |
||||||
|
|
||||||
|
/** Init the module */ |
||||||
|
void Init(); |
||||||
|
|
||||||
|
/** Get the next sample. */ |
||||||
|
float Process(); |
||||||
|
|
||||||
|
private: |
||||||
|
float lp_; |
||||||
|
float hp_; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Naive bass drum model (modulated oscillator with FM + envelope). |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
Inadvertently 909-ish. \n \n
|
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/drums/synthetic_bass_drum.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class SyntheticBassDrum |
||||||
|
{ |
||||||
|
public: |
||||||
|
SyntheticBassDrum() {} |
||||||
|
~SyntheticBassDrum() {} |
||||||
|
|
||||||
|
/** Init the module
|
||||||
|
\param sample_rate Audio engine sample rate. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Generates a distorted sine wave */ |
||||||
|
inline float DistortedSine(float phase, float phase_noise, float dirtiness); |
||||||
|
|
||||||
|
/** Transistor VCA simulation.
|
||||||
|
\param s Input sample. |
||||||
|
\param gain VCA gain. |
||||||
|
*/ |
||||||
|
inline float TransistorVCA(float s, float gain); |
||||||
|
|
||||||
|
/** Get the next sample.
|
||||||
|
\param trigger True triggers the BD. This is optional. |
||||||
|
*/ |
||||||
|
float Process(bool trigger = false); |
||||||
|
|
||||||
|
/** Trigger the drum */ |
||||||
|
void Trig(); |
||||||
|
|
||||||
|
/** Allows the drum to play continuously
|
||||||
|
\param sustain True sets the drum on infinite sustain. |
||||||
|
*/ |
||||||
|
void SetSustain(bool sustain); |
||||||
|
|
||||||
|
/** Sets the amount of accent.
|
||||||
|
\param accent Works 0-1. |
||||||
|
*/ |
||||||
|
void SetAccent(float accent); |
||||||
|
|
||||||
|
/** Set the bass drum's root frequency.
|
||||||
|
\param Frequency in Hz. |
||||||
|
*/ |
||||||
|
void SetFreq(float freq); |
||||||
|
|
||||||
|
/** Sets the overall bright / darkness of the drum.
|
||||||
|
\param tone Works 0-1. |
||||||
|
*/ |
||||||
|
void SetTone(float tone); |
||||||
|
|
||||||
|
/** Sets how long the drum's volume takes to decay.
|
||||||
|
\param Works 0-1. |
||||||
|
*/ |
||||||
|
void SetDecay(float decay); |
||||||
|
|
||||||
|
/** Makes things grimy
|
||||||
|
\param dirtiness Works 0-1. |
||||||
|
*/ |
||||||
|
void SetDirtiness(float dirtiness); |
||||||
|
|
||||||
|
/** Sets how much of a pitch sweep the drum experiences when triggered.
|
||||||
|
\param fm_envelope_amount Works 0-1. |
||||||
|
*/ |
||||||
|
void SetFmEnvelopeAmount(float fm_envelope_amount); |
||||||
|
|
||||||
|
/** Sets how long the initial pitch sweep takes.
|
||||||
|
\param fm_envelope_decay Works 0-1. |
||||||
|
*/ |
||||||
|
void SetFmEnvelopeDecay(float fm_envelope_decay); |
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_; |
||||||
|
|
||||||
|
bool trig_; |
||||||
|
bool sustain_; |
||||||
|
float accent_, new_f0_, tone_, decay_; |
||||||
|
float dirtiness_, fm_envelope_amount_, fm_envelope_decay_; |
||||||
|
|
||||||
|
float f0_; |
||||||
|
float phase_; |
||||||
|
float phase_noise_; |
||||||
|
|
||||||
|
float fm_; |
||||||
|
float fm_lp_; |
||||||
|
float body_env_; |
||||||
|
float body_env_lp_; |
||||||
|
float transient_env_; |
||||||
|
float transient_env_lp_; |
||||||
|
|
||||||
|
float sustain_gain_; |
||||||
|
|
||||||
|
float tone_lp_; |
||||||
|
|
||||||
|
SyntheticBassDrumClick click_; |
||||||
|
SyntheticBassDrumAttackNoise noise_; |
||||||
|
|
||||||
|
int body_env_pulse_width_; |
||||||
|
int fm_pulse_width_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,201 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "synthsnaredrum.h" |
||||||
|
#include <math.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void SyntheticSnareDrum::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
phase_[0] = 0.0f; |
||||||
|
phase_[1] = 0.0f; |
||||||
|
drum_amplitude_ = 0.0f; |
||||||
|
snare_amplitude_ = 0.0f; |
||||||
|
fm_ = 0.0f; |
||||||
|
hold_counter_ = 0; |
||||||
|
sustain_gain_ = 0.0f; |
||||||
|
|
||||||
|
SetSustain(false); |
||||||
|
SetAccent(.6f); |
||||||
|
SetFreq(200.f); |
||||||
|
SetFmAmount(.1f); |
||||||
|
SetDecay(.3f); |
||||||
|
SetSnappy(.7f); |
||||||
|
|
||||||
|
trig_ = false; |
||||||
|
|
||||||
|
drum_lp_.Init(sample_rate_); |
||||||
|
snare_hp_.Init(sample_rate_); |
||||||
|
snare_lp_.Init(sample_rate_); |
||||||
|
} |
||||||
|
|
||||||
|
inline float SyntheticSnareDrum::DistortedSine(float phase) |
||||||
|
{ |
||||||
|
float triangle = (phase < 0.5f ? phase : 1.0f - phase) * 4.0f - 1.3f; |
||||||
|
return 2.0f * triangle / (1.0f + fabsf(triangle)); |
||||||
|
} |
||||||
|
|
||||||
|
bool even = true; |
||||||
|
|
||||||
|
float SyntheticSnareDrum::Process(bool trigger) |
||||||
|
{ |
||||||
|
const float decay_xt = decay_ * (1.0f + decay_ * (decay_ - 1.0f)); |
||||||
|
const float drum_decay |
||||||
|
= 1.0f |
||||||
|
- 1.0f / (0.015f * sample_rate_) |
||||||
|
* powf(2.f, |
||||||
|
kOneTwelfth |
||||||
|
* (-decay_xt * 72.0f - fm_amount_ * 12.0f |
||||||
|
+ snappy_ * 7.0f)); |
||||||
|
|
||||||
|
const float snare_decay |
||||||
|
= 1.0f |
||||||
|
- 1.0f / (0.01f * sample_rate_) |
||||||
|
* powf(2.f, kOneTwelfth * (-decay_ * 60.0f - snappy_ * 7.0f)); |
||||||
|
const float fm_decay = 1.0f - 1.0f / (0.007f * sample_rate_); |
||||||
|
|
||||||
|
float snappy = snappy_ * 1.1f - 0.05f; |
||||||
|
snappy = fclamp(snappy, 0.0f, 1.0f); |
||||||
|
|
||||||
|
const float drum_level = sqrtf(1.0f - snappy); |
||||||
|
const float snare_level = sqrtf(snappy); |
||||||
|
|
||||||
|
const float snare_f_min = fmin(10.0f * f0_, 0.5f); |
||||||
|
const float snare_f_max = fmin(35.0f * f0_, 0.5f); |
||||||
|
|
||||||
|
snare_hp_.SetFreq(snare_f_min * sample_rate_); |
||||||
|
snare_lp_.SetFreq(snare_f_max * sample_rate_); |
||||||
|
snare_lp_.SetRes(0.5f + 2.0f * snappy); |
||||||
|
|
||||||
|
drum_lp_.SetFreq(3.0f * f0_ * sample_rate_); |
||||||
|
|
||||||
|
if(trigger || trig_) |
||||||
|
{ |
||||||
|
trig_ = false; |
||||||
|
snare_amplitude_ = drum_amplitude_ = 0.3f + 0.7f * accent_; |
||||||
|
fm_ = 1.0f; |
||||||
|
phase_[0] = phase_[1] = 0.0f; |
||||||
|
hold_counter_ |
||||||
|
= static_cast<int>((0.04f + decay_ * 0.03f) * sample_rate_); |
||||||
|
} |
||||||
|
|
||||||
|
even = !even; |
||||||
|
if(sustain_) |
||||||
|
{ |
||||||
|
sustain_gain_ = snare_amplitude_ = accent_ * decay_; |
||||||
|
drum_amplitude_ = snare_amplitude_; |
||||||
|
fm_ = 0.0f; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// Compute all D envelopes.
|
||||||
|
// The envelope for the drum has a very long tail.
|
||||||
|
// The envelope for the snare has a "hold" stage which lasts between
|
||||||
|
// 40 and 70 ms
|
||||||
|
drum_amplitude_ |
||||||
|
*= (drum_amplitude_ > 0.03f || even) ? drum_decay : 1.0f; |
||||||
|
if(hold_counter_) |
||||||
|
{ |
||||||
|
--hold_counter_; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
snare_amplitude_ *= snare_decay; |
||||||
|
} |
||||||
|
fm_ *= fm_decay; |
||||||
|
} |
||||||
|
|
||||||
|
// The 909 circuit has a funny kind of oscillator coupling - the signal
|
||||||
|
// leaving Q40's collector and resetting all oscillators allow some
|
||||||
|
// intermodulation.
|
||||||
|
float reset_noise = 0.0f; |
||||||
|
float reset_noise_amount = (0.125f - f0_) * 8.0f; |
||||||
|
reset_noise_amount = fclamp(reset_noise_amount, 0.0f, 1.0f); |
||||||
|
reset_noise_amount *= reset_noise_amount; |
||||||
|
reset_noise_amount *= fm_amount_; |
||||||
|
reset_noise += phase_[0] > 0.5f ? -1.0f : 1.0f; |
||||||
|
reset_noise += phase_[1] > 0.5f ? -1.0f : 1.0f; |
||||||
|
reset_noise *= reset_noise_amount * 0.025f; |
||||||
|
|
||||||
|
float f = f0_ * (1.0f + fm_amount_ * (4.0f * fm_)); |
||||||
|
phase_[0] += f; |
||||||
|
phase_[1] += f * 1.47f; |
||||||
|
if(reset_noise_amount > 0.1f) |
||||||
|
{ |
||||||
|
if(phase_[0] >= 1.0f + reset_noise) |
||||||
|
{ |
||||||
|
phase_[0] = 1.0f - phase_[0]; |
||||||
|
} |
||||||
|
if(phase_[1] >= 1.0f + reset_noise) |
||||||
|
{ |
||||||
|
phase_[1] = 1.0f - phase_[1]; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if(phase_[0] >= 1.0f) |
||||||
|
{ |
||||||
|
phase_[0] -= 1.0f; |
||||||
|
} |
||||||
|
if(phase_[1] >= 1.0f) |
||||||
|
{ |
||||||
|
phase_[1] -= 1.0f; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
float drum = -0.1f; |
||||||
|
drum += DistortedSine(phase_[0]) * 0.60f; |
||||||
|
drum += DistortedSine(phase_[1]) * 0.25f; |
||||||
|
drum *= drum_amplitude_ * drum_level; |
||||||
|
|
||||||
|
drum_lp_.Process(drum); |
||||||
|
drum = drum_lp_.Low(); |
||||||
|
|
||||||
|
float noise = rand() * kRandFrac; |
||||||
|
snare_lp_.Process(noise); |
||||||
|
float snare = snare_lp_.Low(); |
||||||
|
snare_hp_.Process(snare); |
||||||
|
snare = snare_hp_.High(); |
||||||
|
snare = (snare + 0.1f) * (snare_amplitude_ + fm_) * snare_level; |
||||||
|
|
||||||
|
return snare + drum; // It's a snare, it's a drum, it's a snare drum.
|
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticSnareDrum::Trig() |
||||||
|
{ |
||||||
|
trig_ = true; |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticSnareDrum::SetSustain(bool sustain) |
||||||
|
{ |
||||||
|
sustain_ = sustain; |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticSnareDrum::SetAccent(float accent) |
||||||
|
{ |
||||||
|
accent_ = fclamp(accent, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticSnareDrum::SetFreq(float f0) |
||||||
|
{ |
||||||
|
f0 /= sample_rate_; |
||||||
|
f0_ = fclamp(f0, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticSnareDrum::SetFmAmount(float fm_amount) |
||||||
|
{ |
||||||
|
fm_amount = fclamp(fm_amount, 0.f, 1.f); |
||||||
|
fm_amount_ = fm_amount * fm_amount; |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticSnareDrum::SetDecay(float decay) |
||||||
|
{ |
||||||
|
decay_ = fmax(decay, 0.f); |
||||||
|
} |
||||||
|
|
||||||
|
void SyntheticSnareDrum::SetSnappy(float snappy) |
||||||
|
{ |
||||||
|
snappy_ = fclamp(snappy, 0.f, 1.f); |
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_SYNTHSD_H |
||||||
|
#define DSY_SYNTHSD_H |
||||||
|
|
||||||
|
#include "Filters/svf.h" |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file synthsnaredrum.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Naive snare drum model (two modulated oscillators + filtered noise). |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
Uses a few magic numbers taken from the 909 schematics: \n
|
||||||
|
- Ratio between the two modes of the drum set to 1.47. \n |
||||||
|
- Funky coupling between the two modes. \n |
||||||
|
- Noise coloration filters and envelope shapes for the snare. \n \n |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/drums/synthetic_snare_drum.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class SyntheticSnareDrum |
||||||
|
{ |
||||||
|
public: |
||||||
|
SyntheticSnareDrum() {} |
||||||
|
~SyntheticSnareDrum() {} |
||||||
|
|
||||||
|
/** Init the module
|
||||||
|
\param sample_rate Audio engine sample rate |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the next sample.
|
||||||
|
\param trigger True = hit the drum. This argument is optional. |
||||||
|
*/ |
||||||
|
float Process(bool trigger = false); |
||||||
|
|
||||||
|
/** Trigger the drum */ |
||||||
|
void Trig(); |
||||||
|
|
||||||
|
/** Make the drum ring out infinitely.
|
||||||
|
\param sustain True = infinite sustain. |
||||||
|
*/ |
||||||
|
void SetSustain(bool sustain); |
||||||
|
|
||||||
|
/** Set how much accent to use
|
||||||
|
\param accent Works 0-1. |
||||||
|
*/ |
||||||
|
void SetAccent(float accent); |
||||||
|
|
||||||
|
/** Set the drum's root frequency
|
||||||
|
\param f0 Freq in Hz |
||||||
|
*/ |
||||||
|
void SetFreq(float f0); |
||||||
|
|
||||||
|
/** Set the amount of fm sweep.
|
||||||
|
\param fm_amount Works from 0 - 1. |
||||||
|
*/ |
||||||
|
void SetFmAmount(float fm_amount); |
||||||
|
|
||||||
|
/** Set the length of the drum decay
|
||||||
|
\param decay Works with positive numbers |
||||||
|
*/ |
||||||
|
void SetDecay(float decay); |
||||||
|
|
||||||
|
/** Sets the mix between snare and drum.
|
||||||
|
\param snappy 1 = just snare. 0 = just drum. |
||||||
|
*/ |
||||||
|
void SetSnappy(float snappy); |
||||||
|
|
||||||
|
private: |
||||||
|
inline float DistortedSine(float phase); |
||||||
|
|
||||||
|
float sample_rate_; |
||||||
|
|
||||||
|
bool trig_; |
||||||
|
bool sustain_; |
||||||
|
float accent_, f0_, fm_amount_, decay_, snappy_; |
||||||
|
|
||||||
|
float phase_[2]; |
||||||
|
float drum_amplitude_; |
||||||
|
float snare_amplitude_; |
||||||
|
float fm_; |
||||||
|
float sustain_gain_; |
||||||
|
int hold_counter_; |
||||||
|
|
||||||
|
Svf drum_lp_; |
||||||
|
Svf snare_hp_; |
||||||
|
Svf snare_lp_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,56 @@ |
|||||||
|
#include "balance.h" |
||||||
|
#include <math.h> |
||||||
|
#include "../utility/dsp.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Balance::Init(float sample_rate) |
||||||
|
{ |
||||||
|
float b; |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
ihp_ = 10.0f; |
||||||
|
b = 2.0f - cosf(ihp_ * (TWOPI_F / sample_rate_)); |
||||||
|
c2_ = b - sqrtf(b * b - 1.0f); |
||||||
|
c1_ = 1.0f - c2_; |
||||||
|
prvq_ = prvr_ = prva_ = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
float Balance::Process(float sig, float comp) |
||||||
|
{ |
||||||
|
float q, r, a, diff, out; |
||||||
|
float c1 = c1_; |
||||||
|
float c2 = c2_; |
||||||
|
|
||||||
|
q = prvq_; |
||||||
|
r = prvr_; |
||||||
|
float as = sig; |
||||||
|
float cs = comp; |
||||||
|
|
||||||
|
q = c1 * as * as + c2 * q; |
||||||
|
r = c1 * cs * cs + c2 * r; |
||||||
|
|
||||||
|
prvq_ = q; |
||||||
|
prvr_ = r; |
||||||
|
|
||||||
|
if(q != 0.0f) |
||||||
|
{ |
||||||
|
a = sqrtf(r / q); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
a = sqrtf(r); |
||||||
|
} |
||||||
|
|
||||||
|
if((diff = a - prva_) != 0.0f) |
||||||
|
{ |
||||||
|
out = sig * prva_; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
out = sig * a; |
||||||
|
} |
||||||
|
|
||||||
|
prva_ = a; |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
|
||||||
|
#pragma once |
||||||
|
#ifndef DSY_BALANCE_H |
||||||
|
#define DSY_BALANCE_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Balances two sound sources. Sig is boosted to the level of comp.
|
||||||
|
|
||||||
|
*Original author(s) : Barry Vercoe, john ffitch, Gabriel Maldonado |
||||||
|
|
||||||
|
*Year: 1991 |
||||||
|
|
||||||
|
*Ported from soundpipe by Ben Sergentanis, May 2020 |
||||||
|
*/ |
||||||
|
class Balance |
||||||
|
{ |
||||||
|
public: |
||||||
|
Balance() {} |
||||||
|
~Balance() {} |
||||||
|
/** Initializes the balance module.
|
||||||
|
\param sample_rate - The sample rate of the audio engine being run.
|
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
|
||||||
|
/** adjust sig level to level of comp
|
||||||
|
*/ |
||||||
|
float Process(float sig, float comp); |
||||||
|
|
||||||
|
|
||||||
|
/** adjusts the rate at which level compensation happens
|
||||||
|
\param cutoff : Sets half power point of special internal cutoff filter. |
||||||
|
|
||||||
|
defaults to 10 |
||||||
|
*/ |
||||||
|
inline void SetCutoff(float cutoff) { ihp_ = cutoff; } |
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_, ihp_, c2_, c1_, prvq_, prvr_, prva_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,76 @@ |
|||||||
|
// # compressor
|
||||||
|
//
|
||||||
|
// Author: shensley, AvAars
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <cmath> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include "compressor.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
#ifndef max |
||||||
|
#define max(a, b) ((a < b) ? b : a) |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef min |
||||||
|
#define min(a, b) ((a < b) ? a : b) |
||||||
|
#endif |
||||||
|
|
||||||
|
void Compressor::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = min(192000, max(1, sample_rate)); |
||||||
|
sample_rate_inv_ = 1.0f / (float)sample_rate_; |
||||||
|
sample_rate_inv2_ = 2.0f / (float)sample_rate_; |
||||||
|
|
||||||
|
// Initializing the params in this order to avoid dividing by zero
|
||||||
|
|
||||||
|
SetRatio(2.0f); |
||||||
|
SetAttack(0.1f); |
||||||
|
SetRelease(0.1f); |
||||||
|
SetThreshold(-12.0f); |
||||||
|
AutoMakeup(true); |
||||||
|
|
||||||
|
gain_rec_ = 0.1f; |
||||||
|
slope_rec_ = 0.1f; |
||||||
|
} |
||||||
|
|
||||||
|
float Compressor::Process(float in) |
||||||
|
{ |
||||||
|
float inAbs = fabsf(in); |
||||||
|
float cur_slo = ((slope_rec_ > inAbs) ? rel_slo_ : atk_slo_); |
||||||
|
slope_rec_ = ((slope_rec_ * cur_slo) + ((1.0f - cur_slo) * inAbs)); |
||||||
|
gain_rec_ = ((atk_slo2_ * gain_rec_) |
||||||
|
+ (ratio_mul_ |
||||||
|
* fmax(((20.f * fastlog10f(slope_rec_)) - thresh_), 0.f))); |
||||||
|
gain_ = pow10f(0.05f * (gain_rec_ + makeup_gain_)); |
||||||
|
|
||||||
|
return gain_ * in; |
||||||
|
} |
||||||
|
|
||||||
|
void Compressor::ProcessBlock(float *in, float *out, float *key, size_t size) |
||||||
|
{ |
||||||
|
for(size_t i = 0; i < size; i++) |
||||||
|
{ |
||||||
|
Process(key[i]); |
||||||
|
out[i] = Apply(in[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Multi-channel block processing
|
||||||
|
void Compressor::ProcessBlock(float **in, |
||||||
|
float **out, |
||||||
|
float * key, |
||||||
|
size_t channels, |
||||||
|
size_t size) |
||||||
|
{ |
||||||
|
for(size_t i = 0; i < size; i++) |
||||||
|
{ |
||||||
|
Process(key[i]); |
||||||
|
for(size_t c = 0; c < channels; c++) |
||||||
|
{ |
||||||
|
out[c][i] = Apply(in[c][i]); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,199 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_COMPRESSOR_H |
||||||
|
#define DSY_COMPRESSOR_H |
||||||
|
|
||||||
|
#include "Utility/dsp.h" |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** dynamics compressor
|
||||||
|
|
||||||
|
influenced by compressor in soundpipe (from faust). |
||||||
|
|
||||||
|
Modifications made to do: |
||||||
|
- Less calculations during each process loop (coefficients recalculated on parameter change). |
||||||
|
- C++-ified |
||||||
|
- added sidechain support |
||||||
|
- pulled gain apart for monitoring and multichannel support |
||||||
|
- improved readability |
||||||
|
- improved makeup-gain calculations |
||||||
|
- changing controls now costs a lot less |
||||||
|
- a lot less expensive |
||||||
|
|
||||||
|
by: shensley, improved upon by AvAars |
||||||
|
\todo Add soft/hard knee settings |
||||||
|
*/ |
||||||
|
class Compressor |
||||||
|
{ |
||||||
|
public: |
||||||
|
Compressor() {} |
||||||
|
~Compressor() {} |
||||||
|
/** Initializes compressor
|
||||||
|
\param sample_rate rate at which samples will be produced by the audio engine. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Compress the audio input signal, saves the calculated gain
|
||||||
|
\param in audio input signal |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
/** Compresses the audio input signal, keyed by a secondary input.
|
||||||
|
\param in audio input signal (to be compressed) |
||||||
|
\param key audio input that will be used to side-chain the compressor |
||||||
|
*/ |
||||||
|
float Process(float in, float key) |
||||||
|
{ |
||||||
|
Process(key); |
||||||
|
return Apply(in); |
||||||
|
} |
||||||
|
|
||||||
|
/** Apply compression to the audio signal, based on the previously calculated gain
|
||||||
|
\param in audio input signal |
||||||
|
*/ |
||||||
|
float Apply(float in) { return gain_ * in; } |
||||||
|
|
||||||
|
/** Compresses a block of audio
|
||||||
|
\param in audio input signal |
||||||
|
\param out audio output signal |
||||||
|
\param size the size of the block |
||||||
|
*/ |
||||||
|
void ProcessBlock(float *in, float *out, size_t size) |
||||||
|
{ |
||||||
|
ProcessBlock(in, out, in, size); |
||||||
|
} |
||||||
|
|
||||||
|
/** Compresses a block of audio, keyed by a secondary input
|
||||||
|
\param in audio input signal (to be compressed) |
||||||
|
\param out audio output signal |
||||||
|
\param key audio input that will be used to side-chain the compressor |
||||||
|
\param size the size of the block |
||||||
|
*/ |
||||||
|
void ProcessBlock(float *in, float *out, float *key, size_t size); |
||||||
|
|
||||||
|
/** Compresses a block of multiple channels of audio, keyed by a secondary input
|
||||||
|
\param in audio input signals (to be compressed) |
||||||
|
\param out audio output signals |
||||||
|
\param key audio input that will be used to side-chain the compressor |
||||||
|
\param channels the number of audio channels |
||||||
|
\param size the size of the block |
||||||
|
*/ |
||||||
|
void ProcessBlock(float **in, |
||||||
|
float **out, |
||||||
|
float * key, |
||||||
|
size_t channels, |
||||||
|
size_t size); |
||||||
|
|
||||||
|
/** Gets the amount of gain reduction */ |
||||||
|
float GetRatio() { return ratio_; } |
||||||
|
|
||||||
|
/** Sets the amount of gain reduction applied to compressed signals
|
||||||
|
\param ratio Expects 1.0 -> 40. (untested with values < 1.0) |
||||||
|
*/ |
||||||
|
void SetRatio(float ratio) |
||||||
|
{ |
||||||
|
ratio_ = ratio; |
||||||
|
RecalculateRatio(); |
||||||
|
} |
||||||
|
|
||||||
|
/** Gets the threshold in dB */ |
||||||
|
float GetThreshold() { return thresh_; } |
||||||
|
|
||||||
|
/** Sets the threshold in dB at which compression will be applied
|
||||||
|
\param threshold Expects 0.0 -> -80. |
||||||
|
*/ |
||||||
|
void SetThreshold(float threshold) |
||||||
|
{ |
||||||
|
thresh_ = threshold; |
||||||
|
RecalculateMakeup(); |
||||||
|
} |
||||||
|
|
||||||
|
/** Gets the envelope time for onset of compression */ |
||||||
|
float GetAttack() { return atk_; } |
||||||
|
|
||||||
|
/** Sets the envelope time for onset of compression for signals above the threshold.
|
||||||
|
\param attack Expects 0.001 -> 10 |
||||||
|
*/ |
||||||
|
void SetAttack(float attack) |
||||||
|
{ |
||||||
|
atk_ = attack; |
||||||
|
RecalculateAttack(); |
||||||
|
} |
||||||
|
|
||||||
|
/** Gets the envelope time for release of compression */ |
||||||
|
float GetRelease() { return rel_; } |
||||||
|
|
||||||
|
/** Sets the envelope time for release of compression as input signal falls below threshold.
|
||||||
|
\param release Expects 0.001 -> 10 |
||||||
|
*/ |
||||||
|
void SetRelease(float release) |
||||||
|
{ |
||||||
|
rel_ = release; |
||||||
|
RecalculateRelease(); |
||||||
|
} |
||||||
|
|
||||||
|
/** Gets the additional gain to make up for the compression */ |
||||||
|
float GetMakeup() { return makeup_gain_; } |
||||||
|
|
||||||
|
/** Manually sets the additional gain to make up for the compression
|
||||||
|
\param gain Expects 0.0 -> 80 |
||||||
|
*/ |
||||||
|
void SetMakeup(float gain) { makeup_gain_ = gain; } |
||||||
|
|
||||||
|
/** Enables or disables the automatic makeup gain. Disabling sets the makeup gain to 0.0
|
||||||
|
\param enable true to enable, false to disable |
||||||
|
*/ |
||||||
|
void AutoMakeup(bool enable) |
||||||
|
{ |
||||||
|
makeup_auto_ = enable; |
||||||
|
makeup_gain_ = 0.0f; |
||||||
|
RecalculateMakeup(); |
||||||
|
} |
||||||
|
|
||||||
|
/** Gets the gain reduction in dB
|
||||||
|
*/ |
||||||
|
float GetGain() { return fastlog10f(gain_) * 20.0f; } |
||||||
|
|
||||||
|
private: |
||||||
|
float ratio_, thresh_, atk_, rel_; |
||||||
|
float makeup_gain_; |
||||||
|
float gain_; |
||||||
|
|
||||||
|
// Recorded slope and gain, used in next sample
|
||||||
|
float slope_rec_, gain_rec_; |
||||||
|
|
||||||
|
// Internals from faust
|
||||||
|
float atk_slo2_, ratio_mul_, atk_slo_, rel_slo_; |
||||||
|
|
||||||
|
int sample_rate_; |
||||||
|
float sample_rate_inv2_, sample_rate_inv_; |
||||||
|
|
||||||
|
// Auto makeup gain enable
|
||||||
|
bool makeup_auto_; |
||||||
|
|
||||||
|
// Methods for recalculating internals
|
||||||
|
void RecalculateRatio() |
||||||
|
{ |
||||||
|
ratio_mul_ = ((1.0f - atk_slo2_) * ((1.0f / ratio_) - 1.0f)); |
||||||
|
} |
||||||
|
|
||||||
|
void RecalculateAttack() |
||||||
|
{ |
||||||
|
atk_slo_ = expf(-(sample_rate_inv_ / atk_)); |
||||||
|
atk_slo2_ = expf(-(sample_rate_inv2_ / atk_)); |
||||||
|
|
||||||
|
RecalculateRatio(); |
||||||
|
} |
||||||
|
|
||||||
|
void RecalculateRelease() { rel_slo_ = expf((-(sample_rate_inv_ / rel_))); } |
||||||
|
|
||||||
|
void RecalculateMakeup() |
||||||
|
{ |
||||||
|
if(makeup_auto_) |
||||||
|
makeup_gain_ = fabsf(thresh_ - thresh_ / ratio_) * 0.5f; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace daisysp
|
||||||
|
|
||||||
|
#endif // DSY_COMPRESSOR_H
|
@ -0,0 +1,37 @@ |
|||||||
|
#include <math.h> |
||||||
|
#include "crossfade.h" |
||||||
|
#include "../utility/dsp.h" |
||||||
|
|
||||||
|
#define REALLYSMALLFLOAT 0.000001f |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
const float kCrossLogMin = logf(REALLYSMALLFLOAT); |
||||||
|
const float kCrossLogMax = logf(1.0f); |
||||||
|
|
||||||
|
float CrossFade::Process(float &in1, float &in2) |
||||||
|
{ |
||||||
|
float scalar_1, scalar_2; |
||||||
|
switch(curve_) |
||||||
|
{ |
||||||
|
case CROSSFADE_LIN: |
||||||
|
scalar_1 = pos_; |
||||||
|
return (in1 * (1.0f - scalar_1)) + (in2 * scalar_1); |
||||||
|
|
||||||
|
case CROSSFADE_CPOW: |
||||||
|
scalar_1 = sinf(pos_ * HALFPI_F); |
||||||
|
scalar_2 = sinf((1.0f - pos_) * HALFPI_F); |
||||||
|
return (in1 * scalar_2) + (in2 * scalar_1); |
||||||
|
|
||||||
|
case CROSSFADE_LOG: |
||||||
|
scalar_1 |
||||||
|
= expf(pos_ * (kCrossLogMax - kCrossLogMin) + kCrossLogMin); |
||||||
|
return (in1 * (1.0f - scalar_1)) + (in2 * scalar_1); |
||||||
|
|
||||||
|
case CROSSFADE_EXP: |
||||||
|
scalar_1 = pos_ * pos_; |
||||||
|
return (in1 * (1.0f - scalar_1)) + (in2 * scalar_1); |
||||||
|
|
||||||
|
default: return 0; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_CROSSFADE_H |
||||||
|
#define DSY_CROSSFADE_H |
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Curve applied to the CrossFade
|
||||||
|
- LIN = linear |
||||||
|
- CPOW = constant power |
||||||
|
- LOG = logarithmic |
||||||
|
- EXP exponential |
||||||
|
- LAST = end of enum (used for array indexing) |
||||||
|
*/ |
||||||
|
enum |
||||||
|
{ |
||||||
|
CROSSFADE_LIN, |
||||||
|
CROSSFADE_CPOW, |
||||||
|
CROSSFADE_LOG, |
||||||
|
CROSSFADE_EXP, |
||||||
|
CROSSFADE_LAST, |
||||||
|
}; |
||||||
|
|
||||||
|
/** Performs a CrossFade between two signals
|
||||||
|
|
||||||
|
Original author: Paul Batchelor |
||||||
|
|
||||||
|
Ported from Soundpipe by Andrew Ikenberry |
||||||
|
|
||||||
|
added curve option for constant power, etc. |
||||||
|
*/ |
||||||
|
class CrossFade |
||||||
|
{ |
||||||
|
public: |
||||||
|
CrossFade() {} |
||||||
|
~CrossFade() {} |
||||||
|
/** Initializes CrossFade module
|
||||||
|
Defaults |
||||||
|
- current position = .5 |
||||||
|
- curve = linear |
||||||
|
*/ |
||||||
|
inline void Init(int curve) |
||||||
|
{ |
||||||
|
pos_ = 0.5f; |
||||||
|
curve_ = curve < CROSSFADE_LAST ? curve : CROSSFADE_LIN; |
||||||
|
} |
||||||
|
|
||||||
|
/** Initialize with default linear curve
|
||||||
|
*/ |
||||||
|
inline void Init() { Init(CROSSFADE_LIN); } |
||||||
|
/** processes CrossFade and returns single sample
|
||||||
|
*/ |
||||||
|
float Process(float &in1, float &in2); |
||||||
|
|
||||||
|
|
||||||
|
/** Sets position of CrossFade between two input signals
|
||||||
|
Input range: 0 to 1 |
||||||
|
*/ |
||||||
|
inline void SetPos(float pos) { pos_ = pos; } |
||||||
|
/** Sets current curve applied to CrossFade
|
||||||
|
Expected input: See [Curve Options](##curve-options) |
||||||
|
*/ |
||||||
|
inline void SetCurve(uint8_t curve) { curve_ = curve; } |
||||||
|
/** Returns current position
|
||||||
|
*/ |
||||||
|
inline float GetPos(float pos) { return pos_; } |
||||||
|
/** Returns current curve
|
||||||
|
*/ |
||||||
|
inline uint8_t GetCurve(uint8_t curve) { return curve_; } |
||||||
|
|
||||||
|
private: |
||||||
|
float pos_; |
||||||
|
uint8_t curve_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,29 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "limiter.h" |
||||||
|
|
||||||
|
#define SLOPE(out, in, positive, negative) \ |
||||||
|
{ \
|
||||||
|
float error = (in)-out; \
|
||||||
|
out += (error > 0 ? positive : negative) * error; \
|
||||||
|
} |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
void Limiter::Init() |
||||||
|
{ |
||||||
|
peak_ = 0.5f; |
||||||
|
} |
||||||
|
|
||||||
|
void Limiter::ProcessBlock(float *in, size_t size, float pre_gain) |
||||||
|
{ |
||||||
|
while(size--) |
||||||
|
{ |
||||||
|
float pre = *in * pre_gain; |
||||||
|
float peak = fabsf(pre); |
||||||
|
SLOPE(peak_, peak, 0.05f, 0.00002f); |
||||||
|
float gain = (peak_ <= 1.0f ? 1.0f : 1.0f / peak_); |
||||||
|
*in++ = SoftLimit(pre * gain * 0.7f); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} //namespace daisysp
|
@ -0,0 +1,33 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef LIMITER_H |
||||||
|
#define LIMITER_H |
||||||
|
#include <stdlib.h> |
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Simple Peak Limiter
|
||||||
|
|
||||||
|
This was extracted from pichenettes/stmlib. |
||||||
|
|
||||||
|
Credit to pichenettes/Mutable Instruments |
||||||
|
*/ |
||||||
|
class Limiter |
||||||
|
{ |
||||||
|
public: |
||||||
|
Limiter() {} |
||||||
|
~Limiter() {} |
||||||
|
/** Initializes the Limiter instance.
|
||||||
|
*/ |
||||||
|
void Init(); |
||||||
|
|
||||||
|
/** Processes a block of audio through the limiter.
|
||||||
|
\param in - pointer to a block of audio samples to be processed. The buffer is operated on directly. |
||||||
|
\param size - size of the buffer "in" |
||||||
|
\param pre_gain - amount of pre_gain applied to the signal. |
||||||
|
*/ |
||||||
|
void ProcessBlock(float *in, size_t size, float pre_gain); |
||||||
|
|
||||||
|
private: |
||||||
|
float peak_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
@ -0,0 +1,65 @@ |
|||||||
|
#include "autowah.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
|
||||||
|
void Autowah::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sampling_freq_ = sample_rate; |
||||||
|
const1_ = 1413.72f / sampling_freq_; |
||||||
|
const2_ = expf(0.0f - (100.0f / sampling_freq_)); |
||||||
|
const4_ = expf(0.0f - (10.0f / sampling_freq_)); |
||||||
|
|
||||||
|
wet_dry_ = 100.0f; |
||||||
|
level_ = 0.1f; |
||||||
|
wah_ = 0.0; |
||||||
|
|
||||||
|
|
||||||
|
for(int i = 0; i < 2; i++) |
||||||
|
{ |
||||||
|
rec1_[i] = rec2_[i] = rec3_[i] = rec4_[i] = rec5_[i] = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
for(int i = 0; i < 3; i++) |
||||||
|
{ |
||||||
|
rec0_[i] = 0.0f; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
float Autowah::Process(float in) |
||||||
|
{ |
||||||
|
float out; |
||||||
|
float fSlow2 = (0.01f * (wet_dry_ * level_)); |
||||||
|
float fSlow3 = (1.0f - 0.01f * wet_dry_) + (1.f - wah_); |
||||||
|
|
||||||
|
float fTemp1 = fabs(in); |
||||||
|
rec3_[0] |
||||||
|
= fmaxf(fTemp1, (const4_ * rec3_[1]) + ((1.0f - const4_) * fTemp1)); |
||||||
|
rec2_[0] = (const2_ * rec2_[1]) + ((1.0f - const2_) * rec3_[0]); |
||||||
|
float fTemp2 = fminf(1.0f, rec2_[0]); |
||||||
|
float fTemp3 = powf(2.0f, (2.3f * fTemp2)); |
||||||
|
float fTemp4 |
||||||
|
= 1.0f |
||||||
|
- (const1_ * fTemp3 / powf(2.0f, (1.0f + 2.0f * (1.0f - fTemp2)))); |
||||||
|
rec1_[0] |
||||||
|
= ((0.999f * rec1_[1]) |
||||||
|
+ (0.001f |
||||||
|
* (0.0f - (2.0f * (fTemp4 * cosf((const1_ * 2 * fTemp3))))))); |
||||||
|
rec4_[0] = ((0.999f * rec4_[1]) + (0.001f * fTemp4 * fTemp4)); |
||||||
|
rec5_[0] = ((0.999f * rec5_[1]) + (0.0001f * powf(4.0f, fTemp2))); |
||||||
|
rec0_[0] = (0.0f |
||||||
|
- (((rec1_[0] * rec0_[1]) + (rec4_[0] * rec0_[2])) |
||||||
|
- (fSlow2 * (rec5_[0] * in)))); |
||||||
|
|
||||||
|
out = ((wah_ * (rec0_[0] - rec0_[1])) + (fSlow3 * in)); |
||||||
|
rec3_[1] = rec3_[0]; |
||||||
|
rec2_[1] = rec2_[0]; |
||||||
|
rec1_[1] = rec1_[0]; |
||||||
|
rec4_[1] = rec4_[0]; |
||||||
|
rec5_[1] = rec5_[0]; |
||||||
|
rec0_[2] = rec0_[1]; |
||||||
|
rec0_[1] = rec0_[0]; |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_AUTOWAH_H |
||||||
|
#define DSY_AUTOWAH_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Autowah module
|
||||||
|
|
||||||
|
Original author(s) : |
||||||
|
|
||||||
|
Ported from soundpipe by Ben Sergentanis, May 2020 |
||||||
|
*/ |
||||||
|
class Autowah |
||||||
|
{ |
||||||
|
public: |
||||||
|
Autowah() {} |
||||||
|
~Autowah() {} |
||||||
|
/** Initializes the Autowah module.
|
||||||
|
\param sample_rate - The sample rate of the audio engine being run.
|
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
|
||||||
|
/** Initializes the Autowah module.
|
||||||
|
\param in - input signal to be wah'd |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
|
||||||
|
/** sets wah
|
||||||
|
\param wah : set wah amount |
||||||
|
*/ |
||||||
|
inline void SetWah(float wah) { wah_ = wah; } |
||||||
|
/** sets mix amount
|
||||||
|
\param drywet : set effect dry/wet |
||||||
|
*/ |
||||||
|
inline void SetDryWet(float drywet) { wet_dry_ = drywet; } |
||||||
|
/** sets wah level
|
||||||
|
\param level : set wah level |
||||||
|
*/ |
||||||
|
inline void SetLevel(float level) { level_ = level; } |
||||||
|
|
||||||
|
private: |
||||||
|
float sampling_freq_, const1_, const2_, const4_, wah_, level_, wet_dry_, |
||||||
|
rec0_[3], rec1_[2], rec2_[2], rec3_[2], rec4_[2], rec5_[2]; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,34 @@ |
|||||||
|
#include "bitcrush.h" |
||||||
|
#include "fold.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
static Fold fold; |
||||||
|
|
||||||
|
void Bitcrush::Init(float sample_rate) |
||||||
|
{ |
||||||
|
bit_depth_ = 8; |
||||||
|
crush_rate_ = 10000; |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
fold.Init(); |
||||||
|
} |
||||||
|
|
||||||
|
float Bitcrush::Process(float in) |
||||||
|
{ |
||||||
|
float bits = pow(2, bit_depth_); |
||||||
|
float foldamt = sample_rate_ / crush_rate_; |
||||||
|
float out; |
||||||
|
|
||||||
|
out = in * 65536.0f; |
||||||
|
out += 32768; |
||||||
|
out *= (bits / 65536.0f); |
||||||
|
out = floor(out); |
||||||
|
out *= (65536.0f / bits) - 32768; |
||||||
|
|
||||||
|
fold.SetIncrement(foldamt); |
||||||
|
out = fold.Process(out); |
||||||
|
out /= 65536.0; |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_BITCRUSH_H |
||||||
|
#define DSY_BITCRUSH_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** bitcrush module
|
||||||
|
|
||||||
|
Original author(s) : Paul Batchelor,
|
||||||
|
|
||||||
|
Ported from soundpipe by Ben Sergentanis, May 2020 |
||||||
|
*/ |
||||||
|
class Bitcrush |
||||||
|
{ |
||||||
|
public: |
||||||
|
Bitcrush() {} |
||||||
|
~Bitcrush() {} |
||||||
|
/** Initializes the bitcrush module.
|
||||||
|
\param sample_rate - The sample rate of the audio engine being run.
|
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
|
||||||
|
/** bit crushes and downsamples the input
|
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
|
||||||
|
/** adjusts bitdepth
|
||||||
|
\param bitdepth : Sets bit depth.
|
||||||
|
*/ |
||||||
|
inline void SetBitDepth(int bitdepth) { bit_depth_ = bitdepth; } |
||||||
|
/** adjusts the downsampling frequency
|
||||||
|
\param crushrate : Sets rate to downsample to.
|
||||||
|
*/ |
||||||
|
inline void SetCrushRate(float crushrate) { crush_rate_ = crushrate; } |
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_, crush_rate_; |
||||||
|
int bit_depth_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,187 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "chorus.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
//ChorusEngine stuff
|
||||||
|
void ChorusEngine::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
del_.Init(); |
||||||
|
lfo_amp_ = 0.f; |
||||||
|
feedback_ = .2f; |
||||||
|
SetDelay(.75); |
||||||
|
|
||||||
|
lfo_phase_ = 0.f; |
||||||
|
SetLfoFreq(.3f); |
||||||
|
SetLfoDepth(.9f); |
||||||
|
} |
||||||
|
|
||||||
|
float ChorusEngine::Process(float in) |
||||||
|
{ |
||||||
|
float lfo_sig = ProcessLfo(); |
||||||
|
del_.SetDelay(lfo_sig + delay_); |
||||||
|
|
||||||
|
float out = del_.Read(); |
||||||
|
del_.Write(in + out * feedback_); |
||||||
|
|
||||||
|
return (in + out) * .5f; //equal mix
|
||||||
|
} |
||||||
|
|
||||||
|
void ChorusEngine::SetLfoDepth(float depth) |
||||||
|
{ |
||||||
|
depth = fclamp(depth, 0.f, .93f); |
||||||
|
lfo_amp_ = depth * delay_; |
||||||
|
} |
||||||
|
|
||||||
|
void ChorusEngine::SetLfoFreq(float freq) |
||||||
|
{ |
||||||
|
freq = 4.f * freq / sample_rate_; |
||||||
|
freq *= lfo_freq_ < 0.f ? -1.f : 1.f; //if we're headed down, keep going
|
||||||
|
lfo_freq_ = fclamp(freq, -.25f, .25f); //clip at +/- .125 * sr
|
||||||
|
} |
||||||
|
|
||||||
|
void ChorusEngine::SetDelay(float delay) |
||||||
|
{ |
||||||
|
delay = (.1f + delay * 7.9f); //.1 to 8 ms
|
||||||
|
SetDelayMs(delay); |
||||||
|
} |
||||||
|
|
||||||
|
void ChorusEngine::SetDelayMs(float ms) |
||||||
|
{ |
||||||
|
ms = fmax(.1f, ms); |
||||||
|
delay_ = ms * .001f * sample_rate_; //ms to samples
|
||||||
|
|
||||||
|
lfo_amp_ = fmin(lfo_amp_, delay_); //clip this if needed
|
||||||
|
} |
||||||
|
|
||||||
|
void ChorusEngine::SetFeedback(float feedback) |
||||||
|
{ |
||||||
|
feedback_ = fclamp(feedback, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
float ChorusEngine::ProcessLfo() |
||||||
|
{ |
||||||
|
lfo_phase_ += lfo_freq_; |
||||||
|
|
||||||
|
//wrap around and flip direction
|
||||||
|
if(lfo_phase_ > 1.f) |
||||||
|
{ |
||||||
|
lfo_phase_ = 1.f - (lfo_phase_ - 1.f); |
||||||
|
lfo_freq_ *= -1.f; |
||||||
|
} |
||||||
|
else if(lfo_phase_ < -1.f) |
||||||
|
{ |
||||||
|
lfo_phase_ = -1.f - (lfo_phase_ + 1.f); |
||||||
|
lfo_freq_ *= -1.f; |
||||||
|
} |
||||||
|
|
||||||
|
return lfo_phase_ * lfo_amp_; |
||||||
|
} |
||||||
|
|
||||||
|
//Chorus Stuff
|
||||||
|
void Chorus::Init(float sample_rate) |
||||||
|
{ |
||||||
|
engines_[0].Init(sample_rate); |
||||||
|
engines_[1].Init(sample_rate); |
||||||
|
SetPan(.25f, .75f); |
||||||
|
|
||||||
|
gain_frac_ = .5f; |
||||||
|
sigl_ = sigr_ = 0.f; |
||||||
|
} |
||||||
|
|
||||||
|
float Chorus::Process(float in) |
||||||
|
{ |
||||||
|
sigl_ = 0.f; |
||||||
|
sigr_ = 0.f; |
||||||
|
|
||||||
|
for(int i = 0; i < 2; i++) |
||||||
|
{ |
||||||
|
float sig = engines_[i].Process(in); |
||||||
|
sigl_ += (1.f - pan_[i]) * sig; |
||||||
|
sigr_ += pan_[i] * sig; |
||||||
|
} |
||||||
|
|
||||||
|
sigl_ *= gain_frac_; |
||||||
|
sigr_ *= gain_frac_; |
||||||
|
|
||||||
|
return sigl_; |
||||||
|
} |
||||||
|
|
||||||
|
float Chorus::GetLeft() |
||||||
|
{ |
||||||
|
return sigl_; |
||||||
|
} |
||||||
|
|
||||||
|
float Chorus::GetRight() |
||||||
|
{ |
||||||
|
return sigr_; |
||||||
|
} |
||||||
|
|
||||||
|
void Chorus::SetPan(float panl, float panr) |
||||||
|
{ |
||||||
|
pan_[0] = fclamp(panl, 0.f, 1.f); |
||||||
|
pan_[1] = fclamp(panr, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void Chorus::SetPan(float pan) |
||||||
|
{ |
||||||
|
SetPan(pan, pan); |
||||||
|
} |
||||||
|
|
||||||
|
void Chorus::SetLfoDepth(float depthl, float depthr) |
||||||
|
{ |
||||||
|
engines_[0].SetLfoDepth(depthl); |
||||||
|
engines_[1].SetLfoDepth(depthr); |
||||||
|
} |
||||||
|
|
||||||
|
void Chorus::SetLfoDepth(float depth) |
||||||
|
{ |
||||||
|
SetLfoDepth(depth, depth); |
||||||
|
} |
||||||
|
|
||||||
|
void Chorus::SetLfoFreq(float freql, float freqr) |
||||||
|
{ |
||||||
|
engines_[0].SetLfoFreq(freql); |
||||||
|
engines_[1].SetLfoFreq(freqr); |
||||||
|
} |
||||||
|
|
||||||
|
void Chorus::SetLfoFreq(float freq) |
||||||
|
{ |
||||||
|
SetLfoFreq(freq, freq); |
||||||
|
} |
||||||
|
|
||||||
|
void Chorus::SetDelay(float delayl, float delayr) |
||||||
|
{ |
||||||
|
engines_[0].SetDelay(delayl); |
||||||
|
engines_[1].SetDelay(delayr); |
||||||
|
} |
||||||
|
|
||||||
|
void Chorus::SetDelay(float delay) |
||||||
|
{ |
||||||
|
SetDelay(delay, delay); |
||||||
|
} |
||||||
|
|
||||||
|
void Chorus::SetDelayMs(float msl, float msr) |
||||||
|
{ |
||||||
|
engines_[0].SetDelayMs(msl); |
||||||
|
engines_[1].SetDelayMs(msr); |
||||||
|
} |
||||||
|
|
||||||
|
void Chorus::SetDelayMs(float ms) |
||||||
|
{ |
||||||
|
SetDelayMs(ms, ms); |
||||||
|
} |
||||||
|
|
||||||
|
void Chorus::SetFeedback(float feedbackl, float feedbackr) |
||||||
|
{ |
||||||
|
engines_[0].SetFeedback(feedbackl); |
||||||
|
engines_[1].SetFeedback(feedbackr); |
||||||
|
} |
||||||
|
|
||||||
|
void Chorus::SetFeedback(float feedback) |
||||||
|
{ |
||||||
|
SetFeedback(feedback, feedback); |
||||||
|
} |
@ -0,0 +1,182 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_CHORUS_H |
||||||
|
#define DSY_CHORUS_H |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include "Utility/delayline.h" |
||||||
|
|
||||||
|
/** @file chorus.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Single Chorus engine. Used in Chorus. |
||||||
|
@author Ben Sergentanis |
||||||
|
*/ |
||||||
|
class ChorusEngine |
||||||
|
{ |
||||||
|
public: |
||||||
|
ChorusEngine() {} |
||||||
|
~ChorusEngine() {} |
||||||
|
|
||||||
|
/** Initialize the module
|
||||||
|
\param sample_rate Audio engine sample rate. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the next sample
|
||||||
|
\param in Sample to process |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
/** How much to modulate the delay by.
|
||||||
|
\param depth Works 0-1. |
||||||
|
*/ |
||||||
|
void SetLfoDepth(float depth); |
||||||
|
|
||||||
|
/** Set lfo frequency.
|
||||||
|
\param freq Frequency in Hz |
||||||
|
*/ |
||||||
|
void SetLfoFreq(float freq); |
||||||
|
|
||||||
|
/** Set the internal delay rate.
|
||||||
|
\param delay Tuned for 0-1. Maps to .1 to 50 ms. |
||||||
|
*/ |
||||||
|
void SetDelay(float delay); |
||||||
|
|
||||||
|
/** Set the delay time in ms.
|
||||||
|
\param ms Delay time in ms. |
||||||
|
*/ |
||||||
|
void SetDelayMs(float ms); |
||||||
|
|
||||||
|
/** Set the feedback amount.
|
||||||
|
\param feedback Amount from 0-1. |
||||||
|
*/ |
||||||
|
void SetFeedback(float feedback); |
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_; |
||||||
|
static constexpr int32_t kDelayLength |
||||||
|
= 2400; // 50 ms at 48kHz = .05 * 48000
|
||||||
|
|
||||||
|
//triangle lfos
|
||||||
|
float lfo_phase_; |
||||||
|
float lfo_freq_; |
||||||
|
float lfo_amp_; |
||||||
|
|
||||||
|
float feedback_; |
||||||
|
|
||||||
|
float delay_; |
||||||
|
|
||||||
|
DelayLine<float, kDelayLength> del_; |
||||||
|
|
||||||
|
float ProcessLfo(); |
||||||
|
}; |
||||||
|
|
||||||
|
//wraps up all of the chorus engines
|
||||||
|
/**
|
||||||
|
@brief Chorus Effect. |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
Based on https://www.izotope.com/en/learn/understanding-chorus-flangers-and-phasers-in-audio-production.html \n
|
||||||
|
and https://www.researchgate.net/publication/236629475_Implementing_Professional_Audio_Effects_with_DSPs \n
|
||||||
|
*/ |
||||||
|
class Chorus |
||||||
|
{ |
||||||
|
public: |
||||||
|
Chorus() {} |
||||||
|
~Chorus() {} |
||||||
|
|
||||||
|
/** Initialize the module
|
||||||
|
\param sample_rate Audio engine sample rate |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the net floating point sample. Defaults to left channel.
|
||||||
|
\param in Sample to process |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
/** Get the left channel's last sample */ |
||||||
|
float GetLeft(); |
||||||
|
|
||||||
|
/** Get the right channel's last sample */ |
||||||
|
float GetRight(); |
||||||
|
|
||||||
|
/** Pan both channels individually.
|
||||||
|
\param panl Pan the left channel. 0 is left, 1 is right. |
||||||
|
\param panr Pan the right channel. |
||||||
|
*/ |
||||||
|
void SetPan(float panl, float panr); |
||||||
|
|
||||||
|
/** Pan both channels.
|
||||||
|
\param pan Where to pan both channels to. 0 is left, 1 is right. |
||||||
|
*/ |
||||||
|
void SetPan(float pan); |
||||||
|
|
||||||
|
/** Set both lfo depths individually.
|
||||||
|
\param depthl Left channel lfo depth. Works 0-1. |
||||||
|
\param depthr Right channel lfo depth. |
||||||
|
*/ |
||||||
|
void SetLfoDepth(float depthl, float depthr); |
||||||
|
|
||||||
|
/** Set both lfo depths.
|
||||||
|
\param depth Both channels lfo depth. Works 0-1. |
||||||
|
*/ |
||||||
|
void SetLfoDepth(float depth); |
||||||
|
|
||||||
|
/** Set both lfo frequencies individually.
|
||||||
|
\param depthl Left channel lfo freq in Hz. |
||||||
|
\param depthr Right channel lfo freq in Hz. |
||||||
|
*/ |
||||||
|
void SetLfoFreq(float freql, float freqr); |
||||||
|
|
||||||
|
/** Set both lfo frequencies.
|
||||||
|
\param depth Both channel lfo freqs in Hz. |
||||||
|
*/ |
||||||
|
void SetLfoFreq(float freq); |
||||||
|
|
||||||
|
/** Set both channel delay amounts individually.
|
||||||
|
\param delayl Left channel delay amount. Works 0-1. |
||||||
|
\param delayr Right channel delay amount. |
||||||
|
*/ |
||||||
|
void SetDelay(float delayl, float delayr); |
||||||
|
|
||||||
|
/** Set both channel delay amounts.
|
||||||
|
\param delay Both channel delay amount. Works 0-1. |
||||||
|
*/ |
||||||
|
void SetDelay(float delay); |
||||||
|
|
||||||
|
/** Set both channel delay individually.
|
||||||
|
\param msl Left channel delay in ms. |
||||||
|
\param msr Right channel delay in ms. |
||||||
|
*/ |
||||||
|
void SetDelayMs(float msl, float msr); |
||||||
|
|
||||||
|
/** Set both channel delay in ms.
|
||||||
|
\param ms Both channel delay amounts in ms. |
||||||
|
*/ |
||||||
|
void SetDelayMs(float ms); |
||||||
|
|
||||||
|
/** Set both channels feedback individually.
|
||||||
|
\param feedbackl Left channel feedback. Works 0-1. |
||||||
|
\param feedbackr Right channel feedback. |
||||||
|
*/ |
||||||
|
void SetFeedback(float feedbackl, float feedbackr); |
||||||
|
|
||||||
|
/** Set both channels feedback.
|
||||||
|
\param feedback Both channel feedback. Works 0-1. |
||||||
|
*/ |
||||||
|
void SetFeedback(float feedback); |
||||||
|
|
||||||
|
private: |
||||||
|
ChorusEngine engines_[2]; |
||||||
|
float gain_frac_; |
||||||
|
float pan_[2]; |
||||||
|
|
||||||
|
float sigl_, sigr_; |
||||||
|
}; |
||||||
|
} //namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,32 @@ |
|||||||
|
#include "decimator.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Decimator::Init() |
||||||
|
{ |
||||||
|
downsample_factor_ = 1.0f; |
||||||
|
bitcrush_factor_ = 0.0f; |
||||||
|
downsampled_ = 0.0f; |
||||||
|
bitcrushed_ = 0.0f; |
||||||
|
inc_ = 0; |
||||||
|
threshold_ = 0; |
||||||
|
} |
||||||
|
|
||||||
|
float Decimator::Process(float input) |
||||||
|
{ |
||||||
|
int32_t temp; |
||||||
|
//downsample
|
||||||
|
threshold_ = (uint32_t)((downsample_factor_ * downsample_factor_) * 96.0f); |
||||||
|
inc_ += 1; |
||||||
|
if(inc_ > threshold_) |
||||||
|
{ |
||||||
|
inc_ = 0; |
||||||
|
downsampled_ = input; |
||||||
|
} |
||||||
|
//bitcrush
|
||||||
|
temp = (int32_t)(downsampled_ * 65536.0f); |
||||||
|
temp >>= bits_to_crush_; // shift off
|
||||||
|
temp <<= bits_to_crush_; // move back with zeros
|
||||||
|
bitcrushed_ = (float)temp / 65536.0f; |
||||||
|
return bitcrushed_; |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
|
||||||
|
#pragma once |
||||||
|
#ifndef DECIMATOR_H |
||||||
|
#define DECIMATOR_H |
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Performs downsampling and bitcrush effects
|
||||||
|
*/ |
||||||
|
class Decimator |
||||||
|
{ |
||||||
|
public: |
||||||
|
Decimator() {} |
||||||
|
~Decimator() {} |
||||||
|
/** Initializes downsample module
|
||||||
|
*/ |
||||||
|
void Init(); |
||||||
|
|
||||||
|
/** Applies downsample and bitcrush effects to input signal.
|
||||||
|
\return one sample. This should be called once per sample period.
|
||||||
|
*/ |
||||||
|
float Process(float input); |
||||||
|
|
||||||
|
|
||||||
|
/** Sets amount of downsample
|
||||||
|
Input range:
|
||||||
|
*/ |
||||||
|
inline void SetDownsampleFactor(float downsample_factor) |
||||||
|
{ |
||||||
|
downsample_factor_ = downsample_factor; |
||||||
|
} |
||||||
|
|
||||||
|
/** Sets amount of bitcrushing
|
||||||
|
Input range:
|
||||||
|
*/ |
||||||
|
inline void SetBitcrushFactor(float bitcrush_factor) |
||||||
|
{ |
||||||
|
// bitcrush_factor_ = bitcrush_factor;
|
||||||
|
bits_to_crush_ = (uint32_t)(bitcrush_factor * kMaxBitsToCrush); |
||||||
|
} |
||||||
|
|
||||||
|
/** Sets the exact number of bits to crush
|
||||||
|
0-16 bits |
||||||
|
*/ |
||||||
|
inline void SetBitsToCrush(const uint8_t &bits) |
||||||
|
{ |
||||||
|
bits_to_crush_ = bits <= kMaxBitsToCrush ? bits : kMaxBitsToCrush; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Returns current setting of downsample
|
||||||
|
*/ |
||||||
|
inline float GetDownsampleFactor() { return downsample_factor_; } |
||||||
|
/** Returns current setting of bitcrush
|
||||||
|
*/ |
||||||
|
inline float GetBitcrushFactor() { return bitcrush_factor_; } |
||||||
|
|
||||||
|
private: |
||||||
|
const uint8_t kMaxBitsToCrush = 16; |
||||||
|
float downsample_factor_, bitcrush_factor_; |
||||||
|
uint32_t bits_to_crush_; |
||||||
|
float downsampled_, bitcrushed_; |
||||||
|
uint32_t inc_, threshold_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,83 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "flanger.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Flanger::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
SetFeedback(.2f); |
||||||
|
|
||||||
|
del_.Init(); |
||||||
|
lfo_amp_ = 0.f; |
||||||
|
SetDelay(.75); |
||||||
|
|
||||||
|
lfo_phase_ = 0.f; |
||||||
|
SetLfoFreq(.3); |
||||||
|
SetLfoDepth(.9); |
||||||
|
} |
||||||
|
|
||||||
|
float Flanger::Process(float in) |
||||||
|
{ |
||||||
|
float lfo_sig = ProcessLfo(); |
||||||
|
del_.SetDelay(1.f + lfo_sig + delay_); |
||||||
|
|
||||||
|
float out = del_.Read(); |
||||||
|
del_.Write(in + out * feedback_); |
||||||
|
|
||||||
|
return (in + out) * .5f; //equal mix
|
||||||
|
} |
||||||
|
|
||||||
|
void Flanger::SetFeedback(float feedback) |
||||||
|
{ |
||||||
|
feedback_ = fclamp(feedback, 0.f, 1.f); |
||||||
|
feedback_ *= .97f; |
||||||
|
} |
||||||
|
|
||||||
|
void Flanger::SetLfoDepth(float depth) |
||||||
|
{ |
||||||
|
depth = fclamp(depth, 0.f, .93f); |
||||||
|
lfo_amp_ = depth * delay_; |
||||||
|
} |
||||||
|
|
||||||
|
void Flanger::SetLfoFreq(float freq) |
||||||
|
{ |
||||||
|
freq = 4.f * freq / sample_rate_; |
||||||
|
freq *= lfo_freq_ < 0.f ? -1.f : 1.f; //if we're headed down, keep going
|
||||||
|
lfo_freq_ = fclamp(freq, -.25f, .25f); //clip at +/- .125 * sr
|
||||||
|
} |
||||||
|
|
||||||
|
void Flanger::SetDelay(float delay) |
||||||
|
{ |
||||||
|
delay = (.1f + delay * 6.9); //.1 to 7 ms
|
||||||
|
SetDelayMs(delay); |
||||||
|
} |
||||||
|
|
||||||
|
void Flanger::SetDelayMs(float ms) |
||||||
|
{ |
||||||
|
ms = fmax(.1, ms); |
||||||
|
delay_ = ms * .001f * sample_rate_; //ms to samples
|
||||||
|
|
||||||
|
lfo_amp_ = fmin(lfo_amp_, delay_); //clip this if needed
|
||||||
|
} |
||||||
|
|
||||||
|
float Flanger::ProcessLfo() |
||||||
|
{ |
||||||
|
lfo_phase_ += lfo_freq_; |
||||||
|
|
||||||
|
//wrap around and flip direction
|
||||||
|
if(lfo_phase_ > 1.f) |
||||||
|
{ |
||||||
|
lfo_phase_ = 1.f - (lfo_phase_ - 1.f); |
||||||
|
lfo_freq_ *= -1.f; |
||||||
|
} |
||||||
|
else if(lfo_phase_ < -1.f) |
||||||
|
{ |
||||||
|
lfo_phase_ = -1.f - (lfo_phase_ + 1.f); |
||||||
|
lfo_freq_ *= -1.f; |
||||||
|
} |
||||||
|
|
||||||
|
return lfo_phase_ * lfo_amp_; |
||||||
|
} |
@ -0,0 +1,75 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_FLANGER_H |
||||||
|
#define DSY_FLANGER_H |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include "Utility/delayline.h" |
||||||
|
|
||||||
|
/** @file flanger.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** @brief Flanging Audio Effect
|
||||||
|
* |
||||||
|
* Generates a modulating phase shifted copy of a signal, and recombines |
||||||
|
* with the original to create a 'flanging' sound effect. |
||||||
|
*/ |
||||||
|
class Flanger |
||||||
|
{ |
||||||
|
public: |
||||||
|
/** Initialize the modules
|
||||||
|
\param sample_rate Audio engine sample rate. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the next sample
|
||||||
|
\param in Sample to process |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
/** How much of the signal to feedback into the delay line.
|
||||||
|
\param feedback Works 0-1. |
||||||
|
*/ |
||||||
|
void SetFeedback(float feedback); |
||||||
|
|
||||||
|
/** How much to modulate the delay by.
|
||||||
|
\param depth Works 0-1. |
||||||
|
*/ |
||||||
|
void SetLfoDepth(float depth); |
||||||
|
|
||||||
|
/** Set lfo frequency.
|
||||||
|
\param freq Frequency in Hz |
||||||
|
*/ |
||||||
|
void SetLfoFreq(float freq); |
||||||
|
|
||||||
|
/** Set the internal delay rate.
|
||||||
|
\param delay Tuned for 0-1. Maps to .1 to 7 ms. |
||||||
|
*/ |
||||||
|
void SetDelay(float delay); |
||||||
|
|
||||||
|
/** Set the delay time in ms.
|
||||||
|
\param ms Delay time in ms. |
||||||
|
*/ |
||||||
|
void SetDelayMs(float ms); |
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_; |
||||||
|
static constexpr int32_t kDelayLength = 960; // 20 ms at 48kHz = .02 * 48000
|
||||||
|
|
||||||
|
float feedback_; |
||||||
|
|
||||||
|
//triangle lfos
|
||||||
|
float lfo_phase_; |
||||||
|
float lfo_freq_; |
||||||
|
float lfo_amp_; |
||||||
|
|
||||||
|
float delay_; |
||||||
|
|
||||||
|
DelayLine<float, kDelayLength> del_; |
||||||
|
|
||||||
|
float ProcessLfo(); |
||||||
|
}; |
||||||
|
} //namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,31 @@ |
|||||||
|
#include "fold.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
|
||||||
|
void Fold::Init() |
||||||
|
{ |
||||||
|
incr_ = 1000.f; |
||||||
|
sample_index_ = 0; |
||||||
|
index_ = 0.0f; |
||||||
|
value_ = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
float Fold::Process(float in) |
||||||
|
{ |
||||||
|
float out; |
||||||
|
|
||||||
|
if(index_ < sample_index_) |
||||||
|
{ |
||||||
|
index_ += incr_; |
||||||
|
out = value_ = in; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
out = value_; |
||||||
|
} |
||||||
|
|
||||||
|
sample_index_++; |
||||||
|
return out; |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_FOLD_H |
||||||
|
#define DSY_FOLD_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** fold module
|
||||||
|
|
||||||
|
Original author(s) : John FFitch, Gabriel Maldonado |
||||||
|
|
||||||
|
Year : 1998 |
||||||
|
|
||||||
|
Ported from soundpipe by Ben Sergentanis, May 2020 |
||||||
|
*/ |
||||||
|
class Fold |
||||||
|
{ |
||||||
|
public: |
||||||
|
Fold() {} |
||||||
|
~Fold() {} |
||||||
|
/** Initializes the fold module.
|
||||||
|
*/ |
||||||
|
void Init(); |
||||||
|
|
||||||
|
|
||||||
|
/** applies foldvoer distortion to input
|
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
\param incr : set fold increment |
||||||
|
*/ |
||||||
|
inline void SetIncrement(float incr) { incr_ = incr; } |
||||||
|
|
||||||
|
private: |
||||||
|
float incr_, index_, value_; |
||||||
|
int sample_index_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,32 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "overdrive.h" |
||||||
|
#include <algorithm> |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
void Overdrive::Init() |
||||||
|
{ |
||||||
|
SetDrive(.5f); |
||||||
|
} |
||||||
|
|
||||||
|
float Overdrive::Process(float in) |
||||||
|
{ |
||||||
|
float pre = pre_gain_ * in; |
||||||
|
return SoftClip(pre) * post_gain_; |
||||||
|
} |
||||||
|
|
||||||
|
void Overdrive::SetDrive(float drive) |
||||||
|
{ |
||||||
|
drive = fclamp(drive, 0.f, 1.f); |
||||||
|
drive_ = 2.f * drive; |
||||||
|
|
||||||
|
const float drive_2 = drive_ * drive_; |
||||||
|
const float pre_gain_a = drive_ * 0.5f; |
||||||
|
const float pre_gain_b = drive_2 * drive_2 * drive_ * 24.0f; |
||||||
|
pre_gain_ = pre_gain_a + (pre_gain_b - pre_gain_a) * drive_2; |
||||||
|
|
||||||
|
const float drive_squashed = drive_ * (2.0f - drive_); |
||||||
|
post_gain_ = 1.0f / SoftClip(0.33f + drive_squashed * (pre_gain_ - 0.33f)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace daisysp
|
@ -0,0 +1,46 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_OVERDRIVE_H |
||||||
|
#define DSY_OVERDRIVE_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file overdrive.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Distortion / Overdrive Module |
||||||
|
@author Ported by Ben Sergentanis
|
||||||
|
@date Jan 2021
|
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/fx/overdrive.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2014. \n |
||||||
|
*/ |
||||||
|
class Overdrive |
||||||
|
{ |
||||||
|
public: |
||||||
|
Overdrive() {} |
||||||
|
~Overdrive() {} |
||||||
|
|
||||||
|
/** Initializes the module with 0 gain */ |
||||||
|
void Init(); |
||||||
|
|
||||||
|
/** Get the next sample
|
||||||
|
\param in Input to be overdriven |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
/** Set the amount of drive
|
||||||
|
\param drive Works from 0-1 |
||||||
|
*/ |
||||||
|
void SetDrive(float drive); |
||||||
|
|
||||||
|
private: |
||||||
|
float drive_; |
||||||
|
float pre_gain_; |
||||||
|
float post_gain_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,138 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "phaser.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
//PhaserEngine stuff
|
||||||
|
void PhaserEngine::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
del_.Init(); |
||||||
|
lfo_amp_ = 0.f; |
||||||
|
feedback_ = .2f; |
||||||
|
SetFreq(200.f); |
||||||
|
|
||||||
|
del_.SetDelay(0.f); |
||||||
|
|
||||||
|
os_ = 30.f; //30 hertz frequency offset, lower than this introduces crunch
|
||||||
|
deltime_ = 0.f; |
||||||
|
|
||||||
|
last_sample_ = 0.f; |
||||||
|
lfo_phase_ = 0.f; |
||||||
|
SetLfoFreq(.3); |
||||||
|
SetLfoDepth(.9); |
||||||
|
} |
||||||
|
|
||||||
|
float PhaserEngine::Process(float in) |
||||||
|
{ |
||||||
|
float lfo_sig = ProcessLfo(); |
||||||
|
fonepole(deltime_, sample_rate_ / (lfo_sig + ap_freq_ + os_), .0001f); |
||||||
|
|
||||||
|
last_sample_ = del_.Allpass(in + feedback_ * last_sample_, deltime_, .3f); |
||||||
|
|
||||||
|
return (in + last_sample_) * .5f; //equal mix
|
||||||
|
} |
||||||
|
|
||||||
|
void PhaserEngine::SetLfoDepth(float depth) |
||||||
|
{ |
||||||
|
lfo_amp_ = fclamp(depth, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void PhaserEngine::SetLfoFreq(float lfo_freq) |
||||||
|
{ |
||||||
|
lfo_freq = 4.f * lfo_freq / sample_rate_; |
||||||
|
lfo_freq *= lfo_freq_ < 0.f ? -1.f : 1.f; //if we're headed down, keep going
|
||||||
|
lfo_freq_ = fclamp(lfo_freq, -.25f, .25f); //clip at +/- .125 * sr
|
||||||
|
} |
||||||
|
|
||||||
|
void PhaserEngine::SetFreq(float ap_freq) |
||||||
|
{ |
||||||
|
ap_freq_ = fclamp(ap_freq, 0.f, 20000.f); //0 - 20kHz
|
||||||
|
} |
||||||
|
|
||||||
|
void PhaserEngine::SetFeedback(float feedback) |
||||||
|
{ |
||||||
|
feedback_ = fclamp(feedback, 0.f, .75f); |
||||||
|
} |
||||||
|
|
||||||
|
float PhaserEngine::ProcessLfo() |
||||||
|
{ |
||||||
|
lfo_phase_ += lfo_freq_; |
||||||
|
|
||||||
|
//wrap around and flip direction
|
||||||
|
if(lfo_phase_ > 1.f) |
||||||
|
{ |
||||||
|
lfo_phase_ = 1.f - (lfo_phase_ - 1.f); |
||||||
|
lfo_freq_ *= -1.f; |
||||||
|
} |
||||||
|
else if(lfo_phase_ < -1.f) |
||||||
|
{ |
||||||
|
lfo_phase_ = -1.f - (lfo_phase_ + 1.f); |
||||||
|
lfo_freq_ *= -1.f; |
||||||
|
} |
||||||
|
|
||||||
|
return lfo_phase_ * lfo_amp_ * ap_freq_; |
||||||
|
} |
||||||
|
|
||||||
|
//Phaser Stuff
|
||||||
|
void Phaser::Init(float sample_rate) |
||||||
|
{ |
||||||
|
for(size_t i = 0; i < kMaxPoles; i++) |
||||||
|
{ |
||||||
|
engines_[i].Init(sample_rate); |
||||||
|
} |
||||||
|
|
||||||
|
poles_ = 4; |
||||||
|
gain_frac_ = .5f; |
||||||
|
} |
||||||
|
|
||||||
|
float Phaser::Process(float in) |
||||||
|
{ |
||||||
|
float sig = 0.f; |
||||||
|
|
||||||
|
for(int i = 0; i < poles_; i++) |
||||||
|
{ |
||||||
|
sig += engines_[i].Process(in); |
||||||
|
} |
||||||
|
|
||||||
|
return sig; |
||||||
|
} |
||||||
|
|
||||||
|
void Phaser::SetPoles(int poles) |
||||||
|
{ |
||||||
|
poles_ = DSY_CLAMP(poles, 1, 8); |
||||||
|
} |
||||||
|
|
||||||
|
void Phaser::SetLfoDepth(float depth) |
||||||
|
{ |
||||||
|
for(int i = 0; i < kMaxPoles; i++) |
||||||
|
{ |
||||||
|
engines_[i].SetLfoDepth(depth); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Phaser::SetLfoFreq(float lfo_freq) |
||||||
|
{ |
||||||
|
for(int i = 0; i < kMaxPoles; i++) |
||||||
|
{ |
||||||
|
engines_[i].SetLfoFreq(lfo_freq); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Phaser::SetFreq(float ap_freq) |
||||||
|
{ |
||||||
|
for(int i = 0; i < kMaxPoles; i++) |
||||||
|
{ |
||||||
|
engines_[i].SetFreq(ap_freq); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Phaser::SetFeedback(float feedback) |
||||||
|
{ |
||||||
|
for(int i = 0; i < kMaxPoles; i++) |
||||||
|
{ |
||||||
|
engines_[i].SetFeedback(feedback); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,131 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_PHASER_H |
||||||
|
#define DSY_PHASER_H |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include "Utility/delayline.h" |
||||||
|
|
||||||
|
/** @file phaser.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Single Phaser engine. Used in Phaser. |
||||||
|
@author Ben Sergentanis |
||||||
|
*/ |
||||||
|
class PhaserEngine |
||||||
|
{ |
||||||
|
public: |
||||||
|
PhaserEngine() {} |
||||||
|
~PhaserEngine() {} |
||||||
|
|
||||||
|
/** Initialize the module
|
||||||
|
\param sample_rate Audio engine sample rate. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the next sample
|
||||||
|
\param in Sample to process |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
/** How much to modulate the allpass filter by.
|
||||||
|
\param depth Works 0-1. |
||||||
|
*/ |
||||||
|
void SetLfoDepth(float depth); |
||||||
|
|
||||||
|
/** Set lfo frequency.
|
||||||
|
\param lfo_freq Frequency in Hz |
||||||
|
*/ |
||||||
|
void SetLfoFreq(float lfo_freq); |
||||||
|
|
||||||
|
/** Set the allpass frequency
|
||||||
|
\param ap_freq Frequency in Hz. |
||||||
|
*/ |
||||||
|
void SetFreq(float ap_freq); |
||||||
|
|
||||||
|
/** Set the feedback amount.
|
||||||
|
\param feedback Amount from 0-1. |
||||||
|
*/ |
||||||
|
void SetFeedback(float feedback); |
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_; |
||||||
|
static constexpr int32_t kDelayLength |
||||||
|
= 2400; // 50 ms at 48kHz = .05 * 48000
|
||||||
|
|
||||||
|
//triangle lfo
|
||||||
|
float lfo_phase_; |
||||||
|
float lfo_freq_; |
||||||
|
float lfo_amp_; |
||||||
|
|
||||||
|
float os_; |
||||||
|
|
||||||
|
float feedback_; |
||||||
|
float ap_freq_; |
||||||
|
|
||||||
|
float deltime_; |
||||||
|
float last_sample_; |
||||||
|
|
||||||
|
DelayLine<float, kDelayLength> del_; |
||||||
|
|
||||||
|
float ProcessLfo(); |
||||||
|
}; |
||||||
|
|
||||||
|
//wraps up all of the phaser engines
|
||||||
|
/**
|
||||||
|
@brief Phaser Effect. |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
*/ |
||||||
|
class Phaser |
||||||
|
{ |
||||||
|
public: |
||||||
|
Phaser() {} |
||||||
|
~Phaser() {} |
||||||
|
|
||||||
|
/** Initialize the module
|
||||||
|
\param sample_rate Audio engine sample rate |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the next floating point sample.
|
||||||
|
\param in Sample to process |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
/** Number of allpass stages.
|
||||||
|
\param poles Works 1 to 8. |
||||||
|
*/ |
||||||
|
void SetPoles(int poles); |
||||||
|
|
||||||
|
/** Set all lfo depths
|
||||||
|
\param depth Works 0-1. |
||||||
|
*/ |
||||||
|
void SetLfoDepth(float depth); |
||||||
|
|
||||||
|
/** Set all lfo frequencies.
|
||||||
|
\param lfo_freq Lfo freq in Hz. |
||||||
|
*/ |
||||||
|
void SetLfoFreq(float lfo_freq); |
||||||
|
|
||||||
|
/** Set all channel allpass freq in Hz.
|
||||||
|
\param ap_freq Frequency in Hz. |
||||||
|
*/ |
||||||
|
void SetFreq(float ap_freq); |
||||||
|
|
||||||
|
/** Set all channels feedback.
|
||||||
|
\param feedback Works 0-1. |
||||||
|
*/ |
||||||
|
void SetFeedback(float feedback); |
||||||
|
|
||||||
|
private: |
||||||
|
static constexpr int kMaxPoles = 8; |
||||||
|
PhaserEngine engines_[kMaxPoles]; |
||||||
|
float gain_frac_; |
||||||
|
int poles_; |
||||||
|
}; |
||||||
|
} //namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,211 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_PITCHSHIFTER_H |
||||||
|
#define DSY_PITCHSHIFTER_H |
||||||
|
#include <stdint.h> |
||||||
|
#include <cmath> |
||||||
|
#ifdef USE_ARM_DSP |
||||||
|
#include "arm_math.h" |
||||||
|
#endif |
||||||
|
#include "Utility/dsp.h" |
||||||
|
#include "Utility/delayline.h" |
||||||
|
#include "Control/phasor.h" |
||||||
|
|
||||||
|
/** Shift can be 30-100 ms lets just start with 50 for now.
|
||||||
|
0.050 * SR = 2400 samples (at 48kHz) |
||||||
|
*/ |
||||||
|
#define SHIFT_BUFFER_SIZE 16384 |
||||||
|
//#define SHIFT_BUFFER_SIZE 4800
|
||||||
|
//#define SHIFT_BUFFER_SIZE 8192
|
||||||
|
//#define SHIFT_BUFFER_SIZE 1024
|
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
static inline uint32_t hash_xs32(uint32_t x) |
||||||
|
{ |
||||||
|
x ^= x << 13; |
||||||
|
x ^= x >> 17; |
||||||
|
x ^= x << 5; |
||||||
|
return x; |
||||||
|
} |
||||||
|
|
||||||
|
inline uint32_t myrand() |
||||||
|
{ |
||||||
|
static uint32_t seed = 1; |
||||||
|
seed = hash_xs32(seed); |
||||||
|
return seed; |
||||||
|
} |
||||||
|
|
||||||
|
/** time-domain pitchshifter
|
||||||
|
|
||||||
|
Author: shensley |
||||||
|
|
||||||
|
Based on "Pitch Shifting" from ucsd.edu
|
||||||
|
|
||||||
|
t = 1 - ((s *f) / R) |
||||||
|
|
||||||
|
where: |
||||||
|
s is the size of the delay |
||||||
|
f is the frequency of the lfo |
||||||
|
r is the sample_rate |
||||||
|
|
||||||
|
solving for t = 12.0 |
||||||
|
f = (12 - 1) * 48000 / SHIFT_BUFFER_SIZE; |
||||||
|
|
||||||
|
\todo - move hash_xs32 and myrand to dsp.h and give appropriate names |
||||||
|
*/ |
||||||
|
class PitchShifter |
||||||
|
{ |
||||||
|
public: |
||||||
|
PitchShifter() {} |
||||||
|
~PitchShifter() {} |
||||||
|
/** Initialize pitch shifter
|
||||||
|
*/ |
||||||
|
void Init(float sr) |
||||||
|
{ |
||||||
|
force_recalc_ = false; |
||||||
|
sr_ = sr; |
||||||
|
mod_freq_ = 5.0f; |
||||||
|
SetSemitones(); |
||||||
|
for(uint8_t i = 0; i < 2; i++) |
||||||
|
{ |
||||||
|
gain_[i] = 0.0f; |
||||||
|
d_[i].Init(); |
||||||
|
phs_[i].Init(sr, 50, i == 0 ? 0 : PI_F); |
||||||
|
} |
||||||
|
shift_up_ = true; |
||||||
|
del_size_ = SHIFT_BUFFER_SIZE; |
||||||
|
SetDelSize(del_size_); |
||||||
|
fun_ = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
/** process pitch shifter
|
||||||
|
*/ |
||||||
|
float Process(float &in) |
||||||
|
{ |
||||||
|
float val, fade1, fade2; |
||||||
|
// First Process delay mod/crossfade
|
||||||
|
fade1 = phs_[0].Process(); |
||||||
|
fade2 = phs_[1].Process(); |
||||||
|
if(prev_phs_a_ > fade1) |
||||||
|
{ |
||||||
|
mod_a_amt_ = fun_ * ((float)(myrand() % 255) / 255.0f) |
||||||
|
* (del_size_ * 0.5f); |
||||||
|
mod_coeff_[0] |
||||||
|
= 0.0002f + (((float)(myrand() % 255) / 255.0f) * 0.001f); |
||||||
|
} |
||||||
|
if(prev_phs_b_ > fade2) |
||||||
|
{ |
||||||
|
mod_b_amt_ = fun_ * ((float)(myrand() % 255) / 255.0f) |
||||||
|
* (del_size_ * 0.5f); |
||||||
|
mod_coeff_[1] |
||||||
|
= 0.0002f + (((float)(myrand() % 255) / 255.0f) * 0.001f); |
||||||
|
} |
||||||
|
slewed_mod_[0] += mod_coeff_[0] * (mod_a_amt_ - slewed_mod_[0]); |
||||||
|
slewed_mod_[1] += mod_coeff_[1] * (mod_b_amt_ - slewed_mod_[1]); |
||||||
|
prev_phs_a_ = fade1; |
||||||
|
prev_phs_b_ = fade2; |
||||||
|
if(shift_up_) |
||||||
|
{ |
||||||
|
fade1 = 1.0f - fade1; |
||||||
|
fade2 = 1.0f - fade2; |
||||||
|
} |
||||||
|
mod_[0] = fade1 * (del_size_ - 1); |
||||||
|
mod_[1] = fade2 * (del_size_ - 1); |
||||||
|
#ifdef USE_ARM_DSP |
||||||
|
gain_[0] = arm_sin_f32(fade1 * (float)M_PI); |
||||||
|
gain_[1] = arm_sin_f32(fade2 * (float)M_PI); |
||||||
|
#else |
||||||
|
gain_[0] = sinf(fade1 * PI_F); |
||||||
|
gain_[1] = sinf(fade2 * PI_F); |
||||||
|
#endif |
||||||
|
|
||||||
|
// Handle Delay Writing
|
||||||
|
d_[0].Write(in); |
||||||
|
d_[1].Write(in); |
||||||
|
// Modulate Delay Lines
|
||||||
|
//mod_a_amt = mod_b_amt = 0.0f;
|
||||||
|
d_[0].SetDelay(mod_[0] + mod_a_amt_); |
||||||
|
d_[1].SetDelay(mod_[1] + mod_b_amt_); |
||||||
|
d_[0].SetDelay(mod_[0] + slewed_mod_[0]); |
||||||
|
d_[1].SetDelay(mod_[1] + slewed_mod_[1]); |
||||||
|
val = 0.0f; |
||||||
|
val += (d_[0].Read() * gain_[0]); |
||||||
|
val += (d_[1].Read() * gain_[1]); |
||||||
|
return val; |
||||||
|
} |
||||||
|
|
||||||
|
/** sets transposition in semitones
|
||||||
|
*/ |
||||||
|
void SetTransposition(const float &transpose) |
||||||
|
{ |
||||||
|
float ratio; |
||||||
|
uint8_t idx; |
||||||
|
if(transpose_ != transpose || force_recalc_) |
||||||
|
{ |
||||||
|
transpose_ = transpose; |
||||||
|
idx = (uint8_t)fabsf(transpose); |
||||||
|
ratio = semitone_ratios_[idx % 12]; |
||||||
|
ratio *= (uint8_t)(fabsf(transpose) / 12) + 1; |
||||||
|
if(transpose > 0.0f) |
||||||
|
{ |
||||||
|
shift_up_ = true; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
shift_up_ = false; |
||||||
|
} |
||||||
|
mod_freq_ = ((ratio - 1.0f) * sr_) / del_size_; |
||||||
|
if(mod_freq_ < 0.0f) |
||||||
|
{ |
||||||
|
mod_freq_ = 0.0f; |
||||||
|
} |
||||||
|
phs_[0].SetFreq(mod_freq_); |
||||||
|
phs_[1].SetFreq(mod_freq_); |
||||||
|
if(force_recalc_) |
||||||
|
{ |
||||||
|
force_recalc_ = false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** sets delay size changing the timbre of the pitchshifting
|
||||||
|
*/ |
||||||
|
void SetDelSize(uint32_t size) |
||||||
|
{ |
||||||
|
del_size_ = size < SHIFT_BUFFER_SIZE ? size : SHIFT_BUFFER_SIZE; |
||||||
|
force_recalc_ = true; |
||||||
|
SetTransposition(transpose_); |
||||||
|
} |
||||||
|
|
||||||
|
/** sets an amount of internal random modulation, kind of sounds like tape-flutter
|
||||||
|
*/ |
||||||
|
inline void SetFun(float f) { fun_ = f; } |
||||||
|
|
||||||
|
private: |
||||||
|
inline void SetSemitones() |
||||||
|
{ |
||||||
|
for(size_t i = 0; i < 12; i++) |
||||||
|
{ |
||||||
|
semitone_ratios_[i] = powf(2.0f, (float)i / 12); |
||||||
|
} |
||||||
|
} |
||||||
|
typedef DelayLine<float, SHIFT_BUFFER_SIZE> ShiftDelay; |
||||||
|
ShiftDelay d_[2]; |
||||||
|
float pitch_shift_, mod_freq_; |
||||||
|
uint32_t del_size_; |
||||||
|
/** lfo stuff
|
||||||
|
*/ |
||||||
|
bool force_recalc_; |
||||||
|
float sr_; |
||||||
|
bool shift_up_; |
||||||
|
Phasor phs_[2]; |
||||||
|
float gain_[2], mod_[2], transpose_; |
||||||
|
float fun_, mod_a_amt_, mod_b_amt_, prev_phs_a_, prev_phs_b_; |
||||||
|
float slewed_mod_[2], mod_coeff_[2]; |
||||||
|
/** pitch stuff
|
||||||
|
*/ |
||||||
|
float semitone_ratios_[12]; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,283 @@ |
|||||||
|
#include <math.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <string.h> |
||||||
|
#include "reverbsc.h" |
||||||
|
|
||||||
|
#define REVSC_OK 0 |
||||||
|
#define REVSC_NOT_OK 1 |
||||||
|
|
||||||
|
#define DEFAULT_SRATE 48000.0 |
||||||
|
#define MIN_SRATE 5000.0 |
||||||
|
#define MAX_SRATE 1000000.0 |
||||||
|
#define MAX_PITCHMOD 20.0 |
||||||
|
#define DELAYPOS_SHIFT 28 |
||||||
|
#define DELAYPOS_SCALE 0x10000000 |
||||||
|
#define DELAYPOS_MASK 0x0FFFFFFF |
||||||
|
|
||||||
|
#ifndef M_PI |
||||||
|
#define M_PI 3.14159265358979323846 /* pi */ |
||||||
|
#endif |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
/* kReverbParams[n][0] = delay time (in seconds) */ |
||||||
|
/* kReverbParams[n][1] = random variation in delay time (in seconds) */ |
||||||
|
/* kReverbParams[n][2] = random variation frequency (in 1/sec) */ |
||||||
|
/* kReverbParams[n][3] = random seed (0 - 32767) */ |
||||||
|
|
||||||
|
static const float kReverbParams[8][4] |
||||||
|
= {{(2473.0 / DEFAULT_SRATE), 0.0010, 3.100, 1966.0}, |
||||||
|
{(2767.0 / DEFAULT_SRATE), 0.0011, 3.500, 29491.0}, |
||||||
|
{(3217.0 / DEFAULT_SRATE), 0.0017, 1.110, 22937.0}, |
||||||
|
{(3557.0 / DEFAULT_SRATE), 0.0006, 3.973, 9830.0}, |
||||||
|
{(3907.0 / DEFAULT_SRATE), 0.0010, 2.341, 20643.0}, |
||||||
|
{(4127.0 / DEFAULT_SRATE), 0.0011, 1.897, 22937.0}, |
||||||
|
{(2143.0 / DEFAULT_SRATE), 0.0017, 0.891, 29491.0}, |
||||||
|
{(1933.0 / DEFAULT_SRATE), 0.0006, 3.221, 14417.0}}; |
||||||
|
|
||||||
|
static int DelayLineMaxSamples(float sr, float i_pitch_mod, int n); |
||||||
|
//static int InitDelayLine(dsy_reverbsc_dl *lp, int n);
|
||||||
|
static int DelayLineBytesAlloc(float sr, float i_pitch_mod, int n); |
||||||
|
static const float kOutputGain = 0.35; |
||||||
|
static const float kJpScale = 0.25; |
||||||
|
|
||||||
|
int ReverbSc::Init(float sr) |
||||||
|
{ |
||||||
|
i_sample_rate_ = sr; |
||||||
|
sample_rate_ = sr; |
||||||
|
feedback_ = 0.97; |
||||||
|
lpfreq_ = 10000; |
||||||
|
i_pitch_mod_ = 1; |
||||||
|
i_skip_init_ = 0; |
||||||
|
damp_fact_ = 1.0; |
||||||
|
prv_lpfreq_ = 0.0; |
||||||
|
init_done_ = 1; |
||||||
|
int i, n_bytes = 0; |
||||||
|
n_bytes = 0; |
||||||
|
for(i = 0; i < 8; i++) |
||||||
|
{ |
||||||
|
if(n_bytes > DSY_REVERBSC_MAX_SIZE) |
||||||
|
return 1; |
||||||
|
delay_lines_[i].buf = (aux_) + n_bytes; |
||||||
|
InitDelayLine(&delay_lines_[i], i); |
||||||
|
n_bytes += DelayLineBytesAlloc(sr, 1, i); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int DelayLineMaxSamples(float sr, float i_pitch_mod, int n) |
||||||
|
{ |
||||||
|
float max_del; |
||||||
|
|
||||||
|
max_del = kReverbParams[n][0]; |
||||||
|
max_del += (kReverbParams[n][1] * (float)i_pitch_mod * 1.125); |
||||||
|
return (int)(max_del * sr + 16.5); |
||||||
|
} |
||||||
|
|
||||||
|
static int DelayLineBytesAlloc(float sr, float i_pitch_mod, int n) |
||||||
|
{ |
||||||
|
int n_bytes = 0; |
||||||
|
|
||||||
|
n_bytes += (DelayLineMaxSamples(sr, i_pitch_mod, n) * (int)sizeof(float)); |
||||||
|
return n_bytes; |
||||||
|
} |
||||||
|
|
||||||
|
void ReverbSc::NextRandomLineseg(ReverbScDl *lp, int n) |
||||||
|
{ |
||||||
|
float prv_del, nxt_del, phs_inc_val; |
||||||
|
|
||||||
|
/* update random seed */ |
||||||
|
if(lp->seed_val < 0) |
||||||
|
lp->seed_val += 0x10000; |
||||||
|
lp->seed_val = (lp->seed_val * 15625 + 1) & 0xFFFF; |
||||||
|
if(lp->seed_val >= 0x8000) |
||||||
|
lp->seed_val -= 0x10000; |
||||||
|
/* length of next segment in samples */ |
||||||
|
lp->rand_line_cnt = (int)((sample_rate_ / kReverbParams[n][2]) + 0.5); |
||||||
|
prv_del = (float)lp->write_pos; |
||||||
|
prv_del -= ((float)lp->read_pos |
||||||
|
+ ((float)lp->read_pos_frac / (float)DELAYPOS_SCALE)); |
||||||
|
while(prv_del < 0.0) |
||||||
|
prv_del += lp->buffer_size; |
||||||
|
prv_del = prv_del / sample_rate_; /* previous delay time in seconds */ |
||||||
|
nxt_del = (float)lp->seed_val * kReverbParams[n][1] / 32768.0; |
||||||
|
/* next delay time in seconds */ |
||||||
|
nxt_del = kReverbParams[n][0] + (nxt_del * (float)i_pitch_mod_); |
||||||
|
/* calculate phase increment per sample */ |
||||||
|
phs_inc_val = (prv_del - nxt_del) / (float)lp->rand_line_cnt; |
||||||
|
phs_inc_val = phs_inc_val * sample_rate_ + 1.0; |
||||||
|
lp->read_pos_frac_inc = (int)(phs_inc_val * DELAYPOS_SCALE + 0.5); |
||||||
|
} |
||||||
|
|
||||||
|
int ReverbSc::InitDelayLine(ReverbScDl *lp, int n) |
||||||
|
{ |
||||||
|
float read_pos; |
||||||
|
/* int i; */ |
||||||
|
|
||||||
|
/* calculate length of delay line */ |
||||||
|
lp->buffer_size = DelayLineMaxSamples(sample_rate_, 1, n); |
||||||
|
lp->dummy = 0; |
||||||
|
lp->write_pos = 0; |
||||||
|
/* set random seed */ |
||||||
|
lp->seed_val = (int)(kReverbParams[n][3] + 0.5); |
||||||
|
/* set initial delay time */ |
||||||
|
read_pos = (float)lp->seed_val * kReverbParams[n][1] / 32768; |
||||||
|
read_pos = kReverbParams[n][0] + (read_pos * (float)i_pitch_mod_); |
||||||
|
read_pos = (float)lp->buffer_size - (read_pos * sample_rate_); |
||||||
|
lp->read_pos = (int)read_pos; |
||||||
|
read_pos = (read_pos - (float)lp->read_pos) * (float)DELAYPOS_SCALE; |
||||||
|
lp->read_pos_frac = (int)(read_pos + 0.5); |
||||||
|
/* initialise first random line segment */ |
||||||
|
NextRandomLineseg(lp, n); |
||||||
|
/* clear delay line to zero */ |
||||||
|
lp->filter_state = 0.0; |
||||||
|
for(int i = 0; i < lp->buffer_size; i++) |
||||||
|
{ |
||||||
|
lp->buf[i] = 0; |
||||||
|
} |
||||||
|
return REVSC_OK; |
||||||
|
} |
||||||
|
|
||||||
|
int ReverbSc::Process(const float &in1, |
||||||
|
const float &in2, |
||||||
|
float * out1, |
||||||
|
float * out2) |
||||||
|
{ |
||||||
|
float a_in_l, a_in_r, a_out_l, a_out_r; |
||||||
|
float vm1, v0, v1, v2, am1, a0, a1, a2, frac; |
||||||
|
ReverbScDl *lp; |
||||||
|
int read_pos; |
||||||
|
uint32_t n; |
||||||
|
int buffer_size; /* Local copy */ |
||||||
|
float damp_fact = damp_fact_; |
||||||
|
|
||||||
|
//if (init_done_ <= 0) return REVSC_NOT_OK;
|
||||||
|
if(init_done_ <= 0) |
||||||
|
return REVSC_NOT_OK; |
||||||
|
|
||||||
|
/* calculate tone filter coefficient if frequency changed */ |
||||||
|
if(lpfreq_ != prv_lpfreq_) |
||||||
|
{ |
||||||
|
prv_lpfreq_ = lpfreq_; |
||||||
|
damp_fact |
||||||
|
= 2.0f - cosf(prv_lpfreq_ * (2.0f * (float)M_PI) / sample_rate_); |
||||||
|
damp_fact = damp_fact_ |
||||||
|
= damp_fact - sqrtf(damp_fact * damp_fact - 1.0f); |
||||||
|
} |
||||||
|
|
||||||
|
/* calculate "resultant junction pressure" and mix to input signals */ |
||||||
|
|
||||||
|
a_in_l = a_out_l = a_out_r = 0.0; |
||||||
|
for(n = 0; n < 8; n++) |
||||||
|
{ |
||||||
|
a_in_l += delay_lines_[n].filter_state; |
||||||
|
} |
||||||
|
a_in_l *= kJpScale; |
||||||
|
a_in_r = a_in_l + in2; |
||||||
|
a_in_l = a_in_l + in1; |
||||||
|
|
||||||
|
/* loop through all delay lines */ |
||||||
|
|
||||||
|
for(n = 0; n < 8; n++) |
||||||
|
{ |
||||||
|
lp = &delay_lines_[n]; |
||||||
|
buffer_size = lp->buffer_size; |
||||||
|
|
||||||
|
/* send input signal and feedback to delay line */ |
||||||
|
|
||||||
|
lp->buf[lp->write_pos] |
||||||
|
= (float)((n & 1 ? a_in_r : a_in_l) - lp->filter_state); |
||||||
|
if(++lp->write_pos >= buffer_size) |
||||||
|
{ |
||||||
|
lp->write_pos -= buffer_size; |
||||||
|
} |
||||||
|
|
||||||
|
/* read from delay line with cubic interpolation */ |
||||||
|
|
||||||
|
if(lp->read_pos_frac >= DELAYPOS_SCALE) |
||||||
|
{ |
||||||
|
lp->read_pos += (lp->read_pos_frac >> DELAYPOS_SHIFT); |
||||||
|
lp->read_pos_frac &= DELAYPOS_MASK; |
||||||
|
} |
||||||
|
if(lp->read_pos >= buffer_size) |
||||||
|
lp->read_pos -= buffer_size; |
||||||
|
read_pos = lp->read_pos; |
||||||
|
frac = (float)lp->read_pos_frac * (1.0 / (float)DELAYPOS_SCALE); |
||||||
|
|
||||||
|
/* calculate interpolation coefficients */ |
||||||
|
|
||||||
|
a2 = frac * frac; |
||||||
|
a2 -= 1.0; |
||||||
|
a2 *= (1.0 / 6.0); |
||||||
|
a1 = frac; |
||||||
|
a1 += 1.0; |
||||||
|
a1 *= 0.5; |
||||||
|
am1 = a1 - 1.0; |
||||||
|
a0 = 3.0 * a2; |
||||||
|
a1 -= a0; |
||||||
|
am1 -= a2; |
||||||
|
a0 -= frac; |
||||||
|
|
||||||
|
/* read four samples for interpolation */ |
||||||
|
|
||||||
|
if(read_pos > 0 && read_pos < (buffer_size - 2)) |
||||||
|
{ |
||||||
|
vm1 = (float)(lp->buf[read_pos - 1]); |
||||||
|
v0 = (float)(lp->buf[read_pos]); |
||||||
|
v1 = (float)(lp->buf[read_pos + 1]); |
||||||
|
v2 = (float)(lp->buf[read_pos + 2]); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
/* at buffer wrap-around, need to check index */ |
||||||
|
|
||||||
|
if(--read_pos < 0) |
||||||
|
read_pos += buffer_size; |
||||||
|
vm1 = (float)lp->buf[read_pos]; |
||||||
|
if(++read_pos >= buffer_size) |
||||||
|
read_pos -= buffer_size; |
||||||
|
v0 = (float)lp->buf[read_pos]; |
||||||
|
if(++read_pos >= buffer_size) |
||||||
|
read_pos -= buffer_size; |
||||||
|
v1 = (float)lp->buf[read_pos]; |
||||||
|
if(++read_pos >= buffer_size) |
||||||
|
read_pos -= buffer_size; |
||||||
|
v2 = (float)lp->buf[read_pos]; |
||||||
|
} |
||||||
|
v0 = (am1 * vm1 + a0 * v0 + a1 * v1 + a2 * v2) * frac + v0; |
||||||
|
|
||||||
|
/* update buffer read position */ |
||||||
|
|
||||||
|
lp->read_pos_frac += lp->read_pos_frac_inc; |
||||||
|
|
||||||
|
/* apply feedback gain and lowpass filter */ |
||||||
|
|
||||||
|
v0 *= (float)feedback_; |
||||||
|
v0 = (lp->filter_state - v0) * damp_fact + v0; |
||||||
|
lp->filter_state = v0; |
||||||
|
|
||||||
|
/* mix to output */ |
||||||
|
|
||||||
|
if(n & 1) |
||||||
|
{ |
||||||
|
a_out_r += v0; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
a_out_l += v0; |
||||||
|
} |
||||||
|
|
||||||
|
/* start next random line segment if current one has reached endpoint */ |
||||||
|
|
||||||
|
if(--(lp->rand_line_cnt) <= 0) |
||||||
|
{ |
||||||
|
NextRandomLineseg(lp, n); |
||||||
|
} |
||||||
|
} |
||||||
|
/* someday, use a_out_r for multimono out */ |
||||||
|
|
||||||
|
*out1 = a_out_l * kOutputGain; |
||||||
|
*out2 = a_out_r * kOutputGain; |
||||||
|
return REVSC_OK; |
||||||
|
} |
@ -0,0 +1,75 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSYSP_REVERBSC_H |
||||||
|
#define DSYSP_REVERBSC_H |
||||||
|
|
||||||
|
#define DSY_REVERBSC_MAX_SIZE 98936 |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**Delay line for internal reverb use
|
||||||
|
*/ |
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
int write_pos; /**< write position */ |
||||||
|
int buffer_size; /**< buffer size */ |
||||||
|
int read_pos; /**< read position */ |
||||||
|
int read_pos_frac; /**< fractional component of read pos */ |
||||||
|
int read_pos_frac_inc; /**< increment for fractional */ |
||||||
|
int dummy; /**< dummy var */ |
||||||
|
int seed_val; /**< randseed */ |
||||||
|
int rand_line_cnt; /**< number of random lines */ |
||||||
|
float filter_state; /**< state of filter */ |
||||||
|
float *buf; /**< buffer ptr */ |
||||||
|
} ReverbScDl; |
||||||
|
|
||||||
|
/** Stereo Reverb
|
||||||
|
|
||||||
|
Reverb SC: Ported from csound/soundpipe |
||||||
|
|
||||||
|
Original author(s): Sean Costello, Istvan Varga |
||||||
|
|
||||||
|
Year: 1999, 2005 |
||||||
|
|
||||||
|
Ported to soundpipe by: Paul Batchelor |
||||||
|
|
||||||
|
Ported by: Stephen Hensley |
||||||
|
*/ |
||||||
|
class ReverbSc |
||||||
|
{ |
||||||
|
public: |
||||||
|
ReverbSc() {} |
||||||
|
~ReverbSc() {} |
||||||
|
/** Initializes the reverb module, and sets the sample_rate at which the Process function will be called.
|
||||||
|
Returns 0 if all good, or 1 if it runs out of delay times exceed maximum allowed. |
||||||
|
*/ |
||||||
|
int Init(float sample_rate); |
||||||
|
|
||||||
|
/** Process the input through the reverb, and updates values of out1, and out2 with the new processed signal.
|
||||||
|
*/ |
||||||
|
int Process(const float &in1, const float &in2, float *out1, float *out2); |
||||||
|
|
||||||
|
/** controls the reverb time. reverb tail becomes infinite when set to 1.0
|
||||||
|
\param fb - sets reverb time. range: 0.0 to 1.0 |
||||||
|
*/ |
||||||
|
inline void SetFeedback(const float &fb) { feedback_ = fb; } |
||||||
|
/** controls the internal dampening filter's cutoff frequency.
|
||||||
|
\param freq - low pass frequency. range: 0.0 to sample_rate / 2 |
||||||
|
*/ |
||||||
|
inline void SetLpFreq(const float &freq) { lpfreq_ = freq; } |
||||||
|
|
||||||
|
private: |
||||||
|
void NextRandomLineseg(ReverbScDl *lp, int n); |
||||||
|
int InitDelayLine(ReverbScDl *lp, int n); |
||||||
|
float feedback_, lpfreq_; |
||||||
|
float i_sample_rate_, i_pitch_mod_, i_skip_init_; |
||||||
|
float sample_rate_; |
||||||
|
float damp_fact_; |
||||||
|
float prv_lpfreq_; |
||||||
|
int init_done_; |
||||||
|
ReverbScDl delay_lines_[8]; |
||||||
|
float aux_[DSY_REVERBSC_MAX_SIZE]; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
@ -0,0 +1,43 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "sampleratereducer.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
void SampleRateReducer::Init() |
||||||
|
{ |
||||||
|
frequency_ = .2f; |
||||||
|
phase_ = 0.0f; |
||||||
|
sample_ = 0.0f; |
||||||
|
next_sample_ = 0.0f; |
||||||
|
previous_sample_ = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
float SampleRateReducer::Process(float in) |
||||||
|
{ |
||||||
|
float this_sample = next_sample_; |
||||||
|
next_sample_ = 0.f; |
||||||
|
phase_ += frequency_; |
||||||
|
if(phase_ >= 1.0f) |
||||||
|
{ |
||||||
|
phase_ -= 1.0f; |
||||||
|
float t = phase_ / frequency_; |
||||||
|
// t = 0: the transition occurred right at this sample.
|
||||||
|
// t = 1: the transition occurred at the previous sample.
|
||||||
|
// Use linear interpolation to recover the fractional sample.
|
||||||
|
float new_sample |
||||||
|
= previous_sample_ + (in - previous_sample_) * (1.0f - t); |
||||||
|
float discontinuity = new_sample - sample_; |
||||||
|
this_sample += discontinuity * ThisBlepSample(t); |
||||||
|
next_sample_ = discontinuity * NextBlepSample(t); |
||||||
|
sample_ = new_sample; |
||||||
|
} |
||||||
|
next_sample_ += sample_; |
||||||
|
previous_sample_ = in; |
||||||
|
|
||||||
|
return this_sample; |
||||||
|
} |
||||||
|
|
||||||
|
void SampleRateReducer::SetFreq(float frequency) |
||||||
|
{ |
||||||
|
frequency_ = fclamp(frequency, 0.f, 1.f); |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_SR_REDUCER_H |
||||||
|
#define DSY_SR_REDUCER_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file sampleratereducer.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Sample rate reducer. |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/fx/sample_rate_reducer.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2014. \n |
||||||
|
*/ |
||||||
|
class SampleRateReducer |
||||||
|
{ |
||||||
|
public: |
||||||
|
SampleRateReducer() {} |
||||||
|
~SampleRateReducer() {} |
||||||
|
|
||||||
|
/** Initialize the module */ |
||||||
|
void Init(); |
||||||
|
|
||||||
|
/** Get the next floating point sample
|
||||||
|
\param in Sample to be processed. |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
/** Set the new sample rate.
|
||||||
|
\param Works over 0-1. 1 is full quality, .5 is half sample rate, etc. |
||||||
|
*/ |
||||||
|
void SetFreq(float frequency); |
||||||
|
|
||||||
|
private: |
||||||
|
float frequency_; |
||||||
|
float phase_; |
||||||
|
float sample_; |
||||||
|
float previous_sample_; |
||||||
|
float next_sample_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,36 @@ |
|||||||
|
#include "tremolo.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Tremolo::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
osc_.Init(sample_rate_); |
||||||
|
SetDepth(1.f); |
||||||
|
SetFreq(1.f); |
||||||
|
} |
||||||
|
|
||||||
|
float Tremolo::Process(float in) |
||||||
|
{ |
||||||
|
float modsig = dc_os_ + osc_.Process(); |
||||||
|
return in * modsig; |
||||||
|
} |
||||||
|
|
||||||
|
void Tremolo::SetFreq(float freq) |
||||||
|
{ |
||||||
|
osc_.SetFreq(freq); |
||||||
|
} |
||||||
|
|
||||||
|
void Tremolo::SetWaveform(int waveform) |
||||||
|
{ |
||||||
|
osc_.SetWaveform(waveform); |
||||||
|
} |
||||||
|
void Tremolo::SetDepth(float depth) |
||||||
|
{ |
||||||
|
depth = fclamp(depth, 0.f, 1.f); |
||||||
|
depth *= .5f; |
||||||
|
osc_.SetAmp(depth); |
||||||
|
dc_os_ = 1.f - depth; |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_TREMOLO_H |
||||||
|
#define DSY_TREMOLO_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
#include <math.h> |
||||||
|
#include "Synthesis/oscillator.h" |
||||||
|
|
||||||
|
/** @file tremolo.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Tremolo effect. |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
Based on https://christianfloisand.wordpress.com/2012/04/18/coding-some-tremolo/ \n
|
||||||
|
*/ |
||||||
|
class Tremolo |
||||||
|
{ |
||||||
|
public: |
||||||
|
Tremolo() {} |
||||||
|
~Tremolo() {} |
||||||
|
|
||||||
|
/** Initializes the module
|
||||||
|
\param sample_rate The sample rate of the audio engine being run. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/**
|
||||||
|
\param in Input sample. |
||||||
|
\return Next floating point sample. |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
/** Sets the tremolo rate.
|
||||||
|
\param freq Tremolo freq in Hz. |
||||||
|
*/ |
||||||
|
void SetFreq(float freq); |
||||||
|
|
||||||
|
/** Shape of the modulating lfo
|
||||||
|
\param waveform Oscillator waveform. Use Oscillator::WAVE_SIN for example. |
||||||
|
*/ |
||||||
|
void SetWaveform(int waveform); |
||||||
|
|
||||||
|
/** How much to modulate your volume.
|
||||||
|
\param depth Works 0-1. |
||||||
|
*/ |
||||||
|
void SetDepth(float depth); |
||||||
|
|
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_, dc_os_; |
||||||
|
Oscillator osc_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,42 @@ |
|||||||
|
#include "allpass.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Allpass::Init(float sample_rate, float* buff, size_t size) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
rev_time_ = 3.5; |
||||||
|
max_loop_time_ = ((float)size / sample_rate_) - .01; |
||||||
|
loop_time_ = max_loop_time_; |
||||||
|
mod_ = (int)(loop_time_ * sample_rate_); |
||||||
|
buf_ = buff; |
||||||
|
prvt_ = 0.0; |
||||||
|
coef_ = 0.0; |
||||||
|
buf_pos_ = 0; |
||||||
|
} |
||||||
|
|
||||||
|
float Allpass::Process(float in) |
||||||
|
{ |
||||||
|
float y, z, out; |
||||||
|
if(prvt_ != rev_time_) |
||||||
|
{ |
||||||
|
prvt_ = rev_time_; |
||||||
|
coef_ = expf(-6.9078 * loop_time_ / prvt_); |
||||||
|
} |
||||||
|
|
||||||
|
y = buf_[buf_pos_]; |
||||||
|
z = coef_ * y + in; |
||||||
|
buf_[buf_pos_] = z; |
||||||
|
out = y - coef_ * z; |
||||||
|
|
||||||
|
buf_pos_++; |
||||||
|
buf_pos_ %= mod_; |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
void Allpass::SetFreq(float freq) |
||||||
|
{ |
||||||
|
loop_time_ = fmaxf(fminf(freq, max_loop_time_), .0001); |
||||||
|
mod_ = fmaxf(loop_time_ * sample_rate_, 0); |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_ALLPASS_H |
||||||
|
#define DSY_ALLPASS_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
/** @file allpass.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
Allpass filter module \n
|
||||||
|
Passes all frequencies at their original levels, with a phase shift. \n
|
||||||
|
Ported from soundpipe by Ben Sergentanis, May 2020
|
||||||
|
@author Barry Vercoe, John ffitch |
||||||
|
@date 1991 |
||||||
|
*/ |
||||||
|
class Allpass |
||||||
|
{ |
||||||
|
public: |
||||||
|
Allpass() {} |
||||||
|
~Allpass() {} |
||||||
|
|
||||||
|
/**
|
||||||
|
Initializes the allpass module. |
||||||
|
\param sample_rate The sample rate of the audio engine being run. |
||||||
|
\param buff Buffer for allpass to use. |
||||||
|
\param size Size of buff. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate, float* buff, size_t size); |
||||||
|
|
||||||
|
/**
|
||||||
|
\param in Input sample. |
||||||
|
\return Next floating point sample. |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
/**
|
||||||
|
Sets the filter frequency (Implemented by delay time). |
||||||
|
\param looptime Filter looptime in seconds. |
||||||
|
*/ |
||||||
|
void SetFreq(float looptime); |
||||||
|
|
||||||
|
/**
|
||||||
|
\param revtime Reverb time in seconds. |
||||||
|
*/ |
||||||
|
inline void SetRevTime(float revtime) { rev_time_ = revtime; } |
||||||
|
|
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_, rev_time_, loop_time_, prvt_, coef_, max_loop_time_; |
||||||
|
float* buf_; |
||||||
|
int buf_pos_, mod_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,32 @@ |
|||||||
|
#include "atone.h" |
||||||
|
#include <math.h> |
||||||
|
#include "../utility/dsp.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void ATone::Init(float sample_rate) |
||||||
|
{ |
||||||
|
prevout_ = 0.0f; |
||||||
|
freq_ = 1000.0f; |
||||||
|
c2_ = 0.5f; |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
} |
||||||
|
|
||||||
|
float ATone::Process(float &in) |
||||||
|
{ |
||||||
|
float out; |
||||||
|
|
||||||
|
out = c2_ * (prevout_ + in); |
||||||
|
prevout_ = out - in; |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
void ATone::CalculateCoefficients() |
||||||
|
{ |
||||||
|
float b, c2; |
||||||
|
|
||||||
|
b = 2.0f - cosf(TWOPI_F * freq_ / sample_rate_); |
||||||
|
c2 = b - sqrtf(b * b - 1.0f); |
||||||
|
c2_ = c2; |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_ATONE_H |
||||||
|
#define DSY_ATONE_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** A first-order recursive high-pass filter with variable frequency response.
|
||||||
|
Original Author(s): Barry Vercoe, John FFitch, Gabriel Maldonado |
||||||
|
|
||||||
|
Year: 1991 |
||||||
|
|
||||||
|
Original Location: Csound -- OOps/ugens5.c |
||||||
|
|
||||||
|
Ported from soundpipe by Ben Sergentanis, May 2020 |
||||||
|
*/ |
||||||
|
class ATone |
||||||
|
{ |
||||||
|
public: |
||||||
|
ATone() {} |
||||||
|
~ATone() {} |
||||||
|
/** Initializes the ATone module.
|
||||||
|
\param sample_rate - The sample rate of the audio engine being run.
|
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
|
||||||
|
/** Processes one sample through the filter and returns one sample.
|
||||||
|
\param in - input signal
|
||||||
|
*/ |
||||||
|
float Process(float &in); |
||||||
|
|
||||||
|
/** Sets the cutoff frequency or half-way point of the filter.
|
||||||
|
\param freq - frequency value in Hz. Range: Any positive value. |
||||||
|
*/ |
||||||
|
inline void SetFreq(float &freq) |
||||||
|
{ |
||||||
|
freq_ = freq; |
||||||
|
CalculateCoefficients(); |
||||||
|
} |
||||||
|
|
||||||
|
/** get current frequency
|
||||||
|
\return the current value for the cutoff frequency or half-way point of the filter. |
||||||
|
*/ |
||||||
|
inline float GetFreq() { return freq_; } |
||||||
|
|
||||||
|
private: |
||||||
|
void CalculateCoefficients(); |
||||||
|
float out_, prevout_, in_, freq_, c2_, sample_rate_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,59 @@ |
|||||||
|
#include "biquad.h" |
||||||
|
#include <math.h> |
||||||
|
#include "../utility/dsp.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Biquad::Reset() |
||||||
|
{ |
||||||
|
float con = cutoff_ * two_pi_d_sr_; |
||||||
|
float alpha = 1.0f - 2.0f * res_ * cosf(con) * cosf(con) |
||||||
|
+ res_ * res_ * cosf(2 * con); |
||||||
|
float beta = 1.0f + cosf(con); |
||||||
|
float gamma = 1 + cosf(con); |
||||||
|
float m1 = alpha * gamma + beta * sinf(con); |
||||||
|
float m2 = alpha * gamma - beta * sinf(con); |
||||||
|
float den = sqrtf(m1 * m1 + m2 * m2); |
||||||
|
|
||||||
|
b0_ = 1.5f * (alpha * alpha + beta * beta) / den; |
||||||
|
b1_ = b0_; |
||||||
|
b2_ = 0.0f; |
||||||
|
a0_ = 1.0f; |
||||||
|
a1_ = -2.0 * res_ * cosf(con); |
||||||
|
a2_ = res_ * res_; |
||||||
|
} |
||||||
|
|
||||||
|
void Biquad::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
two_pi_d_sr_ = TWOPI_F / sample_rate_; |
||||||
|
|
||||||
|
cutoff_ = 500; |
||||||
|
res_ = 0.7; |
||||||
|
|
||||||
|
Reset(); |
||||||
|
|
||||||
|
xnm1_ = xnm2_ = ynm1_ = ynm2_ = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
float Biquad::Process(float in) |
||||||
|
{ |
||||||
|
float xn, yn; |
||||||
|
float a0 = a0_, a1 = a1_, a2 = a2_; |
||||||
|
float b0 = b0_, b1 = b1_, b2 = b2_; |
||||||
|
float xnm1 = xnm1_, xnm2 = xnm2_, ynm1 = ynm1_, ynm2 = ynm2_; |
||||||
|
|
||||||
|
xn = in; |
||||||
|
yn = (b0 * xn + b1 * xnm1 + b2 * xnm2 - a1 * ynm1 - a2 * ynm2) / a0; |
||||||
|
xnm2 = xnm1; |
||||||
|
xnm1 = xn; |
||||||
|
ynm2 = ynm1; |
||||||
|
ynm1 = yn; |
||||||
|
|
||||||
|
xnm1_ = xnm1; |
||||||
|
xnm2_ = xnm2; |
||||||
|
ynm1_ = ynm1; |
||||||
|
ynm2_ = ynm2; |
||||||
|
|
||||||
|
return yn; |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_BIQUAD_H |
||||||
|
#define DSY_BIQUAD_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Two pole recursive filter
|
||||||
|
|
||||||
|
Original author(s) : Hans Mikelson |
||||||
|
|
||||||
|
Year: 1998 |
||||||
|
|
||||||
|
Ported from soundpipe by Ben Sergentanis, May 2020 |
||||||
|
*/ |
||||||
|
class Biquad |
||||||
|
{ |
||||||
|
public: |
||||||
|
Biquad() {} |
||||||
|
~Biquad() {} |
||||||
|
/** Initializes the biquad module.
|
||||||
|
\param sample_rate - The sample rate of the audio engine being run.
|
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
|
||||||
|
/** Filters the input signal
|
||||||
|
\return filtered output |
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
|
||||||
|
/** Sets resonance amount
|
||||||
|
\param res : Set filter resonance. |
||||||
|
*/ |
||||||
|
inline void SetRes(float res) |
||||||
|
{ |
||||||
|
res_ = res; |
||||||
|
Reset(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Sets filter cutoff in Hz
|
||||||
|
\param cutoff : Set filter cutoff. |
||||||
|
*/ |
||||||
|
inline void SetCutoff(float cutoff) |
||||||
|
{ |
||||||
|
cutoff_ = cutoff; |
||||||
|
Reset(); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_, cutoff_, res_, b0_, b1_, b2_, a0_, a1_, a2_, |
||||||
|
two_pi_d_sr_, xnm1_, xnm2_, ynm1_, ynm2_; |
||||||
|
void Reset(); |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,62 @@ |
|||||||
|
#include "comb.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
static float log001 = -6.9078f; // log .001
|
||||||
|
|
||||||
|
void Comb::Init(float sample_rate, float* buff, size_t size) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
rev_time_ = 3.5; |
||||||
|
max_size_ = size; |
||||||
|
max_loop_time_ = ((float)size / sample_rate_) - .01; |
||||||
|
loop_time_ = max_loop_time_; |
||||||
|
mod_ = sample_rate_ * loop_time_; |
||||||
|
buf_ = buff; |
||||||
|
prvt_ = 0.0f; |
||||||
|
coef_ = 0.0f; |
||||||
|
buf_pos_ = 0; |
||||||
|
} |
||||||
|
|
||||||
|
float Comb::Process(float in) |
||||||
|
{ |
||||||
|
float tmp = 0; |
||||||
|
float coef = coef_; |
||||||
|
float outsamp = 0; |
||||||
|
|
||||||
|
if(prvt_ != rev_time_) |
||||||
|
{ |
||||||
|
prvt_ = rev_time_; |
||||||
|
float exp_arg = (float)(log001 * loop_time_ / prvt_); |
||||||
|
if(exp_arg < -36.8413615) |
||||||
|
{ |
||||||
|
coef = coef_ = 0; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
coef = coef_ = expf(exp_arg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// internal delay line
|
||||||
|
outsamp = buf_[(buf_pos_ + mod_) % max_size_]; |
||||||
|
tmp = (outsamp * coef) + in; |
||||||
|
buf_[(size_t)buf_pos_] = tmp; |
||||||
|
buf_pos_ = (buf_pos_ - 1 + max_size_) % max_size_; |
||||||
|
|
||||||
|
return outsamp; |
||||||
|
} |
||||||
|
|
||||||
|
void Comb::SetPeriod(float looptime) |
||||||
|
{ |
||||||
|
if(looptime > 0) |
||||||
|
{ |
||||||
|
loop_time_ = fminf(looptime, max_loop_time_); |
||||||
|
mod_ = loop_time_ * sample_rate_; |
||||||
|
if(mod_ > max_size_) |
||||||
|
{ |
||||||
|
mod_ = max_size_ - 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_COMB_H |
||||||
|
#define DSY_COMB_H |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
#include "Utility/dsp.h" |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Comb filter module
|
||||||
|
|
||||||
|
Original author(s) : |
||||||
|
|
||||||
|
Ported from soundpipe by Ben Sergentanis, May 2020 |
||||||
|
*/ |
||||||
|
class Comb |
||||||
|
{ |
||||||
|
public: |
||||||
|
Comb() {} |
||||||
|
~Comb() {} |
||||||
|
|
||||||
|
/** Initializes the Comb module.
|
||||||
|
\param sample_rate - The sample rate of the audio engine being run.
|
||||||
|
\param buff - input buffer, kept in either main() or global space |
||||||
|
\param size - size of buff |
||||||
|
*/ |
||||||
|
void Init(float sample_rate, float* buff, size_t size); |
||||||
|
|
||||||
|
|
||||||
|
/** processes the comb filter
|
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
|
||||||
|
/** Sets the period of the comb filter in seconds
|
||||||
|
*/ |
||||||
|
void SetPeriod(float looptime); |
||||||
|
|
||||||
|
/** Sets the frequency of the comb filter in Hz
|
||||||
|
*/ |
||||||
|
inline void SetFreq(float freq) |
||||||
|
{ |
||||||
|
if(freq > 0) |
||||||
|
{ |
||||||
|
SetPeriod(1.f / freq); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Sets the decay time of the comb filter
|
||||||
|
*/ |
||||||
|
inline void SetRevTime(float revtime) { rev_time_ = revtime; } |
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_, rev_time_, loop_time_, prvt_, coef_, max_loop_time_; |
||||||
|
float* buf_; |
||||||
|
size_t buf_pos_, mod_, max_size_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,343 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_FIRFILTER_H |
||||||
|
#define DSY_FIRFILTER_H |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <cstring> // for memset |
||||||
|
#include <cassert> |
||||||
|
#include <utility> |
||||||
|
|
||||||
|
#ifdef USE_ARM_DSP |
||||||
|
#include <arm_math.h> // required for platform-optimized version |
||||||
|
#endif |
||||||
|
|
||||||
|
/** @brief FIR Filter implementation, generic and ARM CMSIS DSP based
|
||||||
|
* @author Alexander Petrov-Savchenko (axp@soft-amp.com) |
||||||
|
* @date February 2021 |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/* use this as a template parameter to indicate user-provided memory storage */ |
||||||
|
#define FIRFILTER_USER_MEMORY 0, 0 |
||||||
|
|
||||||
|
/** Helper class that defines the memory model - internal or user-provided
|
||||||
|
* \param max_size - maximal filter length |
||||||
|
* \param max_block - maximal length of the block processing |
||||||
|
* if both parameters are 0, does NOT allocate any memory and instead |
||||||
|
* requires user-provided memory blocks to be passed as parameters. |
||||||
|
* |
||||||
|
* Not intended to be used directly, so constructor is not exposed |
||||||
|
*/ |
||||||
|
template <size_t max_size, size_t max_block> |
||||||
|
struct FIRMemory |
||||||
|
{ |
||||||
|
/* Public part of the API to be passed through to the FIR users */ |
||||||
|
public: |
||||||
|
/* Reset the internal filter state (but not the coefficients) */ |
||||||
|
void Reset() { memset(state_, 0, state_size_ * sizeof(state_[0])); } |
||||||
|
|
||||||
|
|
||||||
|
protected: |
||||||
|
FIRMemory() : state_{0}, coefs_{0}, size_(0) {} |
||||||
|
|
||||||
|
/* Expression for the maximum block size */ |
||||||
|
static constexpr size_t MaxBlock() { return max_block; } |
||||||
|
|
||||||
|
/** Configure the filter coefficients
|
||||||
|
* \param coefs - pointer to coefficients (tail-first order) |
||||||
|
* \param size - number of coefficients pointed by coefs (filter length) |
||||||
|
* \param reverse - flag to perform reversing of the filter |
||||||
|
* \return true if all conditions are met and the filter is configured |
||||||
|
*/ |
||||||
|
bool SetCoefs(const float coefs[], size_t size, bool reverse) |
||||||
|
{ |
||||||
|
/* truncate silently */ |
||||||
|
size_ = DSY_MIN(size, max_size); |
||||||
|
|
||||||
|
if(reverse) |
||||||
|
{ |
||||||
|
/* reverse the IR */ |
||||||
|
for(size_t i = 0; i < size_; i++) |
||||||
|
{ |
||||||
|
/* start from size, not size_! */ |
||||||
|
coefs_[i] = coefs[size - 1u - i]; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
/* just copy as is */ |
||||||
|
memcpy(coefs_, coefs, size_ * sizeof(coefs[0])); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
static constexpr size_t state_size_ = max_size + max_block - 1u; |
||||||
|
float state_[state_size_]; /*< Internal state buffer */ |
||||||
|
float coefs_[max_size]; /*< Filter coefficients */ |
||||||
|
size_t size_; /*< Active filter length (<= max_size) */ |
||||||
|
}; |
||||||
|
|
||||||
|
/* Specialization for user-provided memory */ |
||||||
|
template <> |
||||||
|
struct FIRMemory<FIRFILTER_USER_MEMORY> |
||||||
|
{ |
||||||
|
/* Public part of the API to be passed through to the FIRFilter user */ |
||||||
|
public: |
||||||
|
/** Set user-provided state buffer
|
||||||
|
* \param state - pointer to the allocated memory block |
||||||
|
* \param length - length of the provided memory block (in elements) |
||||||
|
* The length should be determined as follows
|
||||||
|
* length >= max_filter_size + max_processing_block - 1 |
||||||
|
*/ |
||||||
|
void SetStateBuffer(float state[], size_t length) |
||||||
|
{ |
||||||
|
state_ = state; |
||||||
|
state_size_ = length; |
||||||
|
} |
||||||
|
/* Reset the internal filter state (but not the coefficients) */ |
||||||
|
void Reset() |
||||||
|
{ |
||||||
|
assert(nullptr != state_); |
||||||
|
assert(0 != state_size_); |
||||||
|
if(nullptr != state_) |
||||||
|
{ |
||||||
|
memset(state_, 0, state_size_ * sizeof(state_[0])); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
FIRMemory() : state_(nullptr), coefs_(nullptr), size_(0), state_size_(0) {} |
||||||
|
|
||||||
|
/* Expression for the maximum processing block size currently supported */ |
||||||
|
size_t MaxBlock() const |
||||||
|
{ |
||||||
|
return state_size_ + 1u > size_ ? state_size_ + 1u - size_ : 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** Configure the filter coefficients
|
||||||
|
* \param coefs - pointer to coefficients (tail-first order) |
||||||
|
* \param size - number of coefficients pointed by coefs (filter length) |
||||||
|
* \param reverse - flag to perform reversing of the filter |
||||||
|
* \return true if all conditions are met and the filter is configured |
||||||
|
*/ |
||||||
|
bool SetCoefs(const float coefs[], size_t size, bool reverse) |
||||||
|
{ |
||||||
|
/* reversing of external IR is not supported*/ |
||||||
|
assert(false == reverse); |
||||||
|
assert(nullptr != coefs || 0 == size); |
||||||
|
|
||||||
|
if(false == reverse && (nullptr != coefs || 0 == size)) |
||||||
|
{ |
||||||
|
coefs_ = coefs; |
||||||
|
size_ = size; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/* Internal member variables */ |
||||||
|
|
||||||
|
float* state_; /*< Internal state buffer */ |
||||||
|
const float* coefs_; /*< Filter coefficients */ |
||||||
|
size_t size_; /*< number of filter coefficients */ |
||||||
|
size_t state_size_; /*< length of the state buffer */ |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
/** Generic FIR implementation is always available
|
||||||
|
* \param max_size - maximal filter length |
||||||
|
* \param max_block - maximal block size for ProcessBlock() |
||||||
|
* if both parameters are 0 (via FIRFILTER_USER_MEMORY macro) |
||||||
|
* Assumes the user will provide own memory buffers |
||||||
|
* via SetIR() and SetStateBuffer() functions |
||||||
|
* Otherwise statically allocates the necessary buffers itself |
||||||
|
*/ |
||||||
|
template <size_t max_size, size_t max_block> |
||||||
|
class FIRFilterImplGeneric : public FIRMemory<max_size, max_block> |
||||||
|
{ |
||||||
|
private: |
||||||
|
using FIRMem = FIRMemory<max_size, max_block>; // just a shorthand
|
||||||
|
|
||||||
|
public: |
||||||
|
/* Default constructor */ |
||||||
|
FIRFilterImplGeneric() {} |
||||||
|
|
||||||
|
/* Reset filter state (but not the coefficients) */ |
||||||
|
using FIRMem::Reset; |
||||||
|
|
||||||
|
/* FIR Latency is always 0, but API is unified with FFT and fast convolution */ |
||||||
|
static constexpr size_t GetLatency() { return 0; } |
||||||
|
|
||||||
|
/* Process one sample at a time */ |
||||||
|
float Process(float in) |
||||||
|
{ |
||||||
|
assert(size_ > 0u); |
||||||
|
/* Feed data into the buffer */ |
||||||
|
state_[size_ - 1u] = in; |
||||||
|
|
||||||
|
/* Convolution loop */ |
||||||
|
float acc(0); |
||||||
|
for(size_t i = 0; i < size_ - 1; i++) |
||||||
|
{ |
||||||
|
acc += state_[i] * coefs_[i]; |
||||||
|
/** Shift the state simulatenously
|
||||||
|
* (note: better solutions are available)
|
||||||
|
*/ |
||||||
|
state_[i] = state_[1 + i]; |
||||||
|
} |
||||||
|
acc += in * coefs_[size_ - 1u]; |
||||||
|
|
||||||
|
return acc; |
||||||
|
} |
||||||
|
|
||||||
|
/* Process a block of data */ |
||||||
|
void ProcessBlock(const float* pSrc, float* pDst, size_t block) |
||||||
|
{ |
||||||
|
/* be sure to run debug version from time to time */ |
||||||
|
assert(block <= FIRMem::MaxBlock()); |
||||||
|
assert(size_ > 0u); |
||||||
|
assert(nullptr != pSrc); |
||||||
|
assert(nullptr != pDst); |
||||||
|
|
||||||
|
/* Process the block of data */ |
||||||
|
for(size_t j = 0; j < block; j++) |
||||||
|
{ |
||||||
|
/* Feed data into the buffer */ |
||||||
|
state_[size_ - 1u + j] = pSrc[j]; |
||||||
|
|
||||||
|
/* Convolution loop */ |
||||||
|
float acc = 0.0f; |
||||||
|
for(size_t i = 0; i < size_; i++) |
||||||
|
{ |
||||||
|
acc += state_[j + i] * coefs_[i]; |
||||||
|
} |
||||||
|
|
||||||
|
/* Write output */ |
||||||
|
pDst[j] = acc; |
||||||
|
} |
||||||
|
|
||||||
|
/* Copy data tail for the next block */ |
||||||
|
for(size_t i = 0; i < size_ - 1u; i++) |
||||||
|
{ |
||||||
|
state_[i] = state_[block + i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Set filter coefficients (aka Impulse Response)
|
||||||
|
* Coefficients need to be in reversed order (tail-first) |
||||||
|
* If internal storage is used, makes a local copy |
||||||
|
* and allows reversing the impulse response |
||||||
|
*/ |
||||||
|
bool SetIR(const float* ir, size_t len, bool reverse) |
||||||
|
{ |
||||||
|
/* Function order is important */ |
||||||
|
const bool result = FIRMem::SetCoefs(ir, len, reverse); |
||||||
|
Reset(); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/* Create an alias to comply with DaisySP API conventions */ |
||||||
|
template <typename... Args> |
||||||
|
inline auto Init(Args&&... args) |
||||||
|
-> decltype(SetIR(std::forward<Args>(args)...)) |
||||||
|
{ |
||||||
|
return SetIR(std::forward<Args>(args)...); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
protected: |
||||||
|
using FIRMem::coefs_; /*< FIR coefficients buffer or pointer */ |
||||||
|
using FIRMem::size_; /*< FIR length */ |
||||||
|
using FIRMem::state_; /*< FIR state buffer or pointer */ |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
#if(defined(USE_ARM_DSP) && defined(__arm__)) |
||||||
|
|
||||||
|
/** ARM-specific FIR implementation, expose only on __arm__ platforms
|
||||||
|
* \param max_size - maximal filter length |
||||||
|
* \param max_block - maximal block size for ProcessBlock() |
||||||
|
* if both parameters are 0 (via FIRFILTER_USER_MEMORY macro) |
||||||
|
* Assumes the user will provide own memory buffers |
||||||
|
* Otherwise statically allocates the necessary buffers |
||||||
|
*/ |
||||||
|
template <size_t max_size, size_t max_block> |
||||||
|
class FIRFilterImplARM : public FIRMemory<max_size, max_block> |
||||||
|
{ |
||||||
|
private: |
||||||
|
using FIRMem = FIRMemory<max_size, max_block>; // just a shorthand
|
||||||
|
|
||||||
|
public: |
||||||
|
/* Default constructor */ |
||||||
|
FIRFilterImplARM() : fir_{0} {} |
||||||
|
|
||||||
|
/* Reset filter state (but not the coefficients) */ |
||||||
|
using FIRMem::Reset; |
||||||
|
|
||||||
|
/* FIR Latency is always 0, but API is unified with FFT and FastConv */ |
||||||
|
static constexpr size_t GetLatency() { return 0; } |
||||||
|
|
||||||
|
/* Process one sample at a time */ |
||||||
|
float Process(float in) |
||||||
|
{ |
||||||
|
float out(0); |
||||||
|
arm_fir_f32(&fir_, &in, &out, 1); |
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
/* Process a block of data */ |
||||||
|
void ProcessBlock(float* pSrc, float* pDst, size_t block) |
||||||
|
{ |
||||||
|
assert(block <= FIRMem::MaxBlock()); |
||||||
|
arm_fir_f32(&fir_, pSrc, pDst, block); |
||||||
|
} |
||||||
|
|
||||||
|
/** Set filter coefficients (aka Impulse Response)
|
||||||
|
* Coefficients need to be in reversed order (tail-first) |
||||||
|
* If internal storage is used, makes a local copy |
||||||
|
* and allows reversing the impulse response |
||||||
|
*/ |
||||||
|
bool SetIR(const float* ir, size_t len, bool reverse) |
||||||
|
{ |
||||||
|
/* Function order is important */ |
||||||
|
const bool result = FIRMem::SetCoefs(ir, len, reverse); |
||||||
|
arm_fir_init_f32(&fir_, len, (float*)coefs_, state_, max_block); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/* Create an alias to comply with DaisySP API conventions */ |
||||||
|
template <typename... Args> |
||||||
|
inline auto Init(Args&&... args) |
||||||
|
-> decltype(SetIR(std::forward<Args>(args)...)) |
||||||
|
{ |
||||||
|
return SetIR(std::forward<Args>(args)...); |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
arm_fir_instance_f32 fir_; /*< ARM CMSIS DSP library FIR filter instance */ |
||||||
|
using FIRMem::coefs_; /*< FIR coefficients buffer or pointer */ |
||||||
|
using FIRMem::size_; /*< FIR length*/ |
||||||
|
using FIRMem::state_; /*< FIR state buffer or pointer */ |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
/* default to ARM implementation */ |
||||||
|
template <size_t max_size, size_t max_block> |
||||||
|
using FIR = FIRFilterImplARM<max_size, max_block>; |
||||||
|
|
||||||
|
|
||||||
|
#else // USE_ARM_DSP
|
||||||
|
|
||||||
|
/* default to generic implementation */ |
||||||
|
template <size_t max_size, size_t max_block> |
||||||
|
using FIR = FIRFilterImplGeneric<max_size, max_block>; |
||||||
|
|
||||||
|
#endif // USE_ARM_DSP
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace daisysp
|
||||||
|
|
||||||
|
#endif // DSY_FIRFILTER_H
|
@ -0,0 +1,89 @@ |
|||||||
|
//
|
||||||
|
// Mode
|
||||||
|
//
|
||||||
|
// This code has been extracted from the soundpipe opcode "mode".
|
||||||
|
// It has been modified to work as a Daisy module.
|
||||||
|
//
|
||||||
|
// Originally extracted from Csound.
|
||||||
|
//
|
||||||
|
// Original Author(s): Francois Blanc, Steven Yi
|
||||||
|
// Year: 2001
|
||||||
|
// Location: Opcodes/biquad.c
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <math.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#define ROOT2 (1.4142135623730950488f) |
||||||
|
|
||||||
|
#ifndef M_PI |
||||||
|
#define M_PI 3.14159265358979323846f /* pi */ |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "mode.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Mode::Init(float sample_rate) |
||||||
|
{ |
||||||
|
freq_ = 500.0f; |
||||||
|
q_ = 50; |
||||||
|
|
||||||
|
xnm1_ = ynm1_ = ynm2_ = 0.0f; |
||||||
|
a0_ = a1_ = a2_ = d_ = 0.0f; |
||||||
|
lfq_ = lq_ = -1.0f; |
||||||
|
sr_ = sample_rate; |
||||||
|
} |
||||||
|
|
||||||
|
void Mode::Clear() |
||||||
|
{ |
||||||
|
xnm1_ = ynm1_ = ynm2_ = 0.0f; |
||||||
|
a0_ = a1_ = a2_ = 0.0f; |
||||||
|
d_ = 0.0f; |
||||||
|
lfq_ = -1.0f; |
||||||
|
lq_ = -1.0f; |
||||||
|
} |
||||||
|
|
||||||
|
float Mode::Process(float in) |
||||||
|
{ |
||||||
|
float out; |
||||||
|
float lfq = lfq_, lq = lq_; |
||||||
|
float xn, yn, a0 = a0_, a1 = a1_, a2 = a2_, d = d_; |
||||||
|
float xnm1 = xnm1_, ynm1 = ynm1_, ynm2 = ynm2_; |
||||||
|
float kfq = freq_; |
||||||
|
float kq = q_; |
||||||
|
|
||||||
|
if(lfq != kfq || lq != kq) |
||||||
|
{ |
||||||
|
float kfreq = kfq * (2.0f * (float)M_PI); |
||||||
|
float kalpha = (sr_ / kfreq); |
||||||
|
float kbeta = kalpha * kalpha; |
||||||
|
d = 0.5f * kalpha; |
||||||
|
lq = kq; |
||||||
|
lfq = kfq; |
||||||
|
a0 = 1.0f / (kbeta + d / kfreq); |
||||||
|
a1 = a0 * (1.0f - 2.0f * kbeta); |
||||||
|
a2 = a0 * (kbeta - d / kq); |
||||||
|
} |
||||||
|
xn = in; |
||||||
|
|
||||||
|
yn = a0 * xnm1 - a1 * ynm1 - a2 * ynm2; |
||||||
|
|
||||||
|
xnm1 = xn; |
||||||
|
ynm2 = ynm1; |
||||||
|
ynm1 = yn; |
||||||
|
|
||||||
|
yn = yn * d; |
||||||
|
|
||||||
|
out = yn; |
||||||
|
xnm1_ = xnm1; |
||||||
|
ynm1_ = ynm1; |
||||||
|
ynm2_ = ynm2; |
||||||
|
lfq_ = lfq; |
||||||
|
lq_ = lq; |
||||||
|
d_ = d; |
||||||
|
a0_ = a0; |
||||||
|
a1_ = a1; |
||||||
|
a2_ = a2; |
||||||
|
return out; |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DAISY_MODE |
||||||
|
#define DAISY_MODE |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Resonant Modal Filter
|
||||||
|
|
||||||
|
Extracted from soundpipe to work as a Daisy Module, |
||||||
|
|
||||||
|
originally extracted from csound by Paul Batchelor. |
||||||
|
|
||||||
|
Original Author(s): Francois Blanc, Steven Yi |
||||||
|
|
||||||
|
Year: 2001 |
||||||
|
|
||||||
|
Location: Opcodes/biquad.c (csound) |
||||||
|
*/ |
||||||
|
class Mode |
||||||
|
{ |
||||||
|
public: |
||||||
|
Mode() {} |
||||||
|
~Mode() {} |
||||||
|
/** Initializes the instance of the module.
|
||||||
|
sample_rate: frequency of the audio engine in Hz |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Processes one input sample through the filter, and returns the output.
|
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
/** Clears the filter, returning the output to 0.0
|
||||||
|
*/ |
||||||
|
void Clear(); |
||||||
|
|
||||||
|
/** Sets the resonant frequency of the modal filter.
|
||||||
|
Range: Any frequency such that sample_rate / freq < PI (about 15.2kHz at 48kHz) |
||||||
|
*/ |
||||||
|
inline void SetFreq(float freq) { freq_ = freq; } |
||||||
|
/** Sets the quality factor of the filter.
|
||||||
|
Range: Positive Numbers (Good values range from 70 to 1400) |
||||||
|
*/ |
||||||
|
inline void SetQ(float q) { q_ = q; } |
||||||
|
|
||||||
|
private: |
||||||
|
float freq_, q_; |
||||||
|
float xnm1_, ynm1_, ynm2_, a0_, a1_, a2_; |
||||||
|
float d_, lfq_, lq_, sr_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,105 @@ |
|||||||
|
#include "moogladder.h" |
||||||
|
#include "../utility/dsp.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
float MoogLadder::my_tanh(float x) |
||||||
|
{ |
||||||
|
int sign = 1; |
||||||
|
if(x < 0) |
||||||
|
{ |
||||||
|
sign = -1; |
||||||
|
x = -x; |
||||||
|
return x * sign; |
||||||
|
} |
||||||
|
else if(x >= 4.0f) |
||||||
|
{ |
||||||
|
return sign; |
||||||
|
} |
||||||
|
else if(x < 0.5f) |
||||||
|
{ |
||||||
|
return x * sign; |
||||||
|
} |
||||||
|
return sign * tanhf(x); |
||||||
|
} |
||||||
|
|
||||||
|
void MoogLadder::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
istor_ = 0.0f; |
||||||
|
res_ = 0.4f; |
||||||
|
freq_ = 1000.0f; |
||||||
|
|
||||||
|
for(int i = 0; i < 6; i++) |
||||||
|
{ |
||||||
|
delay_[i] = 0.0; |
||||||
|
tanhstg_[i % 3] = 0.0; |
||||||
|
} |
||||||
|
|
||||||
|
old_freq_ = 0.0f; |
||||||
|
old_res_ = -1.0f; |
||||||
|
} |
||||||
|
|
||||||
|
float MoogLadder::Process(float in) |
||||||
|
{ |
||||||
|
float freq = freq_; |
||||||
|
float res = res_; |
||||||
|
float res4; |
||||||
|
float* delay = delay_; |
||||||
|
float* tanhstg = tanhstg_; |
||||||
|
float stg[4]; |
||||||
|
float acr, tune; |
||||||
|
|
||||||
|
float THERMAL = 0.000025; |
||||||
|
|
||||||
|
if(res < 0) |
||||||
|
{ |
||||||
|
res = 0; |
||||||
|
} |
||||||
|
|
||||||
|
if(old_freq_ != freq || old_res_ != res) |
||||||
|
{ |
||||||
|
float f, fc, fc2, fc3, fcr; |
||||||
|
old_freq_ = freq; |
||||||
|
fc = (freq / sample_rate_); |
||||||
|
f = 0.5f * fc; |
||||||
|
fc2 = fc * fc; |
||||||
|
fc3 = fc2 * fc2; |
||||||
|
|
||||||
|
fcr = 1.8730f * fc3 + 0.4955f * fc2 - 0.6490f * fc + 0.9988f; |
||||||
|
acr = -3.9364f * fc2 + 1.8409f * fc + 0.9968f; |
||||||
|
tune = (1.0f - expf(-((2 * PI_F) * f * fcr))) / THERMAL; |
||||||
|
|
||||||
|
old_res_ = res; |
||||||
|
old_acr_ = acr; |
||||||
|
old_tune_ = tune; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
res = old_res_; |
||||||
|
acr = old_acr_; |
||||||
|
tune = old_tune_; |
||||||
|
} |
||||||
|
|
||||||
|
res4 = 4.0f * res * acr; |
||||||
|
|
||||||
|
for(int j = 0; j < 2; j++) |
||||||
|
{ |
||||||
|
in -= res4 * delay[5]; |
||||||
|
delay[0] = stg[0] |
||||||
|
= delay[0] + tune * (my_tanh(in * THERMAL) - tanhstg[0]); |
||||||
|
for(int k = 1; k < 4; k++) |
||||||
|
{ |
||||||
|
in = stg[k - 1]; |
||||||
|
stg[k] = delay[k] |
||||||
|
+ tune |
||||||
|
* ((tanhstg[k - 1] = my_tanh(in * THERMAL)) |
||||||
|
- (k != 3 ? tanhstg[k] |
||||||
|
: my_tanh(delay[k] * THERMAL))); |
||||||
|
delay[k] = stg[k]; |
||||||
|
} |
||||||
|
delay[5] = (stg[3] + delay[4]) * 0.5f; |
||||||
|
delay[4] = stg[3]; |
||||||
|
} |
||||||
|
return delay[5]; |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_MOOGLADDER_H |
||||||
|
#define DSY_MOOGLADDER_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Moog ladder filter module
|
||||||
|
|
||||||
|
Ported from soundpipe |
||||||
|
|
||||||
|
Original author(s) : Victor Lazzarini, John ffitch (fast tanh), Bob Moog |
||||||
|
|
||||||
|
*/ |
||||||
|
class MoogLadder |
||||||
|
{ |
||||||
|
public: |
||||||
|
MoogLadder() {} |
||||||
|
~MoogLadder() {} |
||||||
|
/** Initializes the MoogLadder module.
|
||||||
|
sample_rate - The sample rate of the audio engine being run.
|
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
|
||||||
|
/** Processes the lowpass filter
|
||||||
|
*/ |
||||||
|
float Process(float in); |
||||||
|
|
||||||
|
/**
|
||||||
|
Sets the cutoff frequency or half-way point of the filter. |
||||||
|
Arguments |
||||||
|
- freq - frequency value in Hz. Range: Any positive value. |
||||||
|
*/ |
||||||
|
inline void SetFreq(float freq) { freq_ = freq; } |
||||||
|
/**
|
||||||
|
Sets the resonance of the filter. |
||||||
|
*/ |
||||||
|
inline void SetRes(float res) { res_ = res; } |
||||||
|
|
||||||
|
private: |
||||||
|
float istor_, res_, freq_, delay_[6], tanhstg_[3], old_freq_, old_res_, |
||||||
|
sample_rate_, old_acr_, old_tune_; |
||||||
|
float my_tanh(float x); |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,95 @@ |
|||||||
|
#include <string.h> |
||||||
|
#include <math.h> |
||||||
|
#include "nlfilt.h" |
||||||
|
#define OK 0 |
||||||
|
#define NOT_OK 1 |
||||||
|
#define FL (float) |
||||||
|
#define TANH tanhf |
||||||
|
#define MAX_DELAY (1024) |
||||||
|
#define MAXAMP (FL(64000.0f)) |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void NlFilt::Init() |
||||||
|
{ |
||||||
|
point_ = 0; // Set delay pointer
|
||||||
|
Set(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* Revised version due to Risto Holopainen 12 Mar 2004 */ |
||||||
|
/* Y{n} =tanh(a Y{n-1} + b Y{n-2} + d Y^2{n-L} + X{n} - C) */ |
||||||
|
|
||||||
|
void NlFilt::ProcessBlock(float *in, float *out, size_t size) |
||||||
|
{ |
||||||
|
uint32_t offset = 0; |
||||||
|
uint32_t n, nsmps = size; |
||||||
|
int32_t point = point_; |
||||||
|
int32_t nm1 = point; |
||||||
|
int32_t nm2 = point - 1; |
||||||
|
int32_t nmL; |
||||||
|
float ynm1, ynm2, ynmL; |
||||||
|
float a = a_, b = b_, d = d_, C = C_; |
||||||
|
float * fp = (float *)delay_; |
||||||
|
float L = L_; |
||||||
|
float maxamp, dvmaxamp, maxampd2; |
||||||
|
|
||||||
|
/* L is k-rate so need to check */ |
||||||
|
if(L < FL(1.0f)) |
||||||
|
L = FL(1.0f); |
||||||
|
else if(L >= MAX_DELAY) |
||||||
|
{ |
||||||
|
L = (float)MAX_DELAY; |
||||||
|
} |
||||||
|
nmL = point - (int32_t)(L)-1; |
||||||
|
if(nm1 < 0) |
||||||
|
nm1 += MAX_DELAY; /* Deal with the wrapping */ |
||||||
|
if(nm2 < 0) |
||||||
|
nm2 += MAX_DELAY; |
||||||
|
if(nmL < 0) |
||||||
|
nmL += MAX_DELAY; |
||||||
|
ynm1 = fp[nm1]; /* Pick up running values */ |
||||||
|
ynm2 = fp[nm2]; |
||||||
|
ynmL = fp[nmL]; |
||||||
|
nsmps = size; |
||||||
|
maxamp = 1.935125f; |
||||||
|
dvmaxamp = FL(1.0f) / maxamp; |
||||||
|
maxampd2 = maxamp * FL(0.5F); |
||||||
|
for(n = offset; n < nsmps; n++) |
||||||
|
{ |
||||||
|
float yn; |
||||||
|
float outv; |
||||||
|
yn = a * ynm1 + b * ynm2 + d * ynmL * ynmL - C; |
||||||
|
yn += in[n] * dvmaxamp; /* Must work in small amplitudes */ |
||||||
|
outv = yn * maxampd2; /* Write output */ |
||||||
|
if(outv > maxamp) |
||||||
|
outv = maxampd2; |
||||||
|
else if(outv < -maxamp) |
||||||
|
outv = -maxampd2; |
||||||
|
out[n] = outv; |
||||||
|
if(++point == MAX_DELAY) |
||||||
|
{ |
||||||
|
point = 0; |
||||||
|
} |
||||||
|
yn = TANH(yn); |
||||||
|
fp[point] = yn; /* and delay line */ |
||||||
|
if(++nmL == MAX_DELAY) |
||||||
|
{ |
||||||
|
nmL = 0; |
||||||
|
} |
||||||
|
ynm2 = ynm1; /* Shuffle along */ |
||||||
|
ynm1 = yn; |
||||||
|
ynmL = fp[nmL]; |
||||||
|
} |
||||||
|
point_ = point; |
||||||
|
} |
||||||
|
|
||||||
|
int32_t NlFilt::Set() |
||||||
|
{ |
||||||
|
// Initializes delay buffer.
|
||||||
|
for(size_t i = 0; i < MAX_DELAY; i++) |
||||||
|
{ |
||||||
|
delay_[i] = 0; |
||||||
|
} |
||||||
|
return OK; |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_NLFILT_H |
||||||
|
#define DSY_NLFILT_H |
||||||
|
#include <stdlib.h> |
||||||
|
#include <stdint.h> |
||||||
|
#define DSY_NLFILT_MAX_DELAY 1024 |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Non-linear filter
|
||||||
|
|
||||||
|
port by: Stephen Hensley, December 2019 |
||||||
|
|
||||||
|
The four 5-coefficients: a, b, d, C, and L are used to configure different filter types. |
||||||
|
|
||||||
|
Structure for Dobson/Fitch nonlinear filter
|
||||||
|
|
||||||
|
Revised Formula from Risto Holopainen 12 Mar 2004 |
||||||
|
|
||||||
|
`Y{n} =tanh(a Y{n-1} + b Y{n-2} + d Y^2{n-L} + X{n} - C)` |
||||||
|
|
||||||
|
Though traditional filter types can be made,
|
||||||
|
the effect will always respond differently to different input. |
||||||
|
|
||||||
|
This Source is a heavily modified version of the original |
||||||
|
source from Csound. |
||||||
|
|
||||||
|
\todo make this work on a single sample instead of just on blocks at a time. |
||||||
|
*/ |
||||||
|
class NlFilt |
||||||
|
{ |
||||||
|
public: |
||||||
|
/** Initializes the NlFilt object.
|
||||||
|
*/ |
||||||
|
void Init(); |
||||||
|
|
||||||
|
/** Process the array pointed to by \*in and updates the output to \*out;
|
||||||
|
This works on a block of audio at once, the size of which is set with the size.
|
||||||
|
*/ |
||||||
|
void ProcessBlock(float *in, float *out, size_t size); |
||||||
|
|
||||||
|
|
||||||
|
/** inputs these are the five coefficients for the filter.
|
||||||
|
*/ |
||||||
|
inline void SetCoefficients(float a, float b, float d, float C, float L) |
||||||
|
{ |
||||||
|
a_ = a; |
||||||
|
b_ = b; |
||||||
|
d_ = d; |
||||||
|
C_ = C; |
||||||
|
L_ = L; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Set Coefficient a
|
||||||
|
*/ |
||||||
|
inline void SetA(float a) { a_ = a; } |
||||||
|
/** Set Coefficient b
|
||||||
|
*/ |
||||||
|
inline void SetB(float b) { b_ = b; } |
||||||
|
/** Set Coefficient d
|
||||||
|
*/ |
||||||
|
inline void SetD(float d) { d_ = d; } |
||||||
|
/** Set Coefficient C
|
||||||
|
*/ |
||||||
|
inline void SetC(float C) { C_ = C; } |
||||||
|
/** Set Coefficient L
|
||||||
|
*/ |
||||||
|
inline void SetL(float L) { L_ = L; } |
||||||
|
|
||||||
|
private: |
||||||
|
int32_t Set(); |
||||||
|
|
||||||
|
float in_, a_, b_, d_, C_, L_; |
||||||
|
float delay_[DSY_NLFILT_MAX_DELAY]; |
||||||
|
int32_t point_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,86 @@ |
|||||||
|
#include <math.h> |
||||||
|
#include "svf.h" |
||||||
|
#include "../utility/dsp.h" |
||||||
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y)) |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Svf::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sr_ = sample_rate; |
||||||
|
fc_ = 200.0f; |
||||||
|
res_ = 0.5f; |
||||||
|
drive_ = 0.5f; |
||||||
|
pre_drive_ = 0.5f; |
||||||
|
freq_ = 0.25f; |
||||||
|
damp_ = 0.0f; |
||||||
|
notch_ = 0.0f; |
||||||
|
low_ = 0.0f; |
||||||
|
high_ = 0.0f; |
||||||
|
band_ = 0.0f; |
||||||
|
peak_ = 0.0f; |
||||||
|
input_ = 0.0f; |
||||||
|
out_notch_ = 0.0f; |
||||||
|
out_low_ = 0.0f; |
||||||
|
out_high_ = 0.0f; |
||||||
|
out_peak_ = 0.0f; |
||||||
|
out_band_ = 0.0f; |
||||||
|
fc_max_ = sr_ / 3.f; |
||||||
|
} |
||||||
|
|
||||||
|
void Svf::Process(float in) |
||||||
|
{ |
||||||
|
input_ = in; |
||||||
|
// first pass
|
||||||
|
notch_ = input_ - damp_ * band_; |
||||||
|
low_ = low_ + freq_ * band_; |
||||||
|
high_ = notch_ - low_; |
||||||
|
band_ = freq_ * high_ + band_ - drive_ * band_ * band_ * band_; |
||||||
|
// take first sample of output
|
||||||
|
out_low_ = 0.5f * low_; |
||||||
|
out_high_ = 0.5f * high_; |
||||||
|
out_band_ = 0.5f * band_; |
||||||
|
out_peak_ = 0.5f * (low_ - high_); |
||||||
|
out_notch_ = 0.5f * notch_; |
||||||
|
// second pass
|
||||||
|
notch_ = input_ - damp_ * band_; |
||||||
|
low_ = low_ + freq_ * band_; |
||||||
|
high_ = notch_ - low_; |
||||||
|
band_ = freq_ * high_ + band_ - drive_ * band_ * band_ * band_; |
||||||
|
// average second pass outputs
|
||||||
|
out_low_ += 0.5f * low_; |
||||||
|
out_high_ += 0.5f * high_; |
||||||
|
out_band_ += 0.5f * band_; |
||||||
|
out_peak_ += 0.5f * (low_ - high_); |
||||||
|
out_notch_ += 0.5f * notch_; |
||||||
|
} |
||||||
|
|
||||||
|
void Svf::SetFreq(float f) |
||||||
|
{ |
||||||
|
fc_ = fclamp(f, 1.0e-6, fc_max_); |
||||||
|
// Set Internal Frequency for fc_
|
||||||
|
freq_ = 2.0f |
||||||
|
* sinf(PI_F |
||||||
|
* MIN(0.25f, |
||||||
|
fc_ / (sr_ * 2.0f))); // fs*2 because double sampled
|
||||||
|
// recalculate damp
|
||||||
|
damp_ = MIN(2.0f * (1.0f - powf(res_, 0.25f)), |
||||||
|
MIN(2.0f, 2.0f / freq_ - freq_ * 0.5f)); |
||||||
|
} |
||||||
|
|
||||||
|
void Svf::SetRes(float r) |
||||||
|
{ |
||||||
|
float res = fclamp(r, 0.f, 1.f); |
||||||
|
res_ = res; |
||||||
|
// recalculate damp
|
||||||
|
damp_ = MIN(2.0f * (1.0f - powf(res_, 0.25f)), |
||||||
|
MIN(2.0f, 2.0f / freq_ - freq_ * 0.5f)); |
||||||
|
drive_ = pre_drive_ * res_; |
||||||
|
} |
||||||
|
|
||||||
|
void Svf::SetDrive(float d) |
||||||
|
{ |
||||||
|
float drv = fclamp(d * 0.1f, 0.f, 1.f); |
||||||
|
pre_drive_ = drv; |
||||||
|
drive_ = pre_drive_ * res_; |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_SVF_H |
||||||
|
#define DSY_SVF_H |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Double Sampled, Stable State Variable Filter
|
||||||
|
|
||||||
|
Credit to Andrew Simper from musicdsp.org |
||||||
|
|
||||||
|
This is his "State Variable Filter (Double Sampled, Stable)" |
||||||
|
|
||||||
|
Additional thanks to Laurent de Soras for stability limit, and
|
||||||
|
Stefan Diedrichsen for the correct notch output |
||||||
|
|
||||||
|
Ported by: Stephen Hensley |
||||||
|
*/ |
||||||
|
class Svf |
||||||
|
{ |
||||||
|
public: |
||||||
|
Svf() {} |
||||||
|
~Svf() {} |
||||||
|
/** Initializes the filter
|
||||||
|
float sample_rate - sample rate of the audio engine being run, and the frequency that the Process function will be called. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Process the input signal, updating all of the outputs. |
||||||
|
*/ |
||||||
|
void Process(float in); |
||||||
|
|
||||||
|
|
||||||
|
/** sets the frequency of the cutoff frequency.
|
||||||
|
f must be between 0.0 and sample_rate / 3 |
||||||
|
*/ |
||||||
|
void SetFreq(float f); |
||||||
|
|
||||||
|
/** sets the resonance of the filter.
|
||||||
|
Must be between 0.0 and 1.0 to ensure stability. |
||||||
|
*/ |
||||||
|
void SetRes(float r); |
||||||
|
|
||||||
|
/** sets the drive of the filter
|
||||||
|
affects the response of the resonance of the filter |
||||||
|
*/ |
||||||
|
void SetDrive(float d); |
||||||
|
/** lowpass output
|
||||||
|
\return low pass output of the filter |
||||||
|
*/ |
||||||
|
inline float Low() { return out_low_; } |
||||||
|
/** highpass output
|
||||||
|
\return high pass output of the filter |
||||||
|
*/ |
||||||
|
inline float High() { return out_high_; } |
||||||
|
/** bandpass output
|
||||||
|
\return band pass output of the filter |
||||||
|
*/ |
||||||
|
inline float Band() { return out_band_; } |
||||||
|
/** notchpass output
|
||||||
|
\return notch pass output of the filter |
||||||
|
*/ |
||||||
|
inline float Notch() { return out_notch_; } |
||||||
|
/** peak output
|
||||||
|
\return peak output of the filter |
||||||
|
*/ |
||||||
|
inline float Peak() { return out_peak_; } |
||||||
|
|
||||||
|
private: |
||||||
|
float sr_, fc_, res_, drive_, freq_, damp_; |
||||||
|
float notch_, low_, high_, band_, peak_; |
||||||
|
float input_; |
||||||
|
float out_low_, out_high_, out_band_, out_peak_, out_notch_; |
||||||
|
float pre_drive_, fc_max_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,34 @@ |
|||||||
|
#include "tone.h" |
||||||
|
#include <math.h> |
||||||
|
#include "../utility/dsp.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Tone::Init(float sample_rate) |
||||||
|
{ |
||||||
|
prevout_ = 0.0f; |
||||||
|
freq_ = 100.0f; |
||||||
|
c1_ = 0.5f; |
||||||
|
c2_ = 0.5f; |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
} |
||||||
|
|
||||||
|
float Tone::Process(float &in) |
||||||
|
{ |
||||||
|
float out; |
||||||
|
|
||||||
|
out = c1_ * in + c2_ * prevout_; |
||||||
|
prevout_ = out; |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
void Tone::CalculateCoefficients() |
||||||
|
{ |
||||||
|
float b, c1, c2; |
||||||
|
b = 2.0f - cosf(TWOPI_F * freq_ / sample_rate_); |
||||||
|
c2 = b - sqrtf(b * b - 1.0f); |
||||||
|
c1 = 1.0f - c2; |
||||||
|
c1_ = c1; |
||||||
|
c2_ = c2; |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_TONE_H |
||||||
|
#define DSY_TONE_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** A first-order recursive low-pass filter with variable frequency response.
|
||||||
|
*/ |
||||||
|
class Tone |
||||||
|
{ |
||||||
|
public: |
||||||
|
Tone() {} |
||||||
|
~Tone() {} |
||||||
|
/** Initializes the Tone module.
|
||||||
|
sample_rate - The sample rate of the audio engine being run.
|
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
|
||||||
|
/** Processes one sample through the filter and returns one sample.
|
||||||
|
in - input signal
|
||||||
|
*/ |
||||||
|
float Process(float &in); |
||||||
|
|
||||||
|
/** Sets the cutoff frequency or half-way point of the filter.
|
||||||
|
|
||||||
|
\param freq - frequency value in Hz. Range: Any positive value. |
||||||
|
*/ |
||||||
|
inline void SetFreq(float &freq) |
||||||
|
{ |
||||||
|
freq_ = freq; |
||||||
|
CalculateCoefficients(); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
\return the current value for the cutoff frequency or half-way point of the filter. |
||||||
|
*/ |
||||||
|
inline float GetFreq() { return freq_; } |
||||||
|
|
||||||
|
private: |
||||||
|
void CalculateCoefficients(); |
||||||
|
float out_, prevout_, in_, freq_, c1_, c2_, sample_rate_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,59 @@ |
|||||||
|
#include <random> |
||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "clockednoise.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void ClockedNoise::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
phase_ = 0.0f; |
||||||
|
sample_ = 0.0f; |
||||||
|
next_sample_ = 0.0f; |
||||||
|
frequency_ = 0.001f; |
||||||
|
} |
||||||
|
|
||||||
|
float ClockedNoise::Process() |
||||||
|
{ |
||||||
|
float next_sample = next_sample_; |
||||||
|
float sample = sample_; |
||||||
|
|
||||||
|
float this_sample = next_sample; |
||||||
|
next_sample = 0.0f; |
||||||
|
|
||||||
|
const float raw_sample = rand() * kRandFrac * 2.0f - 1.0f; |
||||||
|
float raw_amount = 4.0f * (frequency_ - 0.25f); |
||||||
|
raw_amount = fclamp(raw_amount, 0.0f, 1.0f); |
||||||
|
|
||||||
|
phase_ += frequency_; |
||||||
|
|
||||||
|
if(phase_ >= 1.0f) |
||||||
|
{ |
||||||
|
phase_ -= 1.0f; |
||||||
|
float t = phase_ / frequency_; |
||||||
|
float new_sample = raw_sample; |
||||||
|
float discontinuity = new_sample - sample; |
||||||
|
this_sample += discontinuity * ThisBlepSample(t); |
||||||
|
next_sample += discontinuity * NextBlepSample(t); |
||||||
|
sample = new_sample; |
||||||
|
} |
||||||
|
|
||||||
|
next_sample += sample; |
||||||
|
next_sample_ = next_sample; |
||||||
|
sample_ = sample; |
||||||
|
|
||||||
|
return this_sample + raw_amount * (raw_sample - this_sample); |
||||||
|
} |
||||||
|
|
||||||
|
void ClockedNoise::SetFreq(float freq) |
||||||
|
{ |
||||||
|
freq = freq / sample_rate_; |
||||||
|
freq = fclamp(freq, 0.0f, 1.0f); |
||||||
|
frequency_ = freq; |
||||||
|
} |
||||||
|
|
||||||
|
void ClockedNoise::Sync() |
||||||
|
{ |
||||||
|
phase_ = 1.0f; |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_CLOCKEDNOISE_H |
||||||
|
#define DSY_CLOCKEDNOISE_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file clockednoise.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Clocked Noise Module |
||||||
|
@author Ported by Ben Sergentanis
|
||||||
|
@date Jan 2021
|
||||||
|
Noise processed by a sample and hold running at a target frequency. \n \n |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/noise/clocked_noise.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class ClockedNoise |
||||||
|
{ |
||||||
|
public: |
||||||
|
ClockedNoise() {} |
||||||
|
~ClockedNoise() {} |
||||||
|
|
||||||
|
/** Initialize module
|
||||||
|
\param sample_rate Audio engine sample rate |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the next floating point sample */ |
||||||
|
float Process(); |
||||||
|
|
||||||
|
/** Set the frequency at which the next sample is generated.
|
||||||
|
\param freq Frequency in Hz |
||||||
|
*/ |
||||||
|
void SetFreq(float freq); |
||||||
|
|
||||||
|
/** Calling this forces another random float to be generated */ |
||||||
|
void Sync(); |
||||||
|
|
||||||
|
private: |
||||||
|
// Oscillator state.
|
||||||
|
float phase_; |
||||||
|
float sample_; |
||||||
|
float next_sample_; |
||||||
|
|
||||||
|
// For interpolation of parameters.
|
||||||
|
float frequency_; |
||||||
|
|
||||||
|
float sample_rate_; |
||||||
|
|
||||||
|
static constexpr float kRandFrac = 1.f / (float)RAND_MAX; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,54 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_DUST_H |
||||||
|
#define DSY_DUST_H |
||||||
|
#include <cstdlib> |
||||||
|
#include <random> |
||||||
|
#include "Utility/dsp.h" |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file dust.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Dust Module |
||||||
|
@author Ported by Ben Sergentanis
|
||||||
|
@date Jan 2021
|
||||||
|
Randomly Clocked Samples \n \n |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/noise/dust.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
|
||||||
|
*/ |
||||||
|
class Dust |
||||||
|
{ |
||||||
|
public: |
||||||
|
Dust() {} |
||||||
|
~Dust() {} |
||||||
|
|
||||||
|
void Init() { SetDensity(.5f); } |
||||||
|
|
||||||
|
float Process() |
||||||
|
{ |
||||||
|
float inv_density = 1.0f / density_; |
||||||
|
float u = rand() * kRandFrac; |
||||||
|
if(u < density_) |
||||||
|
{ |
||||||
|
return u * inv_density; |
||||||
|
} |
||||||
|
return 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
void SetDensity(float density) |
||||||
|
{ |
||||||
|
density_ = fclamp(density, 0.f, 1.f); |
||||||
|
density_ = density_ * .3f; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
float density_; |
||||||
|
static constexpr float kRandFrac = 1.f / (float)RAND_MAX; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,82 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_FRACTAL_H |
||||||
|
#define DSY_FRACTAL_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file fractal_noise.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Fractal Noise, stacks octaves of a noise source. |
||||||
|
@author Ported by Ben Sergentanis
|
||||||
|
@date Jan 2021
|
||||||
|
T is the noise source to use. T must have SetFreq() and Init(sample_rate) functions. \n |
||||||
|
Order is the number of noise sources to stack. \n \n |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/noise/fractal_random_generator.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
|
||||||
|
*/ |
||||||
|
template <typename T, int order> |
||||||
|
class FractalRandomGenerator |
||||||
|
{ |
||||||
|
public: |
||||||
|
FractalRandomGenerator() {} |
||||||
|
~FractalRandomGenerator() {} |
||||||
|
|
||||||
|
/** Initialize the module
|
||||||
|
\param sample_rate Audio engine sample rate. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
SetColor(.5f); |
||||||
|
SetFreq(440.f); |
||||||
|
for(int i = 0; i < order; ++i) |
||||||
|
{ |
||||||
|
generator_[i].Init(sample_rate_); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Get the next sample. */ |
||||||
|
float Process() |
||||||
|
{ |
||||||
|
float gain = 0.5f; |
||||||
|
float sum = 0.0f; |
||||||
|
float frequency = frequency_; |
||||||
|
|
||||||
|
for(int i = 0; i < order; ++i) |
||||||
|
{ |
||||||
|
generator_[i].SetFreq(frequency); |
||||||
|
sum += generator_[i].Process() * gain; |
||||||
|
gain *= decay_; |
||||||
|
frequency *= 2.0f; |
||||||
|
} |
||||||
|
|
||||||
|
return sum; |
||||||
|
} |
||||||
|
|
||||||
|
/** Set the lowest noise frequency.
|
||||||
|
\param freq Frequency of the lowest noise source in Hz. |
||||||
|
*/ |
||||||
|
void SetFreq(float freq) { frequency_ = fclamp(freq, 0.f, sample_rate_); } |
||||||
|
|
||||||
|
/** Sets the amount of high frequency noise.
|
||||||
|
\** Works 0-1. 1 is the brightest, and 0 is the darkest. |
||||||
|
*/ |
||||||
|
void SetColor(float color) { decay_ = fclamp(color, 0.f, 1.f); } |
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_; |
||||||
|
float frequency_; |
||||||
|
float decay_; |
||||||
|
|
||||||
|
T generator_[order]; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,146 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "grainlet.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void GrainletOscillator::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
carrier_phase_ = 0.0f; |
||||||
|
formant_phase_ = 0.0f; |
||||||
|
next_sample_ = 0.0f; |
||||||
|
|
||||||
|
carrier_shape_ = 0.f; |
||||||
|
carrier_bleed_ = 0.f; |
||||||
|
|
||||||
|
SetFreq(440.f); |
||||||
|
SetFormantFreq(220.f); |
||||||
|
SetShape(.5f); |
||||||
|
SetBleed(.5f); |
||||||
|
} |
||||||
|
|
||||||
|
float GrainletOscillator::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 shape_inc = new_carrier_shape_ - carrier_shape_; |
||||||
|
float bleed_inc = new_carrier_bleed_ - carrier_bleed_; |
||||||
|
|
||||||
|
float before = Grainlet( |
||||||
|
1.0f, |
||||||
|
formant_phase_ + (1.0f - reset_time) * formant_frequency_, |
||||||
|
new_carrier_shape_ + shape_inc * (1.0f - reset_time), |
||||||
|
new_carrier_bleed_ + bleed_inc * (1.0f - reset_time)); |
||||||
|
|
||||||
|
float after |
||||||
|
= Grainlet(0.0f, 0.0f, new_carrier_shape_, new_carrier_bleed_); |
||||||
|
|
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
carrier_bleed_ = new_carrier_bleed_; |
||||||
|
carrier_shape_ = new_carrier_shape_; |
||||||
|
next_sample += Grainlet( |
||||||
|
carrier_phase_, formant_phase_, carrier_shape_, carrier_bleed_); |
||||||
|
next_sample_ = next_sample; |
||||||
|
return this_sample; |
||||||
|
} |
||||||
|
|
||||||
|
void GrainletOscillator::SetFreq(float freq) |
||||||
|
{ |
||||||
|
carrier_frequency_ = freq / sample_rate_; |
||||||
|
carrier_frequency_ = carrier_frequency_ > 0.5f ? 0.5f : carrier_frequency_; |
||||||
|
} |
||||||
|
|
||||||
|
void GrainletOscillator::SetFormantFreq(float freq) |
||||||
|
{ |
||||||
|
formant_frequency_ = freq / sample_rate_; |
||||||
|
formant_frequency_ = formant_frequency_ > 0.5f ? 0.5f : formant_frequency_; |
||||||
|
} |
||||||
|
|
||||||
|
void GrainletOscillator::SetShape(float shape) |
||||||
|
{ |
||||||
|
new_carrier_shape_ = shape; |
||||||
|
} |
||||||
|
|
||||||
|
void GrainletOscillator::SetBleed(float bleed) |
||||||
|
{ |
||||||
|
new_carrier_bleed_ = bleed; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
float GrainletOscillator::Sine(float phase) |
||||||
|
{ |
||||||
|
return sinf(phase * TWOPI_F); |
||||||
|
} |
||||||
|
|
||||||
|
float GrainletOscillator::Carrier(float phase, float shape) |
||||||
|
{ |
||||||
|
shape *= 3.0f; |
||||||
|
int shape_integral = static_cast<int>(shape); |
||||||
|
float shape_fractional = shape - static_cast<float>(shape_integral); |
||||||
|
|
||||||
|
float t = 1.0f - shape_fractional; |
||||||
|
|
||||||
|
if(shape_integral == 0) |
||||||
|
{ |
||||||
|
phase = phase * (1.0f + t * t * t * 15.0f); |
||||||
|
if(phase >= 1.0f) |
||||||
|
{ |
||||||
|
phase = 1.0f; |
||||||
|
} |
||||||
|
phase += 0.75f; |
||||||
|
} |
||||||
|
else if(shape_integral == 1) |
||||||
|
{ |
||||||
|
float breakpoint = 0.001f + 0.499f * t * t * t; |
||||||
|
if(phase < breakpoint) |
||||||
|
{ |
||||||
|
phase *= (0.5f / breakpoint); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
phase = 0.5f + (phase - breakpoint) * 0.5f / (1.0f - breakpoint); |
||||||
|
} |
||||||
|
phase += 0.75f; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
t = 1.0f - t; |
||||||
|
phase = 0.25f + phase * (0.5f + t * t * t * 14.5f); |
||||||
|
if(phase >= 0.75f) |
||||||
|
phase = 0.75f; |
||||||
|
} |
||||||
|
return (Sine(phase) + 1.0f) * 0.25f; |
||||||
|
} |
||||||
|
|
||||||
|
float GrainletOscillator::Grainlet(float carrier_phase, |
||||||
|
float formant_phase, |
||||||
|
float shape, |
||||||
|
float bleed) |
||||||
|
{ |
||||||
|
float carrier = Carrier(carrier_phase, shape); |
||||||
|
float formant = Sine(formant_phase); |
||||||
|
return carrier * (formant + bleed) / (1.0f + bleed); |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_GRAINLET_H |
||||||
|
#define DSY_GRAINLET_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file grainlet.h */ |
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Granular Oscillator Module.
|
||||||
|
@author Ben Sergentanis |
||||||
|
@date Dec 2020
|
||||||
|
A phase-distorted single cycle sine * another continuously running sine, \n |
||||||
|
the whole thing synced to a main oscillator. \n \n |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/oscillator/grainlet_oscillator.h \n
|
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
|
||||||
|
class GrainletOscillator |
||||||
|
{ |
||||||
|
public: |
||||||
|
GrainletOscillator() {} |
||||||
|
~GrainletOscillator() {} |
||||||
|
|
||||||
|
/** Initialize the oscillator
|
||||||
|
\param sample_rate Sample rate of audio engine |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the next sample */ |
||||||
|
float Process(); |
||||||
|
|
||||||
|
/** Sets the carrier frequency
|
||||||
|
\param freq Frequency in Hz |
||||||
|
*/ |
||||||
|
void SetFreq(float freq); |
||||||
|
|
||||||
|
/** Sets the formant frequency
|
||||||
|
\param freq Frequency in Hz |
||||||
|
*/ |
||||||
|
void SetFormantFreq(float freq); |
||||||
|
|
||||||
|
/** Waveshaping
|
||||||
|
\param shape Shapes differently from 0-1, 1-2, and > 2. |
||||||
|
*/ |
||||||
|
void SetShape(float shape); |
||||||
|
|
||||||
|
/** Sets the amount of formant to bleed through
|
||||||
|
\param bleed Works best 0-1 |
||||||
|
*/ |
||||||
|
void SetBleed(float bleed); |
||||||
|
|
||||||
|
private: |
||||||
|
float Sine(float phase); |
||||||
|
|
||||||
|
float Carrier(float phase, float shape); |
||||||
|
|
||||||
|
float Grainlet(float carrier_phase, |
||||||
|
float formant_phase, |
||||||
|
float shape, |
||||||
|
float bleed); |
||||||
|
|
||||||
|
// Oscillator state.
|
||||||
|
float carrier_phase_; |
||||||
|
float formant_phase_; |
||||||
|
float next_sample_; |
||||||
|
|
||||||
|
// For interpolation of parameters.
|
||||||
|
float carrier_frequency_; |
||||||
|
float formant_frequency_; |
||||||
|
float carrier_shape_; |
||||||
|
float carrier_bleed_; |
||||||
|
|
||||||
|
float new_carrier_shape_; |
||||||
|
float new_carrier_bleed_; |
||||||
|
|
||||||
|
float sample_rate_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,95 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "particle.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Particle::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
sync_ = false; |
||||||
|
aux_ = 0.f; |
||||||
|
SetFreq(440.f); |
||||||
|
resonance_ = .9f; |
||||||
|
density_ = .5f; |
||||||
|
gain_ = 1.f; |
||||||
|
spread_ = 1.f; |
||||||
|
|
||||||
|
SetRandomFreq(sample_rate_ / 48.f); //48 is the default block size
|
||||||
|
rand_phase_ = 0.f; |
||||||
|
|
||||||
|
pre_gain_ = 0.0f; |
||||||
|
filter_.Init(sample_rate_); |
||||||
|
filter_.SetDrive(.7f); |
||||||
|
} |
||||||
|
|
||||||
|
float Particle::Process() |
||||||
|
{ |
||||||
|
float u = rand() * kRandFrac; |
||||||
|
float s = 0.0f; |
||||||
|
|
||||||
|
if(u <= density_ || sync_) |
||||||
|
{ |
||||||
|
s = u <= density_ ? u * gain_ : s; |
||||||
|
rand_phase_ += rand_freq_; |
||||||
|
|
||||||
|
if(rand_phase_ >= 1.f || sync_) |
||||||
|
{ |
||||||
|
rand_phase_ = rand_phase_ >= 1.f ? rand_phase_ - 1.f : rand_phase_; |
||||||
|
|
||||||
|
const float u = 2.0f * rand() * kRandFrac - 1.0f; |
||||||
|
const float f |
||||||
|
= fmin(powf(2.f, kRatioFrac * spread_ * u) * frequency_, .25f); |
||||||
|
pre_gain_ = 0.5f / sqrtf(resonance_ * f * sqrtf(density_)); |
||||||
|
filter_.SetFreq(f * sample_rate_); |
||||||
|
filter_.SetRes(resonance_); |
||||||
|
} |
||||||
|
} |
||||||
|
aux_ = s; |
||||||
|
|
||||||
|
filter_.Process(pre_gain_ * s); |
||||||
|
return filter_.Band(); |
||||||
|
} |
||||||
|
|
||||||
|
float Particle::GetNoise() |
||||||
|
{ |
||||||
|
return aux_; |
||||||
|
} |
||||||
|
|
||||||
|
void Particle::SetFreq(float freq) |
||||||
|
{ |
||||||
|
freq /= sample_rate_; |
||||||
|
frequency_ = fclamp(freq, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void Particle::SetResonance(float resonance) |
||||||
|
{ |
||||||
|
resonance_ = fclamp(resonance, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void Particle::SetRandomFreq(float freq) |
||||||
|
{ |
||||||
|
freq /= sample_rate_; |
||||||
|
rand_freq_ = fclamp(freq, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void Particle::SetDensity(float density) |
||||||
|
{ |
||||||
|
density_ = fclamp(density * .3f, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void Particle::SetGain(float gain) |
||||||
|
{ |
||||||
|
gain_ = fclamp(gain, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void Particle::SetSpread(float spread) |
||||||
|
{ |
||||||
|
spread_ = spread < 0.f ? 0.f : spread; |
||||||
|
} |
||||||
|
|
||||||
|
void Particle::SetSync(bool sync) |
||||||
|
{ |
||||||
|
sync_ = sync; |
||||||
|
} |
@ -0,0 +1,92 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_PARTICLE_H |
||||||
|
#define DSY_PARTICLE_H |
||||||
|
|
||||||
|
#include "Filters/svf.h" |
||||||
|
#include <stdint.h> |
||||||
|
#include <cstdlib> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file particle.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Random impulse train processed by a resonant filter. |
||||||
|
@author Ported by Ben Sergentanis
|
||||||
|
@date Jan 2021
|
||||||
|
Noise processed by a sample and hold running at a target frequency. \n \n |
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/noise/particle.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class Particle |
||||||
|
{ |
||||||
|
public: |
||||||
|
Particle() {} |
||||||
|
~Particle() {} |
||||||
|
|
||||||
|
/** Initialize the module
|
||||||
|
\param sample_rate Audio engine sample rate. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the next sample */ |
||||||
|
float Process(); |
||||||
|
|
||||||
|
/** Get the raw noise output. Must call Process() first. */ |
||||||
|
float GetNoise(); |
||||||
|
|
||||||
|
/** Set the resonant filter frequency
|
||||||
|
\param freq Frequency in Hz |
||||||
|
*/ |
||||||
|
void SetFreq(float frequency); |
||||||
|
|
||||||
|
/** Set the filter resonance
|
||||||
|
\param resonance Works 0-1 |
||||||
|
*/ |
||||||
|
void SetResonance(float resonance); |
||||||
|
|
||||||
|
/** How often to randomize filter frequency
|
||||||
|
\param freq Frequency in Hz. |
||||||
|
*/ |
||||||
|
void SetRandomFreq(float freq); |
||||||
|
|
||||||
|
/** Noise density
|
||||||
|
\param Works 0-1. |
||||||
|
*/ |
||||||
|
void SetDensity(float density); |
||||||
|
|
||||||
|
/** Overall module gain
|
||||||
|
\param Works 0-1. |
||||||
|
*/ |
||||||
|
void SetGain(float gain); |
||||||
|
|
||||||
|
/** How much to randomize the set filter frequency.
|
||||||
|
\param spread Works over positive numbers. |
||||||
|
*/ |
||||||
|
void SetSpread(float spread); |
||||||
|
|
||||||
|
/** Force randomize the frequency.
|
||||||
|
\param sync True to randomize freq. |
||||||
|
*/ |
||||||
|
void SetSync(bool sync); |
||||||
|
|
||||||
|
private: |
||||||
|
static constexpr float kRandFrac = 1.f / (float)RAND_MAX; |
||||||
|
static constexpr float kRatioFrac = 1.f / 12.f; |
||||||
|
float sample_rate_; |
||||||
|
float aux_, frequency_, density_, gain_, spread_, resonance_; |
||||||
|
bool sync_; |
||||||
|
|
||||||
|
|
||||||
|
float rand_phase_; |
||||||
|
float rand_freq_; |
||||||
|
|
||||||
|
|
||||||
|
float pre_gain_; |
||||||
|
Svf filter_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,43 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_WHITENOISE_H |
||||||
|
#define DSY_WHITENOISE_H |
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** fast white noise generator
|
||||||
|
|
||||||
|
I think this came from musicdsp.org at some point |
||||||
|
*/ |
||||||
|
class WhiteNoise |
||||||
|
{ |
||||||
|
public: |
||||||
|
WhiteNoise() {} |
||||||
|
~WhiteNoise() {} |
||||||
|
/** Initializes the WhiteNoise object
|
||||||
|
*/ |
||||||
|
void Init() |
||||||
|
{ |
||||||
|
amp_ = 1.0f; |
||||||
|
randseed_ = 1; |
||||||
|
} |
||||||
|
|
||||||
|
/** sets the amplitude of the noise output
|
||||||
|
*/ |
||||||
|
inline void SetAmp(float a) { amp_ = a; } |
||||||
|
/** returns a new sample of noise in the range of -amp_ to amp_
|
||||||
|
*/ |
||||||
|
inline float Process() |
||||||
|
{ |
||||||
|
randseed_ *= 16807; |
||||||
|
return (randseed_ * coeff_) * amp_; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
static constexpr float coeff_ = 4.6566129e-010f; |
||||||
|
float amp_; |
||||||
|
int32_t randseed_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,197 @@ |
|||||||
|
#include <cmath> |
||||||
|
#include "../utility/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(sample_rate_); |
||||||
|
|
||||||
|
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 * sample_rate_; |
||||||
|
iir_damping_filter_.SetFreq(temp_f); |
||||||
|
|
||||||
|
float ratio = powf(2.f, damping_cutoff * kOneTwelfth); |
||||||
|
float damping_compensation = 1.f - 2.f * atanf(1.f / ratio) / (TWOPI_F); |
||||||
|
|
||||||
|
float stretch_point |
||||||
|
= non_linearity_amount_ * (2.0f - non_linearity_amount_) * 0.225f; |
||||||
|
float stretch_correction = (160.0f / sample_rate_) * delay; |
||||||
|
stretch_correction = fclamp(stretch_correction, 1.f, 2.1f); |
||||||
|
|
||||||
|
float noise_amount_sqrt = non_linearity_amount_ > 0.75f |
||||||
|
? 4.0f * (non_linearity_amount_ - 0.75f) |
||||||
|
: 0.0f; |
||||||
|
float noise_amount = noise_amount_sqrt * noise_amount_sqrt * 0.1f; |
||||||
|
float noise_filter = 0.06f + 0.94f * brightness * brightness; |
||||||
|
|
||||||
|
float bridge_curving_sqrt = non_linearity_amount_; |
||||||
|
float bridge_curving = bridge_curving_sqrt * bridge_curving_sqrt * 0.01f; |
||||||
|
|
||||||
|
float ap_gain = -0.618f * non_linearity_amount_ |
||||||
|
/ (0.15f + fabsf(non_linearity_amount_)); |
||||||
|
|
||||||
|
src_phase_ += src_ratio; |
||||||
|
if(src_phase_ > 1.0f) |
||||||
|
{ |
||||||
|
src_phase_ -= 1.0f; |
||||||
|
|
||||||
|
delay = delay * damping_compensation; |
||||||
|
float s = 0.0f; |
||||||
|
|
||||||
|
if(non_linearity == NON_LINEARITY_DISPERSION) |
||||||
|
{ |
||||||
|
float noise = rand() * kRandFrac - 0.5f; |
||||||
|
fonepole(dispersion_noise_, noise, noise_filter); |
||||||
|
delay *= 1.0f + dispersion_noise_ * noise_amount; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
delay *= 1.0f - curved_bridge_ * bridge_curving; |
||||||
|
} |
||||||
|
|
||||||
|
if(non_linearity == NON_LINEARITY_DISPERSION) |
||||||
|
{ |
||||||
|
float ap_delay = delay * stretch_point; |
||||||
|
float main_delay = delay |
||||||
|
- ap_delay * (0.408f - stretch_point * 0.308f) |
||||||
|
* stretch_correction; |
||||||
|
if(ap_delay >= 4.0f && main_delay >= 4.0f) |
||||||
|
{ |
||||||
|
s = string_.Read(main_delay); |
||||||
|
s = stretch_.Allpass(s, ap_delay, ap_gain); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
s = string_.ReadHermite(delay); |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
s = string_.ReadHermite(delay); |
||||||
|
} |
||||||
|
|
||||||
|
if(non_linearity == NON_LINEARITY_CURVED_BRIDGE) |
||||||
|
{ |
||||||
|
float value = fabsf(s) - 0.025f; |
||||||
|
float sign = s > 0.0f ? 1.0f : -1.5f; |
||||||
|
curved_bridge_ = (fabsf(value) + value) * sign; |
||||||
|
} |
||||||
|
|
||||||
|
s += in; |
||||||
|
s = fclamp(s, -20.f, +20.f); |
||||||
|
|
||||||
|
s = dc_blocker_.Process(s); |
||||||
|
|
||||||
|
s = iir_damping_filter_.Process(s); |
||||||
|
string_.Write(s); |
||||||
|
|
||||||
|
out_sample_[1] = out_sample_[0]; |
||||||
|
out_sample_[0] = s; |
||||||
|
} |
||||||
|
|
||||||
|
crossfade_.SetPos(src_phase_); |
||||||
|
return crossfade_.Process(out_sample_[1], out_sample_[0]); |
||||||
|
} |
@ -0,0 +1,103 @@ |
|||||||
|
#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/svf.h" |
||||||
|
#include "Filters/tone.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_; |
||||||
|
|
||||||
|
Tone iir_damping_filter_; |
||||||
|
|
||||||
|
DcBlock dc_blocker_; |
||||||
|
|
||||||
|
CrossFade crossfade_; |
||||||
|
|
||||||
|
float dispersion_noise_; |
||||||
|
float curved_bridge_; |
||||||
|
|
||||||
|
// Very crappy linear interpolation upsampler used for low pitches that
|
||||||
|
// do not fit the delay line. Rarely used.
|
||||||
|
float src_phase_; |
||||||
|
float out_sample_[2]; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,90 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_POLYPLUCK_H |
||||||
|
#define DSY_POLYPLUCK_H |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include "Utility/dsp.h" |
||||||
|
#include "PhysicalModeling/pluck.h" |
||||||
|
#include "Utility/dcblock.h" |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Simplified Pseudo-Polyphonic Pluck Voice
|
||||||
|
|
||||||
|
Template Based Pluck Voice, with configurable number of voices and simple pseudo-polyphony. |
||||||
|
|
||||||
|
DC Blocking included to prevent biases from causing unwanted saturation distortion. |
||||||
|
|
||||||
|
**Author**: shensley |
||||||
|
|
||||||
|
**Date Added**: March 2020 |
||||||
|
*/ |
||||||
|
template <size_t num_voices> |
||||||
|
class PolyPluck |
||||||
|
{ |
||||||
|
public: |
||||||
|
/** Initializes the PolyPluck instance.
|
||||||
|
\param sample_rate: rate in Hz that the Process() function will be called. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate) |
||||||
|
{ |
||||||
|
active_voice_ = 0; |
||||||
|
p_damp_ = 0.95f; |
||||||
|
p_decay_ = 0.75f; |
||||||
|
for(size_t i = 0; i < num_voices; i++) |
||||||
|
{ |
||||||
|
plk_[i].Init(sample_rate, plkbuff_[i], 256, PLUCK_MODE_RECURSIVE); |
||||||
|
plk_[i].SetDamp(0.85f); |
||||||
|
plk_[i].SetAmp(0.18f); |
||||||
|
plk_[i].SetDecay(0.85f); |
||||||
|
} |
||||||
|
blk_.Init(sample_rate); |
||||||
|
} |
||||||
|
|
||||||
|
/** Process function, synthesizes and sums the output of all voices,
|
||||||
|
triggering a new voice with frequency of MIDI note number when trig > 0. |
||||||
|
|
||||||
|
\param trig: value by reference of trig. When trig > 0 a the next voice will be triggered, and trig will be set to 0. |
||||||
|
\param note: MIDI note number for the active_voice. |
||||||
|
*/ |
||||||
|
float Process(float &trig, float note) |
||||||
|
{ |
||||||
|
float sig, tval; |
||||||
|
sig = 0.0f; |
||||||
|
if(trig > 0.0f) |
||||||
|
{ |
||||||
|
// increment active voice
|
||||||
|
active_voice_ = (active_voice_ + 1) % num_voices; |
||||||
|
// set new voice to new note
|
||||||
|
plk_[active_voice_].SetDamp(p_damp_); |
||||||
|
plk_[active_voice_].SetDecay(p_decay_); |
||||||
|
plk_[active_voice_].SetAmp(0.25f); |
||||||
|
} |
||||||
|
plk_[active_voice_].SetFreq(mtof(note)); |
||||||
|
|
||||||
|
for(size_t i = 0; i < num_voices; i++) |
||||||
|
{ |
||||||
|
tval = (trig > 0.0f && i == active_voice_) ? 1.0f : 0.0f; |
||||||
|
sig += plk_[i].Process(tval); |
||||||
|
} |
||||||
|
if(trig > 0.0f) |
||||||
|
trig = 0.0f; |
||||||
|
return blk_.Process(sig); |
||||||
|
} |
||||||
|
|
||||||
|
/** Sets the decay coefficients of the pluck voices.
|
||||||
|
\param p expects 0.0-1.0 input. |
||||||
|
*/ |
||||||
|
void SetDecay(float p) { p_damp_ = p; } |
||||||
|
|
||||||
|
private: |
||||||
|
DcBlock blk_; |
||||||
|
Pluck plk_[num_voices]; |
||||||
|
float plkbuff_[num_voices][256]; |
||||||
|
float p_damp_, p_decay_; |
||||||
|
size_t active_voice_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace daisysp
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,213 @@ |
|||||||
|
#include "drip.h" |
||||||
|
#include <math.h> |
||||||
|
#include <cstdlib> |
||||||
|
#include "../utility/dsp.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
#define WUTR_SOUND_DECAY 0.95f |
||||||
|
#define WUTR_SYSTEM_DECAY 0.996f |
||||||
|
#define WUTR_GAIN 1.0f |
||||||
|
#define WUTR_NUM_SOURCES 10.0f |
||||||
|
#define WUTR_CENTER_FREQ0 450.0f |
||||||
|
#define WUTR_CENTER_FREQ1 600.0f |
||||||
|
#define WUTR_CENTER_FREQ2 750.0f |
||||||
|
#define WUTR_RESON 0.9985f |
||||||
|
#define WUTR_FREQ_SWEEP 1.0001f |
||||||
|
#define MAX_SHAKE 2000.0f |
||||||
|
|
||||||
|
int Drip::my_random(int max) |
||||||
|
{ |
||||||
|
return (rand() % (max + 1)); |
||||||
|
} |
||||||
|
|
||||||
|
float Drip::noise_tick() |
||||||
|
{ |
||||||
|
float temp; |
||||||
|
temp = 1.0f * rand() - 1073741823.5f; |
||||||
|
return temp * (1.0f / 1073741823.0f); |
||||||
|
} |
||||||
|
|
||||||
|
void Drip::Init(float sample_rate, float dettack) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
float temp; |
||||||
|
dettack_ = dettack; |
||||||
|
num_tubes_ = 10; |
||||||
|
damp_ = 0.2f; |
||||||
|
shake_max_ = 0.0f; |
||||||
|
freq_ = 450.0f; |
||||||
|
freq1_ = 600.0f; |
||||||
|
freq2_ = 720.0f; |
||||||
|
amp_ = 0.3f; |
||||||
|
|
||||||
|
snd_level_ = 0.0; |
||||||
|
float tpidsr = 2.0 * PI_F / sample_rate_; |
||||||
|
|
||||||
|
kloop_ = (sample_rate_ * dettack_); |
||||||
|
outputs00_ = 0.0f; |
||||||
|
outputs01_ = 0.0f; |
||||||
|
outputs10_ = 0.0f; |
||||||
|
outputs11_ = 0.0f; |
||||||
|
outputs20_ = 0.0f; |
||||||
|
outputs21_ = 0.0f; |
||||||
|
|
||||||
|
total_energy_ = 0.0f; |
||||||
|
|
||||||
|
center_freqs0_ = res_freq0_ = WUTR_CENTER_FREQ0; |
||||||
|
center_freqs1_ = res_freq1_ = WUTR_CENTER_FREQ1; |
||||||
|
center_freqs2_ = res_freq2_ = WUTR_CENTER_FREQ2; |
||||||
|
num_objects_save_ = num_objects_ = WUTR_NUM_SOURCES; |
||||||
|
sound_decay_ = WUTR_SOUND_DECAY; |
||||||
|
system_decay_ = WUTR_SYSTEM_DECAY; |
||||||
|
temp = logf(WUTR_NUM_SOURCES) * WUTR_GAIN / WUTR_NUM_SOURCES; |
||||||
|
gains0_ = gains1_ = gains2_ = temp; |
||||||
|
coeffs01_ = WUTR_RESON * WUTR_RESON; |
||||||
|
coeffs00_ = -WUTR_RESON * 2.0f * cosf(WUTR_CENTER_FREQ0 * tpidsr); |
||||||
|
coeffs11_ = WUTR_RESON * WUTR_RESON; |
||||||
|
coeffs10_ = -WUTR_RESON * 2.0f * cosf(WUTR_CENTER_FREQ1 * tpidsr); |
||||||
|
coeffs21_ = WUTR_RESON * WUTR_RESON; |
||||||
|
coeffs20_ = -WUTR_RESON * 2.0f * cosf(WUTR_CENTER_FREQ2 * tpidsr); |
||||||
|
|
||||||
|
shake_energy_ = amp_ * 1.0f * MAX_SHAKE * 0.1f; |
||||||
|
shake_damp_ = 0.0f; |
||||||
|
if(shake_energy_ > MAX_SHAKE) |
||||||
|
shake_energy_ = MAX_SHAKE; |
||||||
|
shake_max_save_ = 0.0f; |
||||||
|
num_objects_ = 10.0f; |
||||||
|
finalZ0_ = finalZ1_ = finalZ2_ = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
float Drip::Process(bool trig) |
||||||
|
{ |
||||||
|
float data; |
||||||
|
float lastOutput; |
||||||
|
|
||||||
|
float tpidsr = 2.0f * PI_F / sample_rate_; |
||||||
|
|
||||||
|
if(trig) |
||||||
|
{ |
||||||
|
Init(sample_rate_, dettack_); |
||||||
|
} |
||||||
|
if(num_tubes_ != 0.0f && num_tubes_ != num_objects_) |
||||||
|
{ |
||||||
|
num_objects_ = num_tubes_; |
||||||
|
if(num_objects_ < 1.0f) |
||||||
|
{ |
||||||
|
num_objects_ = 1.0f; |
||||||
|
} |
||||||
|
} |
||||||
|
if(freq_ != 0.0f && freq_ != res_freq0_) |
||||||
|
{ |
||||||
|
res_freq0_ = freq_; |
||||||
|
coeffs00_ = -WUTR_RESON * 2.0f * cosf(res_freq0_ * tpidsr); |
||||||
|
} |
||||||
|
if(damp_ != 0.0f && damp_ != shake_damp_) |
||||||
|
{ |
||||||
|
shake_damp_ = damp_; |
||||||
|
system_decay_ = WUTR_SYSTEM_DECAY + (shake_damp_ * 0.002f); |
||||||
|
} |
||||||
|
if(shake_max_ != 0.0f && shake_max_ != shake_max_save_) |
||||||
|
{ |
||||||
|
shake_max_save_ = shake_max_; |
||||||
|
shake_energy_ += shake_max_save_ * MAX_SHAKE * 0.1f; |
||||||
|
if(shake_energy_ > MAX_SHAKE) |
||||||
|
shake_energy_ = MAX_SHAKE; |
||||||
|
} |
||||||
|
if(freq1_ != 0.0f && freq1_ != res_freq1_) |
||||||
|
{ |
||||||
|
res_freq1_ = freq1_; |
||||||
|
coeffs10_ = -WUTR_RESON * 2.0f * cosf(res_freq1_ * tpidsr); |
||||||
|
} |
||||||
|
if(freq2_ != 0.0f && freq2_ != res_freq2_) |
||||||
|
{ |
||||||
|
res_freq2_ = freq2_; |
||||||
|
coeffs20_ = -WUTR_RESON * 2.0f * cosf(res_freq2_ * tpidsr); |
||||||
|
} |
||||||
|
if((--kloop_) == 0.0f) |
||||||
|
{ |
||||||
|
shake_energy_ = 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
float shakeEnergy = shake_energy_; |
||||||
|
float systemDecay = system_decay_; |
||||||
|
float sndLevel = snd_level_; |
||||||
|
float num_objects = num_objects_; |
||||||
|
float soundDecay = sound_decay_; |
||||||
|
float inputs0, inputs1, inputs2; |
||||||
|
|
||||||
|
shakeEnergy *= systemDecay; /* Exponential system decay */ |
||||||
|
|
||||||
|
sndLevel = shakeEnergy; |
||||||
|
if(my_random(32767) < num_objects) |
||||||
|
{ |
||||||
|
int j; |
||||||
|
j = my_random(3); |
||||||
|
if(j == 0) |
||||||
|
{ |
||||||
|
center_freqs0_ = res_freq1_ * (0.75f + (0.25f * noise_tick())); |
||||||
|
gains0_ = fabsf(noise_tick()); |
||||||
|
} |
||||||
|
else if(j == 1) |
||||||
|
{ |
||||||
|
center_freqs1_ = res_freq1_ * (1.0f + (0.25f * noise_tick())); |
||||||
|
gains1_ = fabsf(noise_tick()); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
center_freqs2_ = res_freq1_ * (1.25f + (0.25f * noise_tick())); |
||||||
|
gains2_ = fabsf(noise_tick()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
gains0_ *= WUTR_RESON; |
||||||
|
if(gains0_ > 0.001f) |
||||||
|
{ |
||||||
|
center_freqs0_ *= WUTR_FREQ_SWEEP; |
||||||
|
coeffs00_ = -WUTR_RESON * 2.0f * cosf(center_freqs0_ * tpidsr); |
||||||
|
} |
||||||
|
gains1_ *= WUTR_RESON; |
||||||
|
if(gains1_ > 0.00f) |
||||||
|
{ |
||||||
|
center_freqs1_ *= WUTR_FREQ_SWEEP; |
||||||
|
coeffs10_ = -WUTR_RESON * 2.0f * cosf(center_freqs1_ * tpidsr); |
||||||
|
} |
||||||
|
gains2_ *= WUTR_RESON; |
||||||
|
if(gains2_ > 0.001f) |
||||||
|
{ |
||||||
|
center_freqs2_ *= WUTR_FREQ_SWEEP; |
||||||
|
coeffs20_ = -WUTR_RESON * 2.0f * cosf(center_freqs2_ * tpidsr); |
||||||
|
} |
||||||
|
|
||||||
|
sndLevel *= soundDecay; |
||||||
|
inputs0 = sndLevel; |
||||||
|
inputs0 *= noise_tick(); |
||||||
|
inputs1 = inputs0 * gains1_; |
||||||
|
inputs2 = inputs0 * gains2_; |
||||||
|
inputs0 *= gains0_; |
||||||
|
inputs0 -= outputs00_ * coeffs00_; |
||||||
|
inputs0 -= outputs01_ * coeffs01_; |
||||||
|
outputs01_ = outputs00_; |
||||||
|
outputs00_ = inputs0; |
||||||
|
data = gains0_ * outputs00_; |
||||||
|
inputs1 -= outputs10_ * coeffs10_; |
||||||
|
inputs1 -= outputs11_ * coeffs11_; |
||||||
|
outputs11_ = outputs10_; |
||||||
|
outputs10_ = inputs1_; |
||||||
|
data += gains1_ * outputs10_; |
||||||
|
inputs2 -= outputs20_ * coeffs20_; |
||||||
|
inputs2 -= outputs21_ * coeffs21_; |
||||||
|
outputs21_ = outputs20_; |
||||||
|
outputs20_ = inputs2_; |
||||||
|
data += gains2_ * outputs20_; |
||||||
|
|
||||||
|
finalZ2_ = finalZ1_; |
||||||
|
finalZ1_ = finalZ0_; |
||||||
|
finalZ0_ = data * 4.0f; |
||||||
|
|
||||||
|
lastOutput = finalZ2_ - finalZ0_; |
||||||
|
lastOutput *= 0.005f; |
||||||
|
shake_energy_ = shakeEnergy; |
||||||
|
snd_level_ = sndLevel; |
||||||
|
return lastOutput; |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_DRIP_H |
||||||
|
#define DSY_DRIP_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file drip.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
Imitates the sound of dripping water via Physical Modeling Synthesis. \n
|
||||||
|
Ported from soundpipe by Ben Sergentanis, May 2020
|
||||||
|
@author Perry Cook
|
||||||
|
@date 2000
|
||||||
|
*/ |
||||||
|
class Drip |
||||||
|
{ |
||||||
|
public: |
||||||
|
Drip() {} |
||||||
|
~Drip() {} |
||||||
|
|
||||||
|
/**
|
||||||
|
Initializes the Drip module.
|
||||||
|
\param sample_rate The sample rate of the audio engine being run. |
||||||
|
\param dettack The period of time over which all sound is stopped. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate, float dettack); |
||||||
|
|
||||||
|
/**
|
||||||
|
Process the next floating point sample. |
||||||
|
\param trig If true, begins a new drip. |
||||||
|
\return Next sample. |
||||||
|
*/ |
||||||
|
float Process(bool trig); |
||||||
|
|
||||||
|
private: |
||||||
|
float gains0_, gains1_, gains2_, kloop_, dettack_, num_tubes_, damp_, |
||||||
|
shake_max_, freq_, freq1_, freq2_, amp_, snd_level_, outputs00_, |
||||||
|
outputs01_, outputs10_, outputs11_, outputs20_, outputs21_, |
||||||
|
total_energy_, center_freqs0_, center_freqs1_, center_freqs2_, |
||||||
|
num_objects_save_, sound_decay_, system_decay_, finalZ0_, finalZ1_, |
||||||
|
finalZ2_, coeffs01_, coeffs00_, coeffs11_, coeffs10_, coeffs21_, |
||||||
|
coeffs20_, shake_energy_, shake_damp_, shake_max_save_, num_objects_, |
||||||
|
sample_rate_, res_freq0_, res_freq1_, res_freq2_, inputs1_, inputs2_; |
||||||
|
|
||||||
|
int my_random(int max); |
||||||
|
float noise_tick(); |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,109 @@ |
|||||||
|
#include "modalvoice.h" |
||||||
|
#include <algorithm> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void ModalVoice::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
aux_ = 0.f; |
||||||
|
|
||||||
|
excitation_filter_.Init(); |
||||||
|
resonator_.Init(0.015f, 24, sample_rate_); |
||||||
|
excitation_filter_.Init(); |
||||||
|
dust_.Init(); |
||||||
|
|
||||||
|
SetSustain(false); |
||||||
|
SetFreq(440.f); |
||||||
|
SetAccent(.3f); |
||||||
|
SetStructure(.6f); |
||||||
|
SetBrightness(.8f); |
||||||
|
SetDamping(.6f); |
||||||
|
} |
||||||
|
|
||||||
|
void ModalVoice::SetSustain(bool sustain) |
||||||
|
{ |
||||||
|
sustain_ = sustain; |
||||||
|
} |
||||||
|
|
||||||
|
void ModalVoice::Trig() |
||||||
|
{ |
||||||
|
trig_ = true; |
||||||
|
} |
||||||
|
|
||||||
|
void ModalVoice::SetFreq(float freq) |
||||||
|
{ |
||||||
|
resonator_.SetFreq(freq); |
||||||
|
f0_ = freq / sample_rate_; |
||||||
|
f0_ = fclamp(f0_, 0.f, .25f); |
||||||
|
} |
||||||
|
|
||||||
|
void ModalVoice::SetAccent(float accent) |
||||||
|
{ |
||||||
|
accent_ = fclamp(accent, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void ModalVoice::SetStructure(float structure) |
||||||
|
{ |
||||||
|
resonator_.SetStructure(structure); |
||||||
|
} |
||||||
|
|
||||||
|
void ModalVoice::SetBrightness(float brightness) |
||||||
|
{ |
||||||
|
brightness_ = fclamp(brightness, 0.f, 1.f); |
||||||
|
density_ = brightness_ * brightness_; |
||||||
|
} |
||||||
|
|
||||||
|
void ModalVoice::SetDamping(float damping) |
||||||
|
{ |
||||||
|
damping_ = fclamp(damping, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
float ModalVoice::GetAux() |
||||||
|
{ |
||||||
|
return aux_; |
||||||
|
} |
||||||
|
|
||||||
|
float ModalVoice::Process(bool trigger) |
||||||
|
{ |
||||||
|
float brightness = brightness_ + 0.25f * accent_ * (1.0f - brightness_); |
||||||
|
float damping = damping_ + 0.25f * accent_ * (1.0f - damping_); |
||||||
|
|
||||||
|
const float range = sustain_ ? 36.0f : 60.0f; |
||||||
|
const float f = sustain_ ? 4.0f * f0_ : 2.0f * f0_; |
||||||
|
const float cutoff = fmin( |
||||||
|
f |
||||||
|
* powf(2.f, |
||||||
|
kOneTwelfth |
||||||
|
* ((brightness * (2.0f - brightness) - 0.5f) * range)), |
||||||
|
0.499f); |
||||||
|
const float q = sustain_ ? 0.7f : 1.5f; |
||||||
|
|
||||||
|
float temp = 0.f; |
||||||
|
// Synthesize excitation signal.
|
||||||
|
if(sustain_) |
||||||
|
{ |
||||||
|
const float dust_f = 0.00005f + 0.99995f * density_ * density_; |
||||||
|
dust_.SetDensity(dust_f); |
||||||
|
temp = dust_.Process() * (4.0f - dust_f * 3.0f) * accent_; |
||||||
|
} |
||||||
|
else if(trigger || trig_) |
||||||
|
{ |
||||||
|
const float attenuation = 1.0f - damping * 0.5f; |
||||||
|
const float amplitude = (0.12f + 0.08f * accent_) * attenuation; |
||||||
|
temp = amplitude * powf(2.f, kOneTwelfth * (cutoff * cutoff * 24.0f)) |
||||||
|
/ cutoff; |
||||||
|
trig_ = false; |
||||||
|
} |
||||||
|
|
||||||
|
const float one = 1.0f; |
||||||
|
excitation_filter_.Process<ResonatorSvf<1>::LOW_PASS, false>( |
||||||
|
&cutoff, &q, &one, temp, &temp); |
||||||
|
|
||||||
|
aux_ = temp; |
||||||
|
|
||||||
|
resonator_.SetBrightness(brightness); |
||||||
|
resonator_.SetDamping(damping); |
||||||
|
|
||||||
|
return resonator_.Process(temp); |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_MODAL_H |
||||||
|
#define DSY_MODAL_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include "Filters/svf.h" |
||||||
|
#include "PhysicalModeling/resonator.h" |
||||||
|
#include "Noise/dust.h" |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
/** @file modalvoice.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/**
|
||||||
|
@brief Simple modal synthesis voice with a mallet exciter: click -> LPF -> resonator. |
||||||
|
@author Ben Sergentanis |
||||||
|
@date Jan 2021 |
||||||
|
The click can be replaced by continuous white noise. \n \n
|
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/physical_modelling/modal_voice.h \n |
||||||
|
and pichenettes/eurorack/plaits/dsp/physical_modelling/modal_voice.cc \n
|
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
class ModalVoice |
||||||
|
{ |
||||||
|
public: |
||||||
|
ModalVoice() {} |
||||||
|
~ModalVoice() {} |
||||||
|
|
||||||
|
/** Initialize the module
|
||||||
|
\param sample_rate Audio engine sample rate |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
/** Get the next sample
|
||||||
|
\param trigger Strike the resonator. Defaults to false. |
||||||
|
*/ |
||||||
|
float Process(bool trigger = false); |
||||||
|
|
||||||
|
/** Continually excite the resonator with noise.
|
||||||
|
\param sustain True turns on the noise. |
||||||
|
*/ |
||||||
|
void SetSustain(bool sustain); |
||||||
|
|
||||||
|
/** Strike the resonator. */ |
||||||
|
void Trig(); |
||||||
|
|
||||||
|
/** Set the resonator root frequency.
|
||||||
|
\param freq Frequency in Hz. |
||||||
|
*/ |
||||||
|
void SetFreq(float freq); |
||||||
|
|
||||||
|
/** Hit the resonator a bit harder.
|
||||||
|
\param accent Works 0-1. |
||||||
|
*/ |
||||||
|
void SetAccent(float accent); |
||||||
|
|
||||||
|
/** Changes the general charater of the resonator (stiffness, brightness)
|
||||||
|
\param structure Works best from 0-1 |
||||||
|
*/ |
||||||
|
void SetStructure(float structure); |
||||||
|
|
||||||
|
/** Set the brighness of the resonator, and the noise density.
|
||||||
|
\param brightness Works best 0-1 |
||||||
|
*/ |
||||||
|
void SetBrightness(float brightness); |
||||||
|
|
||||||
|
/** How long the resonant body takes to decay.
|
||||||
|
\param damping Works best 0-1 |
||||||
|
*/ |
||||||
|
void SetDamping(float damping); |
||||||
|
|
||||||
|
/** Get the raw excitation signal. Must call Process() first. */ |
||||||
|
float GetAux(); |
||||||
|
|
||||||
|
private: |
||||||
|
float sample_rate_; |
||||||
|
|
||||||
|
bool sustain_, trig_; |
||||||
|
float f0_, structure_, brightness_, damping_; |
||||||
|
float density_, accent_; |
||||||
|
float aux_; |
||||||
|
|
||||||
|
ResonatorSvf<1> excitation_filter_; |
||||||
|
Resonator resonator_; |
||||||
|
Dust dust_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,122 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <math.h> |
||||||
|
#include "pluck.h" |
||||||
|
|
||||||
|
#define PLUKMIN 64 |
||||||
|
#define INTERPFACTOR 256.0f |
||||||
|
#define INTERPFACTOR_I 255 |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Pluck::Reinit() |
||||||
|
{ |
||||||
|
int n; |
||||||
|
float val = 0; |
||||||
|
float *ap = buf_; |
||||||
|
//npts_ = (int32_t)roundf(decay_ * (float)(maxpts_ - PLUKMIN) + PLUKMIN);
|
||||||
|
npts_ = (int32_t)(decay_ * (float)(maxpts_ - PLUKMIN) + PLUKMIN); |
||||||
|
//sicps_ = ((float)npts_ * INTERPFACTOR + INTERPFACTOR/2.0f) * (1.0f / _sr);
|
||||||
|
sicps_ = ((float)npts_ * 256.0f + 128.0f) * (1.0f / sample_rate_); |
||||||
|
for(n = npts_; n--;) |
||||||
|
{ |
||||||
|
val = (float)((float)rand() / (float)RAND_MAX); |
||||||
|
*ap++ = (val * 2.0f) - 1.0f; |
||||||
|
} |
||||||
|
phs256_ = 0; |
||||||
|
} |
||||||
|
|
||||||
|
void Pluck::Init(float sample_rate, float *buf, int32_t npts, int32_t mode) |
||||||
|
{ |
||||||
|
amp_ = 0.5f; |
||||||
|
freq_ = 300; |
||||||
|
decay_ = 1.0f; |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
mode_ = mode; |
||||||
|
|
||||||
|
maxpts_ = npts; |
||||||
|
npts_ = npts; |
||||||
|
buf_ = buf; |
||||||
|
|
||||||
|
Reinit(); |
||||||
|
/* tuned pitch convt */ |
||||||
|
sicps_ = (npts * 256.0f + 128.0f) * (1.0f / sample_rate_); |
||||||
|
init_ = 1; |
||||||
|
} |
||||||
|
|
||||||
|
float Pluck::Process(float &trig) |
||||||
|
{ |
||||||
|
float * fp, out; |
||||||
|
int32_t phs256, phsinc, ltwopi, offset; |
||||||
|
float coeff; |
||||||
|
|
||||||
|
// unused variable
|
||||||
|
// float inv_coeff;
|
||||||
|
|
||||||
|
float frac, diff; |
||||||
|
float dampmin = 0.42f; |
||||||
|
|
||||||
|
if(trig != 0) |
||||||
|
{ |
||||||
|
init_ = 0; |
||||||
|
Reinit(); |
||||||
|
} |
||||||
|
|
||||||
|
if(init_) |
||||||
|
{ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
// Set Coeff for mode.
|
||||||
|
switch(mode_) |
||||||
|
{ |
||||||
|
case PLUCK_MODE_RECURSIVE: |
||||||
|
coeff = ((0.5f - dampmin) * damp_) + dampmin; |
||||||
|
break; |
||||||
|
case PLUCK_MODE_WEIGHTED_AVERAGE: |
||||||
|
coeff = 0.05f + (damp_ * 0.90f); |
||||||
|
break; |
||||||
|
default: coeff = 0.5f; break; |
||||||
|
} |
||||||
|
|
||||||
|
// variable set but not used
|
||||||
|
//inv_coeff = 1.0f - coeff;
|
||||||
|
|
||||||
|
phsinc = (int32_t)(freq_ * sicps_); |
||||||
|
phs256 = phs256_; |
||||||
|
ltwopi = npts_ << 8; |
||||||
|
offset = phs256 >> 8; |
||||||
|
fp = (float *)buf_ + offset; /* lookup position */ |
||||||
|
diff = fp[1] - fp[0]; |
||||||
|
frac = (float)(phs256 & 255) / 256.0f; /* w. interpolation */ |
||||||
|
out = (fp[0] + diff * frac) * amp_; /* gives output val */ |
||||||
|
if((phs256 += phsinc) >= ltwopi) |
||||||
|
{ |
||||||
|
int nn; |
||||||
|
float preval; |
||||||
|
phs256 -= ltwopi; |
||||||
|
fp = buf_; |
||||||
|
preval = fp[0]; |
||||||
|
fp[0] = fp[npts_]; |
||||||
|
fp++; |
||||||
|
nn = npts_; |
||||||
|
do |
||||||
|
{ |
||||||
|
/* 1st order recursive filter*/ |
||||||
|
//preval = (*fp + preval) * coeff;
|
||||||
|
/* weighted average - stretches decay times */ |
||||||
|
switch(mode_) |
||||||
|
{ |
||||||
|
case PLUCK_MODE_RECURSIVE: |
||||||
|
preval = (*fp + preval) * coeff; |
||||||
|
break; |
||||||
|
case PLUCK_MODE_WEIGHTED_AVERAGE: |
||||||
|
preval = (*fp * coeff) + (preval * (1.0f - coeff)); |
||||||
|
break; |
||||||
|
default: break; |
||||||
|
} |
||||||
|
*fp++ = preval; |
||||||
|
} while(--nn); |
||||||
|
} |
||||||
|
phs256_ = phs256; |
||||||
|
return out; |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
|
||||||
|
#pragma once |
||||||
|
#ifndef DSY_PLUCK_H |
||||||
|
#define DSY_PLUCK_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** The method of natural decay that the algorithm will use.
|
||||||
|
- RECURSIVE: 1st order recursive filter, with coefs .5. |
||||||
|
- WEIGHTED_AVERAGE: weighted averaging.
|
||||||
|
*/ |
||||||
|
enum |
||||||
|
{ |
||||||
|
PLUCK_MODE_RECURSIVE, |
||||||
|
PLUCK_MODE_WEIGHTED_AVERAGE, |
||||||
|
PLUCK_LAST, |
||||||
|
}; |
||||||
|
|
||||||
|
/** Produces a naturally decaying plucked string or drum sound based on the Karplus-Strong algorithms.
|
||||||
|
|
||||||
|
Ported from soundpipe to DaisySP |
||||||
|
|
||||||
|
This code was originally extracted from the Csound opcode "pluck" |
||||||
|
|
||||||
|
Original Author(s): Barry Vercoe, John ffitch |
||||||
|
Year: 1991 |
||||||
|
|
||||||
|
Location: OOps/ugens4.c |
||||||
|
*/ |
||||||
|
class Pluck |
||||||
|
{ |
||||||
|
public: |
||||||
|
Pluck() {} |
||||||
|
~Pluck() {} |
||||||
|
/** Initializes the Pluck module.
|
||||||
|
|
||||||
|
\param sample_rate: Sample rate of the audio engine being run. |
||||||
|
\param buf: buffer used as an impulse when triggering the Pluck algorithm |
||||||
|
\param npt: number of elementes in buf. |
||||||
|
\param mode: Sets the mode of the algorithm. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate, float *buf, int32_t npt, int32_t mode); |
||||||
|
|
||||||
|
|
||||||
|
/** Processes the waveform to be generated, returning one sample. This should be called once per sample period.
|
||||||
|
*/ |
||||||
|
float Process(float &trig); |
||||||
|
|
||||||
|
/**
|
||||||
|
Sets the amplitude of the output signal. |
||||||
|
Input range: 0-1? |
||||||
|
*/ |
||||||
|
inline void SetAmp(float amp) { amp_ = amp; } |
||||||
|
/** Sets the frequency of the output signal in Hz.
|
||||||
|
Input range: Any positive value |
||||||
|
*/ |
||||||
|
inline void SetFreq(float freq) { freq_ = freq; } |
||||||
|
/** Sets the time it takes for a triggered note to end in seconds.
|
||||||
|
Input range: 0-1 |
||||||
|
*/ |
||||||
|
inline void SetDecay(float decay) { decay_ = decay; } |
||||||
|
/** Sets the dampening factor applied by the filter (based on PLUCK_MODE)
|
||||||
|
Input range: 0-1 |
||||||
|
*/ |
||||||
|
inline void SetDamp(float damp) { damp_ = damp; } |
||||||
|
/** Sets the mode of the algorithm.
|
||||||
|
*/ |
||||||
|
inline void SetMode(int32_t mode) { mode_ = mode; } |
||||||
|
/** Returns the current value for amp.
|
||||||
|
*/ |
||||||
|
inline float GetAmp() { return amp_; } |
||||||
|
/** Returns the current value for freq.
|
||||||
|
*/ |
||||||
|
inline float GetFreq() { return freq_; } |
||||||
|
/** Returns the current value for decay.
|
||||||
|
*/ |
||||||
|
inline float GetDecay() { return decay_; } |
||||||
|
/** Returns the current value for damp.
|
||||||
|
*/ |
||||||
|
inline float GetDamp() { return damp_; } |
||||||
|
/** Returns the current value for mode.
|
||||||
|
*/ |
||||||
|
inline int32_t GetMode() { return mode_; } |
||||||
|
|
||||||
|
private: |
||||||
|
void Reinit(); |
||||||
|
float amp_, freq_, decay_, damp_, ifreq_; |
||||||
|
float sicps_; |
||||||
|
int32_t phs256_, npts_, maxpts_; |
||||||
|
float * buf_; |
||||||
|
float sample_rate_; |
||||||
|
char init_; |
||||||
|
int32_t mode_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,158 @@ |
|||||||
|
#include "resonator.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void Resonator::Init(float position, int resolution, float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
SetFreq(440.f); |
||||||
|
SetStructure(.5f); |
||||||
|
SetBrightness(.5f); |
||||||
|
SetDamping(.5f); |
||||||
|
|
||||||
|
resolution_ = fmin(resolution, kMaxNumModes); |
||||||
|
|
||||||
|
for(int i = 0; i < resolution; ++i) |
||||||
|
{ |
||||||
|
mode_amplitude_[i] = cos(position * TWOPI_F) * 0.25f; |
||||||
|
} |
||||||
|
|
||||||
|
for(int i = 0; i < kMaxNumModes / kModeBatchSize; ++i) |
||||||
|
{ |
||||||
|
mode_filters_[i].Init(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline float NthHarmonicCompensation(int n, float stiffness) |
||||||
|
{ |
||||||
|
float stretch_factor = 1.0f; |
||||||
|
for(int i = 0; i < n - 1; ++i) |
||||||
|
{ |
||||||
|
stretch_factor += stiffness; |
||||||
|
if(stiffness < 0.0f) |
||||||
|
{ |
||||||
|
stiffness *= 0.93f; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
stiffness *= 0.98f; |
||||||
|
} |
||||||
|
} |
||||||
|
return 1.0f / stretch_factor; |
||||||
|
} |
||||||
|
|
||||||
|
float Resonator::Process(const float in) |
||||||
|
{ |
||||||
|
//convert Hz to cycles / sample
|
||||||
|
float out = 0.f; |
||||||
|
|
||||||
|
float stiffness = CalcStiff(structure_); |
||||||
|
float f0 = frequency_ * NthHarmonicCompensation(3, stiffness); |
||||||
|
float brightness = brightness_; |
||||||
|
|
||||||
|
float harmonic = f0; |
||||||
|
float stretch_factor = 1.0f; |
||||||
|
|
||||||
|
float input = damping_ * 79.7f; |
||||||
|
float q_sqrt = powf(2.f, input * ratiofrac_); |
||||||
|
|
||||||
|
float q = 500.0f * q_sqrt * q_sqrt; |
||||||
|
brightness *= 1.0f - structure_ * 0.3f; |
||||||
|
brightness *= 1.0f - damping_ * 0.3f; |
||||||
|
float q_loss = brightness * (2.0f - brightness) * 0.85f + 0.15f; |
||||||
|
|
||||||
|
float mode_q[kModeBatchSize]; |
||||||
|
float mode_f[kModeBatchSize]; |
||||||
|
float mode_a[kModeBatchSize]; |
||||||
|
int batch_counter = 0; |
||||||
|
|
||||||
|
ResonatorSvf<kModeBatchSize>* batch_processor = &mode_filters_[0]; |
||||||
|
|
||||||
|
for(int i = 0; i < resolution_; ++i) |
||||||
|
{ |
||||||
|
float mode_frequency = harmonic * stretch_factor; |
||||||
|
if(mode_frequency >= 0.499f) |
||||||
|
{ |
||||||
|
mode_frequency = 0.499f; |
||||||
|
} |
||||||
|
const float mode_attenuation = 1.0f - mode_frequency * 2.0f; |
||||||
|
|
||||||
|
mode_f[batch_counter] = mode_frequency; |
||||||
|
mode_q[batch_counter] = 1.0f + mode_frequency * q; |
||||||
|
mode_a[batch_counter] = mode_amplitude_[i] * mode_attenuation; |
||||||
|
++batch_counter; |
||||||
|
|
||||||
|
if(batch_counter == kModeBatchSize) |
||||||
|
{ |
||||||
|
batch_counter = 0; |
||||||
|
batch_processor |
||||||
|
->Process<ResonatorSvf<kModeBatchSize>::BAND_PASS, true>( |
||||||
|
mode_f, mode_q, mode_a, in, &out); |
||||||
|
++batch_processor; |
||||||
|
} |
||||||
|
|
||||||
|
stretch_factor += stiffness; |
||||||
|
if(stiffness < 0.0f) |
||||||
|
{ |
||||||
|
// Make sure that the partials do not fold back into negative frequencies.
|
||||||
|
stiffness *= 0.93f; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// This helps adding a few extra partials in the highest frequencies.
|
||||||
|
stiffness *= 0.98f; |
||||||
|
} |
||||||
|
harmonic += f0; |
||||||
|
q *= q_loss; |
||||||
|
} |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
void Resonator::SetFreq(float freq) |
||||||
|
{ |
||||||
|
frequency_ = freq / sample_rate_; |
||||||
|
} |
||||||
|
|
||||||
|
void Resonator::SetStructure(float structure) |
||||||
|
{ |
||||||
|
structure_ = fmax(fmin(structure, 1.f), 0.f); |
||||||
|
} |
||||||
|
|
||||||
|
void Resonator::SetBrightness(float brightness) |
||||||
|
{ |
||||||
|
brightness_ = fmax(fmin(brightness, 1.f), 0.f); |
||||||
|
} |
||||||
|
|
||||||
|
void Resonator::SetDamping(float damping) |
||||||
|
{ |
||||||
|
damping_ = fmax(fmin(damping, 1.f), 0.f); |
||||||
|
} |
||||||
|
|
||||||
|
float Resonator::CalcStiff(float sig) |
||||||
|
{ |
||||||
|
if(sig < .25f) |
||||||
|
{ |
||||||
|
sig = .25 - sig; |
||||||
|
sig = -sig * .25; |
||||||
|
} |
||||||
|
else if(sig < .3f) |
||||||
|
{ |
||||||
|
sig = 0.f; |
||||||
|
} |
||||||
|
else if(sig < .9f) |
||||||
|
{ |
||||||
|
sig -= .3f; |
||||||
|
sig *= stiff_frac_2; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
sig -= .9f; |
||||||
|
sig *= 10; // div by .1
|
||||||
|
sig *= sig; |
||||||
|
sig = 1.5 - cos(sig * PI_F) * .5f; |
||||||
|
} |
||||||
|
return sig; |
||||||
|
} |
@ -0,0 +1,183 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_RESONATOR_H |
||||||
|
#define DSY_RESONATOR_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <stddef.h> |
||||||
|
#include "Utility/dsp.h" |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
|
||||||
|
/** @file resonator.h */ |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
// We render 4 modes simultaneously since there are enough registers to hold
|
||||||
|
// all state variables.
|
||||||
|
/**
|
||||||
|
@brief SVF for use in the Resonator Class \n
|
||||||
|
@author Ported by Ben Sergentanis
|
||||||
|
@date Jan 2021
|
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/physical_modelling/resonator.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n |
||||||
|
*/ |
||||||
|
template <int batch_size> |
||||||
|
class ResonatorSvf |
||||||
|
{ |
||||||
|
public: |
||||||
|
enum FilterMode |
||||||
|
{ |
||||||
|
LOW_PASS, |
||||||
|
BAND_PASS, |
||||||
|
BAND_PASS_NORMALIZED, |
||||||
|
HIGH_PASS |
||||||
|
}; |
||||||
|
|
||||||
|
ResonatorSvf() {} |
||||||
|
~ResonatorSvf() {} |
||||||
|
|
||||||
|
void Init() |
||||||
|
{ |
||||||
|
for(int i = 0; i < batch_size; ++i) |
||||||
|
{ |
||||||
|
state_1_[i] = state_2_[i] = 0.0f; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template <FilterMode mode, bool add> |
||||||
|
void Process(const float* f, |
||||||
|
const float* q, |
||||||
|
const float* gain, |
||||||
|
const float in, |
||||||
|
float* out) |
||||||
|
{ |
||||||
|
float g[batch_size]; |
||||||
|
float r[batch_size]; |
||||||
|
float r_plus_g[batch_size]; |
||||||
|
float h[batch_size]; |
||||||
|
float state_1[batch_size]; |
||||||
|
float state_2[batch_size]; |
||||||
|
float gains[batch_size]; |
||||||
|
for(int i = 0; i < batch_size; ++i) |
||||||
|
{ |
||||||
|
g[i] = fasttan(f[i]); |
||||||
|
r[i] = 1.0f / q[i]; |
||||||
|
h[i] = 1.0f / (1.0f + r[i] * g[i] + g[i] * g[i]); |
||||||
|
r_plus_g[i] = r[i] + g[i]; |
||||||
|
state_1[i] = state_1_[i]; |
||||||
|
state_2[i] = state_2_[i]; |
||||||
|
gains[i] = gain[i]; |
||||||
|
} |
||||||
|
|
||||||
|
float s_in = in; |
||||||
|
float s_out = 0.0f; |
||||||
|
for(int i = 0; i < batch_size; ++i) |
||||||
|
{ |
||||||
|
const float hp |
||||||
|
= (s_in - r_plus_g[i] * state_1[i] - state_2[i]) * h[i]; |
||||||
|
const float bp = g[i] * hp + state_1[i]; |
||||||
|
state_1[i] = g[i] * hp + bp; |
||||||
|
const float lp = g[i] * bp + state_2[i]; |
||||||
|
state_2[i] = g[i] * bp + lp; |
||||||
|
s_out += gains[i] * ((mode == LOW_PASS) ? lp : bp); |
||||||
|
} |
||||||
|
if(add) |
||||||
|
{ |
||||||
|
*out++ += s_out; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
*out++ = s_out; |
||||||
|
} |
||||||
|
|
||||||
|
for(int i = 0; i < batch_size; ++i) |
||||||
|
{ |
||||||
|
state_1_[i] = state_1[i]; |
||||||
|
state_2_[i] = state_2[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
static constexpr float kPiPow3 = PI_F * PI_F * PI_F; |
||||||
|
static constexpr float kPiPow5 = kPiPow3 * PI_F * PI_F; |
||||||
|
static inline float fasttan(float f) |
||||||
|
{ |
||||||
|
const float a = 3.260e-01 * kPiPow3; |
||||||
|
const float b = 1.823e-01 * kPiPow5; |
||||||
|
float f2 = f * f; |
||||||
|
return f * (PI_F + f2 * (a + b * f2)); |
||||||
|
} |
||||||
|
|
||||||
|
float state_1_[batch_size]; |
||||||
|
float state_2_[batch_size]; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Resonant Body Simulation |
||||||
|
@author Ported by Ben Sergentanis
|
||||||
|
@date Jan 2021
|
||||||
|
Ported from pichenettes/eurorack/plaits/dsp/physical_modelling/resonator.h \n |
||||||
|
to an independent module. \n |
||||||
|
Original code written by Emilie Gillet in 2016. \n
|
||||||
|
*/ |
||||||
|
class Resonator |
||||||
|
{ |
||||||
|
public: |
||||||
|
Resonator() {} |
||||||
|
~Resonator() {} |
||||||
|
|
||||||
|
/** Initialize the module
|
||||||
|
\param position Offset the phase of the amplitudes. 0-1 |
||||||
|
\param resolution Quality vs speed scalar |
||||||
|
\param sample_rate Samplerate of the audio engine being run. |
||||||
|
*/ |
||||||
|
void Init(float position, int resolution, float sample_rate); |
||||||
|
|
||||||
|
/** Get the next sample_rate
|
||||||
|
\param in The signal to excited the resonant body |
||||||
|
*/ |
||||||
|
float Process(const float in); |
||||||
|
|
||||||
|
/** Resonator frequency.
|
||||||
|
\param freq Frequency in Hz. |
||||||
|
*/ |
||||||
|
void SetFreq(float freq); |
||||||
|
|
||||||
|
/** Changes the general charater of the resonator (stiffness, brightness)
|
||||||
|
\param structure Works best from 0-1 |
||||||
|
*/ |
||||||
|
void SetStructure(float structure); |
||||||
|
|
||||||
|
/** Set the brighness of the resonator
|
||||||
|
\param brightness Works best 0-1 |
||||||
|
*/ |
||||||
|
void SetBrightness(float brightness); |
||||||
|
|
||||||
|
/** How long the resonant body takes to decay.
|
||||||
|
\param damping Works best 0-1 |
||||||
|
*/ |
||||||
|
void SetDamping(float damping); |
||||||
|
|
||||||
|
private: |
||||||
|
int resolution_; |
||||||
|
float frequency_, brightness_, structure_, damping_; |
||||||
|
|
||||||
|
static constexpr int kMaxNumModes = 24; |
||||||
|
static constexpr int kModeBatchSize = 4; |
||||||
|
static constexpr float ratiofrac_ = 1.f / 12.f; |
||||||
|
static constexpr float stiff_frac_ = 1.f / 64.f; |
||||||
|
static constexpr float stiff_frac_2 = 1.f / .6f; |
||||||
|
|
||||||
|
float sample_rate_; |
||||||
|
|
||||||
|
float CalcStiff(float sig); |
||||||
|
|
||||||
|
float mode_amplitude_[kMaxNumModes]; |
||||||
|
ResonatorSvf<kModeBatchSize> mode_filters_[kMaxNumModes / kModeBatchSize]; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,124 @@ |
|||||||
|
#include "stringvoice.h" |
||||||
|
#include <algorithm> |
||||||
|
#include "../utility/dsp.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void StringVoice::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sample_rate_ = sample_rate; |
||||||
|
|
||||||
|
excitation_filter_.Init(sample_rate); |
||||||
|
string_.Init(sample_rate_); |
||||||
|
dust_.Init(); |
||||||
|
remaining_noise_samples_ = 0; |
||||||
|
|
||||||
|
SetSustain(false); |
||||||
|
SetFreq(440.f); |
||||||
|
SetAccent(.8f); |
||||||
|
SetStructure(.7f); |
||||||
|
SetBrightness(.2f); |
||||||
|
SetDamping(.7f); |
||||||
|
} |
||||||
|
|
||||||
|
void StringVoice::Reset() |
||||||
|
{ |
||||||
|
string_.Reset(); |
||||||
|
} |
||||||
|
|
||||||
|
void StringVoice::SetSustain(bool sustain) |
||||||
|
{ |
||||||
|
sustain_ = sustain; |
||||||
|
} |
||||||
|
|
||||||
|
void StringVoice::Trig() |
||||||
|
{ |
||||||
|
trig_ = true; |
||||||
|
} |
||||||
|
|
||||||
|
void StringVoice::SetFreq(float freq) |
||||||
|
{ |
||||||
|
string_.SetFreq(freq); |
||||||
|
f0_ = freq / sample_rate_; |
||||||
|
f0_ = fclamp(f0_, 0.f, .25f); |
||||||
|
} |
||||||
|
|
||||||
|
void StringVoice::SetAccent(float accent) |
||||||
|
{ |
||||||
|
accent_ = fclamp(accent, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
void StringVoice::SetStructure(float structure) |
||||||
|
{ |
||||||
|
structure = fclamp(structure, 0.f, 1.f); |
||||||
|
const float non_linearity |
||||||
|
= structure < 0.24f |
||||||
|
? (structure - 0.24f) * 4.166f |
||||||
|
: (structure > 0.26f ? (structure - 0.26f) * 1.35135f : 0.0f); |
||||||
|
string_.SetNonLinearity(non_linearity); |
||||||
|
} |
||||||
|
|
||||||
|
void StringVoice::SetBrightness(float brightness) |
||||||
|
{ |
||||||
|
brightness_ = fclamp(brightness, 0.f, 1.f); |
||||||
|
density_ = brightness_ * brightness_; |
||||||
|
} |
||||||
|
|
||||||
|
void StringVoice::SetDamping(float damping) |
||||||
|
{ |
||||||
|
damping_ = fclamp(damping, 0.f, 1.f); |
||||||
|
} |
||||||
|
|
||||||
|
float StringVoice::GetAux() |
||||||
|
{ |
||||||
|
return aux_; |
||||||
|
} |
||||||
|
|
||||||
|
float StringVoice::Process(bool trigger) |
||||||
|
{ |
||||||
|
const float brightness = brightness_ + .25 * accent_ * (1.f - brightness_); |
||||||
|
const float damping = damping_ + .25 * accent_ * (1.f - damping_); |
||||||
|
|
||||||
|
// Synthesize excitation signal.
|
||||||
|
if(trigger || trig_ || sustain_) |
||||||
|
{ |
||||||
|
trig_ = false; |
||||||
|
const float range = 72.0f; |
||||||
|
const float f = 4.0f * f0_; |
||||||
|
const float cutoff = fmin( |
||||||
|
f |
||||||
|
* powf(2.f, |
||||||
|
kOneTwelfth * (brightness * (2.0f - brightness) - 0.5f) |
||||||
|
* range), |
||||||
|
0.499f); |
||||||
|
const float q = sustain_ ? 1.0f : 0.5f; |
||||||
|
remaining_noise_samples_ = static_cast<size_t>(1.0f / f0_); |
||||||
|
excitation_filter_.SetFreq(cutoff * sample_rate_); |
||||||
|
excitation_filter_.SetRes(q); |
||||||
|
} |
||||||
|
|
||||||
|
float temp = 0.f; |
||||||
|
|
||||||
|
if(sustain_) |
||||||
|
{ |
||||||
|
const float dust_f = 0.00005f + 0.99995f * density_ * density_; |
||||||
|
dust_.SetDensity(dust_f); |
||||||
|
temp = dust_.Process() * (8.0f - dust_f * 6.0f) * accent_; |
||||||
|
} |
||||||
|
else if(remaining_noise_samples_) |
||||||
|
{ |
||||||
|
temp = 2.0f * rand() * kRandFrac - 1.0f; |
||||||
|
remaining_noise_samples_--; |
||||||
|
remaining_noise_samples_ = DSY_MAX(remaining_noise_samples_, 0.f); |
||||||
|
} |
||||||
|
|
||||||
|
excitation_filter_.Process(temp); |
||||||
|
temp = excitation_filter_.Low(); |
||||||
|
|
||||||
|
aux_ = temp; |
||||||
|
|
||||||
|
string_.SetBrightness(brightness); |
||||||
|
string_.SetDamping(damping); |
||||||
|
|
||||||
|
return string_.Process(temp); |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
#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 |
@ -0,0 +1,140 @@ |
|||||||
|
#include "blosc.h" |
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
|
||||||
|
void BlOsc::Init(float sample_rate) |
||||||
|
{ |
||||||
|
sampling_freq_ = sample_rate; |
||||||
|
half_sr_ = 0.5 * sampling_freq_; |
||||||
|
quarter_sr_ = sampling_freq_ * 0.25; |
||||||
|
sec_per_sample_ = 1.0 / sampling_freq_; |
||||||
|
two_over_sr_ = (float)(2.0 / sampling_freq_); |
||||||
|
four_over_sr_ = 4.0 / sampling_freq_; |
||||||
|
|
||||||
|
freq_ = 440; |
||||||
|
amp_ = 0.5; |
||||||
|
pw_ = 0.5; |
||||||
|
iota_ = 0; |
||||||
|
mode_ = WAVE_TRIANGLE; |
||||||
|
|
||||||
|
for(int i = 0; i < 2; i++) |
||||||
|
{ |
||||||
|
rec0_[i] = rec1_[i] = vec0_[i] = vec1_[i] = 0.0; |
||||||
|
} |
||||||
|
|
||||||
|
for(int i = 0; i < 4096; i++) |
||||||
|
{ |
||||||
|
vec2_[i] = 0.0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
float BlOsc::ProcessSquare() |
||||||
|
{ |
||||||
|
float out; |
||||||
|
float fSlow2 = fmin(2047.0, sampling_freq_ * (pw_ / freq_)); |
||||||
|
float fSlow5 = (float)((int)fSlow2 + 1) - fSlow2; |
||||||
|
float fSlow6 = (quarter_sr_ / freq_); |
||||||
|
float fSlow7 = (sec_per_sample_ * freq_); |
||||||
|
float fSlow8 = fSlow2 - (int)fSlow2; |
||||||
|
|
||||||
|
rec0_[0] = fmodf(rec0_[1] + fSlow7, 1.0); |
||||||
|
float fTemp0 = 2.0 * rec0_[0] - 1.0; |
||||||
|
fTemp0 *= fTemp0; //mult faster than fpow for squaring?
|
||||||
|
vec1_[0] = fTemp0; |
||||||
|
float fTemp1 = (fSlow6 * ((fTemp0 - vec1_[1]))); |
||||||
|
vec2_[iota_ & 4095] = fTemp1; |
||||||
|
|
||||||
|
out = amp_ |
||||||
|
* (0.0 |
||||||
|
- ((fSlow5 * vec2_[(iota_ - (int)fSlow2) & 4095] |
||||||
|
+ fSlow8 * vec2_[(iota_ - ((int)fSlow2 + 1)) & 4095]) |
||||||
|
- fTemp1)); |
||||||
|
rec0_[1] = rec0_[0]; |
||||||
|
vec1_[1] = vec1_[0]; |
||||||
|
iota_++; |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
float BlOsc::ProcessTriangle() |
||||||
|
{ |
||||||
|
float out; |
||||||
|
float fSlow1 = four_over_sr_ * (amp_ * freq_); |
||||||
|
float fSlow3 = half_sr_ / freq_; |
||||||
|
int iSlow4 = (int)fSlow3; |
||||||
|
int iSlow5 = 1 + iSlow4; |
||||||
|
float fSlow6 = iSlow5 - fSlow3; |
||||||
|
float fSlow7 = quarter_sr_ / freq_; |
||||||
|
float fSlow8 = sec_per_sample_ * freq_; |
||||||
|
float fSlow9 = fSlow3 - iSlow4; //decimal portion
|
||||||
|
|
||||||
|
|
||||||
|
rec1_[0] = fmodf((fSlow8 + rec1_[1]), 1.0); |
||||||
|
float fTemp0 = 2.0 * rec1_[0] - 1.0; |
||||||
|
fTemp0 *= fTemp0; //mult faster than fpow for squaring?
|
||||||
|
vec1_[0] = fTemp0; |
||||||
|
float fTemp1 = fSlow7 * (fTemp0 - vec1_[1]); |
||||||
|
vec2_[iota_ & 4095] = fTemp1; |
||||||
|
rec0_[0] = 0.0 |
||||||
|
- ((fSlow6 * vec2_[(iota_ - iSlow4) & 4095] |
||||||
|
+ fSlow9 * vec2_[(iota_ - iSlow5) & 4095]) |
||||||
|
- (.999 * rec0_[1] + fTemp1)); |
||||||
|
|
||||||
|
out = (float)(fSlow1 * rec0_[0]); |
||||||
|
rec1_[1] = rec1_[0]; |
||||||
|
rec0_[1] = rec0_[0]; |
||||||
|
vec1_[1] = vec1_[0]; |
||||||
|
iota_++; |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
float BlOsc::ProcessSaw() |
||||||
|
{ |
||||||
|
float out; |
||||||
|
//fSlow0 = Slider1 = freq
|
||||||
|
float fSlow1 = sampling_freq_ * (amp_ / freq_); |
||||||
|
float fSlow2 = (two_over_sr_ * freq_); |
||||||
|
float fSlow3 = (sampling_freq_ / freq_); |
||||||
|
|
||||||
|
rec0_[0] = fmod((1.0 + rec0_[1]), fSlow3); |
||||||
|
float fTemp0 = fSlow2 * rec0_[0] - 1.0; |
||||||
|
fTemp0 *= fTemp0; //mult faster than fpow for squaring?
|
||||||
|
vec0_[0] = fTemp0; |
||||||
|
vec1_[0] = 0.25; |
||||||
|
out = (float)(fSlow1 * ((fTemp0 - vec0_[1]) * vec1_[1])); |
||||||
|
rec0_[1] = rec0_[0]; |
||||||
|
vec0_[1] = vec0_[0]; |
||||||
|
vec1_[1] = vec1_[0]; |
||||||
|
|
||||||
|
return out; |
||||||
|
} |
||||||
|
|
||||||
|
void BlOsc::Reset() |
||||||
|
{ |
||||||
|
iota_ = 0; |
||||||
|
|
||||||
|
for(int i = 0; i < 2; i++) |
||||||
|
{ |
||||||
|
rec0_[i] = rec1_[i] = vec0_[i] = vec1_[i] = 0.0; |
||||||
|
} |
||||||
|
|
||||||
|
for(int i = 0; i < 4096; i++) |
||||||
|
{ |
||||||
|
vec2_[i] = 0.0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
float BlOsc::Process() |
||||||
|
{ |
||||||
|
switch(mode_) |
||||||
|
{ |
||||||
|
case WAVE_TRIANGLE: return ProcessTriangle(); |
||||||
|
case WAVE_SAW: return ProcessSaw(); |
||||||
|
case WAVE_SQUARE: return ProcessSquare(); |
||||||
|
default: break; |
||||||
|
} |
||||||
|
|
||||||
|
return 0.0; |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
#pragma once |
||||||
|
#ifndef DSY_BLOSC_H |
||||||
|
#define DSY_BLOSC_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
namespace daisysp |
||||||
|
{ |
||||||
|
/** Band Limited Oscillator
|
||||||
|
|
||||||
|
Based on bltriangle, blsaw, blsquare from soundpipe |
||||||
|
|
||||||
|
Original Author(s): Paul Batchelor, saw2 Faust by Julius Smith |
||||||
|
|
||||||
|
Ported by Ben Sergentanis, May 2020 |
||||||
|
*/ |
||||||
|
class BlOsc |
||||||
|
{ |
||||||
|
public: |
||||||
|
BlOsc() {} |
||||||
|
~BlOsc() {} |
||||||
|
/** Bl Waveforms
|
||||||
|
*/ |
||||||
|
enum Waveforms |
||||||
|
{ |
||||||
|
WAVE_TRIANGLE, |
||||||
|
WAVE_SAW, |
||||||
|
WAVE_SQUARE, |
||||||
|
WAVE_OFF, |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
/** -Initialize oscillator.
|
||||||
|
-Defaults to: 440Hz, .5 amplitude, .5 pw, Triangle. |
||||||
|
*/ |
||||||
|
void Init(float sample_rate); |
||||||
|
|
||||||
|
|
||||||
|
/** - Get next floating point oscillator sample.
|
||||||
|
*/ |
||||||
|
float Process(); |
||||||
|
|
||||||
|
|
||||||
|
/** - Float freq: Set oscillator frequency in Hz.
|
||||||
|
*/ |
||||||
|
inline void SetFreq(float freq) { freq_ = freq; }; |
||||||
|
/** - Float amp: Set oscillator amplitude, 0 to 1.
|
||||||
|
*/ |
||||||
|
inline void SetAmp(float amp) { amp_ = amp; }; |
||||||
|
/** - Float pw: Set square osc pulsewidth, 0 to 1. (no thru 0 at the moment)
|
||||||
|
*/ |
||||||
|
inline void SetPw(float pw) { pw_ = 1 - pw; }; |
||||||
|
/** - uint8_t waveform: select between waveforms from enum above.
|
||||||
|
- i.e. SetWaveform(BL_WAVEFORM_SAW); to set waveform to saw |
||||||
|
*/ |
||||||
|
inline void SetWaveform(uint8_t waveform) { mode_ = waveform; } |
||||||
|
|
||||||
|
/** - reset the phase of the oscillator.
|
||||||
|
*/ |
||||||
|
void Reset(); |
||||||
|
|
||||||
|
private: |
||||||
|
float rec0_[2], rec1_[2], vec0_[2], vec1_[2], vec2_[4096], freq_, amp_, pw_, |
||||||
|
sampling_freq_, half_sr_, quarter_sr_, sec_per_sample_, two_over_sr_, |
||||||
|
four_over_sr_; |
||||||
|
uint8_t mode_; |
||||||
|
int iota_; |
||||||
|
|
||||||
|
float ProcessSquare(); |
||||||
|
float ProcessTriangle(); |
||||||
|
float ProcessSaw(); |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -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,65 @@ |
|||||||
|
#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 "../utility/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,73 @@ |
|||||||
|
#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,166 @@ |
|||||||
|
#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,86 @@ |
|||||||
|
#include "../utility/dsp.h" |
||||||
|
#include "oscillator.h" |
||||||
|
|
||||||
|
using namespace daisysp; |
||||||
|
static inline float Polyblep(float phase_inc, float t); |
||||||
|
|
||||||
|
constexpr float TWO_PI_RECIP = 1.0f / TWOPI_F; |
||||||
|
|
||||||
|
float Oscillator::Process() |
||||||
|
{ |
||||||
|
float out, t; |
||||||
|
switch(waveform_) |
||||||
|
{ |
||||||
|
case WAVE_SIN: out = sinf(phase_); break; |
||||||
|
case WAVE_TRI: |
||||||
|
t = -1.0f + (2.0f * phase_ * TWO_PI_RECIP); |
||||||
|
out = 2.0f * (fabsf(t) - 0.5f); |
||||||
|
break; |
||||||
|
case WAVE_SAW: |
||||||
|
out = -1.0f * (((phase_ * TWO_PI_RECIP * 2.0f)) - 1.0f); |
||||||
|
break; |
||||||
|
case WAVE_RAMP: out = ((phase_ * TWO_PI_RECIP * 2.0f)) - 1.0f; break; |
||||||
|
case WAVE_SQUARE: out = phase_ < PI_F ? (1.0f) : -1.0f; break; |
||||||
|
case WAVE_POLYBLEP_TRI: |
||||||
|
t = phase_ * TWO_PI_RECIP; |
||||||
|
out = phase_ < PI_F ? 1.0f : -1.0f; |
||||||
|
out += Polyblep(phase_inc_, t); |
||||||
|
out -= Polyblep(phase_inc_, fmodf(t + 0.5f, 1.0f)); |
||||||
|
// 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; |
||||||
|
break; |
||||||
|
case WAVE_POLYBLEP_SAW: |
||||||
|
t = phase_ * TWO_PI_RECIP; |
||||||
|
out = (2.0f * t) - 1.0f; |
||||||
|
out -= Polyblep(phase_inc_, t); |
||||||
|
out *= -1.0f; |
||||||
|
break; |
||||||
|
case WAVE_POLYBLEP_SQUARE: |
||||||
|
t = phase_ * TWO_PI_RECIP; |
||||||
|
out = phase_ < PI_F ? 1.0f : -1.0f; |
||||||
|
out += Polyblep(phase_inc_, t); |
||||||
|
out -= Polyblep(phase_inc_, fmodf(t + 0.5f, 1.0f)); |
||||||
|
out *= 0.707f; // ?
|
||||||
|
break; |
||||||
|
default: out = 0.0f; break; |
||||||
|
} |
||||||
|
phase_ += phase_inc_; |
||||||
|
if(phase_ > TWOPI_F) |
||||||
|
{ |
||||||
|
phase_ -= TWOPI_F; |
||||||
|
eoc_ = true; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
eoc_ = false; |
||||||
|
} |
||||||
|
eor_ = (phase_ - phase_inc_ < PI_F && phase_ >= PI_F); |
||||||
|
|
||||||
|
return out * amp_; |
||||||
|
} |
||||||
|
|
||||||
|
float Oscillator::CalcPhaseInc(float f) |
||||||
|
{ |
||||||
|
return (TWOPI_F * f) * sr_recip_; |
||||||
|
} |
||||||
|
|
||||||
|
static float Polyblep(float phase_inc, float t) |
||||||
|
{ |
||||||
|
float dt = phase_inc * TWO_PI_RECIP; |
||||||
|
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,113 @@ |
|||||||
|
#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; |
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
/** 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_ < PI_F; } |
||||||
|
|
||||||
|
/** Returns true if cycle falling.
|
||||||
|
*/ |
||||||
|
inline bool IsFalling() { return phase_ >= PI_F; } |
||||||
|
|
||||||
|
/** 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 (mapped to 0.0-TWO_PI) to the current phase. Useful for PM and "FM" synthesis.
|
||||||
|
*/ |
||||||
|
void PhaseAdd(float _phase) { phase_ += (_phase * TWOPI_F); } |
||||||
|
/** 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_; |
||||||
|
float sr_, sr_recip_, phase_, phase_inc_; |
||||||
|
float last_out_, last_freq_; |
||||||
|
bool eor_, eoc_; |
||||||
|
}; |
||||||
|
} // namespace daisysp
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,147 @@ |
|||||||
|
#include "../utility/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; |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue