diff --git a/README.md b/README.md index c0b2e33..74f4221 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SickoCV v2.6.17-beta7 +# SickoCV v2.6.17-beta9 VCV Rack plugin modules (BETA TEST AREA) Compile or **download binary for ANY platform** on the releases page @@ -17,10 +17,11 @@ Please check your subscription on https://library.vcvrack.com/plugins and look f ## **to do list:** - keySampler: correct midi learn function that doesn't update at first time the new key -## **changelog** +## **changelog** - added 'randLoops' module - added 'simpleSeq4' module - added 'Attenuator' option on adder8 module +- 'sickoLooper' and 'clocker' modules: improved audio click management - 'clocker' and 'clocker2' modules: fixed a bug on /2 clock division that was actually x2, and fixed its display color to red instead of green - 'sickoQuant' and 'sickoQuant4' module browser search made easier with keyword 'squant' - multiRouter / multiSwitcher: added 'cycle' and 'RST input = reverse advance' options in the right-click menu diff --git a/changelog.md b/changelog.md index 3402060..03c4c68 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,8 @@ -### 2.6.17 (2024-12-29) +### 2.6.17 (2025-01-11) - added 'randLoops' module - added 'simpleSeq4' module - added 'Attenuator' option on adder8 module +- 'sickoLooper' and 'clocker' modules: improved audio click management - 'clocker' and 'clocker2' modules: fixed a bug on /2 clock division that was actually x2, and fixed its display color to red instead of green - 'sickoQuant' and 'sickoQuant4' module browser search made easier with keyword 'squant' - multiRouter / multiSwitcher: added 'cycle' and 'RST input = reverse advance' options in the right-click menu diff --git a/plugin.json b/plugin.json index 9675f55..d939409 100644 --- a/plugin.json +++ b/plugin.json @@ -1,7 +1,7 @@ { "slug": "SickoCV", "name": "SickoCV", - "version": "2.6.17-beta7", + "version": "2.6.17-beta9", "license": "GPL-3.0-or-later", "brand": "Sickozell", "author": "Sickozell", diff --git a/src/Clocker.cpp b/src/Clocker.cpp index d8187ff..c6b4cc3 100644 --- a/src/Clocker.cpp +++ b/src/Clocker.cpp @@ -15,6 +15,9 @@ #define MEDIUM_SMOOTH 2 #define HIGH_SMOOTH 3 +#define CLICK_STANDARD 0 +#define CLICK_CUSTOM 3 + #include "plugin.hpp" #include "osdialog.h" //#define DR_WAV_IMPLEMENTATION @@ -139,6 +142,8 @@ struct Clocker : Module { float clickOutput; + int clickSelect = CLICK_STANDARD; + double a0, a1, a2, b1, b2, z1, z2; int click_setting; @@ -361,11 +366,14 @@ struct Clocker : Module { registerValue[i] = 0; */ + /* for (int i = 0; i < 2; i++) { clearSlot(i); play[i] = false; } + */ setClick(0); + Module::onReset(e); } @@ -374,12 +382,15 @@ struct Clocker : Module { oneMsTime = (APP->engine->getSampleRate()) / 1000; //oneMsTime = (APP->engine->getSampleRate()) / 10; // for testing purposes + /* for (int i = 0; i < 2; i++) { if (fileLoaded[i]) { play[i] = false; loadSample(storedPath[i],i); } } + */ + setClick(clickSelect); } json_t *dataToJson() override { @@ -398,6 +409,7 @@ struct Clocker : Module { json_object_set_new(rootJ, "Swing4", json_boolean(divSwing[3])); json_object_set_new(rootJ, "Slot1", json_string(storedPath[0].c_str())); json_object_set_new(rootJ, "Slot2", json_string(storedPath[1].c_str())); + json_object_set_new(rootJ, "clickSelect", json_integer(clickSelect)); return rootJ; } @@ -466,6 +478,7 @@ struct Clocker : Module { if (swing4J) divSwing[3] = json_boolean_value(swing4J); + /* json_t *slot1J = json_object_get(rootJ, "Slot1"); if (slot1J) { storedPath[0] = json_string_value(slot1J); @@ -476,6 +489,31 @@ struct Clocker : Module { storedPath[1] = json_string_value(slot2J); loadSample(storedPath[1], 1); } + */ + json_t *clickSlot1J = json_object_get(rootJ, "Slot1"); + if (clickSlot1J) { + storedPath[0] = json_string_value(clickSlot1J); + if (storedPath[0] == "") + clearSlot(0); + else + loadSample(storedPath[0], 0, true); + } + json_t *clickSlot2J = json_object_get(rootJ, "Slot2"); + if (clickSlot2J) { + storedPath[1] = json_string_value(clickSlot2J); + if (storedPath[1] == "") + clearSlot(1); + else + loadSample(storedPath[1], 1, true); + } + + json_t* clickSelectJ = json_object_get(rootJ, "clickSelect"); + if (clickSelectJ) { + clickSelect = json_integer_value(clickSelectJ); + if (clickSelect < 0 || clickSelect > 3) + clickSelect = CLICK_STANDARD; + setClick(clickSelect); + } } void calcBiquadLpf(double frequency, double samplerate, double Q) { @@ -513,8 +551,14 @@ struct Clocker : Module { char *path = osdialog_file(OSDIALOG_OPEN, NULL, NULL, filters); fileLoaded[slot] = false; if (path) { + /* loadSample(path, slot); storedPath[slot] = std::string(path); + */ + loadSample(path, slot, true); + storedPath[slot] = std::string(path); + if (clickSelect != CLICK_CUSTOM) + setClick(clickSelect); } else { fileLoaded[slot] = true; } @@ -524,7 +568,7 @@ struct Clocker : Module { free(path); } - void loadSample(std::string path, int slot) { + void loadSample(std::string path, int slot, bool customClick) { z1 = 0; z2 = 0; unsigned int c; @@ -640,12 +684,22 @@ struct Clocker : Module { tempBuffer2.clear(); char* pathDup = strdup(path.c_str()); + /* fileDescription[slot] = basename(pathDup); free(pathDup); storedPath[slot] = path; fileLoaded[slot] = true; + */ + if (customClick) { + fileDescription[slot] = basename(pathDup); + + storedPath[slot] = path; + + } + fileLoaded[slot] = true; + free(pathDup); } else { fileFound[slot] = false; @@ -658,27 +712,39 @@ struct Clocker : Module { void clearSlot(int slot) { storedPath[slot] = ""; fileDescription[slot] = "--none--"; - fileFound[slot] = false; - fileLoaded[slot] = false; - playBuffer[slot].clear(); - totalSampleC[slot] = 0; + if (clickSelect == CLICK_CUSTOM) { + fileFound[slot] = false; + fileLoaded[slot] = false; + playBuffer[slot].clear(); + totalSampleC[slot] = 0; + } } void setClick(int clickNo) { switch (clickNo) { case 0: - loadSample(asset::plugin(pluginInstance, "res/clicks/click0_beat.wav"),0); - loadSample(asset::plugin(pluginInstance, "res/clicks/click0_bar.wav"),1); + loadSample(asset::plugin(pluginInstance, "res/clicks/click0_beat.wav"), 0, false); + loadSample(asset::plugin(pluginInstance, "res/clicks/click0_bar.wav"), 1, false); break; case 1: - loadSample(asset::plugin(pluginInstance, "res/clicks/click1_beat.wav"),0); - loadSample(asset::plugin(pluginInstance, "res/clicks/click1_bar.wav"),1); + loadSample(asset::plugin(pluginInstance, "res/clicks/click1_beat.wav"), 0, false); + loadSample(asset::plugin(pluginInstance, "res/clicks/click1_bar.wav"), 1, false); break; case 2: - loadSample(asset::plugin(pluginInstance, "res/clicks/click2_beat.wav"),0); - loadSample(asset::plugin(pluginInstance, "res/clicks/click2_bar.wav"),1); + loadSample(asset::plugin(pluginInstance, "res/clicks/click2_beat.wav"), 0, false); + loadSample(asset::plugin(pluginInstance, "res/clicks/click2_bar.wav"), 1, false); + break; + + case 3: + if (storedPath[0] != "") + loadSample(storedPath[0], 0, true); + else + clearSlot(0); + if (storedPath[1] != "") + loadSample(storedPath[1], 1, true); + else clearSlot(1); break; } } @@ -2024,6 +2090,8 @@ struct ClockerWidget : ModuleWidget { })); */ + + /* menu->addChild(new MenuSeparator()); menu->addChild(createSubmenuItem("Click Presets", "", [=](Menu * menu) { @@ -2042,8 +2110,7 @@ struct ClockerWidget : ModuleWidget { menu->addChild(createMenuItem("File: " + module->fileDescription[1], "", [=]() {module->menuLoadSample(1);})); menu->addChild(createMenuItem("", "Clear", [=]() {module->clearSlot(1);})); - menu->addChild(new MenuSeparator()); - menu->addChild(createBoolPtrMenuItem("Beat pulse also on Bar", "", &module->beatOnBar)); + */ menu->addChild(new MenuSeparator()); menu->addChild(createSubmenuItem("On Run", "", [=](Menu* menu) { @@ -2054,6 +2121,40 @@ struct ClockerWidget : ModuleWidget { menu->addChild(createBoolPtrMenuItem("Reset Bar", "", &module->resetOnStop)); menu->addChild(createBoolPtrMenuItem("Pulse to RST out", "", &module->resetPulseOnStop)); })); + + menu->addChild(new MenuSeparator()); + menu->addChild(createBoolPtrMenuItem("Beat pulse also on Bar", "", &module->beatOnBar)); + + struct ClickItem : MenuItem { + Clocker* module; + int clickSelect; + void onAction(const event::Action& e) override { + module->clickSelect = clickSelect; + module->setClick(clickSelect); + } + }; + + menu->addChild(createSubmenuItem("Click Settings", "", [=](Menu * menu) { + + std::string clickNames[4] = {"Standard", "Click1", "Click2", "Custom"}; + for (int i = 0; i < 4; i++) { + ClickItem* clickItem = createMenuItem(clickNames[i]); + clickItem->rightText = CHECKMARK(module->clickSelect == i); + clickItem->module = module; + clickItem->clickSelect = i; + menu->addChild(clickItem); + } + + menu->addChild(new MenuSeparator()); + + menu->addChild(createMenuItem("Custom BEAT click", "", [=]() {module->menuLoadSample(0);})); + menu->addChild(createMenuItem("File: " + module->fileDescription[0], "", [=]() {module->menuLoadSample(0);})); + menu->addChild(createMenuItem("", "Clear", [=]() {module->clearSlot(0);})); + menu->addChild(new MenuSeparator()); + menu->addChild(createMenuItem("Custom BAR click", "", [=]() {module->menuLoadSample(1);})); + menu->addChild(createMenuItem("File: " + module->fileDescription[1], "", [=]() {module->menuLoadSample(1);})); + menu->addChild(createMenuItem("", "Clear", [=]() {module->clearSlot(1);})); + })); } }; diff --git a/src/RandLoops.cpp b/src/RandLoops.cpp index 37a476b..b7073ed 100644 --- a/src/RandLoops.cpp +++ b/src/RandLoops.cpp @@ -47,9 +47,11 @@ struct RandLoops : Module { float oneMsTime = (APP->engine->getSampleRate()) / 1000; float delTrig = 0; + float prevDelTrig = 0; bool delWait = false; float addTrig = 0; + float prevAddTrig = 0; bool addWait = false; float clock = 0; @@ -484,25 +486,41 @@ struct RandLoops : Module { delTrig = inputs[DEL_INPUT].getVoltage() + params[DEL_BUTTON].getValue(); - if (delTrig >= 1.f) { - delTrig = 1; - delWait = true; - lights[DEL_LIGHT].setBrightness(1.f); - } - if (!bufferedAddDel) + if (bufferedAddDel) { + if (delTrig >= 1.f && prevDelTrig < 1.f) { + delTrig = 1; + delWait = true; + lights[DEL_LIGHT].setBrightness(1.f); + } + } else { + if (delTrig >= 1.f) { + delTrig = 1; + delWait = true; + } lights[DEL_LIGHT].setBrightness(delTrig); + } + + prevDelTrig = delTrig; // -------------------------------- add trigger addTrig = inputs[ADD_INPUT].getVoltage() + params[ADD_BUTTON].getValue(); - if (addTrig >= 1.f) { - addTrig = 1; - addWait = true; - lights[ADD_LIGHT].setBrightness(1.f); - } - if (!bufferedAddDel) + if (bufferedAddDel) { + if (addTrig >= 1.f && prevAddTrig < 1.f) { + addTrig = 1; + addWait = true; + lights[ADD_LIGHT].setBrightness(1.f); + } + } else { + if (addTrig >= 1.f) { + addTrig = 1; + addWait = true; + } lights[ADD_LIGHT].setBrightness(addTrig); + } + + prevAddTrig = addTrig; // -------------------------------- clock trigger diff --git a/src/SickoLooper1.cpp b/src/SickoLooper1.cpp index 0aa49be..3b5a672 100644 --- a/src/SickoLooper1.cpp +++ b/src/SickoLooper1.cpp @@ -37,6 +37,9 @@ #define START_ALL 1 #define STOP_ALL 2 +#define CLICK_STANDARD 0 +#define CLICK_CUSTOM 3 + #define sampleCoeff 2 #include "plugin.hpp" @@ -360,6 +363,8 @@ struct SickoLooper1 : Module { const unsigned int minSamplesToLoad = 9; + int clickSelect = CLICK_STANDARD; + vector clickPlayBuffer[2]; vector clickTempBuffer; vector clickTempBuffer2; @@ -537,6 +542,7 @@ struct SickoLooper1 : Module { json_object_set_new(rootJ, "internalClockAlwaysOn", json_boolean(internalClockAlwaysOn)); json_object_set_new(rootJ, "ClickSlot1", json_string(clickStoredPath[0].c_str())); json_object_set_new(rootJ, "ClickSlot2", json_string(clickStoredPath[1].c_str())); + json_object_set_new(rootJ, "clickSelect", json_integer(clickSelect)); return rootJ; } @@ -586,12 +592,26 @@ struct SickoLooper1 : Module { json_t *clickSlot1J = json_object_get(rootJ, "ClickSlot1"); if (clickSlot1J) { clickStoredPath[0] = json_string_value(clickSlot1J); - clickLoadSample(clickStoredPath[0], 0); + if (clickStoredPath[0] == "") + clickClearSlot(0); + else + clickLoadSample(clickStoredPath[0], 0, true); } json_t *clickSlot2J = json_object_get(rootJ, "ClickSlot2"); if (clickSlot2J) { clickStoredPath[1] = json_string_value(clickSlot2J); - clickLoadSample(clickStoredPath[1], 1); + if (clickStoredPath[1] == "") + clickClearSlot(1); + else + clickLoadSample(clickStoredPath[1], 1, true); + } + + json_t* clickSelectJ = json_object_get(rootJ, "clickSelect"); + if (clickSelectJ) { + clickSelect = json_integer_value(clickSelectJ); + if (clickSelect < 0 || clickSelect > 3) + clickSelect = CLICK_STANDARD; + setClick(clickSelect); } } @@ -690,12 +710,15 @@ struct SickoLooper1 : Module { fastPulseTime = sampleRate / 50; slowPulseTime = sampleRate / 5; + /* for (int i = 0; i < 2; i++) { if (clickFileLoaded[i]) { clickPlay[i] = false; clickLoadSample(clickStoredPath[i],i); } } + */ + setClick(clickSelect); if (trackStatus != EMPTY) { double resampleCoeff; @@ -1417,8 +1440,16 @@ struct SickoLooper1 : Module { char *path = osdialog_file(OSDIALOG_OPEN, NULL, NULL, filters); clickFileLoaded[slot] = false; if (path) { - clickLoadSample(path, slot); + /* + if (clickSelect == CLICK_CUSTOM) + clickLoadSample(path, slot, true); clickStoredPath[slot] = std::string(path); + */ + clickLoadSample(path, slot, true); + clickStoredPath[slot] = std::string(path); + if (clickSelect != CLICK_CUSTOM) + setClick(clickSelect); + } else { clickFileLoaded[slot] = true; } @@ -1428,7 +1459,7 @@ struct SickoLooper1 : Module { free(path); } - void clickLoadSample(std::string path, int slot) { + void clickLoadSample(std::string path, int slot, bool customClick) { z1 = 0; z2 = 0; unsigned int c; @@ -1540,17 +1571,23 @@ struct SickoLooper1 : Module { clickTempBuffer2.clear(); char* pathDup = strdup(path.c_str()); - clickFileDescription[slot] = basename(pathDup); - free(pathDup); - clickStoredPath[slot] = path; + //if (clickSelect == CLICK_CUSTOM) { + if (customClick) { + clickFileDescription[slot] = basename(pathDup); + clickStoredPath[slot] = path; + + } clickFileLoaded[slot] = true; + free(pathDup); } else { clickFileLoaded[slot] = false; - clickStoredPath[slot] = path; - clickFileDescription[slot] = "(!)"+path; + if (clickSelect == CLICK_CUSTOM) { + clickStoredPath[slot] = path; + clickFileDescription[slot] = "(!)"+path; + } } }; @@ -1558,26 +1595,38 @@ struct SickoLooper1 : Module { void clickClearSlot(int slot) { clickStoredPath[slot] = ""; clickFileDescription[slot] = "--none--"; - clickFileLoaded[slot] = false; - clickPlayBuffer[slot].clear(); - clickTotalSampleC[slot] = 0; + if (clickSelect == CLICK_CUSTOM) { + clickFileLoaded[slot] = false; + clickPlayBuffer[slot].clear(); + clickTotalSampleC[slot] = 0; + } } void setClick(int clickNo) { switch (clickNo) { case 0: - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click0_beat.wav"),0); - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click0_bar.wav"),1); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click0_beat.wav"), 0, false); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click0_bar.wav"), 1, false); break; case 1: - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click1_beat.wav"),0); - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click1_bar.wav"),1); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click1_beat.wav"), 0, false); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click1_bar.wav"), 1, false); break; case 2: - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click2_beat.wav"),0); - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click2_bar.wav"),1); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click2_beat.wav"), 0, false); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click2_bar.wav"), 1, false); + break; + + case 3: + if (clickStoredPath[0] != "") + clickLoadSample(clickStoredPath[0], 0, true); + else + clickClearSlot(0); + if (clickStoredPath[1] != "") + clickLoadSample(clickStoredPath[1], 1, true); + else clickClearSlot(1); break; } } @@ -4790,20 +4839,42 @@ struct SickoLooper1Widget : ModuleWidget { }, [=](bool internalClockAlwaysOn) { module->setInternalClock(internalClockAlwaysOn); })); + + struct ClickItem : MenuItem { + SickoLooper1* module; + int clickSelect; + void onAction(const event::Action& e) override { + module->clickSelect = clickSelect; + module->setClick(clickSelect); + } + }; menu->addChild(createSubmenuItem("Click Settings", "", [=](Menu * menu) { + + /* menu->addChild(createSubmenuItem("Click Presets", "", [=](Menu * menu) { menu->addChild(createMenuItem("Standard", "", [=]() {module->setClick(0);})); menu->addChild(createMenuItem("Click1", "", [=]() {module->setClick(1);})); menu->addChild(createMenuItem("Click2", "", [=]() {module->setClick(2);})); - })); + }));*/ + + + //menu->addChild(createMenuLabel("PLAY Button Sequence")); + std::string clickNames[4] = {"Standard", "Click1", "Click2", "Custom"}; + for (int i = 0; i < 4; i++) { + ClickItem* clickItem = createMenuItem(clickNames[i]); + clickItem->rightText = CHECKMARK(module->clickSelect == i); + clickItem->module = module; + clickItem->clickSelect = i; + menu->addChild(clickItem); + } menu->addChild(new MenuSeparator()); - menu->addChild(createMenuItem("Load BEAT click", "", [=]() {module->clickMenuLoadSample(0);})); + menu->addChild(createMenuItem("Custom BEAT click", "", [=]() {module->clickMenuLoadSample(0);})); menu->addChild(createMenuItem("File: " + module->clickFileDescription[0], "", [=]() {module->clickMenuLoadSample(0);})); menu->addChild(createMenuItem("", "Clear", [=]() {module->clickClearSlot(0);})); menu->addChild(new MenuSeparator()); - menu->addChild(createMenuItem("Load BAR click", "", [=]() {module->clickMenuLoadSample(1);})); + menu->addChild(createMenuItem("Custom BAR click", "", [=]() {module->clickMenuLoadSample(1);})); menu->addChild(createMenuItem("File: " + module->clickFileDescription[1], "", [=]() {module->clickMenuLoadSample(1);})); menu->addChild(createMenuItem("", "Clear", [=]() {module->clickClearSlot(1);})); })); diff --git a/src/SickoLooper3.cpp b/src/SickoLooper3.cpp index ebd59aa..f7d494c 100644 --- a/src/SickoLooper3.cpp +++ b/src/SickoLooper3.cpp @@ -38,6 +38,9 @@ #define REC_PLAY_OVERDUB 1 #define REC_OVERDUB_PLAY 2 +#define CLICK_STANDARD 0 +#define CLICK_CUSTOM 3 + #include "plugin.hpp" #include "osdialog.h" //#define DR_WAV_IMPLEMENTATION @@ -347,6 +350,8 @@ struct SickoLooper3 : Module { const unsigned int minSamplesToLoad = 9; + int clickSelect = CLICK_STANDARD; + vector clickPlayBuffer[2]; vector clickTempBuffer; vector clickTempBuffer2; @@ -552,6 +557,7 @@ struct SickoLooper3 : Module { json_object_set_new(rootJ, "internalClockAlwaysOn", json_boolean(internalClockAlwaysOn)); json_object_set_new(rootJ, "ClickSlot1", json_string(clickStoredPath[0].c_str())); json_object_set_new(rootJ, "ClickSlot2", json_string(clickStoredPath[1].c_str())); + json_object_set_new(rootJ, "clickSelect", json_integer(clickSelect)); return rootJ; } @@ -615,6 +621,7 @@ struct SickoLooper3 : Module { setInternalClock(internalClockAlwaysOn); } + /* json_t *clickSlot1J = json_object_get(rootJ, "ClickSlot1"); if (clickSlot1J) { clickStoredPath[0] = json_string_value(clickSlot1J); @@ -625,6 +632,32 @@ struct SickoLooper3 : Module { clickStoredPath[1] = json_string_value(clickSlot2J); clickLoadSample(clickStoredPath[1], 1); } + */ + + json_t *clickSlot1J = json_object_get(rootJ, "ClickSlot1"); + if (clickSlot1J) { + clickStoredPath[0] = json_string_value(clickSlot1J); + if (clickStoredPath[0] == "") + clickClearSlot(0); + else + clickLoadSample(clickStoredPath[0], 0, true); + } + json_t *clickSlot2J = json_object_get(rootJ, "ClickSlot2"); + if (clickSlot2J) { + clickStoredPath[1] = json_string_value(clickSlot2J); + if (clickStoredPath[1] == "") + clickClearSlot(1); + else + clickLoadSample(clickStoredPath[1], 1, true); + } + + json_t* clickSelectJ = json_object_get(rootJ, "clickSelect"); + if (clickSelectJ) { + clickSelect = json_integer_value(clickSelectJ); + if (clickSelect < 0 || clickSelect > 3) + clickSelect = CLICK_STANDARD; + setClick(clickSelect); + } } @@ -716,12 +749,15 @@ struct SickoLooper3 : Module { fastPulseTime = sampleRate / 50; slowPulseTime = sampleRate / 5; + /* for (int i = 0; i < 2; i++) { if (clickFileLoaded[i]) { clickPlay[i] = false; clickLoadSample(clickStoredPath[i],i); } } + */ + setClick(clickSelect); for (int track = 0; track < MAX_TRACKS; track++) { @@ -1136,6 +1172,7 @@ struct SickoLooper3 : Module { json_object_set_new(rootJ, "internalClockAlwaysOn", json_boolean(internalClockAlwaysOn)); json_object_set_new(rootJ, "clickSlot1", json_string(clickStoredPath[0].c_str())); json_object_set_new(rootJ, "clickSlot2", json_string(clickStoredPath[1].c_str())); + json_object_set_new(rootJ, "clickSelect", json_integer(clickSelect)); json_object_set_new(rootJ, "click", json_integer(int(params[CLICK_BUT_PARAM].getValue()))); json_object_set_new(rootJ, "clickVol", json_real(params[CLICKVOL_KNOB_PARAM].getValue())); json_object_set_new(rootJ, "clickToMaster", json_integer(int(params[CLICKTOMASTER_SWITCH].getValue()))); @@ -1204,6 +1241,7 @@ struct SickoLooper3 : Module { internalClockAlwaysOn = json_boolean_value(internalClockAlwaysOnJ); setInternalClock(internalClockAlwaysOn); } + /* json_t *clickSlot1J = json_object_get(rootJ, "clickSlot1"); if (clickSlot1J) { clickStoredPath[0] = json_string_value(clickSlot1J); @@ -1214,6 +1252,33 @@ struct SickoLooper3 : Module { clickStoredPath[1] = json_string_value(clickSlot2J); clickLoadSample(clickStoredPath[1], 1); } + */ + json_t *clickSlot1J = json_object_get(rootJ, "ClickSlot1"); + if (clickSlot1J) { + clickStoredPath[0] = json_string_value(clickSlot1J); + if (clickStoredPath[0] == "") + clickClearSlot(0); + else + clickLoadSample(clickStoredPath[0], 0, true); + } + json_t *clickSlot2J = json_object_get(rootJ, "ClickSlot2"); + if (clickSlot2J) { + clickStoredPath[1] = json_string_value(clickSlot2J); + if (clickStoredPath[1] == "") + clickClearSlot(1); + else + clickLoadSample(clickStoredPath[1], 1, true); + } + + json_t* clickSelectJ = json_object_get(rootJ, "clickSelect"); + if (clickSelectJ) { + clickSelect = json_integer_value(clickSelectJ); + if (clickSelect < 0 || clickSelect > 3) + clickSelect = CLICK_STANDARD; + setClick(clickSelect); + } + + json_t *clickJ = json_object_get(rootJ, "click"); if (clickJ) { params[CLICK_BUT_PARAM].setValue(json_integer_value(clickJ)); @@ -1551,8 +1616,14 @@ struct SickoLooper3 : Module { char *path = osdialog_file(OSDIALOG_OPEN, NULL, NULL, filters); clickFileLoaded[slot] = false; if (path) { + /* clickLoadSample(path, slot); clickStoredPath[slot] = std::string(path); + */ + clickLoadSample(path, slot, true); + clickStoredPath[slot] = std::string(path); + if (clickSelect != CLICK_CUSTOM) + setClick(clickSelect); } else { clickFileLoaded[slot] = true; } @@ -1562,7 +1633,7 @@ struct SickoLooper3 : Module { free(path); } - void clickLoadSample(std::string path, int slot) { + void clickLoadSample(std::string path, int slot, bool customClick) { z1 = 0; z2 = 0; unsigned int c; @@ -1674,12 +1745,22 @@ struct SickoLooper3 : Module { clickTempBuffer2.clear(); char* pathDup = strdup(path.c_str()); + /* clickFileDescription[slot] = basename(pathDup); free(pathDup); clickStoredPath[slot] = path; clickFileLoaded[slot] = true; + */ + if (customClick) { + clickFileDescription[slot] = basename(pathDup); + + clickStoredPath[slot] = path; + + } + clickFileLoaded[slot] = true; + free(pathDup); } else { clickFileLoaded[slot] = false; @@ -1692,26 +1773,38 @@ struct SickoLooper3 : Module { void clickClearSlot(int slot) { clickStoredPath[slot] = ""; clickFileDescription[slot] = "--none--"; - clickFileLoaded[slot] = false; - clickPlayBuffer[slot].clear(); - clickTotalSampleC[slot] = 0; + if (clickSelect == CLICK_CUSTOM) { + clickFileLoaded[slot] = false; + clickPlayBuffer[slot].clear(); + clickTotalSampleC[slot] = 0; + } } void setClick(int clickNo) { switch (clickNo) { case 0: - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click0_beat.wav"),0); - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click0_bar.wav"),1); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click0_beat.wav"), 0, false); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click0_bar.wav"), 1, false); break; case 1: - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click1_beat.wav"),0); - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click1_bar.wav"),1); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click1_beat.wav"), 0, false); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click1_bar.wav"), 1, false); break; case 2: - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click2_beat.wav"),0); - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click2_bar.wav"),1); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click2_beat.wav"), 0, false); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click2_bar.wav"), 1, false); + break; + + case 3: + if (clickStoredPath[0] != "") + clickLoadSample(clickStoredPath[0], 0, true); + else + clickClearSlot(0); + if (clickStoredPath[1] != "") + clickLoadSample(clickStoredPath[1], 1, true); + else clickClearSlot(1); break; } } @@ -5386,6 +5479,8 @@ struct SickoLooper3Widget : ModuleWidget { }, [=](bool internalClockAlwaysOn) { module->setInternalClock(internalClockAlwaysOn); })); + + /* menu->addChild(createSubmenuItem("Click Settings", "", [=](Menu * menu) { menu->addChild(createSubmenuItem("Click Presets", "", [=](Menu * menu) { menu->addChild(createMenuItem("Standard", "", [=]() {module->setClick(0);})); @@ -5403,7 +5498,38 @@ struct SickoLooper3Widget : ModuleWidget { menu->addChild(createMenuItem("File: " + module->clickFileDescription[1], "", [=]() {module->clickMenuLoadSample(1);})); menu->addChild(createMenuItem("", "Clear", [=]() {module->clickClearSlot(1);})); })); - + */ + + struct ClickItem : MenuItem { + SickoLooper3* module; + int clickSelect; + void onAction(const event::Action& e) override { + module->clickSelect = clickSelect; + module->setClick(clickSelect); + } + }; + + menu->addChild(createSubmenuItem("Click Settings", "", [=](Menu * menu) { + + std::string clickNames[4] = {"Standard", "Click1", "Click2", "Custom"}; + for (int i = 0; i < 4; i++) { + ClickItem* clickItem = createMenuItem(clickNames[i]); + clickItem->rightText = CHECKMARK(module->clickSelect == i); + clickItem->module = module; + clickItem->clickSelect = i; + menu->addChild(clickItem); + } + + menu->addChild(new MenuSeparator()); + + menu->addChild(createMenuItem("Custom BEAT click", "", [=]() {module->clickMenuLoadSample(0);})); + menu->addChild(createMenuItem("File: " + module->clickFileDescription[0], "", [=]() {module->clickMenuLoadSample(0);})); + menu->addChild(createMenuItem("", "Clear", [=]() {module->clickClearSlot(0);})); + menu->addChild(new MenuSeparator()); + menu->addChild(createMenuItem("Custom BAR click", "", [=]() {module->clickMenuLoadSample(1);})); + menu->addChild(createMenuItem("File: " + module->clickFileDescription[1], "", [=]() {module->clickMenuLoadSample(1);})); + menu->addChild(createMenuItem("", "Clear", [=]() {module->clickClearSlot(1);})); + })); } }; diff --git a/src/SickoLooper5.cpp b/src/SickoLooper5.cpp index 55d8df3..72b2ef1 100644 --- a/src/SickoLooper5.cpp +++ b/src/SickoLooper5.cpp @@ -38,6 +38,9 @@ #define REC_PLAY_OVERDUB 1 #define REC_OVERDUB_PLAY 2 +#define CLICK_STANDARD 0 +#define CLICK_CUSTOM 3 + #include "plugin.hpp" #include "osdialog.h" //#define DR_WAV_IMPLEMENTATION @@ -347,6 +350,8 @@ struct SickoLooper5 : Module { const unsigned int minSamplesToLoad = 9; + int clickSelect = CLICK_STANDARD; + vector clickPlayBuffer[2]; vector clickTempBuffer; vector clickTempBuffer2; @@ -570,6 +575,7 @@ struct SickoLooper5 : Module { json_object_set_new(rootJ, "internalClockAlwaysOn", json_boolean(internalClockAlwaysOn)); json_object_set_new(rootJ, "ClickSlot1", json_string(clickStoredPath[0].c_str())); json_object_set_new(rootJ, "ClickSlot2", json_string(clickStoredPath[1].c_str())); + json_object_set_new(rootJ, "clickSelect", json_integer(clickSelect)); return rootJ; } @@ -652,6 +658,7 @@ struct SickoLooper5 : Module { setInternalClock(internalClockAlwaysOn); } + /* json_t *clickSlot1J = json_object_get(rootJ, "ClickSlot1"); if (clickSlot1J) { clickStoredPath[0] = json_string_value(clickSlot1J); @@ -662,6 +669,31 @@ struct SickoLooper5 : Module { clickStoredPath[1] = json_string_value(clickSlot2J); clickLoadSample(clickStoredPath[1], 1); } + */ + json_t *clickSlot1J = json_object_get(rootJ, "ClickSlot1"); + if (clickSlot1J) { + clickStoredPath[0] = json_string_value(clickSlot1J); + if (clickStoredPath[0] == "") + clickClearSlot(0); + else + clickLoadSample(clickStoredPath[0], 0, true); + } + json_t *clickSlot2J = json_object_get(rootJ, "ClickSlot2"); + if (clickSlot2J) { + clickStoredPath[1] = json_string_value(clickSlot2J); + if (clickStoredPath[1] == "") + clickClearSlot(1); + else + clickLoadSample(clickStoredPath[1], 1, true); + } + + json_t* clickSelectJ = json_object_get(rootJ, "clickSelect"); + if (clickSelectJ) { + clickSelect = json_integer_value(clickSelectJ); + if (clickSelect < 0 || clickSelect > 3) + clickSelect = CLICK_STANDARD; + setClick(clickSelect); + } } @@ -753,12 +785,15 @@ struct SickoLooper5 : Module { fastPulseTime = sampleRate / 50; slowPulseTime = sampleRate / 5; + /* for (int i = 0; i < 2; i++) { if (clickFileLoaded[i]) { clickPlay[i] = false; clickLoadSample(clickStoredPath[i],i); } } + */ + setClick(clickSelect); for (int track = 0; track < MAX_TRACKS; track++) { @@ -1173,6 +1208,7 @@ struct SickoLooper5 : Module { json_object_set_new(rootJ, "internalClockAlwaysOn", json_boolean(internalClockAlwaysOn)); json_object_set_new(rootJ, "clickSlot1", json_string(clickStoredPath[0].c_str())); json_object_set_new(rootJ, "clickSlot2", json_string(clickStoredPath[1].c_str())); + json_object_set_new(rootJ, "clickSelect", json_integer(clickSelect)); json_object_set_new(rootJ, "click", json_integer(int(params[CLICK_BUT_PARAM].getValue()))); json_object_set_new(rootJ, "clickVol", json_real(params[CLICKVOL_KNOB_PARAM].getValue())); json_object_set_new(rootJ, "clickToMaster", json_integer(int(params[CLICKTOMASTER_SWITCH].getValue()))); @@ -1241,6 +1277,7 @@ struct SickoLooper5 : Module { internalClockAlwaysOn = json_boolean_value(internalClockAlwaysOnJ); setInternalClock(internalClockAlwaysOn); } + /* json_t *clickSlot1J = json_object_get(rootJ, "clickSlot1"); if (clickSlot1J) { clickStoredPath[0] = json_string_value(clickSlot1J); @@ -1251,6 +1288,32 @@ struct SickoLooper5 : Module { clickStoredPath[1] = json_string_value(clickSlot2J); clickLoadSample(clickStoredPath[1], 1); } + */ + json_t *clickSlot1J = json_object_get(rootJ, "ClickSlot1"); + if (clickSlot1J) { + clickStoredPath[0] = json_string_value(clickSlot1J); + if (clickStoredPath[0] == "") + clickClearSlot(0); + else + clickLoadSample(clickStoredPath[0], 0, true); + } + json_t *clickSlot2J = json_object_get(rootJ, "ClickSlot2"); + if (clickSlot2J) { + clickStoredPath[1] = json_string_value(clickSlot2J); + if (clickStoredPath[1] == "") + clickClearSlot(1); + else + clickLoadSample(clickStoredPath[1], 1, true); + } + + json_t* clickSelectJ = json_object_get(rootJ, "clickSelect"); + if (clickSelectJ) { + clickSelect = json_integer_value(clickSelectJ); + if (clickSelect < 0 || clickSelect > 3) + clickSelect = CLICK_STANDARD; + setClick(clickSelect); + } + json_t *clickJ = json_object_get(rootJ, "click"); if (clickJ) { params[CLICK_BUT_PARAM].setValue(json_integer_value(clickJ)); @@ -1694,8 +1757,14 @@ struct SickoLooper5 : Module { char *path = osdialog_file(OSDIALOG_OPEN, NULL, NULL, filters); clickFileLoaded[slot] = false; if (path) { + /* clickLoadSample(path, slot); clickStoredPath[slot] = std::string(path); + */ + clickLoadSample(path, slot, true); + clickStoredPath[slot] = std::string(path); + if (clickSelect != CLICK_CUSTOM) + setClick(clickSelect); } else { clickFileLoaded[slot] = true; } @@ -1705,7 +1774,7 @@ struct SickoLooper5 : Module { free(path); } - void clickLoadSample(std::string path, int slot) { + void clickLoadSample(std::string path, int slot, bool customClick) { z1 = 0; z2 = 0; unsigned int c; @@ -1817,12 +1886,22 @@ struct SickoLooper5 : Module { clickTempBuffer2.clear(); char* pathDup = strdup(path.c_str()); + /* clickFileDescription[slot] = basename(pathDup); free(pathDup); clickStoredPath[slot] = path; clickFileLoaded[slot] = true; + */ + if (customClick) { + clickFileDescription[slot] = basename(pathDup); + + clickStoredPath[slot] = path; + + } + clickFileLoaded[slot] = true; + free(pathDup); } else { clickFileLoaded[slot] = false; @@ -1835,26 +1914,38 @@ struct SickoLooper5 : Module { void clickClearSlot(int slot) { clickStoredPath[slot] = ""; clickFileDescription[slot] = "--none--"; - clickFileLoaded[slot] = false; - clickPlayBuffer[slot].clear(); - clickTotalSampleC[slot] = 0; + if (clickSelect == CLICK_CUSTOM) { + clickFileLoaded[slot] = false; + clickPlayBuffer[slot].clear(); + clickTotalSampleC[slot] = 0; + } } void setClick(int clickNo) { switch (clickNo) { case 0: - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click0_beat.wav"),0); - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click0_bar.wav"),1); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click0_beat.wav"), 0, false); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click0_bar.wav"), 1, false); break; case 1: - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click1_beat.wav"),0); - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click1_bar.wav"),1); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click1_beat.wav"), 0, false); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click1_bar.wav"), 1, false); break; case 2: - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click2_beat.wav"),0); - clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click2_bar.wav"),1); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click2_beat.wav"), 0, false); + clickLoadSample(asset::plugin(pluginInstance, "res/clicks/click2_bar.wav"), 1, false); + break; + + case 3: + if (clickStoredPath[0] != "") + clickLoadSample(clickStoredPath[0], 0, true); + else + clickClearSlot(0); + if (clickStoredPath[1] != "") + clickLoadSample(clickStoredPath[1], 1, true); + else clickClearSlot(1); break; } } @@ -5894,6 +5985,7 @@ struct SickoLooper5Widget : ModuleWidget { }, [=](bool internalClockAlwaysOn) { module->setInternalClock(internalClockAlwaysOn); })); + /* menu->addChild(createSubmenuItem("Click Settings", "", [=](Menu * menu) { menu->addChild(createSubmenuItem("Click Presets", "", [=](Menu * menu) { menu->addChild(createMenuItem("Standard", "", [=]() {module->setClick(0);})); @@ -5911,7 +6003,37 @@ struct SickoLooper5Widget : ModuleWidget { menu->addChild(createMenuItem("File: " + module->clickFileDescription[1], "", [=]() {module->clickMenuLoadSample(1);})); menu->addChild(createMenuItem("", "Clear", [=]() {module->clickClearSlot(1);})); })); - + */ + struct ClickItem : MenuItem { + SickoLooper5* module; + int clickSelect; + void onAction(const event::Action& e) override { + module->clickSelect = clickSelect; + module->setClick(clickSelect); + } + }; + + menu->addChild(createSubmenuItem("Click Settings", "", [=](Menu * menu) { + + std::string clickNames[4] = {"Standard", "Click1", "Click2", "Custom"}; + for (int i = 0; i < 4; i++) { + ClickItem* clickItem = createMenuItem(clickNames[i]); + clickItem->rightText = CHECKMARK(module->clickSelect == i); + clickItem->module = module; + clickItem->clickSelect = i; + menu->addChild(clickItem); + } + + menu->addChild(new MenuSeparator()); + + menu->addChild(createMenuItem("Custom BEAT click", "", [=]() {module->clickMenuLoadSample(0);})); + menu->addChild(createMenuItem("File: " + module->clickFileDescription[0], "", [=]() {module->clickMenuLoadSample(0);})); + menu->addChild(createMenuItem("", "Clear", [=]() {module->clickClearSlot(0);})); + menu->addChild(new MenuSeparator()); + menu->addChild(createMenuItem("Custom BAR click", "", [=]() {module->clickMenuLoadSample(1);})); + menu->addChild(createMenuItem("File: " + module->clickFileDescription[1], "", [=]() {module->clickMenuLoadSample(1);})); + menu->addChild(createMenuItem("", "Clear", [=]() {module->clickClearSlot(1);})); + })); } };