├── doc └── img │ ├── ADSR.png │ ├── Fade.png │ ├── MLFO.png │ ├── Morph.png │ ├── Mult.png │ ├── Noise.png │ ├── OSCiX.png │ ├── Phaser.png │ ├── Ring.png │ ├── Rogue.png │ ├── XSEQ.png │ ├── PAN-VCA.png │ ├── XSEQ_L1.png │ ├── XSEQ_L2.png │ ├── Bitcrusher.png │ ├── BlankPanel.png │ ├── CrazyMult.png │ ├── DualDelay.png │ ├── FullCluster.png │ ├── TreasureVCO.png │ ├── WaveShaper.png │ ├── Wavefolder.png │ ├── RandomSource.png │ ├── SimpleSlider.png │ └── TreasureWaves.png ├── res ├── Fonts │ ├── Sudo.ttf │ ├── Crysta.ttf │ ├── DejaVuSansMono.ttf │ ├── OFL.txt │ └── DejaVu-LICENSE.txt ├── Panels │ ├── MSVCO-Omri-Back.png │ └── MSVCO-Espen-Back.png ├── Slider │ ├── SlidePot.svg │ └── SlidePotHandle.svg ├── Lights │ ├── BlueLight_0.svg │ └── BlueLight_1.svg ├── Button │ ├── ButtonGreen0.svg │ ├── ButtonGreen1.svg │ ├── ColorButton5.svg │ ├── ColorButton3.svg │ ├── ColorButton4.svg │ ├── ColorButton0.svg │ ├── ColorButton1.svg │ ├── ColorButton2.svg │ ├── GreyRoundToggle_0.svg │ ├── GreyRoundToggle_1.svg │ ├── Easteregg_0.svg │ └── Easteregg_1.svg ├── Screws │ ├── MScrewD.svg │ ├── MScrewA.svg │ ├── MScrewB.svg │ └── MScrewC.svg ├── Switch │ ├── VioMSwitch_0.svg │ ├── VioMSwitchVert_0.svg │ ├── VioMSwitchVert_1.svg │ └── VioMSwitch_1.svg └── Port │ ├── SilverSixPort.svg │ ├── SilverSixPortA.svg │ ├── SilverSixPortB.svg │ ├── SilverSixPortC.svg │ ├── SilverSixPortD.svg │ └── SilverSixPortE.svg ├── .gitignore ├── src ├── Additional │ ├── Wave.hpp │ ├── Resources.hpp │ ├── VRand.hpp │ ├── LowFrequencyOscillator.hpp │ ├── Wavefolder.hpp │ ├── WaveShaper.hpp │ ├── DSP.hpp │ ├── Linear_Interpolator.hpp │ ├── samplerate.h │ ├── EnvelopeGenerator.hpp │ └── ExperimentalVCO.hpp ├── MSM.cpp ├── MSM.hpp ├── BlankPanel.cpp ├── slider.cpp ├── Bitcrusher.cpp ├── RingMod.cpp └── Fade.cpp ├── Makefile ├── LICENSE.txt ├── CHANGELOG.txt └── plugin.json /doc/img/ADSR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/ADSR.png -------------------------------------------------------------------------------- /doc/img/Fade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/Fade.png -------------------------------------------------------------------------------- /doc/img/MLFO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/MLFO.png -------------------------------------------------------------------------------- /doc/img/Morph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/Morph.png -------------------------------------------------------------------------------- /doc/img/Mult.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/Mult.png -------------------------------------------------------------------------------- /doc/img/Noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/Noise.png -------------------------------------------------------------------------------- /doc/img/OSCiX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/OSCiX.png -------------------------------------------------------------------------------- /doc/img/Phaser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/Phaser.png -------------------------------------------------------------------------------- /doc/img/Ring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/Ring.png -------------------------------------------------------------------------------- /doc/img/Rogue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/Rogue.png -------------------------------------------------------------------------------- /doc/img/XSEQ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/XSEQ.png -------------------------------------------------------------------------------- /res/Fonts/Sudo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/res/Fonts/Sudo.ttf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /dist 3 | /plugin.dylib 4 | /plugin.dll 5 | /plugin.so 6 | .DS_Store 7 | .vscode/ -------------------------------------------------------------------------------- /doc/img/PAN-VCA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/PAN-VCA.png -------------------------------------------------------------------------------- /doc/img/XSEQ_L1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/XSEQ_L1.png -------------------------------------------------------------------------------- /doc/img/XSEQ_L2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/XSEQ_L2.png -------------------------------------------------------------------------------- /res/Fonts/Crysta.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/res/Fonts/Crysta.ttf -------------------------------------------------------------------------------- /doc/img/Bitcrusher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/Bitcrusher.png -------------------------------------------------------------------------------- /doc/img/BlankPanel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/BlankPanel.png -------------------------------------------------------------------------------- /doc/img/CrazyMult.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/CrazyMult.png -------------------------------------------------------------------------------- /doc/img/DualDelay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/DualDelay.png -------------------------------------------------------------------------------- /doc/img/FullCluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/FullCluster.png -------------------------------------------------------------------------------- /doc/img/TreasureVCO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/TreasureVCO.png -------------------------------------------------------------------------------- /doc/img/WaveShaper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/WaveShaper.png -------------------------------------------------------------------------------- /doc/img/Wavefolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/Wavefolder.png -------------------------------------------------------------------------------- /doc/img/RandomSource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/RandomSource.png -------------------------------------------------------------------------------- /doc/img/SimpleSlider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/SimpleSlider.png -------------------------------------------------------------------------------- /doc/img/TreasureWaves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/doc/img/TreasureWaves.png -------------------------------------------------------------------------------- /res/Fonts/DejaVuSansMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/res/Fonts/DejaVuSansMono.ttf -------------------------------------------------------------------------------- /res/Panels/MSVCO-Omri-Back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/res/Panels/MSVCO-Omri-Back.png -------------------------------------------------------------------------------- /res/Panels/MSVCO-Espen-Back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netboy3/MSM-vcvrack-plugin/HEAD/res/Panels/MSVCO-Espen-Back.png -------------------------------------------------------------------------------- /src/Additional/Wave.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | // Waveforms 4 | // 5 | // 6 | 7 | #pragma once 8 | 9 | static float Sine(float pha) { 10 | static float value; 11 | 12 | value = fastSin(TWO_PI * pha); 13 | 14 | return value; 15 | } 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RACK_DIR ?= ../.. 2 | 3 | # FLAGS will be passed to both the C and C++ compiler 4 | FLAGS += 5 | CFLAGS += 6 | CXXFLAGS += 7 | 8 | 9 | # Careful about linking to libraries, since you can't assume much about the user's environment and library search path. 10 | # Static libraries are fine. 11 | LDFLAGS += 12 | 13 | # Add .cpp and .c files to the build 14 | SOURCES += $(wildcard src/*.cpp) 15 | 16 | # Add files to the ZIP package when running `make dist` 17 | DISTRIBUTABLES += $(wildcard Manual* License*) res 18 | 19 | include $(RACK_DIR)/plugin.mk 20 | 21 | -------------------------------------------------------------------------------- /src/Additional/Resources.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 2 PI 4 | #define TWO_PI (6.28318530717958647692528676655900576) 5 | 6 | // Round 7 | #define ROUND(f) ((float) ((f > 0.0) ? floor(f+0.5) : ceil(f-0.5))) 8 | 9 | // Buffer sizes 10 | #define HISTORY_SIZE_A (1<<21) 11 | #define HISTORY_SIZE_B (1<<21) 12 | 13 | // Divisions 14 | #define DIVISIONS 25 15 | 16 | // SampleRate 17 | #define SR (APP->engine->getSampleRate()) 18 | 19 | // Saturate X 20 | #define _limitX 15.0f 21 | 22 | // Saturate 23 | #define _limit 10.0f 24 | 25 | // Saturate2 26 | #define _limit2 5.0f 27 | 28 | // Threshold for Softclip 29 | #define softClipThreshold (2.0f / 3.0f) 30 | -------------------------------------------------------------------------------- /src/MSM.cpp: -------------------------------------------------------------------------------- 1 | #include "MSM.hpp" 2 | 3 | Plugin *pluginInstance; 4 | 5 | 6 | void init(rack::Plugin *p) { 7 | pluginInstance = p; 8 | 9 | p->addModel(modelVCO); 10 | p->addModel(modelBVCO); 11 | p->addModel(modelExperimentalVCO); 12 | p->addModel(modelNoise); 13 | 14 | p->addModel(modelLFO); 15 | p->addModel(modelVCA); 16 | p->addModel(modelADSR); 17 | p->addModel(modelDelay); 18 | p->addModel(modelWaveShaper); 19 | p->addModel(modelWavefolder); 20 | p->addModel(modelBitcrusher); 21 | p->addModel(modelPhaserModule); 22 | p->addModel(modelMorpher); 23 | p->addModel(modelRingMod); 24 | p->addModel(modelRandomSource); 25 | p->addModel(modelMult); 26 | p->addModel(modelCrazyMult); 27 | p->addModel(modelFade); 28 | p->addModel(modelSimpleSlider); 29 | p->addModel(modelxseq); 30 | p->addModel(modelBlankPanel); 31 | 32 | }; 33 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Struggl Michael 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/MSM.hpp: -------------------------------------------------------------------------------- 1 | // INCLUDES 2 | #include "rack.hpp" 3 | #include "asset.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "Additional/DSP.hpp" 19 | #include "Additional/Linear_Interpolator.hpp" 20 | #include "Additional/samplerate.h" 21 | 22 | using namespace rack; 23 | 24 | extern Plugin *pluginInstance; 25 | 26 | extern Model *modelVCO; 27 | extern Model *modelBVCO; 28 | extern Model *modelExperimentalVCO; 29 | extern Model *modelNoise; 30 | 31 | extern Model *modelLFO; 32 | extern Model *modelVCA; 33 | extern Model *modelADSR; 34 | extern Model *modelDelay; 35 | extern Model *modelWaveShaper; 36 | extern Model *modelWavefolder; 37 | extern Model *modelBitcrusher; 38 | extern Model *modelPhaserModule; 39 | extern Model *modelMorpher; 40 | extern Model *modelRingMod; 41 | extern Model *modelRandomSource; 42 | extern Model *modelMult; 43 | extern Model *modelCrazyMult; 44 | extern Model *modelFade; 45 | extern Model *modelSimpleSlider; 46 | extern Model *modelxseq; 47 | extern Model *modelBlankPanel; 48 | -------------------------------------------------------------------------------- /src/Additional/VRand.hpp: -------------------------------------------------------------------------------- 1 | // Author: Andrew Simper of Vellocet 2 | // andy@vellocet.com 3 | // 4 | // Purpose: c++ class to generate white, pink and brown noise 5 | // 6 | // Sources: 7 | // This is a c++ implementation of put together from the 8 | // code provided by the following people mainly from 9 | // the music-dsp mailing list: Allan Herriman, James McCartney, 10 | // Phil Burk and Paul Kellet and the web page by Robin Whittle: 11 | // http://www.firstpr.com.au/dsp/pink-noise/ 12 | // 13 | 14 | class VRand 15 | { 16 | public: 17 | VRand() 18 | { 19 | //m_white = 0; 20 | m_brown = 0.0f; 21 | //m_seed = 0.0f; 22 | }; 23 | /* 24 | void seed(unsigned int seed=0) { 25 | std::uniform_int_distribution randomNumber(-1, 1); 26 | seed = randomNumber(randomGenerator); 27 | m_seed = seed; 28 | }; 29 | 30 | // returns psuedo random white noise number 31 | // in the range -scale to scale 32 | // 33 | */ 34 | inline float white() { 35 | //m_seed = (m_seed * 196314165) + 907633515; 36 | //m_white = m_seed >> 9; 37 | //m_white |= 0x40000000; 38 | return 2.0f * random::normal(); 39 | }; 40 | 41 | // returns brown noise random number in the range -0.5 to 0.5 42 | // 43 | inline float brown(void) { 44 | while (true) { 45 | float r = white(); 46 | m_brown += r; 47 | if (m_brown<-8.0f || m_brown>8.0f) m_brown -= r; 48 | else break; 49 | } 50 | return m_brown*0.0625f; 51 | } 52 | 53 | private: 54 | //unsigned int m_seed; 55 | //unsigned int m_white; 56 | //unsigned int randomNumber; 57 | 58 | //std::random_device randomGenerator; 59 | float m_brown; 60 | }; 61 | -------------------------------------------------------------------------------- /src/Additional/LowFrequencyOscillator.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | based on Fundamental LFO by Andrew Belt 3 | */ 4 | 5 | class LowFrequencyOscillator { 6 | public: 7 | LowFrequencyOscillator() 8 | { 9 | 10 | } 11 | 12 | void Settings(bool offset, bool invert) { 13 | _offset = offset; 14 | _invert = invert; 15 | } 16 | 17 | void setPitch(float pitch) { 18 | pitch = fminf(pitch, 10.0f); 19 | freq = powf(2.0f, pitch); 20 | } 21 | void setPulseWidth(float pw_) { 22 | const float pwMin = 0.01f; 23 | pw = clamp(pw_, pwMin, 1.0f - pwMin); 24 | } 25 | void setReset(float reset) { 26 | if (resetTrigger.process(reset / 0.01f)) { 27 | phase = 0.0f; 28 | } 29 | } 30 | void step(float dt) { 31 | float deltaPhase = fminf(freq * dt, 0.5f); 32 | phase += deltaPhase; 33 | if (phase >= 1.0f) 34 | phase -= 1.0f; 35 | } 36 | inline float sin() { 37 | if (_offset) 38 | return 1.0f - std::cos(2.0f*M_PI * phase) * (_invert ? -1.0f : 1.0f); 39 | else 40 | return std::sin(2.0f*M_PI * phase) * (_invert ? -1.0f : 1.0f); 41 | } 42 | inline float tri(float x) { 43 | return 4.0f * fabsf(x - std::roundf(x)); 44 | } 45 | inline float tri() { 46 | if (_offset) 47 | return tri(_invert ? phase - 0.5f : phase); 48 | else 49 | return -1.0f + tri(_invert ? phase - 0.25f : phase - 0.75f); 50 | } 51 | inline float saw(float x) { 52 | return 2.0f * (x - std::roundf(x)); 53 | } 54 | inline float saw() { 55 | if (_offset) 56 | return _invert ? 2.0f * (1.0f - phase) : 2.0f * phase; 57 | else 58 | return saw(phase) * (_invert ? -1.0f : 1.0f); 59 | } 60 | inline float sqr() { 61 | float sqr = ((phase < pw) ^ _invert) ? 1.0f : -1.0f; 62 | return _offset ? sqr + 1.0f : sqr; 63 | } 64 | 65 | private: 66 | float phase = 0.0f; 67 | float pw = 0.5f; 68 | float freq = 1.0f; 69 | bool _offset = false; 70 | bool _invert = false; 71 | dsp::SchmittTrigger resetTrigger; 72 | }; 73 | -------------------------------------------------------------------------------- /res/Slider/SlidePot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/Additional/Wavefolder.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Wavefold { 4 | public: 5 | Wavefold() 6 | { 7 | 8 | } 9 | 10 | void Shape(double in, double sc, double sccv, double sym, double range, double up, double down, bool folderactive) { 11 | _sc = sc + sccv; 12 | _sym = sym; 13 | _range = range; 14 | _up = up; 15 | _down = down; 16 | _in = in; 17 | _folderactive = folderactive; 18 | }; 19 | 20 | void processA() { 21 | if(_folderactive) { 22 | double s = 1.0; 23 | double j = -1.0; 24 | double combine = 0, sig1 = 0, sig2 = 0, X = 0, Y = 0, Z = 0, W = 0, A1 = 0, A2 = 0, result = 0; 25 | 26 | combine = (_in + _sym) / (1.0f - _sc + 1.0f); 27 | 28 | sig1 = (-(combine - 2.0f) * _range); 29 | X = std::isgreater(combine, s); 30 | Y = sig1 * X; 31 | //X = combine > 1.0f; 32 | //Y = sig1 * X; 33 | A1 = fastSin(Y * _up) * 2.0f; 34 | 35 | sig2 = (-(combine + 2.0f) * _range); 36 | Z = std::isless(combine, j); 37 | W = sig2 * Z; 38 | //Z = combine < -1.0f; 39 | //W = sig2 * Z; 40 | A2 = fastSin(W * _down) * 2.0f; 41 | 42 | result = combine + A1 + A2; 43 | 44 | folderBuffer = (2.0f * fastSin(tanh_noclip(fastSin(result * M_PI / 2.0f)) / M_PI)) * 10.0f; 45 | } 46 | }; 47 | 48 | void processB() { 49 | if(_folderactive) { 50 | double s = 1.0; 51 | double j = -1.0; 52 | double comb = 0, siga = 0, sigb = 0, d = 0, e = 0, f = 0, g = 0, resultb = 0, upS = 0, downS = 0; 53 | 54 | comb = (_in + _sym) / (1.0f - _sc + 1.0f); 55 | 56 | siga = (-(comb - 4.0f) * _range); 57 | d = std::isgreater(comb, s); 58 | e = siga * d; 59 | //d = comb > 1.0f; 60 | //e = siga * d; 61 | downS = (e * _down) * 1.5f; 62 | 63 | sigb = (-(comb + 4.0) * _range); 64 | f = std::isless(comb, j); 65 | g = sigb * f; 66 | //f = comb < -1.0f; 67 | //g = sigb * f; 68 | upS = (g * _up) * 1.5f; 69 | 70 | resultb = comb + downS + upS; 71 | 72 | folderBuffer = (4.0f * fastSin(atan(fastSin(resultb*M_PI/4.0f))/M_PI)) * 5.0f; 73 | } 74 | }; 75 | 76 | inline double Output() { 77 | return folderBuffer; 78 | }; 79 | 80 | private: 81 | 82 | double folderBuffer = 0.0f; 83 | 84 | double _sc = 0.0f; 85 | double _sym = 0.0f; 86 | double _range = 0.0f; 87 | double _up = 0.0f; 88 | double _down = 0.0f; 89 | double _in = 0.0f; 90 | 91 | bool _folderactive = false; 92 | }; -------------------------------------------------------------------------------- /res/Lights/BlueLight_0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 65 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /res/Lights/BlueLight_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 65 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /res/Button/ButtonGreen0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 64 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /res/Button/ButtonGreen1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 64 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | 3 | v.1.0.0 (Nov 12th 2019) 4 | 5 | VCV Plugin Library Release 1.0.0 6 | 7 | v.1.0.0RC1 (Nov 11th 2019) 8 | 9 | Open source under MIT license. 10 | Porting to Rack V1 11 | Code fixes and some cleanup 12 | Add tool-tips and calibrated values to many knobs and buttons 13 | Dual Delay: Add bypass feature to filters 14 | 15 | v.0.6.51 (23. January 2019) 16 | 17 | ADSR: cv input for sustain fixed (Rack shouldn't crash anymore) 18 | 19 | DUAL DELAY: fixed the wet signal (again), 20 | added the option to clear the buffers of delay A & B via buttons or cv inputs, 21 | rearranged the right side of the module to make it look better 22 | 23 | v.0.6.5 (16. January 2019) 24 | 25 | XSEQ: fixed/updated module layout and internally routings 26 | ADSR: added cv input for the decay shape + an option to choose between 12db or 24db slope via right click menu 27 | CRAZYMULT: fixed/updated module layout and all internal routings. 28 | WAVEFOLDER: fixed the mix control + cv input 29 | DUAL DELAY: now the internal filters also work on the wet signal outputs 30 | 31 | v.0.6.4 (10. November 2018) 32 | 33 | added Treasure VCO 34 | 35 | fixed the sync mode of the Dual Delay module and added dotted and triplet delay times. 36 | added shape knobs for attack, decay and release to the ADSR. 37 | 38 | v.0.6.3 (04. July 2018) 39 | 40 | added Noise 41 | added Phaser 42 | added Bitcrusher 43 | added Morph 44 | added Ring 45 | 46 | fixed ADSR 47 | 48 | v.0.6.2 (13. June 2018) 49 | 50 | added Wavefolder 51 | optimized OSCiX 52 | 53 | v.0.6.1 (01. June 2018) 54 | 55 | added Dual-Delay - A syncable dual delay module which can work in mono, stereo and dual mode 56 | 57 | upgraded the Random Source module with a cv input for the sample input which now allows to modulate the gain of the incoming sample signal. 58 | upgraded every module with a night-mode skin 59 | 60 | 61 | v.0.6.0 (18. March 2018) 62 | 63 | added PAN-VCA - A multifunctional VCA 64 | added TVCF - A filter based on the TwinPeak filter by Rob Hordijk 65 | added Rogue - Voltage Controlled Oscillator with multiple wavform outputs. Sine, Triangle, Sawtooth, Square, Half Wave Rectified Sine, Full Wave Rectified Sine 66 | 67 | upgraded OSCiX with a different wavefolder as well as FM Index knob and cv input and some other internal changes 68 | upgraded the WaveShaper module with additional inputs for each stage 69 | upgraded the Random Source module with a slewed Output which can be modified throu the "Slew" knob 70 | 71 | redesigned switches for all modules 72 | 73 | 74 | v0.5.6 (21. January 2018) 75 | 76 | added XSEQ - A rythmic pattern/Gate generator based on boolean logic 77 | added SIMPLE SLIDER - A VCA /Crossfader 78 | added OSCiX - VCO with build in Wavefolder (Stage 1 of the Waveshaper module) 79 | added FREQDIV - Frequency Divider (adding sub-octaves to a signal) 80 | 81 | added S/H to the MLFO 82 | redesigned MLFO 83 | 84 | deleted RANDOM SOURCE v2 85 | redesigned RANDOM SOURCE (formally v1) 86 | 87 | redesigned MULT 88 | -------------------------------------------------------------------------------- /src/Additional/WaveShaper.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | template 4 | class WaveShape { 5 | public: 6 | WaveShape() 7 | { 8 | 9 | } 10 | 11 | void Shape1(double in, double sc, double sccv, bool folderactive) { 12 | _sc1 = sc + sccv; 13 | _in1 = in; 14 | _folderactive1 = folderactive; 15 | }; 16 | 17 | void Shape2(double in, double sc, double sccv, bool folderactive) { 18 | _sc2 = sc + sccv; 19 | _in2 = in; 20 | _folderactive2 = folderactive; 21 | }; 22 | 23 | void Shape3(double in, double sc, double sccv, bool folderactive) { 24 | _sc3 = sc + sccv; 25 | _in3 = in; 26 | _folderactive3 = folderactive; 27 | }; 28 | 29 | void process() { 30 | if(_folderactive1) { 31 | double s = 1.0; 32 | double j = -1.0; 33 | double k = 0, combine = 0, sig1 = 0, sig2 = 0, X = 0, Y = 0, Z = 0, W = 0, result1 = 0; 34 | 35 | k = _sc1 * (_sc1 - 4.0f); 36 | 37 | combine = (_in1) / (1.0f - k + 1.0f); 38 | 39 | sig1 = -(combine - 1.0f) * 2.0f; 40 | X = std::isgreater(combine, s); 41 | Y = sig1 * X; 42 | 43 | sig2 = -(combine + 1.0f) * 2.0f; 44 | Z = std::isless(combine, j); 45 | W = sig2 * Z; 46 | 47 | result1 = combine + Y + W; 48 | 49 | folderBuffer1 = (2.0f * fastSin(tanh_noclip(fastSin(result1 * M_PI / 2.0f)) / M_PI)) * 10.0f; 50 | } 51 | 52 | if(_folderactive2) { 53 | double comba = 0, comb1 = 0, resultb = 0; 54 | 55 | comba = (_in2) / (1.0f - _sc2 + 1.0f); 56 | comb1 = _in2 + 1.0f; 57 | 58 | resultb = comba * (_sc2 * comb1 + 2.0f); 59 | 60 | folderBuffer2 = (2.0f * fastSin(fastSin(fastSin(resultb * M_PI / 4.0f)) / M_PI)) * 10.0f; 61 | } 62 | 63 | if(_folderactive3) { 64 | double t = 1.0; 65 | double o = -1.0; 66 | double combB = 0, siga = 0, sigb = 0, d = 0, e = 0, f = 0, g = 0, downS = 0, upS = 0, resultc = 0; 67 | 68 | combB = (_in3) / (1.0f - _sc3 + 1.0f); 69 | 70 | siga = (-(combB - 1.0f) * 2.0f); 71 | d = std::isgreater(combB, t); 72 | e = siga * d; 73 | downS = fastSin(e * 1.0f) * 1.0f; 74 | 75 | sigb = (-(combB + 1.0) * 2.0f); 76 | f = std::isless(combB, o); 77 | g = sigb * f; 78 | upS = fastSin(g * 1.0f) * 1.0f; 79 | 80 | resultc = (combB + downS + upS) * 1.0f; 81 | 82 | folderBuffer3 = (2.0f * fastSin(tanh_noclip(fastSin(resultc*M_PI/2.0f))/M_PI)) * 10.0f; 83 | } 84 | }; 85 | 86 | inline double MasterOutput() { 87 | return folderBuffer1 + folderBuffer2 + folderBuffer3; 88 | }; 89 | 90 | private: 91 | 92 | double folderBuffer1 = 0.0f; 93 | double folderBuffer2 = 0.0f; 94 | double folderBuffer3 = 0.0f; 95 | 96 | double _sc1 = 0.0f, _sc2 = 0.0f, _sc3 = 0.0f; 97 | double _in1 = 0.0f, _in2 = 0.0f, _in3 = 0.0f; 98 | 99 | bool _folderactive1 = false, _folderactive2 = false, _folderactive3 = false; 100 | }; -------------------------------------------------------------------------------- /res/Screws/MScrewD.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 66 | 71 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /res/Screws/MScrewA.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 66 | 71 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /res/Screws/MScrewB.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 66 | 71 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /res/Screws/MScrewC.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 66 | 71 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/Additional/DSP.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Resources.hpp" 3 | 4 | //---------------------------------------------------------------------------- 5 | 6 | inline double fastSin(float x) { 7 | if(M_PI < x) { 8 | x = x-static_cast((x+M_PI)/(TWO_PI))*TWO_PI; 9 | } 10 | else if(x < -M_PI) { 11 | x = x-static_cast((x-M_PI)/(TWO_PI))*TWO_PI; 12 | } 13 | 14 | return x*(1 - x*x*(0.16666667f - x*x*(0.00833333f - x*x*(0.0001984f - x*x*0.0000027f)))); 15 | } 16 | 17 | inline float fastSin2(float x) { 18 | if(M_PI < x) { 19 | x = x-static_cast((x+M_PI)/(TWO_PI))*TWO_PI; 20 | } 21 | else if(x < -M_PI) { 22 | x = x-static_cast((x-M_PI)/(TWO_PI))*TWO_PI; 23 | } 24 | 25 | return x*(1 - x*x*(0.16666667f - x*x*(0.00833333f - x*x*(0.0001984f - x*x*0.0000027f)))); 26 | } 27 | 28 | inline float tanh_noclip(float x) { // no clip 29 | return x * ( 27 + x * x ) / ( 27 + 9 * x * x ); 30 | } 31 | 32 | // different clipping algos 33 | static inline float saturateX( float input ) { 34 | float x1 = fabsf( input + _limitX ); 35 | float x2 = fabsf( input - _limitX ); 36 | return 0.5f * (x1 - x2); 37 | } 38 | 39 | static inline float saturate( float input ) { 40 | float x1 = fabsf( input + _limit ); 41 | float x2 = fabsf( input - _limit ); 42 | return 0.5f * (x1 - x2); 43 | } 44 | 45 | static inline float saturate2( float input ) { 46 | float x1 = fabsf( input + _limit2 ); 47 | float x2 = fabsf( input - _limit2 ); 48 | return 0.5f * (x1 - x2); 49 | } 50 | 51 | inline float fastatan( float x ) 52 | { 53 | return (x / (0.05 + 0.2 * (x * x))); 54 | } 55 | 56 | inline float overdrive(float inSample, float dr) { 57 | const float k = 2.f * dr / (1.f - dr); 58 | return (1.f + k) * inSample / (1.f + k * fabs(inSample)); 59 | } 60 | 61 | static inline float softClip(float sample) { 62 | if (sample < -1.f) { 63 | return -softClipThreshold; 64 | } 65 | else if (sample > 1.f) { 66 | return softClipThreshold; 67 | } 68 | else { 69 | return sample - ((sample * sample * sample) / 3.f); 70 | } 71 | } 72 | 73 | static inline float arctangent(float sample, float drive) 74 | { 75 | return (2.f / M_PI)* atan(drive * sample); 76 | } 77 | 78 | static inline float hardClip(float sample) { 79 | if (sample < -5.f) { 80 | return -5.f; 81 | } 82 | else if (sample > 5.f) { 83 | return 5.f; 84 | } 85 | else { 86 | return sample; 87 | } 88 | } 89 | 90 | static inline float cubicShaper(float sample) 91 | { 92 | return 1.5f * sample - 0.5f * sample * sample * sample; 93 | } 94 | 95 | static inline float foldback(float sample, float threshold) { // threshold -> 0-1 96 | if (sample > threshold || sample < -threshold) { 97 | sample = fabs(fabs(fmod(sample - threshold, threshold * 4)) - threshold * 2) - threshold; 98 | } 99 | return sample; 100 | } 101 | 102 | //---------------------------------------------------------------------------- 103 | 104 | 105 | static inline float Limit(float input, float min, float max) { 106 | int above = (input > min); 107 | int notAbove = above != 1; 108 | input = above * input + notAbove * min; 109 | 110 | int below = (input < max); 111 | int notBelow = below != 1; 112 | input = below * input + notBelow * max; 113 | 114 | return input; 115 | } 116 | 117 | static inline void ZeroBuffer(float* buffer, int len) { 118 | for (int i = 0; i < len; i++) 119 | buffer[i] = 0.0f; 120 | } -------------------------------------------------------------------------------- /src/BlankPanel.cpp: -------------------------------------------------------------------------------- 1 | #include "MSM.hpp" 2 | #include "MSMComponentLibrary.hpp" 3 | 4 | struct BlankPanel : Module { 5 | 6 | enum ParamIds { 7 | NUM_PARAMS 8 | }; 9 | 10 | enum InputIds { 11 | NUM_INPUTS 12 | }; 13 | 14 | enum OutputIds { 15 | NUM_OUTPUTS 16 | }; 17 | 18 | enum LightIds { 19 | NUM_LIGHTS 20 | }; 21 | 22 | // Panel Theme 23 | int Theme = 0; 24 | 25 | BlankPanel() { 26 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 27 | } 28 | 29 | void process(const ProcessArgs& args) override {} 30 | 31 | //Json for Panel Theme 32 | json_t *dataToJson() override { 33 | json_t *rootJ = json_object(); 34 | json_object_set_new(rootJ, "Theme", json_integer(Theme)); 35 | return rootJ; 36 | } 37 | void dataFromJson(json_t *rootJ) override { 38 | json_t *ThemeJ = json_object_get(rootJ, "Theme"); 39 | if (ThemeJ) 40 | Theme = json_integer_value(ThemeJ); 41 | } 42 | }; 43 | 44 | struct BlankClassicMenu : MenuItem { 45 | BlankPanel *blankpanel; 46 | void onAction(const event::Action &e) override { 47 | blankpanel->Theme = 0; 48 | } 49 | void step() override { 50 | rightText = (blankpanel->Theme == 0) ? "✔" : ""; 51 | MenuItem::step(); 52 | } 53 | }; 54 | 55 | struct BlankNightModeMenu : MenuItem { 56 | BlankPanel *blankpanel; 57 | void onAction(const event::Action &e) override { 58 | blankpanel->Theme = 1; 59 | } 60 | void step() override { 61 | rightText = (blankpanel->Theme == 1) ? "✔" : ""; 62 | MenuItem::step(); 63 | } 64 | }; 65 | 66 | struct BlankPanelWidget : ModuleWidget { 67 | // Panel Themes 68 | SvgPanel *panelClassic; 69 | SvgPanel *panelNightMode; 70 | 71 | void appendContextMenu(Menu *menu) override { 72 | BlankPanel *blankpanel = dynamic_cast(module); 73 | assert(blankpanel); 74 | 75 | menu->addChild(construct()); 76 | menu->addChild(construct(&MenuLabel::text, "Theme")); 77 | menu->addChild(construct(&BlankClassicMenu::text, "Classic (default)", &BlankClassicMenu::blankpanel, blankpanel)); 78 | menu->addChild(construct(&BlankNightModeMenu::text, "Night Mode", &BlankNightModeMenu::blankpanel, blankpanel)); 79 | } 80 | 81 | BlankPanelWidget(BlankPanel *module); 82 | void step() override; 83 | }; 84 | 85 | BlankPanelWidget::BlankPanelWidget(BlankPanel *module) { 86 | setModule(module); 87 | box.size = Vec(8 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); 88 | panelClassic = new SvgPanel(); 89 | panelClassic->box.size = box.size; 90 | panelClassic->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Panels/BlankPanelLight.svg"))); 91 | panelClassic->visible = true; 92 | addChild(panelClassic); 93 | 94 | panelNightMode = new SvgPanel(); 95 | panelNightMode->box.size = box.size; 96 | panelNightMode->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Panels/BlankPanelDark.svg"))); 97 | panelNightMode->visible = false; 98 | addChild(panelNightMode); 99 | 100 | addChild(createWidget(Vec(15, 0))); 101 | addChild(createWidget(Vec(15, 365))); 102 | addChild(createWidget(Vec(90, 0))); 103 | addChild(createWidget(Vec(90, 365))); 104 | 105 | }; 106 | 107 | void BlankPanelWidget::step() { 108 | if (module) { 109 | BlankPanel *blankpanel = dynamic_cast(module); 110 | assert(blankpanel); 111 | panelClassic->visible = (blankpanel->Theme == 0); 112 | panelNightMode->visible = (blankpanel->Theme == 1); 113 | } 114 | ModuleWidget::step(); 115 | } 116 | 117 | Model *modelBlankPanel = createModel("BlankPanel"); 118 | -------------------------------------------------------------------------------- /res/Slider/SlidePotHandle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 67 | 76 | 81 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /res/Button/ColorButton5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 45 | 46 | 66 | 68 | 69 | 71 | image/svg+xml 72 | 74 | 75 | 76 | 77 | 78 | 83 | 92 | 101 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /res/Button/ColorButton3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 45 | 46 | 66 | 68 | 69 | 71 | image/svg+xml 72 | 74 | 75 | 76 | 77 | 78 | 83 | 92 | 101 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /res/Button/ColorButton4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 45 | 46 | 66 | 68 | 69 | 71 | image/svg+xml 72 | 74 | 75 | 76 | 77 | 78 | 83 | 92 | 101 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /res/Button/ColorButton0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 45 | 46 | 66 | 68 | 69 | 71 | image/svg+xml 72 | 74 | 75 | 76 | 77 | 78 | 83 | 92 | 101 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /res/Button/ColorButton1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 45 | 46 | 66 | 68 | 69 | 71 | image/svg+xml 72 | 74 | 75 | 76 | 77 | 78 | 83 | 92 | 101 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /res/Button/ColorButton2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 45 | 46 | 66 | 68 | 69 | 71 | image/svg+xml 72 | 74 | 75 | 76 | 77 | 78 | 83 | 92 | 101 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /res/Button/GreyRoundToggle_0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 43 | 44 | 67 | 69 | 70 | 72 | image/svg+xml 73 | 75 | 76 | 77 | 78 | 79 | 84 | 88 | 93 | 98 | 103 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /res/Button/GreyRoundToggle_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 44 | 45 | 68 | 70 | 71 | 73 | image/svg+xml 74 | 76 | 77 | 78 | 79 | 80 | 85 | 89 | 94 | 99 | 104 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /res/Switch/VioMSwitch_0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 64 | 73 | 82 | 85 | 97 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /res/Fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Sudo is Copyright (c) 2009-2016, Jens Kutilek (http://www.kutilek.de/). 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /res/Switch/VioMSwitchVert_0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 64 | 67 | 76 | 85 | 88 | 100 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /res/Switch/VioMSwitchVert_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 64 | 67 | 76 | 85 | 88 | 100 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/Additional/Linear_Interpolator.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Posted by scoofy[AT]inf[DOT]elte[DOT]hu at musicdsp.org 3 | 4 | modified by Michael Struggl (2018) 5 | */ 6 | 7 | 8 | class interpolator_linear { 9 | public: 10 | interpolator_linear() 11 | { 12 | reset_hist(); 13 | } 14 | 15 | // reset history 16 | void reset_hist() { 17 | d1 = 0.f; 18 | } 19 | 20 | // 2x interpolator 21 | // out: pointer to float[2] 22 | inline float process2x(float const in, float *out) { 23 | out[0] = d1 + 0.5f*(in-d1); // interpolate 24 | out[1] = in; 25 | d1 = in; // store delay 26 | return *out; 27 | } 28 | 29 | // 2x interpolator 30 | // out: pointer to float[4] 31 | inline float process2xP(float const *in, float x, float *ot) { 32 | float dt1 = 0.f; 33 | float xi = *in-dt1; 34 | ot[0] = dt1 + x*xi; // interpolate 35 | ot[1] = *in; 36 | dt1 = *in; // store delay 37 | return *ot; 38 | } 39 | 40 | // 4x interpolator 41 | // out: pointer to float[4] 42 | inline float process4x(float const in, float *out) { 43 | float y = in-d1; 44 | out[0] = d1 + 0.25f*y; // interpolate 45 | out[1] = d1 + 0.5f*y; 46 | out[2] = d1 + 0.75f*y; 47 | out[3] = in; 48 | d1 = in; // store delay 49 | return *out; 50 | } 51 | 52 | // 4x interpolator 53 | // out: pointer to float[4] 54 | inline double process4xX(double const in, double *out) { 55 | double y = in-d1; 56 | out[0] = d1 + 0.25f*y; // interpolate 57 | out[1] = d1 + 0.5f*y; 58 | out[2] = d1 + 0.75f*y; 59 | out[3] = in; 60 | d1 = in; // store delay 61 | return *out; 62 | } 63 | 64 | // 4x interpolator 65 | // out: pointer to float[4] 66 | inline float process4xP(float const *in, float x, float *ot) { 67 | float dt1 = 0.f; 68 | float xi = *in-dt1; 69 | ot[0] = dt1 + x*xi; // interpolate 70 | ot[1] = dt1 + x*xi; 71 | ot[2] = dt1 + x*xi; 72 | ot[3] = *in; 73 | dt1 = *in; // store delay 74 | return *ot; 75 | } 76 | 77 | 78 | // 8x interpolator 79 | // out: pointer to float[8] 80 | inline float process8x(float const in, float *out) { 81 | float y = in-d1; 82 | out[0] = d1 + 0.125f*y; // interpolate 83 | out[1] = d1 + 0.25f*y; 84 | out[2] = d1 + 0.375f*y; 85 | out[3] = d1 + 0.5f*y; 86 | out[4] = d1 + 0.625f*y; 87 | out[5] = d1 + 0.75f*y; 88 | out[6] = d1 + 0.875f*y; 89 | out[7] = in; 90 | d1 = in; // store delay 91 | return *out; 92 | } 93 | 94 | // 16x interpolator 95 | // out: pointer to float[16] 96 | inline float process16x(float const in, float *out) { 97 | float y = in-d1; 98 | out[0] = d1 + (1.0f/16.0f)*y; // interpolate 99 | out[1] = d1 + (2.0f/16.0f)*y; 100 | out[2] = d1 + (3.0f/16.0f)*y; 101 | out[3] = d1 + (4.0f/16.0f)*y; 102 | out[4] = d1 + (5.0f/16.0f)*y; 103 | out[5] = d1 + (6.0f/16.0f)*y; 104 | out[6] = d1 + (7.0f/16.0f)*y; 105 | out[7] = d1 + (8.0f/16.0f)*y; 106 | out[8] = d1 + (9.0f/16.0f)*y; 107 | out[9] = d1 + (10.0f/16.0f)*y; 108 | out[10] = d1 + (11.0f/16.0f)*y; 109 | out[11] = d1 + (12.0f/16.0f)*y; 110 | out[12] = d1 + (13.0f/16.0f)*y; 111 | out[13] = d1 + (14.0f/16.0f)*y; 112 | out[14] = d1 + (15.0f/16.0f)*y; 113 | out[15] = in; 114 | d1 = in; // store delay 115 | return *out; 116 | } 117 | 118 | // 32x interpolator 119 | // out: pointer to float[32] 120 | inline float process32x(float const in, float *out) { 121 | float y = in-d1; 122 | out[0] = d1 + (1.0f/32.0f)*y; // interpolate 123 | out[1] = d1 + (2.0f/32.0f)*y; 124 | out[2] = d1 + (3.0f/32.0f)*y; 125 | out[3] = d1 + (4.0f/32.0f)*y; 126 | out[4] = d1 + (5.0f/32.0f)*y; 127 | out[5] = d1 + (6.0f/32.0f)*y; 128 | out[6] = d1 + (7.0f/32.0f)*y; 129 | out[7] = d1 + (8.0f/32.0f)*y; 130 | out[8] = d1 + (9.0f/32.0f)*y; 131 | out[9] = d1 + (10.0f/32.0f)*y; 132 | out[10] = d1 + (11.0f/32.0f)*y; 133 | out[11] = d1 + (12.0f/32.0f)*y; 134 | out[12] = d1 + (13.0f/32.0f)*y; 135 | out[13] = d1 + (14.0f/32.0f)*y; 136 | out[14] = d1 + (15.0f/32.0f)*y; 137 | out[15] = d1 + (16.0f/32.0f)*y; 138 | out[16] = d1 + (17.0f/32.0f)*y; 139 | out[17] = d1 + (18.0f/32.0f)*y; 140 | out[18] = d1 + (19.0f/32.0f)*y; 141 | out[19] = d1 + (20.0f/32.0f)*y; 142 | out[20] = d1 + (21.0f/32.0f)*y; 143 | out[21] = d1 + (22.0f/32.0f)*y; 144 | out[22] = d1 + (23.0f/32.0f)*y; 145 | out[23] = d1 + (24.0f/32.0f)*y; 146 | out[24] = d1 + (25.0f/32.0f)*y; 147 | out[25] = d1 + (26.0f/32.0f)*y; 148 | out[26] = d1 + (27.0f/32.0f)*y; 149 | out[27] = d1 + (28.0f/32.0f)*y; 150 | out[28] = d1 + (29.0f/32.0f)*y; 151 | out[29] = d1 + (30.0f/32.0f)*y; 152 | out[30] = d1 + (31.0f/32.0f)*y; 153 | out[31] = in; 154 | d1 = in; // store delay 155 | return *out; 156 | } 157 | 158 | private: 159 | float d1; // previous input 160 | }; -------------------------------------------------------------------------------- /res/Switch/VioMSwitch_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 28 | 29 | 52 | 54 | 55 | 57 | image/svg+xml 58 | 60 | 61 | 62 | 63 | 64 | 69 | 73 | 77 | 80 | 81 | 90 | 99 | 101 | 113 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/slider.cpp: -------------------------------------------------------------------------------- 1 | #include "MSM.hpp" 2 | #include "MSMComponentLibrary.hpp" 3 | 4 | struct SimpleSlider : Module { 5 | 6 | enum ParamIds { 7 | SLIDER_PARAM, 8 | TYPE_PARAM, 9 | NUM_PARAMS 10 | }; 11 | 12 | enum InputIds { 13 | IN1_INPUT, 14 | IN2_INPUT, 15 | CV_INPUT, 16 | NUM_INPUTS 17 | }; 18 | 19 | enum OutputIds { 20 | MAIN_OUTPUT, 21 | NUM_OUTPUTS 22 | }; 23 | 24 | int Theme = 0; 25 | 26 | SimpleSlider() { 27 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); 28 | configParam(SimpleSlider::SLIDER_PARAM, 0.0, 1.0, 0.5, "Slider"); 29 | configParam(SimpleSlider::TYPE_PARAM, 0.0, 1.0, 0.0, "Type"); 30 | configInput(IN1_INPUT, "#1"); 31 | configInput(IN2_INPUT, "#2"); 32 | configInput(CV_INPUT, "Slider Position Control Voltage"); 33 | configOutput(MAIN_OUTPUT, "Master"); 34 | } 35 | 36 | void process(const ProcessArgs& args) override; 37 | 38 | //Json for Panel Theme 39 | json_t *dataToJson() override { 40 | json_t *rootJ = json_object(); 41 | json_object_set_new(rootJ, "Theme", json_integer(Theme)); 42 | return rootJ; 43 | } 44 | void dataFromJson(json_t *rootJ) override { 45 | json_t *ThemeJ = json_object_get(rootJ, "Theme"); 46 | if (ThemeJ) 47 | Theme = json_integer_value(ThemeJ); 48 | } 49 | }; 50 | 51 | void SimpleSlider::process(const ProcessArgs& args) { 52 | 53 | double SLIDER = clamp(params[SLIDER_PARAM].getValue() + inputs[CV_INPUT].getVoltage() / 10.f, 0.0f, 1.0f); 54 | double IN1 = inputs[IN1_INPUT].getVoltage(); 55 | double IN2 = inputs[IN2_INPUT].getVoltage(); 56 | double TYPE = params[TYPE_PARAM].getValue(); 57 | double OUT = outputs[MAIN_OUTPUT].getVoltage(); 58 | 59 | if(TYPE == 0.0f) { 60 | OUT = crossfade(IN1, IN2, SLIDER); 61 | } 62 | else { 63 | OUT = (IN1 + IN2) * SLIDER; 64 | } 65 | outputs[MAIN_OUTPUT].setVoltage(OUT); 66 | 67 | 68 | }; 69 | 70 | struct SimpleClassicMenu : MenuItem { 71 | SimpleSlider *simpleslider; 72 | void onAction(const event::Action &e) override { 73 | simpleslider->Theme = 0; 74 | } 75 | void step() override { 76 | rightText = (simpleslider->Theme == 0) ? "✔" : ""; 77 | MenuItem::step(); 78 | } 79 | }; 80 | 81 | struct SimpleNightModeMenu : MenuItem { 82 | SimpleSlider *simpleslider; 83 | void onAction(const event::Action &e) override { 84 | simpleslider->Theme = 1; 85 | } 86 | void step() override { 87 | rightText = (simpleslider->Theme == 1) ? "✔" : ""; 88 | MenuItem::step(); 89 | } 90 | }; 91 | 92 | 93 | 94 | struct SimpleSliderWidget : ModuleWidget { 95 | // Panel Themes 96 | SvgPanel *panelClassic; 97 | SvgPanel *panelNightMode; 98 | 99 | void appendContextMenu(Menu *menu) override { 100 | SimpleSlider *simpleslider = dynamic_cast(module); 101 | assert(simpleslider); 102 | menu->addChild(construct()); 103 | menu->addChild(construct(&MenuLabel::text, "Theme")); 104 | menu->addChild(construct(&SimpleClassicMenu::text, "Classic (default)", &SimpleClassicMenu::simpleslider, simpleslider)); 105 | menu->addChild(construct(&SimpleNightModeMenu::text, "Night Mode", &SimpleNightModeMenu::simpleslider, simpleslider)); 106 | } 107 | 108 | SimpleSliderWidget(SimpleSlider *module); 109 | void step() override; 110 | }; 111 | 112 | SimpleSliderWidget::SimpleSliderWidget(SimpleSlider *module) { 113 | setModule(module); 114 | box.size = Vec(8 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); 115 | panelClassic = new SvgPanel(); 116 | panelClassic->box.size = box.size; 117 | panelClassic->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Panels/SimpleSlider.svg"))); 118 | panelClassic->visible = true; 119 | addChild(panelClassic); 120 | 121 | panelNightMode = new SvgPanel(); 122 | panelNightMode->box.size = box.size; 123 | panelNightMode->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Panels/SimpleSlider-Dark.svg"))); 124 | panelNightMode->visible = false; 125 | addChild(panelNightMode); 126 | 127 | 128 | addChild(createWidget(Vec(15, 0))); 129 | addChild(createWidget(Vec(15, 365))); 130 | addChild(createWidget(Vec(90, 0))); 131 | addChild(createWidget(Vec(90, 365))); 132 | 133 | addParam(createParam(Vec(32, 90), module, SimpleSlider::SLIDER_PARAM)); 134 | 135 | addParam(createParam(Vec(54.5, 40), module, SimpleSlider::TYPE_PARAM)); 136 | 137 | addInput(createInput(Vec(8, 338), module, SimpleSlider::IN1_INPUT)); 138 | addInput(createInput(Vec(48, 338), module, SimpleSlider::IN2_INPUT)); 139 | 140 | addInput(createInput(Vec(88, 338), module, SimpleSlider::CV_INPUT)); 141 | 142 | addOutput(createOutput(Vec(47, 299), module, SimpleSlider::MAIN_OUTPUT)); 143 | }; 144 | 145 | void SimpleSliderWidget::step() { 146 | if (module) { 147 | SimpleSlider *simpleslider = dynamic_cast(module); 148 | assert(simpleslider); 149 | panelClassic->visible = (simpleslider->Theme == 0); 150 | panelNightMode->visible = (simpleslider->Theme == 1); 151 | } 152 | 153 | ModuleWidget::step(); 154 | } 155 | 156 | Model *modelSimpleSlider = createModel("SimpleSlider"); 157 | -------------------------------------------------------------------------------- /res/Fonts/DejaVu-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. 2 | Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) 3 | 4 | Bitstream Vera Fonts Copyright 5 | ------------------------------ 6 | 7 | Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is 8 | a trademark of Bitstream, Inc. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of the fonts accompanying this license ("Fonts") and associated 12 | documentation files (the "Font Software"), to reproduce and distribute the 13 | Font Software, including without limitation the rights to use, copy, merge, 14 | publish, distribute, and/or sell copies of the Font Software, and to permit 15 | persons to whom the Font Software is furnished to do so, subject to the 16 | following conditions: 17 | 18 | The above copyright and trademark notices and this permission notice shall 19 | be included in all copies of one or more of the Font Software typefaces. 20 | 21 | The Font Software may be modified, altered, or added to, and in particular 22 | the designs of glyphs or characters in the Fonts may be modified and 23 | additional glyphs or characters may be added to the Fonts, only if the fonts 24 | are renamed to names not containing either the words "Bitstream" or the word 25 | "Vera". 26 | 27 | This License becomes null and void to the extent applicable to Fonts or Font 28 | Software that has been modified and is distributed under the "Bitstream 29 | Vera" names. 30 | 31 | The Font Software may be sold as part of a larger software package but no 32 | copy of one or more of the Font Software typefaces may be sold by itself. 33 | 34 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 35 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, 36 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, 37 | TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME 38 | FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING 39 | ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, 40 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 41 | THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE 42 | FONT SOFTWARE. 43 | 44 | Except as contained in this notice, the names of Gnome, the Gnome 45 | Foundation, and Bitstream Inc., shall not be used in advertising or 46 | otherwise to promote the sale, use or other dealings in this Font Software 47 | without prior written authorization from the Gnome Foundation or Bitstream 48 | Inc., respectively. For further information, contact: fonts at gnome dot 49 | org. 50 | 51 | Arev Fonts Copyright 52 | ------------------------------ 53 | 54 | Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. 55 | 56 | Permission is hereby granted, free of charge, to any person obtaining 57 | a copy of the fonts accompanying this license ("Fonts") and 58 | associated documentation files (the "Font Software"), to reproduce 59 | and distribute the modifications to the Bitstream Vera Font Software, 60 | including without limitation the rights to use, copy, merge, publish, 61 | distribute, and/or sell copies of the Font Software, and to permit 62 | persons to whom the Font Software is furnished to do so, subject to 63 | the following conditions: 64 | 65 | The above copyright and trademark notices and this permission notice 66 | shall be included in all copies of one or more of the Font Software 67 | typefaces. 68 | 69 | The Font Software may be modified, altered, or added to, and in 70 | particular the designs of glyphs or characters in the Fonts may be 71 | modified and additional glyphs or characters may be added to the 72 | Fonts, only if the fonts are renamed to names not containing either 73 | the words "Tavmjong Bah" or the word "Arev". 74 | 75 | This License becomes null and void to the extent applicable to Fonts 76 | or Font Software that has been modified and is distributed under the 77 | "Tavmjong Bah Arev" names. 78 | 79 | The Font Software may be sold as part of a larger software package but 80 | no copy of one or more of the Font Software typefaces may be sold by 81 | itself. 82 | 83 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 84 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 85 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 86 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL 87 | TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 88 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 89 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 90 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 91 | OTHER DEALINGS IN THE FONT SOFTWARE. 92 | 93 | Except as contained in this notice, the name of Tavmjong Bah shall not 94 | be used in advertising or otherwise to promote the sale, use or other 95 | dealings in this Font Software without prior written authorization 96 | from Tavmjong Bah. For further information, contact: tavmjong @ free 97 | . fr. 98 | 99 | $Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $ 100 | -------------------------------------------------------------------------------- /src/Additional/samplerate.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright (c) 2002-2016, Erik de Castro Lopo 3 | ** All rights reserved. 4 | ** 5 | ** This code is released under 2-clause BSD license. Please see the 6 | ** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING 7 | */ 8 | 9 | /* 10 | ** API documentation is available here: 11 | ** http://www.mega-nerd.com/SRC/api.html 12 | */ 13 | 14 | #ifndef SAMPLERATE_H 15 | #define SAMPLERATE_H 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif /* __cplusplus */ 20 | 21 | 22 | /* Opaque data type SRC_STATE. */ 23 | typedef struct SRC_STATE_tag SRC_STATE ; 24 | 25 | /* SRC_DATA is used to pass data to src_simple() and src_process(). */ 26 | typedef struct 27 | { const float *data_in ; 28 | float *data_out ; 29 | 30 | long input_frames, output_frames ; 31 | long input_frames_used, output_frames_gen ; 32 | 33 | int end_of_input ; 34 | 35 | double src_ratio ; 36 | } SRC_DATA ; 37 | 38 | /* 39 | ** User supplied callback function type for use with src_callback_new() 40 | ** and src_callback_read(). First parameter is the same pointer that was 41 | ** passed into src_callback_new(). Second parameter is pointer to a 42 | ** pointer. The user supplied callback function must modify *data to 43 | ** point to the start of the user supplied float array. The user supplied 44 | ** function must return the number of frames that **data points to. 45 | */ 46 | 47 | typedef long (*src_callback_t) (void *cb_data, float **data) ; 48 | 49 | /* 50 | ** Standard initialisation function : return an anonymous pointer to the 51 | ** internal state of the converter. Choose a converter from the enums below. 52 | ** Error returned in *error. 53 | */ 54 | 55 | SRC_STATE* src_new (int converter_type, int channels, int *error) ; 56 | 57 | /* 58 | ** Initilisation for callback based API : return an anonymous pointer to the 59 | ** internal state of the converter. Choose a converter from the enums below. 60 | ** The cb_data pointer can point to any data or be set to NULL. Whatever the 61 | ** value, when processing, user supplied function "func" gets called with 62 | ** cb_data as first parameter. 63 | */ 64 | 65 | SRC_STATE* src_callback_new (src_callback_t func, int converter_type, int channels, 66 | int *error, void* cb_data) ; 67 | 68 | /* 69 | ** Cleanup all internal allocations. 70 | ** Always returns NULL. 71 | */ 72 | 73 | SRC_STATE* src_delete (SRC_STATE *state) ; 74 | 75 | /* 76 | ** Standard processing function. 77 | ** Returns non zero on error. 78 | */ 79 | 80 | int src_process (SRC_STATE *state, SRC_DATA *data) ; 81 | 82 | /* 83 | ** Callback based processing function. Read up to frames worth of data from 84 | ** the converter int *data and return frames read or -1 on error. 85 | */ 86 | long src_callback_read (SRC_STATE *state, double src_ratio, long frames, float *data) ; 87 | 88 | /* 89 | ** Simple interface for performing a single conversion from input buffer to 90 | ** output buffer at a fixed conversion ratio. 91 | ** Simple interface does not require initialisation as it can only operate on 92 | ** a single buffer worth of audio. 93 | */ 94 | 95 | int src_simple (SRC_DATA *data, int converter_type, int channels) ; 96 | 97 | /* 98 | ** This library contains a number of different sample rate converters, 99 | ** numbered 0 through N. 100 | ** 101 | ** Return a string giving either a name or a more full description of each 102 | ** sample rate converter or NULL if no sample rate converter exists for 103 | ** the given value. The converters are sequentially numbered from 0 to N. 104 | */ 105 | 106 | const char *src_get_name (int converter_type) ; 107 | const char *src_get_description (int converter_type) ; 108 | const char *src_get_version (void) ; 109 | 110 | /* 111 | ** Set a new SRC ratio. This allows step responses 112 | ** in the conversion ratio. 113 | ** Returns non zero on error. 114 | */ 115 | 116 | int src_set_ratio (SRC_STATE *state, double new_ratio) ; 117 | 118 | /* 119 | ** Get the current channel count. 120 | ** Returns negative on error, positive channel count otherwise 121 | */ 122 | 123 | int src_get_channels (SRC_STATE *state) ; 124 | 125 | /* 126 | ** Reset the internal SRC state. 127 | ** Does not modify the quality settings. 128 | ** Does not free any memory allocations. 129 | ** Returns non zero on error. 130 | */ 131 | 132 | int src_reset (SRC_STATE *state) ; 133 | 134 | /* 135 | ** Return TRUE if ratio is a valid conversion ratio, FALSE 136 | ** otherwise. 137 | */ 138 | 139 | int src_is_valid_ratio (double ratio) ; 140 | 141 | /* 142 | ** Return an error number. 143 | */ 144 | 145 | int src_error (SRC_STATE *state) ; 146 | 147 | /* 148 | ** Convert the error number into a string. 149 | */ 150 | const char* src_strerror (int error) ; 151 | 152 | /* 153 | ** The following enums can be used to set the interpolator type 154 | ** using the function src_set_converter(). 155 | */ 156 | 157 | enum 158 | { 159 | SRC_SINC_BEST_QUALITY = 0, 160 | SRC_SINC_MEDIUM_QUALITY = 1, 161 | SRC_SINC_FASTEST = 2, 162 | SRC_ZERO_ORDER_HOLD = 3, 163 | SRC_LINEAR = 4, 164 | } ; 165 | 166 | /* 167 | ** Extra helper functions for converting from short to float and 168 | ** back again. 169 | */ 170 | 171 | void src_short_to_float_array (const short *in, float *out, int len) ; 172 | void src_float_to_short_array (const float *in, short *out, int len) ; 173 | 174 | void src_int_to_float_array (const int *in, float *out, int len) ; 175 | void src_float_to_int_array (const float *in, int *out, int len) ; 176 | 177 | 178 | #ifdef __cplusplus 179 | } /* extern "C" */ 180 | #endif /* __cplusplus */ 181 | 182 | #endif /* SAMPLERATE_H */ 183 | 184 | -------------------------------------------------------------------------------- /src/Additional/EnvelopeGenerator.hpp: -------------------------------------------------------------------------------- 1 | 2 | struct Generator { 3 | float _current = 0.0; 4 | 5 | Generator() {} 6 | virtual ~Generator() {} 7 | 8 | float current() { 9 | return _current; 10 | } 11 | 12 | float next() { 13 | return _current = _next(); 14 | } 15 | 16 | virtual float _next() = 0; 17 | }; 18 | 19 | struct EnvelopeGenerator : Generator { 20 | float _sampleRate; 21 | float _sampleTime; 22 | 23 | EnvelopeGenerator(float sampleRate = 1000.0f) 24 | : _sampleRate(sampleRate > 1.0 ? sampleRate : 1.0) 25 | , _sampleTime(1.0f / _sampleRate) 26 | { 27 | } 28 | 29 | void setSampleRate(float sampleRate); 30 | virtual void _sampleRateChanged() {} 31 | }; 32 | 33 | struct EnvelopeGen : EnvelopeGenerator { 34 | enum Stage { 35 | STOPPED_STAGE, 36 | ATTACK_STAGE, 37 | DECAY_STAGE, 38 | SUSTAIN_STAGE, 39 | RELEASE_STAGE 40 | }; 41 | 42 | Stage _stage = STOPPED_STAGE; 43 | bool _gated = false; 44 | float _attack = 0.0f; 45 | float _decay = 0.0f; 46 | float _sustain = 1.0f; 47 | float _release = 0.0f; 48 | float _attackShape; 49 | float _decayShape; 50 | float _releaseShape; 51 | float _stageProgress = 0.0f; 52 | float _releaseLevel = 0.0f; 53 | float _envelope = 0.0f; 54 | 55 | EnvelopeGen(char linear = 0, float sampleRate = 1000.0f) : EnvelopeGenerator(sampleRate) { 56 | setLinearShape(linear); 57 | } 58 | 59 | void reset(); 60 | void setGate(bool high, bool retrig); 61 | void setAttack(float seconds); 62 | void setDecay(float seconds); 63 | void setSustain(float level, float cv); 64 | void setRelease(float seconds); 65 | void setLinearShape(char linear); 66 | void setShapes(float attackShape, float decayShape, float releaseShape); 67 | bool isStage(Stage stage) { return _stage == stage; } 68 | 69 | float _next() override; 70 | }; 71 | 72 | void EnvelopeGenerator::setSampleRate(float sampleRate) { 73 | if (_sampleRate != sampleRate && sampleRate >= 1.0) { 74 | _sampleRate = sampleRate; 75 | _sampleTime = 1.0f / sampleRate; 76 | _sampleRateChanged(); 77 | } 78 | } 79 | 80 | void EnvelopeGen::reset() { 81 | _stage = STOPPED_STAGE; 82 | _gated = false; 83 | _envelope = 0.0f; 84 | } 85 | 86 | void EnvelopeGen::setGate(bool high, bool retrig) { 87 | _gated = high; 88 | if(retrig) { 89 | _stage = DECAY_STAGE; 90 | } 91 | } 92 | 93 | void EnvelopeGen::setAttack(float seconds) { 94 | assert(_attack >= 0.0f); 95 | _attack = std::max(seconds, 0.01f); 96 | } 97 | 98 | void EnvelopeGen::setDecay(float seconds) { 99 | assert(_decay >= 0.0f); 100 | _decay = std::max(seconds, 0.01f); 101 | } 102 | 103 | void EnvelopeGen::setSustain(float level, float cv) { 104 | assert(_sustain >= 0.0f); 105 | assert(_sustain <= 1.0f); 106 | _sustain = clamp(level + cv / 10.0f, 0.0f, 1.0f); 107 | } 108 | 109 | void EnvelopeGen::setRelease(float seconds) { 110 | assert(_release >= 0.0f); 111 | _release = std::max(seconds, 0.01f); 112 | } 113 | 114 | void EnvelopeGen::setLinearShape(char linear) { 115 | if(linear == 0){ 116 | setShapes(1.5f, 0.5f, 0.5f); 117 | } 118 | else if(linear == 1) { 119 | setShapes(0.5f, 0.5f, 0.5f); 120 | } 121 | 122 | else if (linear == 2) { 123 | setShapes(1.0f, 1.0f, 1.0f); 124 | } 125 | } 126 | 127 | void EnvelopeGen::setShapes(float attackShape, float decayShape, float releaseShape) { 128 | assert(attackShape >= 0.1f && attackShape <= 10.0f); 129 | assert(decayShape >= 0.1f && decayShape <= 10.0f); 130 | assert(releaseShape >= 0.1f && releaseShape <= 10.0f); 131 | _attackShape = attackShape; 132 | _decayShape = decayShape; 133 | _releaseShape = releaseShape; 134 | } 135 | 136 | float EnvelopeGen::_next() { 137 | if (_gated) { 138 | switch (_stage) { 139 | case STOPPED_STAGE: { 140 | _stage = ATTACK_STAGE; 141 | _stageProgress = 0.0f; 142 | break; 143 | } 144 | case ATTACK_STAGE: { 145 | if (_envelope >= 1.0) { 146 | _stage = DECAY_STAGE; 147 | _stageProgress = 0.0f; 148 | } 149 | break; 150 | } 151 | case DECAY_STAGE: { 152 | if (_stageProgress >= _decay) { 153 | _stage = SUSTAIN_STAGE; 154 | _stageProgress = 0.0f; 155 | } 156 | break; 157 | } 158 | case SUSTAIN_STAGE: { 159 | break; 160 | } 161 | case RELEASE_STAGE: { 162 | _stage = ATTACK_STAGE; 163 | _stageProgress = _attack * powf(_envelope, 1.0f / _releaseShape); 164 | break; 165 | } 166 | } 167 | } 168 | else { 169 | switch (_stage) { 170 | case STOPPED_STAGE: { 171 | break; 172 | } 173 | case ATTACK_STAGE: 174 | case DECAY_STAGE: 175 | 176 | case SUSTAIN_STAGE: { 177 | _stage = RELEASE_STAGE; 178 | _stageProgress = 0.0f; 179 | _releaseLevel = _envelope; 180 | break; 181 | } 182 | case RELEASE_STAGE: { 183 | if (_stageProgress >= _release) { 184 | _stage = STOPPED_STAGE; 185 | } 186 | break; 187 | } 188 | } 189 | } 190 | 191 | switch (_stage) { 192 | case STOPPED_STAGE: { 193 | _envelope = 0.0f; 194 | break; 195 | } 196 | case ATTACK_STAGE: { 197 | _stageProgress += _sampleTime; 198 | _envelope = _stageProgress / _attack; 199 | _envelope = powf(_envelope, _attackShape); 200 | break; 201 | } 202 | case DECAY_STAGE: { 203 | _stageProgress += _sampleTime; 204 | _envelope = _stageProgress / _decay; 205 | _envelope = powf(_envelope, _decayShape); 206 | _envelope *= 1.0f - _sustain; 207 | _envelope = 1.0f - _envelope; 208 | break; 209 | } 210 | case SUSTAIN_STAGE: { 211 | break; 212 | } 213 | case RELEASE_STAGE: { 214 | _stageProgress += _sampleTime; 215 | _envelope = _stageProgress / _release; 216 | _envelope = powf(_envelope, _releaseShape); 217 | _envelope *= _releaseLevel; 218 | _envelope = _releaseLevel - _envelope; 219 | break; 220 | } 221 | } 222 | 223 | return _envelope; 224 | } 225 | -------------------------------------------------------------------------------- /src/Bitcrusher.cpp: -------------------------------------------------------------------------------- 1 | #include "MSM.hpp" 2 | #include "MSMComponentLibrary.hpp" 3 | 4 | class BitcrusherFX { 5 | public: 6 | BitcrusherFX() 7 | { 8 | reset(); 9 | quant = powf(2.0f, 32.0f); 10 | counter = 0.0f; 11 | _drive = 1.0f; 12 | input = 0.0f; 13 | } 14 | 15 | void reset() { 16 | output = 0.0f; 17 | } 18 | 19 | float process(float in, long int Bits, float drive, float sample_rate) { 20 | // Overdrive 21 | _drive = drive; 22 | input = overdrive(in, _drive); 23 | //Bit-reduction 24 | quant = std::pow(2.0, Bits); 25 | 26 | counter += sample_rate; 27 | if (counter >= 1.0f) { 28 | counter -= 1.0f; 29 | output = ROUND((input + 1.0) * quant) / quant - 1.0; 30 | } 31 | return output; 32 | } 33 | 34 | 35 | private: 36 | float quant; 37 | float _drive; 38 | float counter; 39 | float input; 40 | float output; 41 | }; 42 | 43 | struct Bitcrusher : Module { 44 | 45 | enum ParamIds { 46 | BITS_PARAM, 47 | QD_PARAM, 48 | SR_PARAM, 49 | NUM_PARAMS 50 | }; 51 | 52 | enum InputIds { 53 | INPUT, 54 | CV_BITS, 55 | NUM_INPUTS 56 | }; 57 | 58 | enum OutputIds { 59 | OUTPUT, 60 | NUM_OUTPUTS 61 | }; 62 | 63 | enum LightIds { 64 | NUM_LIGHTS 65 | }; 66 | 67 | // Panel Theme 68 | int Theme = 0; 69 | 70 | float out = 0.0f ? 0.0f : 0.0f; 71 | 72 | BitcrusherFX BitC; 73 | 74 | Bitcrusher() { 75 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 76 | configParam(Bitcrusher::BITS_PARAM, 0.0, 8.0, 8.0, "Bits"); 77 | configParam(Bitcrusher::SR_PARAM, 0.01, 1.0, 1.0, "Sample Rate"); 78 | configParam(Bitcrusher::QD_PARAM, 0.0, 0.95, 0.0, "Overdrive"); 79 | configInput(INPUT, "Master"); 80 | configInput(CV_BITS, "Bit Length"); 81 | configOutput(OUTPUT, "Master"); 82 | } 83 | 84 | void process(const ProcessArgs& args) override; 85 | 86 | //Json for Panel Theme 87 | json_t *dataToJson() override { 88 | json_t *rootJ = json_object(); 89 | json_object_set_new(rootJ, "Theme", json_integer(Theme)); 90 | return rootJ; 91 | } 92 | void dataFromJson(json_t *rootJ) override { 93 | json_t *ThemeJ = json_object_get(rootJ, "Theme"); 94 | if (ThemeJ) 95 | Theme = json_integer_value(ThemeJ); 96 | } 97 | 98 | void onReset() override { 99 | BitC.reset(); 100 | } 101 | 102 | }; 103 | 104 | void Bitcrusher::process(const ProcessArgs& args) { 105 | 106 | float in = inputs[INPUT].getVoltage() / 5.0f; 107 | long int bits = params[BITS_PARAM].getValue() + inputs[CV_BITS].getVoltage(); 108 | float qd = params[QD_PARAM].getValue(); 109 | float sr = params[SR_PARAM].getValue(); 110 | 111 | out = 5.0f * BitC.process(in, bits, qd, sr); 112 | 113 | outputs[OUTPUT].setVoltage(saturate(out)); 114 | 115 | }; 116 | 117 | struct BitCClassicMenu : MenuItem { 118 | Bitcrusher *bitcrusher; 119 | void onAction(const event::Action &e) override { 120 | bitcrusher->Theme = 0; 121 | } 122 | void step() override { 123 | rightText = (bitcrusher->Theme == 0) ? "✔" : ""; 124 | MenuItem::step(); 125 | } 126 | }; 127 | 128 | struct BitCNightModeMenu : MenuItem { 129 | Bitcrusher *bitcrusher; 130 | void onAction(const event::Action &e) override { 131 | bitcrusher->Theme = 1; 132 | } 133 | void step() override { 134 | rightText = (bitcrusher->Theme == 1) ? "✔" : ""; 135 | MenuItem::step(); 136 | } 137 | }; 138 | 139 | struct BitcrusherWidget : ModuleWidget { 140 | // Panel Themes 141 | SvgPanel *panelClassic; 142 | SvgPanel *panelNightMode; 143 | 144 | void appendContextMenu(Menu *menu) override { 145 | Bitcrusher *bitcrusher = dynamic_cast(module); 146 | assert(bitcrusher); 147 | 148 | menu->addChild(construct()); 149 | menu->addChild(construct(&MenuLabel::text, "Theme")); 150 | menu->addChild(construct(&BitCClassicMenu::text, "Classic (default)", &BitCClassicMenu::bitcrusher, bitcrusher)); 151 | menu->addChild(construct(&BitCNightModeMenu::text, "Night Mode", &BitCNightModeMenu::bitcrusher, bitcrusher)); 152 | } 153 | 154 | BitcrusherWidget(Bitcrusher *module); 155 | void step() override; 156 | }; 157 | 158 | BitcrusherWidget::BitcrusherWidget(Bitcrusher *module) { 159 | setModule(module); 160 | box.size = Vec(4 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); 161 | panelClassic = new SvgPanel(); 162 | panelClassic->box.size = box.size; 163 | panelClassic->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Panels/Bitcrusher.svg"))); 164 | panelClassic->visible = true; 165 | addChild(panelClassic); 166 | 167 | panelNightMode = new SvgPanel(); 168 | panelNightMode->box.size = box.size; 169 | panelNightMode->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Panels/Bitcrusher-Dark.svg"))); 170 | panelNightMode->visible = false; 171 | addChild(panelNightMode); 172 | 173 | addChild(createWidget(Vec(0, 0))); 174 | addChild(createWidget(Vec(0, 365))); 175 | addChild(createWidget(Vec(45, 0))); 176 | addChild(createWidget(Vec(45, 365))); 177 | 178 | addParam(createParam(Vec(15, 48), module, Bitcrusher::BITS_PARAM)); 179 | addParam(createParam(Vec(15, 98), module, Bitcrusher::SR_PARAM)); 180 | addParam(createParam(Vec(15, 148), module, Bitcrusher::QD_PARAM)); 181 | 182 | addInput(createInput(Vec(18, 210), module, Bitcrusher::INPUT)); 183 | addInput(createInput(Vec(18, 250), module, Bitcrusher::CV_BITS)); 184 | 185 | addOutput(createOutput(Vec(18, 290), module, Bitcrusher::OUTPUT)); 186 | 187 | }; 188 | 189 | void BitcrusherWidget::step() { 190 | if (module) { 191 | Bitcrusher *bitcrusher = dynamic_cast(module); 192 | assert(bitcrusher); 193 | panelClassic->visible = (bitcrusher->Theme == 0); 194 | panelNightMode->visible = (bitcrusher->Theme == 1); 195 | } 196 | ModuleWidget::step(); 197 | } 198 | 199 | Model *modelBitcrusher = createModel("Bitcrusher"); 200 | -------------------------------------------------------------------------------- /src/RingMod.cpp: -------------------------------------------------------------------------------- 1 | #include "MSM.hpp" 2 | #include "MSMComponentLibrary.hpp" 3 | 4 | struct RingMod : Module 5 | { 6 | enum ParamIds { 7 | CARRIER_A_PARAM, 8 | MODULATOR_A_PARAM, 9 | MIXA, 10 | MIXB, 11 | NUM_PARAMS 12 | }; 13 | 14 | enum InputIds { 15 | CARRIER_A, 16 | MODULATOR_A, 17 | MIXA_CV, 18 | CARRIER_B, 19 | MODULATOR_B, 20 | MIXB_CV, 21 | NUM_INPUTS 22 | }; 23 | 24 | enum OutputIds { 25 | OUTPUT_A, 26 | OUTPUT_B, 27 | NUM_OUTPUTS 28 | }; 29 | 30 | enum LightIds 31 | { 32 | NUM_LIGHTS 33 | }; 34 | 35 | int Theme = 0; 36 | 37 | RingMod() { 38 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 39 | configParam(RingMod::MIXA, 0.0f, 1.0f, 0.5f, "Modulation", "%", 0.f, 100); 40 | configParam(RingMod::MIXB, 0.0f, 1.0f, 0.5f, "Modulation", "%", 0.f, 100); 41 | configInput(MIXA_CV, "(A) Modulation Level Control Voltage"); 42 | configInput(MODULATOR_A, "(A) Modulator"); 43 | configInput(CARRIER_A, "(A) Carrier"); 44 | configOutput(OUTPUT_A, "(A) Master"); 45 | configInput(MIXB_CV, "(B) Modulation Level Control Voltage"); 46 | configInput(MODULATOR_B, "(B) Modulator"); 47 | configInput(CARRIER_B, "(B) Carrier"); 48 | configOutput(OUTPUT_B, "(B) Master"); 49 | } 50 | 51 | void process(const ProcessArgs& args) override; 52 | 53 | float carA = 0.0f; 54 | float modA = 0.0f; 55 | float carB = 0.0f; 56 | float modB = 0.0f; 57 | 58 | //Json for Panel Theme 59 | json_t *dataToJson() override { 60 | json_t *rootJ = json_object(); 61 | json_object_set_new(rootJ, "Theme", json_integer(Theme)); 62 | return rootJ; 63 | } 64 | void dataFromJson(json_t *rootJ) override { 65 | json_t *ThemeJ = json_object_get(rootJ, "Theme"); 66 | if (ThemeJ) 67 | Theme = json_integer_value(ThemeJ); 68 | } 69 | 70 | }; 71 | 72 | void RingMod::process(const ProcessArgs& args) 73 | { 74 | carA = inputs[CARRIER_A].getVoltage() / 5.0; 75 | modA = inputs[MODULATOR_A].getVoltage() / 5.0; 76 | 77 | float waveA = clamp(params[MIXA].getValue() + inputs[MIXA_CV].getVoltage() / 10.0f, 0.0f, 1.0f); 78 | float ringoutA = carA * modA * 5.0; 79 | float outA = crossfade(inputs[CARRIER_A].getVoltage(), ringoutA, waveA); 80 | 81 | outputs[OUTPUT_A].setVoltage(outA); 82 | 83 | carB = inputs[CARRIER_B].getVoltage() / 5.0; 84 | modB = inputs[MODULATOR_B].getVoltage() / 5.0; 85 | 86 | float waveB = clamp(params[MIXB].getValue() + inputs[MIXB_CV].getVoltage() / 10.0f, 0.0f, 1.0f); 87 | float ringoutB = carB * modB * 5.0; 88 | float outB = crossfade(inputs[CARRIER_B].getVoltage(), ringoutB, waveB); 89 | 90 | outputs[OUTPUT_B].setVoltage(outB); 91 | }; 92 | 93 | struct RMClassicMenu : MenuItem { 94 | RingMod *ringmod; 95 | void onAction(const event::Action &e) override { 96 | ringmod->Theme = 0; 97 | } 98 | void step() override { 99 | rightText = (ringmod->Theme == 0) ? "✔" : ""; 100 | MenuItem::step(); 101 | } 102 | }; 103 | 104 | struct RMNightModeMenu : MenuItem { 105 | RingMod *ringmod; 106 | void onAction(const event::Action &e) override { 107 | ringmod->Theme = 1; 108 | } 109 | void step() override { 110 | rightText = (ringmod->Theme == 1) ? "✔" : ""; 111 | MenuItem::step(); 112 | } 113 | }; 114 | 115 | struct RingModWidget : ModuleWidget { 116 | // Panel Themes 117 | SvgPanel *panelClassic; 118 | SvgPanel *panelNightMode; 119 | 120 | void appendContextMenu(Menu *menu) override { 121 | RingMod *ringmod = dynamic_cast(module); 122 | assert(ringmod); 123 | menu->addChild(construct()); 124 | menu->addChild(construct(&MenuLabel::text, "Theme")); 125 | menu->addChild(construct(&RMClassicMenu::text, "Classic (default)", &RMClassicMenu::ringmod, ringmod)); 126 | menu->addChild(construct(&RMNightModeMenu::text, "Night Mode", &RMNightModeMenu::ringmod, ringmod)); 127 | } 128 | 129 | RingModWidget(RingMod *module); 130 | void step() override; 131 | }; 132 | 133 | RingModWidget::RingModWidget(RingMod *module) { 134 | setModule(module); 135 | box.size = Vec(5 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); 136 | panelClassic = new SvgPanel(); 137 | panelClassic->box.size = box.size; 138 | panelClassic->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Panels/RingMod.svg"))); 139 | panelClassic->visible = true; 140 | addChild(panelClassic); 141 | 142 | panelNightMode = new SvgPanel(); 143 | panelNightMode->box.size = box.size; 144 | panelNightMode->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Panels/RingMod-Dark.svg"))); 145 | panelNightMode->visible = false; 146 | addChild(panelNightMode); 147 | 148 | addChild(createWidget(Vec(0, 0))); 149 | addChild(createWidget(Vec(box.size.x - 15, 0))); 150 | addChild(createWidget(Vec(0, 365))); 151 | addChild(createWidget(Vec(box.size.x - 15, 365))); 152 | 153 | addParam(createParam(Vec(22, 98), module, RingMod::MIXA)); 154 | addInput(createInput(Vec(5, 144), module, RingMod::MIXA_CV)); 155 | addInput(createInput(Vec(45, 67), module, RingMod::MODULATOR_A)); 156 | addInput(createInput(Vec(5, 67), module, RingMod::CARRIER_A)); 157 | addOutput(createOutput(Vec(45, 144), module, RingMod::OUTPUT_A)); 158 | 159 | addParam(createParam(Vec(22, 242), module, RingMod::MIXB)); 160 | addInput(createInput(Vec(5, 288), module, RingMod::MIXB_CV)); 161 | addInput(createInput(Vec(45, 211), module, RingMod::MODULATOR_B)); 162 | addInput(createInput(Vec(5, 211), module, RingMod::CARRIER_B)); 163 | addOutput(createOutput(Vec(45, 288), module, RingMod::OUTPUT_B)); 164 | 165 | }; 166 | 167 | void RingModWidget::step() { 168 | if (module) { 169 | RingMod *ringmod = dynamic_cast(module); 170 | assert(ringmod); 171 | panelClassic->visible = (ringmod->Theme == 0); 172 | panelNightMode->visible = (ringmod->Theme == 1); 173 | } 174 | 175 | ModuleWidget::step(); 176 | } 177 | 178 | Model *modelRingMod = createModel("Ring"); 179 | -------------------------------------------------------------------------------- /res/Port/SilverSixPort.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /res/Button/Easteregg_0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 91 | 96 | 107 | 108 | -------------------------------------------------------------------------------- /res/Button/Easteregg_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 91 | 96 | 107 | 108 | -------------------------------------------------------------------------------- /src/Fade.cpp: -------------------------------------------------------------------------------- 1 | #include "MSM.hpp" 2 | #include "Additional/Resources.hpp" 3 | #include "MSMComponentLibrary.hpp" 4 | 5 | struct Fade : Module { 6 | 7 | enum ParamIds { 8 | CF_A_PARAM, 9 | CF_B_PARAM, 10 | CF_AB_PARAM, 11 | NUM_PARAMS 12 | }; 13 | 14 | enum InputIds { 15 | IN_1_INPUT, 16 | IN_2_INPUT, 17 | IN_3_INPUT, 18 | IN_4_INPUT, 19 | CVA_INPUT, 20 | CVB_INPUT, 21 | CVAB_INPUT, 22 | NUM_INPUTS 23 | }; 24 | 25 | enum OutputIds { 26 | OUT_A_OUTPUT, 27 | OUT_B_OUTPUT, 28 | OUT_AB_OUTPUT, 29 | NUM_OUTPUTS 30 | }; 31 | 32 | enum LightIds { 33 | NUM_LIGHTS 34 | }; 35 | 36 | int Theme = 0; 37 | 38 | Fade() { 39 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); 40 | configParam(Fade::CF_A_PARAM, 0.0, 1.0, 0.5, "A"); 41 | configParam(Fade::CF_B_PARAM, 0.0, 1.0, 0.5, "B"); 42 | configParam(Fade::CF_AB_PARAM, 0.0, 1.0, 0.5, "AB"); 43 | configInput(IN_1_INPUT, "(A) Fader #1"); 44 | configInput(IN_2_INPUT, "(A) Fader #2"); 45 | configInput(IN_3_INPUT, "(B) Fader #3"); 46 | configInput(IN_4_INPUT, "(B) Fader #4"); 47 | configInput(CVA_INPUT, "(A) Fader Control Voltage"); 48 | configInput(CVB_INPUT, "(B) Fader Control Voltage"); 49 | configInput(CVAB_INPUT, "(AB) Fader Control Voltage"); 50 | configOutput(OUT_A_OUTPUT, "(A) Fader"); 51 | configOutput(OUT_B_OUTPUT, "(B) Fader"); 52 | configOutput(OUT_AB_OUTPUT, "(AB) Fader"); 53 | } 54 | 55 | void process(const ProcessArgs& args) override; 56 | 57 | //Json for Panel Theme 58 | json_t *dataToJson() override { 59 | json_t *rootJ = json_object(); 60 | json_object_set_new(rootJ, "Theme", json_integer(Theme)); 61 | return rootJ; 62 | } 63 | void dataFromJson(json_t *rootJ) override { 64 | json_t *ThemeJ = json_object_get(rootJ, "Theme"); 65 | if (ThemeJ) 66 | Theme = json_integer_value(ThemeJ); 67 | } 68 | }; 69 | 70 | void Fade::process(const ProcessArgs& args) 71 | { 72 | double CrossfadeA = clamp(params[CF_A_PARAM].getValue() + inputs[CVA_INPUT].getVoltage() / 10.f, 0.0f, 1.0f); 73 | double IN_1 = inputs[IN_1_INPUT].getVoltage(); 74 | double IN_2 = inputs[IN_2_INPUT].getVoltage(); 75 | double OutA; 76 | 77 | OutA = crossfade(IN_1, IN_2, CrossfadeA); 78 | outputs[OUT_A_OUTPUT].setVoltage(OutA); 79 | 80 | 81 | double CrossfadeB = clamp(params[CF_B_PARAM].getValue() + inputs[CVB_INPUT].getVoltage() / 10.f, 0.0f, 1.0f); 82 | double IN_3 = inputs[IN_3_INPUT].getVoltage(); 83 | double IN_4 = inputs[IN_4_INPUT].getVoltage(); 84 | double OutB; 85 | 86 | OutB = crossfade(IN_3, IN_4, CrossfadeB); 87 | outputs[OUT_B_OUTPUT].setVoltage(OutB); 88 | 89 | 90 | double CrossfadeAB = clamp(params[CF_AB_PARAM].getValue() + inputs[CVAB_INPUT].getVoltage() / 10.f, 0.0f, 1.0f); 91 | double IN_A = OutA; 92 | double IN_B = OutB; 93 | double OutAB; 94 | 95 | OutAB = crossfade(IN_A, IN_B, CrossfadeAB); 96 | outputs[OUT_AB_OUTPUT].setVoltage(OutAB); 97 | 98 | 99 | }; 100 | 101 | struct FadeClassicMenu : MenuItem { 102 | Fade *fade; 103 | void onAction(const event::Action &e) override { 104 | fade->Theme = 0; 105 | } 106 | void step() override { 107 | rightText = (fade->Theme == 0) ? "✔" : ""; 108 | MenuItem::step(); 109 | } 110 | }; 111 | 112 | struct FadekNightModeMenu : MenuItem { 113 | Fade *fade; 114 | void onAction(const event::Action &e) override { 115 | fade->Theme = 1; 116 | } 117 | void step() override { 118 | rightText = (fade->Theme == 1) ? "✔" : ""; 119 | MenuItem::step(); 120 | } 121 | }; 122 | 123 | struct FadeWidget : ModuleWidget { 124 | // Panel Themes 125 | SvgPanel *panelClassic; 126 | SvgPanel *panelNightMode; 127 | 128 | void appendContextMenu(Menu *menu) override { 129 | Fade *fade = dynamic_cast(module); 130 | assert(fade); 131 | 132 | menu->addChild(construct()); 133 | menu->addChild(construct(&MenuLabel::text, "Theme")); 134 | menu->addChild(construct(&FadeClassicMenu::text, "Classic (default)", &FadeClassicMenu::fade, fade)); 135 | menu->addChild(construct(&FadekNightModeMenu::text, "Night Mode", &FadekNightModeMenu::fade, fade)); 136 | } 137 | 138 | FadeWidget(Fade *module); 139 | void step() override; 140 | }; 141 | 142 | FadeWidget::FadeWidget(Fade *module) { 143 | setModule(module); 144 | box.size = Vec(8 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); 145 | panelClassic = new SvgPanel(); 146 | panelClassic->box.size = box.size; 147 | panelClassic->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Panels/Fade.svg"))); 148 | panelClassic->visible = true; 149 | addChild(panelClassic); 150 | 151 | panelNightMode = new SvgPanel(); 152 | panelNightMode->box.size = box.size; 153 | panelNightMode->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Panels/Fade-Dark.svg"))); 154 | panelNightMode->visible = false; 155 | addChild(panelNightMode); 156 | 157 | addChild(createWidget(Vec(15, 0))); 158 | addChild(createWidget(Vec(15, 365))); 159 | addChild(createWidget(Vec(90, 0))); 160 | addChild(createWidget(Vec(90, 365))); 161 | 162 | //Params 163 | addParam(createParam(Vec(10, 65), module, Fade::CF_A_PARAM)); 164 | addParam(createParam(Vec(64, 65), module, Fade::CF_B_PARAM)); 165 | addParam(createParam(Vec(38, 110), module, Fade::CF_AB_PARAM)); 166 | 167 | //Inputs 168 | addInput(createInput(Vec(10, 240), module, Fade::IN_1_INPUT)); 169 | addInput(createInput(Vec(49, 240), module, Fade::IN_2_INPUT)); 170 | addInput(createInput(Vec(10, 280), module, Fade::IN_3_INPUT)); 171 | addInput(createInput(Vec(49, 280), module, Fade::IN_4_INPUT)); 172 | 173 | addInput(createInput(Vec(10, 320), module, Fade::CVA_INPUT)); 174 | addInput(createInput(Vec(49, 320), module, Fade::CVB_INPUT)); 175 | addInput(createInput(Vec(49, 200), module, Fade::CVAB_INPUT)); 176 | 177 | //Outputs 178 | addOutput(createOutput(Vec(88, 240), module, Fade::OUT_A_OUTPUT)); 179 | addOutput(createOutput(Vec(88, 280), module, Fade::OUT_B_OUTPUT)); 180 | addOutput(createOutput(Vec(88, 320), module, Fade::OUT_AB_OUTPUT)); 181 | }; 182 | 183 | void FadeWidget::step() { 184 | if (module) { 185 | Fade *fade = dynamic_cast(module); 186 | assert(fade); 187 | panelClassic->visible = (fade->Theme == 0); 188 | panelNightMode->visible = (fade->Theme == 1); 189 | } 190 | ModuleWidget::step(); 191 | } 192 | 193 | Model *modelFade = createModel("Fade"); 194 | -------------------------------------------------------------------------------- /src/Additional/ExperimentalVCO.hpp: -------------------------------------------------------------------------------- 1 | 2 | extern float grainTableA[2048]; 3 | extern float grainTableB[2048]; 4 | extern float grainTableC[2048]; 5 | extern float grainTableD[2048]; 6 | extern float grainTableE[2048]; 7 | extern float grainTableF[2048]; 8 | extern float grainTableG[2048]; 9 | extern float grainTableH[2048]; 10 | extern float grainTableI[2048]; 11 | extern float grainClarinett[2048]; 12 | extern float grainCello[2048]; 13 | extern float grainViolin[2048]; 14 | extern float grainpluck[2048]; 15 | extern float grainTableJ[2048]; 16 | 17 | template 18 | class MSVCO { 19 | public: 20 | MSVCO() 21 | { 22 | modder1 = 0.0f; 23 | modder2 = 0.0f; 24 | modder3 = 0.0f; 25 | lastSyncValue = 0.0f; 26 | phase = 0.0f; 27 | pitch = 0.0f; 28 | freq = 0.0f; 29 | deltaPhase = 0.0f; 30 | modder1 = 0.0f; 31 | _wdw = 0.0f; 32 | _type = 0; 33 | interp.reset_hist(); 34 | 35 | } 36 | 37 | void setPitch(float _pitchK, float _pitchCV, char _lfomode) { 38 | pitch = _pitchK; 39 | pitch = roundf(pitch); 40 | pitch += _pitchCV; 41 | 42 | switch(_lfomode) { 43 | case 0: 44 | freq = (261.626f * std::pow(2.0f, pitch / 12.0f)) / 100.0f; 45 | break; 46 | case 1: 47 | freq = 261.626f * std::pow(2.0f, pitch / 12.0f); 48 | break; 49 | } 50 | } 51 | 52 | void Window(float _maxWindow) { 53 | _wdw = _maxWindow; 54 | } 55 | 56 | void Mods(float _mod1, float _mod2, float _mod3) { 57 | modder1 = _mod1; 58 | modder2 = _mod2; 59 | modder3 = _mod3; 60 | } 61 | 62 | void TYPE(float type) { 63 | _type = type; 64 | } 65 | 66 | void outEnabled(bool _enable) { 67 | _outEnabled = _enable; 68 | } 69 | 70 | void process(float deltaTime, float syncValue, bool sync) { 71 | syncEnabled = sync; 72 | // Advance phase 73 | deltaPhase = clamp(freq * deltaTime, 1e-6, 0.5f) * (1.0f / OVERSAMPLE); 74 | // Detect sync 75 | int syncIndex = -1.0f; 76 | float syncCrossing = 0.0f; 77 | if (syncEnabled) { 78 | syncValue -= 0.01; 79 | if (syncValue > 0.0f && lastSyncValue <= 0.0f) { 80 | float deltaSync = syncValue - lastSyncValue; 81 | syncCrossing = 1.0f - syncValue / deltaSync; 82 | syncCrossing *= OVERSAMPLE; 83 | syncIndex = (int)syncCrossing; 84 | syncCrossing -= syncIndex; 85 | } 86 | lastSyncValue = syncValue; 87 | } 88 | 89 | if (syncDirection) 90 | deltaPhase *= -1.0f; 91 | 92 | for (int i = 0; i < OVERSAMPLE; i++) { 93 | if (syncIndex == i) { 94 | phase = 0.0f; 95 | } 96 | 97 | if(_outEnabled) { 98 | for(int y = 0; y < modder1; y++) { 99 | switch(_type) { 100 | case 0: 101 | a[i] = interp.process4x((cosf(yamt * M_PI * phase / modder1) + modder2) * clamp(fastSin((yamt * M_PI) * fastSin(x * interp.process4xP(grainTableA, phase * _wdw, oo))) / modder3, rl, rh), out); 102 | break; 103 | case 1: 104 | a[i] = interp.process4x((fastSin(yamt * M_PI * phase / modder1) + modder2) * clamp(cosf((yamt * M_PI) * cosf(x * interp.process4xP(grainTableB, phase * _wdw, oo))) / modder3, rl, rh), out); 105 | break; 106 | case 2: 107 | a[i] = interp.process8x(clamp(fastSin(fastSin(((yamt / modder3) * M_PI / modder2) * (x * interpolateLinear(grainTableC, phase * _wdw)) / modder1)), rl, rh), out); 108 | break; 109 | case 3: 110 | a[i] = interp.process8x(clamp(fastSin((yamt * M_PI / modder2) * (x * interpolateLinear(grainTableD, phase * _wdw)) / modder1), rl, rh) / modder3, out); 111 | break; 112 | case 4: 113 | a[i] = interp.process8x(clamp(fastSin((yamt * (M_PI / modder3) / modder2) * (sqramt * interpolateLinear(grainTableE, phase * _wdw)) / modder1), rl, rh), out); 114 | break; 115 | case 5: 116 | a[i] = interp.process8x(clamp(fastSin((yamt * M_PI / modder2) * (x * interpolateLinear(grainTableF, phase * _wdw)) / modder1), rl, rh) / modder3, out); 117 | break; 118 | case 6: 119 | a[i] = interp.process8x(clamp(fastSin((yamt * M_PI / modder2) * (x * interpolateLinear(grainTableG, phase * _wdw)) / modder1), rl, rh) / modder3, out); 120 | break; 121 | case 7: 122 | a[i] = interp.process8x(clamp(fastSin((z * M_PI / modder2) * (x * interpolateLinear(grainTableH, phase * _wdw)) / modder1), rl, rh) / modder3, out); 123 | break; 124 | case 8: 125 | a[i] = interp.process8x(clamp(fastSin(((yamt / modder3) * M_PI / modder2) * (x * interpolateLinear(grainTableI, phase * _wdw)) / modder1), rl, rh), out); 126 | break; 127 | case 9: 128 | a[i] = interp.process8x(clamp(fastSin((yamt * M_PI / modder2) * (x * interpolateLinear(grainClarinett, phase * _wdw)) / modder1), rl, rh) / modder3, out); 129 | break; 130 | case 10: 131 | a[i] = interp.process8x(clamp(fastSin((yamt * M_PI / modder2) * (x * interpolateLinear(grainCello, phase * _wdw)) / modder1), rl, rh) / modder3, out); 132 | break; 133 | case 11: 134 | a[i] = interp.process8x(clamp(fastSin((z * M_PI / modder2) * (x * interpolateLinear(grainViolin, phase * _wdw)) / modder1), rl, rh) / modder3, out); 135 | break; 136 | case 12: 137 | a[i] = interp.process8x(clamp(fastSin((yamt * M_PI / modder2) * (x * interpolateLinear(grainpluck, phase * _wdw)) / modder1), rl, rh) / modder3, out); 138 | break; 139 | case 13: 140 | a[i] = interp.process8x(clamp(fastSin((yamt * M_PI / modder2) * (x * interpolateLinear(grainTableJ, phase * _wdw)) / modder1), rl, rh) / modder3, out); 141 | break; 142 | } 143 | } 144 | } 145 | } 146 | 147 | 148 | // Advance phase 149 | phase += deltaPhase; 150 | while (phase > 1.0f) { 151 | phase -= 1.0f; 152 | } 153 | while (phase < 0) { 154 | phase += 1.0f; 155 | } 156 | } 157 | 158 | inline float getOutput() { 159 | return OutDecimator.process(out); 160 | } 161 | 162 | private: 163 | 164 | float a[OVERSAMPLE] = {}; 165 | float out[OVERSAMPLE] = {}; 166 | float oo[OVERSAMPLE] = {}; 167 | interpolator_linear interp; 168 | 169 | dsp::Decimator OutDecimator; 170 | 171 | float lastSyncValue; 172 | float phase; 173 | float freq; 174 | float pitch; 175 | float deltaPhase; 176 | 177 | char _type; 178 | float _wdw; 179 | float modder1; 180 | float modder2; 181 | float modder3; 182 | 183 | bool syncEnabled = false; 184 | bool syncDirection = false; 185 | 186 | bool _outEnabled = false; 187 | 188 | const float x = 1.25f; 189 | const float yamt = 2.0f; 190 | const float z = 1.0f; 191 | const float rl = -6.0f; 192 | const float rh = 6.0f; 193 | const float sqramt = 2.5f; 194 | }; 195 | -------------------------------------------------------------------------------- /res/Port/SilverSixPortA.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /res/Port/SilverSixPortB.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /res/Port/SilverSixPortC.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /res/Port/SilverSixPortD.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /res/Port/SilverSixPortE.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "slug": "MSM", 3 | "name": "MSM", 4 | "version": "2.0.3", 5 | "license": "MIT", 6 | "brand": "MSM", 7 | "author": "Phal-anx, Netboy3", 8 | "authorEmail": "", 9 | "authorUrl": "", 10 | "pluginUrl": "https://github.com/netboy3/MSM-vcvrack-plugin", 11 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#readme", 12 | "sourceUrl": "https://github.com/netboy3/MSM-vcvrack-plugin", 13 | "donateUrl": "", 14 | "changelogUrl": "", 15 | "modules": [ 16 | { 17 | "slug": "ADSR", 18 | "name": "ADSR", 19 | "description": "ADSR Envelope Generator with modifiable curve shapes", 20 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#ADSR", 21 | "tags": [ 22 | "Envelope generator" 23 | ] 24 | }, 25 | { 26 | "slug": "Bitcrusher", 27 | "name": "Bitcrusher", 28 | "description": "Distortion effect by changing the sample's bit length and sample rate", 29 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#Bitcrusher", 30 | "tags": [ 31 | "Effect" 32 | ] 33 | }, 34 | { 35 | "slug": "BlankPanel", 36 | "name": "Blank Panel", 37 | "description": "Blank panel utilizing the MSM logo", 38 | "tags": [ 39 | "Blank" 40 | ] 41 | }, 42 | { 43 | "slug": "DualDelay", 44 | "name": "Dual Delay", 45 | "description": "Two independent delay lines with low/hi pass filters", 46 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#DualDelay", 47 | "tags": [ 48 | "Delay" 49 | ] 50 | }, 51 | { 52 | "slug": "TreasureVCO", 53 | "name": "Treasure VCO", 54 | "description": "Voltage Controlled Oscillator with complex waveform modulation", 55 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#TreasureVCO", 56 | "tags": [ 57 | "Oscillator" 58 | ] 59 | }, 60 | { 61 | "slug": "Fade", 62 | "name": "Fade", 63 | "description": "Triple fader module", 64 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#Fade", 65 | "tags": [ 66 | "Mixer" 67 | ] 68 | }, 69 | { 70 | "slug": "MLFO", 71 | "name": "MLFO", 72 | "description": "Low frequency oscillator with multiple waveform types", 73 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#MLFO", 74 | "tags": [ 75 | "LFO" 76 | ] 77 | }, 78 | { 79 | "slug": "Morph", 80 | "name": "Morph", 81 | "description": "Variable input morpher with 8 inputs", 82 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#Morph", 83 | "tags": [ 84 | "Effect" 85 | ] 86 | }, 87 | { 88 | "slug": "Mult", 89 | "name": "Mult", 90 | "description": "Triple independent add/subtract split utility", 91 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#Mult", 92 | "tags": [ 93 | "Multiple" 94 | ] 95 | }, 96 | { 97 | "slug": "Noise", 98 | "name": "Noise", 99 | "description": "Noise generator with white, pink, brown and variable colored noise outputs", 100 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#Noise", 101 | "tags": [ 102 | "Noise" 103 | ] 104 | }, 105 | { 106 | "slug": "Phaser", 107 | "name": "Phaser", 108 | "description": "2-20 stage phaser distortion effect", 109 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#Phaser", 110 | "tags": [ 111 | "Effect" 112 | ] 113 | }, 114 | { 115 | "slug": "RandomSource", 116 | "name": "Random Source", 117 | "description": "Random source that produces variable shape staircase waveforms", 118 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#RandomSource", 119 | "tags": [ 120 | "Sample and hold", 121 | "Random" 122 | ] 123 | }, 124 | { 125 | "slug": "Ring", 126 | "name": "Ring", 127 | "description": "Dual ring modulator", 128 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#Ring", 129 | "tags": [ 130 | "Effect" 131 | ] 132 | }, 133 | { 134 | "slug": "SimpleSlider", 135 | "name": "Simple Slider", 136 | "description": "Dual input VCA/Crossfader slider", 137 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#SimpleSlider", 138 | "tags": [ 139 | "Mixer", 140 | "Amplifier" 141 | ] 142 | }, 143 | { 144 | "slug": "CrazyMult", 145 | "name": "CrazyMult", 146 | "description": "8 way dual output signal router", 147 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#CrazyMult", 148 | "tags": [ 149 | "Multiple" 150 | ] 151 | }, 152 | { 153 | "slug": "PAN-VCA", 154 | "name": "PAN-VCA", 155 | "description": "Stereo VCA/PAN combination", 156 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#PANVCA", 157 | "tags": [ 158 | "Amplifier", 159 | "Panning" 160 | ] 161 | }, 162 | { 163 | "slug": "OSCiX", 164 | "name": "OSCiX", 165 | "description": "Advanced dual oscillator/LFO with a built-in wave folder", 166 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#OSCiX", 167 | "tags": [ 168 | "Oscillator" 169 | ] 170 | }, 171 | { 172 | "slug": "Rogue", 173 | "name": "Rogue", 174 | "description": "OSCiX's little brother. Advanced oscillator/LFO with multiple waveform type outputs", 175 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#Rogue", 176 | "tags": [ 177 | "Oscillator" 178 | ] 179 | }, 180 | { 181 | "slug": "Wavefolder", 182 | "name": "Wavefolder", 183 | "description": "Advanced wave folder with variable symmetry controls", 184 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#Wavefolder", 185 | "tags": [ 186 | "Waveshaper" 187 | ] 188 | }, 189 | { 190 | "slug": "WaveShaper", 191 | "name": "WaveShaper", 192 | "description": "Unique wave folder with multiple folding algorithms", 193 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#WaveShaper", 194 | "tags": [ 195 | "Waveshaper" 196 | ] 197 | }, 198 | { 199 | "slug": "XSEQ", 200 | "name": "XSEQ", 201 | "description": "Boolean logic sequencer", 202 | "manualUrl": "https://github.com/netboy3/MSM-vcvrack-plugin#XSEQ", 203 | "tags": [ 204 | "Logic", 205 | "Random" 206 | ] 207 | } 208 | ] 209 | } 210 | --------------------------------------------------------------------------------