Browse Source

v2.6.0 Octaves (#47)

Better defaults for ADSR, Burst
Fix Voltio label bug
tags/v2.6.0
Ewan GitHub 1 year ago
parent
commit
c73fb6895c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
9 changed files with 2799 additions and 12 deletions
  1. +8
    -0
      CHANGELOG.md
  2. +12
    -1
      plugin.json
  3. +2383
    -0
      res/panels/Octaves.svg
  4. +4
    -4
      src/ADSR.cpp
  5. +1
    -1
      src/Burst.cpp
  6. +383
    -0
      src/Octaves.cpp
  7. +1
    -1
      src/Voltio.cpp
  8. +1
    -0
      src/plugin.cpp
  9. +6
    -5
      src/plugin.hpp

+ 8
- 0
CHANGELOG.md View File

@@ -1,5 +1,13 @@
# Change Log


## v2.6.0
* Octaves
* Initial release
* Misc
* Better default values for ADSR and Burst


## v2.5.0
* Burst
* Initial release


+ 12
- 1
plugin.json View File

@@ -1,6 +1,6 @@
{
"slug": "Befaco",
"version": "2.5.0",
"version": "2.6.0",
"license": "GPL-3.0-or-later",
"name": "Befaco",
"brand": "Befaco",
@@ -307,6 +307,17 @@
"Polyphonic",
"Utility"
]
},
{
"slug": "Octaves",
"name": "Octaves",
"description": "A harsh and funky take of an additive Oscillator.",
"manualUrl": "https://www.befaco.org/octaves-vco/",
"modularGridUrl": "https://www.modulargrid.net/e/befaco-octaves-vco",
"tags": [
"Hardware clone",
"VCO"
]
}
]
}

+ 2383
- 0
res/panels/Octaves.svg
File diff suppressed because it is too large
View File


+ 4
- 4
src/ADSR.cpp View File

