Browse Source

Added Decimator, SlewLimiter, Trigger, other cleanup

tags/v0.3.0
Andrew Belt 7 years ago
parent
commit
d9c803962b
7 changed files with 192 additions and 34 deletions
  1. +0
    -1
      include/app.hpp
  2. +33
    -6
      include/components.hpp
  3. +94
    -0
      include/dsp.hpp
  4. +3
    -0
      include/engine.hpp
  5. +8
    -19
      include/math.hpp
  6. +14
    -8
      src/app/ModuleWidget.cpp
  7. +40
    -0
      src/dsp.cpp

+ 0
- 1
include/app.hpp View File

@@ -41,7 +41,6 @@ struct ModuleWidget : OpaqueWidget {
void fromJson(json_t *root);
void disconnectPorts();
void resetParams();
void cloneParams(ModuleWidget *source);

void draw(NVGcontext *vg);



+ 33
- 6
include/components.hpp View File

@@ -5,6 +5,21 @@
namespace rack {


////////////////////
// Colors
////////////////////

#define COLOR_BLACK_TRANSPARENT nvgRGBA(0x00, 0x00, 0x00, 0x00)
#define COLOR_BLACK nvgRGB(0x00, 0x00, 0x00)
#define COLOR_WHITE nvgRGB(0xff, 0xff, 0xff)
#define COLOR_RED nvgRGB(0xed, 0x2c, 0x24)
#define COLOR_ORANGE nvgRGB(0xf2, 0xb1, 0x20)
#define COLOR_YELLOW nvgRGB(0xf9, 0xdf, 0x1c)
#define COLOR_GREEN nvgRGB(0x90, 0xc7, 0x3e)
#define COLOR_CYAN nvgRGB(0x22, 0xe6, 0xef)
#define COLOR_BLUE nvgRGB(0x29, 0xb2, 0xef)
#define COLOR_PURPLE nvgRGB(0xd5, 0x2b, 0xed)

////////////////////
// Knobs
////////////////////
@@ -380,19 +395,19 @@ struct ColorValueLight : ValueLight {

struct RedValueLight : ColorValueLight {
RedValueLight() {
baseColor = nvgRGB(0xed, 0x2c, 0x24);
baseColor = COLOR_RED;
}
};

struct YellowValueLight : ColorValueLight {
YellowValueLight() {
baseColor = nvgRGB(0xf9, 0xdf, 0x1c);
baseColor = COLOR_YELLOW;
}
};

struct GreenValueLight : ColorValueLight {
GreenValueLight() {
baseColor = nvgRGB(0x90, 0xc7, 0x3e);
baseColor = COLOR_GREEN;
}
};

@@ -408,8 +423,19 @@ struct PolarityLight : ValueLight {

struct GreenRedPolarityLight : PolarityLight {
GreenRedPolarityLight() {
posColor = nvgRGB(0x90, 0xc7, 0x3e);
negColor = nvgRGB(0xed, 0x2c, 0x24);
posColor = COLOR_GREEN;
negColor = COLOR_RED;
}
};

struct ModeValueLight : ValueLight {
std::vector<NVGcolor> colors;
void step() {
int mode = clampi((int)roundf(getf(value)), 0, colors.size());
color = colors[mode];
}
void addColor(NVGcolor color) {
colors.push_back(color);
}
};

@@ -517,7 +543,8 @@ struct ScrewBlack : SVGScrew {

struct LightPanel : Panel {
LightPanel() {
backgroundColor = nvgRGB(0xe8, 0xe8, 0xe8);
// backgroundColor = nvgRGB(0xe8, 0xe8, 0xe8);
backgroundColor = nvgRGB(0xf4, 0xf4, 0xf4);
borderColor = nvgRGB(0xac, 0xac, 0xac);
}
};


+ 94
- 0
include/dsp.hpp View File

@@ -74,6 +74,13 @@ struct SimpleFFT {
};


typedef void (*stepCallback)(float x, const float y[], float dydt[]);
/** Solve an ODE system using the 1st order Euler method */
void stepEuler(stepCallback f, float x, float dx, float y[], int len);
/** Solve an ODE system using the 4th order Runge-Kutta method */
void stepRK4(stepCallback f, float x, float dx, float y[], int len);


/** A simple cyclic buffer.
S must be a power of 2.
push() is constant time O(1)
@@ -247,10 +254,14 @@ struct SampleRateConverter {
~SampleRateConverter() {
src_delete(state);
}
/** output_sample_rate / input_sample_rate */
void setRatio(float r) {
src_set_ratio(state, r);
data.src_ratio = r;
}
void setRatioSmooth(float r) {
data.src_ratio = r;
}
/** `in` and `out` are interlaced with the number of channels */
void process(const Frame<CHANNELS> *in, int *inFrames, Frame<CHANNELS> *out, int *outFrames) {
// Old versions of libsamplerate use float* here instead of const float*
@@ -268,6 +279,65 @@ struct SampleRateConverter {
};


/** Perform a direct convolution
x[-len + 1] to x[0] must be defined
*/
inline float convolve(const float *x, const float *kernel, int len) {
float y = 0.0;
for (int i = 0; i < len; i++) {
y += x[-i] * kernel[i];
}
return y;
}

inline void blackmanHarrisWindow(float *x, int n) {
const float a0 = 0.35875;
const float a1 = 0.48829;
const float a2 = 0.14128;
const float a3 = 0.01168;
for (int i = 0; i < n; i++) {
x[i] *= a0
- a1 * cosf(2 * M_PI * i / (n - 1))
+ a2 * cosf(4 * M_PI * i / (n - 1))
- a3 * cosf(6 * M_PI * i / (n - 1));
}
}

inline void boxcarFIR(float *x, int n, float cutoff) {
for (int i = 0; i < n; i++) {
float t = (float)i / (n - 1) * 2.0 - 1.0;
x[i] = sincf(t * n * cutoff);
}
}


template<int OVERSAMPLE, int QUALITY>
struct Decimator {
DoubleRingBuffer<float, OVERSAMPLE*QUALITY> inBuffer;
float kernel[OVERSAMPLE*QUALITY];

Decimator(float cutoff = 0.9) {
boxcarFIR(kernel, OVERSAMPLE*QUALITY, cutoff * 0.5 / OVERSAMPLE);
blackmanHarrisWindow(kernel, OVERSAMPLE*QUALITY);
// The sum of the kernel should be 1
float sum = 0.0;
for (int i = 0; i < OVERSAMPLE*QUALITY; i++) {
sum += kernel[i];
}
for (int i = 0; i < OVERSAMPLE*QUALITY; i++) {
kernel[i] /= sum;
}
}
float process(float *in) {
memcpy(inBuffer.endData(), in, OVERSAMPLE*sizeof(float));
inBuffer.endIncr(OVERSAMPLE);
float out = convolve(inBuffer.endData() + OVERSAMPLE*QUALITY, kernel, OVERSAMPLE*QUALITY);
// Ignore the ring buffer's start position
return out;
}
};


// Pre-made minBLEP samples in minBLEP.cpp
extern const float minblep_16_32[];

@@ -340,4 +410,28 @@ struct PeakFilter {
};


struct SlewLimiter {
float rise = 1.0;
float fall = 1.0;
float out = 0.0;
float process(float in) {
float delta = clampf(in - out, -fall, rise);
out += delta;
return out;
}
};


/** Triggered when input value rises above 0.0 */
struct Trigger {
float lastIn = 0.0;
/** Returns whether a trigger is detected */
bool process(float in) {
bool triggered = (lastIn <= 0.0 && in > 0.0);
lastIn = in;
return triggered;
}
};


} // namespace rack

+ 3
- 0
include/engine.hpp View File

@@ -1,5 +1,6 @@
#pragma once
#include <vector>
#include <jansson.h>
#include "util.hpp"


@@ -20,6 +21,8 @@ struct Module {

/** Advances the module by 1 audio frame with duration 1.0 / gSampleRate */
virtual void step() {}
virtual json_t *toJsonData() { return NULL; }
virtual void fromJsonData(json_t *root) {}
};

struct Wire {


+ 8
- 19
include/math.hpp View File

@@ -17,6 +17,11 @@ inline int maxi(int a, int b) {
return a > b ? a : b;
}

/** Limits a value between a minimum and maximum */
inline int clampi(int x, int min, int max) {
return x > max ? max : x < min ? min : x;
}

inline int absi(int a) {
return a >= 0 ? a : -a;
}
@@ -49,30 +54,14 @@ inline float sgnf(float x) {
return copysignf(1.0, x);
}

inline float radtodeg(float x) {
return x * (180.0 / M_PI);
}

inline float degtorad(float x) {
return x * (M_PI / 180.0);
}

/** Limits a value between a minimum and maximum
If min > max for some reason, returns min
*/
/** Limits a value between a minimum and maximum */
inline float clampf(float x, float min, float max) {
if (x > max)
x = max;
if (x < min)
x = min;
return x;
return x > max ? max : x < min ? min : x;
}

/** If the magnitude of x if less than eps, return 0 */
inline float chopf(float x, float eps) {
if (x < eps && x > -eps)
return 0.0;
return x;
return -eps < x && x < eps ? 0.0 : x;
}

inline float mapf(float x, float xMin, float xMax, float yMin, float yMax) {


+ 14
- 8
src/app/ModuleWidget.cpp View File

@@ -56,6 +56,11 @@ json_t *ModuleWidget::toJson() {
json_array_append_new(paramsJ, paramJ);
}
json_object_set_new(root, "params", paramsJ);
// data
json_t *dataJ = module ? module->toJsonData() : NULL;
if (dataJ) {
json_object_set_new(root, "data", dataJ);
}

return root;
}
@@ -76,6 +81,12 @@ void ModuleWidget::fromJson(json_t *root) {
params[paramId]->fromJson(paramJ);
}
}

// data
json_t *dataJ = json_object_get(root, "data");
if (dataJ && module) {
module->fromJsonData(dataJ);
}
}

void ModuleWidget::disconnectPorts() {
@@ -93,13 +104,6 @@ void ModuleWidget::resetParams() {
}
}

void ModuleWidget::cloneParams(ModuleWidget *source) {
assert(params.size() == source->params.size());
for (size_t i = 0; i < params.size(); i++) {
params[i]->setValue(source->params[i]->value);
}
}

void ModuleWidget::draw(NVGcontext *vg) {
Widget::draw(vg);
bndBevel(vg, 0.0, 0.0, box.size.x, box.size.y);
@@ -157,9 +161,11 @@ struct CloneModuleMenuItem : MenuItem {
void onAction() {
// Create new module from model
ModuleWidget *clonedModuleWidget = moduleWidget->model->createModuleWidget();
json_t *moduleJ = moduleWidget->toJson();
clonedModuleWidget->fromJson(moduleJ);
json_decref(moduleJ);
clonedModuleWidget->requestedPos = moduleWidget->box.pos;
clonedModuleWidget->requested = true;
clonedModuleWidget->cloneParams(moduleWidget);
gRackWidget->moduleContainer->addChild(clonedModuleWidget);
}
};


+ 40
- 0
src/dsp.cpp View File

@@ -14,4 +14,44 @@ const float minblep_16_32[] = {
};



void stepEuler(stepCallback f, float x, float dx, float y[], int len) {
float k[len];

f(x, y, k);
for (int i = 0; i < len; i++) {
y[i] += dx * k[i];
}
}

void stepRK4(stepCallback f, float x, float dx, float y[], int len) {
float k1[len];
float k2[len];
float k3[len];
float k4[len];
float yi[len];

f(x, y, k1);

for (int i = 0; i < len; i++) {
yi[i] = y[i] + k1[i] * dx / 2.0;
}
f(x + dx / 2.0, yi, k2);

for (int i = 0; i < len; i++) {
yi[i] = y[i] + k2[i] * dx / 2.0;
}
f(x + dx / 2.0, yi, k3);

for (int i = 0; i < len; i++) {
yi[i] = y[i] + k3[i] * dx;
}
f(x + dx, yi, k4);

for (int i = 0; i < len; i++) {
y[i] += dx * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]) / 6.0;
}
}


} // namespace rack

Loading…
Cancel
Save