Browse Source

Kickall

- only downsample a source wave generated at higher sample rates
- switch to Pade approximation of sin
- new panel SVG from Befaco
- fix LED logic

Moved all chowdsp library code into specific header.
Updated .json
tags/v1.1.0^2
hemmer 4 years ago
parent
commit
2667f30374
6 changed files with 1355 additions and 1733 deletions
  1. +15
    -4
      plugin.json
  2. +880
    -1487
      res/Kickall.svg
  3. +2
    -13
      src/ChoppingKinky.cpp
  4. +420
    -0
      src/ChowDSP.hpp
  5. +13
    -200
      src/Common.hpp
  6. +25
    -29
      src/Kickall.cpp

+ 15
- 4
plugin.json View File

@@ -1,6 +1,6 @@
{
"slug": "Befaco",
"version": "1.0.2",
"version": "1.1.0",
"license": "GPL-3.0-or-later",
"name": "Befaco",
"author": "VCV",
@@ -97,6 +97,7 @@
"tags": [
"Envelope generator",
"Mixer",
"Poly",
"Hardware clone"
]
},
@@ -108,20 +109,30 @@
"tags": [
"Mixer",
"Hardware clone",
"Poly",
"VCA"
]
},
{
"slug": "ChoppingKinky",
"name": "ChoppingKinky",
"description": "",
"tags": []
"description": "Voltage controllable, dual channel wavefolder",
"tags": [
"Dual",
"Hardware clone",
"Voltage-controlled amplifier",
"Waveshaper"
]
},
{
"slug": "Kickall",
"name": "Kickall",
"description": "",
"tags": []
"tags": [
"Drum",
"Hardware clone",
"Synth voice"
]
}
]
}

+ 880
- 1487
res/Kickall.svg
File diff suppressed because it is too large
View File


+ 2
- 13
src/ChoppingKinky.cpp View File

