From 7eb2cfa126f1e5b33995b1fc53afeb0afdfd2cc1 Mon Sep 17 00:00:00 2001 From: bsp2 Date: Sun, 2 Sep 2018 14:23:17 +0200 Subject: [PATCH] add module bsp.AttenuMixer and TunedDelayLine; update module bsp.Scanner --- plugins/community/repos/bsp/README.md | 46 ++ plugins/community/repos/bsp/make.objects | 4 +- .../community/repos/bsp/res/AttenuMixer.svg | 429 ++++++++++++++++++ plugins/community/repos/bsp/res/Scanner.svg | 125 +++-- .../repos/bsp/res/TunedDelayLine.svg | 422 +++++++++++++++++ plugins/community/repos/bsp/res/null.svg | 14 + plugins/community/repos/bsp/res/sway.svg | 14 +- .../community/repos/bsp/src/AttenuMixer.cpp | 159 +++++++ plugins/community/repos/bsp/src/Scanner.cpp | 118 ++++- .../repos/bsp/src/TunedDelayLine.cpp | 234 ++++++++++ plugins/community/repos/bsp/src/bsp.cpp | 4 + vst2_bin/plugins/bsp/res/AttenuMixer.svg | 429 ++++++++++++++++++ vst2_bin/plugins/bsp/res/Scanner.svg | 65 ++- vst2_bin/plugins/bsp/res/TunedDelayLine.svg | 422 +++++++++++++++++ vst2_bin/plugins/bsp/res/null.svg | 14 + vst2_bin/plugins/bsp/res/sway.svg | 14 +- 16 files changed, 2438 insertions(+), 75 deletions(-) create mode 100644 plugins/community/repos/bsp/res/AttenuMixer.svg create mode 100644 plugins/community/repos/bsp/res/TunedDelayLine.svg create mode 100644 plugins/community/repos/bsp/res/null.svg create mode 100644 plugins/community/repos/bsp/src/AttenuMixer.cpp create mode 100644 plugins/community/repos/bsp/src/TunedDelayLine.cpp create mode 100644 vst2_bin/plugins/bsp/res/AttenuMixer.svg create mode 100644 vst2_bin/plugins/bsp/res/TunedDelayLine.svg create mode 100644 vst2_bin/plugins/bsp/res/null.svg diff --git a/plugins/community/repos/bsp/README.md b/plugins/community/repos/bsp/README.md index 683f637e..95bba630 100644 --- a/plugins/community/repos/bsp/README.md +++ b/plugins/community/repos/bsp/README.md @@ -1,4 +1,23 @@ +# AttenuMixer + +A compact attenuator and mixer (/merger), mainly designed for control voltages. + +The scale knobs (0..1 in unipolar mode) have a bias towards small values (subtle modulation). + +The default scaling factor of input 2 is calibrated to +-24 semitones (e.g. MPE pitchbend). + +Inputs 3 and 4 use default scaling factors of 0.5 and 0.25, respectively. + +The switch at the top is used to enabled bipolar scaling (-1..1). + + +Suggested applications: + - Mix pitch voltages, e.g. base pitch + pitchbend + vibrato. + - Mix filter cutoff voltages, e.g. ADSR + LFO + modwheel + + + # Obxd_VCF An adaption of Filatov Vadim's excellent Ob-Xd filter. Distributed under terms of the GNU General Public License V3. @@ -22,6 +41,9 @@ I left it in since it turned out to be useful for synthesizing cymbal and hihat The knob selects the window shape (same as the main shape parameter), and the switch toggles a window offset (this used to be a bug in earlier versions but it sounded nice with some sounds). +The "RND" section (right above the output port) can be used to shuffle / randomize the inputs. +The switch enables the randomizer, and the button next to it is used to generate a new random seed. + NOTE: try modulating the position with the post output (feedback). @@ -38,6 +60,30 @@ The "s+o" knobs are used to apply a final scaling/amplification (-5..5) and offs NOTE: when the min/max time is set to very small values, the module can be used to generate audio-rate noise. +# Tuned Delay Line + +This module was designed for Karplus-Strong synthesis. +If you don't know what this is: The basic idea is to feed short noise bursts into a feedback delay (to "excite the string"). + +The frequency (V/Oct) input at the top controls the delay length. The knob below can be used for finetuning (+- 1 semitone). + +The next two ports are the feedback send and return. They are usually hooked up to a filter module. +The knob controls the feedback amount (usually set to very high values to create sustained sounds). + +If the return jack is left unconnected, a simple builtin filter is used instead. + +The knob at the bottom controls the dry/wet amount (usually set to 100% wet). + +Last but not least, the last two ports are for the audio input, and the audio output. + + +NOTE: make sure to only input very short noise bursts or the output signal will become far too loud very quickly (because of the high feedback amount). One way to do that is to feed the oscillator/noise signal into an AS.KillGate module which is triggered by a pulse oscillator (~C-4). + +NOTE: getting usable sounds out of this module requires a lot of finetuning. The AttenuMixer can be very handy for this. + +NOTE: Here's a [video](https://vimeo.com/287875320) with some example sounds / patches. + + # Known Issues The graphics, especially the texts, look really bad. I currently have no idea to fix that. diff --git a/plugins/community/repos/bsp/make.objects b/plugins/community/repos/bsp/make.objects index d5600927..a45a67bc 100644 --- a/plugins/community/repos/bsp/make.objects +++ b/plugins/community/repos/bsp/make.objects @@ -1,5 +1,7 @@ ALL_OBJ= \ + src/AttenuMixer.o \ src/bsp.o \ src/Obxd_VCF.o \ src/Scanner.o \ - src/Sway.o + src/Sway.o \ + src/TunedDelayLine.o diff --git a/plugins/community/repos/bsp/res/AttenuMixer.svg b/plugins/community/repos/bsp/res/AttenuMixer.svg new file mode 100644 index 00000000..60118d97 --- /dev/null +++ b/plugins/community/repos/bsp/res/AttenuMixer.svg @@ -0,0 +1,429 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/community/repos/bsp/res/Scanner.svg b/plugins/community/repos/bsp/res/Scanner.svg index 9cf767c5..6ffbb302 100644 --- a/plugins/community/repos/bsp/res/Scanner.svg +++ b/plugins/community/repos/bsp/res/Scanner.svg @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="5.6" - inkscape:cx="122.78645" - inkscape:cy="39.0131" + inkscape:zoom="3.959798" + inkscape:cx="63.306492" + inkscape:cy="66.078732" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -148,177 +148,208 @@ id="path863" inkscape:connector-curvature="0" /> - + width="10.705551" + height="10.537507" + x="358.82373" + y="22.478483" + ry="1.2795542" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/plugins/community/repos/bsp/res/null.svg b/plugins/community/repos/bsp/res/null.svg new file mode 100644 index 00000000..30f2866f --- /dev/null +++ b/plugins/community/repos/bsp/res/null.svg @@ -0,0 +1,14 @@ + + + + + + + image/svg+xml + + + + + + + diff --git a/plugins/community/repos/bsp/res/sway.svg b/plugins/community/repos/bsp/res/sway.svg index 49f067bb..ffe1fbf8 100644 --- a/plugins/community/repos/bsp/res/sway.svg +++ b/plugins/community/repos/bsp/res/sway.svg @@ -239,9 +239,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1.4" - inkscape:cx="-105.23473" - inkscape:cy="241.47063" + inkscape:zoom="7.9195959" + inkscape:cx="-7.8270125" + inkscape:cy="39.533899" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -353,6 +353,14 @@ id="path965" inkscape:connector-curvature="0" /> + + +#include "bsp.hpp" + +namespace rack_plugin_bsp { + +typedef union fi_u { + float f; + unsigned int u; + int s; +} fi_t; + +struct AttenuMixer : Module { + enum ParamIds { + IN_1_SCL_PARAM, + IN_1_OFF_PARAM, + IN_2_SCL_PARAM, + IN_2_OFF_PARAM, + IN_3_SCL_PARAM, + IN_3_OFF_PARAM, + IN_4_SCL_PARAM, + IN_4_OFF_PARAM, + BIPOLAR_PARAM, + NUM_PARAMS + }; + enum InputIds { + IN_1_INPUT, + IN_2_INPUT, + IN_3_INPUT, + IN_4_INPUT, + NUM_INPUTS + }; + enum OutputIds { + CTL_OUTPUT, + NUM_OUTPUTS + }; + + AttenuMixer() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { + } + + void step() override; +}; + + +void AttenuMixer::step() { + + float outVal = 0.0f; + + if(params[BIPOLAR_PARAM].value >= 0.5f) + { + for(int i = 0; i < 4; i++) + { + fi_t scl; scl.f = params[IN_1_SCL_PARAM + (i<<1)].value; + scl.f = (2.0f * scl.f) - 1.0f; + uint32_t sclSign = scl.u & 0x80000000u; + scl.f *= scl.f; + scl.f *= scl.f; + scl.u |= sclSign; + outVal += inputs[IN_1_INPUT + i].value * scl.f + params[IN_1_OFF_PARAM + (i<<1)].value; + } + } + else + { + for(int i = 0; i < 4; i++) + { + float scl = params[IN_1_SCL_PARAM + (i<<1)].value; + scl *= scl; + scl *= scl; + outVal += inputs[IN_1_INPUT + i].value * scl + params[IN_1_OFF_PARAM + (i<<1)].value; + } + } + + outputs[CTL_OUTPUT].value = outVal; + +#if 0 + static int xxx = 0; + if(0 == (++xxx & 32767)) + { + printf("xxx params[IN_2_SCL_PARAM].value=%f\n", params[IN_2_SCL_PARAM].value); + } +#endif +} + + +struct AttenuMixerWidget : ModuleWidget { + AttenuMixerWidget(AttenuMixer *module); +}; + +AttenuMixerWidget::AttenuMixerWidget(AttenuMixer *module) : ModuleWidget(module) { + setPanel(SVG::load(assetPlugin(plugin, "res/AttenuMixer.svg"))); + + addChild(Widget::create(Vec(15, 0))); + addChild(Widget::create(Vec(15, 365))); + + addParam(ParamWidget::create(Vec(box.size.x - 19, 18), module, AttenuMixer::BIPOLAR_PARAM, 0.0f, 1.0f, 0.0f)); + +#define STY 42 +#define OFX 17 +#define OFY 20 + float cx = 2.0f; + float cy = 34.0f; + addParam(ParamWidget::create(Vec(cx, cy), module, AttenuMixer::IN_1_SCL_PARAM, 0.0f, 1.0f, 1.0f)); + addParam(ParamWidget::create(Vec(cx + OFX, cy + OFY), module, AttenuMixer::IN_1_OFF_PARAM, -5.0f, 5.0f, 0.0f)); + cy += STY; + addParam(ParamWidget::create(Vec(cx, cy), module, AttenuMixer::IN_2_SCL_PARAM, 0.0f, 1.0f, 0.796f)); + addParam(ParamWidget::create(Vec(cx + OFX, cy + OFY), module, AttenuMixer::IN_2_OFF_PARAM, -5.0f, 5.0f, 0.0f)); + cy += STY; + addParam(ParamWidget::create(Vec(cx, cy), module, AttenuMixer::IN_3_SCL_PARAM, 0.0f, 1.0f, 0.5f)); + addParam(ParamWidget::create(Vec(cx + OFX, cy + OFY), module, AttenuMixer::IN_3_OFF_PARAM, -5.0f, 5.0f, 0.0f)); + cy += STY; + addParam(ParamWidget::create(Vec(cx, cy), module, AttenuMixer::IN_4_SCL_PARAM, 0.0f, 1.0f, 0.25f)); + addParam(ParamWidget::create(Vec(cx + OFX, cy + OFY), module, AttenuMixer::IN_4_OFF_PARAM, -5.0f, 5.0f, 0.0f)); +#undef STX +#undef STY + +#define STY 28.0f + cx = 11.0f; + cy = 208.0f; + addInput(Port::create(Vec(cx, cy), Port::INPUT, module, AttenuMixer::IN_1_INPUT)); + cy += STY; + addInput(Port::create(Vec(cx, cy), Port::INPUT, module, AttenuMixer::IN_2_INPUT)); + cy += STY; + addInput(Port::create(Vec(cx, cy), Port::INPUT, module, AttenuMixer::IN_3_INPUT)); + cy += STY; + addInput(Port::create(Vec(cx, cy), Port::INPUT, module, AttenuMixer::IN_4_INPUT)); + + addOutput(Port::create(Vec(11, 325), Port::OUTPUT, module, AttenuMixer::CTL_OUTPUT)); +} + +} // namespace rack_plugin_bsp + +using namespace rack_plugin_bsp; + +RACK_PLUGIN_MODEL_INIT(bsp, AttenuMixer) { + Model *modelAttenuMixer = Model::create("bsp", "AttenuMixer", "AttenuMixer", ATTENUATOR_TAG, MIXER_TAG); + return modelAttenuMixer; +} diff --git a/plugins/community/repos/bsp/src/Scanner.cpp b/plugins/community/repos/bsp/src/Scanner.cpp index 53f752a5..85c31cac 100644 --- a/plugins/community/repos/bsp/src/Scanner.cpp +++ b/plugins/community/repos/bsp/src/Scanner.cpp @@ -27,8 +27,28 @@ SOFTWARE. namespace rack_plugin_bsp { +typedef union fi_u { + float f; + unsigned int u; + int s; +} fi_t; + +// struct TrigButton : CKD6 { +// struct TrigButton : TL1105 { +struct TrigButton : LEDButton { +}; + +struct NullButton : SVGSwitch, ToggleSwitch { + NullButton() { + addFrame(SVG::load(assetPlugin("res/null.svg"))); + addFrame(SVG::load(assetPlugin("res/null.svg"))); + } +}; + struct Scanner : Module { + static const uint32_t MAX_INPUTS = 16u; + enum ParamIds { POSITION_PARAM, MOD_POSITION_AMOUNT_PARAM, @@ -37,6 +57,9 @@ struct Scanner : Module { TABLE_TYPE_PARAM, OUT_WINDOW_SHAPE_PARAM, OUT_WINDOW_OFFSET_SWITCH_PARAM, + RANDOM_TRIG_PARAM, + RANDOM_ENABLE_PARAM, + RANDOM_SEED_PARAM, NUM_PARAMS }; @@ -101,9 +124,19 @@ struct Scanner : Module { // (note) the table is actually symmetric (center = LUT_SIZE/2) uint32_t out_buffer_idx; + uint32_t input_shuffle_lut[MAX_INPUTS]; + fi_t last_input_shuffle_seed; + fi_t tmp_seed; + float last_rand_enable; + uint32_t last_num_active_inputs; + + Scanner() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { - last_mix_shape = -999; - last_out_shape = -999; + last_mix_shape = -999.f; + last_out_shape = -999.f; + tmp_seed.u = 0u; + last_num_active_inputs = 0u; + last_rand_enable = -999.f; memset((void*)out_buffer, 0, sizeof(out_buffer)); out_buffer_idx = 0u; } @@ -112,6 +145,9 @@ struct Scanner : Module { void calcMixLUT (void); void calcOutLUT (void); + uint32_t fastRand (void); + void calcInputShuffleLUT (uint32_t _numActiveInputs); + void step() override; }; @@ -179,6 +215,56 @@ void Scanner::calcOutLUT(void) { out_lut[i] *= scl; } +uint32_t Scanner::fastRand(void) { + tmp_seed.u *= 16807u; + printf("xxx fastRand()=%u\n", tmp_seed.u); + return tmp_seed.u >> 10; +} + +void Scanner::calcInputShuffleLUT(uint32_t _numActiveInputs) { + + printf("xxx Scanner::calcInputShuffleLUT(numActiveInputs=%u)\n", _numActiveInputs); + + tmp_seed.f = params[RANDOM_SEED_PARAM].value; + tmp_seed.u &= 0xFFffFFu; + tmp_seed.u += (~tmp_seed.u) & 1u; + + if(params[RANDOM_ENABLE_PARAM].value >= 0.5f) + { + for(uint32_t i = 0u; i < _numActiveInputs; i++) + { + // (note) there are other "random" functions that produce non-repeating number sequences + // but this one is good enough (usually <8 iterations to generate 4 unique random values) + bool bDuplicate; + + do + { + input_shuffle_lut[i] = fastRand() % _numActiveInputs; + + bDuplicate = false; + + for(uint32_t j = 0u; j < i; j++) + { + if(input_shuffle_lut[j] == input_shuffle_lut[i]) + { + bDuplicate = true; + break; + } + } + } + while(bDuplicate); + + } + } + else + { + for(uint32_t i = 0u; i < _numActiveInputs; i++) + { + input_shuffle_lut[i] = i; + } + } + +} void Scanner::step() { @@ -206,6 +292,25 @@ void Scanner::step() { } } + if(params[RANDOM_TRIG_PARAM].value >= 0.5f) + { + // (todo) don't handle UI button in the audio thread + params[RANDOM_TRIG_PARAM].value = 0.0f; + fi_t r; r.s = rand(); + params[RANDOM_SEED_PARAM].value = r.f; + } + + if((last_num_active_inputs != numInputs) || + (last_input_shuffle_seed.f != params[RANDOM_SEED_PARAM].value) || + (last_rand_enable != params[RANDOM_ENABLE_PARAM].value) + ) + { + last_num_active_inputs = numInputs; + last_input_shuffle_seed.f = params[RANDOM_SEED_PARAM].value; + last_rand_enable = params[RANDOM_ENABLE_PARAM].value; + calcInputShuffleLUT(numInputs); + } + float mixOut = 0.0f; static int xxx = 0; @@ -260,7 +365,7 @@ void Scanner::step() { for(int i = 0; i < numInputs; i++) { - int portIdx = inputIdx[i]; + int portIdx = inputIdx[input_shuffle_lut[i]]; lights[MIX_1_LIGHT + portIdx].setBrightnessSmooth(outWeights[i]); @@ -369,7 +474,7 @@ ScannerWidget::ScannerWidget(Scanner *module) : ModuleWidget(module) { addParam(ParamWidget::create(Vec(cx, cy), module, Scanner::MOD_POSITION_AMOUNT_PARAM, -1.0f, 1.0f, 0.0f)); addInput(Port::create(Vec(cx+2.3f, cy + 37.0f), Port::INPUT, module, Scanner::MOD_POSITION_INPUT)); cx += STX; - addParam(ParamWidget::create(Vec(cx, cy), module, Scanner::SHAPE_PARAM, 0.0f, 1.0f, 0.0f)); + addParam(ParamWidget::create(Vec(cx, cy), module, Scanner::SHAPE_PARAM, 0.0f, 1.0f, 0.45f)); cx += STX; addParam(ParamWidget::create(Vec(cx, cy), module, Scanner::WIDTH_PARAM, 0.0f, 1.0f, 1.0f)); @@ -377,6 +482,11 @@ ScannerWidget::ScannerWidget(Scanner *module) : ModuleWidget(module) { addParam(ParamWidget::create(Vec(9, box.size.y-58), module, Scanner::OUT_WINDOW_OFFSET_SWITCH_PARAM, 0.0f, 1.0f, 0.0f)); + cy = 286.0f; + addParam(ParamWidget::create(Vec(box.size.x - 45, cy+2.0f), module, Scanner::RANDOM_TRIG_PARAM, 0.0f, 1.0f, 0.0f)); + addParam(ParamWidget::create(Vec(box.size.x - 25, cy), module, Scanner::RANDOM_ENABLE_PARAM, 0.0f, 1.0f, 0.0f)); + addParam(ParamWidget::create(Vec(box.size.x - 70, cy-30), module, Scanner::RANDOM_SEED_PARAM, -INFINITY, INFINITY, 0.0f)); + addOutput(Port::create(Vec(box.size.x - 40, 320), Port::OUTPUT, module, Scanner::MIX_OUTPUT)); addOutput(Port::create(Vec(box.size.x - 90, 320), Port::OUTPUT, module, Scanner::WIN_OUTPUT)); } diff --git a/plugins/community/repos/bsp/src/TunedDelayLine.cpp b/plugins/community/repos/bsp/src/TunedDelayLine.cpp new file mode 100644 index 00000000..2aeabe34 --- /dev/null +++ b/plugins/community/repos/bsp/src/TunedDelayLine.cpp @@ -0,0 +1,234 @@ +/* +Copyright (c) 2018 bsp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/// When defined, use linear interpolation when reading samples from delay line +// #define USE_FRAC defined + +#include +#include // memset + +#include "bsp.hpp" + +namespace rack_plugin_bsp { + +struct TunedDelayLine : Module { + enum ParamIds { + DRYWET_PARAM, + FB_AMT_PARAM, + FINETUNE_PARAM, // or delaytime in seconds when no V/OCT input is connected + POSTFB_PARAM, + NUM_PARAMS + }; + enum InputIds { + VOCT_INPUT, + AUDIO_INPUT, + FB_RET_INPUT, + NUM_INPUTS + }; + enum OutputIds { + FB_SEND_OUTPUT, + AUDIO_OUTPUT, + NUM_OUTPUTS + }; + +#define BUF_SIZE (256u*1024u) +#define BUF_SIZE_MASK (BUF_SIZE - 1u) + float delay_buf[BUF_SIZE]; + uint32_t delay_buf_idx; + float last_dly_val; + + float sample_rate; + + TunedDelayLine() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { + delay_buf_idx = 0u; + ::memset((void*)delay_buf, 0, sizeof(delay_buf)); + handleSampleRateChanged(); + last_dly_val = 0.0f; + } + + void handleSampleRateChanged(void) { + sample_rate = engineGetSampleRate(); + } + + void onSampleRateChange() override { + Module::onSampleRateChange(); + + handleSampleRateChanged(); + } + + void step() override; +}; + + +void TunedDelayLine::step() { + + // Calculate delay length + float dlySmpOff; + + if(inputs[VOCT_INPUT].active) + { + // (note) Freq calculation borrowed from Fundamental.VCO + float pitch = inputs[VOCT_INPUT].value + params[FINETUNE_PARAM].value * (1.0f / 12.0f); + // Note C4 + float freq = 261.626f * powf(2.0f, pitch); + + dlySmpOff = (1.0f * sample_rate) / freq; + } + else + { + // No input connected, set delay time in the range 0..1 seconds + dlySmpOff = sample_rate * (0.5f + 0.5f * params[FINETUNE_PARAM].value); + } + + // Read delayed sample from ring buffer +#ifdef USE_FRAC + uint32_t dlySmpOffI = uint32_t(dlySmpOff); + float dlySmpFrac = dlySmpOff - dlySmpOffI; + dlySmpOffI = (delay_buf_idx - dlySmpOffI) & BUF_SIZE_MASK; + float dlyVal = delay_buf[dlySmpOffI] + (delay_buf[(dlySmpOffI+1u) & BUF_SIZE_MASK] - delay_buf[dlySmpOffI]) * dlySmpFrac; +#else + uint32_t dlySmpOffI = uint32_t(delay_buf_idx - dlySmpOff) & BUF_SIZE_MASK; + float dlyVal = delay_buf[dlySmpOffI]; +#endif + + bool bPostFBOnly = (params[POSTFB_PARAM].value >= 0.5f); + + // Add input signal + float inSmp = inputs[AUDIO_INPUT].value; + + if(bPostFBOnly) + { + dlyVal += inSmp; + } + + // Send it to external module(s) + outputs[FB_SEND_OUTPUT].value = dlyVal; + + float fbVal; + + // Read back processed feedback value + if(inputs[FB_RET_INPUT].active) + { + // Use externally processed feedback sample + // (note) this is actually shifted / delayed by one sample + fbVal = inputs[FB_RET_INPUT].value; + } + else + { + // Fallback: feedback send+return not connected, use builtin filter instead + fbVal = (last_dly_val + dlyVal) * 0.5f; + last_dly_val = dlyVal; + } + + // Apply feedback amount + float fbAmt = params[FB_AMT_PARAM].value; + fbAmt = 1.0f - fbAmt; + fbAmt *= fbAmt; + fbAmt *= fbAmt; + fbAmt = 1.0f - fbAmt; + fbVal *= fbAmt; + + if(!bPostFBOnly) + { + // Add input signal + fbVal += inSmp; + } + + // Write new delay sample to ring buffer + delay_buf[delay_buf_idx] = fbVal; + delay_buf_idx = (delay_buf_idx + 1u) & BUF_SIZE_MASK; + + // Final output + float outVal; + if(bPostFBOnly) + { + outVal = inSmp + (fbVal - inSmp) * params[DRYWET_PARAM].value; + } + else + { + outVal = inSmp + (dlyVal - inSmp) * params[DRYWET_PARAM].value; + } + outputs[AUDIO_OUTPUT].value = outVal; + +#if 0 + static int xxx = 0; + if(0 == (++xxx & 32767)) + { + printf("xxx V/OCT=%f freq=%f inSmp=%f dlySmpOff=%f dlyVal=%f fbVal=%f outVal=%f fbAmt=%f\n", inputs[VOCT_INPUT].value, freq, inSmp, dlySmpOff, dlyVal, fbVal, outVal, fbAmt); + } +#endif + +} + + +struct TunedDelayLineWidget : ModuleWidget { + TunedDelayLineWidget(TunedDelayLine *module); +}; + +TunedDelayLineWidget::TunedDelayLineWidget(TunedDelayLine *module) : ModuleWidget(module) { + setPanel(SVG::load(assetPlugin(plugin, "res/TunedDelayLine.svg"))); + + addChild(Widget::create(Vec(15, 0))); + addChild(Widget::create(Vec(15, 365))); + + float cx; + float cy; + + cx = 9.0f; + cy = 37.0f; + addInput(Port::create(Vec(cx+2.0f, cy), Port::INPUT, module, TunedDelayLine::VOCT_INPUT)); + addParam(ParamWidget::create(Vec(cx, cy + 32), module, TunedDelayLine::FINETUNE_PARAM, -1.0f, 1.0f, 0.0f)); + +#define STY 32.0f + cx = 11.0f; + cy = 120.0f; + addOutput(Port::create(Vec(cx, cy), Port::OUTPUT, module, TunedDelayLine::FB_SEND_OUTPUT)); + cy += STY; + addInput(Port::create(Vec(cx, cy), Port::INPUT, module, TunedDelayLine::FB_RET_INPUT)); + cy += STY; + addParam(ParamWidget::create(Vec(cx-2.0f, cy), module, TunedDelayLine::FB_AMT_PARAM, 0.0f, 1.0f, 0.3f)); +#undef STY + + cx = 16.0f; + cy = 218.0f; + addParam(ParamWidget::create(Vec(cx, cy), module, TunedDelayLine::POSTFB_PARAM, 0.0f, 1.0f, 1.0f)); + + cx = 9.0f; + cy = 245.0f; + addParam(ParamWidget::create(Vec(cx, cy), module, TunedDelayLine::DRYWET_PARAM, 0.0f, 1.0f, 1.0f)); + +#define STY 40.0f + cx = 11.0f; + cy = 325.0f; + addInput(Port::create(Vec(cx, cy - STY), Port::INPUT, module, TunedDelayLine::AUDIO_INPUT)); + addOutput(Port::create(Vec(cx, 325), Port::OUTPUT, module, TunedDelayLine::AUDIO_OUTPUT)); +#undef STY +} + +} // namespace rack_plugin_bsp + +using namespace rack_plugin_bsp; + +RACK_PLUGIN_MODEL_INIT(bsp, TunedDelayLine) { + Model *modelTunedDelayLine = Model::create("bsp", "TunedDelayLine", "Tuned Delay Line", ATTENUATOR_TAG, MIXER_TAG); + return modelTunedDelayLine; +} diff --git a/plugins/community/repos/bsp/src/bsp.cpp b/plugins/community/repos/bsp/src/bsp.cpp index 22ad8f37..72fd2de8 100644 --- a/plugins/community/repos/bsp/src/bsp.cpp +++ b/plugins/community/repos/bsp/src/bsp.cpp @@ -1,15 +1,19 @@ #include "bsp.hpp" +RACK_PLUGIN_MODEL_DECLARE(bsp, AttenuMixer); RACK_PLUGIN_MODEL_DECLARE(bsp, Obxd_VCF); RACK_PLUGIN_MODEL_DECLARE(bsp, Scanner); RACK_PLUGIN_MODEL_DECLARE(bsp, Sway); +RACK_PLUGIN_MODEL_DECLARE(bsp, TunedDelayLine); RACK_PLUGIN_INIT(bsp) { RACK_PLUGIN_INIT_ID(); RACK_PLUGIN_INIT_WEBSITE("https://github.com/bsp2/VeeSeeVSTRack/tree/v0.6/plugins/community/repos/bsp"); + RACK_PLUGIN_MODEL_ADD(bsp, AttenuMixer); RACK_PLUGIN_MODEL_ADD(bsp, Obxd_VCF); RACK_PLUGIN_MODEL_ADD(bsp, Scanner); RACK_PLUGIN_MODEL_ADD(bsp, Sway); + RACK_PLUGIN_MODEL_ADD(bsp, TunedDelayLine); } diff --git a/vst2_bin/plugins/bsp/res/AttenuMixer.svg b/vst2_bin/plugins/bsp/res/AttenuMixer.svg new file mode 100644 index 00000000..60118d97 --- /dev/null +++ b/vst2_bin/plugins/bsp/res/AttenuMixer.svg @@ -0,0 +1,429 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/vst2_bin/plugins/bsp/res/Scanner.svg b/vst2_bin/plugins/bsp/res/Scanner.svg index af9696db..6ffbb302 100644 --- a/vst2_bin/plugins/bsp/res/Scanner.svg +++ b/vst2_bin/plugins/bsp/res/Scanner.svg @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1.4" - inkscape:cx="108.20257" - inkscape:cy="-65.661397" + inkscape:zoom="3.959798" + inkscape:cx="63.306492" + inkscape:cy="66.078732" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -148,14 +148,6 @@ id="path863" inkscape:connector-curvature="0" /> - + width="10.705551" + height="10.537507" + x="358.82373" + y="22.478483" + ry="1.2795542" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/vst2_bin/plugins/bsp/res/null.svg b/vst2_bin/plugins/bsp/res/null.svg new file mode 100644 index 00000000..30f2866f --- /dev/null +++ b/vst2_bin/plugins/bsp/res/null.svg @@ -0,0 +1,14 @@ + + + + + + + image/svg+xml + + + + + + + diff --git a/vst2_bin/plugins/bsp/res/sway.svg b/vst2_bin/plugins/bsp/res/sway.svg index 49f067bb..ffe1fbf8 100644 --- a/vst2_bin/plugins/bsp/res/sway.svg +++ b/vst2_bin/plugins/bsp/res/sway.svg @@ -239,9 +239,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1.4" - inkscape:cx="-105.23473" - inkscape:cy="241.47063" + inkscape:zoom="7.9195959" + inkscape:cx="-7.8270125" + inkscape:cy="39.533899" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -353,6 +353,14 @@ id="path965" inkscape:connector-curvature="0" /> +