@@ -231,10 +231,10 @@ struct ADSR : Module {
configButton(MANUAL_TRIGGER_PARAM, "Trigger envelope");
configParam(SHAPE_PARAM, 0.f, 1.f, 0.f, "Envelope shape");

configParam(ATTACK_PARAM, 0.f, 1.f, 0.f, "Attack time", "s", maxStageTime / minStageTime, minStageTime);
configParam(DECAY_PARAM, 0.f, 1.f, 0.f, "Decay time", "s", maxStageTime / minStageTime, minStageTime);
configParam(SUSTAIN_PARAM, 0.f, 1.f, 0.f, "Sustain level", "%", 0.f, 100.f);
configParam(RELEASE_PARAM, 0.f, 1.f, 0.f, "Release time", "s", maxStageTime / minStageTime, minStageTime);
configParam(ATTACK_PARAM, 0.f, 1.f, 0.4f, "Attack time", "s", maxStageTime / minStageTime, minStageTime);
configParam(DECAY_PARAM, 0.f, 1.f, 0.4f, "Decay time", "s", maxStageTime / minStageTime, minStageTime);
configParam(SUSTAIN_PARAM, 0.f, 1.f, 0.5f, "Sustain level", "%", 0.f, 100.f);
configParam(RELEASE_PARAM, 0.f, 1.f, 0.4f, "Release time", "s", maxStageTime / minStageTime, minStageTime);

configInput(TRIGGER_INPUT, "Trigger");
configInput(CV_ATTACK_INPUT, "Attack CV");


+ 1
- 1
src/Burst.cpp View File

@@ -183,7 +183,7 @@ struct Burst : Module {
Burst() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
configSwitch(Burst::CYCLE_PARAM, 0.0, 1.0, 0.0, "Mode", {"One-shot", "Cycle"});
auto quantityParam = configParam(Burst::QUANTITY_PARAM, 1, MAX_REPETITIONS, 0, "Number of bursts");
auto quantityParam = configParam(Burst::QUANTITY_PARAM, 1, MAX_REPETITIONS, 4, "Number of bursts");
quantityParam->snapEnabled = true;
configButton(Burst::TRIGGER_PARAM, "Manual Trigger");
configParam(Burst::QUANTITY_CV_PARAM, 0.0, 1.0, 1.0, "Quantity CV");


+ 383
- 0
src/Octaves.cpp View File

@@ -0,0 +1,383 @@
#include "plugin.hpp"
#include "ChowDSP.hpp"

using namespace simd;

float aliasSuppressedSaw(const float* phases, float pw) {
float sawBuffer[3];
for (int i = 0; i < 3; ++i) {
float p = 2 * phases[i] - 1.0; // range -1 to +1
float pwp = p + 2 * pw; // phase after pw (pw in [0, 1])
pwp += simd::ifelse(pwp > 1, -2, simd::ifelse(pwp < -1, +2, 0)); // modulo on [-1, +1]
sawBuffer[i] = (pwp * pwp * pwp - pwp) / 6.0; // eq 11
}

return (sawBuffer[0] - 2.0 * sawBuffer[1] + sawBuffer[2]);
}

float aliasSuppressedOffsetSaw(const float* phases, float pw) {
float sawOffsetBuff[3];

for (int i = 0; i < 3; ++i) {
float pwp = 2 * phases[i] - 2 * pw; // range -1 to +1

pwp += simd::ifelse(pwp > 1, -2, 0); // modulo on [-1, +1]
sawOffsetBuff[i] = (pwp * pwp * pwp - pwp) / 6.0; // eq 11
}
return (sawOffsetBuff[0] - 2.0 * sawOffsetBuff[1] + sawOffsetBuff[2]);
}

template<typename T>
class HardClipperADAA {
public:

T process(T x) {
T y = simd::ifelse(simd::abs(x - xPrev) < 1e-5,
f(0.5 * (xPrev + x)),
(F(x) - F(xPrev)) / (x - xPrev));

xPrev = x;
return y;
}


static T f(T x) {
return simd::ifelse(simd::abs(x) < 1, x, simd::sgn(x));
}

static T F(T x) {
return simd::ifelse(simd::abs(x) < 1, 0.5 * x * x, x * simd::sgn(x) - 0.5);
}

void reset() {
xPrev = 0.f;
}

private:
T xPrev = 0.f;
};

struct Octaves : Module {
enum ParamId {
PWM_CV_PARAM,
OCTAVE_PARAM,
TUNE_PARAM,
PWM_PARAM,
RANGE_PARAM,
GAIN_01F_PARAM,
GAIN_02F_PARAM,
GAIN_04F_PARAM,
GAIN_08F_PARAM,
GAIN_16F_PARAM,
GAIN_32F_PARAM,
PARAMS_LEN
};
enum InputId {
VOCT1_INPUT,
VOCT2_INPUT,
SYNC_INPUT,
PWM_INPUT,
GAIN_01F_INPUT,
GAIN_02F_INPUT,
GAIN_04F_INPUT,
GAIN_08F_INPUT,
GAIN_16F_INPUT,
GAIN_32F_INPUT,
INPUTS_LEN
};
enum OutputId {
OUT_01F_OUTPUT,
OUT_02F_OUTPUT,
OUT_04F_OUTPUT,
OUT_08F_OUTPUT,
OUT_16F_OUTPUT,
OUT_32F_OUTPUT,
OUTPUTS_LEN
};
enum LightId {
LIGHTS_LEN
};

bool limitPW = true;
bool removePulseDC = false;
bool useTriangleCore = false;
static const int NUM_OUTPUTS = 6;
const float ranges[3] = {4.f, 1.f, 1.f / 12.f}; // full, octave, semitone

float_4 phase[4] = {}; // phase for core waveform, in [0, 1]
chowdsp::VariableOversampling<6, float_4> oversampler[NUM_OUTPUTS][4]; // uses a 2*6=12th order Butterworth filter
int oversamplingIndex = 1; // default is 2^oversamplingIndex == x2 oversampling

DCBlockerT<2, float_4> blockDCFilter[NUM_OUTPUTS][4]; // optionally block DC with RC filter @ ~22 Hz
dsp::TSchmittTrigger<float_4> syncTrigger[4]; // for hard sync

Octaves() {
config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
configParam(PWM_CV_PARAM, 0.f, 1.f, 1.f, "PWM CV attenuater");

auto octParam = configSwitch(OCTAVE_PARAM, 0.f, 6.f, 1.f, "Octave", {"C1", "C2", "C3", "C4", "C5", "C6", "C7"});
octParam->snapEnabled = true;

configParam(TUNE_PARAM, -1.f, 1.f, 0.f, "Tune");
configParam(PWM_PARAM, 0.5f, 0.f, 0.5f, "PWM");
auto rangeParam = configSwitch(RANGE_PARAM, 0.f, 2.f, 1.f, "Range", {"VCO: Full", "VCO: Octave", "VCO: Semitone"});
rangeParam->snapEnabled = true;

configParam(GAIN_01F_PARAM, 0.f, 1.f, 1.00f, "Gain Fundamental");
configParam(GAIN_02F_PARAM, 0.f, 1.f, 0.75f, "Gain x2 Fundamental");
configParam(GAIN_04F_PARAM, 0.f, 1.f, 0.50f, "Gain x4 Fundamental");
configParam(GAIN_08F_PARAM, 0.f, 1.f, 0.25f, "Gain x8 Fundamental");
configParam(GAIN_16F_PARAM, 0.f, 1.f, 0.f, "Gain x16 Fundamental");
configParam(GAIN_32F_PARAM, 0.f, 1.f, 0.f, "Gain x32 Fundamental");

configInput(VOCT1_INPUT, "V/Octave 1");
configInput(VOCT2_INPUT, "V/Octave 2");
configInput(SYNC_INPUT, "Sync");
configInput(PWM_INPUT, "PWM");
configInput(GAIN_01F_INPUT, "Gain x1F CV");
configInput(GAIN_02F_INPUT, "Gain x1F CV");
configInput(GAIN_04F_INPUT, "Gain x1F CV");
configInput(GAIN_08F_INPUT, "Gain x1F CV");
configInput(GAIN_16F_INPUT, "Gain x1F CV");
configInput(GAIN_32F_INPUT, "Gain x1F CV");

configOutput(OUT_01F_OUTPUT, "x1F");
configOutput(OUT_02F_OUTPUT, "x2F");
configOutput(OUT_04F_OUTPUT, "x4F");
configOutput(OUT_08F_OUTPUT, "x8F");
configOutput(OUT_16F_OUTPUT, "x16F");
configOutput(OUT_32F_OUTPUT, "x32F");

// calculate up/downsampling rates
onSampleRateChange();
}

void onSampleRateChange() override {
float sampleRate = APP->engine->getSampleRate();
for (int c = 0; c < NUM_OUTPUTS; c++) {
for (int i = 0; i < 4; i++) {
oversampler[c][i].setOversamplingIndex(oversamplingIndex);
oversampler[c][i].reset(sampleRate);
blockDCFilter[c][i].setFrequency(22.05 / sampleRate);
}
}
}


void process(const ProcessArgs& args) override {

const int numActivePolyphonyEngines = getNumActivePolyphonyEngines();

// work out active outputs
const std::vector<int> connectedOutputs = getConnectedOutputs();
if (connectedOutputs.size() == 0) {
return;
}
// only process up to highest active channel
const int highestOutput = *std::max_element(connectedOutputs.begin(), connectedOutputs.end());

for (int c = 0; c < numActivePolyphonyEngines; c += 4) {

const int rangeIndex = params[RANGE_PARAM].getValue();
float_4 pitch = ranges[rangeIndex] * params[TUNE_PARAM].getValue() + inputs[VOCT1_INPUT].getPolyVoltageSimd<float_4>(c) + inputs[VOCT2_INPUT].getPolyVoltageSimd<float_4>(c);
pitch += params[OCTAVE_PARAM].getValue() - 3;
const float_4 freq = dsp::FREQ_C4 * dsp::exp2_taylor5(pitch);
// -1 to +1
const float_4 pwmCV = params[PWM_CV_PARAM].getValue() * clamp(inputs[PWM_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f, -1.f, 1.f);
const float_4 pulseWidthLimit = limitPW ? 0.05f : 0.0f;

// pwm in [-0.25 : +0.25]
const float_4 pwm = 2 * clamp(0.5 - params[PWM_PARAM].getValue() + 0.5 * pwmCV, -0.5f + pulseWidthLimit, 0.5f - pulseWidthLimit);

const int oversamplingRatio = oversampler[0][0].getOversamplingRatio();

const float_4 deltaPhase = freq * args.sampleTime / oversamplingRatio;

// process sync
float_4 sync = syncTrigger[c / 4].process(inputs[SYNC_INPUT].getPolyVoltageSimd<float_4>(c));
phase[c / 4] = simd::ifelse(sync, 0.5f, phase[c / 4]);


for (int i = 0; i < oversamplingRatio; i++) {

phase[c / 4] += deltaPhase;
phase[c / 4] -= simd::floor(phase[c / 4]);

float_4 sum = {};
for (int oct = 0; oct <= highestOutput; oct++) {
// derive phases for higher octaves from base phase (this keeps things in sync!)
const float_4 n = (float)(1 << oct);
// this is on [0, 1]
const float_4 effectivePhase = n * simd::fmod(phase[c / 4], 1 / n);
const float_4 gainCV = simd::clamp(inputs[GAIN_01F_INPUT + oct].getNormalPolyVoltageSimd<float_4>(10.f, c) / 10.f, 0.f, 1.0f);
const float_4 gain = params[GAIN_01F_PARAM + oct].getValue() * gainCV;

const float_4 waveTri = 1.0 - 2.0 * simd::abs(2.f * effectivePhase - 1.0);
// build square from triangle + comparator
const float_4 waveSquare = simd::ifelse(waveTri > pwm, +1.f, -1.f);

sum += (useTriangleCore ? waveTri : waveSquare) * gain;
sum = clamp(sum, -1.f, 1.f);

if (outputs[OUT_01F_OUTPUT + oct].isConnected()) {
oversampler[oct][c/4].getOSBuffer()[i] = sum;
sum = 0.f;

// DEBUG("here %f %f %f %f %f", phase[c/4][0], waveTri[0], sum[0], gain[0], gainCV[0]);
}


}

} // end of oversampling loop

// only downsample required channels
for (int oct = 0; oct <= highestOutput; oct++) {
if (outputs[OUT_01F_OUTPUT + oct].isConnected()) {

// downsample (if required)
float_4 out = (oversamplingRatio > 1) ? oversampler[oct][c/4].downsample() : oversampler[oct][c/4].getOSBuffer()[0];
if (removePulseDC) {
out = blockDCFilter[oct][c/4].process(out);
}

outputs[OUT_01F_OUTPUT + oct].setVoltageSimd(5.f * out, c);
}
}
} // end of polyphony loop

for (int connectedOutput : connectedOutputs) {
outputs[OUT_01F_OUTPUT + connectedOutput].setChannels(numActivePolyphonyEngines);
}
}

// polyphony is defined by the largest number of active channels on voct, pwm or gain inputs
int getNumActivePolyphonyEngines() {
int activePolyphonyEngines = 1;
for (int c = 0; c < NUM_OUTPUTS; c++) {
if (inputs[GAIN_01F_INPUT + c].isConnected()) {
activePolyphonyEngines = std::max(activePolyphonyEngines, inputs[GAIN_01F_INPUT + c].getChannels());
}
}
activePolyphonyEngines = std::max({activePolyphonyEngines, inputs[VOCT1_INPUT].getChannels(), inputs[VOCT2_INPUT].getChannels()});
activePolyphonyEngines = std::max(activePolyphonyEngines, inputs[PWM_INPUT].getChannels());

return activePolyphonyEngines;
}

std::vector<int> getConnectedOutputs() {
std::vector<int> connectedOutputs;
for (int c = 0; c < NUM_OUTPUTS; c++) {
if (outputs[OUT_01F_OUTPUT + c].isConnected()) {
connectedOutputs.push_back(c);
}
}
return connectedOutputs;
}

json_t* dataToJson() override {
json_t* rootJ = json_object();
json_object_set_new(rootJ, "removePulseDC", json_boolean(removePulseDC));
json_object_set_new(rootJ, "limitPW", json_boolean(limitPW));
json_object_set_new(rootJ, "oversamplingIndex", json_integer(oversampler[0][0].getOversamplingIndex()));
json_object_set_new(rootJ, "useTriangleCore", json_boolean(useTriangleCore));

return rootJ;
}

void dataFromJson(json_t* rootJ) override {

json_t* removePulseDCJ = json_object_get(rootJ, "removePulseDC");
if (removePulseDCJ) {
removePulseDC = json_boolean_value(removePulseDCJ);
}

json_t* limitPWJ = json_object_get(rootJ, "limitPW");
if (limitPWJ) {
limitPW = json_boolean_value(limitPWJ);
}

json_t* oversamplingIndexJ = json_object_get(rootJ, "oversamplingIndex");
if (oversamplingIndexJ) {
oversamplingIndex = json_integer_value(oversamplingIndexJ);
onSampleRateChange();
}

json_t* useTriangleCoreJ = json_object_get(rootJ, "useTriangleCore");
if (useTriangleCoreJ) {
useTriangleCore = json_boolean_value(useTriangleCoreJ);
}
}
};

struct OctavesWidget : ModuleWidget {
OctavesWidget(Octaves* module) {
setModule(module);
setPanel(createPanel(asset::plugin(pluginInstance, "res/panels/Octaves.svg")));

addChild(createWidget<Knurlie>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<Knurlie>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<Knurlie>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<Knurlie>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));

addParam(createParamCentered<BefacoTinyKnobLightGrey>(mm2px(Vec(52.138, 15.037)), module, Octaves::PWM_CV_PARAM));
addParam(createParam<CKSSVert7>(mm2px(Vec(22.171, 30.214)), module, Octaves::OCTAVE_PARAM));
addParam(createParamCentered<BefacoTinyKnobLightGrey>(mm2px(Vec(10.264, 33.007)), module, Octaves::TUNE_PARAM));
addParam(createParamCentered<Davies1900hLargeRedKnob>(mm2px(Vec(45.384, 40.528)), module, Octaves::PWM_PARAM));
addParam(createParam<CKSSThreeHorizontal>(mm2px(Vec(6.023, 48.937)), module, Octaves::RANGE_PARAM));
addParam(createParam<BefacoSlidePotSmall>(mm2px(Vec(2.9830, 60.342)), module, Octaves::GAIN_01F_PARAM));
addParam(createParam<BefacoSlidePotSmall>(mm2px(Vec(12.967, 60.342)), module, Octaves::GAIN_02F_PARAM));
addParam(createParam<BefacoSlidePotSmall>(mm2px(Vec(22.951, 60.342)), module, Octaves::GAIN_04F_PARAM));
addParam(createParam<BefacoSlidePotSmall>(mm2px(Vec(32.936, 60.342)), module, Octaves::GAIN_08F_PARAM));
addParam(createParam<BefacoSlidePotSmall>(mm2px(Vec(42.920, 60.342)), module, Octaves::GAIN_16F_PARAM));
addParam(createParam<BefacoSlidePotSmall>(mm2px(Vec(52.905, 60.342)), module, Octaves::GAIN_32F_PARAM));

addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(5.247, 15.181)), module, Octaves::VOCT1_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(15.282, 15.181)), module, Octaves::VOCT2_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(25.316, 15.181)), module, Octaves::SYNC_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(37.092, 15.135)), module, Octaves::PWM_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(5.247, 100.492)), module, Octaves::GAIN_01F_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(15.282, 100.492)), module, Octaves::GAIN_02F_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(25.316, 100.492)), module, Octaves::GAIN_04F_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(35.35, 100.492)), module, Octaves::GAIN_08F_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(45.384, 100.492)), module, Octaves::GAIN_16F_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(55.418, 100.492)), module, Octaves::GAIN_32F_INPUT));

addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(5.247, 113.508)), module, Octaves::OUT_01F_OUTPUT));
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(15.282, 113.508)), module, Octaves::OUT_02F_OUTPUT));
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(25.316, 113.508)), module, Octaves::OUT_04F_OUTPUT));
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(35.35, 113.508)), module, Octaves::OUT_08F_OUTPUT));
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(45.384, 113.508)), module, Octaves::OUT_16F_OUTPUT));
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(55.418, 113.508)), module, Octaves::OUT_32F_OUTPUT));

}

void appendContextMenu(Menu* menu) override {
Octaves* module = dynamic_cast<Octaves*>(this->module);
assert(module);

menu->addChild(new MenuSeparator());
menu->addChild(createSubmenuItem("Hardware compatibility", "",
[ = ](Menu * menu) {
menu->addChild(createBoolPtrMenuItem("Limit pulsewidth (5\%-95\%)", "", &module->limitPW));
menu->addChild(createBoolPtrMenuItem("Remove pulse DC", "", &module->removePulseDC));
menu->addChild(createBoolPtrMenuItem("Use triangle core", "", &module->useTriangleCore));
}
));

menu->addChild(createIndexSubmenuItem("Oversampling",
{"Off", "x2", "x4", "x8"},
[ = ]() {
return module->oversamplingIndex;
},
[ = ](int mode) {
module->oversamplingIndex = mode;
module->onSampleRateChange();
}
));

}
};

