Browse Source

Addressing review comments

* use of string::f
* removed unnecessary casts to float_4
* corrected/updated brace initialisation
* removed github actions
tags/v1.1.0^2
hemmer 3 years ago
parent
commit
411ec729b7
11 changed files with 157 additions and 274 deletions
  1. +0
    -103
      .github/workflows/build-plugin.yml
  2. +5
    -5
      src/ABC.cpp
  3. +4
    -4
      src/ChoppingKinky.cpp
  4. +26
    -41
      src/EvenVCO.cpp
  5. +19
    -15
      src/HexmixVCA.cpp
  6. +4
    -9
      src/Mixer.cpp
  7. +9
    -11
      src/Percall.cpp
  8. +8
    -9
      src/PulseGenerator_4.hpp
  9. +37
    -43
      src/Rampage.cpp
  10. +12
    -14
      src/SlewLimiter.cpp
  11. +33
    -20
      src/SpringReverb.cpp

+ 0
- 103
.github/workflows/build-plugin.yml View File

@@ -1,103 +0,0 @@
name: Build VCV Rack Plugin
on: [push, pull_request]
env:
rack-sdk-version: 1.1.6
defaults:
run:
shell: bash
jobs:
build:
name: ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}
strategy:
matrix:
config:
- {
name: Linux,
os: ubuntu-16.04,
prepare-os: sudo apt install -y libglu-dev
}
- {
name: MacOS,
os: macos-latest,
prepare-os: ""
}
- {
name: Windows,
os: windows-latest,
prepare-os: export CC=gcc
}
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Get Rack-SDK
run: |
pushd $HOME
curl -o Rack-SDK.zip https://vcvrack.com/downloads/Rack-SDK-${{ env.rack-sdk-version }}.zip
unzip Rack-SDK.zip
- name: Patch plugin.mk, use 7zip on Windows
if: runner.os == 'Windows'
run: |
sed -i 's/zip -q -9 -r/7z a -tzip -mx=9/' $HOME/Rack-SDK/plugin.mk
- name: Modify plugin version
# only modify plugin version if no tag was created
if: "! startsWith(github.ref, 'refs/tags/v')"
run: |
gitrev=`git rev-parse --short HEAD`
pluginversion=`jq -r '.version' plugin.json`
echo "Set plugin version from $pluginversion to $pluginversion-$gitrev"
cat <<< `jq --arg VERSION "$pluginversion-$gitrev" '.version=$VERSION' plugin.json` > plugin.json
- name: Build plugin
run: |
${{ matrix.config.prepare-os }}
export RACK_DIR=$HOME/Rack-SDK
make -j dep
make -j dist
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
path: dist
name: ${{ matrix.config.name }}
publish:
name: Publish plugin
# only create a release if a tag was created that is called e.g. v1.2.3
# see also https://vcvrack.com/manual/Manifest#version
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-16.04
needs: build
steps:
- uses: actions/checkout@v2
- uses: FranzDiebold/github-env-vars-action@v1.2.1
- name: Check if plugin version matches tag
run: |
pluginversion=`jq -r '.version' plugin.json`
if [ "v$pluginversion" != "${{ env.GITHUB_REF_NAME }}" ]; then
echo "Plugin version from plugin.json 'v$pluginversion' doesn't match with tag version '${{ env.GITHUB_REF_NAME }}'"
exit 1
fi
- name: Create Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
body: |
${{ env.GITHUB_REPOSITORY_NAME }} VCV Rack Plugin ${{ env.GITHUB_REF_NAME }}
draft: false
prerelease: false
- uses: actions/download-artifact@v2
with:
path: _artifacts
- name: Upload release assets
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: _artifacts/**/*.zip
tag: ${{ github.ref }}
file_glob: true

+ 5
- 5
src/ABC.cpp View File

