From 40f28c15c3f59d732d27845ac1acf85369e4c71e Mon Sep 17 00:00:00 2001 From: Va Fu Date: Fri, 4 Dec 2020 01:47:10 +0200 Subject: [PATCH 1/9] wip; negative offset retrig needs to be fixed --- src/apps/sequencer/engine/NoteTrackEngine.cpp | 17 ++++++++++++++- src/apps/sequencer/model/NoteSequence.cpp | 8 +++++++ src/apps/sequencer/model/NoteSequence.h | 12 ++++++++++- .../ui/pages/NoteSequenceEditPage.cpp | 21 +++++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/apps/sequencer/engine/NoteTrackEngine.cpp b/src/apps/sequencer/engine/NoteTrackEngine.cpp index 4ebd20b8..ea5ad20f 100644 --- a/src/apps/sequencer/engine/NoteTrackEngine.cpp +++ b/src/apps/sequencer/engine/NoteTrackEngine.cpp @@ -9,6 +9,7 @@ #include "core/math/Math.h" #include "model/Scale.h" +#include static Random rng; @@ -103,6 +104,7 @@ void NoteTrackEngine::restart() { _currentStep = -1; } +unsigned int _currentStageRepeat = 1; TrackEngine::TickResult NoteTrackEngine::tick(uint32_t tick) { ASSERT(_sequence != nullptr, "invalid sequence"); const auto &sequence = *_sequence; @@ -124,7 +126,10 @@ TrackEngine::TickResult NoteTrackEngine::tick(uint32_t tick) { // handle reset measure if (relativeTick == 0) { reset(); + _currentStageRepeat = 1; } + const auto &sequence = *_sequence; + const auto &step = sequence.step(_sequenceState.step()); // advance sequence switch (_noteTrack.playMode()) { @@ -141,7 +146,16 @@ TrackEngine::TickResult NoteTrackEngine::tick(uint32_t tick) { _freeRelativeTick = 0; } if (relativeTick == 0) { - _sequenceState.advanceFree(sequence.runMode(), sequence.firstStep(), sequence.lastStep(), rng); + + if (_currentStageRepeat >= step.stageRepeats()) { + _sequenceState.advanceFree(sequence.runMode(), sequence.firstStep(), sequence.lastStep(), rng); + _currentStageRepeat = 1; + + } else { + _currentStageRepeat++; + triggerStep(tick + divisor, divisor, _sequenceState.step()); + } + recordStep(tick, divisor); triggerStep(tick, divisor); } @@ -355,6 +369,7 @@ void NoteTrackEngine::recordStep(uint32_t tick, uint32_t divisor) { step.setNoteVariationRange(0); step.setNoteVariationProbability(NoteSequence::NoteVariationProbability::Max); step.setCondition(Types::Condition::Off); + step.setStageRepeats(1); stepWritten = true; }; diff --git a/src/apps/sequencer/model/NoteSequence.cpp b/src/apps/sequencer/model/NoteSequence.cpp index af37b9de..2340bf45 100644 --- a/src/apps/sequencer/model/NoteSequence.cpp +++ b/src/apps/sequencer/model/NoteSequence.cpp @@ -26,6 +26,7 @@ Types::LayerRange NoteSequence::layerRange(Layer layer) { CASE(NoteVariationRange) CASE(NoteVariationProbability) CASE(Condition) + CASE(StageRepeats) case Layer::Last: break; } @@ -66,6 +67,8 @@ int NoteSequence::layerDefaultValue(Layer layer) return step.noteVariationProbability(); case Layer::Condition: return int(step.condition()); + case Layer::StageRepeats: + return step.stageRepeats(); case Layer::Last: break; } @@ -101,6 +104,8 @@ int NoteSequence::Step::layerValue(Layer layer) const { return noteVariationProbability(); case Layer::Condition: return int(condition()); + case Layer::StageRepeats: + return stageRepeats(); case Layer::Last: break; } @@ -149,6 +154,8 @@ void NoteSequence::Step::setLayerValue(Layer layer, int value) { case Layer::Condition: setCondition(Types::Condition(value)); break; + case Layer::StageRepeats: + setStageRepeats(value); case Layer::Last: break; } @@ -170,6 +177,7 @@ void NoteSequence::Step::clear() { setNoteVariationRange(0); setNoteVariationProbability(NoteVariationProbability::Max); setCondition(Types::Condition::Off); + setStageRepeats(1); } void NoteSequence::Step::write(VersionedSerializedWriter &writer) const { diff --git a/src/apps/sequencer/model/NoteSequence.h b/src/apps/sequencer/model/NoteSequence.h index e7c98482..fa6e52d7 100644 --- a/src/apps/sequencer/model/NoteSequence.h +++ b/src/apps/sequencer/model/NoteSequence.h @@ -33,6 +33,7 @@ class NoteSequence { typedef SignedValue<7> NoteVariationRange; typedef UnsignedValue<3> NoteVariationProbability; typedef UnsignedValue<7> Condition; + typedef UnsignedValue<3> StageRepeats; static_assert(int(Types::Condition::Last) <= Condition::Max + 1, "Condition enum does not fit"); @@ -50,6 +51,7 @@ class NoteSequence { NoteVariationRange, NoteVariationProbability, Condition, + StageRepeats, Last }; @@ -68,6 +70,7 @@ class NoteSequence { case Layer::NoteVariationRange: return "NOTE RANGE"; case Layer::NoteVariationProbability: return "NOTE PROB"; case Layer::Condition: return "CONDITION"; + case Layer::StageRepeats: return "REPEAT"; case Layer::Last: break; } return nullptr; @@ -82,6 +85,12 @@ class NoteSequence { // Properties //---------------------------------------- + // stage + void setStageRepeats(int repeats) { + _data1.stageRepeats = StageRepeats::clamp(repeats - 1); + } + unsigned int stageRepeats() const { return _data1.stageRepeats + 1; } + // gate bool gate() const { return _data0.gate ? true : false; } @@ -217,7 +226,8 @@ class NoteSequence { BitField retriggerProbability; BitField gateOffset; BitField condition; - // 16 bits left + BitField stageRepeats; + // 13 bits left } _data1; }; diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index 7472f480..df19025c 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -226,6 +226,12 @@ void NoteSequenceEditPage::draw(Canvas &canvas) { canvas.drawText(x + (stepWidth - canvas.textWidth(str) + 1) / 2, y + 27, str); break; } + case Layer::StageRepeats: { + canvas.setColor(0xf); + FixedStringBuilder<8> str("x%d", step.stageRepeats()); + canvas.drawText(x + (stepWidth - canvas.textWidth(str) + 1) / 2, y + 20, str); + break; + } case Layer::Last: break; } @@ -405,6 +411,8 @@ void NoteSequenceEditPage::encoder(EncoderEvent &event) { case Layer::Condition: step.setCondition(ModelUtils::adjustedEnum(step.condition(), event.value())); break; + case Layer::StageRepeats: + step.setStageRepeats(step.stageRepeats() + event.value()); case Layer::Last: break; } @@ -473,6 +481,12 @@ void NoteSequenceEditPage::switchLayer(int functionKey, bool shift) { case Layer::GateOffset: setLayer(Layer::Slide); break; + case Layer::Slide: + setLayer(Layer::StageRepeats); + break; + case Layer::StageRepeats: + setLayer(Layer::Gate); + break; default: setLayer(Layer::Gate); break; @@ -526,6 +540,7 @@ int NoteSequenceEditPage::activeFunctionKey() { case Layer::GateProbability: case Layer::GateOffset: case Layer::Slide: + case Layer::StageRepeats: return 0; case Layer::Retrigger: case Layer::RetriggerProbability: @@ -690,6 +705,12 @@ void NoteSequenceEditPage::drawDetail(Canvas &canvas, const NoteSequence::Step & canvas.setFont(Font::Small); canvas.drawTextCentered(64 + 32, 16, 96, 32, str); break; + case Layer::StageRepeats: + str.reset(); + str("x%d", step.stageRepeats()); + canvas.setFont(Font::Small); + canvas.drawTextCentered(64 + 32, 16, 64, 32, str); + break; case Layer::Last: break; } From e4f02aaab32c3ae1dd31433cfdcefa56fc257c87 Mon Sep 17 00:00:00 2001 From: Va Fu Date: Sat, 5 Dec 2020 06:19:14 +0200 Subject: [PATCH 2/9] working solution --- src/apps/sequencer/engine/NoteTrackEngine.cpp | 24 ++++++++++------ src/apps/sequencer/engine/NoteTrackEngine.h | 1 + src/apps/sequencer/engine/SequenceState.h | 1 + src/apps/sequencer/model/NoteSequence.h | 23 +++++++++++++-- .../ui/pages/NoteSequenceEditPage.cpp | 19 +++++++++++++ .../sequencer/ui/painters/SequencePainter.cpp | 28 +++++++++++++++++++ .../sequencer/ui/painters/SequencePainter.h | 2 ++ 7 files changed, 87 insertions(+), 11 deletions(-) diff --git a/src/apps/sequencer/engine/NoteTrackEngine.cpp b/src/apps/sequencer/engine/NoteTrackEngine.cpp index ea5ad20f..07c772fc 100644 --- a/src/apps/sequencer/engine/NoteTrackEngine.cpp +++ b/src/apps/sequencer/engine/NoteTrackEngine.cpp @@ -9,6 +9,8 @@ #include "core/math/Math.h" #include "model/Scale.h" +#include "ui/MatrixMap.h" +#include #include static Random rng; @@ -104,7 +106,6 @@ void NoteTrackEngine::restart() { _currentStep = -1; } -unsigned int _currentStageRepeat = 1; TrackEngine::TickResult NoteTrackEngine::tick(uint32_t tick) { ASSERT(_sequence != nullptr, "invalid sequence"); const auto &sequence = *_sequence; @@ -129,7 +130,6 @@ TrackEngine::TickResult NoteTrackEngine::tick(uint32_t tick) { _currentStageRepeat = 1; } const auto &sequence = *_sequence; - const auto &step = sequence.step(_sequenceState.step()); // advance sequence switch (_noteTrack.playMode()) { @@ -147,17 +147,24 @@ TrackEngine::TickResult NoteTrackEngine::tick(uint32_t tick) { } if (relativeTick == 0) { - if (_currentStageRepeat >= step.stageRepeats()) { - _sequenceState.advanceFree(sequence.runMode(), sequence.firstStep(), sequence.lastStep(), rng); - _currentStageRepeat = 1; + if (_currentStageRepeat == 1) { + _sequenceState.advanceFree(sequence.runMode(), sequence.firstStep(), sequence.lastStep(), rng); + } + recordStep(tick, divisor); + const auto &step = sequence.step(_sequenceState.step()); + bool isLastStageStep = ((int) step.stageRepeats() - (int) _currentStageRepeat) <= 0; + + if ((step.stageRepeatMode() == NoteSequence::Each || _currentStageRepeat == 1)) { + triggerStep(tick, divisor); + } + + if (isLastStageStep) { + _currentStageRepeat = 1; } else { _currentStageRepeat++; - triggerStep(tick + divisor, divisor, _sequenceState.step()); } - recordStep(tick, divisor); - triggerStep(tick, divisor); } break; case Types::PlayMode::Last: @@ -315,7 +322,6 @@ void NoteTrackEngine::triggerStep(uint32_t tick, uint32_t divisor) { const auto &evalSequence = useFillSequence ? *_fillSequence : *_sequence; _currentStep = SequenceUtils::rotateStep(_sequenceState.step(), sequence.firstStep(), sequence.lastStep(), rotate); const auto &step = evalSequence.step(_currentStep); - uint32_t gateOffset = (divisor * step.gateOffset()) / (NoteSequence::GateOffset::Max + 1); bool stepGate = evalStepGate(step, _noteTrack.gateProbabilityBias()) || useFillGates; diff --git a/src/apps/sequencer/engine/NoteTrackEngine.h b/src/apps/sequencer/engine/NoteTrackEngine.h index 67c7da8f..1ae74469 100644 --- a/src/apps/sequencer/engine/NoteTrackEngine.h +++ b/src/apps/sequencer/engine/NoteTrackEngine.h @@ -77,6 +77,7 @@ class NoteTrackEngine : public TrackEngine { float _cvOutput; float _cvOutputTarget; bool _slideActive; + unsigned int _currentStageRepeat; struct Gate { uint32_t tick; diff --git a/src/apps/sequencer/engine/SequenceState.h b/src/apps/sequencer/engine/SequenceState.h index df366a80..1acf8668 100644 --- a/src/apps/sequencer/engine/SequenceState.h +++ b/src/apps/sequencer/engine/SequenceState.h @@ -12,6 +12,7 @@ class SequenceState { int prevStep() const { return _prevStep; } int direction() const { return _direction; } uint32_t iteration() const { return _iteration; } + uint32_t nextStepIteration() const { return _nextIteration; } void reset(); diff --git a/src/apps/sequencer/model/NoteSequence.h b/src/apps/sequencer/model/NoteSequence.h index fa6e52d7..02805134 100644 --- a/src/apps/sequencer/model/NoteSequence.h +++ b/src/apps/sequencer/model/NoteSequence.h @@ -34,6 +34,7 @@ class NoteSequence { typedef UnsignedValue<3> NoteVariationProbability; typedef UnsignedValue<7> Condition; typedef UnsignedValue<3> StageRepeats; + typedef UnsignedValue<1> StageGateMode; static_assert(int(Types::Condition::Last) <= Condition::Max + 1, "Condition enum does not fit"); @@ -52,6 +53,7 @@ class NoteSequence { NoteVariationProbability, Condition, StageRepeats, + StageRepeatsMode, Last }; @@ -71,6 +73,7 @@ class NoteSequence { case Layer::NoteVariationProbability: return "NOTE PROB"; case Layer::Condition: return "CONDITION"; case Layer::StageRepeats: return "REPEAT"; + case Layer::StageRepeatsMode: return "REPEAT MODE"; case Layer::Last: break; } return nullptr; @@ -79,18 +82,33 @@ class NoteSequence { static Types::LayerRange layerRange(Layer layer); static int layerDefaultValue(Layer layer); + enum StageRepeatMode { + Each, + First, + }; + class Step { + public: //---------------------------------------- // Properties //---------------------------------------- - + // stage void setStageRepeats(int repeats) { _data1.stageRepeats = StageRepeats::clamp(repeats - 1); } unsigned int stageRepeats() const { return _data1.stageRepeats + 1; } + void setStageRepeatsMode(StageRepeatMode mode) { + _data1.stageRepeatMode = StageGateMode::clamp(mode); + } + + StageRepeatMode stageRepeatMode() const { + int value = _data1.stageRepeatMode ? 1 : 0; + return static_cast(value); + } + // gate bool gate() const { return _data0.gate ? true : false; } @@ -227,7 +245,8 @@ class NoteSequence { BitField gateOffset; BitField condition; BitField stageRepeats; - // 13 bits left + BitField stageRepeatMode; + // 12 bits left } _data1; }; diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index df19025c..66942813 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -2,6 +2,7 @@ #include "Pages.h" +#include "model/NoteSequence.h" #include "ui/LedPainter.h" #include "ui/painters/SequencePainter.h" #include "ui/painters/WindowPainter.h" @@ -232,6 +233,14 @@ void NoteSequenceEditPage::draw(Canvas &canvas) { canvas.drawText(x + (stepWidth - canvas.textWidth(str) + 1) / 2, y + 20, str); break; } + case Layer::StageRepeatsMode: { + SequencePainter::drawStageRepeatMode( + canvas, + x + 2, y + 18, stepWidth - 4, 6, + step.stageRepeatMode() + ); + break; + } case Layer::Last: break; } @@ -413,6 +422,12 @@ void NoteSequenceEditPage::encoder(EncoderEvent &event) { break; case Layer::StageRepeats: step.setStageRepeats(step.stageRepeats() + event.value()); + case Layer::StageRepeatsMode: + step.setStageRepeatsMode( + static_cast( + static_cast(step.stageRepeatMode()) - event.value() + ) + ); case Layer::Last: break; } @@ -485,6 +500,9 @@ void NoteSequenceEditPage::switchLayer(int functionKey, bool shift) { setLayer(Layer::StageRepeats); break; case Layer::StageRepeats: + setLayer(Layer::StageRepeatsMode); + break; + case Layer::StageRepeatsMode: setLayer(Layer::Gate); break; default: @@ -541,6 +559,7 @@ int NoteSequenceEditPage::activeFunctionKey() { case Layer::GateOffset: case Layer::Slide: case Layer::StageRepeats: + case Layer::StageRepeatsMode: return 0; case Layer::Retrigger: case Layer::RetriggerProbability: diff --git a/src/apps/sequencer/ui/painters/SequencePainter.cpp b/src/apps/sequencer/ui/painters/SequencePainter.cpp index 5ae1785a..4b5aa9cc 100644 --- a/src/apps/sequencer/ui/painters/SequencePainter.cpp +++ b/src/apps/sequencer/ui/painters/SequencePainter.cpp @@ -1,4 +1,6 @@ #include "SequencePainter.h" +#include "core/gfx/Canvas.h" +#include "model/NoteSequence.h" void SequencePainter::drawLoopStart(Canvas &canvas, int x, int y, int w) { canvas.vline(x, y - 1, 3); @@ -96,6 +98,32 @@ void SequencePainter::drawSlide(Canvas &canvas, int x, int y, int w, int h, bool } } +void SequencePainter::drawStageRepeatMode(Canvas &canvas, int x, int y, int w, int h, NoteSequence::StageRepeatMode mode) { + canvas.setBlendMode(BlendMode::Set); + canvas.setColor(0xf); + int gw = w / 6; + int bottom = y + h - 1; + int iterations; + + switch (mode) { + case NoteSequence::StageRepeatMode::Each: + iterations = 3; + break; + + case NoteSequence::StageRepeatMode::First: + iterations = 1; + canvas.hline(x + 2 * gw, bottom, w - 2 * gw); + break; + } + + for (int i = 0; i < iterations; i++) { + canvas.vline(x + 2 * i * gw, y, h); + canvas.hline(x + 2 * i * gw, y, gw); + canvas.vline(x + (2 * i + 1) * gw, y, h); + canvas.hline(x + (2 * i + 1) * gw, bottom, gw); + } +} + void SequencePainter::drawSequenceProgress(Canvas &canvas, int x, int y, int w, int h, float progress) { if (progress < 0.f) { return; diff --git a/src/apps/sequencer/ui/painters/SequencePainter.h b/src/apps/sequencer/ui/painters/SequencePainter.h index 875eed83..fdef6ccb 100644 --- a/src/apps/sequencer/ui/painters/SequencePainter.h +++ b/src/apps/sequencer/ui/painters/SequencePainter.h @@ -1,6 +1,7 @@ #pragma once #include "core/gfx/Canvas.h" +#include "model/NoteSequence.h" class SequencePainter { public: @@ -13,6 +14,7 @@ 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 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 2c35626381ea688c1ccd2434abfb3cd1a01e5399 Mon Sep 17 00:00:00 2001 From: Va Fu Date: Sat, 5 Dec 2020 06:46:04 +0200 Subject: [PATCH 3/9] add names for gate modes --- src/apps/sequencer/engine/SequenceState.h | 1 - .../sequencer/ui/pages/NoteSequenceEditPage.cpp | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/apps/sequencer/engine/SequenceState.h b/src/apps/sequencer/engine/SequenceState.h index 1acf8668..df366a80 100644 --- a/src/apps/sequencer/engine/SequenceState.h +++ b/src/apps/sequencer/engine/SequenceState.h @@ -12,7 +12,6 @@ class SequenceState { int prevStep() const { return _prevStep; } int direction() const { return _direction; } uint32_t iteration() const { return _iteration; } - uint32_t nextStepIteration() const { return _nextIteration; } void reset(); diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index 66942813..78cc855d 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -730,6 +730,20 @@ void NoteSequenceEditPage::drawDetail(Canvas &canvas, const NoteSequence::Step & canvas.setFont(Font::Small); canvas.drawTextCentered(64 + 32, 16, 64, 32, str); break; + case Layer::StageRepeatsMode: + str.reset(); + switch (step.stageRepeatMode()) { + case NoteSequence::Each: + str("EACH", step.stageRepeatMode()); + break; + case NoteSequence::First: + str("FIRST", step.stageRepeatMode()); + break; + } + canvas.setFont(Font::Small); + canvas.drawTextCentered(64 + 32, 16, 64, 32, str); + break; + case Layer::Last: break; } From adec08e441bc4e9d77523208b809a93400295a5c Mon Sep 17 00:00:00 2001 From: Va Fu Date: Sat, 5 Dec 2020 07:13:18 +0200 Subject: [PATCH 4/9] added note seq layers --- src/apps/sequencer/model/NoteSequence.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/apps/sequencer/model/NoteSequence.cpp b/src/apps/sequencer/model/NoteSequence.cpp index 2340bf45..76822e28 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::StageRepeatsMode: + return { 0, 1 }; case Layer::GateOffset: // TODO: allow negative gate delay in the future return { 0, GateOffset::Max }; @@ -69,6 +71,8 @@ int NoteSequence::layerDefaultValue(Layer layer) return int(step.condition()); case Layer::StageRepeats: return step.stageRepeats(); + case Layer::StageRepeatsMode: + return step.stageRepeatMode(); case Layer::Last: break; } @@ -106,6 +110,8 @@ int NoteSequence::Step::layerValue(Layer layer) const { return int(condition()); case Layer::StageRepeats: return stageRepeats(); + case Layer::StageRepeatsMode: + return stageRepeatMode(); case Layer::Last: break; } @@ -156,6 +162,8 @@ void NoteSequence::Step::setLayerValue(Layer layer, int value) { break; case Layer::StageRepeats: setStageRepeats(value); + case Layer::StageRepeatsMode: + setStageRepeatsMode(static_cast(value)); case Layer::Last: break; } From 06db06428e6660e16adb70c720319cfd7bf1f073 Mon Sep 17 00:00:00 2001 From: Va Fu Date: Sat, 5 Dec 2020 07:30:45 +0200 Subject: [PATCH 5/9] missing breaks --- src/apps/sequencer/model/NoteSequence.cpp | 1 + src/apps/sequencer/model/NoteSequence.h | 3 +-- src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/apps/sequencer/model/NoteSequence.cpp b/src/apps/sequencer/model/NoteSequence.cpp index 76822e28..5d400278 100644 --- a/src/apps/sequencer/model/NoteSequence.cpp +++ b/src/apps/sequencer/model/NoteSequence.cpp @@ -186,6 +186,7 @@ void NoteSequence::Step::clear() { setNoteVariationProbability(NoteVariationProbability::Max); setCondition(Types::Condition::Off); setStageRepeats(1); + setStageRepeatsMode(StageRepeatMode::Each); } void NoteSequence::Step::write(VersionedSerializedWriter &writer) const { diff --git a/src/apps/sequencer/model/NoteSequence.h b/src/apps/sequencer/model/NoteSequence.h index 02805134..5e6d26d4 100644 --- a/src/apps/sequencer/model/NoteSequence.h +++ b/src/apps/sequencer/model/NoteSequence.h @@ -34,7 +34,6 @@ class NoteSequence { typedef UnsignedValue<3> NoteVariationProbability; typedef UnsignedValue<7> Condition; typedef UnsignedValue<3> StageRepeats; - typedef UnsignedValue<1> StageGateMode; static_assert(int(Types::Condition::Last) <= Condition::Max + 1, "Condition enum does not fit"); @@ -101,7 +100,7 @@ class NoteSequence { unsigned int stageRepeats() const { return _data1.stageRepeats + 1; } void setStageRepeatsMode(StageRepeatMode mode) { - _data1.stageRepeatMode = StageGateMode::clamp(mode); + _data1.stageRepeatMode = mode; } StageRepeatMode stageRepeatMode() const { diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index 78cc855d..b9e51638 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -422,12 +422,14 @@ void NoteSequenceEditPage::encoder(EncoderEvent &event) { break; case Layer::StageRepeats: step.setStageRepeats(step.stageRepeats() + event.value()); + break; case Layer::StageRepeatsMode: step.setStageRepeatsMode( static_cast( static_cast(step.stageRepeatMode()) - event.value() ) ); + break; case Layer::Last: break; } @@ -734,10 +736,10 @@ void NoteSequenceEditPage::drawDetail(Canvas &canvas, const NoteSequence::Step & str.reset(); switch (step.stageRepeatMode()) { case NoteSequence::Each: - str("EACH", step.stageRepeatMode()); + str("EACH"); break; case NoteSequence::First: - str("FIRST", step.stageRepeatMode()); + str("FIRST"); break; } canvas.setFont(Font::Small); From 50e30c695790c28f59d6489903effed2dbe69800 Mon Sep 17 00:00:00 2001 From: Va Fu Date: Sat, 5 Dec 2020 07:34:44 +0200 Subject: [PATCH 6/9] missing breaks --- src/apps/sequencer/model/NoteSequence.cpp | 2 ++ src/apps/sequencer/ui/painters/SequencePainter.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/apps/sequencer/model/NoteSequence.cpp b/src/apps/sequencer/model/NoteSequence.cpp index 5d400278..a4ed43b0 100644 --- a/src/apps/sequencer/model/NoteSequence.cpp +++ b/src/apps/sequencer/model/NoteSequence.cpp @@ -162,8 +162,10 @@ void NoteSequence::Step::setLayerValue(Layer layer, int value) { break; case Layer::StageRepeats: setStageRepeats(value); + breakl case Layer::StageRepeatsMode: setStageRepeatsMode(static_cast(value)); + break; case Layer::Last: break; } diff --git a/src/apps/sequencer/ui/painters/SequencePainter.cpp b/src/apps/sequencer/ui/painters/SequencePainter.cpp index 4b5aa9cc..2581170c 100644 --- a/src/apps/sequencer/ui/painters/SequencePainter.cpp +++ b/src/apps/sequencer/ui/painters/SequencePainter.cpp @@ -107,12 +107,12 @@ void SequencePainter::drawStageRepeatMode(Canvas &canvas, int x, int y, int w, i switch (mode) { case NoteSequence::StageRepeatMode::Each: - iterations = 3; - break; + iterations = 3; + break; case NoteSequence::StageRepeatMode::First: - iterations = 1; - canvas.hline(x + 2 * gw, bottom, w - 2 * gw); + iterations = 1; + canvas.hline(x + 2 * gw, bottom, w - 2 * gw); break; } From ab7986691b194c0213550918fa8dbbff45a1be46 Mon Sep 17 00:00:00 2001 From: Va Fu Date: Sat, 5 Dec 2020 07:36:05 +0200 Subject: [PATCH 7/9] missing breaks --- src/apps/sequencer/model/NoteSequence.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/sequencer/model/NoteSequence.cpp b/src/apps/sequencer/model/NoteSequence.cpp index a4ed43b0..289e6955 100644 --- a/src/apps/sequencer/model/NoteSequence.cpp +++ b/src/apps/sequencer/model/NoteSequence.cpp @@ -162,7 +162,7 @@ void NoteSequence::Step::setLayerValue(Layer layer, int value) { break; case Layer::StageRepeats: setStageRepeats(value); - breakl + break; case Layer::StageRepeatsMode: setStageRepeatsMode(static_cast(value)); break; From 8b612c14cf58e680390d53d34397ee3a3703bb9e Mon Sep 17 00:00:00 2001 From: Va Fu Date: Sat, 5 Dec 2020 08:16:16 +0200 Subject: [PATCH 8/9] edge values for gate mode --- src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp | 4 ++-- src/apps/sequencer/ui/painters/SequencePainter.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index b9e51638..62413563 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -423,10 +423,10 @@ void NoteSequenceEditPage::encoder(EncoderEvent &event) { case Layer::StageRepeats: step.setStageRepeats(step.stageRepeats() + event.value()); break; - case Layer::StageRepeatsMode: + case Layer::StageRepeatsMode: step.setStageRepeatsMode( static_cast( - static_cast(step.stageRepeatMode()) - event.value() + event.value() > 0 ? 1 : 0 ) ); break; diff --git a/src/apps/sequencer/ui/painters/SequencePainter.cpp b/src/apps/sequencer/ui/painters/SequencePainter.cpp index 2581170c..7029df2f 100644 --- a/src/apps/sequencer/ui/painters/SequencePainter.cpp +++ b/src/apps/sequencer/ui/painters/SequencePainter.cpp @@ -109,11 +109,10 @@ void SequencePainter::drawStageRepeatMode(Canvas &canvas, int x, int y, int w, i case NoteSequence::StageRepeatMode::Each: iterations = 3; break; - case NoteSequence::StageRepeatMode::First: iterations = 1; canvas.hline(x + 2 * gw, bottom, w - 2 * gw); - break; + break; } for (int i = 0; i < iterations; i++) { From 2869ea80d3c27f26cfd10c1ab2f14a0a72fa65f9 Mon Sep 17 00:00:00 2001 From: Va Fu Date: Mon, 7 Dec 2020 00:29:45 +0200 Subject: [PATCH 9/9] add tripplets and odd mode --- src/apps/sequencer/engine/NoteTrackEngine.cpp | 18 +++++++++--- src/apps/sequencer/model/NoteSequence.cpp | 3 +- src/apps/sequencer/model/NoteSequence.h | 7 +++-- .../ui/pages/NoteSequenceEditPage.cpp | 16 +++++++---- .../sequencer/ui/painters/SequencePainter.cpp | 28 ++++++++++++------- 5 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/apps/sequencer/engine/NoteTrackEngine.cpp b/src/apps/sequencer/engine/NoteTrackEngine.cpp index 07c772fc..c1f16c66 100644 --- a/src/apps/sequencer/engine/NoteTrackEngine.cpp +++ b/src/apps/sequencer/engine/NoteTrackEngine.cpp @@ -155,10 +155,8 @@ TrackEngine::TickResult NoteTrackEngine::tick(uint32_t tick) { const auto &step = sequence.step(_sequenceState.step()); bool isLastStageStep = ((int) step.stageRepeats() - (int) _currentStageRepeat) <= 0; - if ((step.stageRepeatMode() == NoteSequence::Each || _currentStageRepeat == 1)) { - triggerStep(tick, divisor); - } - + triggerStep(tick, divisor); + if (isLastStageStep) { _currentStageRepeat = 1; } else { @@ -328,6 +326,18 @@ void NoteTrackEngine::triggerStep(uint32_t tick, uint32_t divisor) { if (stepGate) { stepGate = evalStepCondition(step, _sequenceState.iteration(), useFillCondition, _prevCondition); } + switch (step.stageRepeatMode()) { + case NoteSequence::StageRepeatMode::Each: + break; + case NoteSequence::StageRepeatMode::First: + stepGate = stepGate && _currentStageRepeat == 1; + break; + case NoteSequence::StageRepeatMode::Odd: + stepGate = stepGate && _currentStageRepeat % 2 != 0; + break; + case NoteSequence::StageRepeatMode::Triplets: + stepGate = stepGate && (_currentStageRepeat - 1) % 3 == 0; + } if (stepGate) { uint32_t stepLength = (divisor * evalStepLength(step, _noteTrack.lengthBias())) / NoteSequence::Length::Range; diff --git a/src/apps/sequencer/model/NoteSequence.cpp b/src/apps/sequencer/model/NoteSequence.cpp index 289e6955..88e4b7ba 100644 --- a/src/apps/sequencer/model/NoteSequence.cpp +++ b/src/apps/sequencer/model/NoteSequence.cpp @@ -13,8 +13,6 @@ Types::LayerRange NoteSequence::layerRange(Layer layer) { return { 0, 1 }; case Layer::Slide: return { 0, 1 }; - case Layer::StageRepeatsMode: - return { 0, 1 }; case Layer::GateOffset: // TODO: allow negative gate delay in the future return { 0, GateOffset::Max }; @@ -29,6 +27,7 @@ Types::LayerRange NoteSequence::layerRange(Layer layer) { CASE(NoteVariationProbability) CASE(Condition) CASE(StageRepeats) + CASE(StageRepeatsMode) case Layer::Last: break; } diff --git a/src/apps/sequencer/model/NoteSequence.h b/src/apps/sequencer/model/NoteSequence.h index 5e6d26d4..8ff695da 100644 --- a/src/apps/sequencer/model/NoteSequence.h +++ b/src/apps/sequencer/model/NoteSequence.h @@ -34,6 +34,7 @@ class NoteSequence { typedef UnsignedValue<3> NoteVariationProbability; typedef UnsignedValue<7> Condition; typedef UnsignedValue<3> StageRepeats; + typedef UnsignedValue<2> StageRepeatsMode; static_assert(int(Types::Condition::Last) <= Condition::Max + 1, "Condition enum does not fit"); @@ -84,6 +85,8 @@ class NoteSequence { enum StageRepeatMode { Each, First, + Odd, + Triplets }; class Step { @@ -104,7 +107,7 @@ class NoteSequence { } StageRepeatMode stageRepeatMode() const { - int value = _data1.stageRepeatMode ? 1 : 0; + int value = _data1.stageRepeatMode; return static_cast(value); } @@ -244,7 +247,7 @@ class NoteSequence { BitField gateOffset; BitField condition; BitField stageRepeats; - BitField stageRepeatMode; + BitField stageRepeatMode; // 12 bits left } _data1; }; diff --git a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp index 62413563..d4d3a36b 100644 --- a/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp +++ b/src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp @@ -426,7 +426,7 @@ void NoteSequenceEditPage::encoder(EncoderEvent &event) { case Layer::StageRepeatsMode: step.setStageRepeatsMode( static_cast( - event.value() > 0 ? 1 : 0 + step.stageRepeatMode() + event.value() ) ); break; @@ -468,13 +468,13 @@ void NoteSequenceEditPage::switchLayer(int functionKey, bool shift) { if (shift) { switch (Function(functionKey)) { case Function::Gate: - setLayer(Layer::Gate); + setLayer(Layer::StageRepeatsMode); break; case Function::Retrigger: - setLayer(Layer::Retrigger); + setLayer(Layer::StageRepeats); break; case Function::Length: - setLayer(Layer::Length); + setLayer(Layer::StageRepeatsMode); break; case Function::Note: setLayer(Layer::Note); @@ -509,6 +509,7 @@ void NoteSequenceEditPage::switchLayer(int functionKey, bool shift) { break; default: setLayer(Layer::Gate); + break; } break; @@ -741,11 +742,16 @@ void NoteSequenceEditPage::drawDetail(Canvas &canvas, const NoteSequence::Step & case NoteSequence::First: str("FIRST"); break; + case NoteSequence::Odd: + str("ODD"); + break; + case NoteSequence::Triplets: + str("TRIPLET"); + break; } canvas.setFont(Font::Small); canvas.drawTextCentered(64 + 32, 16, 64, 32, str); break; - case Layer::Last: break; } diff --git a/src/apps/sequencer/ui/painters/SequencePainter.cpp b/src/apps/sequencer/ui/painters/SequencePainter.cpp index 7029df2f..cc0551e0 100644 --- a/src/apps/sequencer/ui/painters/SequencePainter.cpp +++ b/src/apps/sequencer/ui/painters/SequencePainter.cpp @@ -1,6 +1,7 @@ #include "SequencePainter.h" #include "core/gfx/Canvas.h" #include "model/NoteSequence.h" +#include void SequencePainter::drawLoopStart(Canvas &canvas, int x, int y, int w) { canvas.vline(x, y - 1, 3); @@ -98,28 +99,35 @@ void SequencePainter::drawSlide(Canvas &canvas, int x, int y, int w, int h, bool } } +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); canvas.setColor(0xf); - int gw = w / 6; int bottom = y + h - 1; - int iterations; + std::bitset<4> enabled; + x += (w - 8) / 2; switch (mode) { case NoteSequence::StageRepeatMode::Each: - iterations = 3; + enabled = 0xf; break; case NoteSequence::StageRepeatMode::First: - iterations = 1; - canvas.hline(x + 2 * gw, bottom, w - 2 * gw); + enabled = 0x1; + break; + case NoteSequence::StageRepeatMode::Odd: + enabled = 0x5; + break; + case NoteSequence::StageRepeatMode::Triplets: + enabled = 0x9; break; } - for (int i = 0; i < iterations; i++) { - canvas.vline(x + 2 * i * gw, y, h); - canvas.hline(x + 2 * i * gw, y, gw); - canvas.vline(x + (2 * i + 1) * gw, y, h); - canvas.hline(x + (2 * i + 1) * gw, bottom, gw); + for (int i = 0; i < 4; i++) { + if (((enabled >> i) & mask) == 1) { + canvas.vline(x + 2 * i, y, h); + } else { + canvas.hline(x + 2 * i, bottom, 1); + } } }