Model* modelOctaves = createModel<Octaves, OctavesWidget>("Octaves");

+ 1
- 1
src/Voltio.cpp View File

@@ -40,7 +40,7 @@ struct Voltio : Module {
auto octParam = configParam(OCT_PARAM, 0.f, 10.f, 0.f, "Octave");
octParam->snapEnabled = true;

configSwitch(RANGE_PARAM, 0.f, 1.f, 0.f, "Range", {"-5 to +5", "0 to 10"});
configSwitch(RANGE_PARAM, 0.f, 1.f, 0.f, "Range", {"0 to 10", "-5 to +5"});
auto semitonesParam = configParam(SEMITONES_PARAM, 0.f, 11.f, 0.f, "Semitones");
semitonesParam->snapEnabled = true;



+ 1
- 0
src/plugin.cpp View File

@@ -29,4 +29,5 @@ void init(rack::Plugin *p) {
p->addModel(modelMotionMTR);
p->addModel(modelBurst);
p->addModel(modelVoltio);
p->addModel(modelOctaves);
}

+ 6
- 5
src/plugin.hpp View File

@@ -30,6 +30,7 @@ extern Model* modelPonyVCO;
extern Model* modelMotionMTR;
extern Model* modelBurst;
extern Model* modelVoltio;
extern Model* modelOctaves;

struct Knurlie : SvgScrew {
Knurlie() {
@@ -312,7 +313,7 @@ private:
};

// Creates a Butterworth 2*Nth order highpass filter for blocking DC
template<int N>
template<int N, typename T>
struct DCBlockerT {

DCBlockerT() {
@@ -325,7 +326,7 @@ struct DCBlockerT {
recalculateCoefficients();
}

float process(float x) {
T process(T x) {
for (int idx = 0; idx < N; idx++) {
x = blockDCFilter[idx].process(x);
}
@@ -342,17 +343,17 @@ private:

for (int idx = 0; idx < N; idx++) {
float Q = 1.0f / (2.0f * std::cos(firstAngle + idx * poleInc));
blockDCFilter[idx].setParameters(dsp::BiquadFilter::HIGHPASS, fc_, Q, 1.0f);
blockDCFilter[idx].setParameters(dsp::TBiquadFilter<T>::HIGHPASS, fc_, Q, 1.0f);
}
}

float fc_;
static const int order = 2 * N;

dsp::BiquadFilter blockDCFilter[N];
dsp::TBiquadFilter<T> blockDCFilter[N];
};

typedef DCBlockerT<2> DCBlocker;
typedef DCBlockerT<2, float> DCBlocker;

/** When triggered, holds a high value for a specified time before going low again */
struct PulseGenerator_4 {


Loading…
Cancel
Save