@@ -56,9 +56,9 @@ struct ABC : Module {

int processSection(simd::float_4* out, InputIds inputA, InputIds inputB, InputIds inputC, ParamIds levelB, ParamIds levelC) {

float_4 inA[4] = {0.f};
float_4 inB[4] = {0.f};
float_4 inC[4] = {0.f};
float_4 inA[4] = {};
float_4 inB[4] = {};
float_4 inC[4] = {};

int channelsA = inputs[inputA].getChannels();
int channelsB = inputs[inputB].getChannels();
@@ -105,13 +105,13 @@ struct ABC : Module {
void process(const ProcessArgs& args) override {

// process upper section
float_4 out1[4] = {0.f};
float_4 out1[4] = {};
int activeEngines1 = 1;
if (outputs[OUT1_OUTPUT].isConnected() || outputs[OUT2_OUTPUT].isConnected()) {
activeEngines1 = processSection(out1, A1_INPUT, B1_INPUT, C1_INPUT, B1_LEVEL_PARAM, C1_LEVEL_PARAM);
}

float_4 out2[4] = {0.f};
float_4 out2[4] = {};
int activeEngines2 = 1;
// process lower section
if (outputs[OUT2_OUTPUT].isConnected()) {


+ 4
- 4
src/ChoppingKinky.cpp View File

@@ -39,9 +39,9 @@ struct ChoppingKinky : Module {
};
static const int WAVESHAPE_CACHE_SIZE = 256;
float waveshapeA[WAVESHAPE_CACHE_SIZE + 1] = {0.f};
float waveshapeBPositive[WAVESHAPE_CACHE_SIZE + 1] = {0.f};
float waveshapeBNegative[WAVESHAPE_CACHE_SIZE + 1] = {0.f};
float waveshapeA[WAVESHAPE_CACHE_SIZE + 1] = {};
float waveshapeBPositive[WAVESHAPE_CACHE_SIZE + 1] = {};
float waveshapeBNegative[WAVESHAPE_CACHE_SIZE + 1] = {};
dsp::SchmittTrigger trigger;
bool outputAToChopp = false;
@@ -349,7 +349,7 @@ struct ChoppingKinkyWidget : ModuleWidget {
}
};
for (int i = 0; i < 5; i++) {
ModeItem* modeItem = createMenuItem<ModeItem>(std::to_string(int (1 << i)) + "x");
ModeItem* modeItem = createMenuItem<ModeItem>(string::f("%dx", int (1 << i)));
modeItem->rightText = CHECKMARK(module->oversamplingIndex == i);
modeItem->module = module;
modeItem->oversamplingIndex = i;


+ 26
- 41
src/EvenVCO.cpp View File

@@ -26,14 +26,14 @@ struct EvenVCO : Module {
NUM_OUTPUTS
};

float_4 phase[4];
float_4 tri[4];
float_4 phase[4] = {};
float_4 tri[4] = {};

/** The value of the last sync input */
float sync = 0.0;
/** The outputs */
/** Whether we are past the pulse width already */
bool halfPhase[PORT_MAX_CHANNELS];
bool halfPhase[PORT_MAX_CHANNELS] = {};

dsp::MinBlepGenerator<16, 32> triSquareMinBlep[PORT_MAX_CHANNELS];
dsp::MinBlepGenerator<16, 32> triMinBlep[PORT_MAX_CHANNELS];
@@ -42,20 +42,11 @@ struct EvenVCO : Module {
dsp::MinBlepGenerator<16, 32> sawMinBlep[PORT_MAX_CHANNELS];
dsp::MinBlepGenerator<16, 32> squareMinBlep[PORT_MAX_CHANNELS];

dsp::RCFilter triFilter;

EvenVCO() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
configParam(OCTAVE_PARAM, -5.0, 4.0, 0.0, "Octave", "'", 0.5);
configParam(TUNE_PARAM, -7.0, 7.0, 0.0, "Tune", " semitones");
configParam(PWM_PARAM, -1.0, 1.0, 0.0, "Pulse width");

for (int i = 0; i < 4; i++) {
phase[i] = 0.f;
tri[i] = 0.f;
}
for (int c = 0; c < PORT_MAX_CHANNELS; c++)
halfPhase[c] = false;
}

void process(const ProcessArgs& args) override {
@@ -70,9 +61,9 @@ struct EvenVCO : Module {
float pitch_0 = 1.f + std::round(params[OCTAVE_PARAM].getValue()) + params[TUNE_PARAM].getValue() / 12.f;

// Compute frequency, pitch is 1V/oct
float_4 pitch[4];
float_4 pitch[4] = {};
for (int c = 0; c < channels; c += 4)
pitch[c / 4] = float_4(pitch_0);
pitch[c / 4] = pitch_0;

if (inputs[PITCH1_INPUT].isConnected()) {
for (int c = 0; c < channels; c += 4)
@@ -89,30 +80,26 @@ struct EvenVCO : Module {
pitch[c / 4] += inputs[FM_INPUT].getPolyVoltageSimd<float_4>(c) / 4.f;
}

float_4 freq[4];
float_4 freq[4] = {};
for (int c = 0; c < channels; c += 4) {
freq[c / 4] = dsp::FREQ_C4 * simd::pow(2.f, pitch[c / 4]);
freq[c / 4] = clamp(freq[c / 4], 0.f, 20000.f);
}

// Pulse width
float pw_0 = params[PWM_PARAM].getValue();
float_4 pw[4];
// Pulse width
float_4 pw[4] = {};
for (int c = 0; c < channels; c += 4)
pw[c / 4] = float_4(pw_0);
pw[c / 4] = params[PWM_PARAM].getValue();

if (inputs[PWM_INPUT].isConnected()) {
for (int c = 0; c < channels; c += 4)
pw[c / 4] += inputs[PWM_INPUT].getPolyVoltageSimd<float_4>(c) / 5.f;
}

const float_4 minPw_4 = float_4(0.05f);
const float_4 m_one_4 = float_4(-1.0f);
const float_4 one_4 = float_4(1.0f);
float_4 deltaPhase[4];
float_4 oldPhase[4];
float_4 deltaPhase[4] = {};
float_4 oldPhase[4] = {};
for (int c = 0; c < channels; c += 4) {
pw[c / 4] = rescale(clamp(pw[c / 4], m_one_4, one_4), m_one_4, one_4, minPw_4, one_4 - minPw_4);
pw[c / 4] = rescale(clamp(pw[c / 4], -1.0f, 1.0f), -1.0f, 1.0f, 0.05f, 1.0f - 0.05f);

// Advance phase
deltaPhase[c / 4] = clamp(freq[c / 4] * args.sampleTime, float_4(1e-6f), float_4(0.5f));
@@ -121,7 +108,6 @@ struct EvenVCO : Module {
}

// the next block can't be done with SIMD instructions:

for (int c = 0; c < channels; c++) {

if (oldPhase[c / 4].s[c % 4] < 0.5 && phase[c / 4].s[c % 4] >= 0.5) {
@@ -148,19 +134,19 @@ struct EvenVCO : Module {
}
}

float_4 triSquareMinBlepOut[4];
float_4 doubleSawMinBlepOut[4];
float_4 sawMinBlepOut[4];
float_4 squareMinBlepOut[4];
float_4 triSquareMinBlepOut[4] = {};
float_4 doubleSawMinBlepOut[4] = {};
float_4 sawMinBlepOut[4] = {};
float_4 squareMinBlepOut[4] = {};

float_4 triSquare[4];
float_4 sine[4];
float_4 doubleSaw[4];
float_4 triSquare[4] = {};
float_4 sine[4] = {};
float_4 doubleSaw[4] = {};

float_4 even[4];
float_4 saw[4];
float_4 square[4];
float_4 triOut[4];
float_4 even[4] = {};
float_4 saw[4] = {};
float_4 square[4] = {};
float_4 triOut[4] = {};

for (int c = 0; c < channels; c++) {
triSquareMinBlepOut[c / 4].s[c % 4] = triSquareMinBlep[c].process();
@@ -170,7 +156,6 @@ struct EvenVCO : Module {
}

// Outputs

outputs[TRI_OUTPUT].setChannels(channels);
outputs[SINE_OUTPUT].setChannels(channels);
outputs[EVEN_OUTPUT].setChannels(channels);
@@ -179,7 +164,7 @@ struct EvenVCO : Module {

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

triSquare[c / 4] = simd::ifelse((phase[c / 4] < 0.5f * one_4), m_one_4, one_4);
triSquare[c / 4] = simd::ifelse((phase[c / 4] < 0.5f), -1.f, +1.f);
triSquare[c / 4] += triSquareMinBlepOut[c / 4];

// Integrate square for triangle
@@ -199,7 +184,7 @@ struct EvenVCO : Module {
saw[c / 4] += sawMinBlepOut[c / 4];
saw[c / 4] *= 5.f;

square[c / 4] = simd::ifelse((phase[c / 4] < pw[c / 4]), m_one_4, one_4) ;
square[c / 4] = simd::ifelse((phase[c / 4] < pw[c / 4]), -1.f, +1.f);
square[c / 4] += squareMinBlepOut[c / 4];
square[c / 4] *= 5.f;



+ 19
- 15
src/HexmixVCA.cpp View File

@@ -35,21 +35,25 @@ struct HexmixVCA : Module {
const static int numRows = 6;
dsp::ClockDivider cvDivider;
float outputLevels[numRows] = {1.f};
float shapes[numRows] = {0.f};
float outputLevels[numRows] = {};
float shapes[numRows] = {};
bool finalRowIsMix = true;
HexmixVCA() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
for (int i = 0; i < numRows; ++i) {
configParam(SHAPE_PARAM + i, -1.f, 1.f, 0.f, "VCA response");
configParam(VOL_PARAM + i, 0.f, 1.f, 1.f, "Output level");
configParam(SHAPE_PARAM + i, -1.f, 1.f, 0.f, string::f("Channel %d VCA response", i));
configParam(VOL_PARAM + i, 0.f, 1.f, 1.f, string::f("Channel %d output level", i));
}
cvDivider.setDivision(16);
for (int row = 0; row < numRows; ++row) {
outputLevels[row] = 1.f;
}
}
void process(const ProcessArgs& args) override {
float_4 mix[4] = {0.f};
float_4 mix[4] = {};
int maxChannels = 1;
// only calculate gains/shapes every 16 samples
@@ -63,7 +67,7 @@ struct HexmixVCA : Module {
for (int row = 0; row < numRows; ++row) {
bool finalRow = (row == numRows - 1);
int channels = 1;
float_4 in[4] = {0.f};
float_4 in[4] = {};
bool inputIsConnected = inputs[IN_INPUT + row].isConnected();
if (inputIsConnected) {
channels = inputs[row].getChannels();
@@ -73,7 +77,7 @@ struct HexmixVCA : Module {
if (finalRowIsMix && (finalRow || !outputs[OUT_OUTPUT + row].isConnected())) {
maxChannels = std::max(maxChannels, channels);
}
float cvGain = clamp(inputs[CV_INPUT + row].getNormalVoltage(10.f) / 10.f, 0.f, 1.f);
float gain = gainFunction(cvGain, shapes[row]) * outputLevels[row];
@@ -81,7 +85,7 @@ struct HexmixVCA : Module {
in[c / 4] = inputs[row].getVoltageSimd<float_4>(c) * gain;
}
}
if (!finalRow) {
if (outputs[OUT_OUTPUT + row].isConnected()) {
// if output is connected, we don't add to mix
@@ -185,19 +189,19 @@ struct HexmixVCAWidget : ModuleWidget {
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(64.222, 108.536)), module, HexmixVCA::OUT_OUTPUT + 5));
}
struct MixMenuItem : MenuItem {
HexmixVCA* module;
void onAction(const event::Action& e) override {
module->finalRowIsMix ^= true;
}
};
void appendContextMenu(Menu* menu) override {
HexmixVCA* module = dynamic_cast<HexmixVCA*>(this->module);
assert(module);
menu->addChild(new MenuSeparator());
struct MixMenuItem : MenuItem {
HexmixVCA* module;
void onAction(const event::Action& e) override {
module->finalRowIsMix ^= true;
}
};
MixMenuItem* mixItem = createMenuItem<MixMenuItem>("Final row is mix", CHECKMARK(module->finalRowIsMix));
mixItem->module = module;
menu->addChild(mixItem);


+ 4
- 9
src/Mixer.cpp View File

@@ -49,31 +49,26 @@ struct Mixer : Module {
out_channels = std::max(out_channels, channels3);
out_channels = std::max(out_channels, channels4);

float_4 mult1 = float_4(params[CH1_PARAM].getValue());
float_4 mult2 = float_4(params[CH2_PARAM].getValue());
float_4 mult3 = float_4(params[CH3_PARAM].getValue());
float_4 mult4 = float_4(params[CH4_PARAM].getValue());

float_4 out[4] = {};

if (inputs[IN1_INPUT].isConnected()) {
for (int c = 0; c < channels1; c += 4)
out[c / 4] += inputs[IN1_INPUT].getVoltageSimd<float_4>(c) * mult1;
out[c / 4] += inputs[IN1_INPUT].getVoltageSimd<float_4>(c) * params[CH1_PARAM].getValue();
}

if (inputs[IN2_INPUT].isConnected()) {
for (int c = 0; c < channels2; c += 4)
out[c / 4] += inputs[IN2_INPUT].getVoltageSimd<float_4>(c) * mult2;
out[c / 4] += inputs[IN2_INPUT].getVoltageSimd<float_4>(c) * params[CH2_PARAM].getValue();
}

if (inputs[IN3_INPUT].isConnected()) {
for (int c = 0; c < channels3; c += 4)
out[c / 4] += inputs[IN3_INPUT].getVoltageSimd<float_4>(c) * mult3;
out[c / 4] += inputs[IN3_INPUT].getVoltageSimd<float_4>(c) * params[CH3_PARAM].getValue();
}

if (inputs[IN4_INPUT].isConnected()) {
for (int c = 0; c < channels4; c += 4)
out[c / 4] += inputs[IN4_INPUT].getVoltageSimd<float_4>(c) * mult4;
out[c / 4] += inputs[IN4_INPUT].getVoltageSimd<float_4>(c) * params[CH4_PARAM].getValue();
}

outputs[OUT1_OUTPUT].setChannels(out_channels);


+ 9
- 11
src/Percall.cpp View File

@@ -28,9 +28,8 @@ struct Percall : Module {
ADEnvelope envs[4];
float gains[4] = {0.f};
float gains[4] = {};
float strength = 1.0f;
dsp::SchmittTrigger trigger[4];
dsp::ClockDivider cvDivider;
dsp::ClockDivider lightDivider;
@@ -43,16 +42,15 @@ struct Percall : Module {
Percall() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
for (int i = 0; i < 4; i++) {
configParam(VOL_PARAMS + i, 0.f, 1.f, 1.f, "Ch " + std::to_string(i + 1) + " level", "%", 0, 100);
configParam(DECAY_PARAMS + i, 0.f, 1.f, 0.f, "Ch " + std::to_string(i + 1) + " decay time");
configParam(VOL_PARAMS + i, 0.f, 1.f, 1.f, string::f("Channel %d level", i + 1), "%", 0, 100);
configParam(DECAY_PARAMS + i, 0.f, 1.f, 0.f, string::f("Channel %d decay time", i + 1));
envs[i].attackTime = attackTime;
envs[i].attackShape = 0.5f;
envs[i].decayShape = 2.0f;
}
for (int i = 0; i < 2; i++) {
std::string description = "Choke " + std::to_string(2 * i + 1) + " to " + std::to_string(2 * i + 2);
configParam(CHOKE_PARAMS + i, 0.f, 1.f, 0.f, description);
for (int i = 0; i < 2; i++) {
configParam(CHOKE_PARAMS + i, 0.f, 1.f, 0.f, string::f("Choke %d to %d", 2 * i + 1, 2 * i + 2));
}
cvDivider.setDivision(16);
@@ -61,9 +59,9 @@ struct Percall : Module {
void process(const ProcessArgs& args) override {
strength = 1.0f;
float strength = 1.0f;
if (inputs[STRENGTH_INPUT].isConnected()) {
strength = sqrt(clamp(inputs[STRENGTH_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f));
strength = std::sqrt(clamp(inputs[STRENGTH_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f));
}
// only calculate gains/decays every 16 samples
@@ -76,7 +74,7 @@ struct Percall : Module {
}
}
float_4 mix[4] = {0.f};
float_4 mix[4] = {};
int maxPolyphonyChannels = 1;
// Mixer channels


+ 8
- 9
src/PulseGenerator_4.hpp View File

@@ -4,26 +4,25 @@

/** When triggered, holds a high value for a specified time before going low again */
struct PulseGenerator_4 {
simd::float_4 remaining = simd::float_4::zero();
simd::float_4 remaining = 0.f;

/** Immediately disables the pulse */
void reset() {
remaining = simd::float_4::zero();
remaining = 0.f;
}

/** Advances the state by `deltaTime`. Returns whether the pulse is in the HIGH state. */
inline simd::float_4 process(float deltaTime) {
simd::float_4 process(float deltaTime) {

simd::float_4 mask = (remaining > simd::float_4::zero());
simd::float_4 mask = (remaining > 0.f);

remaining -= ifelse(mask, simd::float_4(deltaTime), simd::float_4::zero());
return ifelse(mask, simd::float_4::mask(), simd::float_4::zero());
remaining -= ifelse(mask, simd::float_4(deltaTime), 0.f);
return ifelse(mask, simd::float_4::mask(), 0.f);
}

/** Begins a trigger with the given `duration`. */
inline void trigger(simd::float_4 mask, float duration = 1e-3f) {
void trigger(simd::float_4 mask, float duration = 1e-3f) {
// Keep the previous pulse if the existing pulse will be held longer than the currently requested one.
simd::float_4 duration_4 = simd::float_4(duration);
remaining = ifelse(mask & (duration_4 > remaining), duration_4, remaining);
remaining = ifelse(mask & (duration > remaining), duration, remaining);
}
};

+ 37
- 43
src/Rampage.cpp View File

@@ -75,8 +75,8 @@ struct Rampage : Module {
};


float_4 out[2][4];
float_4 gate[2][4]; // use simd __m128 logic instead of bool
float_4 out[2][4] = {};
float_4 gate[2][4] = {}; // use simd __m128 logic instead of bool

dsp::TSchmittTrigger<float_4> trigger_4[2][4];
PulseGenerator_4 endOfCyclePulse[2][4];
@@ -98,15 +98,12 @@ struct Rampage : Module {
configParam(FALL_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 fall time");
configParam(CYCLE_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 cycle");
configParam(BALANCE_PARAM, 0.0, 1.0, 0.5, "Balance");

std::memset(out, 0, sizeof(out));
std::memset(gate, 0, sizeof(gate));
}

void process(const ProcessArgs& args) override {
int channels_in[2];
int channels_trig[2];
int channels[2]; // the larger of in or trig (per-part)
int channels_in[2] = {};
int channels_trig[2] = {};
int channels[2] = {}; // the larger of in or trig (per-part)

// determine number of channels:

@@ -122,7 +119,7 @@ struct Rampage : Module {
outputs[FALLING_A_OUTPUT + part].setChannels(channels[part]);
outputs[EOC_A_OUTPUT + part].setChannels(channels[part]);
}
// total number of active polyphony engines, accounting for both halves
// (channels[0] / channels[1] are the number of active engines per section)
const int channels_max = std::max(channels[0], channels[1]);
@@ -134,14 +131,13 @@ struct Rampage : Module {
// loop over two parts of Rampage:
for (int part = 0; part < 2; part++) {

float_4 in[4] = {0.f};
float_4 in_trig[4] = {0.f};
float_4 riseCV[4] = {0.f};
float_4 fallCV[4] = {0.f};
float_4 cycle[4] = {0.f};
float_4 in[4] = {};
float_4 in_trig[4] = {};
float_4 riseCV[4] = {};
float_4 fallCV[4] = {};
float_4 cycle[4] = {};

// get parameters:
float shape = params[SHAPE_A_PARAM + part].getValue();
float minTime;
switch ((int) params[RANGE_A_PARAM + part].getValue()) {
case 0:
@@ -155,10 +151,10 @@ struct Rampage : Module {
break;
}

float_4 param_rise = float_4(params[RISE_A_PARAM + part].getValue() * 10.0f);
float_4 param_fall = float_4(params[FALL_A_PARAM + part].getValue() * 10.0f);
float_4 param_trig = float_4(params[TRIGG_A_PARAM + part].getValue() * 20.0f);
float_4 param_cycle = float_4(params[CYCLE_A_PARAM + part].getValue() * 10.0f);
float_4 param_rise = params[RISE_A_PARAM + part].getValue() * 10.0f;
float_4 param_fall = params[FALL_A_PARAM + part].getValue() * 10.0f;
float_4 param_trig = params[TRIGG_A_PARAM + part].getValue() * 20.0f;
float_4 param_cycle = params[CYCLE_A_PARAM + part].getValue() * 10.0f;

for (int c = 0; c < channels[part]; c += 4) {
riseCV[c / 4] = param_rise;
@@ -168,7 +164,7 @@ struct Rampage : Module {
}

// read inputs:
if (inputs[IN_A_INPUT + part].isConnected()) {
if (inputs[IN_A_INPUT + part].isConnected()) {
for (int c = 0; c < channels[part]; c += 4)
in[c / 4] = inputs[IN_A_INPUT + part].getPolyVoltageSimd<float_4>(c);
}
@@ -179,7 +175,7 @@ struct Rampage : Module {
}

if (inputs[EXP_CV_A_INPUT + part].isConnected()) {
float_4 expCV[4];
float_4 expCV[4] = {};
for (int c = 0; c < channels[part]; c += 4)
expCV[c / 4] = inputs[EXP_CV_A_INPUT + part].getPolyVoltageSimd<float_4>(c);

@@ -190,11 +186,11 @@ struct Rampage : Module {
}

for (int c = 0; c < channels[part]; c += 4)
riseCV[c / 4] += inputs[RISE_CV_A_INPUT + part].getPolyVoltageSimd<float_4>(c);
riseCV[c / 4] += inputs[RISE_CV_A_INPUT + part].getPolyVoltageSimd<float_4>(c);
for (int c = 0; c < channels[part]; c += 4)
fallCV[c / 4] += inputs[FALL_CV_A_INPUT + part].getPolyVoltageSimd<float_4>(c);
for (int c = 0; c < channels[part]; c += 4)
cycle[c / 4] += inputs[CYCLE_A_INPUT + part].getPolyVoltageSimd<float_4>(c);
cycle[c / 4] += inputs[CYCLE_A_INPUT + part].getPolyVoltageSimd<float_4>(c);

// start processing:
for (int c = 0; c < channels[part]; c += 4) {
@@ -207,39 +203,38 @@ struct Rampage : Module {
float_4 delta = in[c / 4] - out[part][c / 4];

// rise / fall branching

float_4 delta_gt_0 = delta > float_4::zero();
float_4 delta_lt_0 = delta < float_4::zero();
float_4 delta_gt_0 = delta > 0.f;
float_4 delta_lt_0 = delta < 0.f;
float_4 delta_eq_0 = ~(delta_lt_0 | delta_gt_0);

float_4 rateCV = ifelse(delta_gt_0, riseCV[c / 4], float_4::zero());
float_4 rateCV = ifelse(delta_gt_0, riseCV[c / 4], 0.f);
rateCV = ifelse(delta_lt_0, fallCV[c / 4], rateCV);
rateCV = clamp(rateCV, float_4::zero(), float_4(10.0f));
rateCV = clamp(rateCV, 0.f, 10.0f);

float_4 rate = minTime * simd::pow(2.0f, rateCV);

float shape = params[SHAPE_A_PARAM + part].getValue();
out[part][c / 4] += shapeDelta(delta, rate, shape) * args.sampleTime;

float_4 rising = (in[c / 4] - out[part][c / 4]) > float_4(1e-3);
float_4 falling = (in[c / 4] - out[part][c / 4]) < float_4(-1e-3);
float_4 rising = (in[c / 4] - out[part][c / 4]) > 1e-3f;
float_4 falling = (in[c / 4] - out[part][c / 4]) < -1e-3f;
float_4 end_of_cycle = simd::andnot(falling, delta_lt_0);

endOfCyclePulse[part][c / 4].trigger(end_of_cycle, 1e-3);

gate[part][c / 4] = ifelse(simd::andnot(rising, delta_gt_0), float_4::zero(), gate[part][c / 4]);
gate[part][c / 4] = ifelse(end_of_cycle & (cycle[c / 4] >= float_4(4.0f)), float_4::mask(), gate[part][c / 4]);
gate[part][c / 4] = ifelse(delta_eq_0, float_4::zero(), gate[part][c / 4]);
gate[part][c / 4] = ifelse(simd::andnot(rising, delta_gt_0), 0.f, gate[part][c / 4]);
gate[part][c / 4] = ifelse(end_of_cycle & (cycle[c / 4] >= 4.0f), float_4::mask(), gate[part][c / 4]);
gate[part][c / 4] = ifelse(delta_eq_0, 0.f, gate[part][c / 4]);

out[part][c / 4] = ifelse(rising | falling, out[part][c / 4], in[c / 4]);

float_4 out_rising = ifelse(rising, float_4(10.0f), float_4::zero());
float_4 out_falling = ifelse(falling, float_4(10.0f), float_4::zero());
float_4 out_rising = ifelse(rising, 10.0f, 0.f);
float_4 out_falling = ifelse(falling, 10.0f, 0.f);

float_4 pulse = endOfCyclePulse[part][c / 4].process(args.sampleTime);
float_4 out_EOC = ifelse(pulse, float_4(10.f), float_4::zero());

out[part][c / 4].store(outputs[OUT_A_OUTPUT + part].getVoltages(c));
float_4 out_EOC = ifelse(pulse, 10.f, 0.f);

outputs[OUT_A_OUTPUT + part].setVoltageSimd(out[part][c / 4], c);
outputs[RISING_A_OUTPUT + part].setVoltageSimd(out_rising, c);
outputs[FALLING_A_OUTPUT + part].setVoltageSimd(out_falling, c);
outputs[EOC_A_OUTPUT + part].setVoltageSimd(out_EOC, c);
@@ -285,14 +280,13 @@ struct Rampage : Module {
else if (balance > 0.5)
a *= 2.0f * (1.0 - balance);

float_4 comp = ifelse(b > a, float_4(10.0f), float_4::zero());
float_4 comp = ifelse(b > a, 10.0f, 0.f);
float_4 out_min = simd::fmin(a, b);
float_4 out_max = simd::fmax(a, b);

comp.store(outputs[COMPARATOR_OUTPUT].getVoltages(c));
out_min.store(outputs[MIN_OUTPUT].getVoltages(c));
out_max.store(outputs[MAX_OUTPUT].getVoltages(c));

outputs[COMPARATOR_OUTPUT].setVoltageSimd(comp, c);
outputs[MIN_OUTPUT].setVoltageSimd(out_min, c);
outputs[MAX_OUTPUT].setVoltageSimd(out_max, c);
}
// Lights
if (channels_max == 1) {


+ 12
- 14
src/SlewLimiter.cpp View File

@@ -20,22 +20,20 @@ struct SlewLimiter : Module {
NUM_OUTPUTS
};

float_4 out[4];
float_4 out[4] = {};

SlewLimiter() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
configParam(SHAPE_PARAM, 0.0, 1.0, 0.0, "Shape");
configParam(RISE_PARAM, 0.0, 1.0, 0.0, "Rise time");
configParam(FALL_PARAM, 0.0, 1.0, 0.0, "Fall time");

memset(out, 0, sizeof(out));
}

void process(const ProcessArgs& args) override {

float_4 in[4] = {0.f};
float_4 riseCV[4] = {0.f};
float_4 fallCV[4] = {0.f};
float_4 in[4] = {};
float_4 riseCV[4] = {};
float_4 fallCV[4] = {};

// this is the number of active polyphony engines, defined by the input
int numPolyphonyEngines = inputs[IN_INPUT].getChannels();
@@ -46,9 +44,8 @@ struct SlewLimiter : Module {
// Amount of extra slew per voltage difference
const float shapeScale = 1 / 10.f;

const float_4 shape = float_4(params[SHAPE_PARAM].getValue());
const float_4 param_rise = float_4(params[RISE_PARAM].getValue() * 10.f);
const float_4 param_fall = float_4(params[FALL_PARAM].getValue() * 10.f);
const float_4 param_rise = params[RISE_PARAM].getValue() * 10.f;
const float_4 param_fall = params[FALL_PARAM].getValue() * 10.f;

outputs[OUT_OUTPUT].setChannels(numPolyphonyEngines);

@@ -66,21 +63,22 @@ struct SlewLimiter : Module {
fallCV[c / 4] += param_fall;

float_4 delta = in[c / 4] - out[c / 4];
float_4 delta_gt_0 = delta > float_4::zero();
float_4 delta_lt_0 = delta < float_4::zero();
float_4 delta_gt_0 = delta > 0.f;
float_4 delta_lt_0 = delta < 0.f;

float_4 rateCV;
rateCV = ifelse(delta_gt_0, riseCV[c / 4], float_4::zero());
float_4 rateCV = {};
rateCV = ifelse(delta_gt_0, riseCV[c / 4], 0.f);
rateCV = ifelse(delta_lt_0, fallCV[c / 4], rateCV) * 0.1f;

float_4 pm_one = simd::sgn(delta);
float_4 slew = slewMax * simd::pow(float_4(slewMin / slewMax), rateCV);

const float shape = params[SHAPE_PARAM].getValue();
out[c / 4] += slew * simd::crossfade(pm_one, shapeScale * delta, shape) * args.sampleTime;
out[c / 4] = ifelse(delta_gt_0 & (out[c / 4] > in[c / 4]), in[c / 4], out[c / 4]);
out[c / 4] = ifelse(delta_lt_0 & (out[c / 4] < in[c / 4]), in[c / 4], out[c / 4]);

out[c / 4].store(outputs[OUT_OUTPUT].getVoltages(c));
outputs[OUT_OUTPUT].setVoltageSimd(out[c / 4], c);
}
}
};


+ 33
- 20
src/SpringReverb.cpp View File

@@ -34,15 +34,18 @@ struct SpringReverb : Module {
NUM_LIGHTS
};

dsp::RealTimeConvolver *convolver = NULL;
dsp::RealTimeConvolver* convolver = NULL;
dsp::SampleRateConverter<1> inputSrc;
dsp::SampleRateConverter<1> outputSrc;
dsp::DoubleRingBuffer<dsp::Frame<1>, 16 * BLOCK_SIZE> inputBuffer;
dsp::DoubleRingBuffer<dsp::Frame<1>, 16 * BLOCK_SIZE> outputBuffer;

dsp::RCFilter dryFilter;
dsp::PeakFilter vuFilter;
dsp::PeakFilter lightFilter;

dsp::VuMeter2 vuFilter;
dsp::VuMeter2 lightFilter;
dsp::ClockDivider lightRefreshClock;


SpringReverb() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
@@ -53,16 +56,21 @@ struct SpringReverb : Module {

convolver = new dsp::RealTimeConvolver(BLOCK_SIZE);

const float *kernel = (const float *) BINARY_START(src_SpringReverbIR_pcm);
const float* kernel = (const float*) BINARY_START(src_SpringReverbIR_pcm);
size_t kernelLen = BINARY_SIZE(src_SpringReverbIR_pcm) / sizeof(float);
convolver->setKernel(kernel, kernelLen);

vuFilter.mode = dsp::VuMeter2::PEAK;
lightFilter.mode = dsp::VuMeter2::PEAK;

lightRefreshClock.setDivision(32);
}

~SpringReverb() {
delete convolver;
}

void process(const ProcessArgs &args) override {
void process(const ProcessArgs& args) override {
float in1 = inputs[IN1_INPUT].getVoltageSum();
float in2 = inputs[IN2_INPUT].getVoltageSum();
const float levelScale = 0.030;
@@ -92,7 +100,7 @@ struct SpringReverb : Module {
inputSrc.setRates(args.sampleRate, 48000);
int inLen = inputBuffer.size();
int outLen = BLOCK_SIZE;
inputSrc.process(inputBuffer.startData(), &inLen, (dsp::Frame<1> *) input, &outLen);
inputSrc.process(inputBuffer.startData(), &inLen, (dsp::Frame<1>*) input, &outLen);
inputBuffer.startIncr(inLen);
}

@@ -104,7 +112,7 @@ struct SpringReverb : Module {
outputSrc.setRates(48000, args.sampleRate);
int inLen = BLOCK_SIZE;
int outLen = outputBuffer.capacity();
outputSrc.process((dsp::Frame<1> *) output, &inLen, outputBuffer.endData(), &outLen);
outputSrc.process((dsp::Frame<1>*) output, &inLen, outputBuffer.endData(), &outLen);
outputBuffer.endIncr(outLen);
}
}
@@ -112,6 +120,7 @@ struct SpringReverb : Module {
// Set output
if (outputBuffer.empty())
return;

float wet = outputBuffer.shift().samples[0];
float balance = clamp(params[WET_PARAM].getValue() + inputs[MIX_CV_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f);
float mix = crossfade(in1, wet, balance);
@@ -119,24 +128,28 @@ struct SpringReverb : Module {
outputs[WET_OUTPUT].setVoltage(clamp(wet, -10.0f, 10.0f));
outputs[MIX_OUTPUT].setVoltage(clamp(mix, -10.0f, 10.0f));

// Set lights
float lightRate = 5.0 * args.sampleTime;
vuFilter.setLambda(1.f - lightRate);
float vuValue = vuFilter.process(1.f, std::fabs(wet));
lightFilter.setLambda(1.f - lightRate);
float lightValue = lightFilter.process(1.f, std::fabs(dry * 50.0));
for (int i = 0; i < 7; i++) {
float light = std::pow(1.413, i) * vuValue / 10.0 - 1.0;
lights[VU1_LIGHTS + i].value = clamp(light, 0.0f, 1.0f);
// process VU lights
vuFilter.process(args.sampleTime, wet);
// process peak light
lightFilter.process(args.sampleTime, dry * 50.0);

if (lightRefreshClock.process()) {

float brightnessIntervals[8] = {14.f, 14.f, 12.f, 9.f, 6.f, 0.f, -6.f, -12.f};

for (int i = 0; i < 7; i++) {
float brightness = vuFilter.getBrightness(brightnessIntervals[i + 1], brightnessIntervals[i]);
lights[VU1_LIGHTS + i].setBrightness(brightness);
}

lights[PEAK_LIGHT].value = lightFilter.v;
}
lights[PEAK_LIGHT].value = lightValue;
}
};


struct SpringReverbWidget : ModuleWidget {
SpringReverbWidget(SpringReverb *module) {
SpringReverbWidget(SpringReverb* module) {
setModule(module);
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/SpringReverb.svg")));

@@ -173,4 +186,4 @@ struct SpringReverbWidget : ModuleWidget {
};


Model *modelSpringReverb = createModel<SpringReverb, SpringReverbWidget>("SpringReverb");
Model* modelSpringReverb = createModel<SpringReverb, SpringReverbWidget>("SpringReverb");

Loading…
Cancel
Save