Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Metro-like sequencing #259

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions src/apps/sequencer/engine/NoteTrackEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
#include "core/math/Math.h"

#include "model/Scale.h"
#include "ui/MatrixMap.h"
#include <climits>
#include <iostream>

static Random rng;

Expand Down Expand Up @@ -125,7 +128,9 @@ TrackEngine::TickResult NoteTrackEngine::tick(uint32_t tick) {
// handle reset measure
if (relativeTick == 0) {
reset();
_currentStageRepeat = 1;
}
const auto &sequence = *_sequence;

// advance sequence
switch (_noteTrack.playMode()) {
Expand All @@ -142,9 +147,23 @@ TrackEngine::TickResult NoteTrackEngine::tick(uint32_t tick) {
_freeRelativeTick = 0;
}
if (relativeTick == 0) {
_sequenceState.advanceFree(sequence.runMode(), sequence.firstStep(), sequence.lastStep(), rng);

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;

triggerStep(tick, divisor);

if (isLastStageStep) {
_currentStageRepeat = 1;
} else {
_currentStageRepeat++;
}

}
break;
case Types::PlayMode::Last:
Expand Down Expand Up @@ -302,13 +321,24 @@ 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;
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;
Expand Down Expand Up @@ -356,6 +386,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;
};
Expand Down
1 change: 1 addition & 0 deletions src/apps/sequencer/engine/NoteTrackEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class NoteTrackEngine : public TrackEngine {
float _cvOutput;
float _cvOutputTarget;
bool _slideActive;
unsigned int _currentStageRepeat;

struct Gate {
uint32_t tick;
Expand Down
18 changes: 18 additions & 0 deletions src/apps/sequencer/model/NoteSequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Types::LayerRange NoteSequence::layerRange(Layer layer) {
CASE(NoteVariationRange)
CASE(NoteVariationProbability)
CASE(Condition)
CASE(StageRepeats)
CASE(StageRepeatsMode)
case Layer::Last:
break;
}
Expand Down Expand Up @@ -66,6 +68,10 @@ int NoteSequence::layerDefaultValue(Layer layer)
return step.noteVariationProbability();
case Layer::Condition:
return int(step.condition());
case Layer::StageRepeats:
return step.stageRepeats();
case Layer::StageRepeatsMode:
return step.stageRepeatMode();
case Layer::Last:
break;
}
Expand Down Expand Up @@ -101,6 +107,10 @@ int NoteSequence::Step::layerValue(Layer layer) const {
return noteVariationProbability();
case Layer::Condition:
return int(condition());
case Layer::StageRepeats:
return stageRepeats();
case Layer::StageRepeatsMode:
return stageRepeatMode();
case Layer::Last:
break;
}
Expand Down Expand Up @@ -149,6 +159,12 @@ void NoteSequence::Step::setLayerValue(Layer layer, int value) {
case Layer::Condition:
setCondition(Types::Condition(value));
break;
case Layer::StageRepeats:
setStageRepeats(value);
break;
case Layer::StageRepeatsMode:
setStageRepeatsMode(static_cast<NoteSequence::StageRepeatMode>(value));
break;
case Layer::Last:
break;
}
Expand All @@ -170,6 +186,8 @@ void NoteSequence::Step::clear() {
setNoteVariationRange(0);
setNoteVariationProbability(NoteVariationProbability::Max);
setCondition(Types::Condition::Off);
setStageRepeats(1);
setStageRepeatsMode(StageRepeatMode::Each);
}

void NoteSequence::Step::write(VersionedSerializedWriter &writer) const {
Expand Down
33 changes: 32 additions & 1 deletion src/apps/sequencer/model/NoteSequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class NoteSequence {
typedef SignedValue<7> NoteVariationRange;
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");

Expand All @@ -50,6 +52,8 @@ class NoteSequence {
NoteVariationRange,
NoteVariationProbability,
Condition,
StageRepeats,
StageRepeatsMode,
Last
};

Expand All @@ -68,6 +72,8 @@ 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::StageRepeatsMode: return "REPEAT MODE";
case Layer::Last: break;
}
return nullptr;
Expand All @@ -76,11 +82,34 @@ class NoteSequence {
static Types::LayerRange layerRange(Layer layer);
static int layerDefaultValue(Layer layer);

enum StageRepeatMode {
Each,
First,
Odd,
Triplets
};

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 = mode;
}

StageRepeatMode stageRepeatMode() const {
int value = _data1.stageRepeatMode;
return static_cast<StageRepeatMode>(value);
}

// gate

Expand Down Expand Up @@ -217,7 +246,9 @@ class NoteSequence {
BitField<uint32_t, 2, RetriggerProbability::Bits> retriggerProbability;
BitField<uint32_t, 5, GateOffset::Bits> gateOffset;
BitField<uint32_t, 9, Condition::Bits> condition;
// 16 bits left
BitField<uint32_t, 16, StageRepeats::Bits> stageRepeats;
BitField<uint32_t, 19, StageRepeatsMode::Bits> stageRepeatMode;
// 12 bits left
} _data1;
};

Expand Down
68 changes: 65 additions & 3 deletions src/apps/sequencer/ui/pages/NoteSequenceEditPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -226,6 +227,20 @@ 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::StageRepeatsMode: {
SequencePainter::drawStageRepeatMode(
canvas,
x + 2, y + 18, stepWidth - 4, 6,
step.stageRepeatMode()
);
break;
}
case Layer::Last:
break;
}
Expand Down Expand Up @@ -405,6 +420,16 @@ 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());
break;
case Layer::StageRepeatsMode:
step.setStageRepeatsMode(
static_cast<NoteSequence::StageRepeatMode>(
step.stageRepeatMode() + event.value()
)
);
break;
case Layer::Last:
break;
}
Expand Down Expand Up @@ -443,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);
Expand All @@ -473,8 +498,18 @@ 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::StageRepeatsMode);
break;
case Layer::StageRepeatsMode:
setLayer(Layer::Gate);
break;
default:
setLayer(Layer::Gate);

