Added DaisySP

This commit is contained in:
2026-04-24 14:46:05 +02:00
parent 82190216c3
commit dd63b3aed4
96 changed files with 10613 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
#include <math.h>
#include "dcblock.h"
using namespace daisysp;
void DcBlock::Init(float sample_rate)
{
output_ = 0.0;
input_ = 0.0;
gain_ = 1.0 - 10.f / sample_rate;
}
float DcBlock::Process(float in)
{
float out;
out = in - input_ + (gain_ * output_);
output_ = out;
input_ = in;
return out;
}
+37
View File
@@ -0,0 +1,37 @@
/*
Copyright (c) 2020 Electrosmith, Corp
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
#pragma once
#ifndef DSY_DCBLOCK_H
#define DSY_DCBLOCK_H
#ifdef __cplusplus
namespace daisysp
{
/** Removes DC component of a signal
*/
class DcBlock
{
public:
DcBlock(){};
~DcBlock(){};
/** Initializes DcBlock module
*/
void Init(float sample_rate);
/** performs DcBlock Process
*/
float Process(float in);
private:
float input_, output_, gain_;
};
} // namespace daisysp
#endif
#endif
+129
View File
@@ -0,0 +1,129 @@
/*
Copyright (c) 2020 Electrosmith, Corp
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
#pragma once
#ifndef DSY_DELAY_H
#define DSY_DELAY_H
#include <stdlib.h>
#include <stdint.h>
namespace daisysp
{
/** Simple Delay line.
November 2019
Converted to Template December 2019
declaration example: (1 second of floats)
DelayLine<float, SAMPLE_RATE> del;
By: shensley
*/
template <typename T, size_t max_size>
class DelayLine
{
public:
DelayLine() {}
~DelayLine() {}
/** initializes the delay line by clearing the values within, and setting delay to 1 sample.
*/
void Init() { Reset(); }
/** clears buffer, sets write ptr to 0, and delay to 1 sample.
*/
void Reset()
{
for(size_t i = 0; i < max_size; i++)
{
line_[i] = T(0);
}
write_ptr_ = 0;
delay_ = 1;
}
/** sets the delay time in samples
If a float is passed in, a fractional component will be calculated for interpolating the delay line.
*/
inline void SetDelay(size_t delay)
{
frac_ = 0.0f;
delay_ = delay < max_size ? delay : max_size - 1;
}
/** sets the delay time in samples
If a float is passed in, a fractional component will be calculated for interpolating the delay line.
*/
inline void SetDelay(float delay)
{
int32_t int_delay = static_cast<int32_t>(delay);
frac_ = delay - static_cast<float>(int_delay);
delay_ = static_cast<size_t>(int_delay) < max_size ? int_delay
: max_size - 1;
}
/** writes the sample of type T to the delay line, and advances the write ptr
*/
inline void Write(const T sample)
{
line_[write_ptr_] = sample;
write_ptr_ = (write_ptr_ - 1 + max_size) % max_size;
}
/** returns the next sample of type T in the delay line, interpolated if necessary.
*/
inline const T Read() const
{
T a = line_[(write_ptr_ + delay_) % max_size];
T b = line_[(write_ptr_ + delay_ + 1) % max_size];
return a + (b - a) * frac_;
}
/** Read from a set location */
inline const T Read(float delay) const
{
int32_t delay_integral = static_cast<int32_t>(delay);
float delay_fractional = delay - static_cast<float>(delay_integral);
const T a = line_[(write_ptr_ + delay_integral) % max_size];
const T b = line_[(write_ptr_ + delay_integral + 1) % max_size];
return a + (b - a) * delay_fractional;
}
inline const T ReadHermite(float delay) const
{
int32_t delay_integral = static_cast<int32_t>(delay);
float delay_fractional = delay - static_cast<float>(delay_integral);
int32_t t = (write_ptr_ + delay_integral + max_size);
const T xm1 = line_[(t - 1) % max_size];
const T x0 = line_[(t) % max_size];
const T x1 = line_[(t + 1) % max_size];
const T x2 = line_[(t + 2) % max_size];
const float c = (x1 - xm1) * 0.5f;
const float v = x0 - x1;
const float w = c + v;
const float a = w + v + (x2 - x0) * 0.5f;
const float b_neg = w + a;
const float f = delay_fractional;
return (((a * f) - b_neg) * f + c) * f + x0;
}
inline const T Allpass(const T sample, size_t delay, const T coefficient)
{
T read = line_[(write_ptr_ + delay) % max_size];
T write = sample + coefficient * read;
Write(write);
return -write * coefficient + read;
}
private:
float frac_;
size_t write_ptr_;
size_t delay_;
T line_[max_size];
};
} // namespace daisysp
#endif
+352
View File
@@ -0,0 +1,352 @@
/*
Copyright (c) 2020 Electrosmith, Corp, Emilie Gillet
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
/** Helpful defines, functions, and other utilities for use in/with daisysp modules.
*/
#pragma once
#ifndef DSY_CORE_DSP
#define DSY_CORE_DSP
#include <cassert>
#include <cstdint>
#include <random>
#include <cmath>
/** PIs
*/
#define PI_F 3.1415927410125732421875f
#define TWOPI_F (2.0f * PI_F)
#define HALFPI_F (PI_F * 0.5f)
#define DSY_MIN(in, mn) (in < mn ? in : mn)
#define DSY_MAX(in, mx) (in > mx ? in : mx)
#define DSY_CLAMP(in, mn, mx) (DSY_MIN(DSY_MAX(in, mn), mx))
#define DSY_COUNTOF(_arr) (sizeof(_arr) / sizeof(_arr[0]))
namespace daisysp
{
//Avoids division for random floats. e.g. rand() * kRandFrac
static constexpr float kRandFrac = 1.f / (float)RAND_MAX;
//Convert from semitones to other units. e.g. 2 ^ (kOneTwelfth * x)
static constexpr float kOneTwelfth = 1.f / 12.f;
/** efficient floating point min/max
c/o stephen mccaul
*/
inline float fmax(float a, float b)
{
float r;
#ifdef __arm__
asm("vmaxnm.f32 %[d], %[n], %[m]" : [d] "=t"(r) : [n] "t"(a), [m] "t"(b) :);
#else
r = (a > b) ? a : b;
#endif // __arm__
return r;
}
inline float fmin(float a, float b)
{
float r;
#ifdef __arm__
asm("vminnm.f32 %[d], %[n], %[m]" : [d] "=t"(r) : [n] "t"(a), [m] "t"(b) :);
#else
r = (a < b) ? a : b;
#endif // __arm__
return r;
}
/** quick fp clamp
*/
inline float fclamp(float in, float min, float max)
{
return fmin(fmax(in, min), max);
}
/** From Musicdsp.org "Fast power and root estimates for 32bit floats)
Original code by Stefan Stenzel
These are approximations
*/
inline float fastpower(float f, int n)
{
long *lp, l;
lp = (long *)(&f);
l = *lp;
l -= 0x3F800000;
l <<= (n - 1);
l += 0x3F800000;
*lp = l;
return f;
}
inline float fastroot(float f, int n)
{
long *lp, l;
lp = (long *)(&f);
l = *lp;
l -= 0x3F800000;
l >>= (n - 1);
l += 0x3F800000;
*lp = l;
return f;
}
/** Significantly more efficient than fmodf(x, 1.0f) for calculating
* the decimal part of a floating point value.
*/
inline float fastmod1f(float x)
{
return x - floorf(x);
}
/** From http://openaudio.blogspot.com/2017/02/faster-log10-and-pow.html
No approximation, pow10f(x) gives a 90% speed increase over powf(10.f, x)
*/
inline float pow10f(float f)
{
return expf(2.302585092994046f * f);
}
/* Original code for fastlog2f by Dr. Paul Beckmann from the ARM community forum, adapted from the CMSIS-DSP library
About 25% performance increase over std::log10f
*/
inline float fastlog2f(float f)
{
float frac;
int exp;
frac = frexpf(fabsf(f), &exp);
f = 1.23149591368684f;
f *= frac;
f += -4.11852516267426f;
f *= frac;
f += 6.02197014179219f;
f *= frac;
f += -3.13396450166353f;
f += exp;
return (f);
}
inline float fastlog10f(float f)
{
return fastlog2f(f) * 0.3010299956639812f;
}
/** Midi to frequency helper
*/
inline float mtof(float m)
{
return powf(2, (m - 69.0f) / 12.0f) * 440.0f;
}
/** one pole lpf
out is passed by reference, and must be retained between
calls to properly filter the signal
coeff can be calculated:
coeff = 1.0 / (time * sample_rate) ; where time is in seconds
*/
inline void fonepole(float &out, float in, float coeff)
{
out += coeff * (in - out);
}
/** Curves to use with the fmap function */
enum class Mapping
{
LINEAR,
EXP,
LOG,
};
/** Maps a float between a specified range, using a specified curve.
*
* \param in a value between 0 to 1 that will be mapped to the new range.
* \param min the new minimum value
* \param max the new maxmimum value
* \param curve a Mapping Value to adjust the response curve of the transformation
* defaults to Linear. @see Mapping
*
* When using the log curve min and max, must be greater than zero.
*
* \retval returns the transformed float within the new range.
*/
inline float
fmap(float in, float min, float max, Mapping curve = Mapping::LINEAR)
{
switch(curve)
{
case Mapping::EXP:
return fclamp(min + (in * in) * (max - min), min, max);
case Mapping::LOG:
{
const float a = 1.f / log10f(max / min);
return fclamp(min * powf(10, in / a), min, max);
}
case Mapping::LINEAR:
default: return fclamp(min + in * (max - min), min, max);
}
}
/** Simple 3-point median filter
c/o stephen mccaul
*/
template <typename T>
T median(T a, T b, T c)
{
return (b < a) ? (b < c) ? (c < a) ? c : a : b
: (a < c) ? (c < b) ? c : b : a;
}
/** Ported from pichenettes/eurorack/plaits/dsp/oscillator/oscillator.h
*/
inline float ThisBlepSample(float t)
{
return 0.5f * t * t;
}
/** Ported from pichenettes/eurorack/plaits/dsp/oscillator/oscillator.h
*/
inline float NextBlepSample(float t)
{
t = 1.0f - t;
return -0.5f * t * t;
}
/** Ported from pichenettes/eurorack/plaits/dsp/oscillator/oscillator.h
*/
inline float NextIntegratedBlepSample(float t)
{
const float t1 = 0.5f * t;
const float t2 = t1 * t1;
const float t4 = t2 * t2;
return 0.1875f - t1 + 1.5f * t2 - t4;
}
/** Ported from pichenettes/eurorack/plaits/dsp/oscillator/oscillator.h
*/
inline float ThisIntegratedBlepSample(float t)
{
return NextIntegratedBlepSample(1.0f - t);
}
/** Soft Limiting function ported extracted from pichenettes/stmlib */
inline float SoftLimit(float x)
{
return x * (27.f + x * x) / (27.f + 9.f * x * x);
}
/** Soft Clipping function extracted from pichenettes/stmlib */
inline float SoftClip(float x)
{
if(x < -3.0f)
return -1.0f;
else if(x > 3.0f)
return 1.0f;
else
return SoftLimit(x);
}
/** Quick check for Invalid float values (NaN, Inf, out of range)
** \param x value passed by reference, replaced by y if invalid.
** \param y value to replace x if invalidity is found.
**
** When DEBUG is true in the build, this will halt
** execution for tracing the reason for the invalidity. */
inline void TestFloat(float &x, float y = 0.f)
{
if(!std::isnormal(x) && x != 0)
{
#if defined(__arm__) && defined(DEBUG)
asm("bkpt 255");
#else
x = y;
#endif
}
}
/** Based on soft saturate from:
[musicdsp.org](musicdsp.org/en/latest/Effects/42-soft-saturation.html)
Bram de Jong (2002-01-17)
This still needs to be tested/fixed. Definitely does some weird stuff
described as:
x < a:
f(x) = x
x > a:
f(x) = a + (x-a)/(1+((x-a)/(1-a))^2)
x > 1:
f(x) = (a + 1)/2
*/
inline float soft_saturate(float in, float thresh)
{
bool flip;
float val, out;
//val = fabsf(in);
out = 0.f;
flip = in < 0.0f;
val = flip ? -in : in;
if(val < thresh)
{
out = in;
}
else if(val > 1.0f)
{
out = (thresh + 1.0f) / 2.0f;
if(flip)
out *= -1.0f;
}
else if(val > thresh)
{
float temp;
temp = (val - thresh) / (1 - thresh);
out = thresh + (val - thresh) / (1.0f + (temp * temp));
if(flip)
out *= -1.0f;
}
return out;
// return val < thresh
// ? val
// : val > 1.0f
// ? (thresh + 1.0f) / 2.0f
// : thresh
// + (val - thresh)
// / (1.0f
// + (((val - thresh) / (1.0f - thresh))
// * ((val - thresh) / (1.0f - thresh))));
}
constexpr bool is_power2(uint32_t x)
{
return ((x - 1) & x) == 0;
}
/** Prior to C++14 constexpr functions were required to be a single return statement.
* So this clause guards against that behavior to allow the library, and this function
* to continue to work with C++11.
* The function itself is not currently (12 May 2021) used within the library itself.
*/
#if __cplusplus <= 201103L
inline uint32_t get_next_power2(uint32_t x)
#else
constexpr uint32_t get_next_power2(uint32_t x)
#endif
{
x--;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
x++;
assert(is_power2(x));
return x;
}
} // namespace daisysp
#endif
#ifdef DSY_CUSTOM_DSP
#include "custom_dsp.h"
#endif
+314
View File
@@ -0,0 +1,314 @@
/*
Copyright (c) 2020 Electrosmith, Corp
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
#pragma once
#include <algorithm>
#include "dsp.h"
namespace daisysp
{
/** Multimode audio looper
*
* Modes are:
* - Normal
* - Onetime Dub
* - Replace
* - Frippertronics
*
* Read more about the looper modes in the mode enum documentation.
*/
class Looper
{
public:
Looper() {}
~Looper() {}
/**
** Normal Mode: Input is added to the existing loop infinitely while recording
**
** Onetime Dub Mode: Recording starts at the first sample of the buffer and is added
** to the existing buffer contents. Recording automatically stops after one full loop.
**
** Replace Mode: Audio in the buffer is replaced while recording is on.
**
** Frippertronics Mode: infinite looping recording with fixed decay on each loop. The module acts like tape-delay set up.
*/
enum class Mode
{
NORMAL,
ONETIME_DUB,
REPLACE,
FRIPPERTRONICS,
};
void Init(float *mem, size_t size)
{
buffer_size_ = size;
buff_ = mem;
InitBuff();
state_ = State::EMPTY;
mode_ = Mode::NORMAL;
half_speed_ = false;
reverse_ = false;
rec_queue_ = false;
win_idx_ = 0;
increment_size = 1.0;
}
/** Handles reading/writing to the Buffer depending on the mode. */
float Process(const float input)
{
float sig = 0.f;
float inc;
bool hitloop = false;
// Record forward at normal speed during the first loop no matter what.
inc = state_ == State::EMPTY || state_ == State::REC_FIRST
? 1.f
: GetIncrementSize();
win_ = WindowVal(win_idx_ * kWindowFactor);
switch(state_)
{
case State::EMPTY:
sig = 0.0f;
pos_ = 0;
recsize_ = 0;
break;
case State::REC_FIRST:
sig = 0.f;
Write(pos_, input * win_);
if(win_idx_ < kWindowSamps - 1)
win_idx_ += 1;
recsize_ = pos_;
pos_ += inc;
if(pos_ > buffer_size_ - 1)
{
state_ = State::PLAYING;
recsize_ = pos_ - 1;
pos_ = 0;
}
break;
case State::PLAYING:
sig = Read(pos_);
/** This is a way of 'seamless looping'
** The first N samps after recording is done are recorded with the input faded out.
*/
if(win_idx_ < kWindowSamps - 1)
{
Write(pos_, sig + input * (1.f - win_));
win_idx_ += 1;
}
pos_ += inc;
if(pos_ > recsize_ - 1)
{
pos_ = 0;
hitloop = true;
}
else if(pos_ < 0)
{
pos_ = recsize_ - 1;
hitloop = true;
}
if(hitloop)
{
if(rec_queue_ && mode_ == Mode::ONETIME_DUB)
{
rec_queue_ = false;
state_ = State::REC_DUB;
win_idx_ = 0;
}
}
break;
case State::REC_DUB:
sig = Read(pos_);
switch(mode_)
{
case Mode::REPLACE: Write(pos_, input * win_); break;
case Mode::FRIPPERTRONICS:
Write(pos_, (input * win_) + (sig * kFripDecayVal));
break;
case Mode::NORMAL:
case Mode::ONETIME_DUB:
default: Write(pos_, (input * win_) + sig); break;
}
if(win_idx_ < kWindowSamps - 1)
win_idx_ += 1;
pos_ += inc;
if(pos_ > recsize_ - 1)
{
pos_ = 0;
hitloop = true;
}
else if(pos_ < 0)
{
pos_ = recsize_ - 1;
hitloop = true;
}
if(hitloop && mode_ == Mode::ONETIME_DUB)
{
state_ = State::PLAYING;
win_idx_ = 0;
}
break;
default: break;
}
near_beginning_ = state_ != State::EMPTY && !Recording() && pos_ < 4800
? true
: false;
return sig;
}
/** Effectively erases the buffer
** Note: This does not actually change what is in the buffer */
inline void Clear() { state_ = State::EMPTY; }
/** Engages/Disengages the recording, depending on Mode.
** In all modes, the first time this is triggered a new loop will be started.
** The second trigger will set the loop size, and begin playback of the loop.
*/
inline void TrigRecord()
{
switch(state_)
{
case State::EMPTY:
pos_ = 0;
recsize_ = 0;
state_ = State::REC_FIRST;
half_speed_ = false;
reverse_ = false;
break;
case State::REC_FIRST:
case State::REC_DUB: state_ = State::PLAYING; break;
case State::PLAYING:
if(mode_ == Mode::ONETIME_DUB)
rec_queue_ = true;
else
state_ = State::REC_DUB;
break;
default: state_ = State::EMPTY; break;
}
if(!rec_queue_)
win_idx_ = 0;
}
/** Returns true if the looper is currently being written to. */
inline const bool Recording() const
{
return state_ == State::REC_DUB || state_ == State::REC_FIRST;
}
inline const bool RecordingQueued() const { return rec_queue_; }
/** Increments the Mode by one step useful for buttons, etc. that need to step through the Looper modes. */
inline void IncrementMode()
{
int m = static_cast<int>(mode_);
m = m + 1;
if(m > kNumModes - 1)
m = 0;
mode_ = static_cast<Mode>(m);
}
/** Sets the recording mode to the specified Mode. */
inline void SetMode(Mode mode) { mode_ = mode; }
/** Returns the specific recording mode that is currently set. */
inline const Mode GetMode() const { return mode_; }
inline void ToggleReverse() { reverse_ = !reverse_; }
inline void SetReverse(bool state) { reverse_ = state; }
inline bool GetReverse() const { return reverse_; }
inline void ToggleHalfSpeed() { half_speed_ = !half_speed_; }
inline void SetHalfSpeed(bool state) { half_speed_ = state; }
inline bool GetHalfSpeed() const { return half_speed_; }
inline bool IsNearBeginning() const { return near_beginning_; }
inline float GetIncrementSize() const
{
float inc = increment_size;
if(half_speed_)
inc *= 0.5f;
return reverse_ ? -inc : inc;
}
void SetIncrementSize(float increment) { increment_size = increment; }
inline float GetPos() const { return pos_; }
inline size_t GetRecSize() const { return recsize_; }
private:
/** Constants */
/** Decay value for frippertronics mode is sin(PI / 4) */
static constexpr float kFripDecayVal = 0.7071067811865476f;
static constexpr int kNumModes = 4;
static constexpr int kNumPlaybackSpeeds = 3;
static constexpr int kWindowSamps = 1200;
static constexpr float kWindowFactor = (1.f / kWindowSamps);
/** Private Member Functions */
/** Initialize the buffer */
void InitBuff() { std::fill(&buff_[0], &buff_[buffer_size_ - 1], 0); }
/** Get a floating point sample from the buffer */
inline const float Read(size_t pos) const { return buff_[pos]; }
/** Reads from a specified point in the delay line using linear interpolation */
float ReadF(float pos)
{
float a, b, frac;
uint32_t i_idx = static_cast<uint32_t>(pos);
frac = pos - i_idx;
a = buff_[i_idx];
b = buff_[(i_idx + 1) % buffer_size_];
return a + (b - a) * frac;
}
/** Write to a known location in the buffer */
inline void Write(size_t pos, float val) { buff_[pos] = val; }
/** Linear to Constpower approximation for windowing*/
float WindowVal(float in) { return sin(HALFPI_F * in); }
// Private Enums
/** Internal looper state */
enum class State
{
EMPTY,
REC_FIRST,
PLAYING,
REC_DUB,
};
/** Private Member Variables */
Mode mode_;
State state_;
float *buff_;
size_t buffer_size_;
float pos_, win_;
size_t win_idx_;
bool half_speed_;
bool reverse_;
size_t recsize_;
bool rec_queue_;
bool near_beginning_;
float increment_size;
};
} // namespace daisysp
+44
View File
@@ -0,0 +1,44 @@
/*
Copyright (c) 2020 Electrosmith, Corp, Paul Batchelor
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
#pragma once
#ifndef DSY_MAYTRIG_H
#define DSY_MAYTRIG_H
#include <stdint.h>
#ifdef __cplusplus
namespace daisysp
{
/** Probabilistic trigger module
Original author(s) : Paul Batchelor
Ported from soundpipe by Ben Sergentanis, May 2020
*/
class Maytrig
{
public:
Maytrig() {}
~Maytrig() {}
/** probabilistically generates triggers
\param prob (1 always returns true, 0 always false)
\return given a probability 0 to 1, returns true or false.
*/
inline float Process(float prob)
{
return ((float)rand() / (float)RAND_MAX) <= prob ? true : false;
}
private:
};
} // namespace daisysp
#endif
#endif
+30
View File
@@ -0,0 +1,30 @@
#include <math.h>
#include "metro.h"
#include "dsp.h"
using namespace daisysp;
void Metro::Init(float freq, float sample_rate)
{
freq_ = freq;
phs_ = 0.0f;
sample_rate_ = sample_rate;
phs_inc_ = (TWOPI_F * freq_) / sample_rate_;
}
uint8_t Metro::Process()
{
phs_ += phs_inc_;
if(phs_ >= TWOPI_F)
{
phs_ -= TWOPI_F;
return 1;
}
return 0;
}
void Metro::SetFreq(float freq)
{
freq_ = freq;
phs_inc_ = (TWOPI_F * freq_) / sample_rate_;
}
+54
View File
@@ -0,0 +1,54 @@
/*
Copyright (c) 2020 Electrosmith, Corp
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
#pragma once
#ifndef DSY_METRO_H
#define DSY_METRO_H
#include <stdint.h>
#ifdef __cplusplus
namespace daisysp
{
/** Creates a clock signal at a specific frequency.
*/
class Metro
{
public:
Metro() {}
~Metro() {}
/** Initializes Metro module.
Arguments:
- freq: frequency at which new clock signals will be generated
Input Range:
- sample_rate: sample rate of audio engine
Input range:
*/
void Init(float freq, float sample_rate);
/** checks current state of Metro object and updates state if necesary.
*/
uint8_t Process();
/** resets phase to 0
*/
inline void Reset() { phs_ = 0.0f; }
/** Sets frequency at which Metro module will run at.
*/
void SetFreq(float freq);
/** Returns current value for frequency.
*/
inline float GetFreq() { return freq_; }
private:
float freq_;
float phs_, sample_rate_, phs_inc_;
};
} // namespace daisysp
#endif
#endif
+69
View File
@@ -0,0 +1,69 @@
/*
Copyright (c) 2020 Electrosmith, Corp, Paul Batchelor
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
#pragma once
#ifndef DSY_SAMPLEHOLD_H
#define DSY_SAMPLEHOLD_H
#include <stdint.h>
#ifdef __cplusplus
namespace daisysp
{
/** Dual track and hold / Sample and hold module. \n
Ported from soundpipe by Ben Sergentanis, June 2020.
@author Paul Batchelor
@date 2015
*/
class SampleHold
{
public:
SampleHold() {}
~SampleHold() {}
enum Mode
{
MODE_SAMPLE_HOLD,
MODE_TRACK_HOLD,
MODE_LAST,
};
/** Process the next sample. Both sample and track and hold are run in parallel
\param trigger Trigger the sample/track and hold
\param input Signal to be sampled/tracked and held
\param mode Whether to output the tracked or sampled values.
*/
inline float
Process(bool trigger, float input, Mode mode = MODE_SAMPLE_HOLD)
{
Update(trigger, input);
return mode == MODE_SAMPLE_HOLD ? sample_ : track_;
}
private:
float track_ = 0;
float sample_ = 0;
bool previous_ = false;
inline void Update(bool trigger, float input)
{
if(trigger)
{
if(!previous_)
{
sample_ = input;
}
track_ = input;
}
previous_ = trigger;
}
};
} // namespace daisysp
#endif
#endif
+85
View File
@@ -0,0 +1,85 @@
/*
Copyright (c) 2020 Electrosmith, Corp, Emilie Gillet
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
#pragma once
#ifndef DSY_SMOOTHRANDOM_H
#define DSY_SMOOTHRANDOM_H
#include "dsp.h"
#include <stdint.h>
#include <stdlib.h>
#ifdef __cplusplus
/** @file smooth_random.h */
namespace daisysp
{
/**
@brief Smooth random generator for internal modulation. \n
@author Ported by Ben Sergentanis
@date Jan 2021
Ported from pichenettes/eurorack/plaits/dsp/noise/smooth_random_generator.h \n
to an independent module. \n
Original code written by Emilie Gillet in 2016. \n
*/
class SmoothRandomGenerator
{
public:
SmoothRandomGenerator() {}
~SmoothRandomGenerator() {}
/** Initialize the module
\param sample_rate Audio engine sample rate.
*/
void Init(float sample_rate)
{
sample_rate_ = sample_rate;
SetFreq(1.f);
phase_ = 0.0f;
from_ = 0.0f;
interval_ = 0.0f;
}
/** Get the next float. Ranges from -1 to 1. */
float Process()
{
phase_ += frequency_;
if(phase_ >= 1.0f)
{
phase_ -= 1.0f;
from_ += interval_;
interval_ = rand() * kRandFrac * 2.0f - 1.0f - from_;
}
float t = phase_ * phase_ * (3.0f - 2.0f * phase_);
return from_ + interval_ * t;
}
/** How often to slew to a new random value
\param freq Rate in Hz
*/
void SetFreq(float freq)
{
freq = freq / sample_rate_;
frequency_ = fclamp(freq, 0.f, 1.f);
}
private:
float frequency_;
float phase_;
float from_;
float interval_;
float sample_rate_;
static constexpr float kRandFrac = 1.f / (float)RAND_MAX;
};
} // namespace daisysp
#endif
#endif