From 0221b6dd13d89bd392f15c64bc4b899c8b3a8bb3 Mon Sep 17 00:00:00 2001 From: mebitek Date: Thu, 25 Jan 2024 22:33:40 +0100 Subject: [PATCH 01/33] issue #43 Moving steps in a sequence --- src/apps/sequencer/model/ModelUtils.h | 17 +++++++++++++---- src/apps/sequencer/ui/StepSelection.h | 17 +++++++++++++++++ .../sequencer/ui/pages/NoteSequenceEditPage.cpp | 2 ++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/apps/sequencer/model/ModelUtils.h b/src/apps/sequencer/model/ModelUtils.h index b1acdf4c..984a280b 100644 --- a/src/apps/sequencer/model/ModelUtils.h +++ b/src/apps/sequencer/model/ModelUtils.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace ModelUtils { @@ -73,12 +74,20 @@ static void shiftSteps(std::array &steps, const std::bitset &selecte if (selected[i]) indices[count++] = i; } if (direction == 1) { - for (int i = count - 2; i >= 0; --i) { - std::swap(steps[indices[i]], steps[indices[i + 1]]); + for (int i = count - 1; i >= 0; --i) { + int index = indices[i]+1; + if (index == N) { + index = 0; + } + std::swap(steps[indices[i]], steps[index]); } } else if (direction == -1) { - for (int i = 0; i < count - 1; ++i) { - std::swap(steps[indices[i]], steps[indices[i + 1]]); + for (int i = 0; i < count; ++i) { + int index = indices[i]-1; + if (index == -1) { + index = N-1; + } + std::swap(steps[indices[i]], steps[index]); } } } diff --git a/src/apps/sequencer/ui/StepSelection.h b/src/apps/sequencer/ui/StepSelection.h index 99dfa018..e66c0f97 100644 --- a/src/apps/sequencer/ui/StepSelection.h +++ b/src/apps/sequencer/ui/StepSelection.h @@ -109,6 +109,23 @@ class StepSelection { _first = 0; } + void shiftLeft() { + rotateL(_selected, 1); + } + + void shiftRight() { + rotateR(_selected, 1); + + } + + inline void rotateR(std::bitset& b, unsigned m) { + b = b << m | b >> (N-m); + } + + inline void rotateL(std::bitset& b, unsigned m) { + b = b >> m | b << (N-m); + } + void selectEqualSteps(int stepIndex) { _mode = Mode::Persist; for (int i = 0; i < int(_selected.size()); ++i) { diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index 802af0a5..9a244996 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -396,6 +396,7 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { if (key.isLeft()) { if (key.shiftModifier()) { sequence.shiftSteps(_stepSelection.selected(), -1); + _stepSelection.shiftLeft(); } else { track.setPatternFollowDisplay(false); _section = std::max(0, _section - 1); @@ -405,6 +406,7 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { if (key.isRight()) { if (key.shiftModifier()) { sequence.shiftSteps(_stepSelection.selected(), 1); + _stepSelection.shiftRight(); } else { track.setPatternFollowDisplay(false); _section = std::min(3, _section + 1); From 58fe56278b665f01e51c3f2ddc2f5c1534031460 Mon Sep 17 00:00:00 2001 From: mebitek Date: Fri, 26 Jan 2024 08:39:35 +0100 Subject: [PATCH 02/33] issue #45 INIT by step selected --- src/apps/sequencer/model/NoteSequence.cpp | 12 ++++++++++++ src/apps/sequencer/model/NoteSequence.h | 1 + src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/apps/sequencer/model/NoteSequence.cpp b/src/apps/sequencer/model/NoteSequence.cpp index 1c5f6bf5..509aae10 100644 --- a/src/apps/sequencer/model/NoteSequence.cpp +++ b/src/apps/sequencer/model/NoteSequence.cpp @@ -256,6 +256,18 @@ void NoteSequence::clearSteps() { } } +void NoteSequence::clearStepsSelected(const std::bitset &selected) { + if (selected.any()) { + for (size_t i = 0; i < CONFIG_STEP_COUNT; ++i) { + if (selected[i]) { + _steps[i].clear(); + } + } + } else { + clearSteps(); + } +} + bool NoteSequence::isEdited() const { auto clearStep = Step(); for (const auto &step : _steps) { diff --git a/src/apps/sequencer/model/NoteSequence.h b/src/apps/sequencer/model/NoteSequence.h index 0e2e0c88..f3688b9d 100644 --- a/src/apps/sequencer/model/NoteSequence.h +++ b/src/apps/sequencer/model/NoteSequence.h @@ -495,6 +495,7 @@ class NoteSequence { void clear(); void clearSteps(); + void clearStepsSelected(const std::bitset &selected); bool isEdited() const; diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index 9a244996..7e721c07 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -924,7 +924,7 @@ bool NoteSequenceEditPage::contextActionEnabled(int index) const { } void NoteSequenceEditPage::initSequence() { - _project.selectedNoteSequence().clearSteps(); + _project.selectedNoteSequence().clearStepsSelected(_stepSelection.selected()); showMessage("STEPS INITIALIZED"); } From 531e31352bca64c19c6cf96cccb866ce431c6599 Mon Sep 17 00:00:00 2001 From: mebitek Date: Fri, 26 Jan 2024 08:40:49 +0100 Subject: [PATCH 03/33] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 836cf325..ebf34a41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +# v0.1.4.48 () +- Moving steps in a sequence +- INIT by step selected + # v0.1.4.47 (24 January 2024) - launchpad circuit mode improvements - random generator: random seed just on init method From 2447a70af1f4e7a81c5164ab2853968f7e0714ca Mon Sep 17 00:00:00 2001 From: mebitek Date: Fri, 26 Jan 2024 08:43:17 +0100 Subject: [PATCH 04/33] update to version 0.1.48 --- src/apps/sequencer/Config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/sequencer/Config.h b/src/apps/sequencer/Config.h index b1f4551d..97507284 100644 --- a/src/apps/sequencer/Config.h +++ b/src/apps/sequencer/Config.h @@ -7,7 +7,7 @@ #define CONFIG_VERSION_NAME "PER|FORMER SEQUENCER" #define CONFIG_VERSION_MAJOR 0 #define CONFIG_VERSION_MINOR 1 -#define CONFIG_VERSION_REVISION 47 +#define CONFIG_VERSION_REVISION 48 // Task priorities #define CONFIG_DRIVER_TASK_PRIORITY 5 From 0a424c851dd918d378b8dd8ca31d61920387cefc Mon Sep 17 00:00:00 2001 From: mebitek Date: Fri, 26 Jan 2024 10:26:22 +0100 Subject: [PATCH 05/33] issue #41 Cycle throgh Follow Modes --- src/apps/sequencer/engine/Engine.cpp | 14 +++++++++++ src/apps/sequencer/engine/Engine.h | 7 +++++- .../sequencer/model/BaseTrackPatternFollow.h | 23 +++++++++++------- src/apps/sequencer/ui/ControllerManager.cpp | 21 ---------------- src/apps/sequencer/ui/ControllerManager.h | 24 +++++++++++++++++++ .../ui/pages/NoteSequenceEditPage.cpp | 5 +++- 6 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/apps/sequencer/engine/Engine.cpp b/src/apps/sequencer/engine/Engine.cpp index 6ffba0dc..1a1509ae 100644 --- a/src/apps/sequencer/engine/Engine.cpp +++ b/src/apps/sequencer/engine/Engine.cpp @@ -5,6 +5,7 @@ #include "core/Debug.h" #include "core/midi/MidiMessage.h" +#include "ui/ControllerManager.h" #include "os/os.h" @@ -663,17 +664,30 @@ void Engine::updateOverrides() { } void Engine::usbMidiConnect(uint16_t vendorId, uint16_t productId) { + if (_usbMidiConnectHandler) { _usbMidiConnectHandler(vendorId, productId); + + auto lp = ControllerManager::findController(vendorId, productId); + + if (lp) { + _deviceConnected = true; + } } } void Engine::usbMidiDisconnect() { if (_usbMidiDisconnectHandler) { _usbMidiDisconnectHandler(); + _deviceConnected = false; } } +bool Engine::isLaunchpadConnected() { + return _deviceConnected; + +} + void Engine::receiveMidi() { // reset MIDI monitoring if monitoring config has changed if (_midiMonitoring.inputChanged(_project)) { diff --git a/src/apps/sequencer/engine/Engine.h b/src/apps/sequencer/engine/Engine.h index a4457410..8b98c578 100644 --- a/src/apps/sequencer/engine/Engine.h +++ b/src/apps/sequencer/engine/Engine.h @@ -160,6 +160,9 @@ class Engine : private Clock::Listener { Stats stats() const; + bool isLaunchpadConnected(); + + private: // Clock::Listener virtual void onClockOutput(const Clock::OutputState &state) override; @@ -173,7 +176,7 @@ class Engine : private Clock::Listener { void usbMidiConnect(uint16_t vendorId, uint16_t productId); void usbMidiDisconnect(); - + void receiveMidi(); void receiveMidi(MidiPort port, uint8_t cable, const MidiMessage &message); void monitorMidi(const MidiMessage &message); @@ -259,4 +262,6 @@ class Engine : private Clock::Listener { std::array _cvOutputOverrideValues; MessageHandler _messageHandler; + + bool _deviceConnected = false; }; diff --git a/src/apps/sequencer/model/BaseTrackPatternFollow.h b/src/apps/sequencer/model/BaseTrackPatternFollow.h index 22527357..03c189ce 100644 --- a/src/apps/sequencer/model/BaseTrackPatternFollow.h +++ b/src/apps/sequencer/model/BaseTrackPatternFollow.h @@ -58,16 +58,23 @@ class BaseTrackPatternFollow { } - void togglePatternFollowDisplay() { - const auto pattern_follow = patternFollow(); - - const bool disp_tracking = isPatternFollowDisplayOn(); + void togglePatternFollowDisplay(bool isLaunchpadConnected = false) { + auto pattern_follow = patternFollow(); + + if (isLaunchpadConnected) { + pattern_follow = static_cast((static_cast(pattern_follow) + 1) %4); + } else { + + if (pattern_follow == Types::PatternFollow::Off) { + pattern_follow = Types::PatternFollow::Display; + } else { + pattern_follow = Types::PatternFollow::Off; + } + + } - const bool lp_tracking = - (pattern_follow == Types::PatternFollow::LaunchPad || - pattern_follow == Types::PatternFollow::DispAndLP); - setPatternFollow(not disp_tracking, lp_tracking); + setPatternFollow(pattern_follow); } void editPatternFollow(int value, bool shift) { diff --git a/src/apps/sequencer/ui/ControllerManager.cpp b/src/apps/sequencer/ui/ControllerManager.cpp index 0d4a9abc..0a6feeef 100644 --- a/src/apps/sequencer/ui/ControllerManager.cpp +++ b/src/apps/sequencer/ui/ControllerManager.cpp @@ -1,26 +1,5 @@ #include "ControllerManager.h" -static const ControllerInfo controllerInfos[] = { - { 0x1235, 0x0020, ControllerInfo::Type::Launchpad }, // Novation Launchpad S - { 0x1235, 0x0036, ControllerInfo::Type::Launchpad }, // Novation Launchpad Mini Mk1 - { 0x1235, 0x0037, ControllerInfo::Type::Launchpad }, // Novation Launchpad Mini Mk2 - { 0x1235, 0x0069, ControllerInfo::Type::Launchpad }, // Novation Launchpad Mk2 - { 0x1235, 0x0051, ControllerInfo::Type::Launchpad }, // Novation Launchpad Pro - { 0x1235, 0x0113, ControllerInfo::Type::Launchpad }, // Novation Launchpad Mini Mk3 - { 0x1235, 0x0103, ControllerInfo::Type::Launchpad }, // Novation Launchpad X - { 0x1235, 0x0123, ControllerInfo::Type::Launchpad }, // Novation Launchpad Pro Mk3 -}; - -static const ControllerInfo *findController(uint16_t vendorId, uint16_t productId) { - for (size_t i = 0; i < sizeof(controllerInfos) / sizeof(controllerInfos[0]); ++i) { - auto info = &controllerInfos[i]; - if (info->vendorId == vendorId && info->productId == productId) { - return info; - } - } - return nullptr; -} - ControllerManager::ControllerManager(Model &model, Engine &engine) : _model(model), diff --git a/src/apps/sequencer/ui/ControllerManager.h b/src/apps/sequencer/ui/ControllerManager.h index d54ea039..270122ba 100644 --- a/src/apps/sequencer/ui/ControllerManager.h +++ b/src/apps/sequencer/ui/ControllerManager.h @@ -11,7 +11,21 @@ #include "core/midi/MidiMessage.h" #include "core/utils/Container.h" + static const ControllerInfo controllerInfos[] = { + { 0x1235, 0x0020, ControllerInfo::Type::Launchpad }, // Novation Launchpad S + { 0x1235, 0x0036, ControllerInfo::Type::Launchpad }, // Novation Launchpad Mini Mk1 + { 0x1235, 0x0037, ControllerInfo::Type::Launchpad }, // Novation Launchpad Mini Mk2 + { 0x1235, 0x0069, ControllerInfo::Type::Launchpad }, // Novation Launchpad Mk2 + { 0x1235, 0x0051, ControllerInfo::Type::Launchpad }, // Novation Launchpad Pro + { 0x1235, 0x0113, ControllerInfo::Type::Launchpad }, // Novation Launchpad Mini Mk3 + { 0x1235, 0x0103, ControllerInfo::Type::Launchpad }, // Novation Launchpad X + { 0x1235, 0x0123, ControllerInfo::Type::Launchpad }, // Novation Launchpad Pro Mk3 + }; + class ControllerManager { + + + public: ControllerManager(Model &model, Engine &engine); @@ -26,6 +40,16 @@ class ControllerManager { bool recvMidi(MidiPort port, uint8_t cable, const MidiMessage &message); + static const ControllerInfo *findController(uint16_t vendorId, uint16_t productId) { + for (size_t i = 0; i < sizeof(controllerInfos) / sizeof(controllerInfos[0]); ++i) { + auto info = &controllerInfos[i]; + if (info->vendorId == vendorId && info->productId == productId) { + return info; + } + } + return nullptr; + } + private: bool sendMidi(uint8_t cable, const MidiMessage &message); diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index 7e721c07..f53e46bc 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -12,6 +12,7 @@ #include "os/os.h" #include "core/utils/StringBuilder.h" +#include enum class ContextAction { Init, @@ -334,7 +335,9 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { if (key.isQuickEdit()) { if (key.is(Key::Step15)) { - track.togglePatternFollowDisplay(); + bool lpConnected = _engine.isLaunchpadConnected(); + + track.togglePatternFollowDisplay(lpConnected); } else { quickEdit(key.quickEdit()); } From d30070ed364533671af12a8dde130c734c0166e4 Mon Sep 17 00:00:00 2001 From: mebitek Date: Fri, 26 Jan 2024 10:27:05 +0100 Subject: [PATCH 06/33] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebf34a41..006b7ab6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ # v0.1.4.48 () - Moving steps in a sequence - INIT by step selected +- smart cycling on patter follow modes (check if launchpad is connected) # v0.1.4.47 (24 January 2024) - launchpad circuit mode improvements From c540e24109b07a1256b3cafbda3b01e842420b41 Mon Sep 17 00:00:00 2001 From: mebitek Date: Fri, 26 Jan 2024 10:56:59 +0100 Subject: [PATCH 07/33] issue #46 Show lp settings when lp is connected --- src/apps/sequencer/model/Settings.h | 5 +++ src/apps/sequencer/model/UserSettings.h | 15 +++++++-- .../launchpad/LaunchpadController.cpp | 4 +-- .../sequencer/ui/model/SettingsListModel.h | 32 +++++++++++++++++++ src/apps/sequencer/ui/pages/SystemPage.cpp | 9 ++++-- src/apps/sequencer/ui/pages/SystemPage.h | 2 ++ 6 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/apps/sequencer/model/Settings.h b/src/apps/sequencer/model/Settings.h index 50a90d42..825b7aea 100644 --- a/src/apps/sequencer/model/Settings.h +++ b/src/apps/sequencer/model/Settings.h @@ -18,6 +18,10 @@ class Settings { const UserSettings &userSettings() const { return _userSettings; } UserSettings &userSettings() { return _userSettings; } + + const LaunchpadSettings &launchpadSettings() const { return _launchpadSettings; } + LaunchpadSettings &launchpadSettings() { return _launchpadSettings; } + void clear(); void write(VersionedSerializedWriter &writer) const; @@ -29,4 +33,5 @@ class Settings { private: Calibration _calibration; UserSettings _userSettings; + LaunchpadSettings _launchpadSettings; }; diff --git a/src/apps/sequencer/model/UserSettings.h b/src/apps/sequencer/model/UserSettings.h index 9c72fcfb..4c7aadec 100644 --- a/src/apps/sequencer/model/UserSettings.h +++ b/src/apps/sequencer/model/UserSettings.h @@ -198,9 +198,9 @@ class UserSettings { addSetting(new ScreensaverSetting()); addSetting(new WakeModeSetting()); addSetting(new DimSequenceSetting()); - addSetting(new LaunchpadStyleSetting()); + addSetting(new PatternChange()); - addSetting(new LaunchpadNoteStyle()); + addSetting(new SyncSong()); } @@ -219,7 +219,7 @@ class UserSettings { void write(VersionedSerializedWriter &writer) const; void read(VersionedSerializedReader &reader); -private: +protected: std::vector _settings; template @@ -227,4 +227,13 @@ class UserSettings { _settings.push_back(setting); } BaseSetting *_get(const std::string &key); + +}; + +class LaunchpadSettings : public UserSettings{ + public: + LaunchpadSettings() { + addSetting(new LaunchpadStyleSetting()); + addSetting(new LaunchpadNoteStyle()); + } }; diff --git a/src/apps/sequencer/ui/controllers/launchpad/LaunchpadController.cpp b/src/apps/sequencer/ui/controllers/launchpad/LaunchpadController.cpp index a73dbcba..7f5ab5b2 100644 --- a/src/apps/sequencer/ui/controllers/launchpad/LaunchpadController.cpp +++ b/src/apps/sequencer/ui/controllers/launchpad/LaunchpadController.cpp @@ -106,7 +106,7 @@ static const RangeMap *curveSequenceLayerRangeMap[] = { [int(CurveSequence::Layer::GateProbability)] = nullptr, }; -UserSettings _userSettings; +LaunchpadSettings _userSettings; int _style = 0; int _patternChangeDefault = 0; int _noteStyle = 0; @@ -157,7 +157,7 @@ LaunchpadController::LaunchpadController(ControllerManager &manager, Model &mode setMode(Mode::Sequence); - _userSettings = model.settings().userSettings(); + _userSettings = model.settings().launchpadSettings(); } LaunchpadController::~LaunchpadController() { diff --git a/src/apps/sequencer/ui/model/SettingsListModel.h b/src/apps/sequencer/ui/model/SettingsListModel.h index f9177855..56f32a60 100644 --- a/src/apps/sequencer/ui/model/SettingsListModel.h +++ b/src/apps/sequencer/ui/model/SettingsListModel.h @@ -37,3 +37,35 @@ class SettingsListModel : public ListModel { private: UserSettings &_userSettings; }; + +class LpSettingsListModel : public ListModel { +public: + LpSettingsListModel(LaunchpadSettings &userSettings) : + _launchpadSettings(userSettings) + {} + + int rows() const override { + return _launchpadSettings.all().size(); + } + + int columns() const override { + return 2; + } + + void cell(int row, int column, StringBuilder &str) const override { + if (column == 0) { + str("%s", _launchpadSettings.get(row)->getMenuItem().c_str()); + } else if (column == 1) { + str("%s", _launchpadSettings.get(row)->getMenuItemKey().c_str()); + } + } + + void edit(int row, int column, int value, bool shift) override { + if (column == 1) { + _launchpadSettings.shift(row, value); + } + } + +private: + LaunchpadSettings &_launchpadSettings; +}; diff --git a/src/apps/sequencer/ui/pages/SystemPage.cpp b/src/apps/sequencer/ui/pages/SystemPage.cpp index 3b13b599..f8ce6f0a 100644 --- a/src/apps/sequencer/ui/pages/SystemPage.cpp +++ b/src/apps/sequencer/ui/pages/SystemPage.cpp @@ -42,7 +42,8 @@ static const ContextMenuModel::Item contextMenuItems[] = { SystemPage::SystemPage(PageManager &manager, PageContext &context) : ListPage(manager, context, _cvOutputListModel), _settings(context.model.settings()), - _settingsListModel(_settings.userSettings()) + _settingsListModel(_settings.userSettings()), + _lpSettingsListModel(_settings.launchpadSettings()) { setOutputIndex(0); } @@ -243,7 +244,11 @@ void SystemPage::setMode(Mode mode) { setListModel(_utilitiesListModel); break; case Mode::Settings: - setListModel(_settingsListModel); + if (_engine.isLaunchpadConnected()) { + setListModel(_lpSettingsListModel); + } else { + setListModel(_settingsListModel); + } break; default: break; diff --git a/src/apps/sequencer/ui/pages/SystemPage.h b/src/apps/sequencer/ui/pages/SystemPage.h index b80c0205..fc046b38 100644 --- a/src/apps/sequencer/ui/pages/SystemPage.h +++ b/src/apps/sequencer/ui/pages/SystemPage.h @@ -59,5 +59,7 @@ class SystemPage : public ListPage { UtilitiesListModel _utilitiesListModel; SettingsListModel _settingsListModel; + LpSettingsListModel _lpSettingsListModel; + uint32_t _encoderDownTicks; }; From 90cb0f27261cdd448301da8cf61106b53eed84f7 Mon Sep 17 00:00:00 2001 From: mebitek Date: Fri, 26 Jan 2024 10:57:43 +0100 Subject: [PATCH 08/33] issue #46 Show lp settings when lp is connected --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 006b7ab6..13b4f171 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Moving steps in a sequence - INIT by step selected - smart cycling on patter follow modes (check if launchpad is connected) +- Show launchpad settings only when a launchpad is connected # v0.1.4.47 (24 January 2024) - launchpad circuit mode improvements From af5b560b2e6a8afd63d1f2c2b48448e58deccd05 Mon Sep 17 00:00:00 2001 From: mebitek Date: Fri, 26 Jan 2024 16:17:08 +0100 Subject: [PATCH 09/33] issue #8 Apply random for selected steps only --- .../sequencer/engine/generators/Generator.cpp | 4 +- .../sequencer/engine/generators/Generator.h | 2 +- .../engine/generators/RandomGenerator.cpp | 32 ++++--- .../engine/generators/RandomGenerator.h | 4 +- src/apps/sequencer/ui/StepSelection.h | 5 ++ .../ui/pages/CurveSequenceEditPage.cpp | 7 +- src/apps/sequencer/ui/pages/GeneratorPage.cpp | 89 ++++++++++--------- src/apps/sequencer/ui/pages/GeneratorPage.h | 12 ++- .../ui/pages/NoteSequenceEditPage.cpp | 10 ++- 9 files changed, 106 insertions(+), 59 deletions(-) diff --git a/src/apps/sequencer/engine/generators/Generator.cpp b/src/apps/sequencer/engine/generators/Generator.cpp index 7947c77c..ca1b5339 100644 --- a/src/apps/sequencer/engine/generators/Generator.cpp +++ b/src/apps/sequencer/engine/generators/Generator.cpp @@ -13,7 +13,7 @@ static void initLayer(SequenceBuilder &builder) { builder.clearLayer(); } -Generator *Generator::execute(Generator::Mode mode, SequenceBuilder &builder) { +Generator *Generator::execute(Generator::Mode mode, SequenceBuilder &builder, std::bitset &selected) { switch (mode) { case Mode::InitLayer: initLayer(builder); @@ -21,7 +21,7 @@ Generator *Generator::execute(Generator::Mode mode, SequenceBuilder &builder) { case Mode::Euclidean: return generatorContainer.create(builder, euclideanParams); case Mode::Random: - return generatorContainer.create(builder, randomParams); + return generatorContainer.create(builder, randomParams, selected); case Mode::Last: break; } diff --git a/src/apps/sequencer/engine/generators/Generator.h b/src/apps/sequencer/engine/generators/Generator.h index 00e80534..3a20485d 100644 --- a/src/apps/sequencer/engine/generators/Generator.h +++ b/src/apps/sequencer/engine/generators/Generator.h @@ -51,7 +51,7 @@ class Generator { virtual void update() = 0; - static Generator *execute(Generator::Mode mode, SequenceBuilder &builder); + static Generator *execute(Generator::Mode mode, SequenceBuilder &builder, std::bitset &selected); protected: SequenceBuilder &_builder; diff --git a/src/apps/sequencer/engine/generators/RandomGenerator.cpp b/src/apps/sequencer/engine/generators/RandomGenerator.cpp index 884382b4..0f8807cd 100644 --- a/src/apps/sequencer/engine/generators/RandomGenerator.cpp +++ b/src/apps/sequencer/engine/generators/RandomGenerator.cpp @@ -1,12 +1,14 @@ #include "RandomGenerator.h" #include "core/utils/Random.h" +#include +#include #include - -RandomGenerator::RandomGenerator(SequenceBuilder &builder, Params ¶ms) : +RandomGenerator::RandomGenerator(SequenceBuilder &builder, Params ¶ms, std::bitset &selected) : Generator(builder), - _params(params) + _params(params), + _selected(selected) { update(); } @@ -59,12 +61,18 @@ void RandomGenerator::update() { int size = _pattern.size(); for (int i = 0; i < size; ++i) { - _pattern[i] = rng.nextRange(255); + if (_selected[i]) { + _pattern[i] = rng.nextRange(255); + } else { + _pattern[i] = 0; + } } for (int iteration = 0; iteration < _params.smooth; ++iteration) { for (int i = 0; i < size; ++i) { - _pattern[i] = (4 * _pattern[i] + _pattern[(i - 1 + size) % size] + _pattern[(i + 1) % size] + 3) / 6; + if (_selected[i]) { + _pattern[i] = (4 * _pattern[i] + _pattern[(i - 1 + size) % size] + _pattern[(i + 1) % size] + 3) / 6; + } } } @@ -72,13 +80,17 @@ void RandomGenerator::update() { int scale = _params.scale; for (int i = 0; i < size; ++i) { - int value = _pattern[i]; - // value = ((value - 127) * scale) / 10 + 127 + bias; - value = ((value + bias - 127) * scale) / 10 + 127; - _pattern[i] = clamp(value, 0, 255); + if (_selected[i]) { + int value = _pattern[i]; + // value = ((value - 127) * scale) / 10 + 127 + bias; + value = ((value + bias - 127) * scale) / 10 + 127; + _pattern[i] = clamp(value, 0, 255); + } } for (size_t i = 0; i < _pattern.size(); ++i) { - _builder.setValue(i, _pattern[i] * (1.f / 255.f)); + if (_selected[i]) { + _builder.setValue(i, _pattern[i] * (1.f / 255.f)); + } } } diff --git a/src/apps/sequencer/engine/generators/RandomGenerator.h b/src/apps/sequencer/engine/generators/RandomGenerator.h index 0cd021a3..e66c1263 100644 --- a/src/apps/sequencer/engine/generators/RandomGenerator.h +++ b/src/apps/sequencer/engine/generators/RandomGenerator.h @@ -5,6 +5,7 @@ #include "Generator.h" #include "core/math/Math.h" +#include class RandomGenerator : public Generator { public: @@ -23,7 +24,7 @@ class RandomGenerator : public Generator { uint8_t scale = 5; }; - RandomGenerator(SequenceBuilder &builder, Params ¶ms); + RandomGenerator(SequenceBuilder &builder, Params ¶ms, std::bitset &selected); Mode mode() const override { return Mode::Random; } @@ -64,4 +65,5 @@ class RandomGenerator : public Generator { private: Params &_params; GeneratorPattern _pattern; + std::bitset &_selected; }; diff --git a/src/apps/sequencer/ui/StepSelection.h b/src/apps/sequencer/ui/StepSelection.h index e66c0f97..8cbd81c2 100644 --- a/src/apps/sequencer/ui/StepSelection.h +++ b/src/apps/sequencer/ui/StepSelection.h @@ -147,6 +147,10 @@ class StepSelection { return _first; } + int at(int index) const { + return _selected[index]; + } + int firstSetIndex() const { for (size_t i = 0; i < _selected.size(); ++i) { if (_selected[i]) { @@ -183,6 +187,7 @@ class StepSelection { } const std::bitset &selected() const { return _selected; } + std::bitset &selected() { return _selected; } bool operator[](int index) const { return _selected[index]; diff --git a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp index fa7d8582..57a84b55 100644 --- a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp @@ -735,9 +735,12 @@ void CurveSequenceEditPage::generateSequence() { _manager.pages().generatorSelect.show([this] (bool success, Generator::Mode mode) { if (success) { auto builder = _builderContainer.create(_project.selectedCurveSequence(), layer()); - auto generator = Generator::execute(mode, *builder); + if (_stepSelection.none()) { + _stepSelection.selectAll(); + } + auto generator = Generator::execute(mode, *builder, _stepSelection.selected()); if (generator) { - _manager.pages().generator.show(generator); + _manager.pages().generator.show(generator, &_stepSelection); } } }); diff --git a/src/apps/sequencer/ui/pages/GeneratorPage.cpp b/src/apps/sequencer/ui/pages/GeneratorPage.cpp index a706195b..67542184 100644 --- a/src/apps/sequencer/ui/pages/GeneratorPage.cpp +++ b/src/apps/sequencer/ui/pages/GeneratorPage.cpp @@ -1,6 +1,7 @@ #include "GeneratorPage.h" #include "ui/painters/WindowPainter.h" +#include "ui/LedPainter.h" #include "engine/generators/Generator.h" #include "engine/generators/EuclideanGenerator.h" @@ -27,8 +28,9 @@ GeneratorPage::GeneratorPage(PageManager &manager, PageContext &context) : BasePage(manager, context) {} -void GeneratorPage::show(Generator *generator) { +void GeneratorPage::show(Generator *generator, StepSelection *stepSelection) { _generator = generator; + _stepSelection = stepSelection; BasePage::show(); } @@ -86,44 +88,38 @@ void GeneratorPage::draw(Canvas &canvas) { void GeneratorPage::updateLeds(Leds &leds) { // value range - for (int i = 0; i < 8; ++i) { + /*for (int i = 0; i < 8; ++i) { bool inRange = (i >= _valueRange.first && i <= _valueRange.second) || (i >= _valueRange.second && i <= _valueRange.first); bool inverted = _valueRange.first > _valueRange.second; leds.set(MatrixMap::toStep(i), inRange && inverted, inRange && !inverted); } for (int i = 0; i < 7; ++i) { leds.set(MatrixMap::toStep(8 + i), false, false); - } -} - -void GeneratorPage::keyDown(KeyEvent &event) { - const auto &key = event.key(); + }*/ - if (key.isGlobal()) { - return; - } + const auto &trackEngine = _engine.selectedTrackEngine().as(); + const auto &sequence = _project.selectedNoteSequence(); + int currentStep = trackEngine.isActiveSequence(sequence) ? trackEngine.currentStep() : -1; - if (key.isStep()) { + for (int i = 0; i < 16; ++i) { + int stepIndex = stepOffset() + i; + bool red = (stepIndex == currentStep) || _stepSelection->at(stepIndex); + bool green = (stepIndex != currentStep) && (sequence.step(stepIndex).gate() || _stepSelection->at(stepIndex)); + leds.set(MatrixMap::fromStep(i), red, green); } +} - event.consume(); +void GeneratorPage::keyDown(KeyEvent &event) { + _stepSelection->keyDown(event, stepOffset()); } void GeneratorPage::keyUp(KeyEvent &event) { - const auto &key = event.key(); - - if (key.isGlobal()) { - return; - } - - if (key.isStep()) { - } - - event.consume(); + _stepSelection->keyUp(event, stepOffset()); } void GeneratorPage::keyPress(KeyPressEvent &event) { const auto &key = event.key(); + auto &track = _project.selectedTrack().noteTrack(); if (key.isContextMenu()) { contextShow(); @@ -135,27 +131,37 @@ void GeneratorPage::keyPress(KeyPressEvent &event) { return; } - if (key.isStep()) { - int secondStep = key.step(); - if (secondStep < 8) { - int count = 0; - int firstStep = -1; - for (int step = 0; step < 8; ++step) { - if (key.state()[MatrixMap::fromStep(step)]) { - ++count; - if (step != secondStep) { - firstStep = step; - } - } - } - if (count == 2) { - _valueRange.first = firstStep; - _valueRange.second = secondStep; - DBG("range %d %d", firstStep, secondStep); - } + if (key.isStep() && key.shiftModifier()) { + _generator->update(); + event.consume(); + return; + } + + if (key.isShift() && event.count() == 2) { + if (_stepSelection->none()) { + _stepSelection->selectAll(); + _generator->update(); + } else { + _stepSelection->clear(); + _generator->update(); + _generator->revert(); } + + event.consume(); + return; + } + if (key.isLeft()) { + track.setPatternFollowDisplay(false); + _section = std::max(0, _section - 1); + event.consume(); + } + if (key.isRight()) { + track.setPatternFollowDisplay(false); + _section = std::min(3, _section + 1); + event.consume(); } + event.consume(); } @@ -249,14 +255,17 @@ bool GeneratorPage::contextActionEnabled(int index) const { } void GeneratorPage::init() { + _stepSelection->clear(); _generator->init(); } void GeneratorPage::revert() { + _stepSelection->clear(); _generator->revert(); close(); } void GeneratorPage::commit() { + _stepSelection->clear(); close(); } diff --git a/src/apps/sequencer/ui/pages/GeneratorPage.h b/src/apps/sequencer/ui/pages/GeneratorPage.h index c533d727..b21ea705 100644 --- a/src/apps/sequencer/ui/pages/GeneratorPage.h +++ b/src/apps/sequencer/ui/pages/GeneratorPage.h @@ -1,6 +1,8 @@ #pragma once #include "BasePage.h" +#include "ui/StepSelection.h" +#include "engine/generators/SequenceBuilder.h" class Generator; class EuclideanGenerator; @@ -11,7 +13,7 @@ class GeneratorPage : public BasePage { GeneratorPage(PageManager &manager, PageContext &context); using BasePage::show; - void show(Generator *generator); + void show(Generator *generator, StepSelection *_stepSelection); virtual void enter() override; virtual void exit() override; @@ -26,6 +28,10 @@ class GeneratorPage : public BasePage { virtual void keyPress(KeyPressEvent &event) override; virtual void encoder(EncoderEvent &event) override; + static const int StepCount = 16; + + int stepOffset() const { return _section * StepCount; } + void contextShow(); void contextAction(int index); bool contextActionEnabled(int index) const; @@ -40,4 +46,8 @@ class GeneratorPage : public BasePage { Generator *_generator; std::pair _valueRange; + StepSelection *_stepSelection; + int _section = 0; + + Container _builderContainer; }; diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index f53e46bc..f24cb0e7 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -12,6 +12,7 @@ #include "os/os.h" #include "core/utils/StringBuilder.h" +#include #include enum class ContextAction { @@ -980,9 +981,14 @@ void NoteSequenceEditPage::generateSequence() { _manager.pages().generatorSelect.show([this] (bool success, Generator::Mode mode) { if (success) { auto builder = _builderContainer.create(_project.selectedNoteSequence(), layer()); - auto generator = Generator::execute(mode, *builder); + + if (_stepSelection.none()) { + _stepSelection.selectAll(); + } + + auto generator = Generator::execute(mode, *builder, _stepSelection.selected()); if (generator) { - _manager.pages().generator.show(generator); + _manager.pages().generator.show(generator, &_stepSelection); } } }); From ecca9adf28461e8cbd290ca22002c4b33b7254cd Mon Sep 17 00:00:00 2001 From: mebitek Date: Fri, 26 Jan 2024 16:25:49 +0100 Subject: [PATCH 10/33] issue #8 Apply random for selected steps only --- src/apps/sequencer/ui/pages/GeneratorPage.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/apps/sequencer/ui/pages/GeneratorPage.cpp b/src/apps/sequencer/ui/pages/GeneratorPage.cpp index 67542184..7f2e17a2 100644 --- a/src/apps/sequencer/ui/pages/GeneratorPage.cpp +++ b/src/apps/sequencer/ui/pages/GeneratorPage.cpp @@ -213,7 +213,11 @@ void GeneratorPage::drawRandomGenerator(Canvas &canvas, const RandomGenerator &g for (int i = 0; i < steps; ++i) { int h = stepHeight - 2; int h2 = (h * pattern[i]) / 255; - canvas.setColor(Color::Low); + if ( i / 16 == _section ) { + canvas.setColor(Color::Medium); + } else { + canvas.setColor(Color::Low); + } canvas.drawRect(x + 1, y + 1, stepWidth - 2, h); canvas.setColor(Color::Bright); canvas.hline(x + 1, y + 1 + h - h2, stepWidth - 2); From 3f5e1eae5f5d49fcf74de736ae002cc4de168af2 Mon Sep 17 00:00:00 2001 From: mebitek Date: Fri, 26 Jan 2024 16:26:24 +0100 Subject: [PATCH 11/33] issue #8 Apply random for selected steps only --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13b4f171..270cab7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - INIT by step selected - smart cycling on patter follow modes (check if launchpad is connected) - Show launchpad settings only when a launchpad is connected +- Apply random for selected steps only # v0.1.4.47 (24 January 2024) - launchpad circuit mode improvements From 9db85b163ad22e6852e8fa34f0d6cda4afc6f620 Mon Sep 17 00:00:00 2001 From: mebitek Date: Fri, 26 Jan 2024 16:39:12 +0100 Subject: [PATCH 12/33] issue #8 Apply random for selected steps only --- src/apps/sequencer/ui/pages/GeneratorPage.cpp | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/apps/sequencer/ui/pages/GeneratorPage.cpp b/src/apps/sequencer/ui/pages/GeneratorPage.cpp index 7f2e17a2..f6f81f30 100644 --- a/src/apps/sequencer/ui/pages/GeneratorPage.cpp +++ b/src/apps/sequencer/ui/pages/GeneratorPage.cpp @@ -87,26 +87,38 @@ void GeneratorPage::draw(Canvas &canvas) { } void GeneratorPage::updateLeds(Leds &leds) { - // value range - /*for (int i = 0; i < 8; ++i) { - bool inRange = (i >= _valueRange.first && i <= _valueRange.second) || (i >= _valueRange.second && i <= _valueRange.first); - bool inverted = _valueRange.first > _valueRange.second; - leds.set(MatrixMap::toStep(i), inRange && inverted, inRange && !inverted); - } - for (int i = 0; i < 7; ++i) { - leds.set(MatrixMap::toStep(8 + i), false, false); - }*/ - - const auto &trackEngine = _engine.selectedTrackEngine().as(); - const auto &sequence = _project.selectedNoteSequence(); - int currentStep = trackEngine.isActiveSequence(sequence) ? trackEngine.currentStep() : -1; - - for (int i = 0; i < 16; ++i) { - int stepIndex = stepOffset() + i; - bool red = (stepIndex == currentStep) || _stepSelection->at(stepIndex); - bool green = (stepIndex != currentStep) && (sequence.step(stepIndex).gate() || _stepSelection->at(stepIndex)); - leds.set(MatrixMap::fromStep(i), red, green); + + int currentStep; + switch (_project.selectedTrack().trackMode()) { + case Track::TrackMode::Note: { + const auto &trackEngine = _engine.selectedTrackEngine().as(); + const auto &sequence = _project.selectedNoteSequence(); + currentStep = trackEngine.isActiveSequence(sequence) ? trackEngine.currentStep() : -1; + for (int i = 0; i < 16; ++i) { + int stepIndex = stepOffset() + i; + bool red = (stepIndex == currentStep) || _stepSelection->at(stepIndex); + bool green = (stepIndex != currentStep) && (sequence.step(stepIndex).gate() || _stepSelection->at(stepIndex)); + leds.set(MatrixMap::fromStep(i), red, green); + } + } + break; + case Track::TrackMode::Curve: { + const auto &trackEngine = _engine.selectedTrackEngine().as(); + const auto &sequence = _project.selectedCurveSequence(); + currentStep = trackEngine.isActiveSequence(sequence) ? trackEngine.currentStep() : -1; + for (int i = 0; i < 16; ++i) { + int stepIndex = stepOffset() + i; + bool red = (stepIndex == currentStep) || _stepSelection->at(stepIndex); + bool green = (stepIndex != currentStep) && (sequence.step(stepIndex).gate() || _stepSelection->at(stepIndex)); + leds.set(MatrixMap::fromStep(i), red, green); + } + } + break; + default: + return; } + + } void GeneratorPage::keyDown(KeyEvent &event) { @@ -119,7 +131,6 @@ void GeneratorPage::keyUp(KeyEvent &event) { void GeneratorPage::keyPress(KeyPressEvent &event) { const auto &key = event.key(); - auto &track = _project.selectedTrack().noteTrack(); if (key.isContextMenu()) { contextShow(); @@ -152,12 +163,10 @@ void GeneratorPage::keyPress(KeyPressEvent &event) { } if (key.isLeft()) { - track.setPatternFollowDisplay(false); _section = std::max(0, _section - 1); event.consume(); } if (key.isRight()) { - track.setPatternFollowDisplay(false); _section = std::min(3, _section + 1); event.consume(); } From 26d366905470834ca37585549385561abed4dcd1 Mon Sep 17 00:00:00 2001 From: mebitek Date: Fri, 26 Jan 2024 17:21:38 +0100 Subject: [PATCH 13/33] issue #18 (#314) Curve mode reverse mode should reverse playback --- src/apps/sequencer/engine/CurveTrackEngine.h | 4 ++++ src/apps/sequencer/engine/SequenceState.cpp | 18 ++++++++++++++++++ src/apps/sequencer/engine/SequenceState.h | 2 +- .../ui/pages/CurveSequenceEditPage.cpp | 15 +++++++++++++-- 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/apps/sequencer/engine/CurveTrackEngine.h b/src/apps/sequencer/engine/CurveTrackEngine.h index 99aaebda..1c102ab1 100644 --- a/src/apps/sequencer/engine/CurveTrackEngine.h +++ b/src/apps/sequencer/engine/CurveTrackEngine.h @@ -45,6 +45,10 @@ class CurveTrackEngine : public TrackEngine { void setMonitorStep(int index) { _monitorStepIndex = (index >= 0 && index < CONFIG_STEP_COUNT) ? index : -1; } void setMonitorStepLevel(MonitorLevel level) { _monitorStepLevel = level; } + SequenceState sequenceState() { + return _sequenceState; + } + private: void triggerStep(uint32_t tick, uint32_t divisor); void updateOutput(uint32_t relativeTick, uint32_t divisor); diff --git a/src/apps/sequencer/engine/SequenceState.cpp b/src/apps/sequencer/engine/SequenceState.cpp index 34269b01..61c2a3aa 100644 --- a/src/apps/sequencer/engine/SequenceState.cpp +++ b/src/apps/sequencer/engine/SequenceState.cpp @@ -120,6 +120,7 @@ void SequenceState::calculateNextStepFree(Types::RunMode runMode, int firstStep, } else { ++_nextStep; } + _direction = 1; break; case Types::RunMode::Backward: if (_step <= firstStep) { @@ -128,6 +129,7 @@ void SequenceState::calculateNextStepFree(Types::RunMode runMode, int firstStep, } else { --_nextStep; } + _direction = -1; break; case Types::RunMode::Pendulum: case Types::RunMode::PingPong: @@ -178,20 +180,34 @@ void SequenceState::calculateNextStepAligned(int absoluteStep, Types::RunMode ru case Types::RunMode::Forward: _nextStep = firstStep + absoluteStep % stepCount; _iteration = absoluteStep / stepCount; + _direction = 1; break; case Types::RunMode::Backward: _nextStep = lastStep - absoluteStep % stepCount; _iteration = absoluteStep / stepCount; + _direction = -1; break; case Types::RunMode::Pendulum: _iteration = absoluteStep / (2 * stepCount); absoluteStep %= (2 * stepCount); _nextStep = (absoluteStep < stepCount) ? (firstStep + absoluteStep) : (lastStep - (absoluteStep - stepCount)); + if (_direction > 0 && _step>= lastStep) { + _direction = -1; + } else if (_direction < 0 && _step <= firstStep) { + _direction = 1; + ++_nextIteration; + } break; case Types::RunMode::PingPong: _iteration = absoluteStep / (2 * stepCount - 2); absoluteStep %= (2 * stepCount - 2); _nextStep = (absoluteStep < stepCount) ? (firstStep + absoluteStep) : (lastStep - (absoluteStep - stepCount) - 1); + if (_direction > 0 && _step>= lastStep) { + _direction = -1; + } else if (_direction < 0 && _step <= firstStep) { + _direction = 1; + ++_nextIteration; + } break; case Types::RunMode::Random: _nextStep = firstStep + rng.nextRange(stepCount); @@ -211,8 +227,10 @@ void SequenceState::advanceRandomWalk(int firstStep, int lastStep, Random &rng) int dir = rng.nextRange(2); if (dir == 0) { _nextStep = _step == firstStep ? lastStep : _step - 1; + _direction = -1; } else { _nextStep = _step == lastStep ? firstStep : _step + 1; + _direction = 1; } } } diff --git a/src/apps/sequencer/engine/SequenceState.h b/src/apps/sequencer/engine/SequenceState.h index b9c800ed..5eb09c3e 100644 --- a/src/apps/sequencer/engine/SequenceState.h +++ b/src/apps/sequencer/engine/SequenceState.h @@ -11,7 +11,7 @@ class SequenceState { int step() const { return _step; } int prevStep() const { return _prevStep; } int nextStep() const { return _nextStep; } - int direction() const { return _direction; } + int direction() { return _direction; } uint32_t iteration() const { return _iteration; } diff --git a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp index 57a84b55..84f77cca 100644 --- a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp @@ -177,7 +177,7 @@ void CurveSequenceEditPage::draw(Canvas &canvas) { WindowPainter::drawActiveFunction(canvas, CurveSequence::layerName(layer())); WindowPainter::drawFooter(canvas, functionNames, pageKeyState(), activeFunctionKey()); - const auto &trackEngine = _engine.selectedTrackEngine().as(); + auto &trackEngine = _engine.selectedTrackEngine().as(); const auto &sequence = _project.selectedCurveSequence(); int currentStep = trackEngine.isActiveSequence(sequence) ? trackEngine.currentStep() : -1; @@ -314,7 +314,18 @@ void CurveSequenceEditPage::draw(Canvas &canvas) { // draw cursor if (isActiveSequence) { canvas.setColor(Color::Bright); - int x = ((trackEngine.currentStep() - stepOffset) + trackEngine.currentStepFraction()) * stepWidth; + + int x; + + + auto dir = trackEngine.sequenceState().direction(); + std::cerr << dir << "\n"; + if (dir == 1) { + x = ((trackEngine.currentStep() - stepOffset) + trackEngine.currentStepFraction()) * stepWidth; + } else if (dir == -1) { + x = ((trackEngine.currentStep() + stepOffset)- trackEngine.currentStepFraction()) * stepWidth; + } + canvas.vline(x, curveY, curveHeight); } From ce97b5a9da1ffe4c87f00ab9def77aa837b76543 Mon Sep 17 00:00:00 2001 From: mebitek Date: Sun, 28 Jan 2024 11:11:20 +0100 Subject: [PATCH 14/33] issue #47 shift and page: dobule click page to enter context menu --- src/apps/sequencer/engine/CurveTrackEngine.cpp | 2 +- src/apps/sequencer/ui/model/ContextMenuModel.h | 1 + src/apps/sequencer/ui/pages/ContextMenu.h | 10 ++++++++-- .../sequencer/ui/pages/ContextMenuPage.cpp | 18 +++++++++++++++++- .../ui/pages/CurveSequenceEditPage.cpp | 14 +++++++++----- .../sequencer/ui/pages/CurveSequenceEditPage.h | 2 +- .../sequencer/ui/pages/CurveSequencePage.cpp | 12 ++++++++++-- .../sequencer/ui/pages/CurveSequencePage.h | 2 +- src/apps/sequencer/ui/pages/GeneratorPage.cpp | 12 ++++++++++-- src/apps/sequencer/ui/pages/GeneratorPage.h | 2 +- .../ui/pages/NoteSequenceEditPage.cpp | 9 +++++++-- .../sequencer/ui/pages/NoteSequenceEditPage.h | 2 +- .../sequencer/ui/pages/NoteSequencePage.cpp | 12 ++++++++++-- src/apps/sequencer/ui/pages/NoteSequencePage.h | 2 +- src/apps/sequencer/ui/pages/PatternPage.cpp | 12 ++++++++++-- src/apps/sequencer/ui/pages/PatternPage.h | 2 +- src/apps/sequencer/ui/pages/ProjectPage.cpp | 11 +++++++++-- src/apps/sequencer/ui/pages/ProjectPage.h | 2 +- src/apps/sequencer/ui/pages/SongPage.cpp | 11 +++++++++-- src/apps/sequencer/ui/pages/SongPage.h | 2 +- src/apps/sequencer/ui/pages/SystemPage.cpp | 12 ++++++++++-- src/apps/sequencer/ui/pages/SystemPage.h | 2 +- src/apps/sequencer/ui/pages/TrackPage.cpp | 12 ++++++++++-- src/apps/sequencer/ui/pages/TrackPage.h | 2 +- src/apps/sequencer/ui/pages/UserScalePage.cpp | 12 ++++++++++-- src/apps/sequencer/ui/pages/UserScalePage.h | 2 +- 26 files changed, 142 insertions(+), 40 deletions(-) diff --git a/src/apps/sequencer/engine/CurveTrackEngine.cpp b/src/apps/sequencer/engine/CurveTrackEngine.cpp index 6d9d82b3..41efbde4 100644 --- a/src/apps/sequencer/engine/CurveTrackEngine.cpp +++ b/src/apps/sequencer/engine/CurveTrackEngine.cpp @@ -218,7 +218,7 @@ void CurveTrackEngine::updateOutput(uint32_t relativeTick, uint32_t divisor) { const auto &evalSequence = fillNextPattern ? *_fillSequence : *_sequence; const auto &step = evalSequence.step(_currentStep); - float value = evalStepShape(step, _shapeVariation || fillVariation, fillInvert, _currentStepFraction); + float value = evalStepShape(step, _shapeVariation || fillVariation, fillInvert || _sequenceState.direction() == -1, _currentStepFraction); value = range.denormalize(value); _cvOutputTarget = value; } diff --git a/src/apps/sequencer/ui/model/ContextMenuModel.h b/src/apps/sequencer/ui/model/ContextMenuModel.h index f1c9abff..1435f61d 100644 --- a/src/apps/sequencer/ui/model/ContextMenuModel.h +++ b/src/apps/sequencer/ui/model/ContextMenuModel.h @@ -13,4 +13,5 @@ class ContextMenuModel { virtual const Item &item(int index) const = 0; virtual bool itemEnabled(int index) const = 0; + virtual bool doubleClick() const = 0; }; diff --git a/src/apps/sequencer/ui/pages/ContextMenu.h b/src/apps/sequencer/ui/pages/ContextMenu.h index 2333fcb4..28602fcb 100644 --- a/src/apps/sequencer/ui/pages/ContextMenu.h +++ b/src/apps/sequencer/ui/pages/ContextMenu.h @@ -15,17 +15,22 @@ class ContextMenu : public ContextMenuModel { const Item items[], int itemCount, ActionCallback actionCallback, - ItemEnabledCallback itemEnabledCallback = [] (int) { return true; } + ItemEnabledCallback itemEnabledCallback = [] (int) { return true; }, bool doubleClick = false ) : _items(items), _itemCount(itemCount), _actionCallback(actionCallback), - _itemEnabledCallback(itemEnabledCallback) + _itemEnabledCallback(itemEnabledCallback), + _doubleClick(doubleClick) { } const ActionCallback &actionCallback() const { return _actionCallback; } + virtual bool doubleClick() const override{ + return _doubleClick; + } + private: virtual int itemCount() const override { return _itemCount; @@ -43,4 +48,5 @@ class ContextMenu : public ContextMenuModel { int _itemCount; ActionCallback _actionCallback; ItemEnabledCallback _itemEnabledCallback; + bool _doubleClick = false; }; diff --git a/src/apps/sequencer/ui/pages/ContextMenuPage.cpp b/src/apps/sequencer/ui/pages/ContextMenuPage.cpp index 098599ce..650df891 100644 --- a/src/apps/sequencer/ui/pages/ContextMenuPage.cpp +++ b/src/apps/sequencer/ui/pages/ContextMenuPage.cpp @@ -3,18 +3,31 @@ #include "ui/painters/WindowPainter.h" #include "core/math/Math.h" +#include ContextMenuPage::ContextMenuPage(PageManager &manager, PageContext &context) : BasePage(manager, context) -{} +{} + +uint32_t lastTicks; void ContextMenuPage::show(ContextMenuModel &contextMenuModel, ResultCallback callback) { _contextMenuModel = &contextMenuModel; _callback = callback; + lastTicks = os::ticks(); BasePage::show(); } void ContextMenuPage::draw(Canvas &canvas) { + + + uint32_t currentTicks = os::ticks(); + uint32_t deltaTicks = currentTicks - lastTicks; + if (deltaTicks > os::time::ms(2000)) { + close(); + } + + canvas.setFont(Font::Tiny); canvas.setBlendMode(BlendMode::Set); @@ -53,6 +66,9 @@ void ContextMenuPage::draw(Canvas &canvas) { void ContextMenuPage::keyUp(KeyEvent &event) { const auto &key = event.key(); + if (_contextMenuModel->doubleClick()) { + return; + } if (!key.pageModifier() || !key.shiftModifier()) { close(); event.consume(); diff --git a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp index 84f77cca..03f0a02f 100644 --- a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp @@ -316,10 +316,7 @@ void CurveSequenceEditPage::draw(Canvas &canvas) { canvas.setColor(Color::Bright); int x; - - auto dir = trackEngine.sequenceState().direction(); - std::cerr << dir << "\n"; if (dir == 1) { x = ((trackEngine.currentStep() - stepOffset) + trackEngine.currentStepFraction()) * stepWidth; } else if (dir == -1) { @@ -393,6 +390,12 @@ void CurveSequenceEditPage::keyPress(KeyPressEvent &event) { return; } + if (key.pageModifier() && event.count() == 2) { + contextShow(true); + event.consume(); + return; + } + if (key.isQuickEdit()) { // XXX Added here, but should we move it to pageModifier structure? if (key.is(Key::Step15)) { @@ -681,12 +684,13 @@ void CurveSequenceEditPage::drawDetail(Canvas &canvas, const CurveSequence::Step } } -void CurveSequenceEditPage::contextShow() { +void CurveSequenceEditPage::contextShow(bool doubleClick) { showContextMenu(ContextMenu( contextMenuItems, int(ContextAction::Last), [&] (int index) { contextAction(index); }, - [&] (int index) { return contextActionEnabled(index); } + [&] (int index) { return contextActionEnabled(index); }, + doubleClick )); } diff --git a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.h b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.h index 8010ac84..aaa05888 100644 --- a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.h +++ b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.h @@ -38,7 +38,7 @@ class CurveSequenceEditPage : public BasePage { void drawDetail(Canvas &canvas, const CurveSequence::Step &step); - void contextShow(); + void contextShow(bool doubleClick = false); void contextAction(int index); bool contextActionEnabled(int index) const; diff --git a/src/apps/sequencer/ui/pages/CurveSequencePage.cpp b/src/apps/sequencer/ui/pages/CurveSequencePage.cpp index 781089e1..c466d897 100644 --- a/src/apps/sequencer/ui/pages/CurveSequencePage.cpp +++ b/src/apps/sequencer/ui/pages/CurveSequencePage.cpp @@ -58,6 +58,13 @@ void CurveSequencePage::keyPress(KeyPressEvent &event) { return; } + + if (key.pageModifier() && event.count() == 2) { + contextShow(true); + event.consume(); + return; + } + if (key.pageModifier()) { return; } @@ -67,12 +74,13 @@ void CurveSequencePage::keyPress(KeyPressEvent &event) { } } -void CurveSequencePage::contextShow() { +void CurveSequencePage::contextShow(bool doubleClick) { showContextMenu(ContextMenu( contextMenuItems, int(ContextAction::Last), [&] (int index) { contextAction(index); }, - [&] (int index) { return contextActionEnabled(index); } + [&] (int index) { return contextActionEnabled(index); }, + doubleClick )); } diff --git a/src/apps/sequencer/ui/pages/CurveSequencePage.h b/src/apps/sequencer/ui/pages/CurveSequencePage.h index 87250edb..f38591ee 100644 --- a/src/apps/sequencer/ui/pages/CurveSequencePage.h +++ b/src/apps/sequencer/ui/pages/CurveSequencePage.h @@ -17,7 +17,7 @@ class CurveSequencePage : public ListPage { virtual void keyPress(KeyPressEvent &event) override; private: - void contextShow(); + void contextShow(bool doubleClick = false); void contextAction(int index); bool contextActionEnabled(int index) const; diff --git a/src/apps/sequencer/ui/pages/GeneratorPage.cpp b/src/apps/sequencer/ui/pages/GeneratorPage.cpp index f6f81f30..54bfd0b8 100644 --- a/src/apps/sequencer/ui/pages/GeneratorPage.cpp +++ b/src/apps/sequencer/ui/pages/GeneratorPage.cpp @@ -138,6 +138,13 @@ void GeneratorPage::keyPress(KeyPressEvent &event) { return; } + + if (key.pageModifier() && event.count() == 2) { + contextShow(true); + event.consume(); + return; + } + if (key.isGlobal()) { return; } @@ -235,12 +242,13 @@ void GeneratorPage::drawRandomGenerator(Canvas &canvas, const RandomGenerator &g } } -void GeneratorPage::contextShow() { +void GeneratorPage::contextShow(bool doubleClick) { showContextMenu(ContextMenu( contextMenuItems, int(ContextAction::Last), [&] (int index) { contextAction(index); }, - [&] (int index) { return contextActionEnabled(index); } + [&] (int index) { return contextActionEnabled(index); }, + doubleClick )); } diff --git a/src/apps/sequencer/ui/pages/GeneratorPage.h b/src/apps/sequencer/ui/pages/GeneratorPage.h index b21ea705..27780541 100644 --- a/src/apps/sequencer/ui/pages/GeneratorPage.h +++ b/src/apps/sequencer/ui/pages/GeneratorPage.h @@ -32,7 +32,7 @@ class GeneratorPage : public BasePage { int stepOffset() const { return _section * StepCount; } - void contextShow(); + void contextShow(bool doubleClick = false); void contextAction(int index); bool contextActionEnabled(int index) const; void init(); diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index f24cb0e7..619d6dba 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -333,6 +333,11 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { event.consume(); return; } + if (key.pageModifier() && event.count() == 2) { + contextShow(true); + event.consume(); + return; + } if (key.isQuickEdit()) { if (key.is(Key::Step15)) { @@ -887,12 +892,12 @@ void NoteSequenceEditPage::drawDetail(Canvas &canvas, const NoteSequence::Step & } } -void NoteSequenceEditPage::contextShow() { +void NoteSequenceEditPage::contextShow(bool doubleClick) { showContextMenu(ContextMenu( contextMenuItems, int(ContextAction::Last), [&] (int index) { contextAction(index); }, - [&] (int index) { return contextActionEnabled(index); } + [&] (int index) { return contextActionEnabled(index); }, doubleClick )); } diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.h b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.h index 4df9d155..f4c9f640 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.h +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.h @@ -39,7 +39,7 @@ class NoteSequenceEditPage : public BasePage { void updateMonitorStep(); void drawDetail(Canvas &canvas, const NoteSequence::Step &step); - void contextShow(); + void contextShow(bool doubleClick = false); void contextAction(int index); bool contextActionEnabled(int index) const; diff --git a/src/apps/sequencer/ui/pages/NoteSequencePage.cpp b/src/apps/sequencer/ui/pages/NoteSequencePage.cpp index bc773484..e159b8ff 100644 --- a/src/apps/sequencer/ui/pages/NoteSequencePage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequencePage.cpp @@ -60,6 +60,13 @@ void NoteSequencePage::keyPress(KeyPressEvent &event) { return; } + + if (key.pageModifier() && event.count() == 2) { + contextShow(true); + event.consume(); + return; + } + if (key.pageModifier()) { return; } @@ -75,12 +82,13 @@ void NoteSequencePage::keyPress(KeyPressEvent &event) { } } -void NoteSequencePage::contextShow() { +void NoteSequencePage::contextShow(bool doubleClick) { showContextMenu(ContextMenu( contextMenuItems, int(ContextAction::Last), [&] (int index) { contextAction(index); }, - [&] (int index) { return contextActionEnabled(index); } + [&] (int index) { return contextActionEnabled(index); }, + doubleClick )); } diff --git a/src/apps/sequencer/ui/pages/NoteSequencePage.h b/src/apps/sequencer/ui/pages/NoteSequencePage.h index ff0880f7..2ed17ffc 100644 --- a/src/apps/sequencer/ui/pages/NoteSequencePage.h +++ b/src/apps/sequencer/ui/pages/NoteSequencePage.h @@ -17,7 +17,7 @@ class NoteSequencePage : public ListPage { virtual void keyPress(KeyPressEvent &event) override; private: - void contextShow(); + void contextShow(bool doubleClick = false); void contextAction(int index); bool contextActionEnabled(int index) const; diff --git a/src/apps/sequencer/ui/pages/PatternPage.cpp b/src/apps/sequencer/ui/pages/PatternPage.cpp index 0fee00ef..c23b1247 100644 --- a/src/apps/sequencer/ui/pages/PatternPage.cpp +++ b/src/apps/sequencer/ui/pages/PatternPage.cpp @@ -232,6 +232,13 @@ void PatternPage::keyPress(KeyPressEvent &event) { return; } + + if (key.pageModifier() && event.count() == 2) { + contextShow(true); + event.consume(); + return; + } + if (key.pageModifier()) { return; } @@ -337,12 +344,13 @@ void PatternPage::encoder(EncoderEvent &event) { _project.editSelectedPatternIndex(event.value(), event.pressed()); } -void PatternPage::contextShow() { +void PatternPage::contextShow(bool doubleClick) { showContextMenu(ContextMenu( contextMenuItems, int(ContextAction::Last), [&] (int index) { contextAction(index); }, - [&] (int index) { return contextActionEnabled(index); } + [&] (int index) { return contextActionEnabled(index); }, + doubleClick )); } diff --git a/src/apps/sequencer/ui/pages/PatternPage.h b/src/apps/sequencer/ui/pages/PatternPage.h index 71d7c9fe..8132d3a6 100644 --- a/src/apps/sequencer/ui/pages/PatternPage.h +++ b/src/apps/sequencer/ui/pages/PatternPage.h @@ -26,7 +26,7 @@ class PatternPage : public BasePage { int *getPressedKeySteps(Key key); private: - void contextShow(); + void contextShow(bool doubleClick = false); void contextAction(int index); bool contextActionEnabled(int index) const; diff --git a/src/apps/sequencer/ui/pages/ProjectPage.cpp b/src/apps/sequencer/ui/pages/ProjectPage.cpp index 64820e0c..567065f3 100644 --- a/src/apps/sequencer/ui/pages/ProjectPage.cpp +++ b/src/apps/sequencer/ui/pages/ProjectPage.cpp @@ -57,6 +57,12 @@ void ProjectPage::keyPress(KeyPressEvent &event) { return; } + if (key.pageModifier() && event.count() == 2) { + contextShow(true); + event.consume(); + return; + } + if (key.pageModifier()) { // easter egg if (key.is(Key::Step15)) { @@ -89,12 +95,13 @@ void ProjectPage::encoder(EncoderEvent &event) { ListPage::encoder(event); } -void ProjectPage::contextShow() { +void ProjectPage::contextShow(bool doubleClick) { showContextMenu(ContextMenu( contextMenuItems, int(ContextAction::Last), [&] (int index) { contextAction(index); }, - [&] (int index) { return contextActionEnabled(index); } + [&] (int index) { return contextActionEnabled(index); }, + doubleClick )); } diff --git a/src/apps/sequencer/ui/pages/ProjectPage.h b/src/apps/sequencer/ui/pages/ProjectPage.h index b8ee65ae..cf9f923e 100644 --- a/src/apps/sequencer/ui/pages/ProjectPage.h +++ b/src/apps/sequencer/ui/pages/ProjectPage.h @@ -18,7 +18,7 @@ class ProjectPage : public ListPage { virtual void encoder(EncoderEvent &event) override; private: - void contextShow(); + void contextShow(bool doubleClick = false); void contextAction(int index); bool contextActionEnabled(int index) const; diff --git a/src/apps/sequencer/ui/pages/SongPage.cpp b/src/apps/sequencer/ui/pages/SongPage.cpp index 5c22a8f4..2dbaa7d1 100644 --- a/src/apps/sequencer/ui/pages/SongPage.cpp +++ b/src/apps/sequencer/ui/pages/SongPage.cpp @@ -224,6 +224,12 @@ void SongPage::keyPress(KeyPressEvent &event) { return; } + if (key.pageModifier() && event.count() == 2) { + contextShow(true); + event.consume(); + return; + } + if (key.pageModifier()) { return; } @@ -377,12 +383,13 @@ uint8_t SongPage::pressedTrackKeys() const { return tracks; } -void SongPage::contextShow() { +void SongPage::contextShow(bool doubleClick) { showContextMenu(ContextMenu( contextMenuItems, int(ContextAction::Last), [&] (int index) { contextAction(index); }, - [&] (int index) { return contextActionEnabled(index); } + [&] (int index) { return contextActionEnabled(index); }, + doubleClick )); } diff --git a/src/apps/sequencer/ui/pages/SongPage.h b/src/apps/sequencer/ui/pages/SongPage.h index 9a6ad9f6..0ce08e77 100644 --- a/src/apps/sequencer/ui/pages/SongPage.h +++ b/src/apps/sequencer/ui/pages/SongPage.h @@ -29,7 +29,7 @@ class SongPage : public BasePage { uint8_t pressedTrackKeys() const; - void contextShow(); + void contextShow(bool doubleClick = false); void contextAction(int index); bool contextActionEnabled(int index) const; diff --git a/src/apps/sequencer/ui/pages/SystemPage.cpp b/src/apps/sequencer/ui/pages/SystemPage.cpp index f8ce6f0a..0fd3e3f5 100644 --- a/src/apps/sequencer/ui/pages/SystemPage.cpp +++ b/src/apps/sequencer/ui/pages/SystemPage.cpp @@ -167,6 +167,13 @@ void SystemPage::keyPress(KeyPressEvent &event) { } } + + if (key.pageModifier() && event.count() == 2) { + contextShow(true); + event.consume(); + return; + } + if (key.isFunction()) { if (_mode == Mode::Calibration && edit()) { if (CalibrationEditFunction(key.function()) == CalibrationEditFunction::Auto) { @@ -277,12 +284,13 @@ void SystemPage::executeUtilityItem(UtilitiesListModel::Item item) { } } -void SystemPage::contextShow() { +void SystemPage::contextShow(bool doubleClick) { showContextMenu(ContextMenu( contextMenuItems, int(ContextAction::Last), [&] (int index) { contextAction(index); }, - [&] (int index) { return contextActionEnabled(index); } + [&] (int index) { return contextActionEnabled(index); }, + doubleClick )); } diff --git a/src/apps/sequencer/ui/pages/SystemPage.h b/src/apps/sequencer/ui/pages/SystemPage.h index fc046b38..0fdf97ab 100644 --- a/src/apps/sequencer/ui/pages/SystemPage.h +++ b/src/apps/sequencer/ui/pages/SystemPage.h @@ -37,7 +37,7 @@ class SystemPage : public ListPage { void executeUtilityItem(UtilitiesListModel::Item item); - void contextShow(); + void contextShow(bool doubleClick = false); void contextAction(int index); bool contextActionEnabled(int index) const; diff --git a/src/apps/sequencer/ui/pages/TrackPage.cpp b/src/apps/sequencer/ui/pages/TrackPage.cpp index 69b4ad6e..9fce99d2 100644 --- a/src/apps/sequencer/ui/pages/TrackPage.cpp +++ b/src/apps/sequencer/ui/pages/TrackPage.cpp @@ -55,6 +55,13 @@ void TrackPage::keyPress(KeyPressEvent &event) { return; } + + if (key.pageModifier() && event.count() == 2) { + contextShow(true); + event.consume(); + return; + } + if (key.pageModifier()) { return; } @@ -127,12 +134,13 @@ void TrackPage::setTrack(Track &track) { } } -void TrackPage::contextShow() { +void TrackPage::contextShow(bool doubleClick) { showContextMenu(ContextMenu( contextMenuItems, int(ContextAction::Last), [&] (int index) { contextAction(index); }, - [&] (int index) { return contextActionEnabled(index); } + [&] (int index) { return contextActionEnabled(index); }, + doubleClick )); } diff --git a/src/apps/sequencer/ui/pages/TrackPage.h b/src/apps/sequencer/ui/pages/TrackPage.h index 8bcb252e..f3d7289c 100644 --- a/src/apps/sequencer/ui/pages/TrackPage.h +++ b/src/apps/sequencer/ui/pages/TrackPage.h @@ -21,7 +21,7 @@ class TrackPage : public ListPage { private: void setTrack(Track &track); - void contextShow(); + void contextShow(bool doubleClick = false); void contextAction(int index); bool contextActionEnabled(int index) const; diff --git a/src/apps/sequencer/ui/pages/UserScalePage.cpp b/src/apps/sequencer/ui/pages/UserScalePage.cpp index 984250f2..2c5eb71b 100644 --- a/src/apps/sequencer/ui/pages/UserScalePage.cpp +++ b/src/apps/sequencer/ui/pages/UserScalePage.cpp @@ -55,6 +55,13 @@ void UserScalePage::keyPress(KeyPressEvent &event) { return; } + + if (key.pageModifier() && event.count() == 2) { + contextShow(true); + event.consume(); + return; + } + if (key.isFunction()) { if (key.function() < CONFIG_USER_SCALE_COUNT) { setSelectedIndex(key.function()); @@ -84,12 +91,13 @@ void UserScalePage::setSelectedIndex(int index) { setEdit(false); } -void UserScalePage::contextShow() { +void UserScalePage::contextShow(bool doubleClick) { showContextMenu(ContextMenu( contextMenuItems, int(ContextAction::Last), [&] (int index) { contextAction(index); }, - [&] (int index) { return contextActionEnabled(index); } + [&] (int index) { return contextActionEnabled(index); }, + doubleClick )); } diff --git a/src/apps/sequencer/ui/pages/UserScalePage.h b/src/apps/sequencer/ui/pages/UserScalePage.h index d63f9fc5..74e72651 100644 --- a/src/apps/sequencer/ui/pages/UserScalePage.h +++ b/src/apps/sequencer/ui/pages/UserScalePage.h @@ -20,7 +20,7 @@ class UserScalePage : public ListPage { private: void setSelectedIndex(int index); - void contextShow(); + void contextShow(bool doubleClick = false); void contextAction(int index); bool contextActionEnabled(int index) const; From accfab98e9498fded733c989f88794b2ed46f783 Mon Sep 17 00:00:00 2001 From: mebitek Date: Sun, 28 Jan 2024 11:18:17 +0100 Subject: [PATCH 15/33] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 270cab7e..e29d0590 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - smart cycling on patter follow modes (check if launchpad is connected) - Show launchpad settings only when a launchpad is connected - Apply random for selected steps only +- double click page to enter context menu for 2 seconds # v0.1.4.47 (24 January 2024) - launchpad circuit mode improvements From 678714495ad14cb541898e4d815b76f76d7e59d5 Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 07:33:49 +0100 Subject: [PATCH 16/33] issue #49 Prevent very short output clock pulses at higher BPMs --- src/apps/sequencer/engine/Clock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/sequencer/engine/Clock.cpp b/src/apps/sequencer/engine/Clock.cpp index 425e7c1b..ab340416 100644 --- a/src/apps/sequencer/engine/Clock.cpp +++ b/src/apps/sequencer/engine/Clock.cpp @@ -419,7 +419,7 @@ void Clock::outputTick(uint32_t tick) { if (tick == _output.nextTick) { uint32_t divisor = _output.divisor; - uint32_t clockDuration = std::max(uint32_t(1), uint32_t(_masterBpm * _ppqn * _output.pulse / (60 * 1000))); + uint32_t clockDuration = std::max(uint32_t(10), uint32_t(_masterBpm * _ppqn * _output.pulse / (60 * 1000))); _output.nextTickOn = applySwing(_output.nextTick); _output.nextTickOff = std::min(_output.nextTickOn + clockDuration, applySwing(_output.nextTick + divisor) - 1); From d13d613cb5d6ab3680c41338190bdd834cb92012 Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 07:36:55 +0100 Subject: [PATCH 17/33] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e29d0590..a1517720 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Show launchpad settings only when a launchpad is connected - Apply random for selected steps only - double click page to enter context menu for 2 seconds +- Prevent very short output clock pulses at higher BPMs # v0.1.4.47 (24 January 2024) - launchpad circuit mode improvements From 209ce0d1f0bfea063d5e69c44abaa5cced40092b Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 07:55:49 +0100 Subject: [PATCH 18/33] issue #47 shift and page: dobule click page to enter context menu --- src/apps/sequencer/ui/pages/ContextMenuPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/sequencer/ui/pages/ContextMenuPage.cpp b/src/apps/sequencer/ui/pages/ContextMenuPage.cpp index 650df891..dc53aade 100644 --- a/src/apps/sequencer/ui/pages/ContextMenuPage.cpp +++ b/src/apps/sequencer/ui/pages/ContextMenuPage.cpp @@ -23,7 +23,7 @@ void ContextMenuPage::draw(Canvas &canvas) { uint32_t currentTicks = os::ticks(); uint32_t deltaTicks = currentTicks - lastTicks; - if (deltaTicks > os::time::ms(2000)) { + if (deltaTicks > os::time::ms(2000) && _contextMenuModel->doubleClick()) { close(); } From 345d04bf870129017f61773731f39b5f03066d34 Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 08:39:34 +0100 Subject: [PATCH 19/33] issue #38 Undo function --- src/apps/sequencer/model/NoteTrack.cpp | 1 + src/apps/sequencer/model/NoteTrack.h | 4 ++++ src/apps/sequencer/model/Project.h | 4 ++++ .../sequencer/ui/pages/NoteSequenceEditPage.cpp | 16 +++++++++++++++- 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/apps/sequencer/model/NoteTrack.cpp b/src/apps/sequencer/model/NoteTrack.cpp index fc932e2f..d6089361 100644 --- a/src/apps/sequencer/model/NoteTrack.cpp +++ b/src/apps/sequencer/model/NoteTrack.cpp @@ -54,6 +54,7 @@ void NoteTrack::clear() { } } + void NoteTrack::write(VersionedSerializedWriter &writer) const { writer.write(_name, NameLength + 1); writer.write(_playMode); diff --git a/src/apps/sequencer/model/NoteTrack.h b/src/apps/sequencer/model/NoteTrack.h index df109494..f5b11991 100644 --- a/src/apps/sequencer/model/NoteTrack.h +++ b/src/apps/sequencer/model/NoteTrack.h @@ -273,6 +273,10 @@ class NoteTrack : public BaseTrack, public BaseTrackPatternFollow { const NoteSequence &sequence(int index) const { return _sequences[index]; } NoteSequence &sequence(int index) { return _sequences[index]; } + void setSequence(int index, NoteSequence seq) { + _sequences[index] = seq; + } + //---------------------------------------- // Routing //---------------------------------------- diff --git a/src/apps/sequencer/model/Project.h b/src/apps/sequencer/model/Project.h index 4eff1a40..074a5145 100644 --- a/src/apps/sequencer/model/Project.h +++ b/src/apps/sequencer/model/Project.h @@ -458,6 +458,10 @@ class Project { const NoteSequence &selectedNoteSequence() const { return noteSequence(_selectedTrackIndex, selectedPatternIndex()); } NoteSequence &selectedNoteSequence() { return noteSequence(_selectedTrackIndex, selectedPatternIndex()); } + void setSelectedNoteSequence(NoteSequence seq) { + _tracks[_selectedTrackIndex].noteTrack().setSequence(selectedPatternIndex(), seq); + } + // curveSequence const CurveSequence &curveSequence(int trackIndex, int patternIndex) const { return _tracks[trackIndex].curveTrack().sequence(patternIndex); } diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index 619d6dba..7e680faf 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -41,6 +41,8 @@ enum class Function { static const char *functionNames[] = { "GATE", "RETRIG", "LENGTH", "NOTE", "COND" }; +NoteSequence _inMemorySequence; + static const NoteSequenceListModel::Item quickEditItems[8] = { NoteSequenceListModel::Item::FirstStep, NoteSequenceListModel::Item::LastStep, @@ -65,6 +67,8 @@ NoteSequenceEditPage::NoteSequenceEditPage(PageManager &manager, PageContext &co void NoteSequenceEditPage::enter() { updateMonitorStep(); + _inMemorySequence = _project.selectedNoteSequence(); + _showDetail = false; } @@ -351,6 +355,13 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { return; } + if (key.pageModifier() && key.is(Key::Step6)) { + // undo function + _project.setSelectedNoteSequence(_inMemorySequence); + event.consume(); + return; + } + if (key.pageModifier()) { return; } @@ -362,6 +373,7 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { int stepIndex = stepOffset() + key.step(); switch (layer()) { case Layer::Gate: + _inMemorySequence = _project.selectedNoteSequence(); sequence.step(stepIndex).toggleGate(); event.consume(); break; @@ -375,6 +387,7 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { if (!key.shiftModifier() && key.isStep() && keyPressEvent.count() == 2) { int stepIndex = stepOffset() + key.step(); if (layer() != Layer::Gate) { + _inMemorySequence = _project.selectedNoteSequence(); sequence.step(stepIndex).toggleGate(); event.consume(); } @@ -382,6 +395,7 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { if (key.isFunction()) { if(key.shiftModifier() && key.function() == 2 && _stepSelection.any()) { + _inMemorySequence = _project.selectedNoteSequence(); tieNotes(); event.consume(); return; @@ -392,7 +406,7 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { if (key.isEncoder()) { track.setPatternFollowDisplay(false); - + _inMemorySequence = _project.selectedNoteSequence(); if (!_showDetail && _stepSelection.any() && allSelectedStepsActive()) { setSelectedStepsGate(false); } else { From d3b6add134201c4b0a8b40a760a27297db085a27 Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 08:40:33 +0100 Subject: [PATCH 20/33] update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1517720..f2b085ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ - Show launchpad settings only when a launchpad is connected - Apply random for selected steps only - double click page to enter context menu for 2 seconds -- Prevent very short output clock pulses at higher BPMs +- Prevent very short output clock pulses at higher BPMs3 +- Undo function (alt+s7) # v0.1.4.47 (24 January 2024) - launchpad circuit mode improvements From ff89f149de51e7fc4b2f4dd77ce91df1426eb89e Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 09:30:25 +0100 Subject: [PATCH 21/33] issue #18 (#314) Curve mode reverse mode should reverse playback --- .../sequencer/engine/CurveTrackEngine.cpp | 7 +- src/apps/sequencer/model/Curve.cpp | 8 ++ src/apps/sequencer/model/Curve.h | 84 ++++++++++++++++++- .../ui/pages/CurveSequenceEditPage.cpp | 43 +--------- 4 files changed, 96 insertions(+), 46 deletions(-) diff --git a/src/apps/sequencer/engine/CurveTrackEngine.cpp b/src/apps/sequencer/engine/CurveTrackEngine.cpp index 41efbde4..98894d32 100644 --- a/src/apps/sequencer/engine/CurveTrackEngine.cpp +++ b/src/apps/sequencer/engine/CurveTrackEngine.cpp @@ -14,8 +14,11 @@ static Random rng; -static float evalStepShape(const CurveSequence::Step &step, bool variation, bool invert, float fraction) { +static float evalStepShape(const CurveSequence::Step &step, bool variation, bool invert, float fraction, int direction) { auto function = Curve::function(Curve::Type(variation ? step.shapeVariation() : step.shape())); + if (direction == -1) { + function = Curve::function(Curve::Type(variation ? step.shapeVariation() : Curve::revAt(step.shape()))); + } float value = function(fraction); if (invert) { value = 1.f - value; @@ -218,7 +221,7 @@ void CurveTrackEngine::updateOutput(uint32_t relativeTick, uint32_t divisor) { const auto &evalSequence = fillNextPattern ? *_fillSequence : *_sequence; const auto &step = evalSequence.step(_currentStep); - float value = evalStepShape(step, _shapeVariation || fillVariation, fillInvert || _sequenceState.direction() == -1, _currentStepFraction); + float value = evalStepShape(step, _shapeVariation || fillVariation, fillInvert, _currentStepFraction, _sequenceState.direction()); value = range.denormalize(value); _cvOutputTarget = value; } diff --git a/src/apps/sequencer/model/Curve.cpp b/src/apps/sequencer/model/Curve.cpp index 47a3acd8..9c2ba26b 100644 --- a/src/apps/sequencer/model/Curve.cpp +++ b/src/apps/sequencer/model/Curve.cpp @@ -206,4 +206,12 @@ Curve::Function Curve::function(Type type) { float Curve::eval(Type type, float x) { return functions[type](x); +} + +Curve::Type Curve::invAt(int i) { + return INV_SHAPE_MAP.at(i); +} + +Curve::Type Curve::revAt(int i) { + return REV_SHAPE_MAP.at(i); } \ No newline at end of file diff --git a/src/apps/sequencer/model/Curve.h b/src/apps/sequencer/model/Curve.h index 2a533bbd..b32ebc5a 100644 --- a/src/apps/sequencer/model/Curve.h +++ b/src/apps/sequencer/model/Curve.h @@ -2,6 +2,7 @@ #include class Curve { + public: typedef float (*Function)(float x); @@ -53,9 +54,88 @@ class Curve { static float eval(Type type, float x); - + static Type invAt(int i); + static Type revAt(int i); +}; + static const std::map INV_SHAPE_MAP = { + {0, Curve::High}, + {1, Curve::Low}, + {2, Curve::RampDown}, + {3, Curve::RampUp}, + {4, Curve::rampDownHalf}, + {5, Curve::rampUpHalf}, + {6, Curve::doubleRampDownHalf}, + {7, Curve::doubleRampUpHalf}, + {8, Curve::ExpDown}, + {9, Curve::ExpUp}, + {10, Curve::expDownHalf}, + {11, Curve:: expUpHalf}, + {12, Curve::doubleExpDownHalf}, + {13, Curve::doubleExpUpHalf}, + {14, Curve::LogDown}, + {15, Curve::LogUp}, + {16, Curve::logDownHalf}, + {17, Curve::logUpHalf}, + {18, Curve::doubleLogDownHalf}, + {19, Curve::doubleLogUpHalf}, + {20, Curve::SmoothDown}, + {21, Curve::SmoothUp}, + {22, Curve::smoothDownHalf}, + {23, Curve::smoothUpHalf}, + {24, Curve::doubleSmoothDownHalf}, + {25, Curve::doubleSmoothUpHalf}, + {26, Curve::RevTriangle}, + {27, Curve::Triangle}, + {28, Curve::RevBell}, + {29, Curve::Bell}, + {30, Curve::StepDown}, + {31, Curve::StepUp}, + {32, Curve::ExpUp2x}, + {33, Curve::ExpDown2x}, + {34, Curve::ExpUp3x}, + {35, Curve::ExpUp4x}, + {36, Curve::ExpDown4x} + }; -}; + static const std::map REV_SHAPE_MAP = { + {0, Curve::Low}, + {1, Curve::High}, + {2, Curve::RampDown}, + {3, Curve::RampUp}, + {4, Curve::rampDownHalf}, + {5, Curve::rampUpHalf}, + {6, Curve::doubleRampDownHalf}, + {7, Curve::doubleRampUpHalf}, + {8, Curve::ExpDown}, + {9, Curve::ExpUp}, + {10, Curve::expDownHalf}, + {11, Curve:: expUpHalf}, + {12, Curve::doubleExpDownHalf}, + {13, Curve::doubleExpUpHalf}, + {14, Curve::LogDown}, + {15, Curve::LogUp}, + {16, Curve::logDownHalf}, + {17, Curve::logUpHalf}, + {18, Curve::doubleLogDownHalf}, + {19, Curve::doubleLogUpHalf}, + {20, Curve::SmoothDown}, + {21, Curve::SmoothUp}, + {22, Curve::smoothDownHalf}, + {23, Curve::smoothUpHalf}, + {24, Curve::doubleSmoothDownHalf}, + {25, Curve::doubleSmoothUpHalf}, + {26, Curve::Triangle}, + {27, Curve::RevTriangle}, + {28, Curve::Bell}, + {29, Curve::RevBell}, + {30, Curve::StepDown}, + {31, Curve::StepUp}, + {32, Curve::ExpUp2x}, + {33, Curve::ExpDown2x}, + {34, Curve::ExpUp3x}, + {35, Curve::ExpUp4x}, + {36, Curve::ExpDown4x} + }; diff --git a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp index 03f0a02f..55e467c9 100644 --- a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp @@ -36,46 +36,6 @@ enum class Function { Gate = 3, }; -static const std::map _shapeMap = { - {0, Curve::High}, - {1, Curve::Low}, - {2, Curve::RampDown}, - {3, Curve::RampUp}, - {4, Curve::rampDownHalf}, - {5, Curve::rampUpHalf}, - {6, Curve::doubleRampDownHalf}, - {7, Curve::doubleRampUpHalf}, - {8, Curve::ExpDown}, - {9, Curve::ExpUp}, - {10, Curve::expDownHalf}, - {11, Curve:: expUpHalf}, - {12, Curve::doubleExpDownHalf}, - {13, Curve::doubleExpUpHalf}, - {14, Curve::LogDown}, - {15, Curve::LogUp}, - {16, Curve::logDownHalf}, - {17, Curve::logUpHalf}, - {18, Curve::doubleLogDownHalf}, - {19, Curve::doubleLogUpHalf}, - {20, Curve::SmoothDown}, - {21, Curve::SmoothUp}, - {22, Curve::smoothDownHalf}, - {23, Curve::smoothUpHalf}, - {24, Curve::doubleSmoothDownHalf}, - {25, Curve::doubleSmoothUpHalf}, - {26, Curve::RevTriangle}, - {27, Curve::Triangle}, - {28, Curve::RevBell}, - {29, Curve::Bell}, - {30, Curve::StepDown}, - {31, Curve::StepUp}, - {32, Curve::ExpUp2x}, - {33, Curve::ExpDown2x}, - {34, Curve::ExpUp3x}, - {35, Curve::ExpUp4x}, - {36, Curve::ExpDown4x} - }; - static const char *functionNames[] = { "SHAPE", "MIN", "MAX", "GATE", nullptr }; static const CurveSequenceListModel::Item quickEditItems[8] = { @@ -407,12 +367,11 @@ void CurveSequenceEditPage::keyPress(KeyPressEvent &event) { return; } - if (key.isEncoder() && layer() == Layer::Shape && globalKeyState()[Key::Shift] && _stepSelection.count() > 1) { for (size_t stepIndex = 0; stepIndex < _stepSelection.size(); ++stepIndex) { if (_stepSelection[stepIndex]) { auto &step = sequence.step(stepIndex); - auto reverseShape = _shapeMap.at(step.shape()); + auto reverseShape = Curve::invAt(step.shape()); step.setShape(reverseShape); } } From f221213ad643c918af93004f472a75c71a57057f Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 09:31:28 +0100 Subject: [PATCH 22/33] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2b085ed..11463e7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - double click page to enter context menu for 2 seconds - Prevent very short output clock pulses at higher BPMs3 - Undo function (alt+s7) +- Curve mode backward run modes play reverse playback # v0.1.4.47 (24 January 2024) - launchpad circuit mode improvements From debac0ee13aa507abc47b0a05cd291c0f621a5bc Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 10:42:39 +0100 Subject: [PATCH 23/33] issue #48 Bypass the Voltage Table in specific steps of the sequence --- src/apps/sequencer/engine/NoteTrackEngine.cpp | 15 ++++++++ src/apps/sequencer/model/NoteSequence.cpp | 10 ++++++ src/apps/sequencer/model/NoteSequence.h | 12 ++++++- .../launchpad/LaunchpadController.cpp | 1 + .../ui/pages/NoteSequenceEditPage.cpp | 36 +++++++++++++++++-- .../sequencer/ui/painters/SequencePainter.cpp | 11 ++++++ .../sequencer/ui/painters/SequencePainter.h | 2 ++ 7 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/apps/sequencer/engine/NoteTrackEngine.cpp b/src/apps/sequencer/engine/NoteTrackEngine.cpp index 3cedd664..7f3355bd 100644 --- a/src/apps/sequencer/engine/NoteTrackEngine.cpp +++ b/src/apps/sequencer/engine/NoteTrackEngine.cpp @@ -74,6 +74,21 @@ static int evalTransposition(const Scale &scale, int octave, int transpose) { // evaluate note voltage static float evalStepNote(const NoteSequence::Step &step, int probabilityBias, const Scale &scale, int rootNote, int octave, int transpose, bool useVariation = true) { + + + if (step.bypassScale()) { + const Scale &bypassScale = Scale::get(0); + int note = step.note() + evalTransposition(bypassScale, octave, transpose); + int probability = clamp(step.noteVariationProbability() + probabilityBias, -1, NoteSequence::NoteVariationProbability::Max); + if (useVariation && int(rng.nextRange(NoteSequence::NoteVariationProbability::Range)) <= probability) { + int offset = step.noteVariationRange() == 0 ? 0 : rng.nextRange(std::abs(step.noteVariationRange()) + 1); + if (step.noteVariationRange() < 0) { + offset = -offset; + } + note = NoteSequence::Note::clamp(note + offset); + } + return bypassScale.noteToVolts(note) + (bypassScale.isChromatic() ? rootNote : 0) * (1.f / 12.f); + } int note = step.note() + evalTransposition(scale, octave, transpose); int probability = clamp(step.noteVariationProbability() + probabilityBias, -1, NoteSequence::NoteVariationProbability::Max); if (useVariation && int(rng.nextRange(NoteSequence::NoteVariationProbability::Range)) <= probability) { diff --git a/src/apps/sequencer/model/NoteSequence.cpp b/src/apps/sequencer/model/NoteSequence.cpp index 509aae10..fcc6d2ed 100644 --- a/src/apps/sequencer/model/NoteSequence.cpp +++ b/src/apps/sequencer/model/NoteSequence.cpp @@ -13,6 +13,8 @@ Types::LayerRange NoteSequence::layerRange(Layer layer) { return { 0, 1 }; case Layer::Slide: return { 0, 1 }; + case Layer::BypassScale: + return {0 ,1 }; CASE(GateOffset) CASE(GateProbability) CASE(Retrigger) @@ -48,6 +50,8 @@ int NoteSequence::layerDefaultValue(Layer layer) return step.gateOffset(); case Layer::Slide: return step.slide(); + case Layer::BypassScale: + return step.bypassScale(); case Layer::Retrigger: return step.retrigger(); case Layer::RetriggerProbability: @@ -83,6 +87,8 @@ int NoteSequence::Step::layerValue(Layer layer) const { return gate() ? 1 : 0; case Layer::Slide: return slide() ? 1 : 0; + case Layer::BypassScale: + return bypassScale() ? 1 : 0; case Layer::GateProbability: return gateProbability(); case Layer::GateOffset: @@ -124,6 +130,9 @@ void NoteSequence::Step::setLayerValue(Layer layer, int value) { case Layer::Slide: setSlide(value); break; + case Layer::BypassScale: + setBypassScale(value); + break; case Layer::GateProbability: setGateProbability(value); break; @@ -175,6 +184,7 @@ void NoteSequence::Step::clear() { setGateProbability(GateProbability::Max); setGateOffset(0); setSlide(false); + setBypassScale(false); setRetrigger(0); setRetriggerProbability(RetriggerProbability::Max); setLength(Length::Max / 2); diff --git a/src/apps/sequencer/model/NoteSequence.h b/src/apps/sequencer/model/NoteSequence.h index f3688b9d..efe39255 100644 --- a/src/apps/sequencer/model/NoteSequence.h +++ b/src/apps/sequencer/model/NoteSequence.h @@ -55,6 +55,7 @@ class NoteSequence { NoteVariationRange, NoteVariationProbability, Slide, + BypassScale, Condition, Last }; @@ -65,6 +66,7 @@ class NoteSequence { case Layer::GateProbability: return "GATE PROB"; case Layer::GateOffset: return "GATE OFFSET"; case Layer::Slide: return "SLIDE"; + case Layer::BypassScale: return "BYPASS SCALE"; case Layer::Retrigger: return "RETRIG"; case Layer::RetriggerProbability: return "RETRIG PROB"; case Layer::Length: return "LENGTH"; @@ -214,6 +216,14 @@ class NoteSequence { int layerValue(Layer layer) const; void setLayerValue(Layer layer, int value); + bool bypassScale() const { return _data0.bypassScale ? true : false; } + void setBypassScale(bool bypass) { + _data0.bypassScale = bypass; + } + void toggleBypassScale() { + setBypassScale(!bypassScale()); + } + //---------------------------------------- // Methods //---------------------------------------- @@ -244,7 +254,7 @@ class NoteSequence { BitField note; BitField noteVariationRange; BitField noteVariationProbability; - // 1 bits left + BitField bypassScale; } _data0; union { uint32_t raw; diff --git a/src/apps/sequencer/ui/controllers/launchpad/LaunchpadController.cpp b/src/apps/sequencer/ui/controllers/launchpad/LaunchpadController.cpp index 7f5ab5b2..2427c4ec 100644 --- a/src/apps/sequencer/ui/controllers/launchpad/LaunchpadController.cpp +++ b/src/apps/sequencer/ui/controllers/launchpad/LaunchpadController.cpp @@ -64,6 +64,7 @@ static const LayerMapItem noteSequenceLayerMap[] = { [int(NoteSequence::Layer::NoteVariationRange)] = { 1, 3 }, [int(NoteSequence::Layer::NoteVariationProbability)] = { 2, 3 }, [int(NoteSequence::Layer::Slide)] = { 3, 3 }, + [int(NoteSequence::Layer::BypassScale)] = { 4, 3 }, [int(NoteSequence::Layer::Condition)] = { 0, 4 }, diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index 7e680faf..5fee0fa2 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -1,5 +1,6 @@ #include "NoteSequenceEditPage.h" +#include "LayoutPage.h" #include "Pages.h" #include "model/NoteSequence.h" @@ -213,7 +214,19 @@ void NoteSequenceEditPage::draw(Canvas &canvas) { int rootNote = sequence.selectedRootNote(_model.project().rootNote()); canvas.setColor(Color::Bright); FixedStringBuilder<8> str; + + if (step.bypassScale()) { + const Scale &bypassScale = std::ref(Scale::get(0)); + bypassScale.noteName(str, step.note(), rootNote, Scale::Short1); + + canvas.drawText(x + (stepWidth - canvas.textWidth(str) + 1) / 2, y + 20, str); + str.reset(); + bypassScale.noteName(str, step.note(), rootNote, Scale::Short2); + canvas.drawText(x + (stepWidth - canvas.textWidth(str) + 1) / 2, y + 27, str); + break; + } scale.noteName(str, step.note(), rootNote, Scale::Short1); + canvas.drawText(x + (stepWidth - canvas.textWidth(str) + 1) / 2, y + 20, str); str.reset(); scale.noteName(str, step.note(), rootNote, Scale::Short2); @@ -240,6 +253,13 @@ void NoteSequenceEditPage::draw(Canvas &canvas) { step.slide() ); break; + case Layer::BypassScale: + SequencePainter::drawBypassScale( + canvas, + x + 4, y + 18, stepWidth - 8, 4, + step.bypassScale() + ); + break; case Layer::Condition: { canvas.setColor(Color::Bright); FixedStringBuilder<8> str; @@ -272,7 +292,7 @@ void NoteSequenceEditPage::draw(Canvas &canvas) { // handle detail display if (_showDetail) { - if (layer() == Layer::Gate || layer() == Layer::Slide || _stepSelection.none()) { + if (layer() == Layer::Gate || layer() == Layer::Slide || _stepSelection.none() || layer() == Layer::BypassScale) { _showDetail = false; } if (_stepSelection.isPersisted() && os::ticks() > _showDetailTicks + os::time::ms(500)) { @@ -477,7 +497,7 @@ void NoteSequenceEditPage::encoder(EncoderEvent &event) { setLayer(event.value() > 0 ? Layer::Length : Layer::LengthVariationRange); break; case Layer::Note: - setLayer(event.value() > 0 ? Layer::NoteVariationRange : Layer::Slide); + setLayer(event.value() > 0 ? Layer::NoteVariationRange : Layer::BypassScale); break; case Layer::NoteVariationRange: setLayer(event.value() > 0 ? Layer::NoteVariationProbability : Layer::Note); @@ -486,8 +506,10 @@ void NoteSequenceEditPage::encoder(EncoderEvent &event) { setLayer(event.value() > 0 ? Layer::Slide : Layer::NoteVariationRange); break; case Layer::Slide: - setLayer(event.value() > 0 ? Layer::Note : Layer::NoteVariationProbability); + setLayer(event.value() > 0 ? Layer::BypassScale : Layer::NoteVariationProbability); break; + case Layer::BypassScale: + setLayer(event.value() > 0 ? Layer::Note : Layer::Slide); default: break; } @@ -542,6 +564,9 @@ void NoteSequenceEditPage::encoder(EncoderEvent &event) { case Layer::Slide: step.setSlide(event.value() > 0); break; + case Layer::BypassScale: + step.setBypassScale(event.value() > 0); + break; case Layer::Condition: step.setCondition(ModelUtils::adjustedEnum(step.condition(), event.value())); break; @@ -678,6 +703,9 @@ void NoteSequenceEditPage::switchLayer(int functionKey, bool shift) { case Layer::NoteVariationProbability: setLayer(Layer::Slide); break; + case Layer::Slide: + setLayer(Layer::BypassScale); + break; default: setLayer(Layer::Note); break; @@ -708,6 +736,7 @@ int NoteSequenceEditPage::activeFunctionKey() { case Layer::NoteVariationRange: case Layer::NoteVariationProbability: case Layer::Slide: + case Layer::BypassScale: return 3; case Layer::Condition: return 4; @@ -754,6 +783,7 @@ void NoteSequenceEditPage::drawDetail(Canvas &canvas, const NoteSequence::Step & switch (layer()) { case Layer::Gate: case Layer::Slide: + case Layer::BypassScale: break; case Layer::GateProbability: SequencePainter::drawProbability( diff --git a/src/apps/sequencer/ui/painters/SequencePainter.cpp b/src/apps/sequencer/ui/painters/SequencePainter.cpp index 553c3173..c15fc0cf 100644 --- a/src/apps/sequencer/ui/painters/SequencePainter.cpp +++ b/src/apps/sequencer/ui/painters/SequencePainter.cpp @@ -99,6 +99,17 @@ void SequencePainter::drawSlide(Canvas &canvas, int x, int y, int w, int h, bool } } +void SequencePainter::drawBypassScale(Canvas &canvas, int x, int y, int w, int h, bool active) { + canvas.setBlendMode(BlendMode::Set); + canvas.setColor(Color::Bright); + + if (active) { + canvas.drawText(x,y+4, "1"); + } else { + canvas.drawText(x,y+4, "0"); + } +} + const std::bitset<4> mask = 0x1; void SequencePainter::drawStageRepeatMode(Canvas &canvas, int x, int y, int w, int h, NoteSequence::StageRepeatMode mode) { canvas.setBlendMode(BlendMode::Set); diff --git a/src/apps/sequencer/ui/painters/SequencePainter.h b/src/apps/sequencer/ui/painters/SequencePainter.h index fdef6ccb..9e0d9f5c 100644 --- a/src/apps/sequencer/ui/painters/SequencePainter.h +++ b/src/apps/sequencer/ui/painters/SequencePainter.h @@ -14,6 +14,8 @@ class SequencePainter { static void drawLength(Canvas &canvas, int x, int y, int w, int h, int length, int maxLength); static void drawLengthRange(Canvas &canvas, int x, int y, int w, int h, int length, int range, int maxLength); static void drawSlide(Canvas &canvas, int x, int y, int w, int h, bool active); + static void drawBypassScale(Canvas &canvas, int x, int y, int w, int h, bool active); + static void drawStageRepeatMode(Canvas &canvas, int x, int y, int w, int h, NoteSequence::StageRepeatMode mode); static void drawSequenceProgress(Canvas &canvas, int x, int y, int w, int h, float progress); From e3ddfb3a4ef809dd7d955bfa336a6ac88df139f2 Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 11:13:11 +0100 Subject: [PATCH 24/33] issue #45 INIT by step selected --- src/apps/sequencer/model/CurveSequence.cpp | 12 ++++++++++++ src/apps/sequencer/model/CurveSequence.h | 2 ++ src/apps/sequencer/model/CurveTrack.cpp | 1 + .../sequencer/ui/pages/CurveSequenceEditPage.cpp | 2 +- 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/apps/sequencer/model/CurveSequence.cpp b/src/apps/sequencer/model/CurveSequence.cpp index d1f0f8b8..4a6bb8d3 100644 --- a/src/apps/sequencer/model/CurveSequence.cpp +++ b/src/apps/sequencer/model/CurveSequence.cpp @@ -170,6 +170,18 @@ void CurveSequence::clear() { clearSteps(); } +void CurveSequence::clearStepsSelected(const std::bitset &selected) { + if (selected.any()) { + for (size_t i = 0; i < CONFIG_STEP_COUNT; ++i) { + if (selected[i]) { + _steps[i].clear(); + } + } + } else { + clearSteps(); + } +} + void CurveSequence::clearSteps() { for (auto &step : _steps) { step.clear(); diff --git a/src/apps/sequencer/model/CurveSequence.h b/src/apps/sequencer/model/CurveSequence.h index 61467de9..b1a96f1d 100644 --- a/src/apps/sequencer/model/CurveSequence.h +++ b/src/apps/sequencer/model/CurveSequence.h @@ -322,6 +322,8 @@ class CurveSequence { void clear(); void clearSteps(); + void clearStepsSelected(const std::bitset &selected); + bool isEdited() const; diff --git a/src/apps/sequencer/model/CurveTrack.cpp b/src/apps/sequencer/model/CurveTrack.cpp index 91d10989..a77bdd6b 100644 --- a/src/apps/sequencer/model/CurveTrack.cpp +++ b/src/apps/sequencer/model/CurveTrack.cpp @@ -38,6 +38,7 @@ void CurveTrack::clear() { } } + void CurveTrack::write(VersionedSerializedWriter &writer) const { writer.write(_name, NameLength + 1); writer.write(_playMode); diff --git a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp index 55e467c9..155c6f7e 100644 --- a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp @@ -685,7 +685,7 @@ bool CurveSequenceEditPage::contextActionEnabled(int index) const { } void CurveSequenceEditPage::initSequence() { - _project.selectedCurveSequence().clearSteps(); + _project.selectedCurveSequence().clearStepsSelected(_stepSelection.selected()); showMessage("STEPS INITIALIZED"); } From 8a00f992ae0b5f8f45dd92be9dfd3bc48a9c1287 Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 11:38:06 +0100 Subject: [PATCH 25/33] issue #38 Undo function --- src/apps/sequencer/model/CurveTrack.h | 4 ++++ src/apps/sequencer/model/Project.h | 4 ++++ src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp | 2 +- src/apps/sequencer/ui/pages/NoteSequenceEditPage.h | 2 ++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/apps/sequencer/model/CurveTrack.h b/src/apps/sequencer/model/CurveTrack.h index e660041e..85499c33 100644 --- a/src/apps/sequencer/model/CurveTrack.h +++ b/src/apps/sequencer/model/CurveTrack.h @@ -206,6 +206,10 @@ class CurveTrack : public BaseTrack, public BaseTrackPatternFollow { const CurveSequence &sequence(int index) const { return _sequences[index]; } CurveSequence &sequence(int index) { return _sequences[index]; } + void setSequence(int index, CurveSequence seq) { + _sequences[index] = seq; + } + //---------------------------------------- // Routing //---------------------------------------- diff --git a/src/apps/sequencer/model/Project.h b/src/apps/sequencer/model/Project.h index 074a5145..0c2beb9e 100644 --- a/src/apps/sequencer/model/Project.h +++ b/src/apps/sequencer/model/Project.h @@ -443,6 +443,10 @@ class Project { CurveSequence::Layer selectedCurveSequenceLayer() const { return _selectedCurveSequenceLayer; } void setSelectedCurveSequenceLayer(CurveSequence::Layer layer) { _selectedCurveSequenceLayer = layer; } + void setSelectedCurveSequence(CurveSequence seq) { + _tracks[_selectedTrackIndex].curveTrack().setSequence(selectedPatternIndex(), seq); + } + // selectedTrack const Track &selectedTrack() const { return _tracks[_selectedTrackIndex]; } diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index 5fee0fa2..02c55bb1 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -42,7 +42,6 @@ enum class Function { static const char *functionNames[] = { "GATE", "RETRIG", "LENGTH", "NOTE", "COND" }; -NoteSequence _inMemorySequence; static const NoteSequenceListModel::Item quickEditItems[8] = { NoteSequenceListModel::Item::FirstStep, @@ -369,6 +368,7 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { track.togglePatternFollowDisplay(lpConnected); } else { + _inMemorySequence = _project.selectedNoteSequence(); quickEdit(key.quickEdit()); } event.consume(); diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.h b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.h index f4c9f640..7a6edb28 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.h +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.h @@ -74,4 +74,6 @@ class NoteSequenceEditPage : public BasePage { StepSelection _stepSelection; Container _builderContainer; + + NoteSequence _inMemorySequence; }; From e7182bd8dfe285ab9ddde8b949c224b3ce7c988b Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 11:43:17 +0100 Subject: [PATCH 26/33] issue #38 Undo function --- .../ui/pages/CurveSequenceEditPage.cpp | 18 +++++++++++++++++- .../sequencer/ui/pages/CurveSequenceEditPage.h | 2 ++ .../ui/pages/NoteSequenceEditPage.cpp | 2 ++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp index 155c6f7e..8430e329 100644 --- a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp @@ -38,6 +38,7 @@ enum class Function { static const char *functionNames[] = { "SHAPE", "MIN", "MAX", "GATE", nullptr }; + static const CurveSequenceListModel::Item quickEditItems[8] = { CurveSequenceListModel::Item::FirstStep, CurveSequenceListModel::Item::LastStep, @@ -117,6 +118,8 @@ CurveSequenceEditPage::CurveSequenceEditPage(PageManager &manager, PageContext & void CurveSequenceEditPage::enter() { updateMonitorStep(); + _inMemorySequence = _project.selectedCurveSequence(); + _showDetail = false; } @@ -336,6 +339,7 @@ void CurveSequenceEditPage::keyDown(KeyEvent &event) { void CurveSequenceEditPage::keyUp(KeyEvent &event) { _stepSelection.keyUp(event, stepOffset()); + updateMonitorStep(); } @@ -361,13 +365,22 @@ void CurveSequenceEditPage::keyPress(KeyPressEvent &event) { if (key.is(Key::Step15)) { track.togglePatternFollowDisplay(); } else { + _inMemorySequence = _project.selectedCurveSequence(); quickEdit(key.quickEdit()); } event.consume(); return; } + if (key.pageModifier() && key.is(Key::Step6)) { + // undo function + _project.setSelectedCurveSequence(_inMemorySequence); + event.consume(); + return; + } + if (key.isEncoder() && layer() == Layer::Shape && globalKeyState()[Key::Shift] && _stepSelection.count() > 1) { + _inMemorySequence = _project.selectedCurveSequence(); for (size_t stepIndex = 0; stepIndex < _stepSelection.size(); ++stepIndex) { if (_stepSelection[stepIndex]) { auto &step = sequence.step(stepIndex); @@ -386,12 +399,15 @@ void CurveSequenceEditPage::keyPress(KeyPressEvent &event) { } if (key.isEncoder()) { + _inMemorySequence = _project.selectedCurveSequence(); + track.setPatternFollowDisplay(false); event.consume(); } if (key.isLeft()) { if (key.shiftModifier()) { + _inMemorySequence = _project.selectedCurveSequence(); sequence.shiftSteps(_stepSelection.selected(), -1); } else { track.setPatternFollowDisplay(false); @@ -401,6 +417,7 @@ void CurveSequenceEditPage::keyPress(KeyPressEvent &event) { } if (key.isRight()) { if (key.shiftModifier()) { + _inMemorySequence = _project.selectedCurveSequence(); sequence.shiftSteps(_stepSelection.selected(), 1); } else { track.setPatternFollowDisplay(false); @@ -412,7 +429,6 @@ void CurveSequenceEditPage::keyPress(KeyPressEvent &event) { void CurveSequenceEditPage::encoder(EncoderEvent &event) { auto &sequence = _project.selectedCurveSequence(); - if (!_stepSelection.any()) { switch (layer()) { case Layer::Shape: diff --git a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.h b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.h index aaa05888..814da1f8 100644 --- a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.h +++ b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.h @@ -64,4 +64,6 @@ class CurveSequenceEditPage : public BasePage { StepSelection _stepSelection; Container _builderContainer; + + CurveSequence _inMemorySequence; }; diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index 02c55bb1..7e9d2959 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -438,6 +438,7 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { if (key.isLeft()) { if (key.shiftModifier()) { + _inMemorySequence = _project.selectedNoteSequence(); sequence.shiftSteps(_stepSelection.selected(), -1); _stepSelection.shiftLeft(); } else { @@ -448,6 +449,7 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { } if (key.isRight()) { if (key.shiftModifier()) { + _inMemorySequence = _project.selectedNoteSequence(); sequence.shiftSteps(_stepSelection.selected(), 1); _stepSelection.shiftRight(); } else { From 2ff6b7ada282aa5c6f60e7c6a2fee7a3dfeead42 Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 11:43:38 +0100 Subject: [PATCH 27/33] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11463e7d..d771f010 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Prevent very short output clock pulses at higher BPMs3 - Undo function (alt+s7) - Curve mode backward run modes play reverse playback +- Bypass the Voltage Table in specific steps of the sequence # v0.1.4.47 (24 January 2024) - launchpad circuit mode improvements From 98c467ff516fcd1c20e47a88eee159c32702ef97 Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 12:35:01 +0100 Subject: [PATCH 28/33] issue #18 (#314) Curve mode reverse mode should reverse playback --- .../sequencer/engine/CurveTrackEngine.cpp | 42 ++++++++- src/apps/sequencer/model/Curve.cpp | 8 -- src/apps/sequencer/model/Curve.h | 86 +------------------ .../ui/pages/CurveSequenceEditPage.cpp | 44 +++++++++- 4 files changed, 84 insertions(+), 96 deletions(-) diff --git a/src/apps/sequencer/engine/CurveTrackEngine.cpp b/src/apps/sequencer/engine/CurveTrackEngine.cpp index 98894d32..f90194d7 100644 --- a/src/apps/sequencer/engine/CurveTrackEngine.cpp +++ b/src/apps/sequencer/engine/CurveTrackEngine.cpp @@ -14,10 +14,50 @@ static Random rng; +static const std::map REV_SHAPE_MAP = { + {0, Curve::Low}, + {1, Curve::High}, + {2, Curve::RampDown}, + {3, Curve::RampUp}, + {4, Curve::rampDownHalf}, + {5, Curve::rampUpHalf}, + {6, Curve::doubleRampDownHalf}, + {7, Curve::doubleRampUpHalf}, + {8, Curve::ExpDown}, + {9, Curve::ExpUp}, + {10, Curve::expDownHalf}, + {11, Curve:: expUpHalf}, + {12, Curve::doubleExpDownHalf}, + {13, Curve::doubleExpUpHalf}, + {14, Curve::LogDown}, + {15, Curve::LogUp}, + {16, Curve::logDownHalf}, + {17, Curve::logUpHalf}, + {18, Curve::doubleLogDownHalf}, + {19, Curve::doubleLogUpHalf}, + {20, Curve::SmoothDown}, + {21, Curve::SmoothUp}, + {22, Curve::smoothDownHalf}, + {23, Curve::smoothUpHalf}, + {24, Curve::doubleSmoothDownHalf}, + {25, Curve::doubleSmoothUpHalf}, + {26, Curve::Triangle}, + {27, Curve::RevTriangle}, + {28, Curve::Bell}, + {29, Curve::RevBell}, + {30, Curve::StepDown}, + {31, Curve::StepUp}, + {32, Curve::ExpUp2x}, + {33, Curve::ExpDown2x}, + {34, Curve::ExpUp3x}, + {35, Curve::ExpUp4x}, + {36, Curve::ExpDown4x} + }; + static float evalStepShape(const CurveSequence::Step &step, bool variation, bool invert, float fraction, int direction) { auto function = Curve::function(Curve::Type(variation ? step.shapeVariation() : step.shape())); if (direction == -1) { - function = Curve::function(Curve::Type(variation ? step.shapeVariation() : Curve::revAt(step.shape()))); + function = Curve::function(Curve::Type(variation ? step.shapeVariation() : REV_SHAPE_MAP.at(step.shape()))); } float value = function(fraction); if (invert) { diff --git a/src/apps/sequencer/model/Curve.cpp b/src/apps/sequencer/model/Curve.cpp index 9c2ba26b..47a3acd8 100644 --- a/src/apps/sequencer/model/Curve.cpp +++ b/src/apps/sequencer/model/Curve.cpp @@ -206,12 +206,4 @@ Curve::Function Curve::function(Type type) { float Curve::eval(Type type, float x) { return functions[type](x); -} - -Curve::Type Curve::invAt(int i) { - return INV_SHAPE_MAP.at(i); -} - -Curve::Type Curve::revAt(int i) { - return REV_SHAPE_MAP.at(i); } \ No newline at end of file diff --git a/src/apps/sequencer/model/Curve.h b/src/apps/sequencer/model/Curve.h index b32ebc5a..fe133a5b 100644 --- a/src/apps/sequencer/model/Curve.h +++ b/src/apps/sequencer/model/Curve.h @@ -1,6 +1,5 @@ #pragma once -#include class Curve { public: @@ -53,89 +52,6 @@ class Curve { static Function function(Type type); static float eval(Type type, float x); - - static Type invAt(int i); - static Type revAt(int i); -}; - - static const std::map INV_SHAPE_MAP = { - {0, Curve::High}, - {1, Curve::Low}, - {2, Curve::RampDown}, - {3, Curve::RampUp}, - {4, Curve::rampDownHalf}, - {5, Curve::rampUpHalf}, - {6, Curve::doubleRampDownHalf}, - {7, Curve::doubleRampUpHalf}, - {8, Curve::ExpDown}, - {9, Curve::ExpUp}, - {10, Curve::expDownHalf}, - {11, Curve:: expUpHalf}, - {12, Curve::doubleExpDownHalf}, - {13, Curve::doubleExpUpHalf}, - {14, Curve::LogDown}, - {15, Curve::LogUp}, - {16, Curve::logDownHalf}, - {17, Curve::logUpHalf}, - {18, Curve::doubleLogDownHalf}, - {19, Curve::doubleLogUpHalf}, - {20, Curve::SmoothDown}, - {21, Curve::SmoothUp}, - {22, Curve::smoothDownHalf}, - {23, Curve::smoothUpHalf}, - {24, Curve::doubleSmoothDownHalf}, - {25, Curve::doubleSmoothUpHalf}, - {26, Curve::RevTriangle}, - {27, Curve::Triangle}, - {28, Curve::RevBell}, - {29, Curve::Bell}, - {30, Curve::StepDown}, - {31, Curve::StepUp}, - {32, Curve::ExpUp2x}, - {33, Curve::ExpDown2x}, - {34, Curve::ExpUp3x}, - {35, Curve::ExpUp4x}, - {36, Curve::ExpDown4x} - }; - - static const std::map REV_SHAPE_MAP = { - {0, Curve::Low}, - {1, Curve::High}, - {2, Curve::RampDown}, - {3, Curve::RampUp}, - {4, Curve::rampDownHalf}, - {5, Curve::rampUpHalf}, - {6, Curve::doubleRampDownHalf}, - {7, Curve::doubleRampUpHalf}, - {8, Curve::ExpDown}, - {9, Curve::ExpUp}, - {10, Curve::expDownHalf}, - {11, Curve:: expUpHalf}, - {12, Curve::doubleExpDownHalf}, - {13, Curve::doubleExpUpHalf}, - {14, Curve::LogDown}, - {15, Curve::LogUp}, - {16, Curve::logDownHalf}, - {17, Curve::logUpHalf}, - {18, Curve::doubleLogDownHalf}, - {19, Curve::doubleLogUpHalf}, - {20, Curve::SmoothDown}, - {21, Curve::SmoothUp}, - {22, Curve::smoothDownHalf}, - {23, Curve::smoothUpHalf}, - {24, Curve::doubleSmoothDownHalf}, - {25, Curve::doubleSmoothUpHalf}, - {26, Curve::Triangle}, - {27, Curve::RevTriangle}, - {28, Curve::Bell}, - {29, Curve::RevBell}, - {30, Curve::StepDown}, - {31, Curve::StepUp}, - {32, Curve::ExpUp2x}, - {33, Curve::ExpDown2x}, - {34, Curve::ExpUp3x}, - {35, Curve::ExpUp4x}, - {36, Curve::ExpDown4x} - }; +}; diff --git a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp index 8430e329..ddb53c68 100644 --- a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp @@ -38,6 +38,46 @@ enum class Function { static const char *functionNames[] = { "SHAPE", "MIN", "MAX", "GATE", nullptr }; +static const std::map INV_SHAPE_MAP = { + {0, Curve::High}, + {1, Curve::Low}, + {2, Curve::RampDown}, + {3, Curve::RampUp}, + {4, Curve::rampDownHalf}, + {5, Curve::rampUpHalf}, + {6, Curve::doubleRampDownHalf}, + {7, Curve::doubleRampUpHalf}, + {8, Curve::ExpDown}, + {9, Curve::ExpUp}, + {10, Curve::expDownHalf}, + {11, Curve:: expUpHalf}, + {12, Curve::doubleExpDownHalf}, + {13, Curve::doubleExpUpHalf}, + {14, Curve::LogDown}, + {15, Curve::LogUp}, + {16, Curve::logDownHalf}, + {17, Curve::logUpHalf}, + {18, Curve::doubleLogDownHalf}, + {19, Curve::doubleLogUpHalf}, + {20, Curve::SmoothDown}, + {21, Curve::SmoothUp}, + {22, Curve::smoothDownHalf}, + {23, Curve::smoothUpHalf}, + {24, Curve::doubleSmoothDownHalf}, + {25, Curve::doubleSmoothUpHalf}, + {26, Curve::RevTriangle}, + {27, Curve::Triangle}, + {28, Curve::RevBell}, + {29, Curve::Bell}, + {30, Curve::StepDown}, + {31, Curve::StepUp}, + {32, Curve::ExpUp2x}, + {33, Curve::ExpDown2x}, + {34, Curve::ExpUp3x}, + {35, Curve::ExpUp4x}, + {36, Curve::ExpDown4x} + }; + static const CurveSequenceListModel::Item quickEditItems[8] = { CurveSequenceListModel::Item::FirstStep, @@ -278,7 +318,7 @@ void CurveSequenceEditPage::draw(Canvas &canvas) { if (isActiveSequence) { canvas.setColor(Color::Bright); - int x; + int x = 0; auto dir = trackEngine.sequenceState().direction(); if (dir == 1) { x = ((trackEngine.currentStep() - stepOffset) + trackEngine.currentStepFraction()) * stepWidth; @@ -384,7 +424,7 @@ void CurveSequenceEditPage::keyPress(KeyPressEvent &event) { for (size_t stepIndex = 0; stepIndex < _stepSelection.size(); ++stepIndex) { if (_stepSelection[stepIndex]) { auto &step = sequence.step(stepIndex); - auto reverseShape = Curve::invAt(step.shape()); + auto reverseShape = INV_SHAPE_MAP.at(step.shape()); step.setShape(reverseShape); } } From 74131c267f8723e6cc33f0ce4a841229865bfeca Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 12:43:09 +0100 Subject: [PATCH 29/33] issue #18 (#314) Curve mode reverse mode should reverse playback --- .../sequencer/engine/CurveTrackEngine.cpp | 42 +-------- src/apps/sequencer/model/Curve.cpp | 88 +++++++++++++++++++ src/apps/sequencer/model/Curve.h | 5 +- .../ui/pages/CurveSequenceEditPage.cpp | 62 +------------ 4 files changed, 96 insertions(+), 101 deletions(-) diff --git a/src/apps/sequencer/engine/CurveTrackEngine.cpp b/src/apps/sequencer/engine/CurveTrackEngine.cpp index f90194d7..98894d32 100644 --- a/src/apps/sequencer/engine/CurveTrackEngine.cpp +++ b/src/apps/sequencer/engine/CurveTrackEngine.cpp @@ -14,50 +14,10 @@ static Random rng; -static const std::map REV_SHAPE_MAP = { - {0, Curve::Low}, - {1, Curve::High}, - {2, Curve::RampDown}, - {3, Curve::RampUp}, - {4, Curve::rampDownHalf}, - {5, Curve::rampUpHalf}, - {6, Curve::doubleRampDownHalf}, - {7, Curve::doubleRampUpHalf}, - {8, Curve::ExpDown}, - {9, Curve::ExpUp}, - {10, Curve::expDownHalf}, - {11, Curve:: expUpHalf}, - {12, Curve::doubleExpDownHalf}, - {13, Curve::doubleExpUpHalf}, - {14, Curve::LogDown}, - {15, Curve::LogUp}, - {16, Curve::logDownHalf}, - {17, Curve::logUpHalf}, - {18, Curve::doubleLogDownHalf}, - {19, Curve::doubleLogUpHalf}, - {20, Curve::SmoothDown}, - {21, Curve::SmoothUp}, - {22, Curve::smoothDownHalf}, - {23, Curve::smoothUpHalf}, - {24, Curve::doubleSmoothDownHalf}, - {25, Curve::doubleSmoothUpHalf}, - {26, Curve::Triangle}, - {27, Curve::RevTriangle}, - {28, Curve::Bell}, - {29, Curve::RevBell}, - {30, Curve::StepDown}, - {31, Curve::StepUp}, - {32, Curve::ExpUp2x}, - {33, Curve::ExpDown2x}, - {34, Curve::ExpUp3x}, - {35, Curve::ExpUp4x}, - {36, Curve::ExpDown4x} - }; - static float evalStepShape(const CurveSequence::Step &step, bool variation, bool invert, float fraction, int direction) { auto function = Curve::function(Curve::Type(variation ? step.shapeVariation() : step.shape())); if (direction == -1) { - function = Curve::function(Curve::Type(variation ? step.shapeVariation() : REV_SHAPE_MAP.at(step.shape()))); + function = Curve::function(Curve::Type(variation ? step.shapeVariation() : Curve::revAt(step.shape()))); } float value = function(fraction); if (invert) { diff --git a/src/apps/sequencer/model/Curve.cpp b/src/apps/sequencer/model/Curve.cpp index 47a3acd8..81e0b2c7 100644 --- a/src/apps/sequencer/model/Curve.cpp +++ b/src/apps/sequencer/model/Curve.cpp @@ -7,6 +7,86 @@ static const float Pi = 3.1415926536f; static const float TwoPi = 2.f * Pi; + static const std::map INV_SHAPE_MAP = { + {0, Curve::High}, + {1, Curve::Low}, + {2, Curve::RampDown}, + {3, Curve::RampUp}, + {4, Curve::rampDownHalf}, + {5, Curve::rampUpHalf}, + {6, Curve::doubleRampDownHalf}, + {7, Curve::doubleRampUpHalf}, + {8, Curve::ExpDown}, + {9, Curve::ExpUp}, + {10, Curve::expDownHalf}, + {11, Curve:: expUpHalf}, + {12, Curve::doubleExpDownHalf}, + {13, Curve::doubleExpUpHalf}, + {14, Curve::LogDown}, + {15, Curve::LogUp}, + {16, Curve::logDownHalf}, + {17, Curve::logUpHalf}, + {18, Curve::doubleLogDownHalf}, + {19, Curve::doubleLogUpHalf}, + {20, Curve::SmoothDown}, + {21, Curve::SmoothUp}, + {22, Curve::smoothDownHalf}, + {23, Curve::smoothUpHalf}, + {24, Curve::doubleSmoothDownHalf}, + {25, Curve::doubleSmoothUpHalf}, + {26, Curve::RevTriangle}, + {27, Curve::Triangle}, + {28, Curve::RevBell}, + {29, Curve::Bell}, + {30, Curve::StepDown}, + {31, Curve::StepUp}, + {32, Curve::ExpUp2x}, + {33, Curve::ExpDown2x}, + {34, Curve::ExpUp3x}, + {35, Curve::ExpUp4x}, + {36, Curve::ExpDown4x} + }; + + static const std::map REV_SHAPE_MAP = { + {0, Curve::Low}, + {1, Curve::High}, + {2, Curve::RampDown}, + {3, Curve::RampUp}, + {4, Curve::rampDownHalf}, + {5, Curve::rampUpHalf}, + {6, Curve::doubleRampDownHalf}, + {7, Curve::doubleRampUpHalf}, + {8, Curve::ExpDown}, + {9, Curve::ExpUp}, + {10, Curve::expDownHalf}, + {11, Curve:: expUpHalf}, + {12, Curve::doubleExpDownHalf}, + {13, Curve::doubleExpUpHalf}, + {14, Curve::LogDown}, + {15, Curve::LogUp}, + {16, Curve::logDownHalf}, + {17, Curve::logUpHalf}, + {18, Curve::doubleLogDownHalf}, + {19, Curve::doubleLogUpHalf}, + {20, Curve::SmoothDown}, + {21, Curve::SmoothUp}, + {22, Curve::smoothDownHalf}, + {23, Curve::smoothUpHalf}, + {24, Curve::doubleSmoothDownHalf}, + {25, Curve::doubleSmoothUpHalf}, + {26, Curve::Triangle}, + {27, Curve::RevTriangle}, + {28, Curve::Bell}, + {29, Curve::RevBell}, + {30, Curve::StepDown}, + {31, Curve::StepUp}, + {32, Curve::ExpUp2x}, + {33, Curve::ExpDown2x}, + {34, Curve::ExpUp3x}, + {35, Curve::ExpUp4x}, + {36, Curve::ExpDown4x} + }; + static float low(float x) { return 0.f; } @@ -206,4 +286,12 @@ Curve::Function Curve::function(Type type) { float Curve::eval(Type type, float x) { return functions[type](x); +} + +Curve::Type Curve::invAt(int i) { + return INV_SHAPE_MAP.at(i); +} + +Curve::Type Curve::revAt(int i) { + return REV_SHAPE_MAP.at(i); } \ No newline at end of file diff --git a/src/apps/sequencer/model/Curve.h b/src/apps/sequencer/model/Curve.h index fe133a5b..fe027b6e 100644 --- a/src/apps/sequencer/model/Curve.h +++ b/src/apps/sequencer/model/Curve.h @@ -52,6 +52,9 @@ class Curve { static Function function(Type type); static float eval(Type type, float x); -}; + + static Type invAt(int i); + static Type revAt(int i); +}; diff --git a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp index ddb53c68..0df44946 100644 --- a/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/CurveSequenceEditPage.cpp @@ -38,47 +38,6 @@ enum class Function { static const char *functionNames[] = { "SHAPE", "MIN", "MAX", "GATE", nullptr }; -static const std::map INV_SHAPE_MAP = { - {0, Curve::High}, - {1, Curve::Low}, - {2, Curve::RampDown}, - {3, Curve::RampUp}, - {4, Curve::rampDownHalf}, - {5, Curve::rampUpHalf}, - {6, Curve::doubleRampDownHalf}, - {7, Curve::doubleRampUpHalf}, - {8, Curve::ExpDown}, - {9, Curve::ExpUp}, - {10, Curve::expDownHalf}, - {11, Curve:: expUpHalf}, - {12, Curve::doubleExpDownHalf}, - {13, Curve::doubleExpUpHalf}, - {14, Curve::LogDown}, - {15, Curve::LogUp}, - {16, Curve::logDownHalf}, - {17, Curve::logUpHalf}, - {18, Curve::doubleLogDownHalf}, - {19, Curve::doubleLogUpHalf}, - {20, Curve::SmoothDown}, - {21, Curve::SmoothUp}, - {22, Curve::smoothDownHalf}, - {23, Curve::smoothUpHalf}, - {24, Curve::doubleSmoothDownHalf}, - {25, Curve::doubleSmoothUpHalf}, - {26, Curve::RevTriangle}, - {27, Curve::Triangle}, - {28, Curve::RevBell}, - {29, Curve::Bell}, - {30, Curve::StepDown}, - {31, Curve::StepUp}, - {32, Curve::ExpUp2x}, - {33, Curve::ExpDown2x}, - {34, Curve::ExpUp3x}, - {35, Curve::ExpUp4x}, - {36, Curve::ExpDown4x} - }; - - static const CurveSequenceListModel::Item quickEditItems[8] = { CurveSequenceListModel::Item::FirstStep, CurveSequenceListModel::Item::LastStep, @@ -158,8 +117,6 @@ CurveSequenceEditPage::CurveSequenceEditPage(PageManager &manager, PageContext & void CurveSequenceEditPage::enter() { updateMonitorStep(); - _inMemorySequence = _project.selectedCurveSequence(); - _showDetail = false; } @@ -379,7 +336,6 @@ void CurveSequenceEditPage::keyDown(KeyEvent &event) { void CurveSequenceEditPage::keyUp(KeyEvent &event) { _stepSelection.keyUp(event, stepOffset()); - updateMonitorStep(); } @@ -405,26 +361,17 @@ void CurveSequenceEditPage::keyPress(KeyPressEvent &event) { if (key.is(Key::Step15)) { track.togglePatternFollowDisplay(); } else { - _inMemorySequence = _project.selectedCurveSequence(); quickEdit(key.quickEdit()); } event.consume(); return; } - if (key.pageModifier() && key.is(Key::Step6)) { - // undo function - _project.setSelectedCurveSequence(_inMemorySequence); - event.consume(); - return; - } - if (key.isEncoder() && layer() == Layer::Shape && globalKeyState()[Key::Shift] && _stepSelection.count() > 1) { - _inMemorySequence = _project.selectedCurveSequence(); for (size_t stepIndex = 0; stepIndex < _stepSelection.size(); ++stepIndex) { if (_stepSelection[stepIndex]) { auto &step = sequence.step(stepIndex); - auto reverseShape = INV_SHAPE_MAP.at(step.shape()); + auto reverseShape = Curve::invAt(step.shape()); step.setShape(reverseShape); } } @@ -439,15 +386,12 @@ void CurveSequenceEditPage::keyPress(KeyPressEvent &event) { } if (key.isEncoder()) { - _inMemorySequence = _project.selectedCurveSequence(); - track.setPatternFollowDisplay(false); event.consume(); } if (key.isLeft()) { if (key.shiftModifier()) { - _inMemorySequence = _project.selectedCurveSequence(); sequence.shiftSteps(_stepSelection.selected(), -1); } else { track.setPatternFollowDisplay(false); @@ -457,7 +401,6 @@ void CurveSequenceEditPage::keyPress(KeyPressEvent &event) { } if (key.isRight()) { if (key.shiftModifier()) { - _inMemorySequence = _project.selectedCurveSequence(); sequence.shiftSteps(_stepSelection.selected(), 1); } else { track.setPatternFollowDisplay(false); @@ -469,6 +412,7 @@ void CurveSequenceEditPage::keyPress(KeyPressEvent &event) { void CurveSequenceEditPage::encoder(EncoderEvent &event) { auto &sequence = _project.selectedCurveSequence(); + if (!_stepSelection.any()) { switch (layer()) { case Layer::Shape: @@ -741,7 +685,7 @@ bool CurveSequenceEditPage::contextActionEnabled(int index) const { } void CurveSequenceEditPage::initSequence() { - _project.selectedCurveSequence().clearStepsSelected(_stepSelection.selected()); + _project.selectedCurveSequence().clearSteps(); showMessage("STEPS INITIALIZED"); } From f7cbc44bfd3fa22c61813c572d01c41a90e9574e Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 13:24:01 +0100 Subject: [PATCH 30/33] issue #18 (#314) Curve mode reverse mode should reverse playback --- src/apps/sequencer/model/Curve.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apps/sequencer/model/Curve.cpp b/src/apps/sequencer/model/Curve.cpp index 81e0b2c7..a2985180 100644 --- a/src/apps/sequencer/model/Curve.cpp +++ b/src/apps/sequencer/model/Curve.cpp @@ -3,6 +3,7 @@ #include #include +#include static const float Pi = 3.1415926536f; static const float TwoPi = 2.f * Pi; From 794cf3fda2ffed82ce4274c6fc0ee53c8befa551 Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 14:04:51 +0100 Subject: [PATCH 31/33] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d771f010..bd9c23df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - Show launchpad settings only when a launchpad is connected - Apply random for selected steps only - double click page to enter context menu for 2 seconds -- Prevent very short output clock pulses at higher BPMs3 +- Prevent very short output clock pulses at higher BPMs - Undo function (alt+s7) - Curve mode backward run modes play reverse playback - Bypass the Voltage Table in specific steps of the sequence From e3a9f29d1dce1d4774e778b264236c093933c801 Mon Sep 17 00:00:00 2001 From: mebitek Date: Mon, 29 Jan 2024 21:40:28 +0100 Subject: [PATCH 32/33] issue #48 Bypass the Voltage Table in specific steps of the sequence --- src/apps/sequencer/model/NoteSequence.cpp | 3 +++ src/apps/sequencer/model/ProjectVersion.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/apps/sequencer/model/NoteSequence.cpp b/src/apps/sequencer/model/NoteSequence.cpp index fcc6d2ed..c81e86a5 100644 --- a/src/apps/sequencer/model/NoteSequence.cpp +++ b/src/apps/sequencer/model/NoteSequence.cpp @@ -219,6 +219,9 @@ void NoteSequence::Step::read(VersionedSerializedReader &reader) { } else { reader.read(_data0.raw); reader.read(_data1.raw); + if (reader.dataVersion() < ProjectVersion::Version34) { + setBypassScale(false); + } } } diff --git a/src/apps/sequencer/model/ProjectVersion.h b/src/apps/sequencer/model/ProjectVersion.h index c4fb936b..5dd4d32b 100644 --- a/src/apps/sequencer/model/ProjectVersion.h +++ b/src/apps/sequencer/model/ProjectVersion.h @@ -96,6 +96,9 @@ enum ProjectVersion { // added Track::name and expand noteRetrigger to 3 bits and nprobability to 6bits Version33 = 33, + // add bypass scale + Version34 = 34, + // automatically derive latest version Last, Latest = Last - 1, From 9fceb0e3ea8c8ded96ca4a0ddeb7dc39f9c3cd69 Mon Sep 17 00:00:00 2001 From: mebitek Date: Tue, 30 Jan 2024 08:09:40 +0100 Subject: [PATCH 33/33] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd9c23df..b04a0d2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -# v0.1.4.48 () +# v0.1.4.48 (30 January 2024) - Moving steps in a sequence - INIT by step selected - smart cycling on patter follow modes (check if launchpad is connected)