break;
}
break;
Expand Down Expand Up @@ -526,6 +561,8 @@ int NoteSequenceEditPage::activeFunctionKey() {
case Layer::GateProbability:
case Layer::GateOffset:
case Layer::Slide:
case Layer::StageRepeats:
case Layer::StageRepeatsMode:
return 0;
case Layer::Retrigger:
case Layer::RetriggerProbability:
Expand Down Expand Up @@ -690,6 +727,31 @@ 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::StageRepeatsMode:
str.reset();
switch (step.stageRepeatMode()) {
case NoteSequence::Each:
str("EACH");
break;
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;
}
Expand Down
35 changes: 35 additions & 0 deletions src/apps/sequencer/ui/painters/SequencePainter.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#include "SequencePainter.h"
#include "core/gfx/Canvas.h"
#include "model/NoteSequence.h"
#include <bitset>

void SequencePainter::drawLoopStart(Canvas &canvas, int x, int y, int w) {
canvas.vline(x, y - 1, 3);
Expand Down Expand Up @@ -96,6 +99,38 @@ 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 bottom = y + h - 1;
std::bitset<4> enabled;
x += (w - 8) / 2;

switch (mode) {
case NoteSequence::StageRepeatMode::Each:
enabled = 0xf;
break;
case NoteSequence::StageRepeatMode::First:
enabled = 0x1;
break;
case NoteSequence::StageRepeatMode::Odd:
enabled = 0x5;
break;
case NoteSequence::StageRepeatMode::Triplets:
enabled = 0x9;
break;
}

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);
}
}
}

void SequencePainter::drawSequenceProgress(Canvas &canvas, int x, int y, int w, int h, float progress) {
if (progress < 0.f) {
return;
Expand Down
2 changes: 2 additions & 0 deletions src/apps/sequencer/ui/painters/SequencePainter.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "core/gfx/Canvas.h"
#include "model/NoteSequence.h"

class SequencePainter {
public:
Expand All @@ -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);
};