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