From 7d35a86d6b21a7967032a05125dac75b96617cb4 Mon Sep 17 00:00:00 2001 From: Guillaume Libersat Date: Wed, 10 Jan 2024 22:24:14 +0100 Subject: [PATCH] [Pattern Follow #20] Add track menu entry, allow following either on Display, Launchpad or both --- src/apps/sequencer/engine/NoteTrackEngine.cpp | 14 +++--- src/apps/sequencer/model/NoteTrack.h | 39 +++++++++++++++- src/apps/sequencer/model/Types.h | 21 +++++++++ .../sequencer/ui/model/NoteTrackListModel.h | 8 ++++ .../ui/pages/NoteSequenceEditPage.cpp | 45 ++++++++++++++++--- .../sequencer/ui/pages/NoteSequenceEditPage.h | 3 +- 6 files changed, 115 insertions(+), 15 deletions(-) diff --git a/src/apps/sequencer/engine/NoteTrackEngine.cpp b/src/apps/sequencer/engine/NoteTrackEngine.cpp index 0b67a25b..3cedd664 100644 --- a/src/apps/sequencer/engine/NoteTrackEngine.cpp +++ b/src/apps/sequencer/engine/NoteTrackEngine.cpp @@ -141,9 +141,9 @@ TrackEngine::TickResult NoteTrackEngine::tick(uint32_t tick) { _sequenceState.advanceAligned(relativeTick / divisor, sequence.runMode(), sequence.firstStep(), sequence.lastStep(), rng); recordStep(tick, divisor); triggerStep(tick, divisor); - + _sequenceState.calculateNextStepAligned( - (relativeTick + divisor) / divisor, + (relativeTick + divisor) / divisor, sequence.runMode(), sequence.firstStep(), sequence.lastStep(), @@ -166,11 +166,11 @@ TrackEngine::TickResult NoteTrackEngine::tick(uint32_t tick) { recordStep(tick, divisor); const auto &step = sequence.step(_sequenceState.step()); bool isLastStageStep = ((int) (step.stageRepeats()+1) - (int) _currentStageRepeat) <= 0; - + triggerStep(tick+divisor, divisor); - + if (isLastStageStep) { - _currentStageRepeat = 1; + _currentStageRepeat = 1; } else { _currentStageRepeat++; } @@ -332,7 +332,7 @@ void NoteTrackEngine::triggerStep(uint32_t tick, uint32_t divisor, bool forNextS // TODO do we need to encounter rotate? _currentStep = SequenceUtils::rotateStep(_sequenceState.step(), sequence.firstStep(), sequence.lastStep(), rotate); - + int stepIndex; if (forNextStep) { @@ -397,7 +397,7 @@ void NoteTrackEngine::triggerStep(uint32_t tick, uint32_t divisor, bool forNextS case 6: stepGate = stepGate && (_currentStageRepeat - 1) % 3 == 0; break; - + } break; } diff --git a/src/apps/sequencer/model/NoteTrack.h b/src/apps/sequencer/model/NoteTrack.h index ec860b0f..dcdbf218 100644 --- a/src/apps/sequencer/model/NoteTrack.h +++ b/src/apps/sequencer/model/NoteTrack.h @@ -14,7 +14,7 @@ class NoteTrack { //---------------------------------------- // Types //---------------------------------------- - static constexpr size_t NameLength = FileHeader::NameLength; + static constexpr size_t NameLength = FileHeader::NameLength; typedef std::array NoteSequenceArray; @@ -270,6 +270,42 @@ class NoteTrack { str("%+.1f%%", noteProbabilityBias() * 12.5f); } + // patternFollow + Types::PatternFollow patternFollow() const { return _patternFollow; } + void setPatternFollow(const Types::PatternFollow patternFollow) { + _patternFollow = ModelUtils::clampedEnum(patternFollow); + } + + void setPatternFollow(bool trackDisplay, bool trackLP) { + + if (trackDisplay && trackLP) { + setPatternFollow(Types::PatternFollow::DispAndLP); + return; + } + + else if (trackDisplay) { + setPatternFollow(Types::PatternFollow::Display); + return; + } + + else if (trackLP) { + setPatternFollow(Types::PatternFollow::LaunchPad); + return; + } + + setPatternFollow(Types::PatternFollow::Off); + + return; + } + + void editPatternFollow(int value, bool shift) { + setPatternFollow(ModelUtils::adjustedEnum(patternFollow(), value)); + } + + void printPatternFollow(StringBuilder &str) const { + str(Types::patternFollowName(patternFollow())); + } + // sequences const NoteSequenceArray &sequences() const { return _sequences; } @@ -319,6 +355,7 @@ class NoteTrack { Routable _retriggerProbabilityBias; Routable _lengthBias; Routable _noteProbabilityBias; + Types::PatternFollow _patternFollow; NoteSequenceArray _sequences; diff --git a/src/apps/sequencer/model/Types.h b/src/apps/sequencer/model/Types.h index 89884bb8..036f684e 100644 --- a/src/apps/sequencer/model/Types.h +++ b/src/apps/sequencer/model/Types.h @@ -138,6 +138,27 @@ class Types { return nullptr; } + // Pattern Follow + enum class PatternFollow : uint8_t { + Off, + Display, + LaunchPad, + DispAndLP, + Last + }; + + static const char *patternFollowName(PatternFollow patternFollow) { + switch (patternFollow) { + case PatternFollow::Off: return "Off"; + case PatternFollow::Display: return "Display"; + case PatternFollow::LaunchPad: return "LaunchPad"; + case PatternFollow::DispAndLP: return "Display+LP"; + case PatternFollow::Last: break; + } + return nullptr; + } + + // Condition enum class Condition : uint8_t { diff --git a/src/apps/sequencer/ui/model/NoteTrackListModel.h b/src/apps/sequencer/ui/model/NoteTrackListModel.h index 2e86075f..06637705 100644 --- a/src/apps/sequencer/ui/model/NoteTrackListModel.h +++ b/src/apps/sequencer/ui/model/NoteTrackListModel.h @@ -72,6 +72,7 @@ class NoteTrackListModel : public RoutableListModel { RetriggerProbabilityBias, LengthBias, NoteProbabilityBias, + PatternFollow, Last }; @@ -90,6 +91,7 @@ class NoteTrackListModel : public RoutableListModel { case RetriggerProbabilityBias: return "Retrig P. Bias"; case LengthBias: return "Length Bias"; case NoteProbabilityBias: return "Note P. Bias"; + case PatternFollow: return "Pattern Follow"; case Last: break; } return nullptr; @@ -140,6 +142,9 @@ class NoteTrackListModel : public RoutableListModel { case NoteProbabilityBias: _track->printNoteProbabilityBias(str); break; + case PatternFollow: + _track->printPatternFollow(str); + break; case Last: break; } @@ -186,6 +191,9 @@ class NoteTrackListModel : public RoutableListModel { case NoteProbabilityBias: _track->editNoteProbabilityBias(value, shift); break; + case PatternFollow: + _track->editPatternFollow(value, shift); + break; case Last: break; } diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index b6318ead..e68d4f4f 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -73,9 +73,13 @@ void NoteSequenceEditPage::exit() { void NoteSequenceEditPage::draw(Canvas &canvas) { WindowPainter::clear(canvas); + const auto ¬e_track = _project.selectedTrack().noteTrack(); + /* Prepare flags shown before mode name (top right header) */ const char *mode_flags = NULL; - if (_sectionTracking) { + + const auto pattern_follow = note_track.patternFollow(); + if (pattern_follow == Types::PatternFollow::Display || pattern_follow == Types::PatternFollow::DispAndLP) { const char *st_flag = "F"; mode_flags = st_flag; } @@ -86,6 +90,7 @@ void NoteSequenceEditPage::draw(Canvas &canvas) { WindowPainter::drawFooter(canvas, functionNames, pageKeyState(), activeFunctionKey()); const auto &trackEngine = _engine.selectedTrackEngine().as(); + const auto &sequence = _project.selectedNoteSequence(); const auto &scale = sequence.selectedScale(_project.scale()); int currentStep = trackEngine.isActiveSequence(sequence) ? trackEngine.currentStep() : -1; @@ -96,9 +101,8 @@ void NoteSequenceEditPage::draw(Canvas &canvas) { const int loopY = 16; - // Track Pattern Section on the UI - if (_sectionTracking && _engine.state().running()) { + if (isSectionTracking() && _engine.state().running()) { bool section_change = bool((currentStep) % StepCount == 0); // StepCount is relative to screen int section_no = int((currentStep) / StepCount); if (section_change && section_no != _section) { @@ -347,7 +351,7 @@ void NoteSequenceEditPage::keyPress(KeyPressEvent &event) { if (key.pageModifier()) { // XXX Added here, but should we move it to pageModifier structure? if (key.is(Key::Step5)) { - setSectionTracking(not _sectionTracking); + toggleSectionTracking(); event.consume(); } return; @@ -932,8 +936,37 @@ void NoteSequenceEditPage::duplicateSequence() { /* * Makes the UI track the current step section */ -void NoteSequenceEditPage::setSectionTracking(bool track) { - _sectionTracking = track; +void NoteSequenceEditPage::setSectionTracking(bool trackDisplay) { + auto ¬e_track = _project.selectedTrack().noteTrack(); + const auto pattern_follow = note_track.patternFollow(); + + const bool lp_tracking = + (pattern_follow == Types::PatternFollow::LaunchPad || + pattern_follow == Types::PatternFollow::DispAndLP); + + note_track.setPatternFollow(trackDisplay, lp_tracking); +} + +bool NoteSequenceEditPage::isSectionTracking() { + auto ¬e_track = _project.selectedTrack().noteTrack(); + const auto pattern_follow = note_track.patternFollow(); + + return (pattern_follow == Types::PatternFollow::Display || + pattern_follow == Types::PatternFollow::DispAndLP); +} + + +void NoteSequenceEditPage::toggleSectionTracking() { + auto ¬e_track = _project.selectedTrack().noteTrack(); + const auto pattern_follow = note_track.patternFollow(); + + const bool disp_tracking = isSectionTracking(); + + const bool lp_tracking = + (pattern_follow == Types::PatternFollow::LaunchPad || + pattern_follow == Types::PatternFollow::DispAndLP); + + note_track.setPatternFollow(not disp_tracking, lp_tracking); } void NoteSequenceEditPage::tieNotes() { diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.h b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.h index aa375fa9..4df9d155 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.h +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.h @@ -56,12 +56,13 @@ class NoteSequenceEditPage : public BasePage { void setSelectedStepsGate(bool gate); void setSectionTracking(bool track); + bool isSectionTracking(); + void toggleSectionTracking(); NoteSequence::Layer layer() const { return _project.selectedNoteSequenceLayer(); }; void setLayer(NoteSequence::Layer layer) { _project.setSelectedNoteSequenceLayer(layer); } int _section = 0; - bool _sectionTracking = false; bool _showDetail; uint32_t _showDetailTicks;