From ee6b4cd86e543ba0a5153797f1b5ab723745b460 Mon Sep 17 00:00:00 2001 From: sickozell Date: Thu, 17 Oct 2024 16:52:08 +0200 Subject: [PATCH] update to v2.6.16 --- README.md | 48 +- changelog.md | 8 +- extra/crossCompiler.md | 6 +- plugin.json | 10 +- res/KeySampler.svg | 2148 +++++++++++++++++++ src/Clocker.cpp | 36 +- src/Clocker2.cpp | 48 +- src/KeySampler.cpp | 4488 ++++++++++++++++++++++++++++++++++++++++ src/notes.hpp | 530 +++++ src/plugin.cpp | 1 + src/plugin.hpp | 1 + 11 files changed, 7274 insertions(+), 50 deletions(-) create mode 100644 res/KeySampler.svg create mode 100644 src/KeySampler.cpp create mode 100644 src/notes.hpp diff --git a/README.md b/README.md index 4c181c4..2346624 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# SickoCV v2.6.15 +# SickoCV v2.6.16 VCV Rack plugin modules -![SickoCV modules 2 6 14](https://github.com/sickozell/SickoCV/assets/80784296/8c5fc876-a3a8-4d59-9357-6a5ffff85d7f) +![SickoCV modules 2 6 16](https://github.com/user-attachments/assets/9124dcb4-4ded-4102-ab24-4c131dac600a) ## table of contents - [Common modules behavior](#common-modules-behavior) @@ -19,6 +19,7 @@ VCV Rack plugin modules - [drumPlayer / drumPlayer+ / drumPlayerXtra](#drumplayer--drumplayer--drumplayerxtra) - [enver](#enver) - [holder / holder Compact / holder8](#holder--holder-compact--holder8) +- [keySampler](#keysampler) - [modulator / modulator7 / modulator7 Compact](#modulator--modulator7--modulator7-compact) - [multiRouter / multiSwitcher](#multirouter--multiswitcher) - [parking](#parking) @@ -537,6 +538,49 @@ Comparison: **Noise Generator preset** This function sets the module to Track & Hold mode, sample on HIGH gate, scale on 100% and offset to 0v, just to output white noise if trig/gate input is not connected or not triggered. +[back to top](#table-of-contents) + +## keySampler +### Keyboard controlled sampler + +#### - DESCRIPTION +- up to 8 mono/stereo sample player/recorder +- routable samples to 8 separate stereo outputs +- keyboard controlled player with velocity and pitch bend controls +- programmable key zones for each sample with midi learn +- mono/stereo samples and 1-cycle waveforms player +- gate or triggered play mode +- envelope generator with adjustable shape, loop, reverse, pingpong +- anti-aliasing filter, phase-scan feature +- 90s style cyclic time-stretch feature + +![keysampler](https://github.com/user-attachments/assets/aeb16f51-e9c9-46d9-9b37-5c4d11eb5f4f) + +#### - INSTRUCTIONS + +COMING SOON + +Some hints: + +Every sample is stored in one of the 8 available slots and can be individually routed to one of the 8 outputs. +Multiple slots can be routed to the same output. +Each slot has its individual settings. + +PB button enables pitchbending on the desired sample. Pitchbend range can be set in the right-click menu. + +The 'Quantize incoming v/Oct' option in the right-click menu avoids incorrect pitches when using variable or less than perfect v/oct signals. + +Key zones programming: +Every zone is composed by lower, upper and reference notes. The reference note is the one that is supposed to be the original recorded pitch. +Zones of just one note are allowed, usually for percussions or not pitchable samples. + +Press the yellow KEY SELECT button to choose the parameter to modify, then adjust the KEY knob to set the interested note or enter midi learn. + +Midi Learn is activated by holding the KEY SELECT button for about 1 second, the yellow led will fade in and out waiting the key to be pressed. Then the yellow led will flash for a while. Midi learn can be aborted by clicking the KEY SELECT button again. + + + + [back to top](#table-of-contents) ## modulator / modulator7 / modulator7 Compact diff --git a/changelog.md b/changelog.md index aeed0d8..595777b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,8 +1,12 @@ -### 2.6.15 (2024-08-17) +### 2.6.16 (2024-10-17) +- added 'keySampler' module. +- added 'x48' and '/48' multipliyng options on 'clocker' and 'clocker2' modules + +### 2.6.15 (2024-08-07) - enver: changed envelope shapes order and increased curve resolution. - polyMuter modules: added 'Shrink channels' and 'Show OUT channels' options in the right-click menu. - polyMuter8, polyMuter16: added 'exclude -10v channels too' option in addition to channel shrinking. -- sickoSampler / sickoSampler2: fixed some naming bugs when samples are saved. +- sickoSampler / sickoSampler2: fixed a bug that doesn't store 'wav' extension when saving. ### 2.6.14 (2024-06-23) - added 'slewer' module. diff --git a/extra/crossCompiler.md b/extra/crossCompiler.md index e3c0d7b..42408a5 100644 --- a/extra/crossCompiler.md +++ b/extra/crossCompiler.md @@ -1,11 +1,11 @@ -git tag v2.6.15-beta2 -m "create v2.6.15-beta2" +git tag v2.6.16-beta2 -m "create v2.6.16-beta2" git push origin --tags delete local tag -git tag -d v2.6.15-beta +git tag -d v2.6.16-beta delete remote -git push --delete origin v2.6.15-beta +git push --delete origin v2.6.16-beta ### How to build a VCVRack plugin with Github Action diff --git a/plugin.json b/plugin.json index 3bd8a66..bd64227 100644 --- a/plugin.json +++ b/plugin.json @@ -1,7 +1,7 @@ { "slug": "SickoCV", "name": "SickoCV", - "version": "2.6.15", + "version": "2.6.16", "license": "GPL-3.0-or-later", "brand": "Sickozell", "author": "Sickozell", @@ -231,6 +231,14 @@ "Polyphonic" ] }, + { + "slug": "KeySampler", + "name": "keySampler", + "description": "Keyboard controlled sampler", + "tags": [ + "Sampler" + ] + }, { "slug": "Modulator", "name": "modulator", diff --git a/res/KeySampler.svg b/res/KeySampler.svg new file mode 100644 index 0000000..141ea03 --- /dev/null +++ b/res/KeySampler.svg @@ -0,0 +1,2148 @@ + + + + diff --git a/src/Clocker.cpp b/src/Clocker.cpp index 8e3a3bd..de5e28b 100644 --- a/src/Clocker.cpp +++ b/src/Clocker.cpp @@ -37,8 +37,8 @@ struct tpSignature : ParamQuantity { struct tpDivMult : ParamQuantity { std::string getDisplayValueString() override { - const std::string valueDisplay[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; + const std::string valueDisplay[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; return valueDisplay[int(getValue())]; } }; @@ -102,11 +102,11 @@ struct Clocker : Module { const int beatMaxPerBar[17] = {2, 3, 4, 5, 6, 7, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; int currentBeatMaxPerBar = 4; - const std::string divMultDisplay[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; + const std::string divMultDisplay[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; - const float divMult[43] = {256, 128, 64, 32, 24, 17, 16, 15, 14, 13, 12, 11, 10, 9 , 8, 7, 6, 5, 4, 3, 2, 1, - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 24, 32, 64, 128, 256}; + const float divMult[45] = {256, 128, 64, 48, 32, 24, 17, 16, 15, 14, 13, 12, 11, 10, 9 , 8, 7, 6, 5, 4, 3, 2, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 24, 32, 48, 64, 128, 256}; //************************************************************** // @@ -1527,9 +1527,9 @@ struct ClockerDisplayDiv1 : TransparentWidget { } }; - const std::string menuNames[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; - for (int i = 0; i < 43; i++) { + const std::string menuNames[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; + for (int i = 0; i < 45; i++) { ThisItem* thisItem = createMenuItem(menuNames[i]); thisItem->rightText = CHECKMARK(int(module->params[module->DIVMULT_KNOB_PARAM+0].getValue()) == i); thisItem->module = module; @@ -1595,9 +1595,9 @@ struct ClockerDisplayDiv2 : TransparentWidget { } }; - const std::string menuNames[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; - for (int i = 0; i < 43; i++) { + const std::string menuNames[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; + for (int i = 0; i < 45; i++) { ThisItem* thisItem = createMenuItem(menuNames[i]); thisItem->rightText = CHECKMARK(int(module->params[module->DIVMULT_KNOB_PARAM+1].getValue()) == i); thisItem->module = module; @@ -1663,9 +1663,9 @@ struct ClockerDisplayDiv3 : TransparentWidget { } }; - const std::string menuNames[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; - for (int i = 0; i < 43; i++) { + const std::string menuNames[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; + for (int i = 0; i < 45; i++) { ThisItem* thisItem = createMenuItem(menuNames[i]); thisItem->rightText = CHECKMARK(int(module->params[module->DIVMULT_KNOB_PARAM+2].getValue()) == i); thisItem->module = module; @@ -1731,9 +1731,9 @@ struct ClockerDisplayDiv4 : TransparentWidget { } }; - const std::string menuNames[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; - for (int i = 0; i < 43; i++) { + const std::string menuNames[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; + for (int i = 0; i < 45; i++) { ThisItem* thisItem = createMenuItem(menuNames[i]); thisItem->rightText = CHECKMARK(int(module->params[module->DIVMULT_KNOB_PARAM+3].getValue()) == i); thisItem->module = module; diff --git a/src/Clocker2.cpp b/src/Clocker2.cpp index 0a408e7..3da6716 100644 --- a/src/Clocker2.cpp +++ b/src/Clocker2.cpp @@ -30,8 +30,8 @@ using namespace std; struct tpDivMult : ParamQuantity { std::string getDisplayValueString() override { - const std::string valueDisplay[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; + const std::string valueDisplay[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; return valueDisplay[int(getValue())]; } }; @@ -83,11 +83,11 @@ struct Clocker2 : Module { //************************************************************** // - const std::string divMultDisplay[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; + const std::string divMultDisplay[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; - const float divMult[43] = {256, 128, 64, 32, 24, 17, 16, 15, 14, 13, 12, 11, 10, 9 , 8, 7, 6, 5, 4, 3, 2, 1, - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 24, 32, 64, 128, 256}; + const float divMult[45] = {256, 128, 64, 48, 32, 24, 17, 16, 15, 14, 13, 12, 11, 10, 9 , 8, 7, 6, 5, 4, 3, 2, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 24, 32, 48, 64, 128, 256}; //************************************************************** // @@ -1022,9 +1022,9 @@ struct Clocker2DisplayDiv1 : TransparentWidget { } }; - const std::string menuNames[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; - for (int i = 0; i < 43; i++) { + const std::string menuNames[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; + for (int i = 0; i < 45; i++) { ThisItem* thisItem = createMenuItem(menuNames[i]); thisItem->rightText = CHECKMARK(int(module->params[module->DIVMULT_KNOB_PARAM+0].getValue()) == i); thisItem->module = module; @@ -1088,9 +1088,9 @@ struct Clocker2DisplayDiv2 : TransparentWidget { } }; - const std::string menuNames[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; - for (int i = 0; i < 43; i++) { + const std::string menuNames[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; + for (int i = 0; i < 45; i++) { ThisItem* thisItem = createMenuItem(menuNames[i]); thisItem->rightText = CHECKMARK(int(module->params[module->DIVMULT_KNOB_PARAM+1].getValue()) == i); thisItem->module = module; @@ -1154,9 +1154,9 @@ struct Clocker2DisplayDiv3 : TransparentWidget { } }; - const std::string menuNames[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; - for (int i = 0; i < 43; i++) { + const std::string menuNames[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; + for (int i = 0; i < 45; i++) { ThisItem* thisItem = createMenuItem(menuNames[i]); thisItem->rightText = CHECKMARK(int(module->params[module->DIVMULT_KNOB_PARAM+2].getValue()) == i); thisItem->module = module; @@ -1220,9 +1220,9 @@ struct Clocker2DisplayDiv4 : TransparentWidget { } }; - const std::string menuNames[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; - for (int i = 0; i < 43; i++) { + const std::string menuNames[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; + for (int i = 0; i < 45; i++) { ThisItem* thisItem = createMenuItem(menuNames[i]); thisItem->rightText = CHECKMARK(int(module->params[module->DIVMULT_KNOB_PARAM+3].getValue()) == i); thisItem->module = module; @@ -1286,9 +1286,9 @@ struct Clocker2DisplayDiv5 : TransparentWidget { } }; - const std::string menuNames[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; - for (int i = 0; i < 43; i++) { + const std::string menuNames[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; + for (int i = 0; i < 45; i++) { ThisItem* thisItem = createMenuItem(menuNames[i]); thisItem->rightText = CHECKMARK(int(module->params[module->DIVMULT_KNOB_PARAM+4].getValue()) == i); thisItem->module = module; @@ -1352,9 +1352,9 @@ struct Clocker2DisplayDiv6 : TransparentWidget { } }; - const std::string menuNames[43] = {"/256", "/128", "/64", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", - "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x64", "x128", "x256"}; - for (int i = 0; i < 43; i++) { + const std::string menuNames[45] = {"/256", "/128", "/64", "/48", "/32", "/24", "/17", "/16", "/15", "/14", "/13", "/12", "/11", "/10", "/9", "/8", "/7", "/6", "/5", "/4", "/3", "/2", "x1", + "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x24", "x32", "x48", "x64", "x128", "x256"}; + for (int i = 0; i < 45; i++) { ThisItem* thisItem = createMenuItem(menuNames[i]); thisItem->rightText = CHECKMARK(int(module->params[module->DIVMULT_KNOB_PARAM+5].getValue()) == i); thisItem->module = module; diff --git a/src/KeySampler.cpp b/src/KeySampler.cpp new file mode 100644 index 0000000..5202d13 --- /dev/null +++ b/src/KeySampler.cpp @@ -0,0 +1,4488 @@ +#define COLOR_LCD_RED 0xff, 0x44, 0x44, 0xff +#define COLOR_LCD_GREEN 0x33, 0xff, 0x33, 0xff +#define COLOR_LCD_BLUE 0xca, 0xca, 0xff, 0xff +#define COLOR_LCD_BLACK 0x00, 0x00, 0x00, 0xff + +#define LEFT 0 +#define RIGHT 1 +#define STOP_STAGE 0 +#define ATTACK_STAGE 1 +#define DECAY_STAGE 2 +#define SUSTAIN_STAGE 3 +#define RELEASE_STAGE 4 +#define GATE_MODE 0 +#define TRIG_MODE 1 +#define START_STOP 0 +#define START_RESTART 1 +#define MONOPHONIC 0 +#define POLYPHONIC 1 +#define SAVE_FULL 0 +#define SAVE_CUE 1 +#define SAVE_LOOP 2 +#define NO_FADE 0 +#define CROSS_FADE 1 +#define FADE_OUT 2 +#define PINGPONG_FADE 3 +#define FORWARD 0 +#define REVERSE 1 + +#define LOKEY 0 +#define HIKEY 1 +#define REFKEY 2 + +#define recOutChan 0 +#define sampleCoeff 2 +#define xWav 5 +#define wavDisp 138 +#define yLineUp 13 +#define yLineDn 41 +#define yZeroLine 27 +#define yInfo 10.8 +#define yInfo2 50 +#define xSlot 6 +#define xMagg 12 +#define xRoute 18 +#define xTitle 27 +#define maxTitleChars 18 +#define xTime 118 +#define xCh 6 +#define xToSave 24 +#define xLoVal 33 +#define xHiVal 70 +#define xRfVal 107 +#define xKeyL 32 +#define xKeyH 69 +#define xKeyR 106 +#define yKey 41.7 +#define keyW 36 +#define keyH 9 + +#include "plugin.hpp" +#include "osdialog.h" +//#define DR_WAV_IMPLEMENTATION +#include "dr_wav.h" +#include +#include "cmath" +#include +#include +#include +#include + +using namespace std; + +struct KeySampler : Module { + + #include "notes.hpp" + #include "shapes.hpp" + + enum ParamIds { + GAIN_PARAM, + REC_PARAM, + MONITOR_SWITCH, + SLOT_SELECTOR_PARAM, + RANGE_PARAM, + RANGE_SELECTOR_PARAM, + OUT_SELECTOR_PARAM, + CUESTART_PARAM, + CUEEND_PARAM, + LOOPSTART_PARAM, + LOOPEND_PARAM, + REV_PARAM, + XFADE_PARAM, + LOOP_PARAM, + PINGPONG_PARAM, + TRIGGATEMODE_SWITCH, + TRIGMODE_SWITCH, + PB_SWITCH, + PHASESCAN_SWITCH, + ATTACK_PARAM, + DECAY_PARAM, + SUSTAIN_PARAM, + RELEASE_PARAM, + CURVE_PARAM, + STRETCH_PARAM, + STR_SIZE_PARAM, + VOL_PARAM, + PREVSAMPLE_PARAM, + NEXTSAMPLE_PARAM, + NUM_PARAMS + }; + enum InputIds { + ENUMS(IN_INPUT, 2), + GATE_INPUT, + VO_INPUT, + VEL_INPUT, + PB_INPUT, + NUM_INPUTS + }; + enum OutputIds { + ENUMS(LEFT_OUTPUT, 8), + ENUMS(RIGHT_OUTPUT, 8), + NUM_OUTPUTS + }; + enum LightIds { + REC_LIGHT, + MONITOR_LIGHT, + RANGE_SELECTOR_LIGHT, + LOOP_LIGHT, + REV_LIGHT, + PINGPONG_LIGHT, + TRIGMODE_LIGHT, + PB_LIGHT, + PHASESCAN_LIGHT, + NUM_LIGHTS + }; + + int currSlot = 0; + int prevSlot = -1; + + unsigned int fileChannels[8]; + unsigned int channels[8]; + unsigned int vcvSampleRate = APP->engine->getSampleRate(); + unsigned int sampleRate = vcvSampleRate * 2; + drwav_uint64 totalSampleC[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + drwav_uint64 totalSamples[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + const unsigned int minSamplesToLoad = 124; + + vector playBuffer[8][2][2]; + vector tempBuffer[2]; + + vector displayBuff[8]; + int currentDisplay = 0; + int displaySlot = 0; + float voctDisplay = 100.f; + bool velConnected = false; + + float velKey = 0; + float vOctKey = 0; + int vOctNote = 0; + int vOctDiff = 0; + float vOctDiffFloat = 0; + + bool fileLoaded[8] = {false, false, false, false, false, false, false, false}; + + bool play[8][16] = {{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + }; + + double samplePos[8][16] = {{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0} + }; + double distancePos[8][16] = {{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0} + }; + + double cueStartPos[8]; + double cueEndPos[8]; + double loopStartPos[8]; + double loopEndPos[8]; + + float knobCueStartPos[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + float knobCueEndPos[8] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}; + float prevKnobCueStartPos[8] = {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f}; + float prevKnobCueEndPos[8] = {2.f, 2.f, 2.f, 2.f, 2.f, 2.f, 2.f, 2.f}; + + float knobLoopStartPos[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + float knobLoopEndPos[8] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}; + float prevKnobLoopStartPos[8] = {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f}; + float prevKnobLoopEndPos[8] = {2.f, 2.f, 2.f, 2.f, 2.f, 2.f, 2.f, 2.f}; + + float voct[8][16] = {{0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f} + }; + float prevVoctDiffFloat[8][16] = {{11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f}, + {11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f}, + {11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f}, + {11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f}, + {11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f}, + {11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f}, + {11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f}, + {11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f, 11.f} + }; + + + float speedVoct[8][16]; + + bool searchingCueStartPhase[8] = {false, false, false, false, false, false, false, false}; + bool searchingCueEndPhase[8] = {false, false, false, false, false, false, false, false}; + double scanCueStartSample[8]; + double scanCueEndSample[8]; + + bool searchingLoopStartPhase[8] = {false, false, false, false, false, false, false, false}; + bool searchingLoopEndPhase[8] = {false, false, false, false, false, false, false, false}; + double scanLoopStartSample[8]; + double scanLoopEndSample[8]; + + double currSampleWeight[8][16] = {{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0} + }; + + std::string storedPath[8] = {"", "", "", "", "", "", "", ""}; + std::string fileDescription[8] = {"--none--", "--none--", "--none--", "--none--", "--none--", "--none--", "--none--", "--none--"}; + std::string fileDisplay[8] = {"", "", "", "", "", "", "", ""}; + std::string channelsDisplay[8] = {"", "", "", "", "", "", "", ""}; + std::string timeDisplay[8] = {"", "", "", "", "", "", "", ""}; + std::string recTimeDisplay[8] = {"", "", "", "", "", "", "", ""}; + std::string samplerateDisplay[8] = {"", "", "", "", "", "", "", ""}; + + std::string userFolder = ""; + std::string currentFolder = ""; + vector currentFolderV; + int currentFile = 0; + + bool rootFound = false; + bool fileFound[8] = {false, false, false, false, false, false, false, false}; + + std::string tempDir = ""; + vector> folderTreeData; + vector> folderTreeDisplay; + vector tempTreeData; + vector tempTreeDisplay; + + int seconds; + int minutes; + int recSeconds; + int recMinutes; + + drwav_uint64 recSamples = 0; + + float trigValue[8][16] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + + float prevTrigValue[8][16] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + + + bool trimOnSave = false; + int antiAlias = 1; + bool quantize = false; + + bool prevPhaseScan[8] = {false, false, false, false, false, false, false, false}; + + bool firstLoad[8] = {true, true, true, true, true, true, true, true}; + bool resetCursorsOnLoad = true; + bool disableNav = false; + bool saveOversampled = false; + bool autoMonOff = true; + + bool sampleInPatch = true; + + bool loadFromPatch[8] = {false, false, false, false, false, false, false, false}; + bool restoreLoadFromPatch[8] = {false, false, false, false, false, false, false, false}; + + float fadeCoeff[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + int fadingType[8][16] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + float fadingValue[8][16] = {{1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f} + }; + + double fadedPosition[8][16] = {{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0} + }; + double fadeSamples[8] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + + float prevXfade[8] = {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f}; + + float currentOutput = 0.f; + float currentOutputR = 0.f; + float sumOutput[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + float sumOutputR[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + + /* + std::string debugDisplay[16] = {"X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X", "X"}; + std::string debugDisplay2 = "X"; + std::string debugDisplay3 = "X"; + std::string debugDisplay4 = "X"; + int debugInt = 0; + bool debugBool = false; + */ + + double a0, a1, a2, b1, b2, z1, z2, z1r, z2r; + + float attackValue[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + float decayValue[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + float sustainValue[8] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}; + float releaseValue[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + + bool loop[8] = {false, false, false, false, false, false, false, false}; + float xFade[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + bool pingpong[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + bool reverseStart[8] = {false, false, false, false, false, false, false, false}; + int trigMode[8] = {0, 0, 0, 0, 0, 0, 0 ,0}; + int trigType[8] = {0, 0, 0, 0, 0, 0, 0 ,0}; + bool allowPB[8] = {false, false, false, false, false, false, false, false}; + bool phaseScan[8] = {true, true, true, true, true, true, true, true}; + float attackKnob[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + float decayKnob[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + float releaseKnob[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + float shape[8] = {0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f}; + float stretchKnob[8] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}; + float cycleKnob[8] = {48.f, 48.f, 48.f, 48.f, 48.f, 48.f, 48.f, 48.f}; + float volumeValue[8] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}; + + int refKey[8][3] = {{0, 127, 72}, + {0, 127, 72}, + {0, 127, 72}, + {0, 127, 72}, + {0, 127, 72}, + {0, 127, 72}, + {0, 127, 72}, + {0, 127, 72}}; + + int selectedRange = 0; + int outRoute[8] = {0, 1, 2, 3, 4, 5, 6, 7}; + + float pitchBend = 0.f; + float pbValue = 0.f; + const float pbRangeTable[15] = {60.f, 30.f, 20.f, 15.f, 12.f, 10.f, 8.571428571428571f, 7.5f, 6.666666666666667f, 6.f, 5.454545454545454f, 5.f, 2.5f, 1.666666666666667f, 1.25f}; + + int pbRange = 1; + + bool pbConnected = false; + + float prevAttackKnob[8] = {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f}; + float prevDecayKnob[8] = {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f}; + float prevReleaseKnob[8] = {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f}; + + float trigRange = 0; + float prevTrigRange = 0; + + int stage[8][16] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + float stageLevel[8][16] = {{0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f} + }; + + float env[8][16] = {{0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f} + }; + + float refValue[8][16] = {{0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f} + }; + + float deltaValue[8][16] = {{1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f} + }; + + float slopeCorr[8][16] = {{1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}, + {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f} + }; + + /* // // RELEASE NEW DEVE SERVIRE SOLO PER GLI EOC ????? SI PUO' TOGLIERE + bool releaseNew[8][16] = {{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false} + }; + */ + + float oneMsSamples = (APP->engine->getSampleRate()) / 1000; // number of samples in 1ms + float envSrCoeff = 1 / APP->engine->getSampleRate(); + + int chan; + + + float stageCoeff[8][16] = {{0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f} + }; + float masterLevel[8][16] = {{0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f} + }; + + + int reversePlaying[8][16] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + + + int recSlot = 0; + int recButton = 0; + int prevRecButton = 0; + float recTrig = 0.f; + float prevRecTrig = 0.f; + float recStopTrig = 0.f; + float prevRecStopTrig = 0.f; + int recordingState = 0; + + drwav_uint64 currRecPosition = 0; + + int recChannels = 1; + float currRecValue[2] = {0.f, 0.f}; + float prevRecValue[2] = {0.f, 0.f}; + + float recFadeValue = 0.f; + bool recFadeIn = false; + bool recFadeOut = false; + + int fileSampleRate[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + bool resampled[8] = {false, false, false, false, false, false, false, false}; + bool toSave[8] = {false, false, false, false, false, false, false, false}; + std::string infoToSave[8] = {"", "", "", "", "", "", "", ""}; + + int monitorSwitch = 0; + int prevMonitorSwitch = 0; + bool monitorFade = false; + float monitorFadeValue = 0.f; + float monitorFadeCoeff = 10 / (APP->engine->getSampleRate()); + + int saveMode = 0; + + bool nextSample = false; + bool prevNextSample = false; + bool prevSample = false; + bool prevPrevSample = false; + + bool unlimitedRecording = false; + const drwav_uint64 recordingLimit = 52428800 * 2; + //const drwav_uint64 recordingLimit = 480000 * 2; // 10 sec for test purposes + + drwav_uint64 currentRecordingLimit = recordingLimit; + + static constexpr float minStageTime = 1.f; // in milliseconds + static constexpr float maxStageTime = 10000.f; // in milliseconds + const float maxAdsrTime = 10.f; + const float minAdsrTime = 0.001f; + const float noEnvTime = 0.00101f; + + int grainCount[8][16] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + double grainSampleCount[8][16] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + double stretchMaxPos[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + double grainPos[8][16] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + float grainFadeValue[8][16] = {{0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f} + }; + float grainFadeCoeff[8][16] = {{0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f} + }; + bool grainFade[8][16] = {{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false} + }; + + bool learn = false; + + bool waitingLearnEnter = false; + int waitingLearnEnterTime = 0; + float learnSamples = APP->engine->getSampleRate(); + + bool storedLearn = false; + int storedLearnTime = 0; + int maxStoredLearnTime = APP->engine->getSampleRate() * 0.75; + + float keyToStore; + float learnKey; + float prevLearnKey = -11.f; + + bool learningButLight = false; + float learnButLightDelta = 2 / APP->engine->getSampleRate(); + float learnButLightValue = 0.f; + int learnPulseTime = 0; + bool learnExitButStatus = false; + int lightPulseTime = (APP->engine->getSampleRate()) / 10; + + KeySampler() { + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); + + configSwitch(PREVSAMPLE_PARAM, 0.f, 1.f, 0.f, "Previous Sample"); + configSwitch(NEXTSAMPLE_PARAM, 0.f, 1.f, 0.f, "Next Sample"); + + //****************************************************************************** + + configInput(IN_INPUT,"Left"); + configInput(IN_INPUT+1,"Right"); + configSwitch(REC_PARAM, 0.f, 1.f, 0.f, "REC", {"OFF", "ON"}); + configParam(GAIN_PARAM, 0.f, 2.0f, 1.0f, "REC Gain", "%", 0, 100); + + configSwitch(MONITOR_SWITCH, 0.f, 1.f, 0.f, "Monitor", {"Off", "On"}); + + //****************************************************************************** + + configParam(SLOT_SELECTOR_PARAM, 1.f, 8.f, 1.f, "Slot"); + paramQuantities[SLOT_SELECTOR_PARAM]->snapEnabled = true; + + configParam(RANGE_PARAM, 0.f, 127.f, 72.f, "Midi"); + paramQuantities[RANGE_PARAM]->snapEnabled = true; + + configSwitch(RANGE_SELECTOR_PARAM, 0.f, 1.f, 0.f, "Key Select"); + + configParam(OUT_SELECTOR_PARAM, 1.f, 8.f, 1.f, "Out"); + paramQuantities[OUT_SELECTOR_PARAM]->snapEnabled = true; + + //****************************************************************************** + + configParam(CUESTART_PARAM, 0.f, 1.0f, 0.f, "Cue Start", "%", 0, 100); + configParam(CUEEND_PARAM, 0.f, 1.0f, 1.f, "Cue End", "%", 0, 100); + + configParam(LOOPSTART_PARAM, 0.f, 1.0f, 0.f, "Loop Start", "%", 0, 100); + configParam(LOOPEND_PARAM, 0.f, 1.0f, 1.0f, "Loop End", "%", 0, 100); + + configSwitch(REV_PARAM, 0.f, 1.f, 0.f, "Playback Start", {"Forward", "Reverse"}); + configParam(XFADE_PARAM, 0.f, 1.f, 0.f, "Crossfade", "ms", maxStageTime / minStageTime, minStageTime); + + configSwitch(LOOP_PARAM, 0.f, 1.f, 0.f, "Loop", {"Off", "On"}); + configSwitch(PINGPONG_PARAM, 0.f, 1.f, 0.f, "PingPong", {"Off", "On"}); + + //****************************************************************************** + + configSwitch(TRIGGATEMODE_SWITCH, 0.f, 1.f, 0.f, "Mode", {"Gate", "Trig"}); + configSwitch(TRIGMODE_SWITCH, 0.f, 1.f, 0.f, "Trig Type", {"Start/Stop", "Restart"}); + + configSwitch(PB_SWITCH, 0.f, 1.f, 0.f, "Allow PB", {"Off", "On"}); + + configSwitch(PHASESCAN_SWITCH, 0.f, 1.f, 1.f, "Phase Scan", {"Off", "On"}); + + //****************************************************************************** + + configParam(ATTACK_PARAM, 0.f, 1.f, 0.f, "Attack", "ms", maxStageTime / minStageTime, minStageTime); + configParam(DECAY_PARAM, 0.f, 1.f, 0.f, "Decay", "ms", maxStageTime / minStageTime, minStageTime); + configParam(SUSTAIN_PARAM, 0.f, 1.0f, 1.0f, "Sustain","%", 0, 100); + configParam(RELEASE_PARAM, 0.f, 1.f, 0.f, "Release", "ms", maxStageTime / minStageTime, minStageTime); + + configParam(CURVE_PARAM, 0.f, 1.0f, 0.5f, "Shape","", 0, 100); + + //****************************************************************************** + + configParam(STRETCH_PARAM, 0.01f, 9.99f, 1.f, "Time Stretch", "%", 0, 100); + configParam(STR_SIZE_PARAM, 1.f, 99.f, 48.f, "Cycle Size", "ms"); + + configParam(VOL_PARAM, 0.f, 1.0f, 1.0f, "Sample Volume", "%", 0, 100); + + //****************************************************************************** + + configInput(GATE_INPUT,"Gate/Trig"); + configInput(VO_INPUT,"V/Oct"); + configInput(VEL_INPUT,"Velocity"); + configInput(PB_INPUT,"Pitch Bend"); + + //****************************************************************************** + + configOutput(LEFT_OUTPUT,"Left #1"); + configOutput(RIGHT_OUTPUT,"Right #1"); + configOutput(LEFT_OUTPUT+1,"Left #2"); + configOutput(RIGHT_OUTPUT+1,"Right #2"); + configOutput(LEFT_OUTPUT+2,"Left #3"); + configOutput(RIGHT_OUTPUT+2,"Right #3"); + configOutput(LEFT_OUTPUT+3,"Left #4"); + configOutput(RIGHT_OUTPUT+3,"Right #4"); + configOutput(LEFT_OUTPUT+4,"Left #5"); + configOutput(RIGHT_OUTPUT+4,"Right #5"); + configOutput(LEFT_OUTPUT+5,"Left #6"); + configOutput(RIGHT_OUTPUT+5,"Right #6"); + configOutput(LEFT_OUTPUT+6,"Left #7"); + configOutput(RIGHT_OUTPUT+6,"Right #7"); + configOutput(LEFT_OUTPUT+7,"Left #8"); + configOutput(RIGHT_OUTPUT+7,"Right #8"); + + //configOutput(EOC_OUTPUT,"End of Cycle"); + //configOutput(EOR_OUTPUT,"End of Release"); + + //****************************************************************************** + + playBuffer[0][0][0].resize(0); + playBuffer[0][0][1].resize(0); + playBuffer[0][1][0].resize(0); + playBuffer[0][1][1].resize(0); + + playBuffer[1][0][0].resize(0); + playBuffer[1][0][1].resize(0); + playBuffer[1][1][0].resize(0); + playBuffer[1][1][1].resize(0); + + playBuffer[2][0][0].resize(0); + playBuffer[2][0][1].resize(0); + playBuffer[2][1][0].resize(0); + playBuffer[2][1][1].resize(0); + + playBuffer[3][0][0].resize(0); + playBuffer[3][0][1].resize(0); + playBuffer[3][1][0].resize(0); + playBuffer[3][1][1].resize(0); + + playBuffer[4][0][0].resize(0); + playBuffer[4][0][1].resize(0); + playBuffer[4][1][0].resize(0); + playBuffer[4][1][1].resize(0); + + playBuffer[5][0][0].resize(0); + playBuffer[5][0][1].resize(0); + playBuffer[5][1][0].resize(0); + playBuffer[5][1][1].resize(0); + + playBuffer[6][0][0].resize(0); + playBuffer[6][0][1].resize(0); + playBuffer[6][1][0].resize(0); + playBuffer[6][1][1].resize(0); + + playBuffer[7][0][0].resize(0); + playBuffer[7][0][1].resize(0); + playBuffer[7][1][0].resize(0); + playBuffer[7][1][1].resize(0); + + tempBuffer[0].resize(0); + tempBuffer[1].resize(0); + + } + + static float convertCVToSeconds(float cv) { + return minStageTime * std::pow(maxStageTime / minStageTime, cv) / 1000; + } + + void onReset(const ResetEvent &e) override { + + for (int s = 0; s < 8; s++) { + + for (int c = 0; c < 16; c++) { + play[s][c] = false; + fadingType[s][c] = NO_FADE; + stage[s][c] = STOP_STAGE; + stageLevel[s][c] = 0; + env[s][c] = 0; + voct[s][c] = 0.f; + prevVoctDiffFloat[s][c] = 11.f; + reversePlaying[s][c] = FORWARD; + } + + clearSlot(s); + + firstLoad[s] = true; + totalSampleC[s] = 0; + totalSamples[s] = 0; + + prevPhaseScan[s] = false; + prevXfade[s] = -1.f; + + knobCueStartPos[s] = 0.f; + knobCueEndPos[s] = 1.f; + prevKnobCueStartPos[s] = -1.f; + prevKnobCueEndPos[s] = 2.f; + + knobLoopStartPos[s] = 0.f; + knobLoopEndPos[s] = 1.f; + prevKnobLoopStartPos[s] = -1.f; + prevKnobLoopEndPos[s] = 2.f; + + prevAttackKnob[s] = -1; + prevDecayKnob[s] = -1; + prevReleaseKnob[s] = -1; + + reverseStart[s] = false; + xFade[s] = 0; + loop[s] = false; + pingpong[s] = false; + + attackKnob[s] = 0; + decayKnob[s] = 0; + sustainValue[s] = 1; + releaseKnob[s] = 0; + + trigMode[s] = 0; + trigType[s] = 0; + allowPB[s] = false; + phaseScan[s] = true; + shape[s] = 0.5; + stretchKnob[s] = 1; + cycleKnob[s] = 48; + volumeValue[s] = 1; + + refKey[s][LOKEY] = 0; + refKey[s][HIKEY] = 127; + refKey[s][REFKEY] = 72; + + outRoute[s] = s; + + } + + prevSlot = -1; + + selectedRange = 0; + + trimOnSave = false; + antiAlias = 1; + + quantize = false; + pbRange = 1; + + autoMonOff = true; + + resetCursorsOnLoad = true; + saveOversampled = false; + + disableNav = false; + sampleInPatch = true; + unlimitedRecording = false; + + recordingState = 0; + recButton = 0; + prevMonitorSwitch = 0; + + system::removeRecursively(getPatchStorageDirectory().c_str()); + Module::onReset(e); + } + + void onAdd(const AddEvent& e) override { + for (int slot = 0; slot < 8; slot++) { + if (!fileLoaded[slot]) { + std::string patchFile = system::join(getPatchStorageDirectory(), ("slot"+to_string(slot+1)+".wav").c_str()); + loadFromPatch[slot] = true; + loadSample(patchFile, slot); + } + } + Module::onAdd(e); + } + + void onSave(const SaveEvent& e) override { + system::removeRecursively(getPatchStorageDirectory().c_str()); + + for (int slot = 0; slot < 8; slot++) { + if (fileLoaded[slot]) { + if (sampleInPatch) { + std::string patchFile = system::join(createPatchStorageDirectory(), ("slot"+to_string(slot+1)+".wav").c_str()); + saveMode = SAVE_FULL; + loadFromPatch[slot] = true; + saveSample(patchFile, slot); + } + } + } + Module::onSave(e); + } + + json_t *dataToJson() override { + json_t *rootJ = json_object(); + json_object_set_new(rootJ, "TrimOnSave", json_boolean(trimOnSave)); + json_object_set_new(rootJ, "SaveOversampled", json_boolean(saveOversampled)); + json_object_set_new(rootJ, "AntiAlias", json_integer(antiAlias)); + json_object_set_new(rootJ, "quantize", json_boolean(quantize)); + json_object_set_new(rootJ, "pbRange", json_integer(pbRange)); + json_object_set_new(rootJ, "AutoMonOff", json_integer(autoMonOff)); + json_object_set_new(rootJ, "ResetCursorsOnLoad", json_boolean(resetCursorsOnLoad)); + json_object_set_new(rootJ, "DisableNav", json_boolean(disableNav)); + json_object_set_new(rootJ, "sampleInPatch", json_boolean(sampleInPatch)); + json_object_set_new(rootJ, "unlimitedRecording", json_boolean(unlimitedRecording)); + json_object_set_new(rootJ, "currSlot", json_integer(currSlot)); + json_object_set_new(rootJ, "selectedRange", json_integer(selectedRange)); + + for (int slot = 0; slot < 8; slot++){ + json_object_set_new(rootJ, ("knobCueStartPos"+to_string(slot)).c_str(), json_real(knobCueStartPos[slot])); + json_object_set_new(rootJ, ("knobCueEndPos"+to_string(slot)).c_str(), json_real(knobCueEndPos[slot])); + json_object_set_new(rootJ, ("knobLoopStartPos"+to_string(slot)).c_str(), json_real(knobLoopStartPos[slot])); + json_object_set_new(rootJ, ("knobLoopEndPos"+to_string(slot)).c_str(), json_real(knobLoopEndPos[slot])); + json_object_set_new(rootJ, ("reverseStart"+to_string(slot)).c_str(), json_boolean(reverseStart[slot])); + json_object_set_new(rootJ, ("xFade"+to_string(slot)).c_str(), json_real(xFade[slot])); + json_object_set_new(rootJ, ("loop"+to_string(slot)).c_str(), json_boolean(loop[slot])); + json_object_set_new(rootJ, ("pingpong"+to_string(slot)).c_str(), json_boolean(pingpong[slot])); + json_object_set_new(rootJ, ("trigMode"+to_string(slot)).c_str(), json_integer(trigMode[slot])); + json_object_set_new(rootJ, ("trigType"+to_string(slot)).c_str(), json_integer(trigType[slot])); + json_object_set_new(rootJ, ("allowPB"+to_string(slot)).c_str(), json_boolean(allowPB[slot])); + json_object_set_new(rootJ, ("phaseScan"+to_string(slot)).c_str(), json_boolean(phaseScan[slot])); + json_object_set_new(rootJ, ("attackKnob"+to_string(slot)).c_str(), json_real(attackKnob[slot])); + json_object_set_new(rootJ, ("decayKnob"+to_string(slot)).c_str(), json_real(decayKnob[slot])); + json_object_set_new(rootJ, ("sustainValue"+to_string(slot)).c_str(), json_real(sustainValue[slot])); + json_object_set_new(rootJ, ("releaseKnob"+to_string(slot)).c_str(), json_real(releaseKnob[slot])); + json_object_set_new(rootJ, ("shape"+to_string(slot)).c_str(), json_real(shape[slot])); + json_object_set_new(rootJ, ("stretchKnob"+to_string(slot)).c_str(), json_real(stretchKnob[slot])); + json_object_set_new(rootJ, ("cycleKnob"+to_string(slot)).c_str(), json_real(cycleKnob[slot])); + json_object_set_new(rootJ, ("volumeValue"+to_string(slot)).c_str(), json_real(volumeValue[slot])); + json_object_set_new(rootJ, ("loKey"+to_string(slot)).c_str(), json_integer(refKey[slot][LOKEY])); + json_object_set_new(rootJ, ("hiKey"+to_string(slot)).c_str(), json_integer(refKey[slot][HIKEY])); + json_object_set_new(rootJ, ("refKey"+to_string(slot)).c_str(), json_integer(refKey[slot][REFKEY])); + json_object_set_new(rootJ, ("outRoute"+to_string(slot)).c_str(), json_integer(outRoute[slot])); + json_object_set_new(rootJ, ("storedPath"+to_string(slot)).c_str(), json_string(storedPath[slot].c_str())); + } + + json_object_set_new(rootJ, "UserFolder", json_string(userFolder.c_str())); + return rootJ; + } + + void dataFromJson(json_t *rootJ) override { + json_t* trimOnSaveJ = json_object_get(rootJ, "TrimOnSave"); + if (trimOnSaveJ) + trimOnSave = json_boolean_value(trimOnSaveJ); + json_t* saveOversampledJ = json_object_get(rootJ, "SaveOversampled"); + if (saveOversampledJ) + saveOversampled = json_boolean_value(saveOversampledJ); + json_t* antiAliasJ = json_object_get(rootJ, "AntiAlias"); + if (antiAliasJ) + antiAlias = json_integer_value(antiAliasJ); + json_t* quantizeJ = json_object_get(rootJ, "quantize"); + if (quantizeJ) + quantize = json_boolean_value(quantizeJ); + json_t* pbRangeJ = json_object_get(rootJ, "pbRange"); + if (pbRangeJ) + pbRange = json_integer_value(pbRangeJ); + json_t* autoMonOffJ = json_object_get(rootJ, "AutoMonOff"); + if (autoMonOffJ) + autoMonOff = json_integer_value(autoMonOffJ); + json_t* resetCursorsOnLoadJ = json_object_get(rootJ, "ResetCursorsOnLoad"); + if (resetCursorsOnLoadJ) + resetCursorsOnLoad = json_boolean_value(resetCursorsOnLoadJ); + json_t* disableNavJ = json_object_get(rootJ, "DisableNav"); + if (disableNavJ) + disableNav = json_boolean_value(disableNavJ); + json_t* sampleInPatchJ = json_object_get(rootJ, "sampleInPatch"); + if (sampleInPatchJ) + sampleInPatch = json_boolean_value(sampleInPatchJ); + json_t* unlimitedRecordingJ = json_object_get(rootJ, "unlimitedRecording"); + if (unlimitedRecordingJ) + unlimitedRecording = json_boolean_value(unlimitedRecordingJ); + json_t* currSlotJ = json_object_get(rootJ, "currSlot"); + if (currSlotJ) + currSlot = json_integer_value(currSlotJ); + json_t* selectedRangeJ = json_object_get(rootJ, "selectedRange"); + if (selectedRangeJ) + selectedRange = json_integer_value(selectedRangeJ); + + for (int slot = 0; slot < 8; slot++) { + json_t *knobCueStartPosJ = json_object_get(rootJ, ("knobCueStartPos"+to_string(slot)).c_str()); + if (knobCueStartPosJ) + knobCueStartPos[slot] = json_real_value(knobCueStartPosJ); + json_t *knobCueEndPosJ = json_object_get(rootJ, ("knobCueEndPos"+to_string(slot)).c_str()); + if (knobCueEndPosJ) + knobCueEndPos[slot] = json_real_value(knobCueEndPosJ); + json_t *knobLoopStartPosJ = json_object_get(rootJ, ("knobLoopStartPos"+to_string(slot)).c_str()); + if (knobLoopStartPosJ) + knobLoopStartPos[slot] = json_real_value(knobLoopStartPosJ); + json_t *knobLoopEndPosJ = json_object_get(rootJ, ("knobLoopEndPos"+to_string(slot)).c_str()); + if (knobLoopEndPosJ) + knobLoopEndPos[slot] = json_real_value(knobLoopEndPosJ); + json_t* reverseStartJ = json_object_get(rootJ, ("reverseStart"+to_string(slot)).c_str()); + if (reverseStartJ) + reverseStart[slot] = json_boolean_value(reverseStartJ); + json_t *xFadeJ = json_object_get(rootJ, ("xFade"+to_string(slot)).c_str()); + if (xFadeJ) { + xFade[slot] = json_real_value(xFadeJ); + if (xFade[slot] == 0) + fadeSamples[slot] = 0; + else + fadeSamples[slot] = floor(convertCVToSeconds(xFade[slot]) * APP->engine->getSampleRate()); // number of samples before starting fade + } + json_t* loopJ = json_object_get(rootJ, ("loop"+to_string(slot)).c_str()); + if (loopJ) + loop[slot] = json_boolean_value(loopJ); + json_t* pingpongJ = json_object_get(rootJ, ("pingpong"+to_string(slot)).c_str()); + if (pingpongJ) + pingpong[slot] = json_boolean_value(pingpongJ); + json_t* trigModeJ = json_object_get(rootJ, ("trigMode"+to_string(slot)).c_str()); + if (trigModeJ) + trigMode[slot] = json_integer_value(trigModeJ); + json_t* trigTypeJ = json_object_get(rootJ, ("trigType"+to_string(slot)).c_str()); + if (trigTypeJ) + trigType[slot] = json_integer_value(trigTypeJ); + json_t* allowPBJ = json_object_get(rootJ, ("allowPB"+to_string(slot)).c_str()); + if (allowPBJ) + allowPB[slot] = json_boolean_value(allowPBJ); + json_t* phaseScanJ = json_object_get(rootJ, ("phaseScan"+to_string(slot)).c_str()); + if (phaseScanJ) + phaseScan[slot] = json_boolean_value(phaseScanJ); + json_t *attackKnobJ = json_object_get(rootJ, ("attackKnob"+to_string(slot)).c_str()); + if (attackKnobJ){ + attackKnob[slot] = json_real_value(attackKnobJ); + calcAttack(slot); + } + json_t *decayKnobJ = json_object_get(rootJ, ("decayKnob"+to_string(slot)).c_str()); + if (decayKnobJ) { + decayKnob[slot] = json_real_value(decayKnobJ); + calcDecay(slot); + } + json_t *sustainValueJ = json_object_get(rootJ, ("sustainValue"+to_string(slot)).c_str()); + if (sustainValueJ) + sustainValue[slot] = json_real_value(sustainValueJ); + json_t *releaseKnobJ = json_object_get(rootJ, ("releaseKnob"+to_string(slot)).c_str()); + if (releaseKnobJ) { + releaseKnob[slot] = json_real_value(releaseKnobJ); + calcRelease(slot); + } + json_t *shapeJ = json_object_get(rootJ, ("shape"+to_string(slot)).c_str()); + if (shapeJ) + shape[slot] = json_real_value(shapeJ); + json_t *stretchKnobJ = json_object_get(rootJ, ("stretchKnob"+to_string(slot)).c_str()); + if (stretchKnobJ) + stretchKnob[slot] = json_real_value(stretchKnobJ); + json_t *cycleKnobJ = json_object_get(rootJ, ("cycleKnob"+to_string(slot)).c_str()); + if (cycleKnobJ) { + cycleKnob[slot] = json_real_value(cycleKnobJ); + stretchMaxPos[slot] = oneMsSamples * cycleKnob[slot]; + } + json_t *volumeValueJ = json_object_get(rootJ, ("volumeValue"+to_string(slot)).c_str()); + if (volumeValueJ) + volumeValue[slot] = json_real_value(volumeValueJ); + json_t* loKeyJ = json_object_get(rootJ, ("loKey"+to_string(slot)).c_str()); + if (loKeyJ) + refKey[slot][LOKEY] = json_integer_value(loKeyJ); + json_t* hiKeyJ = json_object_get(rootJ, ("hiKey"+to_string(slot)).c_str()); + if (hiKeyJ) + refKey[slot][HIKEY] = json_integer_value(hiKeyJ); + json_t* refKeyJ = json_object_get(rootJ, ("refKey"+to_string(slot)).c_str()); + if (refKeyJ) + refKey[slot][REFKEY] = json_integer_value(refKeyJ); + json_t* outRouteJ = json_object_get(rootJ, ("outRoute"+to_string(slot)).c_str()); + if (outRouteJ) + outRoute[slot] = json_integer_value(outRouteJ); + json_t *storedPathJ = json_object_get(rootJ, ("storedPath"+to_string(slot)).c_str()); + if (storedPathJ) { + storedPath[slot] = json_string_value(storedPathJ); + if (storedPath[slot] != "") + loadSample(storedPath[slot], slot); + else + firstLoad[slot] = false; + } + } + + json_t *userFolderJ = json_object_get(rootJ, "UserFolder"); + if (userFolderJ) { + userFolder = json_string_value(userFolderJ); + if (userFolder != "") { + createFolder(userFolder); + if (rootFound) { + folderTreeData.push_back(tempTreeData); + folderTreeDisplay.push_back(tempTreeDisplay); + } + } + } + } + + inline void calcAttack(int slot) { + attackValue[slot] = convertCVToSeconds(attackKnob[slot]); + if (attackValue[slot] > maxAdsrTime) + attackValue[slot] = maxAdsrTime; + else if (attackValue[slot] < minAdsrTime) + attackValue[slot] = minAdsrTime; + prevAttackKnob[slot] = attackKnob[slot]; + } + + inline void calcDecay(int slot) { + decayValue[slot] = convertCVToSeconds(decayKnob[slot]); + if (decayValue[slot] > maxAdsrTime) + decayValue[slot] = maxAdsrTime; + else if (decayValue[slot] < minAdsrTime) + decayValue[slot] = minAdsrTime; + prevDecayKnob[slot] = decayKnob[slot]; + } + + inline void calcRelease(int slot) { + releaseValue[slot] = convertCVToSeconds(releaseKnob[slot]); + if (releaseValue[slot] > maxAdsrTime) + releaseValue[slot] = maxAdsrTime; + else if (releaseValue[slot] < minAdsrTime) + releaseValue[slot] = minAdsrTime; + prevReleaseKnob[slot] = releaseKnob[slot]; + } + + void calcBiquadLpf(double frequency, double samplerate, double Q) { + z1 = z2 = 0.0; + double Fc = frequency / samplerate; + double norm; + double K = tan(M_PI * Fc); + norm = 1 / (1 + K / Q + K * K); + a0 = K * K * norm; + a1 = 2 * a0; + a2 = a0; + b1 = 2 * (K * K - 1) * norm; + b2 = (1 - K / Q + K * K) * norm; + } + + float biquadLpf(float in) { + double out = in * a0 + z1; + z1 = in * a1 + z2 - b1 * out; + z2 = in * a2 - b2 * out; + return out; + } + + float biquadLpf2(float in) { + double out = in * a0 + z1r; + z1r = in * a1 + z2r - b1 * out; + z2r = in * a2 - b2 * out; + return out; + } + + double hermiteInterpol(double x0, double x1, double x2, double x3, double t) { + double c0 = x1; + double c1 = .5F * (x2 - x0); + double c2 = x0 - (2.5F * x1) + (2 * x2) - (.5F * x3); + double c3 = (.5F * (x3 - x0)) + (1.5F * (x1 - x2)); + return (((((c3 * t) + c2) * t) + c1) * t) + c0; + } + + void selectRootFolder() { + const char* prevFolder = userFolder.c_str(); + char *path = osdialog_file(OSDIALOG_OPEN_DIR, prevFolder, NULL, NULL); + if (path) { + folderTreeData.clear(); + folderTreeDisplay.clear(); + userFolder = std::string(path); + createFolder(userFolder); + if (rootFound) { + folderTreeData.push_back(tempTreeData); + folderTreeDisplay.push_back(tempTreeDisplay); + } + } + free(path); + }; + + void refreshRootFolder() { + folderTreeData.clear(); + folderTreeDisplay.clear(); + createFolder(userFolder); + if (rootFound) { + folderTreeData.push_back(tempTreeData); + folderTreeDisplay.push_back(tempTreeDisplay); + } + } + + void createFolder(std::string dir_path) { + vector browserList; + vector browserListDisplay; + vector browserDir; + vector browserDirDisplay; + vector browserFiles; + vector browserFilesDisplay; + + tempTreeData.clear(); + tempTreeDisplay.clear(); + + std::string lastChar = dir_path.substr(dir_path.length()-1,dir_path.length()-1); + if (lastChar != "/") + dir_path += "/"; + + DIR *dir = opendir(dir_path.c_str()); + + if (dir) { + rootFound = true; + struct dirent *d; + while ((d = readdir(dir))) { + std::string filename = d->d_name; + if (filename != "." && filename != "..") { + std::string filepath = std::string(dir_path) + filename; + struct stat statbuf; + if (stat(filepath.c_str(), &statbuf) == 0 && (statbuf.st_mode & S_IFMT) == S_IFDIR) { + //browserDir.push_back(filepath + "/"); + browserDir.push_back(filepath); + browserDirDisplay.push_back(filename); + } else { + std::size_t found = filename.find(".wav",filename.length()-5); + if (found==std::string::npos) + found = filename.find(".WAV",filename.length()-5); + if (found!=std::string::npos) { + browserFiles.push_back(filepath); + browserFilesDisplay.push_back(filename.substr(0, filename.length()-4)); + } + } + } + } + closedir(dir); + + sort(browserDir.begin(), browserDir.end()); + sort(browserDirDisplay.begin(), browserDirDisplay.end()); + sort(browserFiles.begin(), browserFiles.end()); + sort(browserFilesDisplay.begin(), browserFilesDisplay.end()); + + // this adds "/" to browserDir after sorting to avoid wrong sorting with foldernames with spaces + int dirSize = (int)browserDir.size(); + for (int i=0; i < dirSize; i++) + browserDir[i] += "/"; + + tempTreeData.push_back(dir_path); + tempTreeDisplay.push_back(dir_path); + + for (unsigned int i = 0; i < browserDir.size(); i++) { + tempTreeData.push_back(browserDir[i]); + tempTreeDisplay.push_back(browserDirDisplay[i]); + } + for (unsigned int i = 0; i < browserFiles.size(); i++) { + tempTreeData.push_back(browserFiles[i]); + tempTreeDisplay.push_back(browserFilesDisplay[i]); + } + } else { + rootFound = false; + } + }; + + void createCurrentFolder(std::string dir_path) { + vector browserList; + vector browserFiles; + + tempTreeData.clear(); + + std::string lastChar = dir_path.substr(dir_path.length()-1,dir_path.length()-1); + if (lastChar != "/") + dir_path += "/"; + + DIR *dir = opendir(dir_path.c_str()); + struct dirent *d; + while ((d = readdir(dir))) { + std::string filename = d->d_name; + if (filename != "." && filename != "..") { + std::string filepath = std::string(dir_path) + filename; + + std::size_t found = filename.find(".wav",filename.length()-5); + if (found==std::string::npos) + found = filename.find(".WAV",filename.length()-5); + if (found!=std::string::npos) { + browserFiles.push_back(filepath); + } + } + } + closedir(dir); + + sort(browserFiles.begin(), browserFiles.end()); + + for (unsigned int i = 0; i < browserFiles.size(); i++) { + tempTreeData.push_back(browserFiles[i]); + } + }; + +/* + + ██████╗░███████╗░██████╗░█████╗░███╗░░░███╗██████╗░██╗░░░░░██╗███╗░░██╗░██████╗░ + ██╔══██╗██╔════╝██╔════╝██╔══██╗████╗░████║██╔══██╗██║░░░░░██║████╗░██║██╔════╝░ + ██████╔╝█████╗░░╚█████╗░███████║██╔████╔██║██████╔╝██║░░░░░██║██╔██╗██║██║░░██╗░ + ██╔══██╗██╔══╝░░░╚═══██╗██╔══██║██║╚██╔╝██║██╔═══╝░██║░░░░░██║██║╚████║██║░░╚██╗ + ██║░░██║███████╗██████╔╝██║░░██║██║░╚═╝░██║██║░░░░░███████╗██║██║░╚███║╚██████╔╝ + ╚═╝░░╚═╝╚══════╝╚═════╝░╚═╝░░╚═╝╚═╝░░░░░╚═╝╚═╝░░░░░╚══════╝╚═╝╚═╝░░╚══╝░╚═════╝░ +*/ +// https://fsymbols.com/generators/carty/ + + void onSampleRateChange() override { + + monitorFadeCoeff = 10 / (APP->engine->getSampleRate()); // 100ms monitor fade + oneMsSamples = (APP->engine->getSampleRate())/1000; // number of samples for 1 ms used for output triggers + learnButLightDelta = 2 / APP->engine->getSampleRate(); + lightPulseTime = (APP->engine->getSampleRate()) / 10; + learnSamples = APP->engine->getSampleRate(); + maxStoredLearnTime = APP->engine->getSampleRate() * 0.75; + + prevLearnKey = -11.f; + + prevXfade[0] = -1.f; + prevXfade[1] = -1.f; + prevXfade[2] = -1.f; + prevXfade[3] = -1.f; + prevXfade[4] = -1.f; + prevXfade[5] = -1.f; + prevXfade[6] = -1.f; + prevXfade[7] = -1.f; + + vcvSampleRate = APP->engine->getSampleRate(); + envSrCoeff = 1 / APP->engine->getSampleRate(); + sampleRate = vcvSampleRate * 2; + + double oldSampleRate = sampleRate; + double newSampleRate = APP->engine->getSampleRate(); + //sampleRate = newSampleRate * 2; + + if (newSampleRate != oldSampleRate/2) { + + for (int slot = 0; slot < 8; slot++) { + + if (fileLoaded[slot]) { + + double resampleCoeff; + + fileLoaded[slot] = false; + + z1 = 0; z2 = 0; z1r = 0; z2r = 0; + + tempBuffer[0].clear(); + tempBuffer[1].clear(); + + for (unsigned int i=0; i < totalSampleC[slot]; i++) { + tempBuffer[LEFT].push_back(playBuffer[slot][LEFT][0][i]); + if (channels[slot] == 2) { + tempBuffer[RIGHT].push_back(playBuffer[slot][RIGHT][0][i]); + } + } + + playBuffer[slot][LEFT][0].clear(); + playBuffer[slot][LEFT][1].clear(); + playBuffer[slot][RIGHT][0].clear(); + playBuffer[slot][RIGHT][1].clear(); + + drwav_uint64 tempSampleC = totalSampleC[slot]; + drwav_uint64 tempSamples = totalSamples[slot]; + + resampleCoeff = oldSampleRate / (newSampleRate) / 2; + + double currResamplePos = 0; + double floorCurrResamplePos = 0; + + playBuffer[slot][LEFT][0].push_back(tempBuffer[LEFT][0]); + if (channels[slot] == 2) + playBuffer[slot][RIGHT][0].push_back(tempBuffer[RIGHT][0]); + + currResamplePos += resampleCoeff; + floorCurrResamplePos = floor(currResamplePos); + int temp; + + while ( floorCurrResamplePos < 1 ) { + temp = tempBuffer[LEFT][floorCurrResamplePos]* (1-(currResamplePos - floorCurrResamplePos)) + + tempBuffer[LEFT][floorCurrResamplePos+1]*(currResamplePos - floorCurrResamplePos); + playBuffer[slot][LEFT][0].push_back(temp); + if (channels[slot] == 2) { + temp = tempBuffer[RIGHT][floorCurrResamplePos]* (1-(currResamplePos - floorCurrResamplePos)) + + tempBuffer[RIGHT][floorCurrResamplePos+1]*(currResamplePos - floorCurrResamplePos); + playBuffer[slot][RIGHT][0].push_back(temp); + } + currResamplePos += resampleCoeff; + floorCurrResamplePos = floor(currResamplePos); + } + + while ( currResamplePos < tempSamples - 1) { + playBuffer[slot][LEFT][0].push_back( hermiteInterpol(tempBuffer[LEFT][floorCurrResamplePos - 1], + tempBuffer[LEFT][floorCurrResamplePos], + tempBuffer[LEFT][floorCurrResamplePos + 1], + tempBuffer[LEFT][floorCurrResamplePos + 2], + currResamplePos - floorCurrResamplePos) + ); + if (channels[slot] == 2) + playBuffer[slot][RIGHT][0].push_back( hermiteInterpol(tempBuffer[RIGHT][floorCurrResamplePos - 1], + tempBuffer[RIGHT][floorCurrResamplePos], + tempBuffer[RIGHT][floorCurrResamplePos + 1], + tempBuffer[RIGHT][floorCurrResamplePos + 2], + currResamplePos - floorCurrResamplePos) + ); + currResamplePos += resampleCoeff; + floorCurrResamplePos = floor(currResamplePos); + } + + while ( floorCurrResamplePos < tempSampleC ) { + temp = tempBuffer[LEFT][floorCurrResamplePos]* (1-(currResamplePos - floorCurrResamplePos)) + + tempBuffer[LEFT][floorCurrResamplePos+1]*(currResamplePos - floorCurrResamplePos); + playBuffer[slot][LEFT][0].push_back(temp); + if (channels[slot] == 2) { + temp = tempBuffer[RIGHT][floorCurrResamplePos]* (1-(currResamplePos - floorCurrResamplePos)) + + tempBuffer[RIGHT][floorCurrResamplePos+1]*(currResamplePos - floorCurrResamplePos); + playBuffer[slot][RIGHT][0].push_back(temp); + } + currResamplePos += resampleCoeff; + floorCurrResamplePos = floor(currResamplePos); + } + + tempBuffer[LEFT].clear(); + tempBuffer[RIGHT].clear(); + + // *************************************************************************** + totalSampleC[slot] = playBuffer[slot][LEFT][0].size(); + totalSamples[slot] = totalSampleC[slot]-1; // ***** DA VERIFICARE se è -2 ******************************************** + //sampleRate = newSampleRate * 2; // già impostato all'inizio + // ************************************************************* + + calcBiquadLpf(20000.0, sampleRate, 1); + for (unsigned int i = 0; i < totalSampleC[slot]; i++) { + playBuffer[slot][LEFT][1].push_back(biquadLpf(playBuffer[slot][LEFT][0][i])); + if (channels[slot] == 2) + playBuffer[slot][RIGHT][1].push_back(biquadLpf2(playBuffer[slot][RIGHT][0][i])); + } + + prevKnobCueStartPos[slot] = -1.f; + prevKnobCueEndPos[slot] = 2.f; + prevKnobLoopStartPos[slot] = -1.f; + prevKnobLoopEndPos[slot] = 2.f; + + vector().swap(displayBuff[slot]); + for (int i = 0; i < floor(totalSampleC[slot]); i = i + floor(totalSampleC[slot]/wavDisp)) + displayBuff[slot].push_back(playBuffer[slot][0][0][i]); + + resampled[slot] = true; + //recSamples = 0; // *********** messo fuori del loop + fileLoaded[slot] = true; + } + } + recSamples = 0; + } + } +/* + + ░██████╗░█████╗░██╗░░░██╗███████╗ + ██╔════╝██╔══██╗██║░░░██║██╔════╝ + ╚█████╗░███████║╚██╗░██╔╝█████╗░░ + ░╚═══██╗██╔══██║░╚████╔╝░██╔══╝░░ + ██████╔╝██║░░██║░░╚██╔╝░░███████╗ + ╚═════╝░╚═╝░░╚═╝░░░╚═╝░░░╚══════╝ +*/ + void menuSaveSample(int mode, int slot) { + fileChannels[slot] = channels[slot]; + fileLoaded[slot] = false; + static const char FILE_FILTERS[] = "Wave (.wav):wav,WAV"; + osdialog_filters* filters = osdialog_filters_parse(FILE_FILTERS); + DEFER({osdialog_filters_free(filters);}); + char *path = osdialog_file(OSDIALOG_SAVE, NULL, NULL, filters); + if (path) { + saveMode = mode; + saveSample(path, slot); + std::string newPath = std::string(path); + if (newPath.substr(newPath.size() - 4) != ".wav" && newPath.substr(newPath.size() - 4) != ".WAV") + newPath += ".wav"; + storedPath[slot] = newPath; + } + channels[slot] = fileChannels[slot]; + fileLoaded[slot] = true; + free(path); + }; + + void saveSample(std::string path, int slot) { + + std::string newPath = path; + + drwav_uint64 samples; + drwav_uint64 saveStartSamplePos = 0; + drwav_uint64 saveEndSamplePos = 0; + + int overSampleCoeff; + if (saveOversampled) + overSampleCoeff = 1; + else + overSampleCoeff = 2; + + switch (saveMode) { + case SAVE_FULL: + saveStartSamplePos = 0; + saveEndSamplePos = floor(totalSampleC[slot]/overSampleCoeff); + break; + case SAVE_CUE: + saveStartSamplePos = floor(cueStartPos[slot]/overSampleCoeff); + saveEndSamplePos = floor(cueEndPos[slot]/overSampleCoeff); + break; + case SAVE_LOOP: + saveStartSamplePos = floor(loopStartPos[slot]/overSampleCoeff); + saveEndSamplePos = floor(loopEndPos[slot]/overSampleCoeff); + break; + } + + if (!saveOversampled) { + if (saveStartSamplePos % 2 != 0 && saveStartSamplePos > 0) + saveStartSamplePos--; + + if (saveEndSamplePos % 2 != 0 && saveEndSamplePos < totalSamples[slot]) + saveEndSamplePos++; + } + + samples = floor(saveEndSamplePos - saveStartSamplePos); + + std::vector data; + + switch (fileChannels[slot]) { + case 1: + for (unsigned int i = saveStartSamplePos; i <= saveEndSamplePos; i++) + data.push_back(playBuffer[slot][LEFT][0][i*overSampleCoeff] / 5); + break; + + case 2: + for (unsigned int i = saveStartSamplePos; i <= saveEndSamplePos; i++) { + data.push_back(playBuffer[slot][LEFT][0][i*overSampleCoeff] / 5); + data.push_back(playBuffer[slot][RIGHT][0][i*overSampleCoeff] / 5); + } + break; + } + + drwav_data_format format; + format.container = drwav_container_riff; + format.format = DR_WAVE_FORMAT_IEEE_FLOAT; + + if (fileChannels[slot] == 1) + format.channels = 1; + else { + format.channels = 2; + samples *= 2; + } + + if (saveOversampled) + format.sampleRate = sampleRate; + else + format.sampleRate = sampleRate / 2; + + format.bitsPerSample = 32; + + if (path.substr(path.size() - 4) != ".wav" && path.substr(path.size() - 4) != ".WAV") + path += ".wav"; + + drwav *pWav = drwav_open_file_write(path.c_str(), &format); + drwav_write(pWav, samples, data.data()); + drwav_close(pWav); + toSave[slot] = false; + infoToSave[slot] = ""; + + char* pathDup = strdup(path.c_str()); + //fileDescription = basename(pathDup); + if (!loadFromPatch[slot]) { + toSave[slot] = false; + infoToSave[slot] = ""; + fileDescription[slot] = basename(pathDup); + } else { + /*if (fileDescription[slot] != "_unknown_.wav") + fileDescription[slot] = basename(pathDup);*/ + if (fileDescription[slot] != "_slot"+to_string(slot+1)+".wav") + fileDescription[slot] = basename(pathDup); + } + + // *** CHARs CHECK according to font + std::string tempFileDisplay = fileDescription[slot].substr(0, fileDescription[slot].size()-4); + /* + char tempFileChar; + fileDisplay[slot] = ""; + for (int i = 0; i < int(tempFileDisplay.length()); i++) { + tempFileChar = tempFileDisplay.at(i); + if ( (int(tempFileChar) > 47 && int(tempFileChar) < 58) + || (int(tempFileChar) > 64 && int(tempFileChar) < 123) + || int(tempFileChar) == 45 || int(tempFileChar) == 46 || int(tempFileChar) == 95 ) + fileDisplay[slot] += tempFileChar; + } + fileDisplay[slot] = fileDisplay[slot].substr(0, maxTitleChars); + */ + fileDisplay[slot] = tempFileDisplay.substr(0, maxTitleChars); + + free(pathDup); + data.clear(); + + //storedPath[slot] = path; + if (!loadFromPatch[slot]) { + storedPath[slot] = path; + + if (trimOnSave && saveMode == SAVE_LOOP) { + // set position knob to 0 and 1 + knobCueStartPos[slot] = 0; + knobCueEndPos[slot] = 1; + knobLoopStartPos[slot] = 0; + knobLoopEndPos[slot] = 1; + params[CUESTART_PARAM].setValue(0); + params[CUEEND_PARAM].setValue(1); + params[LOOPSTART_PARAM].setValue(0); + params[LOOPEND_PARAM].setValue(1); + + loadSample(path, slot); + } else if (trimOnSave && saveMode == SAVE_CUE) { + float tempKnobLoopStartPos = (knobLoopStartPos[slot] - knobCueStartPos[slot] ) / (knobCueEndPos[slot] - knobCueStartPos[slot]); + float tempKnobLoopEndPos = (knobLoopEndPos[slot] - knobCueStartPos[slot] ) / (knobCueEndPos[slot] - knobCueStartPos[slot]); + loadSample(path, slot); + + params[CUESTART_PARAM].setValue(0); + params[CUEEND_PARAM].setValue(1); + + if (tempKnobLoopStartPos < 0) + params[LOOPSTART_PARAM].setValue(0); + else + params[LOOPSTART_PARAM].setValue(tempKnobLoopStartPos); + if (tempKnobLoopEndPos > 1) + params[LOOPEND_PARAM].setValue(1); + else + params[LOOPEND_PARAM].setValue(tempKnobLoopEndPos); + } + } + + fileLoaded[slot] = true; + fileFound[slot] = true; + channels[slot] = fileChannels[slot]; + } +/* + + ██╗░░░░░░█████╗░░█████╗░██████╗░ + ██║░░░░░██╔══██╗██╔══██╗██╔══██╗ + ██║░░░░░██║░░██║███████║██║░░██║ + ██║░░░░░██║░░██║██╔══██║██║░░██║ + ███████╗╚█████╔╝██║░░██║██████╔╝ + ╚══════╝░╚════╝░╚═╝░░╚═╝╚═════╝░ +*/ + void menuLoadSample(int slot) { + static const char FILE_FILTERS[] = "Wave (.wav):wav,WAV;All files (*.*):*.*"; + osdialog_filters* filters = osdialog_filters_parse(FILE_FILTERS); + DEFER({osdialog_filters_free(filters);}); + char *path = osdialog_file(OSDIALOG_OPEN, NULL, NULL, filters); + fileLoaded[slot] = false; + restoreLoadFromPatch[slot] = false; + if (path) { + //loadFromPatch[slot] = false; // ****************************************** QUESTO C'ERA SOLO SU DRUMPLAYER + loadSample(path, slot); + storedPath[slot] = std::string(path); + } else { + restoreLoadFromPatch[slot] = true; + fileLoaded[slot] = true; + } + if ((storedPath[slot] == "" || fileFound[slot] == false) && !toSave[slot]) { + fileLoaded[slot] = false; + } + free(path); + } + + void loadSample(std::string fromPath, int slot) { + std::string path = fromPath; + z1 = 0; z2 = 0; z1r = 0; z2r = 0; + + tempBuffer[0].clear(); + tempBuffer[1].clear(); + double tempSampleRate; + + unsigned int c; + unsigned int sr; + drwav_uint64 tsc; + float* pSampleData; + pSampleData = drwav_open_and_read_file_f32(path.c_str(), &c, &sr, &tsc); + + if (pSampleData != NULL && tsc > minSamplesToLoad * c) { + + fileFound[slot] = true; + fileChannels[slot] = c; + tempSampleRate = sr * 2; + fileSampleRate[slot] = sr; + + for (int c=0; c < 16; c++) + samplePos[slot][c] = 0; + + playBuffer[slot][LEFT][0].clear(); + playBuffer[slot][LEFT][1].clear(); + playBuffer[slot][RIGHT][0].clear(); + playBuffer[slot][RIGHT][1].clear(); + tempBuffer[LEFT].clear(); + tempBuffer[RIGHT].clear(); + displayBuff[slot].clear(); + + if (!unlimitedRecording) { + if (tsc > recordingLimit / 2) + tsc = recordingLimit / 2; // set memory allocation limit to 200Mb for samples (~18mins at 48.000khz MONO) + } + + if (sr == APP->engine->getSampleRate()) { // ************************** NO RESAMPLE ************************ + for (unsigned int i=0; i < tsc; i = i + c) { + playBuffer[slot][LEFT][0].push_back(pSampleData[i] * 5); + playBuffer[slot][LEFT][0].push_back(0); + if (fileChannels[slot] == 2) { + playBuffer[slot][RIGHT][0].push_back(pSampleData[i+1] * 5); + playBuffer[slot][RIGHT][0].push_back(0); + } + } + totalSampleC[slot] = playBuffer[slot][LEFT][0].size(); + totalSamples[slot] = totalSampleC[slot]-1; // ***** DA VERIFICARE se è -2 ******************************************** + drwav_free(pSampleData); + + for (unsigned int i = 1; i < totalSamples[slot]; i = i+2) { // **************** tempSamples o tempSampleC ???? + playBuffer[slot][LEFT][0][i] = playBuffer[slot][LEFT][0][i-1] * .5f + playBuffer[slot][LEFT][0][i+1] * .5f; + if (fileChannels[slot] == 2) + playBuffer[slot][RIGHT][0][i] = playBuffer[slot][RIGHT][0][i-1] * .5f + playBuffer[slot][RIGHT][0][i+1] * .5f; + } + + playBuffer[slot][LEFT][0][totalSamples[slot]] = playBuffer[slot][LEFT][0][totalSamples[slot]-1] * .5f; // halve the last sample + if (fileChannels[slot] == 2) + playBuffer[slot][RIGHT][0][totalSamples[slot]] = playBuffer[slot][RIGHT][0][totalSamples[slot]-1] * .5f; + + resampled[slot] = false; + + } else if (sr == APP->engine->getSampleRate() * 2) { // ***** LOAD DIRECTLY OVERSAMPLED, NO RESAMPLE ***** + for (unsigned int i=0; i < tsc; i = i + c) { + playBuffer[slot][LEFT][0].push_back(pSampleData[i] * 5); + if (fileChannels[slot] == 2) { + playBuffer[slot][RIGHT][0].push_back(pSampleData[i+1] * 5); + } + } + totalSampleC[slot] = playBuffer[slot][LEFT][0].size(); + totalSamples[slot] = totalSampleC[slot]-1; // ***** DA VERIFICARE se è -2 ******************************************** + drwav_free(pSampleData); + + resampled[slot] = false; + + //sampleRate = APP->engine->getSampleRate() * 2; + + } else { // ***************** RESAMPLE **************************************** + for (unsigned int i=0; i < tsc; i = i + c) { + tempBuffer[LEFT].push_back(pSampleData[i] * 5); + tempBuffer[LEFT].push_back(0); + if (fileChannels[slot] == 2) { + tempBuffer[RIGHT].push_back(pSampleData[i+1] * 5); + tempBuffer[RIGHT].push_back(0); + } + } + + drwav_free(pSampleData); + + drwav_uint64 tempSampleC = tempBuffer[LEFT].size(); + drwav_uint64 tempSamples = tempSampleC-1; // ***** DA VERIFICARE se è -2 ******************************************** + + + for (unsigned int i = 1; i < tempSamples; i = i+2) { // **************** tempSamples o tempSampleC ???? + tempBuffer[LEFT][i] = tempBuffer[LEFT][i-1] * .5f + tempBuffer[LEFT][i+1] * .5f; + if (fileChannels[slot] == 2) + tempBuffer[RIGHT][i] = tempBuffer[RIGHT][i-1] * .5f + tempBuffer[RIGHT][i+1] * .5f; + } + + tempBuffer[LEFT][tempSamples] = tempBuffer[LEFT][tempSamples-1] * .5f; // halve the last sample + if (fileChannels[slot] == 2) + tempBuffer[RIGHT][tempSamples] = tempBuffer[RIGHT][tempSamples-1] * .5f; + + // *************************************************************************** + + double resampleCoeff = tempSampleRate * .5 / (APP->engine->getSampleRate()); + double currResamplePos = 0; + int floorCurrResamplePos = 0; + + playBuffer[slot][LEFT][0].push_back(tempBuffer[LEFT][0]); + if (fileChannels[slot] == 2) + playBuffer[slot][RIGHT][0].push_back(tempBuffer[RIGHT][0]); + + currResamplePos += resampleCoeff; + floorCurrResamplePos = floor(currResamplePos); + int temp; + + while ( floorCurrResamplePos < 1 ) { + temp = tempBuffer[LEFT][floorCurrResamplePos]* (1-(currResamplePos - floorCurrResamplePos)) + + tempBuffer[LEFT][floorCurrResamplePos+1]*(currResamplePos - floorCurrResamplePos); + playBuffer[slot][LEFT][0].push_back(temp); + if (fileChannels[slot] == 2) { + temp = tempBuffer[RIGHT][floorCurrResamplePos]* (1-(currResamplePos - floorCurrResamplePos)) + + tempBuffer[RIGHT][floorCurrResamplePos+1]*(currResamplePos - floorCurrResamplePos); + playBuffer[slot][RIGHT][0].push_back(temp); + } + currResamplePos += resampleCoeff; + floorCurrResamplePos = floor(currResamplePos); + } + + while ( currResamplePos < tempSamples - 1) { + playBuffer[slot][LEFT][0].push_back( hermiteInterpol(tempBuffer[LEFT][floorCurrResamplePos - 1], + tempBuffer[LEFT][floorCurrResamplePos], + tempBuffer[LEFT][floorCurrResamplePos + 1], + tempBuffer[LEFT][floorCurrResamplePos + 2], + currResamplePos - floorCurrResamplePos) + ); + if (fileChannels[slot] == 2) + playBuffer[slot][RIGHT][0].push_back( hermiteInterpol(tempBuffer[RIGHT][floorCurrResamplePos - 1], + tempBuffer[RIGHT][floorCurrResamplePos], + tempBuffer[RIGHT][floorCurrResamplePos + 1], + tempBuffer[RIGHT][floorCurrResamplePos + 2], + currResamplePos - floorCurrResamplePos) + ); + currResamplePos += resampleCoeff; + floorCurrResamplePos = floor(currResamplePos); + } + + while ( floorCurrResamplePos < double(tempSampleC) ) { + temp = tempBuffer[LEFT][floorCurrResamplePos]* (1-(currResamplePos - floorCurrResamplePos)) + + tempBuffer[LEFT][floorCurrResamplePos+1]*(currResamplePos - floorCurrResamplePos); + playBuffer[slot][LEFT][0].push_back(temp); + if (fileChannels[slot] == 2) { + temp = tempBuffer[RIGHT][floorCurrResamplePos]* (1-(currResamplePos - floorCurrResamplePos)) + + tempBuffer[RIGHT][floorCurrResamplePos+1]*(currResamplePos - floorCurrResamplePos); + playBuffer[slot][RIGHT][0].push_back(temp); + } + currResamplePos += resampleCoeff; + floorCurrResamplePos = floor(currResamplePos); + } + + tempBuffer[LEFT].clear(); + tempBuffer[RIGHT].clear(); + // *************************************************************************** + + totalSampleC[slot] = playBuffer[slot][LEFT][0].size(); + totalSamples[slot] = totalSampleC[slot]-1; // ***** DA VERIFICARE se è -2 ******************************************** + + //sampleRate = APP->engine->getSampleRate() * 2; + + resampled[slot] = true; + } + + calcBiquadLpf(20000.0, sampleRate, 1); + for (unsigned int i = 0; i < totalSampleC[slot]; i++) { + playBuffer[slot][LEFT][1].push_back(biquadLpf(playBuffer[slot][LEFT][0][i])); + if (fileChannels[slot] == 2) + playBuffer[slot][RIGHT][1].push_back(biquadLpf2(playBuffer[slot][RIGHT][0][i])); + } + + vector().swap(displayBuff[slot]); + for (int i = 0; i < floor(totalSampleC[slot]); i = i + floor(totalSampleC[slot]/wavDisp)) + displayBuff[slot].push_back(playBuffer[slot][0][0][i]); + + seconds = totalSampleC[slot] * 0.5 / (APP->engine->getSampleRate()); + minutes = (seconds / 60) % 60; + seconds = seconds % 60; + + timeDisplay[slot] = std::to_string(minutes) + ":"; + if (seconds < 10) + timeDisplay[slot] += "0"; + timeDisplay[slot] += std::to_string(seconds); + + if (loadFromPatch[slot]) { + if (storedPath[slot] != "") + path = storedPath[slot]; + else + //path = "_unknown_.wav"; + path = "_slot"+to_string(slot+1)+".waw"; + } + + char* pathDup = strdup(path.c_str()); + fileDescription[slot] = basename(pathDup); + + if (loadFromPatch[slot]) { + if (storedPath[slot] != "") + fileDescription[slot] = "(!)"+fileDescription[slot]; + } + + // *** CHARs CHECK according to font + std::string tempFileDisplay = fileDescription[slot].substr(0, fileDescription[slot].size()-4); + /* + char tempFileChar; + fileDisplay[slot] = ""; + for (int i = 0; i < int(tempFileDisplay.length()); i++) { + tempFileChar = tempFileDisplay.at(i); + if ( (int(tempFileChar) > 47 && int(tempFileChar) < 58) + || (int(tempFileChar) > 64 && int(tempFileChar) < 123) + || int(tempFileChar) == 45 || int(tempFileChar) == 46 || int(tempFileChar) == 95 ) + fileDisplay[slot] += tempFileChar; + } + fileDisplay[slot] = fileDisplay[slot].substr(0, maxTitleChars); + */ + fileDisplay[slot] = tempFileDisplay.substr(0, maxTitleChars); + + samplerateDisplay[slot] = std::to_string(fileSampleRate[slot]); + channelsDisplay[slot] = std::to_string(fileChannels[slot]) + "ch"; + free(pathDup); + storedPath[slot] = path; + + if (!loadFromPatch[slot]) { + currentFolder = system::getDirectory(path); + createCurrentFolder(currentFolder); + currentFolderV.clear(); + currentFolderV = tempTreeData; + for (unsigned int i = 0; i < currentFolderV.size(); i++) { + if (system::getFilename(path) == system::getFilename(currentFolderV[i])) { + currentFile = i; + i = currentFolderV.size(); + } + } + + if (!firstLoad[slot] || toSave[slot]) { + prevKnobCueStartPos[slot] = -1.f; + prevKnobCueEndPos[slot] = 2.f; + prevKnobLoopStartPos[slot] = -1.f; + prevKnobLoopEndPos[slot] = 2.f; + + if (resetCursorsOnLoad) { + params[CUESTART_PARAM].setValue(0.f); + params[CUEEND_PARAM].setValue(1.f); + params[LOOPSTART_PARAM].setValue(0.f); + params[LOOPEND_PARAM].setValue(1.f); + knobCueStartPos[slot] = 0.f; + knobCueEndPos[slot] = 1.f; + knobLoopStartPos[slot] = 0.f; + knobLoopEndPos[slot] = 1.f; + } + + } + + cueStartPos[slot] = floor(totalSamples[slot] * knobCueStartPos[slot]); + cueEndPos[slot] = ceil(totalSamples[slot] * knobCueEndPos[slot]); + loopStartPos[slot] = floor(totalSamples[slot] * knobLoopStartPos[slot]); + loopEndPos[slot] = ceil(totalSamples[slot] * knobLoopEndPos[slot]); + + scanCueStartSample[slot] = cueStartPos[slot]; + scanCueEndSample[slot] = cueEndPos[slot]; + scanLoopStartSample[slot] = loopStartPos[slot]; + scanLoopEndSample[slot] = loopEndPos[slot]; + + + toSave[slot] = false; + infoToSave[slot] = ""; + + } else { + + // Questi commenti sotto evitano di sovrascrivere i valori dei knob sui diversi slot + // da testare quando vengono caricati slot NON IN PATCH + + //knobCueStartPos[slot] = params[CUESTART_PARAM].getValue(); + //knobCueEndPos[slot] = params[CUEEND_PARAM].getValue(); + prevKnobCueStartPos[slot] = knobCueStartPos[slot]; + prevKnobCueEndPos[slot] = knobCueEndPos[slot]; + //knobLoopStartPos[slot] = params[LOOPSTART_PARAM].getValue(); + //knobLoopEndPos[slot] = params[LOOPEND_PARAM].getValue(); + prevKnobLoopStartPos[slot] = knobLoopStartPos[slot]; + prevKnobLoopEndPos[slot] = knobLoopEndPos[slot]; + + cueStartPos[slot] = floor(totalSamples[slot] * knobCueStartPos[slot]); + cueEndPos[slot] = ceil(totalSamples[slot] * knobCueEndPos[slot]); + loopStartPos[slot] = floor(totalSamples[slot] * knobLoopStartPos[slot]); + loopEndPos[slot] = ceil(totalSamples[slot] * knobLoopEndPos[slot]); + + scanCueStartSample[slot] = cueStartPos[slot]; + scanCueEndSample[slot] = cueEndPos[slot]; + scanLoopStartSample[slot] = loopStartPos[slot]; + scanLoopEndSample[slot] = loopEndPos[slot]; + + toSave[slot] = true; + infoToSave[slot] = "s"; + } + + recSeconds = 0; + recMinutes = 0; + + firstLoad[slot] = false; + + fileLoaded[slot] = true; + channels[slot] = fileChannels[slot]; + + } else { + fileFound[slot] = false; + fileLoaded[slot] = false; + //storedPath = path; + if (loadFromPatch[slot]) + path = storedPath[slot]; + fileDescription[slot] = "(!)"+path; + fileDisplay[slot] = ""; + timeDisplay[slot] = ""; + recSamples = 0; + recTimeDisplay[slot] = ""; + channelsDisplay[slot] = ""; + } + }; + + void clearSlot(int slot) { + fileLoaded[slot] = false; + fileFound[slot] = false; + loadFromPatch[slot] = false; + restoreLoadFromPatch[slot] = false; + storedPath[slot] = ""; + fileDescription[slot] = "--none--"; + fileDisplay[slot] = ""; + timeDisplay[slot] = ""; + recSamples = 0; + recTimeDisplay[slot] = ""; + channelsDisplay[slot] = ""; + playBuffer[slot][LEFT][0].clear(); + playBuffer[slot][RIGHT][0].clear(); + playBuffer[slot][LEFT][1].clear(); + playBuffer[slot][RIGHT][1].clear(); + displayBuff[slot].clear(); + totalSampleC[slot] = 0; + totalSamples[slot] = 0; + resampled[slot] = false; + toSave[slot] = false; + infoToSave[slot] = ""; + for (int slot = 0; slot < 8; slot++) + for (int c=0; c < 16; c++) + fadingValue[slot][c] = 1; + } + + void resetCursors() { + params[CUESTART_PARAM].setValue(0); + params[CUEEND_PARAM].setValue(1); + params[LOOPSTART_PARAM].setValue(0); + params[LOOPEND_PARAM].setValue(1); + } + + void copyCueToLoop() { + loopStartPos[currSlot] = cueStartPos[currSlot]; + loopEndPos[currSlot] = cueEndPos[currSlot]; + params[LOOPSTART_PARAM].setValue(params[CUESTART_PARAM].getValue()); + params[LOOPEND_PARAM].setValue(params[CUEEND_PARAM].getValue()); + prevKnobLoopStartPos[currSlot] = params[CUESTART_PARAM].getValue(); + prevKnobLoopEndPos[currSlot] = params[CUEEND_PARAM].getValue(); + knobLoopStartPos[currSlot] = params[CUESTART_PARAM].getValue(); + knobLoopEndPos[currSlot] = params[CUEEND_PARAM].getValue(); + } + + void copyLoopToCue() { + cueStartPos[currSlot] = loopStartPos[currSlot]; + cueEndPos[currSlot] = loopEndPos[currSlot]; + params[CUESTART_PARAM].setValue(params[LOOPSTART_PARAM].getValue()); + params[CUEEND_PARAM].setValue(params[LOOPEND_PARAM].getValue()); + prevKnobCueStartPos[currSlot] = params[LOOPSTART_PARAM].getValue(); + prevKnobCueEndPos[currSlot] = params[LOOPEND_PARAM].getValue(); + knobCueStartPos[currSlot] = params[LOOPSTART_PARAM].getValue(); + knobCueEndPos[currSlot] = params[LOOPEND_PARAM].getValue(); + } + + void setPreset(int presetNo) { + switch (presetNo) { + case 0: // wavetable + params[PHASESCAN_SWITCH].setValue(0); + params[TRIGGATEMODE_SWITCH].setValue(GATE_MODE); + + params[REV_PARAM].setValue(0.f); + params[XFADE_PARAM].setValue(0.f); + params[LOOP_PARAM].setValue(1.f); + params[PINGPONG_PARAM].setValue(0.f); + + params[CUESTART_PARAM].setValue(0.f); + params[CUEEND_PARAM].setValue(1.f); + params[LOOPSTART_PARAM].setValue(0.f); + params[LOOPEND_PARAM].setValue(1.f); + prevKnobCueStartPos[currSlot] = -1.f; + prevKnobCueEndPos[currSlot] = 2.f; + prevKnobLoopStartPos[currSlot] = -1.f; + prevKnobLoopEndPos[currSlot] = 2.f; + break; + + case 1: // trig with envelope + params[PHASESCAN_SWITCH].setValue(1); + params[TRIGGATEMODE_SWITCH].setValue(TRIG_MODE); + params[TRIGMODE_SWITCH].setValue(START_STOP); + + params[XFADE_PARAM].setValue(0.f); + params[LOOP_PARAM].setValue(0.f); + params[PINGPONG_PARAM].setValue(0.f); + + params[ATTACK_PARAM].setValue(0.33f); + params[DECAY_PARAM].setValue(0.f); + params[SUSTAIN_PARAM].setValue(1.f); + params[RELEASE_PARAM].setValue(0.33f); + + prevKnobCueStartPos[currSlot] = -1.f; + prevKnobCueEndPos[currSlot] = 2.f; + prevKnobLoopStartPos[currSlot] = -1.f; + prevKnobLoopEndPos[currSlot] = 2.f; + break; + + case 2: // drum player + params[PHASESCAN_SWITCH].setValue(0); + params[TRIGGATEMODE_SWITCH].setValue(TRIG_MODE); + params[TRIGMODE_SWITCH].setValue(START_RESTART); + + params[REV_PARAM].setValue(0.f); + params[XFADE_PARAM].setValue(0.0001f); + params[LOOP_PARAM].setValue(0.f); + params[PINGPONG_PARAM].setValue(0.f); + + params[ATTACK_PARAM].setValue(0.f); + params[DECAY_PARAM].setValue(0.f); + params[SUSTAIN_PARAM].setValue(1.f); + params[RELEASE_PARAM].setValue(0.f); + + params[CUESTART_PARAM].setValue(0.f); + params[CUEEND_PARAM].setValue(1.f); + params[LOOPSTART_PARAM].setValue(0.f); + params[LOOPEND_PARAM].setValue(1.f); + prevKnobCueStartPos[currSlot] = -1.f; + prevKnobCueEndPos[currSlot] = 2.f; + prevKnobLoopStartPos[currSlot] = -1.f; + prevKnobLoopEndPos[currSlot] = 2.f; + break; + } + } + + /* + bool isPolyOuts() { + return polyOuts; + } + + void setPolyOuts(bool poly) { + if (poly) + polyOuts = 1; + else { + polyOuts = 0; + //outputs[OUT_OUTPUT].setChannels(1); + //outputs[OUT_OUTPUT+1].setChannels(1); + //outputs[EOC_OUTPUT].setChannels(1); + //outputs[EOR_OUTPUT].setChannels(1); + } + } + */ + + float shapeResponse(float value, int slot) { + + if (shape[slot] < 0.5f) + return (expTable[0][int(expTableCoeff * value)] * (1 - (shape[slot] * 2.f))) + (value * (shape[slot] * 2.f)); + else + return (value * (1 - ((shape[slot] - 0.5f) * 2.f))) + (expTable[1][int(expTableCoeff * value)] * ((shape[slot] - 0.5f) * 2.f)); + + } + + float shapeResponse2(float value, int slot) { + + if (shape[slot] < 0.5f) + return (expTable[1][int(expTableCoeff * value)] * (1 - (shape[slot] * 2.f))) + (value * (shape[slot] * 2.f)); + else + return (value * (1 - ((shape[slot] - 0.5f) * 2.f))) + (expTable[0][int(expTableCoeff * value)] * ((shape[slot] - 0.5f) * 2.f)); + + } + + +// ██████╗░██████╗░░█████╗░░█████╗░███████╗░██████╗░██████╗ +// ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔════╝██╔════╝ +// ██████╔╝██████╔╝██║░░██║██║░░╚═╝█████╗░░╚█████╗░╚█████╗░ +// ██╔═══╝░██╔══██╗██║░░██║██║░░██╗██╔══╝░░░╚═══██╗░╚═══██╗ +// ██║░░░░░██║░░██║╚█████╔╝╚█████╔╝███████╗██████╔╝██████╔╝ +// ╚═╝░░░░░╚═╝░░╚═╝░╚════╝░░╚════╝░╚══════╝╚═════╝░╚═════╝░ + + void process(const ProcessArgs &args) override { + + currSlot = int(params[SLOT_SELECTOR_PARAM].getValue())-1; + if (currSlot != prevSlot) { + params[CUESTART_PARAM].setValue(knobCueStartPos[currSlot]); + params[CUEEND_PARAM].setValue(knobCueEndPos[currSlot]); + params[LOOPSTART_PARAM].setValue(knobLoopStartPos[currSlot]); + params[LOOPEND_PARAM].setValue(knobLoopEndPos[currSlot]); + params[REV_PARAM].setValue(reverseStart[currSlot]); + params[XFADE_PARAM].setValue(xFade[currSlot]); + params[LOOP_PARAM].setValue(loop[currSlot]); + params[PINGPONG_PARAM].setValue(pingpong[currSlot]); + params[TRIGGATEMODE_SWITCH].setValue(trigMode[currSlot]); + params[TRIGMODE_SWITCH].setValue(trigType[currSlot]); + params[PB_SWITCH].setValue(allowPB[currSlot]); + params[PHASESCAN_SWITCH].setValue(phaseScan[currSlot]); + params[ATTACK_PARAM].setValue(attackKnob[currSlot]); + params[DECAY_PARAM].setValue(decayKnob[currSlot]); + params[SUSTAIN_PARAM].setValue(sustainValue[currSlot]); + params[RELEASE_PARAM].setValue(releaseKnob[currSlot]); + params[CURVE_PARAM].setValue(shape[currSlot]); + params[STRETCH_PARAM].setValue(stretchKnob[currSlot]); + params[STR_SIZE_PARAM].setValue(cycleKnob[currSlot]); + params[VOL_PARAM].setValue(volumeValue[currSlot]); + params[OUT_SELECTOR_PARAM].setValue(outRoute[currSlot]+1); + params[RANGE_PARAM].setValue(refKey[currSlot][selectedRange]); + + prevSlot = currSlot; + //DEBUG("current slot %d", currSlot); + } + + // *************** CURRENT SLOT PARAMS + + + reverseStart[currSlot] = params[REV_PARAM].getValue(); + lights[REV_LIGHT].setBrightness(reverseStart[currSlot]); + + xFade[currSlot] = params[XFADE_PARAM].getValue(); + + loop[currSlot] = params[LOOP_PARAM].getValue(); + lights[LOOP_LIGHT].setBrightness(loop[currSlot]); + + pingpong[currSlot] = params[PINGPONG_PARAM].getValue(); + lights[PINGPONG_LIGHT].setBrightness(pingpong[currSlot]); + + trigMode[currSlot] = params[TRIGGATEMODE_SWITCH].getValue(); + trigType[currSlot] = params[TRIGMODE_SWITCH].getValue(); + lights[TRIGMODE_LIGHT].setBrightness(trigType[currSlot]); + + allowPB[currSlot] = params[PB_SWITCH].getValue(); + lights[PB_LIGHT].setBrightness(allowPB[currSlot]); + + phaseScan[currSlot] = params[PHASESCAN_SWITCH].getValue(); + lights[PHASESCAN_LIGHT].setBrightness(phaseScan[currSlot]); + + attackKnob[currSlot] = params[ATTACK_PARAM].getValue(); + if (attackKnob[currSlot] != prevAttackKnob[currSlot]) + calcAttack(currSlot); + + decayKnob[currSlot] = params[DECAY_PARAM].getValue(); + if (decayKnob[currSlot] != prevDecayKnob[currSlot]) + calcDecay(currSlot); + + sustainValue[currSlot] = params[SUSTAIN_PARAM].getValue(); + + releaseKnob[currSlot] = params[RELEASE_PARAM].getValue(); + if (releaseKnob[currSlot] != prevReleaseKnob[currSlot]) + calcRelease(currSlot); + + shape[currSlot] = params[CURVE_PARAM].getValue(); + + stretchKnob[currSlot] = params[STRETCH_PARAM].getValue(); + cycleKnob[currSlot] = params[STR_SIZE_PARAM].getValue(); + stretchMaxPos[currSlot] = oneMsSamples * cycleKnob[currSlot]; + + volumeValue[currSlot] = params[VOL_PARAM].getValue(); + + outRoute[currSlot] = int(params[OUT_SELECTOR_PARAM].getValue())-1; + + refKey[currSlot][selectedRange] = params[RANGE_PARAM].getValue(); + + trigRange = params[RANGE_SELECTOR_PARAM].getValue(); + if (!learn && !storedLearn) { + learnButLightValue = trigRange; + } else { + if (learningButLight) { + if (learnButLightValue > 1 || learnButLightValue < 0) { + learnButLightDelta *= -1; + } + learnButLightValue += learnButLightDelta; + } + } + + if (trigRange && !prevTrigRange) { + if (!waitingLearnEnter) { + waitingLearnEnter = true; + waitingLearnEnterTime = learnSamples; + if (learn) { + learn = false; + learnButLightValue = 0.f; + learningButLight = false; + selectedRange--; + if (selectedRange < LOKEY) + selectedRange = REFKEY; + params[RANGE_PARAM].setValue(refKey[currSlot][selectedRange]); + } + } + } else if (!trigRange && prevTrigRange) { // controlla il rilascio del tasto learn + if (!learn) { + selectedRange++; + if (selectedRange > REFKEY) + selectedRange = LOKEY; + params[RANGE_PARAM].setValue(refKey[currSlot][selectedRange]); + } + waitingLearnEnter = false; + } + prevTrigRange = trigRange; + + if (waitingLearnEnter) { + waitingLearnEnterTime--; + if (waitingLearnEnterTime < 0) { + waitingLearnEnter = false; + learn = true; + learningButLight = true; + learnButLightValue = 0.f; + prevLearnKey = -11.f; + } + } + + chan = std::max(1, inputs[GATE_INPUT].getChannels()); + + if (learn) { + for (int c = 0; c < chan; c++) { + learnKey = inputs[GATE_INPUT].getVoltage(c); + if (learnKey >= 1.f && prevLearnKey < 1.f) { + keyToStore = inputs[VO_INPUT].getVoltage(c); + } else if (learnKey < 1.f && prevLearnKey >= 1.f) { + learn = false; + + learningButLight = false; + learnButLightValue = 1.f; + + storedLearn = true; + learnPulseTime = lightPulseTime; + learnExitButStatus = true; + storedLearnTime = maxStoredLearnTime; + + refKey[currSlot][selectedRange] = int((keyToStore + 6.041666666666667f) * 12); + + if (refKey[currSlot][selectedRange] < 0) + refKey[currSlot][selectedRange] = 0; + else if (refKey[currSlot][selectedRange] > 127) + refKey[currSlot][selectedRange] = 127; + + params[RANGE_PARAM].setValue(refKey[currSlot][selectedRange]); + + } + prevLearnKey = learnKey; + } + + } + + if (storedLearn) { + storedLearnTime--; + if (storedLearnTime < 0) { + lights[RANGE_SELECTOR_LIGHT].setBrightness(0); + storedLearn = false; + } else { + learnPulseTime--; + if (learnPulseTime < 0) { + learnPulseTime = lightPulseTime; + learnExitButStatus = !learnExitButStatus; + learnButLightValue = learnExitButStatus; + } + } + } + + lights[RANGE_SELECTOR_LIGHT].setBrightness(learnButLightValue); + + if (selectedRange == LOKEY && refKey[currSlot][LOKEY] > refKey[currSlot][HIKEY]) { + refKey[currSlot][LOKEY] = refKey[currSlot][HIKEY]; + params[RANGE_PARAM].setValue(refKey[currSlot][HIKEY]); + } else if (selectedRange == HIKEY && refKey[currSlot][HIKEY] < refKey[currSlot][LOKEY]) { + refKey[currSlot][HIKEY] = refKey[currSlot][LOKEY]; + params[RANGE_PARAM].setValue(refKey[currSlot][LOKEY]); + } + + // ************* NAV BUTTONS ******************* + + if (!disableNav && !loadFromPatch[currSlot]) { + nextSample = params[NEXTSAMPLE_PARAM].getValue(); + if (fileLoaded[currSlot] && fileFound[currSlot] && recordingState == 0 && nextSample && !prevNextSample) { + for (int i = 0; i < 16; i++) + play[currSlot][i] = false; + currentFile++; + if (currentFile >= int(currentFolderV.size())) + currentFile = 0; + loadSample(currentFolderV[currentFile], currSlot); + } + prevNextSample = nextSample; + + prevSample = params[PREVSAMPLE_PARAM].getValue(); + if (fileLoaded[currSlot] && fileFound[currSlot] && recordingState == 0 && prevSample && !prevPrevSample) { + for (int i = 0; i < 16; i++) + play[currSlot][i] = false; + currentFile--; + if (currentFile < 0) + currentFile = currentFolderV.size()-1; + loadSample(currentFolderV[currentFile], currSlot); + } + prevPrevSample = prevSample; + } + + + // ************************ + + monitorSwitch = int(params[MONITOR_SWITCH].getValue()); + lights[MONITOR_LIGHT].setBrightness(monitorSwitch); + + + velConnected = inputs[VEL_INPUT].isConnected(); + + pbConnected = inputs[PB_INPUT].isConnected(); + if (pbConnected) { + pitchBend = inputs[PB_INPUT].getVoltage(); + //pbValue = pitchBend / 5 / 6; + //pbValue = pitchBend / 30; + pbValue = pitchBend / pbRangeTable[pbRange]; + } else { + pbValue = 0.f; + } + + //trigButValue = params[TESTBUT_PARAM].getValue(); + //lights[TESTBUT_LIGHT].setBrightness(trigButValue); + + + // *********************************************************************************************** PLAY SECTION ******************************* + + sumOutput[0] = 0; + sumOutputR[0] = 0; + sumOutput[1] = 0; + sumOutputR[1] = 0; + sumOutput[2] = 0; + sumOutputR[2] = 0; + sumOutput[3] = 0; + sumOutputR[3] = 0; + sumOutput[4] = 0; + sumOutputR[4] = 0; + sumOutput[5] = 0; + sumOutputR[5] = 0; + sumOutput[6] = 0; + sumOutputR[6] = 0; + sumOutput[7] = 0; + sumOutputR[7] = 0; + currentOutput = 0; + currentOutputR = 0; + + if (!fileLoaded[currSlot]) { + + for (int c = 0; c < chan; c++) { + play[currSlot][c] = false; + fadingType[currSlot][c] = NO_FADE; + stage[currSlot][c] = STOP_STAGE; + stageLevel[currSlot][c] = 0; + env[currSlot][c] = 0; + voct[currSlot][c] = 0.f; + prevVoctDiffFloat[currSlot][c] = 11.f; + outputs[LEFT_OUTPUT+outRoute[currSlot]].setVoltage(0, c); + outputs[RIGHT_OUTPUT+outRoute[currSlot]].setVoltage(0, c); + } + + } else { + knobCueEndPos[currSlot] = params[CUEEND_PARAM].getValue(); + if (knobCueEndPos[currSlot] != prevKnobCueEndPos[currSlot]) { + if (knobCueEndPos[currSlot] < prevKnobCueEndPos[currSlot]) + cueEndPos[currSlot] = floor(totalSamples[currSlot] * knobCueEndPos[currSlot]); + else + cueEndPos[currSlot] = ceil(totalSamples[currSlot] * knobCueEndPos[currSlot]); + prevKnobCueEndPos[currSlot] = knobCueEndPos[currSlot]; + searchingCueEndPhase[currSlot] = true; + scanCueEndSample[currSlot] = cueEndPos[currSlot]; + if (cueEndPos[currSlot] < cueStartPos[currSlot]) + cueEndPos[currSlot] = cueStartPos[currSlot]; + } + knobCueStartPos[currSlot] = params[CUESTART_PARAM].getValue(); + if (knobCueStartPos[currSlot] != prevKnobCueStartPos[currSlot]) { + if (knobCueStartPos[currSlot] < prevKnobCueStartPos[currSlot]) + cueStartPos[currSlot] = floor(totalSamples[currSlot] * knobCueStartPos[currSlot]); + else + cueStartPos[currSlot] = ceil(totalSamples[currSlot] * knobCueStartPos[currSlot]); + prevKnobCueStartPos[currSlot] = knobCueStartPos[currSlot]; + searchingCueStartPhase[currSlot] = true; + scanCueStartSample[currSlot] = cueStartPos[currSlot]; + if (cueStartPos[currSlot] > cueEndPos[currSlot]) + cueStartPos[currSlot] = cueEndPos[currSlot]; + } + knobLoopEndPos[currSlot] = params[LOOPEND_PARAM].getValue(); + if (knobLoopEndPos[currSlot] != prevKnobLoopEndPos[currSlot]) { + if (knobLoopEndPos[currSlot] < prevKnobLoopEndPos[currSlot]) + loopEndPos[currSlot] = floor(totalSamples[currSlot] * knobLoopEndPos[currSlot]); + else + loopEndPos[currSlot] = ceil(totalSamples[currSlot] * knobLoopEndPos[currSlot]); + prevKnobLoopEndPos[currSlot] = knobLoopEndPos[currSlot]; + searchingLoopEndPhase[currSlot] = true; + scanLoopEndSample[currSlot] = loopEndPos[currSlot]; + if (loopEndPos[currSlot] < loopStartPos[currSlot]) + loopEndPos[currSlot] = loopStartPos[currSlot]; + } + knobLoopStartPos[currSlot] = params[LOOPSTART_PARAM].getValue(); + if (knobLoopStartPos[currSlot] != prevKnobLoopStartPos[currSlot]) { + if (knobLoopStartPos[currSlot] < prevKnobLoopStartPos[currSlot]) + loopStartPos[currSlot] = floor(totalSamples[currSlot] * knobLoopStartPos[currSlot]); + else + loopStartPos[currSlot] = ceil(totalSamples[currSlot] * knobLoopStartPos[currSlot]); + prevKnobLoopStartPos[currSlot] = knobLoopStartPos[currSlot]; + searchingLoopStartPhase[currSlot] = true; + scanLoopStartSample[currSlot] = loopStartPos[currSlot]; + if (loopStartPos[currSlot] > loopEndPos[currSlot]) + loopStartPos[currSlot] = loopEndPos[currSlot]; + } + + + if (phaseScan[currSlot] && !prevPhaseScan[currSlot]) { + prevPhaseScan[currSlot] = true; + searchingCueEndPhase[currSlot] = true; + searchingCueStartPhase[currSlot] = true; + searchingLoopEndPhase[currSlot] = true; + searchingLoopStartPhase[currSlot] = true; + } + + if (phaseScan[currSlot]) { + float tempKnob; + if (searchingCueEndPhase[currSlot]) { + if (playBuffer[currSlot][LEFT][antiAlias][scanCueEndSample[currSlot]-1] <= 0 && playBuffer[currSlot][LEFT][antiAlias][scanCueEndSample[currSlot]] >= 0) { + cueEndPos[currSlot] = scanCueEndSample[currSlot]; + searchingCueEndPhase[currSlot] = false; + tempKnob = cueEndPos[currSlot]/totalSamples[currSlot]; + params[CUEEND_PARAM].setValue(tempKnob); + knobCueEndPos[currSlot] = tempKnob; + } else { + scanCueEndSample[currSlot]--; + if (scanCueEndSample[currSlot] < cueStartPos[currSlot]) { + cueEndPos[currSlot] = cueStartPos[currSlot]; + searchingCueEndPhase[currSlot] = false; + tempKnob = cueEndPos[currSlot]/totalSamples[currSlot]; + params[CUEEND_PARAM].setValue(tempKnob); + knobCueEndPos[currSlot] = tempKnob; + + } + } + + } + if (searchingCueStartPhase[currSlot]) { + if (playBuffer[currSlot][LEFT][antiAlias][scanCueStartSample[currSlot]+1] >= 0 && playBuffer[currSlot][LEFT][antiAlias][scanCueStartSample[currSlot]] <= 0) { + cueStartPos[currSlot] = scanCueStartSample[currSlot]; + searchingCueStartPhase[currSlot] = false; + tempKnob = cueStartPos[currSlot]/totalSamples[currSlot]; + params[CUESTART_PARAM].setValue(tempKnob); + prevKnobCueStartPos[currSlot] = tempKnob; + } else { + scanCueStartSample[currSlot]++; + if (scanCueStartSample[currSlot] > cueEndPos[currSlot]) { + cueStartPos[currSlot] = cueEndPos[currSlot]; + searchingCueStartPhase[currSlot] = false; + tempKnob = cueStartPos[currSlot]/totalSamples[currSlot]; + params[CUESTART_PARAM].setValue(tempKnob); + prevKnobCueStartPos[currSlot] = tempKnob; + } + } + } + if (searchingLoopEndPhase[currSlot]) { + if (playBuffer[currSlot][LEFT][antiAlias][scanLoopEndSample[currSlot]-1] <= 0 && playBuffer[currSlot][LEFT][antiAlias][scanLoopEndSample[currSlot]] >= 0) { + loopEndPos[currSlot] = scanLoopEndSample[currSlot]; + searchingLoopEndPhase[currSlot] = false; + tempKnob = loopEndPos[currSlot]/totalSamples[currSlot]; + params[LOOPEND_PARAM].setValue(tempKnob); + prevKnobLoopEndPos[currSlot] = tempKnob; + } else { + scanLoopEndSample[currSlot]--; + if (scanLoopEndSample[currSlot] < loopStartPos[currSlot]) { + loopEndPos[currSlot] = loopStartPos[currSlot]; + searchingLoopEndPhase[currSlot] = false; + tempKnob = loopEndPos[currSlot]/totalSamples[currSlot]; + params[LOOPEND_PARAM].setValue(tempKnob); + prevKnobLoopEndPos[currSlot] = tempKnob; + } + } + } + if (searchingLoopStartPhase[currSlot]) { + if (playBuffer[currSlot][LEFT][antiAlias][scanLoopStartSample[currSlot]+1] >= 0 && playBuffer[currSlot][LEFT][antiAlias][scanLoopStartSample[currSlot]] <= 0) { + loopStartPos[currSlot] = scanLoopStartSample[currSlot]; + searchingLoopStartPhase[currSlot] = false; + tempKnob = loopStartPos[currSlot]/totalSamples[currSlot]; + params[LOOPSTART_PARAM].setValue(tempKnob); + prevKnobLoopStartPos[currSlot] = tempKnob; + } else { + scanLoopStartSample[currSlot]++; + if (scanLoopStartSample[currSlot] > loopEndPos[currSlot]) { + loopStartPos[currSlot] = loopEndPos[currSlot]; + searchingLoopStartPhase[currSlot] = false; + tempKnob = loopStartPos[currSlot]/totalSamples[currSlot]; + params[LOOPSTART_PARAM].setValue(tempKnob); + prevKnobLoopStartPos[currSlot] = tempKnob; + } + } + } + + } else { + prevPhaseScan[currSlot] = false; + } + + + //trigMode[currSlot] = params[TRIGGATEMODE_SWITCH].getValue(); + //trigType[currSlot] = params[TRIGMODE_SWITCH].getValue(); + + //sustainValue[currSlot] = params[SUSTAIN_PARAM].getValue(); + + if (xFade[currSlot] != prevXfade[currSlot]) { + if (xFade[currSlot] == 0) + fadeSamples[currSlot] = 0; + else + fadeSamples[currSlot] = floor(convertCVToSeconds(xFade[currSlot]) * vcvSampleRate); // number of samples before starting fade + + prevXfade[currSlot] = xFade[currSlot]; + } + + } + + // ********************************************************************* START CHANNEL MANAGEMENT + + if (!learn) { + + voctDisplay = 100.f; + + for (int c = 0; c < chan; c++) { + + vOctKey = inputs[VO_INPUT].getVoltage(c); + + if (vOctKey < -6.039) + vOctNote = 0; + else if (vOctKey > 4.624) + vOctNote = 127; + + vOctNote = floor((vOctKey + 6.041666666666667f) * 12); + + velKey = inputs[VEL_INPUT].getVoltage(c); + + for (int slot = 0; slot < 8; slot++) { + + if (!fileLoaded[slot]) { + + play[slot][c] = false; + fadingType[slot][c] = NO_FADE; + stage[slot][c] = STOP_STAGE; + stageLevel[slot][c] = 0; + env[slot][c] = 0; + voct[slot][c] = 0.f; + prevVoctDiffFloat[slot][c] = 11.f; + + } else { + + if (vOctKey >= noteTable[refKey[slot][LOKEY]][1] && vOctKey < noteTable[refKey[slot][HIKEY]][2]) { + //if (vOctNote >= refKey[slot][LOKEY] && vOctNote <= refKey[slot][HIKEY]) { + + masterLevel[slot][c] = volumeValue[slot]; + if (velConnected) + masterLevel[slot][c] *= velKey * 0.1f; + + //if (c == 0 && slot == currSlot && trigButValue) + // trigValue[slot][c] = 1; + //else + trigValue[slot][c] = inputs[GATE_INPUT].getVoltage(c); + + switch (trigMode[slot]) { + case GATE_MODE: // ***** GATE MODE ***** + if (trigValue[slot][c] >= 1) { + if (!play[slot][c]) { + play[slot][c] = true; + + grainSampleCount[slot][c] = 0; + grainCount[slot][c] = 1; + grainFade[slot][c] = false; + + if (reverseStart[slot]) { + reversePlaying[slot][c] = REVERSE; + samplePos[slot][c] = floor(cueEndPos[slot]-1); + currSampleWeight[slot][c] = sampleCoeff; + } else { + reversePlaying[slot][c] = FORWARD; + samplePos[slot][c] = floor(cueStartPos[slot]+1); + currSampleWeight[slot][c] = sampleCoeff; + } + stage[slot][c] = ATTACK_STAGE; + refValue[slot][c] = 0; + deltaValue[slot][c] = 1.f; + slopeCorr[slot][c] = 1.f; + stageCoeff[slot][c] = envSrCoeff / attackValue[slot]; + } else { + if (stage[slot][c] == RELEASE_STAGE) { + stage[slot][c] = ATTACK_STAGE; + stageLevel[slot][c] = 0; + refValue[slot][c] = env[slot][c]; + deltaValue[slot][c] = 1-env[slot][c]; + slopeCorr[slot][c] = 1-env[slot][c]; + stageCoeff[slot][c] = envSrCoeff / attackValue[slot]; + } + } + } else { + if (play[slot][c]) { + + if (stage[slot][c] != RELEASE_STAGE) { + stage[slot][c] = RELEASE_STAGE; + //releaseNew[slot][c] = true; + + /* + if (stageLevel[slot][c] != 0) + stageCoeff[slot][c] = stageLevel[slot][c] / (vcvSampleRate * releaseValue[slot]); + else + stageCoeff[slot][c] = 1; + */ + + stageLevel[slot][c] = 1; + refValue[slot][c] = 0; + deltaValue[slot][c] = env[slot][c]; + stageCoeff[slot][c] = envSrCoeff / releaseValue[slot]; + } + } + } + break; + + case TRIG_MODE: // ***** TRIG MODE ***** + + if ((trigValue[slot][c] >= 1 && prevTrigValue[slot][c] < 1)) { + + switch (trigType[slot]) { + case START_STOP: // trig type: Start/Stop + + if (play[slot][c]) { + + if (stage[slot][c] != RELEASE_STAGE) { + + stage[slot][c] = RELEASE_STAGE; + //releaseNew[slot][c] = true; + + /* + if (stageLevel[slot][c] != 0) + stageCoeff[slot][c] = stageLevel[slot][c] / (vcvSampleRate * releaseValue[slot]); + else + stageCoeff[slot][c] = 1; + */ + stageLevel[slot][c] = 1; + refValue[slot][c] = 0; + deltaValue[slot][c] = env[slot][c]; + stageCoeff[slot][c] = envSrCoeff / releaseValue[slot]; + + } else { + stage[slot][c] = ATTACK_STAGE; + //stageCoeff[slot][c] = (1-stageLevel[slot][c]) / (vcvSampleRate * attackValue[slot]); + stageLevel[slot][c] = 0; + refValue[slot][c] = env[slot][c]; + deltaValue[slot][c] = 1-env[slot][c]; + slopeCorr[slot][c] = 1-env[slot][c]; + stageCoeff[slot][c] = envSrCoeff / attackValue[slot]; + } + + } else { + play[slot][c] = true; + + grainSampleCount[slot][c] = 0; + grainCount[slot][c] = 1; + grainFade[slot][c] = false; + + if (reverseStart[slot]) { + reversePlaying[slot][c] = REVERSE; + samplePos[slot][c] = floor(cueEndPos[slot]-1); + currSampleWeight[slot][c] = sampleCoeff; + } else { + reversePlaying[slot][c] = FORWARD; + samplePos[slot][c] = floor(cueStartPos[slot]+1); + currSampleWeight[slot][c] = sampleCoeff; + } + stage[slot][c] = ATTACK_STAGE; + //stageCoeff[slot][c] = (1-stageLevel[slot][c]) / (vcvSampleRate * attackValue[slot]); + stageLevel[slot][c] = 0; + refValue[slot][c] = 0; + deltaValue[slot][c] = 1.f; + slopeCorr[slot][c] = 1.f; + stageCoeff[slot][c] = envSrCoeff / attackValue[slot]; + + } + break; + + case START_RESTART: // trig type: START RESTART + if (!play[slot][c]) { // ******* START PLAYBACK + + play[slot][c] = true; + + grainSampleCount[slot][c] = 0; + grainCount[slot][c] = 1; + grainFade[slot][c] = false; + + if (reverseStart[slot]) { + reversePlaying[slot][c] = REVERSE; + samplePos[slot][c] = floor(cueEndPos[slot]-1); + currSampleWeight[slot][c] = sampleCoeff; + } else { + reversePlaying[slot][c] = FORWARD; + samplePos[slot][c] = floor(cueStartPos[slot]+1); + currSampleWeight[slot][c] = sampleCoeff; + } + stage[slot][c] = ATTACK_STAGE; + //stageCoeff[slot][c] = (1-stageLevel[slot][c]) / (vcvSampleRate * attackValue[slot]); + stageLevel[slot][c] = 0; + refValue[slot][c] = 0; + deltaValue[slot][c] = 1.f; + slopeCorr[slot][c] = 1.f; + stageCoeff[slot][c] = envSrCoeff / attackValue[slot]; + + } else { // ******* RESTART PLAYBACK + + grainSampleCount[slot][c] = 0; + grainCount[slot][c] = 1; + grainFade[slot][c] = false; + + fadingValue[slot][c] = 1.f; + fadedPosition[slot][c] = samplePos[slot][c]; + if (fadeSamples[slot]) + fadeCoeff[slot] = 1 / convertCVToSeconds(xFade[slot]) / vcvSampleRate; + else + fadeCoeff[slot] = 1; + fadingType[slot][c] = CROSS_FADE; + if (reverseStart[slot]) { + reversePlaying[slot][c] = REVERSE; + samplePos[slot][c] = floor(cueEndPos[slot]-1); + currSampleWeight[slot][c] = sampleCoeff; + } else { + reversePlaying[slot][c] = FORWARD; + samplePos[slot][c] = floor(cueStartPos[slot]+1); + currSampleWeight[slot][c] = sampleCoeff; + } + + // *********** ATTENZIONE IN CASO DI RESTART VERRA' FATTO UN SEMPLICE CROSSFADE LINEARE PER LA DURATA DI XFADE KNOB + //stage[slot][c] = ATTACK_STAGE; + //stageCoeff[slot][c] = (1-stageLevel[slot][c]) / (vcvSampleRate * attackValue[slot]); + + /* + stageLevel[slot][c] = 0; + refValue[slot][c] = 0; + deltaValue[slot][c] = 1.f; + slopeCorr[slot][c] = 1.f; + stageCoeff[slot][c] = envSrCoeff / attackValue[slot]; + */ + } + break; + + } + } + prevTrigValue[slot][c] = trigValue[slot][c]; + + break; + } + + currentOutput = 0.f; + currentOutputR = 0.f; + + if (play[slot][c]) { + + + voct[slot][c] = vOctKey; + + if (quantize) { + + vOctDiff = vOctNote - refKey[slot][REFKEY]; + + if (allowPB[slot] && pbValue != 0) { + speedVoct[slot][c] = pow(2,noteTable[72+vOctDiff][0] + pbValue); + } else if (vOctDiff >= 0) { + speedVoct[slot][c] = semiTonesPos[vOctDiff]; + } else { + speedVoct[slot][c] = semiTonesNeg[refKey[slot][REFKEY] - vOctNote]; + } + + } else { + + vOctDiffFloat = vOctKey - noteTable[refKey[slot][REFKEY]][0]; + + if (allowPB[slot] && pbValue != 0) { + speedVoct[slot][c] = pow(2,vOctDiffFloat + pbValue); + } else if (vOctDiffFloat != prevVoctDiffFloat[slot][c]) { + speedVoct[slot][c] = pow(2,vOctDiffFloat); + prevVoctDiffFloat[slot][c] = vOctDiffFloat; + } + + } + + distancePos[slot][c] = speedVoct[slot][c] * sampleCoeff; + + + //if (slot == currSlot && play[slot][c] && voct[slot][c] <= voctDisplay) { + if (slot == currSlot && voct[slot][c] <= voctDisplay) { + displaySlot = slot; + currentDisplay = c; + voctDisplay = voct[slot][c]; + } + + switch (reversePlaying[slot][c]) { + case FORWARD: // ******************************************************************************************** FORWARD PLAYBACK *********** + + if (loop[slot] && samplePos[slot][c] > floor(loopEndPos[slot] - (fadeSamples[slot] * distancePos[slot][c]))) { // *** REACHED END OF LOOP *** + + grainSampleCount[slot][c] = 0; + grainCount[slot][c] = 1; + grainFade[slot][c] = false; + + fadingValue[slot][c] = 1.f; + fadedPosition[slot][c] = samplePos[slot][c]; + if (fadeSamples[slot]) + fadeCoeff[slot] = 1 / convertCVToSeconds(xFade[slot]) / vcvSampleRate; + else + fadeCoeff[slot] = 1; + + if (pingpong[slot]) { + fadingType[slot][c] = PINGPONG_FADE; + reversePlaying[slot][c] = REVERSE; + samplePos[slot][c] = floor(loopEndPos[slot])-1; + currSampleWeight[slot][c] = sampleCoeff; + } else { + fadingType[slot][c] = CROSS_FADE; + samplePos[slot][c] = floor(loopStartPos[slot])+1; + currSampleWeight[slot][c] = sampleCoeff; + } + + } else if (!fadingType[slot][c] && floor(samplePos[slot][c]) > (totalSamples[slot] - (fadeSamples[slot] * distancePos[slot][c]))) { + + fadingType[slot][c] = FADE_OUT; + fadingValue[slot][c] = 1.f; + fadedPosition[slot][c] = samplePos[slot][c]; + if (fadeSamples[slot]) + fadeCoeff[slot] = 1 / convertCVToSeconds(xFade[slot]) / vcvSampleRate; + else + fadeCoeff[slot] = 1; + + } else if (floor(samplePos[slot][c]) > totalSamples[slot]) { // *** REACHED END OF SAMPLE *** + + play[slot][c] = false; + + } else if (samplePos[slot][c] > cueEndPos[slot]) { // *** REACHED CUE END *** + + if (stage[slot][c] != RELEASE_STAGE) { + stage[slot][c] = RELEASE_STAGE; + //releaseNew[slot][c] = true; + + /* + if (stageLevel[slot][c] != 0) + stageCoeff[slot][c] = stageLevel[slot][c] / (vcvSampleRate * releaseValue[slot]); + else + stageCoeff[slot][c] = 1; + */ + + } + if (trigMode[slot] == GATE_MODE) { + if (pingpong[slot]) { + grainSampleCount[slot][c] = 0; + grainCount[slot][c] = 1; + grainFade[slot][c] = false; + + reversePlaying[slot][c] = REVERSE; + samplePos[slot][c] = floor(cueEndPos[slot])-1; + currSampleWeight[slot][c] = sampleCoeff; + } else { + grainSampleCount[slot][c] = 0; + grainCount[slot][c] = 1; + grainFade[slot][c] = false; + + fadingType[slot][c] = CROSS_FADE; + fadingValue[slot][c] = 1.f; + fadedPosition[slot][c] = samplePos[slot][c]; + samplePos[slot][c] = floor(cueStartPos[slot])+1; + currSampleWeight[slot][c] = sampleCoeff; + } + } + + } + break; + + case REVERSE: // ******************************************************************************************** REVERSE PLAYBACK *********** + + if (loop[slot] && samplePos[slot][c] < floor(loopStartPos[slot] + (fadeSamples[slot] * distancePos[slot][c]))) { // *** REACHED BEGIN OF LOOP *** + + fadingValue[slot][c] = 1.f; + fadedPosition[slot][c] = samplePos[slot][c]; + if (fadeSamples[slot]) + fadeCoeff[slot] = 1 / convertCVToSeconds(xFade[slot]) / vcvSampleRate; + else + fadeCoeff[slot] = 1; + + if (pingpong[slot]) { + fadingType[slot][c] = PINGPONG_FADE; + reversePlaying[slot][c] = FORWARD; + + grainSampleCount[slot][c] = 0; + grainCount[slot][c] = 1; + grainFade[slot][c] = false; + + samplePos[slot][c] = floor(loopStartPos[slot])+1; + currSampleWeight[slot][c] = sampleCoeff; + } else { + fadingType[slot][c] = CROSS_FADE; + + grainSampleCount[slot][c] = 0; + grainCount[slot][c] = 1; + grainFade[slot][c] = false; + + samplePos[slot][c] = floor(loopEndPos[slot])-1; + currSampleWeight[slot][c] = sampleCoeff; + } + + } else if (!fadingType[slot][c] && floor(samplePos[slot][c]) < (fadeSamples[slot] * distancePos[slot][c])) { + + fadingType[slot][c] = FADE_OUT; + fadingValue[slot][c] = 1.f; + fadedPosition[slot][c] = samplePos[slot][c]; + if (fadeSamples[slot]) + fadeCoeff[slot] = 1 / convertCVToSeconds(xFade[slot]) / vcvSampleRate; + else + fadeCoeff[slot] = 1; + + } else if (floor(samplePos[slot][c]) < 0) { // *** REACHED START OF SAMPLE *** + + play[slot][c] = false; + + } else if (samplePos[slot][c] < cueStartPos[slot]) { // *** REACHED CUE START *** + + if (stage[slot][c] != RELEASE_STAGE) { + stage[slot][c] = RELEASE_STAGE; + //releaseNew[slot][c] = true; + + /* + if (stageLevel[slot][c] != 0) + stageCoeff[slot][c] = stageLevel[slot][c] / (vcvSampleRate * releaseValue[slot]); + else + stageCoeff[slot][c] = 1; + */ + + } + if (trigMode[slot] == GATE_MODE) { + if (pingpong[slot]) { + reversePlaying[slot][c] = FORWARD; + + grainSampleCount[slot][c] = 0; + grainCount[slot][c] = 1; + grainFade[slot][c] = false; + + samplePos[slot][c] = floor(cueStartPos[slot])+1; + currSampleWeight[slot][c] = sampleCoeff; + } else { + fadingType[slot][c] = CROSS_FADE; + fadingValue[slot][c] = 1.f; + fadedPosition[slot][c] = samplePos[slot][c]; + + grainSampleCount[slot][c] = 0; + grainCount[slot][c] = 1; + grainFade[slot][c] = false; + + samplePos[slot][c] = floor(cueEndPos[slot])-1; + currSampleWeight[slot][c] = sampleCoeff; + } + } + } + + break; + } + + if (play[slot][c]) { // it's false only if end of sample has reached, see above + + // *** SICKOSAMPLER USES HERMITE INTERPOLATION ONLY *** + if (currSampleWeight[slot][c] == 0) { // if no distance between samples, it means that speed is 1 and samplerates match -> no interpolation + + currentOutput = playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])]; + if (channels[slot] == 2) + currentOutputR = playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])]; + + } else { + if (floor(samplePos[slot][c]) > 0 && floor(samplePos[slot][c]) < totalSamples[slot] - 1) { + + // below is translation of the above function + double a1 = .5F * (playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])+1] - playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])-1]); + double a2 = playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])-1] - (2.5F * playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])]) + (2 * playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])+1]) - (.5F * playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])+2]); + double a3 = (.5F * (playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])+2] - playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])-1])) + (1.5F * (playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])] - playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])+1])); + currentOutput = (((((a3 * currSampleWeight[slot][c]) + a2) * currSampleWeight[slot][c]) + a1) * currSampleWeight[slot][c]) + playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])]; + if (channels[slot] == 2) { + a1 = .5F * (playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])+1] - playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])-1]); + a2 = playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])-1] - (2.5F * playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])]) + (2 * playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])+1]) - (.5F * playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])+2]); + a3 = (.5F * (playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])+2] - playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])-1])) + (1.5F * (playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])] - playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])+1])); + currentOutputR = (((((a3 * currSampleWeight[slot][c]) + a2) * currSampleWeight[slot][c]) + a1) * currSampleWeight[slot][c]) + playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])]; + } + + } else { // if playing sample is the first or one of the last 3 -> no interpolation + + if (floor(samplePos[slot][c]) >= 0 && floor(samplePos[slot][c]) < totalSampleC[slot]) { + currentOutput = playBuffer[slot][LEFT][antiAlias][floor(samplePos[slot][c])]; + if (channels[slot] == 2) + currentOutputR = playBuffer[slot][RIGHT][antiAlias][floor(samplePos[slot][c])]; + } + } + } + + + // ******************************************************** G R A I N F A D E *********************************************** + + if (grainFade[slot][c]) { + grainFadeValue[slot][c] -= grainFadeCoeff[slot][c]; + if (grainFadeValue[slot][c] < 0) { + grainFade[slot][c] = false; + } else { + float tempGrainFadeOutput; + + if (floor(grainPos[slot][c]) > 0 && floor(grainPos[slot][c]) < totalSamples[slot] - 1) { + + tempGrainFadeOutput = hermiteInterpol(playBuffer[slot][LEFT][antiAlias][floor(grainPos[slot][c])-1], + playBuffer[slot][LEFT][antiAlias][floor(grainPos[slot][c])], + playBuffer[slot][LEFT][antiAlias][floor(grainPos[slot][c])+1], + playBuffer[slot][LEFT][antiAlias][floor(grainPos[slot][c])+2], + currSampleWeight[slot][c]); + currentOutput = (currentOutput * (1-grainFadeValue[slot][c])) + (tempGrainFadeOutput * grainFadeValue[slot][c]); + if (channels[slot] == 2) { + tempGrainFadeOutput = hermiteInterpol(playBuffer[slot][RIGHT][antiAlias][floor(grainPos[slot][c])-1], + playBuffer[slot][RIGHT][antiAlias][floor(grainPos[slot][c])], + playBuffer[slot][RIGHT][antiAlias][floor(grainPos[slot][c])+1], + playBuffer[slot][RIGHT][antiAlias][floor(grainPos[slot][c])+2], + currSampleWeight[slot][c]); + currentOutputR = (currentOutputR * (1-grainFadeValue[slot][c])) + (tempGrainFadeOutput * grainFadeValue[slot][c]); + } + + } else { // if playing sample is the first or one of the last 3 -> no interpolation + + if (floor(grainPos[slot][c]) >= 0 && floor(grainPos[slot][c]) < totalSampleC[slot]) { + tempGrainFadeOutput = playBuffer[slot][LEFT][antiAlias][floor(grainPos[slot][c])]; + currentOutput = (currentOutput * (1-grainFadeValue[slot][c])) + (tempGrainFadeOutput * grainFadeValue[slot][c]); + if (channels[slot] == 2) { + tempGrainFadeOutput = playBuffer[slot][RIGHT][antiAlias][floor(grainPos[slot][c])]; + currentOutputR = (currentOutputR * (1-grainFadeValue[slot][c])) + (tempGrainFadeOutput * grainFadeValue[slot][c]); + } + } + } + + if (!reversePlaying[slot][c]) + grainPos[slot][c] += distancePos[slot][c]; + else + grainPos[slot][c] -= distancePos[slot][c]; + } + } + + // --------------------- A D S R + + switch (stage[slot][c]) { + case ATTACK_STAGE: + stageLevel[slot][c] += stageCoeff[slot][c] / slopeCorr[slot][c]; + + if (stageLevel[slot][c] >= 1) { + stageLevel[slot][c] = 1; + refValue[slot][c] = sustainValue[slot]; + deltaValue[slot][c] = 1.f - sustainValue[slot]; + + stage[slot][c] = DECAY_STAGE; + //stageCoeff[slot][c] = (1-sustainValue[slot]) / (vcvSampleRate * decayValue[slot]); + //stageCoeff[slot][c] = 1 / (vcvSampleRate * decayValue[slot]); + stageCoeff[slot][c] = envSrCoeff / decayValue[slot]; + + } + env[slot][c] = refValue[slot][c] + (shapeResponse(stageLevel[slot][c], slot) * deltaValue[slot][c]); + break; + + case DECAY_STAGE: + stageLevel[slot][c] -= stageCoeff[slot][c]; + + //if (stageLevel[slot][c] <= sustainValue[slot]) { + if (stageLevel[slot][c] <= 0) { + //stageLevel[slot][c] = sustainValue[slot]; + stageLevel[slot][c] = 1; + refValue[slot][c] = sustainValue[slot]; + deltaValue[slot][c] = 0; + stage[slot][c] = SUSTAIN_STAGE; + } + env[slot][c] = refValue[slot][c] + (shapeResponse2(stageLevel[slot][c], slot) * deltaValue[slot][c]); + break; + + case SUSTAIN_STAGE: + //stageLevel[slot][c] = sustainValue[slot]; + refValue[slot][c] = sustainValue[slot]; + env[slot][c] = sustainValue[slot]; + break; + + case RELEASE_STAGE: + stageLevel[slot][c] -= stageCoeff[slot][c]; + //env[slot][c] = shapeResponse2(stageLevel[slot][c], slot); + + if (stageLevel[slot][c] <= 0) { // if release has ended + stageLevel[slot][c] = 0; + refValue[slot][c] = 0; + deltaValue[slot][c] = 1; + + stage[slot][c] = STOP_STAGE; + play[slot][c] = false; + + //if (releaseNew[slot][c]) + // releaseNew[slot][c] = false; + + } + env[slot][c] = refValue[slot][c] + (shapeResponse2(stageLevel[slot][c], slot) * deltaValue[slot][c]); + break; + } + + if (reversePlaying[slot][c]) { + //currentOutput *= stageLevel[slot][c] * masterLevel[slot][c] * -1; + currentOutput *= env[slot][c] * masterLevel[slot][c] * -1; + if (channels[slot] == 2) + //currentOutputR *= stageLevel[slot][c] * masterLevel[slot][c] * -1; + currentOutputR *= env[slot][c] * masterLevel[slot][c] * -1; + } else { + //currentOutput *= stageLevel[slot][c] * masterLevel[slot][c]; + currentOutput *= env[slot][c] * masterLevel[slot][c]; + if (channels[slot] == 2) + //currentOutputR *= stageLevel[slot][c] * masterLevel[slot][c]; + currentOutputR *= env[slot][c] * masterLevel[slot][c]; + } + + + // ███████╗░█████╗░██████╗░███████╗ + // ██╔════╝██╔══██╗██╔══██╗██╔════╝ + // █████╗░░███████║██║░░██║█████╗░░ + // ██╔══╝░░██╔══██║██║░░██║██╔══╝░░ + // ██║░░░░░██║░░██║██████╔╝███████╗ + // ╚═╝░░░░░╚═╝░░╚═╝╚═════╝░╚══════╝ + + + switch (fadingType[slot][c]) { + case NO_FADE: + break; + + case CROSS_FADE: + if (fadingValue[slot][c] > 0) { + fadingValue[slot][c] -= fadeCoeff[slot]; + switch (reversePlaying[slot][c]) { + case FORWARD: + + if (floor(fadedPosition[slot][c]) < totalSampleC[slot]) { + currentOutput *= 1 - fadingValue[slot][c]; + //currentOutput += (playBuffer[slot][LEFT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * stageLevel[slot][c]); + //currentOutput += (playBuffer[slot][LEFT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c]); + currentOutput += (playBuffer[slot][LEFT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * env[slot][c]); + if (channels[slot] == 2) { + currentOutputR *= 1 - fadingValue[slot][c]; + //currentOutputR += (playBuffer[slot][RIGHT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * stageLevel[slot][c]); + currentOutputR += (playBuffer[slot][RIGHT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * env[slot][c]); + } + } else { + fadingType[slot][c] = NO_FADE; + } + + fadedPosition[slot][c] += distancePos[slot][c]; + break; + case REVERSE: + + if (floor(fadedPosition[slot][c]) >= 0) { + currentOutput *= 1 - fadingValue[slot][c]; + //currentOutput += (playBuffer[slot][LEFT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * stageLevel[slot][c] * -1); + currentOutput += (playBuffer[slot][LEFT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * env[slot][c] * -1); + if (channels[slot] == 2) { + currentOutputR *= 1 - fadingValue[slot][c]; + //currentOutputR += (playBuffer[slot][RIGHT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * stageLevel[slot][c] * -1); + currentOutputR += (playBuffer[slot][RIGHT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * env[slot][c] * -1); + } + } else { + fadingType[slot][c] = NO_FADE; + } + + fadedPosition[slot][c] -= distancePos[slot][c]; + + break; + } + } else { + fadingType[slot][c] = NO_FADE; + } + break; + + case FADE_OUT: + if (fadingValue[slot][c] > 0) { + fadingValue[slot][c] -= fadeCoeff[slot]; + currentOutput *= fadingValue[slot][c]; + if (channels[slot] == 2) + currentOutputR *= 1 - fadingValue[slot][c]; + } else + fadingType[slot][c] = NO_FADE; + break; + + case PINGPONG_FADE: + if (fadingValue[slot][c] > 0) { + fadingValue[slot][c] -= fadeCoeff[slot]; + switch (reversePlaying[slot][c]) { + case FORWARD: + + if (fadedPosition[slot][c] >= 0) { + currentOutput *= 1 - fadingValue[slot][c]; + //currentOutput += (playBuffer[slot][LEFT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * stageLevel[slot][c] * -1); + currentOutput += (playBuffer[slot][LEFT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * env[slot][c] * -1); + if (channels[slot] == 2) { + currentOutputR *= 1 - fadingValue[slot][c]; + //currentOutputR += (playBuffer[slot][RIGHT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * stageLevel[slot][c] * -1); + currentOutputR += (playBuffer[slot][RIGHT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * env[slot][c] * -1); + } + } else { + fadingType[slot][c] = NO_FADE; + } + + fadedPosition[slot][c] -= distancePos[slot][c]; + break; + case REVERSE: + + if (fadedPosition[slot][c] < totalSampleC[slot]) { + currentOutput *= 1 - fadingValue[slot][c]; + //currentOutput += (playBuffer[slot][LEFT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * stageLevel[slot][c]); + currentOutput += (playBuffer[slot][LEFT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * env[slot][c]); + if (channels[slot] == 2) { + currentOutputR *= 1 - fadingValue[slot][c]; + //currentOutputR += (playBuffer[slot][RIGHT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * stageLevel[slot][c]); + currentOutputR += (playBuffer[slot][RIGHT][antiAlias][floor(fadedPosition[slot][c])] * fadingValue[slot][c] * masterLevel[slot][c] * env[slot][c]); + } + } else { + fadingType[slot][c] = NO_FADE; + } + + fadedPosition[slot][c] += distancePos[slot][c]; + break; + } + } else + fadingType[slot][c] = NO_FADE; + break; + } + + // ------------------------------------------------------- U P D A T E S A M P L E P O S I T I O N ----------------- + + //if (params[STRETCH_PARAM].getValue() != 1) { + if (stretchKnob[slot] != 1) { + grainSampleCount[slot][c]++; + //if (params[STRETCH_PARAM].getValue() > 1) { + if (stretchKnob[slot] > 1) { + + if (grainSampleCount[slot][c] > stretchMaxPos[slot] ) { // + grainCount[slot][c]++; + grainPos[slot][c] = samplePos[slot][c]; + grainFadeValue[slot][c] = 1; + + //grainFadeCoeff[c] = 10 / (params[STR_SIZE_PARAM].getValue() * oneMsSamples); + grainFadeCoeff[slot][c] = 10 / (cycleKnob[slot] * oneMsSamples); + grainFade[slot][c] = true; + + //if (grainCount[c] > (params[STRETCH_PARAM].getValue())) { + if (grainCount[slot][c] > stretchKnob[slot]) { + //double tempStretch1 = params[STRETCH_PARAM].getValue()-floor(params[STRETCH_PARAM].getValue()); + double tempStretch1 = stretchKnob[slot]-floor(stretchKnob[slot]); + if (!reversePlaying[slot][c]) + samplePos[slot][c] -= stretchMaxPos[slot] * tempStretch1 * distancePos[slot][c]; + else + samplePos[slot][c] += stretchMaxPos[slot] * tempStretch1 * distancePos[slot][c]; + grainSampleCount[slot][c] = -stretchMaxPos[slot] * tempStretch1; + grainCount[slot][c] = 1; + } else { + if (!reversePlaying[slot][c]) + samplePos[slot][c] -= stretchMaxPos[slot] * distancePos[slot][c]; + else + samplePos[slot][c] += stretchMaxPos[slot] * distancePos[slot][c]; + grainSampleCount[slot][c] = grainSampleCount[slot][c] - stretchMaxPos[slot]; + } + } + + } else { + + if (grainSampleCount[slot][c] > stretchMaxPos[slot]) { + + grainPos[slot][c] = samplePos[slot][c]; + grainFadeValue[slot][c] = 1; + + //grainFadeCoeff[c] = 10 / (params[STR_SIZE_PARAM].getValue() * oneMsSamples); + grainFadeCoeff[slot][c] = 10 / (cycleKnob[slot] * oneMsSamples); + grainFade[slot][c] = true; + + if (!reversePlaying[slot][c]) + //samplePos[c] += ((stretchMaxPos / params[STRETCH_PARAM].getValue()) - stretchMaxPos) * distancePos[c]; + samplePos[slot][c] += ((stretchMaxPos[slot] / stretchKnob[slot]) - stretchMaxPos[slot]) * distancePos[slot][c]; + else + //samplePos[c] -= ((stretchMaxPos / params[STRETCH_PARAM].getValue()) - stretchMaxPos) * distancePos[c]; + samplePos[slot][c] -= ((stretchMaxPos[slot] / stretchKnob[slot]) - stretchMaxPos[slot]) * distancePos[slot][c]; + + grainSampleCount[slot][c] = grainSampleCount[slot][c] - stretchMaxPos[slot]; + } + } + } + + if (reversePlaying[slot][c]) + samplePos[slot][c] -= distancePos[slot][c]; + else + samplePos[slot][c] += distancePos[slot][c]; + + currSampleWeight[slot][c] = samplePos[slot][c] - floor(samplePos[slot][c]); + + // ------------------------------------------------------------ END POSITION UPDATE + + } + + } else { + play[slot][c] = false; + fadingType[slot][c] = NO_FADE; + } + + sumOutput[outRoute[slot]] += currentOutput; + if (channels[slot] == 2) + sumOutputR[outRoute[slot]] += currentOutputR; + + } // END OF IF IN RANGE + + } // END OF IF fileLoaded + + } // END OF SLOT CYCLE + + } // END OF CHANNEL MANAGEMENT + + +// +// ██████╗░███████╗░█████╗░░█████╗░██████╗░██████╗░ +// ██╔══██╗██╔════╝██╔══██╗██╔══██╗██╔══██╗██╔══██╗ +// ██████╔╝█████╗░░██║░░╚═╝██║░░██║██████╔╝██║░░██║ +// ██╔══██╗██╔══╝░░██║░░██╗██║░░██║██╔══██╗██║░░██║ +// ██║░░██║███████╗╚█████╔╝╚█████╔╝██║░░██║██████╔╝ +// ╚═╝░░╚═╝╚══════╝░╚════╝░░╚════╝░╚═╝░░╚═╝╚═════╝░ + + + recTrig = params[REC_PARAM].getValue(); + + if (recTrig && !prevRecTrig) { + if (!fileLoaded[currSlot]) { + if (recordingState) { + recButton = 0; + } else { + recButton = 1; + recSlot = currSlot; + } + lights[REC_LIGHT].setBrightness(recButton); + } else { + lights[REC_LIGHT].setBrightness(0.f); + } + + } + prevRecTrig = recTrig; + + //if (!fileLoaded[currSlot]) { + if (!fileLoaded[recSlot]) { + // ********* NEW RECORDING **** START STOP COMMANDS + + //masterLevel[currSlot][recOutChan] = params[VOL_PARAM].getValue() + (inputs[VEL_INPUT].getVoltage(recOutChan) * 0.1); + masterLevel[recSlot][recOutChan] = volumeValue[recSlot]; + + /* + if (masterLevel[currSlot][recOutChan] > 1) + masterLevel[currSlot][recOutChan] = 1; + else if (masterLevel[currSlot][recOutChan] < 0) + masterLevel[currSlot][recOutChan] = 0; + */ + + if (recButton && recordingState == 0) { + + if (inputs[IN_INPUT].isConnected()){ + z1 = 0; z2 = 0; z1r = 0; z2r = 0; + recordingState = 1; + recSamples = 0; + if (inputs[IN_INPUT+1].isConnected()) + recChannels = 2; + else + recChannels = 1; + + currentRecordingLimit = recordingLimit / recChannels; + + fileChannels[recSlot] = recChannels; + + sampleRate = vcvSampleRate * 2; + calcBiquadLpf(20000.0, sampleRate, 1); + + playBuffer[recSlot][LEFT][0].clear(); + playBuffer[recSlot][LEFT][1].clear(); + if (recChannels == 2) { + playBuffer[recSlot][RIGHT][0].clear(); + playBuffer[recSlot][RIGHT][1].clear(); + } + + currRecPosition = 1; + + // set position knob to 0 and 1 + knobCueStartPos[recSlot] = 0; + knobLoopStartPos[recSlot] = 0; + knobCueEndPos[recSlot] = 1; + knobLoopEndPos[recSlot] = 1; + + params[CUESTART_PARAM].setValue(0); + params[CUEEND_PARAM].setValue(1); + params[LOOPSTART_PARAM].setValue(0); + params[LOOPEND_PARAM].setValue(1); + + if (xFade[recSlot] > 0) { + recFadeValue = 0; + recFadeIn = true; + } else { + recFadeIn = false; + recFadeValue = 1; + } + + //currRecValue[LEFT] = inputs[IN_INPUT].getVoltage() * params[GAIN_PARAM].getValue() * recFadeValue; + //currRecValue[RIGHT] = inputs[IN_INPUT+1].getVoltage() * params[GAIN_PARAM].getValue() * recFadeValue; + currRecValue[LEFT] = inputs[IN_INPUT].getVoltageSum() * params[GAIN_PARAM].getValue() * recFadeValue; + currRecValue[RIGHT] = inputs[IN_INPUT+1].getVoltageSum() * params[GAIN_PARAM].getValue() * recFadeValue; + prevRecValue[LEFT] = currRecValue[LEFT]; + prevRecValue[RIGHT] = currRecValue[RIGHT]; + + recFadeOut = false; + + } else { // **** if rec is pressed or triggered and no left cable is plugged, do nothing + params[REC_PARAM].setValue(0); + lights[REC_LIGHT].setBrightness(0.f); + recButton = 0; + } + + } else if (recButton == 0 && recordingState == 1 && recFadeOut == false) { // ********** S T O P R E C O R D I N G **************** + if (recFadeValue > 0) { + recFadeOut = true; + + if (xFade[recSlot] <= 0) + recFadeValue = 0; + } + + if (recFadeValue <= 0) { + recordingState = 0; + + channels[recSlot] = recChannels; + //fileDescription[recSlot] = "_unknown_.wav"; + //fileDisplay[recSlot] = "_unknown_"; + fileDescription[recSlot] = "_slot"+to_string(recSlot+1)+".waw"; + fileDisplay[recSlot] = "_slot"+to_string(recSlot+1); + storedPath[recSlot] = "_slot"+to_string(recSlot+1)+".waw";; + samplerateDisplay[recSlot] = std::to_string(int(vcvSampleRate)); + channelsDisplay[recSlot] = std::to_string(recChannels) + "ch"; + + cueStartPos[recSlot] = 0; + cueEndPos[recSlot] = totalSamples[recSlot]; + loopStartPos[recSlot] = 0; + loopEndPos[recSlot] = totalSamples[recSlot]; + prevKnobCueStartPos[recSlot] = -1.f; + prevKnobCueEndPos[recSlot] = 2.f; + prevKnobLoopStartPos[recSlot] = -1.f; + prevKnobLoopEndPos[recSlot] = 2.f; + + samplePos[recSlot][recOutChan] = 0; + + totalSampleC[recSlot] = playBuffer[recSlot][0][0].size(); + totalSamples[recSlot] = totalSampleC[recSlot]-1; + + vector().swap(displayBuff[recSlot]); + + for (int i = 0; i < floor(totalSampleC[recSlot]); i = i + floor(totalSampleC[recSlot]/wavDisp)) + displayBuff[recSlot].push_back(playBuffer[recSlot][0][0][i]); + + seconds = totalSampleC[recSlot] * 0.5 / (APP->engine->getSampleRate()); + minutes = (seconds / 60) % 60; + seconds = seconds % 60; + + timeDisplay[recSlot] = std::to_string(minutes) + ":"; + if (seconds < 10) + timeDisplay[recSlot] += "0"; + timeDisplay[recSlot] += std::to_string(seconds); + + toSave[recSlot] = true; + infoToSave[recSlot] ="s"; + fileLoaded[recSlot] = true; + + if (autoMonOff) { + params[MONITOR_SWITCH].setValue(0.f); + } + + } + } + } + + if (recordingState) { + + // **************************************************************************** R E C O R D I N G S E C T I O N ***************** + + if (recFadeIn) { + if (recFadeValue < 1) + recFadeValue += 1 / (convertCVToSeconds(xFade[recSlot]) * vcvSampleRate); + if (recFadeValue >= 1) { + recFadeValue = 1; + recFadeIn = false; + } + + } else if (recFadeOut) { + + if (recFadeValue > 0) + recFadeValue -= 1 / (convertCVToSeconds(xFade[recSlot]) * vcvSampleRate); + if (recFadeValue <= 0) { + recFadeValue = 0; + recFadeOut = false; + } + } + + + playBuffer[recSlot][LEFT][0].push_back(0); + playBuffer[recSlot][LEFT][1].push_back(0); + playBuffer[recSlot][LEFT][0].push_back(0); + playBuffer[recSlot][LEFT][1].push_back(0); + if (recChannels == 2) { + playBuffer[recSlot][RIGHT][0].push_back(0); + playBuffer[recSlot][RIGHT][1].push_back(0); + playBuffer[recSlot][RIGHT][0].push_back(0); + playBuffer[recSlot][RIGHT][1].push_back(0); + } + + int rc = 0; + + prevRecValue[rc] = currRecValue[rc]; + currRecValue[rc] = inputs[IN_INPUT+rc].getVoltage() * params[GAIN_PARAM].getValue() * recFadeValue; + + playBuffer[recSlot][rc][0][currRecPosition] = currRecValue[rc]; + playBuffer[recSlot][rc][0][currRecPosition-1] = (currRecValue[rc] + prevRecValue[rc]) / 2; + + playBuffer[recSlot][rc][1][currRecPosition-1] = biquadLpf(playBuffer[recSlot][rc][0][currRecPosition-1]); // filtered vector + playBuffer[recSlot][rc][1][currRecPosition] = biquadLpf(playBuffer[recSlot][rc][0][currRecPosition]); // filtered vector + + if (recChannels == 2) { + rc = 1; + + prevRecValue[rc] = currRecValue[rc]; + currRecValue[rc] = inputs[IN_INPUT+rc].getVoltage() * params[GAIN_PARAM].getValue() * recFadeValue; + + playBuffer[recSlot][rc][0][currRecPosition] = currRecValue[rc]; + playBuffer[recSlot][rc][0][currRecPosition-1] = (currRecValue[rc] + prevRecValue[rc]) / 2; + + playBuffer[recSlot][rc][1][currRecPosition-1] = biquadLpf2(playBuffer[recSlot][rc][0][currRecPosition-1]); // filtered vector + playBuffer[recSlot][rc][1][currRecPosition] = biquadLpf2(playBuffer[recSlot][rc][0][currRecPosition]); // filtered vector + } + + recSamples++; + currRecPosition = currRecPosition + 2; + + if (!unlimitedRecording && currRecPosition > currentRecordingLimit) { + params[REC_PARAM].setValue(0); + lights[REC_LIGHT].setBrightness(0); + recButton = 0; + } + + } // end if RECORDING STATE = true + + } +// ███╗░░░███╗░█████╗░███╗░░██╗██╗████████╗░█████╗░██████╗░ +// ████╗░████║██╔══██╗████╗░██║██║╚══██╔══╝██╔══██╗██╔══██╗ +// ██╔████╔██║██║░░██║██╔██╗██║██║░░░██║░░░██║░░██║██████╔╝ +// ██║╚██╔╝██║██║░░██║██║╚████║██║░░░██║░░░██║░░██║██╔══██╗ +// ██║░╚═╝░██║╚█████╔╝██║░╚███║██║░░░██║░░░╚█████╔╝██║░░██║ +// ╚═╝░░░░░╚═╝░╚════╝░╚═╝░░╚══╝╚═╝░░░╚═╝░░░░╚════╝░╚═╝░░╚═╝ + + + if (!fileLoaded[recSlot] && !recordingState) { + if (inputs[IN_INPUT+1].isConnected()) + channels[currSlot] = 2; + else + channels[currSlot] = 1; + } + + //monitorSwitch = int(params[MONITOR_SWITCH].getValue()); + if (monitorSwitch != prevMonitorSwitch) { + monitorFadeValue = 0; + monitorFade = true; + if (monitorSwitch == 1 && recordingState == 1) { + monitorFadeValue = 0; + monitorFade = true; + } + } + + if (monitorFade) { + if (monitorFadeValue < 1) + monitorFadeValue += monitorFadeCoeff; + else { + monitorFadeValue = 1; + monitorFade = false; + } + } + + if (monitorSwitch) { + sumOutput[outRoute[currSlot]] += inputs[IN_INPUT].getVoltageSum() * params[GAIN_PARAM].getValue() * monitorFadeValue; + //if (channels[currSlot] == 2) // superfluo, eliminato per velocizzare + sumOutputR[outRoute[currSlot]] += inputs[IN_INPUT+1].getVoltageSum() * params[GAIN_PARAM].getValue() * monitorFadeValue; + // n.b. : se lo slot è già registrato mono, verrà monitorato solo il canale sinistro + } else if (monitorFade) { + sumOutput[outRoute[currSlot]] += inputs[IN_INPUT].getVoltageSum() * params[GAIN_PARAM].getValue() * (1-monitorFadeValue); + //if (channels[currSlot] == 2) // superfluo, eliminato per velocizzare + sumOutputR[outRoute[currSlot]] += inputs[IN_INPUT+1].getVoltageSum() * params[GAIN_PARAM].getValue() * (1-monitorFadeValue); + // n.b. : se lo slot è già registrato mono, verrà monitorato solo il canale sinistro + } + + prevMonitorSwitch = monitorSwitch; + + + for (int slot = 0; slot < 8; slot++) { + + // *** HARD CLIP *** + + if (sumOutput[slot] > 10) + sumOutput[slot] = 10; + else if (sumOutput[slot] < -10) + sumOutput[slot] = -10; + if (channels[slot] == 2) { + if (sumOutputR[slot] > 10) + sumOutputR[slot] = 10; + else if (sumOutputR[slot] < -10) + sumOutputR[slot] = -10; + } + + //if (outputs[LEFT_OUTPUT+slot].isConnected()) { // condizione superflua ? + outputs[LEFT_OUTPUT+slot].setVoltage(sumOutput[slot]); + //outputs[LEFT_OUTPUT+outRoute[slot]].setChannels(1); // facoltativo ? + //} + + if (outputs[RIGHT_OUTPUT+slot].isConnected()) { + if (channels[slot] == 2) + outputs[RIGHT_OUTPUT+slot].setVoltage(sumOutputR[slot]); + else + outputs[RIGHT_OUTPUT+slot].setVoltage(sumOutput[slot]); + //outputs[RIGHT_OUTPUT+slot].setChannels(1); // facoltativo? + } + } + + // ****************************************************************************************************** + // ***************************** R E C O R D I N G E N D *************************************** + // ****************************************************************************************************** + + /* + if (polyOuts) { + outputs[EOC_OUTPUT].setChannels(chan); + outputs[EOR_OUTPUT].setChannels(chan); + } else { + if (eoc[0]) { + eocTime[0]--; + if (eocTime[0] < 0) { + eoc[0] = false; + outputs[EOC_OUTPUT].setVoltage(0.f, 0); + } else + outputs[EOC_OUTPUT].setVoltage(10.f, 0); + } + + if (eor[0]) { + eorTime[0]--; + if (eorTime[0] < 0) { + eor[0] = false; + outputs[EOR_OUTPUT].setVoltage(0.f, 0); + } else + outputs[EOR_OUTPUT].setVoltage(10.f, 0); + } + } + */ + } +}; + + + + +// ███████╗███╗░░██╗██████╗░  ██████╗░██████╗░░█████╗░░█████╗░███████╗░██████╗░██████╗ +// ██╔════╝████╗░██║██╔══██╗  ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔════╝██╔════╝ +// █████╗░░██╔██╗██║██║░░██║  ██████╔╝██████╔╝██║░░██║██║░░╚═╝█████╗░░╚█████╗░╚█████╗░ +// ██╔══╝░░██║╚████║██║░░██║  ██╔═══╝░██╔══██╗██║░░██║██║░░██╗██╔══╝░░░╚═══██╗░╚═══██╗ +// ███████╗██║░╚███║██████╔╝  ██║░░░░░██║░░██║╚█████╔╝╚█████╔╝███████╗██████╔╝██████╔╝ +// ╚══════╝╚═╝░░╚══╝╚═════╝░  ╚═╝░░░░░╚═╝░░╚═╝░╚════╝░░╚════╝░╚══════╝╚═════╝░╚═════╝░ + +/* +struct KeySamplerDebugDisplay : TransparentWidget { + KeySampler *module; + int frame = 0; + KeySamplerDebugDisplay() { + } + + void drawLayer(const DrawArgs &args, int layer) override { + if (module) { + if (layer ==1) { + shared_ptr font = APP->window->loadFont(asset::system("res/fonts/Nunito-bold.ttf")); + nvgFontSize(args.vg, 10); + nvgFontFaceId(args.vg, font->handle); + nvgTextLetterSpacing(args.vg, 0); + nvgFillColor(args.vg, nvgRGBA(0xff, 0xff, 0xff, 0xff)); + + //module->debugDisplay[0] = to_string(module->fadingType[0][0]) + " " + to_string(module->stretchMaxPos[0]); + //module->debugDisplay[1] = to_string(module->fadingType[1][1]) + " " + to_string(module->stretchMaxPos[1]); + module->debugDisplay[0] = "cueStartPos " + to_string(module->cueStartPos[2]) ; + module->debugDisplay[1] = "cueEndPos " + to_string(module->cueEndPos[2]) ; + module->debugDisplay[2] = "KN cueStartPos " + to_string(module->knobCueStartPos[2]) ; + module->debugDisplay[3] = "KN cueEndPos " + to_string(module->knobCueEndPos[2]) ; + + for (int i = 0; i < 16; i++) + nvgTextBox(args.vg, 0, i*11,120, module->debugDisplay[i].c_str(), NULL); + + } + } + Widget::drawLayer(args, layer); + } +}; +*/ + +struct KeySamplerDisplay : TransparentWidget { + KeySampler *module; + int frame = 0; + KeySamplerDisplay() { + } + + void onButton(const event::Button &e) override { + /*if (e.button == GLFW_MOUSE_BUTTON_LEFT && e.action == GLFW_PRESS) + e.consume(this);*/ + + if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT && (e.mods & RACK_MOD_MASK) == 0) { + createContextMenu(); + e.consume(this); + } + } + + void drawLayer(const DrawArgs &args, int layer) override { + if (module) { + if (layer ==1) { + + shared_ptr font = APP->window->loadFont(asset::plugin(pluginInstance, "res/vgatrue.ttf")); + nvgFontSize(args.vg, 12); + nvgFontFaceId(args.vg, font->handle); + nvgTextLetterSpacing(args.vg, 0); + + nvgFillColor(args.vg, nvgRGBA(0xdd, 0x33, 0x33, 0xff)); + nvgTextBox(args.vg, xTitle, yInfo,247, module->fileDisplay[module->currSlot].c_str(), NULL); + + if (module->fileLoaded[module->currSlot]) { + nvgFillColor(args.vg, nvgRGBA(0x33, 0xdd, 0x33, 0xff)); + nvgTextBox(args.vg, xTime, yInfo,97, module->timeDisplay[module->currSlot].c_str(), NULL); + + } else if (module->recordingState) { + module->recSeconds = module->recSamples / (APP->engine->getSampleRate()); + module->recMinutes = (module->recSeconds / 60) % 60; + module->recSeconds = module->recSeconds % 60; + module->recTimeDisplay[module->currSlot] = std::to_string(module->recMinutes) + ":"; + if (module->recSeconds < 10) + module->recTimeDisplay[module->currSlot] += "0"; + module->recTimeDisplay[module->currSlot] += std::to_string(module->recSeconds); + module->timeDisplay[module->currSlot] = module->recTimeDisplay[module->currSlot]; + nvgTextBox(args.vg, xTime, yInfo,97, module->timeDisplay[module->currSlot].c_str(), NULL); + } + + std::string loVal = "LO:" + module->noteNames[module->refKey[module->currSlot][LOKEY]]; + std::string hiVal = "HI:" + module->noteNames[module->refKey[module->currSlot][HIKEY]]; + std::string rfVal = "RF:" + module->noteNames[module->refKey[module->currSlot][REFKEY]]; + + switch (module->selectedRange) { + case LOKEY: + if (module->learn) + loVal = "LO:---"; + nvgBeginPath(args.vg); + nvgRect(args.vg, xKeyL, yKey, keyW, keyH); + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_RED)); + nvgFill(args.vg); + + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_BLACK)); + nvgTextBox(args.vg, xLoVal, yInfo2, 80, loVal.c_str(), NULL); + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_GREEN)); + nvgTextBox(args.vg, xHiVal, yInfo2, 80, hiVal.c_str(), NULL); + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_BLUE)); + nvgTextBox(args.vg, xRfVal, yInfo2, 80, rfVal.c_str(), NULL); + break; + + case HIKEY: + if (module->learn) + hiVal = "HI:---"; + nvgBeginPath(args.vg); + nvgRect(args.vg, xKeyH, yKey, keyW, keyH); + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_GREEN)); + nvgFill(args.vg); + + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_RED)); + nvgTextBox(args.vg, xLoVal, yInfo2, 80, loVal.c_str(), NULL); + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_BLACK)); + nvgTextBox(args.vg, xHiVal, yInfo2, 80, hiVal.c_str(), NULL); + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_BLUE)); + nvgTextBox(args.vg, xRfVal, yInfo2, 80, rfVal.c_str(), NULL); + break; + + case REFKEY: + if (module->learn) + rfVal = "RF:---"; + nvgBeginPath(args.vg); + nvgRect(args.vg, xKeyR, yKey, keyW, keyH); + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_BLUE)); + nvgFill(args.vg); + + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_RED)); + nvgTextBox(args.vg, xLoVal, yInfo2, 80, loVal.c_str(), NULL); + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_GREEN)); + nvgTextBox(args.vg, xHiVal, yInfo2, 80, hiVal.c_str(), NULL); + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_BLACK)); + nvgTextBox(args.vg, xRfVal, yInfo2, 80, rfVal.c_str(), NULL); + break; + } + + nvgFillColor(args.vg, nvgRGBA(0x88, 0xaa, 0xff, 0xff)); + nvgTextBox(args.vg, xMagg, yInfo,20, std::string(">").c_str(), NULL); + nvgTextBox(args.vg, xCh, yInfo2,97, module->channelsDisplay[module->currSlot].c_str(), NULL); + + nvgFillColor(args.vg, nvgRGBA(0xff, 0xff, 0x33, 0xff)); + nvgTextBox(args.vg, xSlot, yInfo,20, to_string(module->currSlot+1).c_str(), NULL); + + nvgFillColor(args.vg, nvgRGBA(0xcc, 0xcc, 0x22, 0xff)); + nvgTextBox(args.vg, xRoute, yInfo,20, to_string(module->outRoute[module->currSlot]+1).c_str(), NULL); + + nvgFillColor(args.vg, nvgRGBA(0xee, 0xee, 0x22, 0xff)); + nvgTextBox(args.vg, xToSave, yInfo2,20, module->infoToSave[module->currSlot].c_str(), NULL); + + + // Zero line + nvgStrokeColor(args.vg, nvgRGBA(0xff, 0xff, 0xff, 0x40)); + { + nvgBeginPath(args.vg); + nvgMoveTo(args.vg, xWav, yZeroLine); + nvgLineTo(args.vg, 143, yZeroLine); + nvgClosePath(args.vg); + } + nvgStroke(args.vg); + + if (module->fileLoaded[module->currSlot]) { + int xLine; + + // Playback line + if (module->recordingState) + nvgStrokeColor(args.vg, nvgRGBA(0xf5, 0x00, 0xf5, 0xff)); + else + nvgStrokeColor(args.vg, nvgRGBA(0xf5, 0xf5, 0xf5, 0xff)); + nvgStrokeWidth(args.vg, 0.8); + + if (module->currSlot == module->displaySlot) { + nvgBeginPath(args.vg); + xLine = xWav + floor(module->samplePos[module->currSlot][module->currentDisplay] * wavDisp / module->totalSampleC[module->currSlot]); + nvgMoveTo(args.vg, xLine, yLineUp); + nvgLineTo(args.vg, xLine, yLineDn); + nvgClosePath(args.vg); + } + nvgStroke(args.vg); + + // Cue Start line + nvgStrokeColor(args.vg, nvgRGBA(0x00, 0xf0, 0x00, 0xf0)); + nvgStrokeWidth(args.vg, 1); + { + nvgBeginPath(args.vg); + xLine = xWav + floor(module->cueStartPos[module->currSlot] * wavDisp / module->totalSampleC[module->currSlot]); + nvgMoveTo(args.vg, xLine , yLineUp); + nvgLineTo(args.vg, xLine , yLineDn); + nvgClosePath(args.vg); + } + nvgStroke(args.vg); + + // Cue End line + nvgStrokeColor(args.vg, nvgRGBA(0xf0, 0x00, 0x00, 0xf0)); + nvgStrokeWidth(args.vg, 1); + { + nvgBeginPath(args.vg); + xLine = xWav + floor(module->cueEndPos[module->currSlot] * wavDisp / module->totalSampleC[module->currSlot]); + nvgMoveTo(args.vg, xLine , yLineUp); + nvgLineTo(args.vg, xLine , yLineDn); + nvgClosePath(args.vg); + } + nvgStroke(args.vg); + + // Loop Start line + nvgStrokeColor(args.vg, nvgRGBA(0xf8, 0xec, 0x2e, 0xf0)); + nvgStrokeWidth(args.vg, 1); + { + nvgBeginPath(args.vg); + xLine = xWav + floor(module->loopStartPos[module->currSlot] * wavDisp / module->totalSampleC[module->currSlot]); + nvgMoveTo(args.vg, xLine , yLineUp); + nvgLineTo(args.vg, xLine , yLineDn); + nvgClosePath(args.vg); + } + nvgStroke(args.vg); + + // Loop End line + nvgStrokeColor(args.vg, nvgRGBA(0xea, 0x79, 0x26, 0xf0)); + nvgStrokeWidth(args.vg, 1); + { + nvgBeginPath(args.vg); + xLine = xWav + floor(module->loopEndPos[module->currSlot] * wavDisp / module->totalSampleC[module->currSlot]); + nvgMoveTo(args.vg, xLine , yLineUp); + nvgLineTo(args.vg, xLine , yLineDn); + nvgClosePath(args.vg); + } + nvgStroke(args.vg); + + // Waveform + nvgStrokeColor(args.vg, nvgRGBA(0x22, 0x44, 0xc9, 0xc0)); + nvgSave(args.vg); + Rect b = Rect(Vec(xWav, 13), Vec(wavDisp, 28)); + nvgScissor(args.vg, b.pos.x, b.pos.y, b.size.x, b.size.y); + nvgBeginPath(args.vg); + for (unsigned int i = 0; i < module->displayBuff[module->currSlot].size(); i++) { + float x, y; + x = (float)i / (module->displayBuff[module->currSlot].size() - 1); + y = module->displayBuff[module->currSlot][i] / 10.0 + 0.5; + Vec p; + p.x = b.pos.x + b.size.x * x; + p.y = b.pos.y + b.size.y * (1.0 - y); + if (i == 0) + nvgMoveTo(args.vg, p.x, p.y); + else + nvgLineTo(args.vg, p.x, p.y); + } + nvgLineCap(args.vg, NVG_ROUND); + nvgMiterLimit(args.vg, 2.0); + nvgStrokeWidth(args.vg, 1.5); + nvgGlobalCompositeOperation(args.vg, NVG_LIGHTER); + nvgStroke(args.vg); + nvgResetScissor(args.vg); + nvgRestore(args.vg); + } + + + } + } + Widget::drawLayer(args, layer); + } + + void loadSubfolder(rack::ui::Menu *menu, std::string path) { + KeySampler *module = dynamic_cast(this->module); + assert(module); + std::string currentDir = path; + int tempIndex = 1; + if (module->folderTreeData.size() < 2) { + module->createFolder(currentDir.substr(0,currentDir.length()-1)); + module->folderTreeData.push_back(module->tempTreeData); + module->folderTreeDisplay.push_back(module->tempTreeDisplay); + module->folderTreeData[1][0] = currentDir; + module->folderTreeDisplay[1][0] = currentDir; + } else { + bool exited = false; + for (unsigned int i = 1 ; i < module->folderTreeData.size(); i++) { + if (module->folderTreeData[i][0] == currentDir) { + tempIndex = i; + i = module->folderTreeData.size(); + exited = true; + } + } + if (!exited) { + module->createFolder(currentDir); + module->folderTreeData.push_back(module->tempTreeData); + module->folderTreeDisplay.push_back(module->tempTreeDisplay); + tempIndex = module->folderTreeData.size()-1; + } + } + if (module->folderTreeData[tempIndex].size() > 1) { + for (unsigned int i = 1; i < module->folderTreeData[tempIndex].size(); i++) { + if (module->folderTreeData[tempIndex][i].substr(module->folderTreeData[tempIndex][i].length()-1,module->folderTreeData[tempIndex][i].length()-1) == "/") { + module->tempDir = module->folderTreeData[tempIndex][i]; + menu->addChild(createSubmenuItem(module->folderTreeDisplay[tempIndex][i], "", [=](Menu* menu) { + loadSubfolder(menu, module->folderTreeData[tempIndex][i]); + })); + } else { + menu->addChild(createMenuItem(module->folderTreeDisplay[tempIndex][i], "", [=]() {module->loadFromPatch[module->currSlot] = false;module->loadSample(module->folderTreeData[tempIndex][i], module->currSlot);})); + } + } + } + } + + void createContextMenu() { + KeySampler *module = dynamic_cast(this->module); + assert(module); + + if (module) { + ui::Menu *menu = createMenu(); + + menu->addChild(createMenuItem("Load Sample", "", [=]() { + //module->menuLoadSample(); + bool temploadFromPatch = module->loadFromPatch[module->currSlot]; + module->loadFromPatch[module->currSlot] = false; + module->menuLoadSample(module->currSlot); + if (module->restoreLoadFromPatch[module->currSlot]) + module->loadFromPatch[module->currSlot] = temploadFromPatch; + })); + + if (module->folderTreeData.size() > 0) { + menu->addChild(createSubmenuItem("Samples Browser", "", [=](Menu* menu) { + //module->folderTreeData.resize(1); + //module->folderTreeDisplay.resize(1); + module->refreshRootFolder(); + for (unsigned int i = 1; i < module->folderTreeData[0].size(); i++) { + if (module->folderTreeData[0][i].substr(module->folderTreeData[0][i].length()-1, module->folderTreeData[0][i].length()-1) == "/") { + module->tempDir = module->folderTreeData[0][i]; + menu->addChild(createSubmenuItem(module->folderTreeDisplay[0][i], "", [=](Menu* menu) { + loadSubfolder(menu, module->folderTreeData[0][i]); + })); + } else { + menu->addChild(createMenuItem(module->folderTreeDisplay[0][i], "", [=]() {module->loadFromPatch[module->currSlot] = false;module->loadSample(module->folderTreeData[0][i], module->currSlot);})); + } + } + })); + } + + if (module->fileLoaded[module->currSlot]) { + menu->addChild(new MenuSeparator()); + menu->addChild(createMenuLabel("Current Sample:")); + menu->addChild(createMenuLabel(module->fileDescription[module->currSlot])); + menu->addChild(createMenuLabel(" " + module->samplerateDisplay[module->currSlot] + " - " + std::to_string(module->channels[module->currSlot]) + "ch")); + std::string tempDisplay = " "; + if (module->resampled[module->currSlot]) { + tempDisplay += "resampled to " + std::to_string(int(APP->engine->getSampleRate())); + if (module->toSave[module->currSlot]) + tempDisplay += " - "; + } + if (module->toSave[module->currSlot]) + tempDisplay += "to SAVE"; + if (tempDisplay != " ") + menu->addChild(createMenuLabel(tempDisplay)); + + menu->addChild(createMenuItem("", "Clear", [=]() {module->clearSlot(module->currSlot);})); + + menu->addChild(createMenuItem("Save FULL Sample", "", [=]() {module->menuSaveSample(SAVE_FULL, module->currSlot);})); + menu->addChild(createMenuItem("Save CUE Region", "", [=]() {module->menuSaveSample(SAVE_CUE, module->currSlot);})); + menu->addChild(createMenuItem("Save LOOP Region", "", [=]() {module->menuSaveSample(SAVE_LOOP, module->currSlot);})); + + /* + menu->addChild(createBoolPtrMenuItem("Trim Sample after Save", "", &module->trimOnSave)); + menu->addChild(createBoolPtrMenuItem("Save Oversampled", "", &module->saveOversampled)); + */ + } + + menu->addChild(new MenuSeparator()); + menu->addChild(createMenuItem("Reset Cursors", "", [=]() {module->resetCursors();})); + menu->addChild(createMenuItem("Copy Cue to Loop", "", [=]() {module->copyCueToLoop();})); + menu->addChild(createMenuItem("Copy Loop to Cue", "", [=]() {module->copyLoopToCue();})); + + menu->addChild(createSubmenuItem("Presets", "", [=](Menu * menu) { + menu->addChild(createMenuItem("Wavetable", "", [=]() {module->setPreset(0);})); + menu->addChild(createMenuItem("Triggered Sample with Envelope", "", [=]() {module->setPreset(1);})); + menu->addChild(createMenuItem("Drum Player", "", [=]() {module->setPreset(2);})); + })); + } + } +}; + +struct KeySamplerWidget : ModuleWidget { + KeySamplerWidget(KeySampler *module) { + setModule(module); + setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/KeySampler.svg"))); + + addChild(createWidget(Vec(0, 0))); + addChild(createWidget(Vec(box.size.x - RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(0, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(createWidget(Vec(box.size.x - RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + { + KeySamplerDisplay *display = new KeySamplerDisplay(); + display->box.pos = mm2px(Vec(0.6, 23.5)); + display->box.size = mm2px(Vec(49.5, 17.9)); + display->module = module; + addChild(display); + } + + /* + { + KeySamplerDebugDisplay *display = new KeySamplerDebugDisplay(); + display->box.pos = Vec(209, 30); + display->box.size = Vec(307, 100); + display->module = module; + addChild(display); + } + */ + + const float xInL = 6.4f; + const float xInR = 15.4f; + const float xGain = 25.9f; + const float yGain = 17.1f; + const float xRecBut = 36.4f; + const float xMon = 45.4f; + const float yMon = 17.5f; + const float yInputs = 16.9f; + + const float xSampSelect = 63.1f; + const float ySampSelect = 14.7f; + //const float xTrigBut = 66.f; + //const float yTrigBut = 18.4f; + + const float xRngBt = 56.f; + const float yRngBt = 25.7f; + + const float xRngKn = 65.2f; + const float yRngKn = 27.5f; + + const float xOutKn = 56.7f; + const float yOutKn = 35.f; + + const float xStart = 11.903f; + const float xEnd = 24.903f; + const float yCue = 52.647f; + const float yLoop = 62.647f; + + const float xRev = 35.203f; + const float yRev = 51.147f; + const float xXfade = 43.903f; + const float yXfade = 51.647f; + + const float xLp = 35.403f; + const float yLp = 60.647f; + + const float xPng = 43.903f; + const float yPng = 63.147f; + + const float xTrigGate = 9.68f; + const float yTrigGate = 77.6f; + + const float xTrigMode = 25.2f; + const float yTrigMode = 77.9f; + + const float xPb = 34.1f; + const float yPb = 77.9f; + + const float xScan = 45.4f; + const float yScan = 77.9f; + + const float xEnv1 = 7.2f; + const float xEnv1Add = 9.f; + const float yEnv1 = 89.7f; + + const float xCurve = 44.2f; + + const float xStretch = 9.7f; + const float yStretch = 103.f; + + const float xStretchSiz = 23.f; + const float yStretchSiz = 103.2f; + + const float xVol = 43.1f; + const float yVol = 101.8f; + + const float xGateIn = 7.9f; + const float xVoct = 19.9f; + const float xInVol = 31.9f; + const float xInPb = 43.9f; + + const float yIns = 117.8f; + + const float xOut1 = 56.1f; + const float xOut2 = 65.1f; + const float yOut = 50.9f; + const float yOutAdd = 9.5f; + + addParam(createParamCentered(mm2px(Vec(11, 4)), module, KeySampler::PREVSAMPLE_PARAM)); + addParam(createParamCentered(mm2px(Vec(60, 4)), module, KeySampler::NEXTSAMPLE_PARAM)); + + //---------------------------------------------------------------------------------------------------------------------------- + + addInput(createInputCentered(mm2px(Vec(xInL, yInputs)), module, KeySampler::IN_INPUT)); + addInput(createInputCentered(mm2px(Vec(xInR, yInputs)), module, KeySampler::IN_INPUT+1)); + + addParam(createParamCentered(mm2px(Vec(xGain, yGain)), module, KeySampler::GAIN_PARAM)); + + addParam(createLightParamCentered>(mm2px(Vec(xRecBut, yInputs)), module, KeySampler::REC_PARAM, KeySampler::REC_LIGHT)); + + addParam(createLightParamCentered>>(mm2px(Vec(xMon, yMon)), module, KeySampler::MONITOR_SWITCH, KeySampler::MONITOR_LIGHT)); + + //------------------------------------------------------------------------------------------------------------------------------ + + addParam(createParamCentered(mm2px(Vec(xSampSelect, ySampSelect)), module, KeySampler::SLOT_SELECTOR_PARAM)); + //addParam(createLightParamCentered>>(mm2px(Vec(xTrigBut, yTrigBut)), module, KeySampler::TESTBUT_PARAM, KeySampler::TESTBUT_LIGHT)); + addParam(createLightParamCentered>>(mm2px(Vec(xRngBt, yRngBt)), module, KeySampler::RANGE_SELECTOR_PARAM, KeySampler::RANGE_SELECTOR_LIGHT)); + addParam(createParamCentered(mm2px(Vec(xRngKn, yRngKn)), module, KeySampler::RANGE_PARAM)); + addParam(createParamCentered(mm2px(Vec(xOutKn, yOutKn)), module, KeySampler::OUT_SELECTOR_PARAM)); + + //------------------------------------------------------------------------------------------------------------------------------ + + addParam(createParamCentered(mm2px(Vec(xTrigGate, yTrigGate)), module, KeySampler::TRIGGATEMODE_SWITCH)); + addParam(createLightParamCentered>>(mm2px(Vec(xTrigMode, yTrigMode)), module, KeySampler::TRIGMODE_SWITCH, KeySampler::TRIGMODE_LIGHT)); + + addParam(createLightParamCentered>>(mm2px(Vec(xPb, yPb)), module, KeySampler::PB_SWITCH, KeySampler::PB_LIGHT)); + addParam(createLightParamCentered>>(mm2px(Vec(xScan, yScan)), module, KeySampler::PHASESCAN_SWITCH, KeySampler::PHASESCAN_LIGHT)); + + //---------------------------------------------------------------------------------------------------------------------------- + + addParam(createParamCentered(mm2px(Vec(xStart, yCue)), module, KeySampler::CUESTART_PARAM)); + addParam(createParamCentered(mm2px(Vec(xEnd, yCue)), module, KeySampler::CUEEND_PARAM)); + + addParam(createParamCentered(mm2px(Vec(xStart, yLoop)), module, KeySampler::LOOPSTART_PARAM)); + addParam(createParamCentered(mm2px(Vec(xEnd, yLoop)), module, KeySampler::LOOPEND_PARAM)); + + addParam(createLightParamCentered>>(mm2px(Vec(xRev, yRev)), module, KeySampler::REV_PARAM, KeySampler::REV_LIGHT)); + addParam(createParamCentered(mm2px(Vec(xXfade, yXfade)), module, KeySampler::XFADE_PARAM)); + + addParam(createLightParamCentered>>(mm2px(Vec(xLp, yLp)), module, KeySampler::LOOP_PARAM, KeySampler::LOOP_LIGHT)); + addParam(createLightParamCentered>>(mm2px(Vec(xPng, yPng)), module, KeySampler::PINGPONG_PARAM, KeySampler::PINGPONG_LIGHT)); + + //---------------------------------------------------------------------------------------------------------------------------- + + addParam(createParamCentered(mm2px(Vec(xEnv1, yEnv1)), module, KeySampler::ATTACK_PARAM)); + addParam(createParamCentered(mm2px(Vec(xEnv1+xEnv1Add, yEnv1)), module, KeySampler::DECAY_PARAM)); + addParam(createParamCentered(mm2px(Vec(xEnv1+xEnv1Add*2, yEnv1)), module, KeySampler::SUSTAIN_PARAM)); + addParam(createParamCentered(mm2px(Vec(xEnv1+xEnv1Add*3, yEnv1)), module, KeySampler::RELEASE_PARAM)); + + addParam(createParamCentered(mm2px(Vec(xCurve, yEnv1)), module, KeySampler::CURVE_PARAM)); + + //---------------------------------------------------------------------------------------------------------------------------- + + addParam(createParamCentered(mm2px(Vec(xStretch, yStretch)), module, KeySampler::STRETCH_PARAM)); + addParam(createParamCentered(mm2px(Vec(xStretchSiz, yStretchSiz)), module, KeySampler::STR_SIZE_PARAM)); + + addParam(createParamCentered(mm2px(Vec(xVol, yVol)), module, KeySampler::VOL_PARAM)); + + //---------------------------------------------------------------------------------------------------------------------------- + + addInput(createInputCentered(mm2px(Vec(xGateIn, yIns)), module, KeySampler::GATE_INPUT)); + addInput(createInputCentered(mm2px(Vec(xVoct, yIns)), module, KeySampler::VO_INPUT)); + addInput(createInputCentered(mm2px(Vec(xInVol, yIns)), module, KeySampler::VEL_INPUT)); + addInput(createInputCentered(mm2px(Vec(xInPb, yIns)), module, KeySampler::PB_INPUT)); + + //---------------------------------------------------------------------------------------------------------------------------- + + for (int i = 0; i < 8; i++) { + addOutput(createOutputCentered(mm2px(Vec(xOut1, yOut+(yOutAdd*i))), module, KeySampler::LEFT_OUTPUT+i)); + addOutput(createOutputCentered(mm2px(Vec(xOut2, yOut+(yOutAdd*i))), module, KeySampler::RIGHT_OUTPUT+i)); + } + + } + + /* + void loadSubfolder(rack::ui::Menu *menu, std::string path) { + KeySampler *module = dynamic_cast(this->module); + assert(module); + std::string currentDir = path; + int tempIndex = 1; + if (module->folderTreeData.size() < 2) { + module->createFolder(currentDir.substr(0,currentDir.length()-1)); + module->folderTreeData.push_back(module->tempTreeData); + module->folderTreeDisplay.push_back(module->tempTreeDisplay); + module->folderTreeData[1][0] = currentDir; + module->folderTreeDisplay[1][0] = currentDir; + } else { + bool exited = false; + for (unsigned int i = 1 ; i < module->folderTreeData.size(); i++) { + if (module->folderTreeData[i][0] == currentDir) { + tempIndex = i; + i = module->folderTreeData.size(); + exited = true; + } + } + if (!exited) { + module->createFolder(currentDir); + module->folderTreeData.push_back(module->tempTreeData); + module->folderTreeDisplay.push_back(module->tempTreeDisplay); + tempIndex = module->folderTreeData.size()-1; + } + } + if (module->folderTreeData[tempIndex].size() > 1) { + for (unsigned int i = 1; i < module->folderTreeData[tempIndex].size(); i++) { + if (module->folderTreeData[tempIndex][i].substr(module->folderTreeData[tempIndex][i].length()-1,module->folderTreeData[tempIndex][i].length()-1) == "/") { + module->tempDir = module->folderTreeData[tempIndex][i]; + menu->addChild(createSubmenuItem(module->folderTreeDisplay[tempIndex][i], "", [=](Menu* menu) { + loadSubfolder(menu, module->folderTreeData[tempIndex][i]); + })); + } else { + menu->addChild(createMenuItem(module->folderTreeDisplay[tempIndex][i], "", [=]() {module->loadFromPatch[module->currSlot] = false;module->loadSample(module->folderTreeData[tempIndex][i], module->currSlot);})); + } + } + } + } + */ + + void appendContextMenu(Menu *menu) override { + KeySampler *module = dynamic_cast(this->module); + assert(module); + + /* + menu->addChild(new MenuSeparator()); + menu->addChild(createMenuItem("Load Sample", "", [=]() { + //module->menuLoadSample(); + bool temploadFromPatch = module->loadFromPatch[module->currSlot]; + module->loadFromPatch[module->currSlot] = false; + module->menuLoadSample(module->currSlot); + if (module->restoreLoadFromPatch[module->currSlot]) + module->loadFromPatch[module->currSlot] = temploadFromPatch; + })); + + if (module->folderTreeData.size() > 0) { + menu->addChild(createSubmenuItem("Samples Browser", "", [=](Menu* menu) { + //module->folderTreeData.resize(1); + //module->folderTreeDisplay.resize(1); + module->refreshRootFolder(); + for (unsigned int i = 1; i < module->folderTreeData[0].size(); i++) { + if (module->folderTreeData[0][i].substr(module->folderTreeData[0][i].length()-1, module->folderTreeData[0][i].length()-1) == "/") { + module->tempDir = module->folderTreeData[0][i]; + menu->addChild(createSubmenuItem(module->folderTreeDisplay[0][i], "", [=](Menu* menu) { + loadSubfolder(menu, module->folderTreeData[0][i]); + })); + } else { + menu->addChild(createMenuItem(module->folderTreeDisplay[0][i], "", [=]() {module->loadFromPatch[module->currSlot] = false;module->loadSample(module->folderTreeData[0][i], module->currSlot);})); + } + } + })); + } + + if (module->fileLoaded[module->currSlot]) { + menu->addChild(new MenuSeparator()); + menu->addChild(createMenuLabel("Current Sample:")); + menu->addChild(createMenuLabel(module->fileDescription[module->currSlot])); + menu->addChild(createMenuLabel(" " + module->samplerateDisplay[module->currSlot] + " - " + std::to_string(module->channels[module->currSlot]) + "ch")); + std::string tempDisplay = " "; + if (module->resampled[module->currSlot]) { + tempDisplay += "resampled to " + std::to_string(int(APP->engine->getSampleRate())); + if (module->toSave[module->currSlot]) + tempDisplay += " - "; + } + if (module->toSave[module->currSlot]) + tempDisplay += "to SAVE"; + if (tempDisplay != " ") { + menu->addChild(createMenuLabel(tempDisplay)); + } + + menu->addChild(createMenuItem("", "Clear", [=]() {module->clearSlot(module->currSlot);})); + + menu->addChild(new MenuSeparator()); + + menu->addChild(createMenuItem("Save FULL Sample", "", [=]() {module->menuSaveSample(SAVE_FULL, module->currSlot);})); + menu->addChild(createMenuItem("Save CUE Region", "", [=]() {module->menuSaveSample(SAVE_CUE, module->currSlot);})); + menu->addChild(createMenuItem("Save LOOP Region", "", [=]() {module->menuSaveSample(SAVE_LOOP, module->currSlot);})); + + menu->addChild(createBoolPtrMenuItem("Trim Sample after Save", "", &module->trimOnSave)); + menu->addChild(createBoolPtrMenuItem("Save Oversampled", "", &module->saveOversampled)); + + } else if (module->storedPath[module->currSlot] != "" && module->fileFound[module->currSlot] == false) { + menu->addChild(createMenuLabel("Sample ERROR:")); + menu->addChild(createMenuLabel(module->fileDescription[module->currSlot])); + menu->addChild(createMenuItem("", "Clear", [=]() {module->clearSlot(module->currSlot);})); + } + */ + + menu->addChild(new MenuSeparator()); + menu->addChild(createMenuItem("Select Samples Root", "", [=]() {module->selectRootFolder();})); + + if (module->userFolder != "") { + menu->addChild(createMenuLabel(module->userFolder)); + //menu->addChild(createMenuItem("", "Refresh", [=]() {module->refreshRootFolder();})); + } + + menu->addChild(new MenuSeparator()); + menu->addChild(createBoolPtrMenuItem("Trim Sample after Save", "", &module->trimOnSave)); + menu->addChild(createBoolPtrMenuItem("Save Oversampled", "", &module->saveOversampled)); + + menu->addChild(new MenuSeparator()); + menu->addChild(createBoolPtrMenuItem("Quantize incoming v/Oct", "", &module->quantize)); + menu->addChild(new MenuSeparator()); + + struct PbItem : MenuItem { + KeySampler* module; + int pbRange; + void onAction(const event::Action& e) override { + module->pbRange = pbRange; + } + }; + + std::string pbNames[15] = {"1 semitone", "2 semitones", "3 semitones", "4 semitones", "5 semitones", "6 semitones", "7 semitones", "8 semitones", "9 semitones", "10 semitones", "11 semitones", "1 octave", "2 octaves", "3 octaves", "4 octaves"}; + menu->addChild(createSubmenuItem("Pitch Bend Range", pbNames[module->pbRange], [=](Menu* menu) { + for (int i = 0; i < 15; i++) { + PbItem* pbItem = createMenuItem(pbNames[i]); + pbItem->rightText = CHECKMARK(module->pbRange == i); + pbItem->module = module; + pbItem->pbRange = i; + menu->addChild(pbItem); + } })); + + menu->addChild(new MenuSeparator()); + menu->addChild(createBoolPtrMenuItem("Anti-aliasing filter", "", &module->antiAlias)); + + menu->addChild(new MenuSeparator()); + /* + menu->addChild(createBoolMenuItem("Polyphonic OUTs", "", [=]() { + return module->isPolyOuts(); + }, [=](bool poly) { + module->setPolyOuts(poly); + })); + menu->addChild(createBoolPtrMenuItem("Polyphonic Master IN", "", &module->polyMaster)); + */ + + menu->addChild(createBoolPtrMenuItem("Auto Monitor Off", "", &module->autoMonOff)); + + /* + menu->addChild(new MenuSeparator()); + menu->addChild(createSubmenuItem("EOC pulse from", "", [=](Menu* menu) { + menu->addChild(createBoolPtrMenuItem("TRG/GATE (stop)", "", &module->eocFromTrg)); + menu->addChild(createBoolPtrMenuItem("CUE END", "", &module->eocFromCueEnd)); + menu->addChild(createBoolPtrMenuItem("CUE START", "", &module->eocFromCueStart)); + menu->addChild(createBoolPtrMenuItem("LOOP END", "", &module->eocFromLoopEnd)); + menu->addChild(createBoolPtrMenuItem("LOOP START", "", &module->eocFromLoopStart)); + menu->addChild(createBoolPtrMenuItem("PING", "", &module->eocFromPing)); + menu->addChild(createBoolPtrMenuItem("PONG", "", &module->eocFromPong)); + })); + */ + menu->addChild(createBoolPtrMenuItem("Disable NAV Buttons", "", &module->disableNav)); + //menu->addChild(createMenuItem("Reset Cursors", "", [=]() {module->resetCursors();})); + menu->addChild(createBoolPtrMenuItem("Reset cursors on Load", "", &module->resetCursorsOnLoad)); + /* + menu->addChild(createSubmenuItem("Presets", "", [=](Menu * menu) { + menu->addChild(createMenuItem("Wavetable", "", [=]() {module->setPreset(0);})); + menu->addChild(createMenuItem("Triggered Sample with Envelope", "", [=]() {module->setPreset(1);})); + menu->addChild(createMenuItem("Drum Player", "", [=]() {module->setPreset(2);})); + })); + */ + + menu->addChild(new MenuSeparator()); + menu->addChild(createBoolPtrMenuItem("Store Sample in Patch", "", &module->sampleInPatch)); + menu->addChild(createBoolPtrMenuItem("Unlimited REC (risky)", "", &module->unlimitedRecording)); + } +}; + +Model *modelKeySampler = createModel("KeySampler"); diff --git a/src/notes.hpp b/src/notes.hpp new file mode 100644 index 0000000..3fea1d4 --- /dev/null +++ b/src/notes.hpp @@ -0,0 +1,530 @@ +const float noteTable[128][3] = { + + { -6.000000000000000 , -6.041666666666670 , -5.958333333333330 }, + { -5.916666666666670 , -5.958333333333330 , -5.875000000000000 }, + { -5.833333333333330 , -5.875000000000000 , -5.791666666666670 }, + { -5.750000000000000 , -5.791666666666670 , -5.708333333333330 }, + { -5.666666666666670 , -5.708333333333330 , -5.625000000000000 }, + { -5.583333333333330 , -5.625000000000000 , -5.541666666666670 }, + { -5.500000000000000 , -5.541666666666670 , -5.458333333333330 }, + { -5.416666666666670 , -5.458333333333330 , -5.375000000000000 }, + { -5.333333333333330 , -5.375000000000000 , -5.291666666666670 }, + { -5.250000000000000 , -5.291666666666670 , -5.208333333333330 }, + { -5.166666666666670 , -5.208333333333330 , -5.125000000000000 }, + { -5.083333333333330 , -5.125000000000000 , -5.041666666666670 }, + { -5.000000000000000 , -5.041666666666670 , -4.958333333333330 }, + { -4.916666666666670 , -4.958333333333330 , -4.875000000000000 }, + { -4.833333333333330 , -4.875000000000000 , -4.791666666666670 }, + { -4.750000000000000 , -4.791666666666670 , -4.708333333333330 }, + { -4.666666666666670 , -4.708333333333330 , -4.625000000000000 }, + { -4.583333333333330 , -4.625000000000000 , -4.541666666666670 }, + { -4.500000000000000 , -4.541666666666670 , -4.458333333333330 }, + { -4.416666666666670 , -4.458333333333330 , -4.375000000000000 }, + { -4.333333333333330 , -4.375000000000000 , -4.291666666666670 }, + { -4.250000000000000 , -4.291666666666670 , -4.208333333333330 }, + { -4.166666666666670 , -4.208333333333330 , -4.125000000000000 }, + { -4.083333333333330 , -4.125000000000000 , -4.041666666666670 }, + { -4.000000000000000 , -4.041666666666670 , -3.958333333333330 }, + { -3.916666666666670 , -3.958333333333330 , -3.875000000000000 }, + { -3.833333333333330 , -3.875000000000000 , -3.791666666666670 }, + { -3.750000000000000 , -3.791666666666670 , -3.708333333333330 }, + { -3.666666666666670 , -3.708333333333330 , -3.625000000000000 }, + { -3.583333333333330 , -3.625000000000000 , -3.541666666666670 }, + { -3.500000000000000 , -3.541666666666670 , -3.458333333333330 }, + { -3.416666666666670 , -3.458333333333330 , -3.375000000000000 }, + { -3.333333333333330 , -3.375000000000000 , -3.291666666666670 }, + { -3.250000000000000 , -3.291666666666670 , -3.208333333333330 }, + { -3.166666666666670 , -3.208333333333330 , -3.125000000000000 }, + { -3.083333333333330 , -3.125000000000000 , -3.041666666666670 }, + { -3.000000000000000 , -3.041666666666670 , -2.958333333333330 }, + { -2.916666666666670 , -2.958333333333330 , -2.875000000000000 }, + { -2.833333333333330 , -2.875000000000000 , -2.791666666666670 }, + { -2.750000000000000 , -2.791666666666670 , -2.708333333333330 }, + { -2.666666666666670 , -2.708333333333330 , -2.625000000000000 }, + { -2.583333333333330 , -2.625000000000000 , -2.541666666666670 }, + { -2.500000000000000 , -2.541666666666670 , -2.458333333333330 }, + { -2.416666666666670 , -2.458333333333330 , -2.375000000000000 }, + { -2.333333333333330 , -2.375000000000000 , -2.291666666666670 }, + { -2.250000000000000 , -2.291666666666670 , -2.208333333333330 }, + { -2.166666666666670 , -2.208333333333330 , -2.125000000000000 }, + { -2.083333333333330 , -2.125000000000000 , -2.041666666666670 }, + { -2.000000000000000 , -2.041666666666670 , -1.958333333333330 }, + { -1.916666666666670 , -1.958333333333330 , -1.875000000000000 }, + { -1.833333333333330 , -1.875000000000000 , -1.791666666666670 }, + { -1.750000000000000 , -1.791666666666670 , -1.708333333333330 }, + { -1.666666666666670 , -1.708333333333330 , -1.625000000000000 }, + { -1.583333333333330 , -1.625000000000000 , -1.541666666666670 }, + { -1.500000000000000 , -1.541666666666670 , -1.458333333333330 }, + { -1.416666666666670 , -1.458333333333330 , -1.375000000000000 }, + { -1.333333333333330 , -1.375000000000000 , -1.291666666666670 }, + { -1.250000000000000 , -1.291666666666670 , -1.208333333333330 }, + { -1.166666666666670 , -1.208333333333330 , -1.125000000000000 }, + { -1.083333333333330 , -1.125000000000000 , -1.041666666666670 }, + { -1.000000000000000 , -1.041666666666670 , -0.958333333333333 }, + { -0.916666666666667 , -0.958333333333333 , -0.875000000000000 }, + { -0.833333333333333 , -0.875000000000000 , -0.791666666666667 }, + { -0.750000000000000 , -0.791666666666667 , -0.708333333333333 }, + { -0.666666666666667 , -0.708333333333333 , -0.625000000000000 }, + { -0.583333333333333 , -0.625000000000000 , -0.541666666666667 }, + { -0.500000000000000 , -0.541666666666667 , -0.458333333333333 }, + { -0.416666666666667 , -0.458333333333333 , -0.375000000000000 }, + { -0.333333333333333 , -0.375000000000000 , -0.291666666666667 }, + { -0.250000000000000 , -0.291666666666667 , -0.208333333333333 }, + { -0.166666666666667 , -0.208333333333333 , -0.125000000000000 }, + { -0.083333333333333 , -0.125000000000000 , -0.041666666666667 }, + { 0.000000000000000 , -0.041666666666667 , 0.041666666666667 }, + { 0.083333333333333 , 0.041666666666667 , 0.125000000000000 }, + { 0.166666666666667 , 0.125000000000000 , 0.208333333333333 }, + { 0.250000000000000 , 0.208333333333333 , 0.291666666666667 }, + { 0.333333333333333 , 0.291666666666667 , 0.375000000000000 }, + { 0.416666666666667 , 0.375000000000000 , 0.458333333333333 }, + { 0.500000000000000 , 0.458333333333333 , 0.541666666666667 }, + { 0.583333333333333 , 0.541666666666667 , 0.625000000000000 }, + { 0.666666666666667 , 0.625000000000000 , 0.708333333333333 }, + { 0.750000000000000 , 0.708333333333333 , 0.791666666666667 }, + { 0.833333333333333 , 0.791666666666667 , 0.875000000000000 }, + { 0.916666666666667 , 0.875000000000000 , 0.958333333333333 }, + { 1.000000000000000 , 0.958333333333333 , 1.041666666666670 }, + { 1.083333333333330 , 1.041666666666670 , 1.125000000000000 }, + { 1.166666666666670 , 1.125000000000000 , 1.208333333333330 }, + { 1.250000000000000 , 1.208333333333330 , 1.291666666666670 }, + { 1.333333333333330 , 1.291666666666670 , 1.375000000000000 }, + { 1.416666666666670 , 1.375000000000000 , 1.458333333333330 }, + { 1.500000000000000 , 1.458333333333330 , 1.541666666666670 }, + { 1.583333333333330 , 1.541666666666670 , 1.625000000000000 }, + { 1.666666666666670 , 1.625000000000000 , 1.708333333333330 }, + { 1.750000000000000 , 1.708333333333330 , 1.791666666666670 }, + { 1.833333333333330 , 1.791666666666670 , 1.875000000000000 }, + { 1.916666666666670 , 1.875000000000000 , 1.958333333333330 }, + { 2.000000000000000 , 1.958333333333330 , 2.041666666666670 }, + { 2.083333333333330 , 2.041666666666670 , 2.125000000000000 }, + { 2.166666666666670 , 2.125000000000000 , 2.208333333333330 }, + { 2.250000000000000 , 2.208333333333330 , 2.291666666666670 }, + { 2.333333333333330 , 2.291666666666670 , 2.375000000000000 }, + { 2.416666666666670 , 2.375000000000000 , 2.458333333333330 }, + { 2.500000000000000 , 2.458333333333330 , 2.541666666666670 }, + { 2.583333333333330 , 2.541666666666670 , 2.625000000000000 }, + { 2.666666666666670 , 2.625000000000000 , 2.708333333333330 }, + { 2.750000000000000 , 2.708333333333330 , 2.791666666666670 }, + { 2.833333333333330 , 2.791666666666670 , 2.875000000000000 }, + { 2.916666666666670 , 2.875000000000000 , 2.958333333333330 }, + { 3.000000000000000 , 2.958333333333330 , 3.041666666666670 }, + { 3.083333333333330 , 3.041666666666670 , 3.125000000000000 }, + { 3.166666666666670 , 3.125000000000000 , 3.208333333333330 }, + { 3.250000000000000 , 3.208333333333330 , 3.291666666666670 }, + { 3.333333333333330 , 3.291666666666670 , 3.375000000000000 }, + { 3.416666666666670 , 3.375000000000000 , 3.458333333333330 }, + { 3.500000000000000 , 3.458333333333330 , 3.541666666666670 }, + { 3.583333333333330 , 3.541666666666670 , 3.625000000000000 }, + { 3.666666666666670 , 3.625000000000000 , 3.708333333333330 }, + { 3.750000000000000 , 3.708333333333330 , 3.791666666666670 }, + { 3.833333333333330 , 3.791666666666670 , 3.875000000000000 }, + { 3.916666666666670 , 3.875000000000000 , 3.958333333333330 }, + { 4.000000000000000 , 3.958333333333330 , 4.041666666666670 }, + { 4.083333333333330 , 4.041666666666670 , 4.125000000000000 }, + { 4.166666666666670 , 4.125000000000000 , 4.208333333333330 }, + { 4.250000000000000 , 4.208333333333330 , 4.291666666666670 }, + { 4.333333333333330 , 4.291666666666670 , 4.375000000000000 }, + { 4.416666666666670 , 4.375000000000000 , 4.458333333333330 }, + { 4.500000000000000 , 4.458333333333330 , 4.541666666666670 }, + { 4.583333333333330 , 4.541666666666670 , 4.625000000000000 } + +}; + +const std::string noteNames[128] = { + "C-2" , + "C#-2" , + "D-2" , + "D#-2" , + "E-2" , + "F-2" , + "F#-2" , + "G-2" , + "G#-2" , + "A-2" , + "A#-2" , + "B-2" , + "C-1" , + "C#-1" , + "D-1" , + "D#-1" , + "E-1" , + "F-1" , + "F#-1" , + "G-1" , + "G#-1" , + "A-1" , + "A#-1" , + "B-1" , + "C0" , + "C#0" , + "D0" , + "D#0" , + "E0" , + "F0" , + "F#0" , + "G0" , + "G#0" , + "A0" , + "A#0" , + "B0" , + "C1" , + "C#1" , + "D1" , + "D#1" , + "E1" , + "F1" , + "F#1" , + "G1" , + "G#1" , + "A1" , + "A#1" , + "B1" , + "C2" , + "C#2" , + "D2" , + "D#2" , + "E2" , + "F2" , + "F#2" , + "G2" , + "G#2" , + "A2" , + "A#2" , + "B2" , + "C3" , + "C#3" , + "D3" , + "D#3" , + "E3" , + "F3" , + "F#3" , + "G3" , + "G#3" , + "A3" , + "A#3" , + "B3" , + "C4" , + "C#4" , + "D4" , + "D#4" , + "E4" , + "F4" , + "F#4" , + "G4" , + "G#4" , + "A4" , + "A#4" , + "B4" , + "C5" , + "C#5" , + "D5" , + "D#5" , + "E5" , + "F5" , + "F#5" , + "G5" , + "G#5" , + "A5" , + "A#5" , + "B5" , + "C6" , + "C#6" , + "D6" , + "D#6" , + "E6" , + "F6" , + "F#6" , + "G6" , + "G#6" , + "A6" , + "A#6" , + "B6" , + "C7" , + "C#7" , + "D7" , + "D#7" , + "E7" , + "F7" , + "F#7" , + "G7" , + "G#7" , + "A7" , + "A#7" , + "B7" , + "C8" , + "C#8" , + "D8" , + "D#8" , + "E8" , + "F8" , + "F#8" , + "G8" + +}; + +const float semiTonesPos[128] = { + + 1.000000000000000 , + 1.059463094359300 , + 1.122462048309370 , + 1.189207115002720 , + 1.259921049894870 , + 1.334839854170030 , + 1.414213562373100 , + 1.498307076876680 , + 1.587401051968200 , + 1.681792830507430 , + 1.781797436280680 , + 1.887748625363390 , + 2.000000000000000 , + 2.118926188718590 , + 2.244924096618750 , + 2.378414230005440 , + 2.519842099789750 , + 2.669679708340070 , + 2.828427124746190 , + 2.996614153753360 , + 3.174802103936400 , + 3.363585661014860 , + 3.563594872561360 , + 3.775497250726770 , + 4.000000000000000 , + 4.237852377437180 , + 4.489848193237490 , + 4.756828460010880 , + 5.039684199579490 , + 5.339359416680140 , + 5.656854249492380 , + 5.993228307506730 , + 6.349604207872800 , + 6.727171322029720 , + 7.127189745122710 , + 7.550994501453550 , + 8.000000000000000 , + 8.475704754874360 , + 8.979696386474980 , + 9.513656920021770 , + 10.079368399159000 , + 10.678718833360300 , + 11.313708498984800 , + 11.986456615013500 , + 12.699208415745600 , + 13.454342644059400 , + 14.254379490245400 , + 15.101989002907100 , + 16.000000000000000 , + 16.951409509748700 , + 17.959392772950000 , + 19.027313840043500 , + 20.158736798318000 , + 21.357437666720600 , + 22.627416997969500 , + 23.972913230026900 , + 25.398416831491200 , + 26.908685288118900 , + 28.508758980490800 , + 30.203978005814200 , + 32.000000000000000 , + 33.902819019497400 , + 35.918785545899900 , + 38.054627680087100 , + 40.317473596635900 , + 42.714875333441100 , + 45.254833995939000 , + 47.945826460053800 , + 50.796833662982400 , + 53.817370576237700 , + 57.017517960981700 , + 60.407956011628400 , + 64.000000000000000 , + 67.805638038994900 , + 71.837571091799900 , + 76.109255360174200 , + 80.634947193271800 , + 85.429750666882200 , + 90.509667991878100 , + 95.891652920107600 , + 101.593667325965000 , + 107.634741152475000 , + 114.035035921963000 , + 120.815912023257000 , + 128.000000000000000 , + 135.611276077990000 , + 143.675142183600000 , + 152.218510720348000 , + 161.269894386544000 , + 170.859501333764000 , + 181.019335983756000 , + 191.783305840215000 , + 203.187334651930000 , + 215.269482304951000 , + 228.070071843927000 , + 241.631824046513000 , + 256.000000000000000 , + 271.222552155980000 , + 287.350284367199000 , + 304.437021440696000 , + 322.539788773088000 , + 341.719002667529000 , + 362.038671967512000 , + 383.566611680430000 , + 406.374669303859000 , + 430.538964609902000 , + 456.140143687854000 , + 483.263648093027000 , + 512.000000000000000 , + 542.445104311959000 , + 574.700568734399000 , + 608.874042881393000 , + 645.079577546175000 , + 683.438005335057000 , + 724.077343935025000 , + 767.133223360861000 , + 812.749338607718000 , + 861.077929219803000 , + 912.280287375708000 , + 966.527296186053000 , + 1024.000000000000000 , + 1084.890208623920000 , + 1149.401137468800000 , + 1217.748085762790000 , + 1290.159155092350000 , + 1366.876010670110000 , + 1448.154687870050000 , + 1534.266446721720000 + +}; + +const float semiTonesNeg[128] = { + + 1.000000000000000 , + 0.943874312681693 , + 0.890898718140339 , + 0.840896415253715 , + 0.793700525984100 , + 0.749153538438341 , + 0.707106781186547 , + 0.667419927085017 , + 0.629960524947437 , + 0.594603557501361 , + 0.561231024154687 , + 0.529731547179648 , + 0.500000000000000 , + 0.471937156340847 , + 0.445449359070170 , + 0.420448207626857 , + 0.396850262992050 , + 0.374576769219170 , + 0.353553390593274 , + 0.333709963542509 , + 0.314980262473718 , + 0.297301778750680 , + 0.280615512077343 , + 0.264865773589824 , + 0.250000000000000 , + 0.235968578170423 , + 0.222724679535085 , + 0.210224103813429 , + 0.198425131496025 , + 0.187288384609585 , + 0.176776695296637 , + 0.166854981771254 , + 0.157490131236859 , + 0.148650889375340 , + 0.140307756038672 , + 0.132432886794912 , + 0.125000000000000 , + 0.117984289085212 , + 0.111362339767542 , + 0.105112051906714 , + 0.099212565748013 , + 0.093644192304793 , + 0.088388347648318 , + 0.083427490885627 , + 0.078745065618430 , + 0.074325444687670 , + 0.070153878019336 , + 0.066216443397456 , + 0.062500000000000 , + 0.058992144542606 , + 0.055681169883771 , + 0.052556025953357 , + 0.049606282874006 , + 0.046822096152396 , + 0.044194173824159 , + 0.041713745442814 , + 0.039372532809215 , + 0.037162722343835 , + 0.035076939009668 , + 0.033108221698728 , + 0.031250000000000 , + 0.029496072271303 , + 0.027840584941886 , + 0.026278012976679 , + 0.024803141437003 , + 0.023411048076198 , + 0.022097086912080 , + 0.020856872721407 , + 0.019686266404607 , + 0.018581361171918 , + 0.017538469504834 , + 0.016554110849364 , + 0.015625000000000 , + 0.014748036135652 , + 0.013920292470943 , + 0.013139006488339 , + 0.012401570718502 , + 0.011705524038099 , + 0.011048543456040 , + 0.010428436360703 , + 0.009843133202304 , + 0.009290680585959 , + 0.008769234752417 , + 0.008277055424682 , + 0.007812500000000 , + 0.007374018067826 , + 0.006960146235471 , + 0.006569503244170 , + 0.006200785359251 , + 0.005852762019050 , + 0.005524271728020 , + 0.005214218180352 , + 0.004921566601152 , + 0.004645340292979 , + 0.004384617376208 , + 0.004138527712341 , + 0.003906250000000 , + 0.003687009033913 , + 0.003480073117736 , + 0.003284751622085 , + 0.003100392679625 , + 0.002926381009525 , + 0.002762135864010 , + 0.002607109090176 , + 0.002460783300576 , + 0.002322670146490 , + 0.002192308688104 , + 0.002069263856171 , + 0.001953125000000 , + 0.001843504516956 , + 0.001740036558868 , + 0.001642375811042 , + 0.001550196339813 , + 0.001463190504762 , + 0.001381067932005 , + 0.001303554545088 , + 0.001230391650288 , + 0.001161335073245 , + 0.001096154344052 , + 0.001034631928085 , + 0.000976562500000 , + 0.000921752258478 , + 0.000870018279434 , + 0.000821187905521 , + 0.000775098169906 , + 0.000731595252381 , + 0.000690533966002 , + 0.000651777272544 + +}; \ No newline at end of file diff --git a/src/plugin.cpp b/src/plugin.cpp index bdda8f2..2385293 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -33,6 +33,7 @@ void init(Plugin* p) { p->addModel(modelHolder); p->addModel(modelHolderCompact); p->addModel(modelHolder8); + p->addModel(modelKeySampler); p->addModel(modelModulator); p->addModel(modelModulator7); p->addModel(modelModulator7Compact); diff --git a/src/plugin.hpp b/src/plugin.hpp index 0cfb748..0f4b6e6 100644 --- a/src/plugin.hpp +++ b/src/plugin.hpp @@ -33,6 +33,7 @@ extern Model* modelEnver; extern Model* modelHolder; extern Model* modelHolderCompact; extern Model* modelHolder8; +extern Model* modelKeySampler; extern Model* modelModulator; extern Model* modelModulator7; extern Model* modelModulator7Compact;