@@ -1,21 +1,10 @@
#include "plugin.hpp"
#include "Common.hpp"
#include "ChowDSP.hpp"
static const size_t BUF_LEN = 32;
template <typename T>
T sin2pi_pade_05_5_4(T x) {
x -= 0.5f;
return (T(-6.283185307) * x + T(33.19863968) * simd::pow(x, 3) - T(32.44191367) * simd::pow(x, 5))
/ (1 + T(1.296008659) * simd::pow(x, 2) + T(0.7028072946) * simd::pow(x, 4));
}
template <typename T>
T tanh_pade(T x) {
T x2 = x * x;
T q = 12.f + x2;
return 12.f * x * q / (36.f * x2 + q * q);
}
static float foldResponse(float inputGain, float G) {
return std::tanh(inputGain) + G * std::sin(M_PI * inputGain);
@@ -69,7 +58,7 @@ struct ChoppingKinky : Module {
bool outputAToChopp;
float previousA = 0.0;
VariableOversampling<> oversampler[NUM_CHANNELS];
chowdsp::VariableOversampling<> oversampler[NUM_CHANNELS];
int oversamplingIndex = 2;
dsp::BiquadFilter blockDCFilter;


+ 420
- 0
src/ChowDSP.hpp View File

@@ -0,0 +1,420 @@
#pragma once
namespace chowdsp {
// code taken from https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/, commit 21701fb
// * AAFilter.hpp
// * VariableOversampling.hpp
// * oversampling.hpp
// * iir.hpp
template <int ORDER, typename T = float>
struct IIRFilter {
/** transfer function numerator coefficients: b_0, b_1, etc.*/
T b[ORDER] = {};
/** transfer function denominator coefficients: a_0, a_1, etc.*/
T a[ORDER] = {};
/** filter state */
T z[ORDER];
IIRFilter() {
reset();
}
void reset() {
std::fill(z, &z[ORDER], 0.0f);
}
void setCoefficients(const T* b, const T* a) {
for (int i = 0; i < ORDER; i++) {
this->b[i] = b[i];
}
for (int i = 1; i < ORDER; i++) {
this->a[i] = a[i];
}
}
template <int N = ORDER>
inline typename std::enable_if <N == 2, T>::type process(T x) noexcept {
T y = z[1] + x * b[0];
z[1] = x * b[1] - y * a[1];
return y;
}
template <int N = ORDER>
inline typename std::enable_if <N == 3, T>::type process(T x) noexcept {
T y = z[1] + x * b[0];
z[1] = z[2] + x * b[1] - y * a[1];
z[2] = x * b[2] - y * a[2];
return y;
}
template <int N = ORDER>
inline typename std::enable_if < (N > 3), T >::type process(T x) noexcept {
T y = z[1] + x * b[0];
for (int i = 1; i < ORDER - 1; ++i)
z[i] = z[i + 1] + x * b[i] - y * a[i];
z[ORDER - 1] = x * b[ORDER - 1] - y * a[ORDER - 1];
return y;
}
/** Computes the complex transfer function $H(s)$ at a particular frequency
s: normalized angular frequency equal to $2 \pi f / f_{sr}$ ($\pi$ is the Nyquist frequency)
*/
std::complex<T> getTransferFunction(T s) {
// Compute sum(a_k z^-k) / sum(b_k z^-k) where z = e^(i s)
std::complex<T> bSum(b[0], 0);
std::complex<T> aSum(1, 0);
for (int i = 1; i < ORDER; i++) {
T p = -i * s;
std::complex<T> z(simd::cos(p), simd::sin(p));
bSum += b[i] * z;
aSum += a[i - 1] * z;
}
return bSum / aSum;
}
T getFrequencyResponse(T f) {
return simd::abs(getTransferFunction(2 * M_PI * f));
}
T getFrequencyPhase(T f) {
return simd::arg(getTransferFunction(2 * M_PI * f));
}
};
template <typename T = float>
struct TBiquadFilter : IIRFilter<3, T> {
enum Type {
LOWPASS,
HIGHPASS,
LOWSHELF,
HIGHSHELF,
BANDPASS,
PEAK,
NOTCH,
NUM_TYPES
};
TBiquadFilter() {
setParameters(LOWPASS, 0.f, 0.f, 1.f);
}
/** Calculates and sets the biquad transfer function coefficients.
f: normalized frequency (cutoff frequency / sample rate), must be less than 0.5
Q: quality factor
V: gain
*/
void setParameters(Type type, float f, float Q, float V) {
float K = std::tan(M_PI * f);
switch (type) {
case LOWPASS: {
float norm = 1.f / (1.f + K / Q + K * K);
this->b[0] = K * K * norm;
this->b[1] = 2.f * this->b[0];
this->b[2] = this->b[0];
this->a[1] = 2.f * (K * K - 1.f) * norm;
this->a[2] = (1.f - K / Q + K * K) * norm;
} break;
case HIGHPASS: {
float norm = 1.f / (1.f + K / Q + K * K);
this->b[0] = norm;
this->b[1] = -2.f * this->b[0];
this->b[2] = this->b[0];
this->a[1] = 2.f * (K * K - 1.f) * norm;
this->a[2] = (1.f - K / Q + K * K) * norm;
} break;
case LOWSHELF: {
float sqrtV = std::sqrt(V);
if (V >= 1.f) {
float norm = 1.f / (1.f + M_SQRT2 * K + K * K);
this->b[0] = (1.f + M_SQRT2 * sqrtV * K + V * K * K) * norm;
this->b[1] = 2.f * (V * K * K - 1.f) * norm;
this->b[2] = (1.f - M_SQRT2 * sqrtV * K + V * K * K) * norm;
this->a[1] = 2.f * (K * K - 1.f) * norm;
this->a[2] = (1.f - M_SQRT2 * K + K * K) * norm;
}
else {
float norm = 1.f / (1.f + M_SQRT2 / sqrtV * K + K * K / V);
this->b[0] = (1.f + M_SQRT2 * K + K * K) * norm;
this->b[1] = 2.f * (K * K - 1) * norm;
this->b[2] = (1.f - M_SQRT2 * K + K * K) * norm;
this->a[1] = 2.f * (K * K / V - 1.f) * norm;
this->a[2] = (1.f - M_SQRT2 / sqrtV * K + K * K / V) * norm;
}
} break;
case HIGHSHELF: {
float sqrtV = std::sqrt(V);
if (V >= 1.f) {
float norm = 1.f / (1.f + M_SQRT2 * K + K * K);
this->b[0] = (V + M_SQRT2 * sqrtV * K + K * K) * norm;
this->b[1] = 2.f * (K * K - V) * norm;
this->b[2] = (V - M_SQRT2 * sqrtV * K + K * K) * norm;
this->a[1] = 2.f * (K * K - 1.f) * norm;
this->a[2] = (1.f - M_SQRT2 * K + K * K) * norm;
}
else {
float norm = 1.f / (1.f / V + M_SQRT2 / sqrtV * K + K * K);
this->b[0] = (1.f + M_SQRT2 * K + K * K) * norm;
this->b[1] = 2.f * (K * K - 1.f) * norm;
this->b[2] = (1.f - M_SQRT2 * K + K * K) * norm;
this->a[1] = 2.f * (K * K - 1.f / V) * norm;
this->a[2] = (1.f / V - M_SQRT2 / sqrtV * K + K * K) * norm;
}
} break;
case BANDPASS: {
float norm = 1.f / (1.f + K / Q + K * K);
this->b[0] = K / Q * norm;
this->b[1] = 0.f;
this->b[2] = -this->b[0];
this->a[1] = 2.f * (K * K - 1.f) * norm;
this->a[2] = (1.f - K / Q + K * K) * norm;
} break;
case PEAK: {
float c = 1.0f / K;
float phi = c * c;
float Knum = c / Q;
float Kdenom = Knum;
if (V > 1.0f)
Knum *= V;
else
Kdenom /= V;
float norm = phi + Kdenom + 1.0;
this->b[0] = (phi + Knum + 1.0f) / norm;
this->b[1] = 2.0f * (1.0f - phi) / norm;
this->b[2] = (phi - Knum + 1.0f) / norm;
this->a[1] = 2.0f * (1.0f - phi) / norm;
this->a[2] = (phi - Kdenom + 1.0f) / norm;
} break;
case NOTCH: {
float norm = 1.f / (1.f + K / Q + K * K);
this->b[0] = (1.f + K * K) * norm;
this->b[1] = 2.f * (K * K - 1.f) * norm;
this->b[2] = this->b[0];
this->a[1] = this->b[1];
this->a[2] = (1.f - K / Q + K * K) * norm;
} break;
default: break;
}
}
};
typedef TBiquadFilter<> BiquadFilter;
/**
High-order filter to be used for anti-aliasing or anti-imaging.
The template parameter N should be 1/2 the desired filter order.
Currently uses an 2*N-th order Butterworth filter.
source: https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/AAFilter.hpp
*/
template<int N>
class AAFilter {
public:
AAFilter() = default;
/** Calculate Q values for a Butterworth filter of a given order */
static std::vector<float> calculateButterQs(int order) {
const int lim = int (order / 2);
std::vector<float> Qs;
for (int k = 1; k <= lim; ++k) {
auto b = -2.0f * std::cos((2.0f * k + order - 1) * 3.14159 / (2.0f * order));
Qs.push_back(1.0f / b);
}
std::reverse(Qs.begin(), Qs.end());
return Qs;
}
/**
* Resets the filter to process at a new sample rate.
*
* @param sampleRate: The base (i.e. pre-oversampling) sample rate of the audio being processed
* @param osRatio: The oversampling ratio at which the filter is being used
*/
void reset(float sampleRate, int osRatio) {
float fc = 0.98f * (sampleRate / 2.0f);
auto Qs = calculateButterQs(2 * N);
for (int i = 0; i < N; ++i)
filters[i].setParameters(BiquadFilter::Type::LOWPASS, fc / (osRatio * sampleRate), Qs[i], 1.0f);
}
inline float process(float x) noexcept {
for (int i = 0; i < N; ++i)
x = filters[i].process(x);
return x;
}
private:
BiquadFilter filters[N];
};
/**
* Base class for oversampling of any order
* source: https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/oversampling.hpp
*/
class BaseOversampling {
public:
BaseOversampling() = default;
virtual ~BaseOversampling() {}
/** Resets the oversampler for processing at some base sample rate */
virtual void reset(float /*baseSampleRate*/) = 0;
/** Upsample a single input sample and update the oversampled buffer */
virtual void upsample(float) noexcept = 0;
/** Output a downsampled output sample from the current oversampled buffer */
virtual float downsample() noexcept = 0;
/** Returns a pointer to the oversampled buffer */
virtual float* getOSBuffer() noexcept = 0;
};
/**
Class to implement an oversampled process.
To use, create an object and prepare using `reset()`.
Then use the following code to process samples:
@code
oversample.upsample(x);
for(int k = 0; k < ratio; k++)
oversample.osBuffer[k] = processSample(oversample.osBuffer[k]);
float y = oversample.downsample();
@endcode
*/
template<int ratio, int filtN = 4>
class Oversampling : public BaseOversampling {
public:
Oversampling() = default;
virtual ~Oversampling() {}
void reset(float baseSampleRate) override {
aaFilter.reset(baseSampleRate, ratio);
aiFilter.reset(baseSampleRate, ratio);
std::fill(osBuffer, &osBuffer[ratio], 0.0f);
}
inline void upsample(float x) noexcept override {
osBuffer[0] = ratio * x;
std::fill(&osBuffer[1], &osBuffer[ratio], 0.0f);
for (int k = 0; k < ratio; k++)
osBuffer[k] = aiFilter.process(osBuffer[k]);
}
inline float downsample() noexcept override {
float y = 0.0f;
for (int k = 0; k < ratio; k++)
y = aaFilter.process(osBuffer[k]);
return y;
}
inline float* getOSBuffer() noexcept override {
return osBuffer;
}
float osBuffer[ratio];
private:
AAFilter<filtN> aaFilter; // anti-aliasing filter
AAFilter<filtN> aiFilter; // anti-imaging filter
};
/**
Class to implement an oversampled process, with variable
oversampling factor. To use, create an object, set the oversampling
factor using `setOversamplingindex()` and prepare using `reset()`.
Then use the following code to process samples:
@code
oversample.upsample(x);
float* osBuffer = oversample.getOSBuffer();
for(int k = 0; k < ratio; k++)
osBuffer[k] = processSample(osBuffer[k]);
float y = oversample.downsample();
@endcode
source (modified): https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/VariableOversampling.hpp
*/
template<int filtN = 4>
class VariableOversampling {
public:
VariableOversampling() = default;
/** Prepare the oversampler to process audio at a given sample rate */
void reset(float sampleRate) {
for (auto* os : oss)
os->reset(sampleRate);
}
/** Sets the oversampling factor as 2^idx */
void setOversamplingIndex(int newIdx) {
osIdx = newIdx;
}
/** Returns the oversampling index */
int getOversamplingIndex() const noexcept {
return osIdx;
}
/** Upsample a single input sample and update the oversampled buffer */
inline void upsample(float x) noexcept {
oss[osIdx]->upsample(x);
}
/** Output a downsampled output sample from the current oversampled buffer */
inline float downsample() noexcept {
return oss[osIdx]->downsample();
}
/** Returns a pointer to the oversampled buffer */
inline float* getOSBuffer() noexcept {
return oss[osIdx]->getOSBuffer();
}
/** Returns the current oversampling factor */
int getOversamplingRatio() const noexcept {
return 1 << osIdx;
}
private:
enum {
NumOS = 5, // number of oversampling options
};
int osIdx = 0;
Oversampling < 1 << 0, filtN > os0; // 1x
Oversampling < 1 << 1, filtN > os1; // 2x
Oversampling < 1 << 2, filtN > os2; // 4x
Oversampling < 1 << 3, filtN > os3; // 8x
Oversampling < 1 << 4, filtN > os4; // 16x
BaseOversampling* oss[NumOS] = { &os0, &os1, &os2, &os3, &os4 };
};
}

+ 13
- 200
src/Common.hpp View File

@@ -45,203 +45,16 @@ struct BefacoInputPort : app::SvgPort {
}
};

/**
High-order filter to be used for anti-aliasing or anti-imaging.
The template parameter N should be 1/2 the desired filter order.

Currently uses an 2*N-th order Butterworth filter.
source: https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/AAFilter.hpp
*/
template<int N>
class AAFilter {
public:
AAFilter() = default;

/** Calculate Q values for a Butterworth filter of a given order */
static std::vector<float> calculateButterQs(int order) {
const int lim = int (order / 2);
std::vector<float> Qs;

for (int k = 1; k <= lim; ++k) {
auto b = -2.0f * std::cos((2.0f * k + order - 1) * 3.14159 / (2.0f * order));
Qs.push_back(1.0f / b);
}

std::reverse(Qs.begin(), Qs.end());
return Qs;
}

/**
* Resets the filter to process at a new sample rate.
*
* @param sampleRate: The base (i.e. pre-oversampling) sample rate of the audio being processed
* @param osRatio: The oversampling ratio at which the filter is being used
*/
void reset(float sampleRate, int osRatio) {
float fc = 0.98f * (sampleRate / 2.0f);
auto Qs = calculateButterQs(2 * N);

for (int i = 0; i < N; ++i)
filters[i].setParameters(dsp::BiquadFilter::Type::LOWPASS, fc / (osRatio * sampleRate), Qs[i], 1.0f);
}

inline float process(float x) noexcept {
for (int i = 0; i < N; ++i)
x = filters[i].process(x);

return x;
}

private:
dsp::BiquadFilter filters[N];
};



/**
* Base class for oversampling of any order
* source: https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/oversampling.hpp
*/
class BaseOversampling {
public:
BaseOversampling() = default;
virtual ~BaseOversampling() {}

/** Resets the oversampler for processing at some base sample rate */
virtual void reset(float /*baseSampleRate*/) = 0;

/** Upsample a single input sample and update the oversampled buffer */
virtual void upsample(float) noexcept = 0;

/** Output a downsampled output sample from the current oversampled buffer */
virtual float downsample() noexcept = 0;

/** Returns a pointer to the oversampled buffer */
virtual float* getOSBuffer() noexcept = 0;
};

/**
Class to implement an oversampled process.
To use, create an object and prepare using `reset()`.

Then use the following code to process samples:
@code
oversample.upsample(x);
for(int k = 0; k < ratio; k++)
oversample.osBuffer[k] = processSample(oversample.osBuffer[k]);
float y = oversample.downsample();
@endcode
*/
template<int ratio, int filtN = 4>
class Oversampling : public BaseOversampling {
public:
Oversampling() = default;
virtual ~Oversampling() {}

void reset(float baseSampleRate) override {
aaFilter.reset(baseSampleRate, ratio);
aiFilter.reset(baseSampleRate, ratio);
std::fill(osBuffer, &osBuffer[ratio], 0.0f);
}

inline void upsample(float x) noexcept override {
osBuffer[0] = ratio * x;
std::fill(&osBuffer[1], &osBuffer[ratio], 0.0f);

for (int k = 0; k < ratio; k++)
osBuffer[k] = aiFilter.process(osBuffer[k]);
}

inline float downsample() noexcept override {
float y = 0.0f;
for (int k = 0; k < ratio; k++)
y = aaFilter.process(osBuffer[k]);

return y;
}

inline float* getOSBuffer() noexcept override {
return osBuffer;
}

float osBuffer[ratio];

private:
AAFilter<filtN> aaFilter; // anti-aliasing filter
AAFilter<filtN> aiFilter; // anti-imaging filter
};


/**
Class to implement an oversampled process, with variable
oversampling factor. To use, create an object, set the oversampling
factor using `setOversamplingindex()` and prepare using `reset()`.

Then use the following code to process samples:
@code
oversample.upsample(x);
float* osBuffer = oversample.getOSBuffer();
for(int k = 0; k < ratio; k++)
osBuffer[k] = processSample(osBuffer[k]);
float y = oversample.downsample();
@endcode

source (modified): https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/VariableOversampling.hpp
*/
template<int filtN = 4>
class VariableOversampling {
public:
VariableOversampling() = default;

/** Prepare the oversampler to process audio at a given sample rate */
void reset(float sampleRate) {
for (auto* os : oss)
os->reset(sampleRate);
}

/** Sets the oversampling factor as 2^idx */
void setOversamplingIndex(int newIdx) {
osIdx = newIdx;
}

/** Returns the oversampling index */
int getOversamplingIndex() const noexcept {
return osIdx;
}

/** Upsample a single input sample and update the oversampled buffer */
inline void upsample(float x) noexcept {
oss[osIdx]->upsample(x);
}

/** Output a downsampled output sample from the current oversampled buffer */
inline float downsample() noexcept {
return oss[osIdx]->downsample();
}

/** Returns a pointer to the oversampled buffer */
inline float* getOSBuffer() noexcept {
return oss[osIdx]->getOSBuffer();
}

/** Returns the current oversampling factor */
int getOversamplingRatio() const noexcept {
return 1 << osIdx;
}


private:
enum {
NumOS = 5, // number of oversampling options
};

int osIdx = 0;

Oversampling < 1 << 0, filtN > os0; // 1x
Oversampling < 1 << 1, filtN > os1; // 2x
Oversampling < 1 << 2, filtN > os2; // 4x
Oversampling < 1 << 3, filtN > os3; // 8x
Oversampling < 1 << 4, filtN > os4; // 16x
BaseOversampling* oss[NumOS] = { &os0, &os1, &os2, &os3, &os4 };

};
template <typename T>
T sin2pi_pade_05_5_4(T x) {
x -= 0.5f;
return (T(-6.283185307) * x + T(33.19863968) * simd::pow(x, 3) - T(32.44191367) * simd::pow(x, 5))
/ (1 + T(1.296008659) * simd::pow(x, 2) + T(0.7028072946) * simd::pow(x, 4));
}

template <typename T>
T tanh_pade(T x) {
T x2 = x * x;
T q = 12.f + x2;
return 12.f * x * q / (36.f * x2 + q * q);
}

+ 25
- 29
src/Kickall.cpp View File

@@ -1,5 +1,6 @@
#include "plugin.hpp"
#include "Common.hpp"
#include "ChowDSP.hpp"
struct ADEnvelope {
@@ -13,6 +14,7 @@ struct ADEnvelope {
float env = 0.f;
float attackTime = 0.1, decayTime = 0.1;
// TODO: add shape, and generlise to use with Percall
ADEnvelope() { };
void process(const float& sampleTime) {
@@ -98,8 +100,8 @@ struct Kickall : Module {
static constexpr float FREQ_A0 = 27.5f;
static constexpr float FREQ_B2 = 123.471f;
static const int UPSAMPLE = 4;
Oversampling<UPSAMPLE> oversampler;
static const int UPSAMPLE = 8;
chowdsp::Oversampling<UPSAMPLE> oversampler;
float shaperBuf[UPSAMPLE];
void process(const ProcessArgs& args) override {
@@ -124,19 +126,8 @@ struct Kickall : Module {
float freq = params[TUNE_PARAM].getValue();
freq *= std::pow(2, inputs[TUNE_INPUT].getVoltage());
//float kickFrequency = std::max(50.0f, params[TUNE_PARAM].getValue() + bendRange * bend * pitch.env);
float kickFrequency = std::max(50.0f, freq + bendRange * bend * pitch.env);
phase += args.sampleTime * kickFrequency;
if (phase > 1.0f) {
phase -= 1.0f;
}
// TODO: faster approximation
float wave = std::sin(2.0 * M_PI * phase);
oversampler.upsample(wave);
float* inputBuf = oversampler.getOSBuffer();
const float kickFrequency = std::max(10.0f, freq + bendRange * bend * pitch.env);
const float phaseInc = args.sampleTime * kickFrequency / UPSAMPLE;
float shape = clamp(inputs[SHAPE_INPUT].getVoltage() / 2.f + params[SHAPE_PARAM].getValue() * 5.0f, 0.0f, 10.0f);
shape = clamp(shape, 0.f, 5.0f) * 0.2f;
@@ -144,14 +135,19 @@ struct Kickall : Module {
const float shapeB = (1.0f - shape) / (1.0f + shape);
const float shapeA = (4.0f * shape) / ((1.0f - shape) * (1.0f + shape));
float* inputBuf = oversampler.getOSBuffer();
for (int i = 0; i < UPSAMPLE; ++i) {
phase += phaseInc;
phase -= std::floor(phase);
inputBuf[i] = sin2pi_pade_05_5_4(phase);
inputBuf[i] = inputBuf[i] * (shapeA + shapeB) / ((std::abs(inputBuf[i]) * shapeA) + shapeB);
}
float out = volume.env * oversampler.downsample() * 5.0f * vcaGain;
outputs[OUT_OUTPUT].setVoltage(out);
lights[ENV_LIGHT].setBrightness(volume.stage);
lights[ENV_LIGHT].setBrightness(volume.env);
}
};
@@ -164,22 +160,22 @@ struct KickallWidget : ModuleWidget {
addChild(createWidget<Knurlie>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<Knurlie>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addParam(createParamCentered<BefacoTinyKnobGrey>(mm2px(Vec(8.76, 29.068)), module, Kickall::TUNE_PARAM));
addParam(createParamCentered<BefacoPush>(mm2px(Vec(22.651, 29.068)), module, Kickall::TRIGG_BUTTON_PARAM));
addParam(createParamCentered<Davies1900hLargeGreyKnob>(mm2px(Vec(15.781, 49.278)), module, Kickall::SHAPE_PARAM));
addParam(createParam<BefacoSlidePot>(mm2px(Vec(19.913, 64.004)), module, Kickall::DECAY_PARAM));
addParam(createParamCentered<BefacoTinyKnobWhite>(mm2px(Vec(8.977, 71.626)), module, Kickall::TIME_PARAM));
addParam(createParamCentered<BefacoTinyKnobWhite>(mm2px(Vec(8.977, 93.549)), module, Kickall::BEND_PARAM));
addParam(createParamCentered<BefacoTinyKnobGrey>(mm2px(Vec(8.472, 28.97)), module, Kickall::TUNE_PARAM));
addParam(createParamCentered<BefacoPush>(mm2px(Vec(22.409, 29.159)), module, Kickall::TRIGG_BUTTON_PARAM));
addParam(createParamCentered<Davies1900hLargeGreyKnob>(mm2px(Vec(15.526, 49.292)), module, Kickall::SHAPE_PARAM));
addParam(createParam<BefacoSlidePot>(mm2px(Vec(19.667, 63.897)), module, Kickall::DECAY_PARAM));
addParam(createParamCentered<BefacoTinyKnob>(mm2px(Vec(8.521, 71.803)), module, Kickall::TIME_PARAM));
addParam(createParamCentered<BefacoTinyKnob>(mm2px(Vec(8.521, 93.517)), module, Kickall::BEND_PARAM));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(5.715, 14.651)), module, Kickall::TRIGG_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(15.748, 14.651)), module, Kickall::VOLUME_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(5.697, 113.286)), module, Kickall::TUNE_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(15.794, 113.286)), module, Kickall::SHAPE_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(25.891, 113.286)), module, Kickall::DECAY_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(15.501, 14.508)), module, Kickall::VOLUME_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(5.499, 14.536)), module, Kickall::TRIGG_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(25.525, 113.191)), module, Kickall::DECAY_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(5.499, 113.208)), module, Kickall::TUNE_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(15.485, 113.208)), module, Kickall::SHAPE_INPUT));
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(25.781, 14.651)), module, Kickall::OUT_OUTPUT));
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(25.525, 14.52)), module, Kickall::OUT_OUTPUT));
addChild(createLightCentered<SmallLight<RedLight>>(mm2px(Vec(15.723, 34.971)), module, Kickall::ENV_LIGHT));
addChild(createLightCentered<SmallLight<RedLight>>(mm2px(Vec(15.535, 34.943)), module, Kickall::ENV_LIGHT));
}
};


Loading…
Cancel
Save