BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

commit 4cda085e051af2672fc3df931450d46daedfa453
parent a9c2d02b82073eae6120de7805c93709438fe6d0
Author: Matt Demanett <matt@demanett.net>
Date:   Thu, 27 Jan 2022 23:26:20 -0500

PGMR: add option to save last triggered step to patch. #189

Diffstat:
MREADME-prerelease.md | 2++
Msrc/Pgmr.cpp | 47+++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Pgmr.hpp | 12+++++++++++-
3 files changed, 60 insertions(+), 1 deletion(-)

diff --git a/README-prerelease.md b/README-prerelease.md @@ -1160,6 +1160,8 @@ The current step can be selected many ways: The leftmost bottom output emits a trigger whenever the step changes. The outputs below each channel selector emit a trigger when that step is selected. +If context-menu option "Save last selected step to patch" is enabled, PGMR will remember the last selected step in the patch, and restore it on patch load. This is the last step selected by pressing a step button, or triggering a step -- any effect from CLOCK or SELECT always follows the current state of the patch as it runs. + Any PGMRX expanders must be positioned to the right of, and ajacent to, the base PGMR module, or the previous PGMRX in the chain. See <a href="#expanders">notes on expanders</a>. _Polyphony:_ <a href="#polyphony">polyphonic</a>, with polyphony defined by the CLOCK input. This can be set to the SELECT CV input on the context menu. diff --git a/src/Pgmr.cpp b/src/Pgmr.cpp @@ -2,6 +2,9 @@ #include "Pgmr.hpp" #define SELECT_TRIGGERS "SELECT_TRIGGERS" +#define SAVE_LAST_TRIGGERED_TO_PATCH "save_last_triggered_to_patch" +#define LAST_TRIGGERED_STEP "last_triggered_step" +#define LAST_TRIGGERED_ELEMENTS_COUNT "last_triggered_elements_count" void Pgmr::reset() { std::lock_guard<SpinLock> lock(_elementsLock); @@ -22,15 +25,49 @@ void Pgmr::sampleRateChange() { json_t* Pgmr::saveToJson(json_t* root) { root = OutputRangeAddressableSequenceModule::saveToJson(root); json_object_set_new(root, SELECT_TRIGGERS, json_boolean(_selectTriggers)); + json_object_set_new(root, SAVE_LAST_TRIGGERED_TO_PATCH, json_boolean(_saveLastTriggeredToPatch)); + if (_saveLastTriggeredToPatch) { + json_t* a = json_array(); + for (int c = 0; c < maxChannels; ++c) { + json_array_append_new(a, json_integer(_step[c])); + } + json_object_set_new(root, LAST_TRIGGERED_STEP, a); + json_object_set_new(root, LAST_TRIGGERED_ELEMENTS_COUNT, json_integer(_elements.size())); + } return root; } void Pgmr::loadFromJson(json_t* root) { OutputRangeAddressableSequenceModule::loadFromJson(root); + json_t* st = json_object_get(root, SELECT_TRIGGERS); if (st) { _selectTriggers = json_is_true(st); } + + json_t* sl = json_object_get(root, SAVE_LAST_TRIGGERED_TO_PATCH); + if (sl) { + _saveLastTriggeredToPatch = json_is_true(sl); + if (_saveLastTriggeredToPatch) { + json_t* a = json_object_get(root, LAST_TRIGGERED_STEP); + json_t* sz = json_object_get(root, LAST_TRIGGERED_ELEMENTS_COUNT); + if (a && json_array_size(a) == maxChannels && sz) { + _restoreLastTriggeredExpectedElementsN = json_integer_value(sz); + std::vector<int> restoreSteps(maxChannels); + for (int c = 0; c < maxChannels; ++c) { + json_t* s = json_array_get(a, c); + if (s) { + restoreSteps[c] = json_integer_value(s); + } + } + _restoreLastTriggered = new std::function<void()>([this, restoreSteps]() { + for (int c = 0; c < maxChannels; ++c) { + setStep(c, restoreSteps[c], _elements.size()); + } + }); + } + } + } } void Pgmr::modulate() { @@ -125,6 +162,14 @@ void Pgmr::processChannel(const ProcessArgs& args, int c) { } } +void Pgmr::elementsChanged() { + if (_restoreLastTriggered && (int)_elements.size() == _restoreLastTriggeredExpectedElementsN) { + (*_restoreLastTriggered)(); + delete _restoreLastTriggered; + _restoreLastTriggered = NULL; + } +} + struct PgmrWidget : AddressableSequenceBaseModuleWidget { static constexpr int hp = 15; @@ -238,6 +283,8 @@ struct PgmrWidget : AddressableSequenceBaseModuleWidget { so->addItem(OptionMenuItem("Trigger", [m]() { return m->_selectTriggers; }, [m]() { m->_selectTriggers = true; })); OptionsMenuItem::addToMenu(so, menu); + menu->addChild(new BoolOptionMenuItem("Save last selected step to patch", [m]() { return &m->_saveLastTriggeredToPatch; })); + OutputRangeOptionMenuItem::addOutputRangeOptionsToMenu(module, menu); } }; diff --git a/src/Pgmr.hpp b/src/Pgmr.hpp @@ -1,6 +1,7 @@ #pragma once #include <algorithm> +#include <functional> #include "Pgmr_shared.hpp" @@ -70,6 +71,9 @@ struct Pgmr : PgmrBase { bool _selectTriggers = false; int _lastSteps[maxChannels] {}; rack::dsp::PulseGenerator _allPulseGens[maxChannels]; + bool _saveLastTriggeredToPatch = false; + std::function<void()>* _restoreLastTriggered = NULL; + int _restoreLastTriggeredExpectedElementsN = 0; Pgmr() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); @@ -123,7 +127,12 @@ struct Pgmr : PgmrBase { registerBase(); setExpanderModelPredicate([](Model* m) { return m == modelPgmrX; }); - std::fill_n(_lastSteps, maxChannels, -1); + std::fill_n(_lastSteps, maxChannels, 0); + } + virtual ~Pgmr() { + if (_restoreLastTriggered) { + delete _restoreLastTriggered; + } } void reset() override; @@ -133,6 +142,7 @@ struct Pgmr : PgmrBase { void modulate() override; void processAlways(const ProcessArgs& args) override; void processChannel(const ProcessArgs& args, int c) override; + void elementsChanged() override; }; } // namespace bogaudio