* use of string::f * removed unnecessary casts to float_4 * corrected/updated brace initialisation * removed github actionstags/v1.1.0^2
| @@ -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 | |||||
| @@ -56,9 +56,9 @@ struct ABC : Module { | |||||
| int processSection(simd::float_4* out, InputIds inputA, InputIds inputB, InputIds inputC, ParamIds levelB, ParamIds levelC) { | 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 channelsA = inputs[inputA].getChannels(); | ||||
| int channelsB = inputs[inputB].getChannels(); | int channelsB = inputs[inputB].getChannels(); | ||||
| @@ -105,13 +105,13 @@ struct ABC : Module { | |||||
| void process(const ProcessArgs& args) override { | void process(const ProcessArgs& args) override { | ||||
| // process upper section | // process upper section | ||||
| float_4 out1[4] = {0.f}; | |||||
| float_4 out1[4] = {}; | |||||
| int activeEngines1 = 1; | int activeEngines1 = 1; | ||||
| if (outputs[OUT1_OUTPUT].isConnected() || outputs[OUT2_OUTPUT].isConnected()) { | if (outputs[OUT1_OUTPUT].isConnected() || outputs[OUT2_OUTPUT].isConnected()) { | ||||
| activeEngines1 = processSection(out1, A1_INPUT, B1_INPUT, C1_INPUT, B1_LEVEL_PARAM, C1_LEVEL_PARAM); | 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; | int activeEngines2 = 1; | ||||
| // process lower section | // process lower section | ||||
| if (outputs[OUT2_OUTPUT].isConnected()) { | if (outputs[OUT2_OUTPUT].isConnected()) { | ||||
| @@ -39,9 +39,9 @@ struct ChoppingKinky : Module { | |||||
| }; | }; | ||||
| static const int WAVESHAPE_CACHE_SIZE = 256; | 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; | dsp::SchmittTrigger trigger; | ||||
| bool outputAToChopp = false; | bool outputAToChopp = false; | ||||
| @@ -349,7 +349,7 @@ struct ChoppingKinkyWidget : ModuleWidget { | |||||
| } | } | ||||
| }; | }; | ||||
| for (int i = 0; i < 5; i++) { | 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->rightText = CHECKMARK(module->oversamplingIndex == i); | ||||
| modeItem->module = module; | modeItem->module = module; | ||||
| modeItem->oversamplingIndex = i; | modeItem->oversamplingIndex = i; | ||||
| @@ -26,14 +26,14 @@ struct EvenVCO : Module { | |||||
| NUM_OUTPUTS | 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 */ | /** The value of the last sync input */ | ||||
| float sync = 0.0; | float sync = 0.0; | ||||
| /** The outputs */ | /** The outputs */ | ||||
| /** Whether we are past the pulse width already */ | /** 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> triSquareMinBlep[PORT_MAX_CHANNELS]; | ||||
| dsp::MinBlepGenerator<16, 32> triMinBlep[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> sawMinBlep[PORT_MAX_CHANNELS]; | ||||
| dsp::MinBlepGenerator<16, 32> squareMinBlep[PORT_MAX_CHANNELS]; | dsp::MinBlepGenerator<16, 32> squareMinBlep[PORT_MAX_CHANNELS]; | ||||
| dsp::RCFilter triFilter; | |||||
| EvenVCO() { | EvenVCO() { | ||||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); | ||||
| configParam(OCTAVE_PARAM, -5.0, 4.0, 0.0, "Octave", "'", 0.5); | configParam(OCTAVE_PARAM, -5.0, 4.0, 0.0, "Octave", "'", 0.5); | ||||
| configParam(TUNE_PARAM, -7.0, 7.0, 0.0, "Tune", " semitones"); | configParam(TUNE_PARAM, -7.0, 7.0, 0.0, "Tune", " semitones"); | ||||
| configParam(PWM_PARAM, -1.0, 1.0, 0.0, "Pulse width"); | 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 { | 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; | float pitch_0 = 1.f + std::round(params[OCTAVE_PARAM].getValue()) + params[TUNE_PARAM].getValue() / 12.f; | ||||
| // Compute frequency, pitch is 1V/oct | // Compute frequency, pitch is 1V/oct | ||||
| float_4 pitch[4]; | |||||
| float_4 pitch[4] = {}; | |||||
| for (int c = 0; c < channels; c += 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()) { | if (inputs[PITCH1_INPUT].isConnected()) { | ||||
| for (int c = 0; c < channels; c += 4) | 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; | 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) { | for (int c = 0; c < channels; c += 4) { | ||||
| freq[c / 4] = dsp::FREQ_C4 * simd::pow(2.f, pitch[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); | 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) | 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()) { | if (inputs[PWM_INPUT].isConnected()) { | ||||
| for (int c = 0; c < channels; c += 4) | for (int c = 0; c < channels; c += 4) | ||||
| pw[c / 4] += inputs[PWM_INPUT].getPolyVoltageSimd<float_4>(c) / 5.f; | 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) { | 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 | // Advance phase | ||||
| deltaPhase[c / 4] = clamp(freq[c / 4] * args.sampleTime, float_4(1e-6f), float_4(0.5f)); | 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: | // the next block can't be done with SIMD instructions: | ||||
| for (int c = 0; c < channels; c++) { | for (int c = 0; c < channels; c++) { | ||||
| if (oldPhase[c / 4].s[c % 4] < 0.5 && phase[c / 4].s[c % 4] >= 0.5) { | 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++) { | for (int c = 0; c < channels; c++) { | ||||
| triSquareMinBlepOut[c / 4].s[c % 4] = triSquareMinBlep[c].process(); | triSquareMinBlepOut[c / 4].s[c % 4] = triSquareMinBlep[c].process(); | ||||
| @@ -170,7 +156,6 @@ struct EvenVCO : Module { | |||||
| } | } | ||||
| // Outputs | // Outputs | ||||
| outputs[TRI_OUTPUT].setChannels(channels); | outputs[TRI_OUTPUT].setChannels(channels); | ||||
| outputs[SINE_OUTPUT].setChannels(channels); | outputs[SINE_OUTPUT].setChannels(channels); | ||||
| outputs[EVEN_OUTPUT].setChannels(channels); | outputs[EVEN_OUTPUT].setChannels(channels); | ||||
| @@ -179,7 +164,7 @@ struct EvenVCO : Module { | |||||
| for (int c = 0; c < channels; c += 4) { | 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]; | triSquare[c / 4] += triSquareMinBlepOut[c / 4]; | ||||
| // Integrate square for triangle | // Integrate square for triangle | ||||
| @@ -199,7 +184,7 @@ struct EvenVCO : Module { | |||||
| saw[c / 4] += sawMinBlepOut[c / 4]; | saw[c / 4] += sawMinBlepOut[c / 4]; | ||||
| saw[c / 4] *= 5.f; | 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] += squareMinBlepOut[c / 4]; | ||||
| square[c / 4] *= 5.f; | square[c / 4] *= 5.f; | ||||
| @@ -35,21 +35,25 @@ struct HexmixVCA : Module { | |||||
| const static int numRows = 6; | const static int numRows = 6; | ||||
| dsp::ClockDivider cvDivider; | dsp::ClockDivider cvDivider; | ||||
| float outputLevels[numRows] = {1.f}; | |||||
| float shapes[numRows] = {0.f}; | |||||
| float outputLevels[numRows] = {}; | |||||
| float shapes[numRows] = {}; | |||||
| bool finalRowIsMix = true; | bool finalRowIsMix = true; | ||||
| HexmixVCA() { | HexmixVCA() { | ||||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | ||||
| for (int i = 0; i < numRows; ++i) { | 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); | cvDivider.setDivision(16); | ||||
| for (int row = 0; row < numRows; ++row) { | |||||
| outputLevels[row] = 1.f; | |||||
| } | |||||
| } | } | ||||
| void process(const ProcessArgs& args) override { | void process(const ProcessArgs& args) override { | ||||
| float_4 mix[4] = {0.f}; | |||||
| float_4 mix[4] = {}; | |||||
| int maxChannels = 1; | int maxChannels = 1; | ||||
| // only calculate gains/shapes every 16 samples | // only calculate gains/shapes every 16 samples | ||||
| @@ -63,7 +67,7 @@ struct HexmixVCA : Module { | |||||
| for (int row = 0; row < numRows; ++row) { | for (int row = 0; row < numRows; ++row) { | ||||
| bool finalRow = (row == numRows - 1); | bool finalRow = (row == numRows - 1); | ||||
| int channels = 1; | int channels = 1; | ||||
| float_4 in[4] = {0.f}; | |||||
| float_4 in[4] = {}; | |||||
| bool inputIsConnected = inputs[IN_INPUT + row].isConnected(); | bool inputIsConnected = inputs[IN_INPUT + row].isConnected(); | ||||
| if (inputIsConnected) { | if (inputIsConnected) { | ||||
| channels = inputs[row].getChannels(); | channels = inputs[row].getChannels(); | ||||
| @@ -73,7 +77,7 @@ struct HexmixVCA : Module { | |||||
| if (finalRowIsMix && (finalRow || !outputs[OUT_OUTPUT + row].isConnected())) { | if (finalRowIsMix && (finalRow || !outputs[OUT_OUTPUT + row].isConnected())) { | ||||
| maxChannels = std::max(maxChannels, channels); | maxChannels = std::max(maxChannels, channels); | ||||
| } | } | ||||
| float cvGain = clamp(inputs[CV_INPUT + row].getNormalVoltage(10.f) / 10.f, 0.f, 1.f); | float cvGain = clamp(inputs[CV_INPUT + row].getNormalVoltage(10.f) / 10.f, 0.f, 1.f); | ||||
| float gain = gainFunction(cvGain, shapes[row]) * outputLevels[row]; | 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; | in[c / 4] = inputs[row].getVoltageSimd<float_4>(c) * gain; | ||||
| } | } | ||||
| } | } | ||||
| if (!finalRow) { | if (!finalRow) { | ||||
| if (outputs[OUT_OUTPUT + row].isConnected()) { | if (outputs[OUT_OUTPUT + row].isConnected()) { | ||||
| // if output is connected, we don't add to mix | // 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)); | 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 { | void appendContextMenu(Menu* menu) override { | ||||
| HexmixVCA* module = dynamic_cast<HexmixVCA*>(this->module); | HexmixVCA* module = dynamic_cast<HexmixVCA*>(this->module); | ||||
| assert(module); | assert(module); | ||||
| menu->addChild(new MenuSeparator()); | 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)); | MixMenuItem* mixItem = createMenuItem<MixMenuItem>("Final row is mix", CHECKMARK(module->finalRowIsMix)); | ||||
| mixItem->module = module; | mixItem->module = module; | ||||
| menu->addChild(mixItem); | menu->addChild(mixItem); | ||||
| @@ -49,31 +49,26 @@ struct Mixer : Module { | |||||
| out_channels = std::max(out_channels, channels3); | out_channels = std::max(out_channels, channels3); | ||||
| out_channels = std::max(out_channels, channels4); | 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] = {}; | float_4 out[4] = {}; | ||||
| if (inputs[IN1_INPUT].isConnected()) { | if (inputs[IN1_INPUT].isConnected()) { | ||||
| for (int c = 0; c < channels1; c += 4) | 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()) { | if (inputs[IN2_INPUT].isConnected()) { | ||||
| for (int c = 0; c < channels2; c += 4) | 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()) { | if (inputs[IN3_INPUT].isConnected()) { | ||||
| for (int c = 0; c < channels3; c += 4) | 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()) { | if (inputs[IN4_INPUT].isConnected()) { | ||||
| for (int c = 0; c < channels4; c += 4) | 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); | outputs[OUT1_OUTPUT].setChannels(out_channels); | ||||
| @@ -28,9 +28,8 @@ struct Percall : Module { | |||||
| ADEnvelope envs[4]; | ADEnvelope envs[4]; | ||||
| float gains[4] = {0.f}; | |||||
| float gains[4] = {}; | |||||
| float strength = 1.0f; | |||||
| dsp::SchmittTrigger trigger[4]; | dsp::SchmittTrigger trigger[4]; | ||||
| dsp::ClockDivider cvDivider; | dsp::ClockDivider cvDivider; | ||||
| dsp::ClockDivider lightDivider; | dsp::ClockDivider lightDivider; | ||||
| @@ -43,16 +42,15 @@ struct Percall : Module { | |||||
| Percall() { | Percall() { | ||||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | ||||
| for (int i = 0; i < 4; i++) { | 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].attackTime = attackTime; | ||||
| envs[i].attackShape = 0.5f; | envs[i].attackShape = 0.5f; | ||||
| envs[i].decayShape = 2.0f; | 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); | cvDivider.setDivision(16); | ||||
| @@ -61,9 +59,9 @@ struct Percall : Module { | |||||
| void process(const ProcessArgs& args) override { | void process(const ProcessArgs& args) override { | ||||
| strength = 1.0f; | |||||
| float strength = 1.0f; | |||||
| if (inputs[STRENGTH_INPUT].isConnected()) { | 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 | // 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; | int maxPolyphonyChannels = 1; | ||||
| // Mixer channels | // Mixer channels | ||||
| @@ -4,26 +4,25 @@ | |||||
| /** When triggered, holds a high value for a specified time before going low again */ | /** When triggered, holds a high value for a specified time before going low again */ | ||||
| struct PulseGenerator_4 { | struct PulseGenerator_4 { | ||||
| simd::float_4 remaining = simd::float_4::zero(); | |||||
| simd::float_4 remaining = 0.f; | |||||
| /** Immediately disables the pulse */ | /** Immediately disables the pulse */ | ||||
| void reset() { | void reset() { | ||||
| remaining = simd::float_4::zero(); | |||||
| remaining = 0.f; | |||||
| } | } | ||||
| /** Advances the state by `deltaTime`. Returns whether the pulse is in the HIGH state. */ | /** 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`. */ | /** 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. | // 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); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -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]; | dsp::TSchmittTrigger<float_4> trigger_4[2][4]; | ||||
| PulseGenerator_4 endOfCyclePulse[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(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(CYCLE_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 cycle"); | ||||
| configParam(BALANCE_PARAM, 0.0, 1.0, 0.5, "Balance"); | 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 { | 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: | // determine number of channels: | ||||
| @@ -122,7 +119,7 @@ struct Rampage : Module { | |||||
| outputs[FALLING_A_OUTPUT + part].setChannels(channels[part]); | outputs[FALLING_A_OUTPUT + part].setChannels(channels[part]); | ||||
| outputs[EOC_A_OUTPUT + part].setChannels(channels[part]); | outputs[EOC_A_OUTPUT + part].setChannels(channels[part]); | ||||
| } | } | ||||
| // total number of active polyphony engines, accounting for both halves | // total number of active polyphony engines, accounting for both halves | ||||
| // (channels[0] / channels[1] are the number of active engines per section) | // (channels[0] / channels[1] are the number of active engines per section) | ||||
| const int channels_max = std::max(channels[0], channels[1]); | const int channels_max = std::max(channels[0], channels[1]); | ||||
| @@ -134,14 +131,13 @@ struct Rampage : Module { | |||||
| // loop over two parts of Rampage: | // loop over two parts of Rampage: | ||||
| for (int part = 0; part < 2; part++) { | 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: | // get parameters: | ||||
| float shape = params[SHAPE_A_PARAM + part].getValue(); | |||||
| float minTime; | float minTime; | ||||
| switch ((int) params[RANGE_A_PARAM + part].getValue()) { | switch ((int) params[RANGE_A_PARAM + part].getValue()) { | ||||
| case 0: | case 0: | ||||
| @@ -155,10 +151,10 @@ struct Rampage : Module { | |||||
| break; | 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) { | for (int c = 0; c < channels[part]; c += 4) { | ||||
| riseCV[c / 4] = param_rise; | riseCV[c / 4] = param_rise; | ||||
| @@ -168,7 +164,7 @@ struct Rampage : Module { | |||||
| } | } | ||||
| // read inputs: | // 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) | for (int c = 0; c < channels[part]; c += 4) | ||||
| in[c / 4] = inputs[IN_A_INPUT + part].getPolyVoltageSimd<float_4>(c); | 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()) { | 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) | for (int c = 0; c < channels[part]; c += 4) | ||||
| expCV[c / 4] = inputs[EXP_CV_A_INPUT + part].getPolyVoltageSimd<float_4>(c); | 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) | 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) | for (int c = 0; c < channels[part]; c += 4) | ||||
| fallCV[c / 4] += inputs[FALL_CV_A_INPUT + part].getPolyVoltageSimd<float_4>(c); | fallCV[c / 4] += inputs[FALL_CV_A_INPUT + part].getPolyVoltageSimd<float_4>(c); | ||||
| for (int c = 0; c < channels[part]; c += 4) | 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: | // start processing: | ||||
| for (int c = 0; c < channels[part]; c += 4) { | 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]; | float_4 delta = in[c / 4] - out[part][c / 4]; | ||||
| // rise / fall branching | // 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 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 = 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_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; | 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); | float_4 end_of_cycle = simd::andnot(falling, delta_lt_0); | ||||
| endOfCyclePulse[part][c / 4].trigger(end_of_cycle, 1e-3); | 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]); | 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 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[RISING_A_OUTPUT + part].setVoltageSimd(out_rising, c); | ||||
| outputs[FALLING_A_OUTPUT + part].setVoltageSimd(out_falling, c); | outputs[FALLING_A_OUTPUT + part].setVoltageSimd(out_falling, c); | ||||
| outputs[EOC_A_OUTPUT + part].setVoltageSimd(out_EOC, c); | outputs[EOC_A_OUTPUT + part].setVoltageSimd(out_EOC, c); | ||||
| @@ -285,14 +280,13 @@ struct Rampage : Module { | |||||
| else if (balance > 0.5) | else if (balance > 0.5) | ||||
| a *= 2.0f * (1.0 - balance); | 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_min = simd::fmin(a, b); | ||||
| float_4 out_max = simd::fmax(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 | // Lights | ||||
| if (channels_max == 1) { | if (channels_max == 1) { | ||||
| @@ -20,22 +20,20 @@ struct SlewLimiter : Module { | |||||
| NUM_OUTPUTS | NUM_OUTPUTS | ||||
| }; | }; | ||||
| float_4 out[4]; | |||||
| float_4 out[4] = {}; | |||||
| SlewLimiter() { | SlewLimiter() { | ||||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); | ||||
| configParam(SHAPE_PARAM, 0.0, 1.0, 0.0, "Shape"); | configParam(SHAPE_PARAM, 0.0, 1.0, 0.0, "Shape"); | ||||
| configParam(RISE_PARAM, 0.0, 1.0, 0.0, "Rise time"); | configParam(RISE_PARAM, 0.0, 1.0, 0.0, "Rise time"); | ||||
| configParam(FALL_PARAM, 0.0, 1.0, 0.0, "Fall time"); | configParam(FALL_PARAM, 0.0, 1.0, 0.0, "Fall time"); | ||||
| memset(out, 0, sizeof(out)); | |||||
| } | } | ||||
| void process(const ProcessArgs& args) override { | 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 | // this is the number of active polyphony engines, defined by the input | ||||
| int numPolyphonyEngines = inputs[IN_INPUT].getChannels(); | int numPolyphonyEngines = inputs[IN_INPUT].getChannels(); | ||||
| @@ -46,9 +44,8 @@ struct SlewLimiter : Module { | |||||
| // Amount of extra slew per voltage difference | // Amount of extra slew per voltage difference | ||||
| const float shapeScale = 1 / 10.f; | 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); | outputs[OUT_OUTPUT].setChannels(numPolyphonyEngines); | ||||
| @@ -66,21 +63,22 @@ struct SlewLimiter : Module { | |||||
| fallCV[c / 4] += param_fall; | fallCV[c / 4] += param_fall; | ||||
| float_4 delta = in[c / 4] - out[c / 4]; | 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; | rateCV = ifelse(delta_lt_0, fallCV[c / 4], rateCV) * 0.1f; | ||||
| float_4 pm_one = simd::sgn(delta); | float_4 pm_one = simd::sgn(delta); | ||||
| float_4 slew = slewMax * simd::pow(float_4(slewMin / slewMax), rateCV); | 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] += 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_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] = 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); | |||||
| } | } | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -34,15 +34,18 @@ struct SpringReverb : Module { | |||||
| NUM_LIGHTS | NUM_LIGHTS | ||||
| }; | }; | ||||
| dsp::RealTimeConvolver *convolver = NULL; | |||||
| dsp::RealTimeConvolver* convolver = NULL; | |||||
| dsp::SampleRateConverter<1> inputSrc; | dsp::SampleRateConverter<1> inputSrc; | ||||
| dsp::SampleRateConverter<1> outputSrc; | dsp::SampleRateConverter<1> outputSrc; | ||||
| dsp::DoubleRingBuffer<dsp::Frame<1>, 16 * BLOCK_SIZE> inputBuffer; | dsp::DoubleRingBuffer<dsp::Frame<1>, 16 * BLOCK_SIZE> inputBuffer; | ||||
| dsp::DoubleRingBuffer<dsp::Frame<1>, 16 * BLOCK_SIZE> outputBuffer; | dsp::DoubleRingBuffer<dsp::Frame<1>, 16 * BLOCK_SIZE> outputBuffer; | ||||
| dsp::RCFilter dryFilter; | dsp::RCFilter dryFilter; | ||||
| dsp::PeakFilter vuFilter; | |||||
| dsp::PeakFilter lightFilter; | |||||
| dsp::VuMeter2 vuFilter; | |||||
| dsp::VuMeter2 lightFilter; | |||||
| dsp::ClockDivider lightRefreshClock; | |||||
| SpringReverb() { | SpringReverb() { | ||||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | ||||
| @@ -53,16 +56,21 @@ struct SpringReverb : Module { | |||||
| convolver = new dsp::RealTimeConvolver(BLOCK_SIZE); | 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); | size_t kernelLen = BINARY_SIZE(src_SpringReverbIR_pcm) / sizeof(float); | ||||
| convolver->setKernel(kernel, kernelLen); | convolver->setKernel(kernel, kernelLen); | ||||
| vuFilter.mode = dsp::VuMeter2::PEAK; | |||||
| lightFilter.mode = dsp::VuMeter2::PEAK; | |||||
| lightRefreshClock.setDivision(32); | |||||
| } | } | ||||
| ~SpringReverb() { | ~SpringReverb() { | ||||
| delete convolver; | delete convolver; | ||||
| } | } | ||||
| void process(const ProcessArgs &args) override { | |||||
| void process(const ProcessArgs& args) override { | |||||
| float in1 = inputs[IN1_INPUT].getVoltageSum(); | float in1 = inputs[IN1_INPUT].getVoltageSum(); | ||||
| float in2 = inputs[IN2_INPUT].getVoltageSum(); | float in2 = inputs[IN2_INPUT].getVoltageSum(); | ||||
| const float levelScale = 0.030; | const float levelScale = 0.030; | ||||
| @@ -92,7 +100,7 @@ struct SpringReverb : Module { | |||||
| inputSrc.setRates(args.sampleRate, 48000); | inputSrc.setRates(args.sampleRate, 48000); | ||||
| int inLen = inputBuffer.size(); | int inLen = inputBuffer.size(); | ||||
| int outLen = BLOCK_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); | inputBuffer.startIncr(inLen); | ||||
| } | } | ||||
| @@ -104,7 +112,7 @@ struct SpringReverb : Module { | |||||
| outputSrc.setRates(48000, args.sampleRate); | outputSrc.setRates(48000, args.sampleRate); | ||||
| int inLen = BLOCK_SIZE; | int inLen = BLOCK_SIZE; | ||||
| int outLen = outputBuffer.capacity(); | 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); | outputBuffer.endIncr(outLen); | ||||
| } | } | ||||
| } | } | ||||
| @@ -112,6 +120,7 @@ struct SpringReverb : Module { | |||||
| // Set output | // Set output | ||||
| if (outputBuffer.empty()) | if (outputBuffer.empty()) | ||||
| return; | return; | ||||
| float wet = outputBuffer.shift().samples[0]; | 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 balance = clamp(params[WET_PARAM].getValue() + inputs[MIX_CV_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f); | ||||
| float mix = crossfade(in1, wet, balance); | float mix = crossfade(in1, wet, balance); | ||||
| @@ -119,24 +128,28 @@ struct SpringReverb : Module { | |||||
| outputs[WET_OUTPUT].setVoltage(clamp(wet, -10.0f, 10.0f)); | outputs[WET_OUTPUT].setVoltage(clamp(wet, -10.0f, 10.0f)); | ||||
| outputs[MIX_OUTPUT].setVoltage(clamp(mix, -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 { | struct SpringReverbWidget : ModuleWidget { | ||||
| SpringReverbWidget(SpringReverb *module) { | |||||
| SpringReverbWidget(SpringReverb* module) { | |||||
| setModule(module); | setModule(module); | ||||
| setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/SpringReverb.svg"))); | 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"); | |||||