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

Add trigger negative offsets #234

Open
wants to merge 6 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
45 changes: 36 additions & 9 deletions src/apps/sequencer/engine/NoteTrackEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "core/utils/Random.h"
#include "core/math/Math.h"

#include "model/NoteSequence.h"
#include "model/Scale.h"

static Random rng;
Expand Down Expand Up @@ -133,6 +134,15 @@ 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,
sequence.runMode(),
sequence.firstStep(),
sequence.lastStep(),
rng
);
triggerStep(tick + divisor, divisor, true);
}
break;
case Types::PlayMode::Free:
Expand Down Expand Up @@ -270,8 +280,7 @@ void NoteTrackEngine::setMonitorStep(int index) {
_stepRecorder.setStepIndex(index);
}
}

void NoteTrackEngine::triggerStep(uint32_t tick, uint32_t divisor) {
void NoteTrackEngine::triggerStep(uint32_t tick, uint32_t divisor, bool forNextStep) {
int octave = _noteTrack.octave();
int transpose = _noteTrack.transpose();
int rotate = _noteTrack.rotate();
Expand All @@ -282,10 +291,24 @@ void NoteTrackEngine::triggerStep(uint32_t tick, uint32_t divisor) {

const auto &sequence = *_sequence;
const auto &evalSequence = useFillSequence ? *_fillSequence : *_sequence;

// TODO do we need to encounter rotate?
_currentStep = SequenceUtils::rotateStep(_sequenceState.step(), sequence.firstStep(), sequence.lastStep(), rotate);
const auto &step = evalSequence.step(_currentStep);

int stepIndex;

if (forNextStep) {
stepIndex = _sequenceState.nextStep();
} else {
stepIndex = _currentStep;
}

uint32_t gateOffset = (divisor * step.gateOffset()) / (NoteSequence::GateOffset::Max + 1);
if (stepIndex < 0) return;

const auto &step = evalSequence.step(stepIndex);

int gateOffset = ((int) divisor * step.gateOffset()) / (NoteSequence::GateOffset::Max + 1);
uint32_t stepTick = (int) tick + gateOffset;

bool stepGate = evalStepGate(step, _noteTrack.gateProbabilityBias()) || useFillGates;
if (stepGate) {
Expand All @@ -299,23 +322,27 @@ void NoteTrackEngine::triggerStep(uint32_t tick, uint32_t divisor) {
uint32_t retriggerLength = divisor / stepRetrigger;
uint32_t retriggerOffset = 0;
while (stepRetrigger-- > 0 && retriggerOffset <= stepLength) {
_gateQueue.pushReplace({ Groove::applySwing(tick + gateOffset + retriggerOffset, swing()), true });
_gateQueue.pushReplace({ Groove::applySwing(tick + gateOffset + retriggerOffset + retriggerLength / 2, swing()), false });
_gateQueue.pushReplace({ Groove::applySwing(stepTick + retriggerOffset, swing()), true });
_gateQueue.pushReplace({ Groove::applySwing(stepTick + retriggerOffset + retriggerLength / 2, swing()), false });
retriggerOffset += retriggerLength;
}
} else {
_gateQueue.pushReplace({ Groove::applySwing(tick + gateOffset, swing()), true });
_gateQueue.pushReplace({ Groove::applySwing(tick + gateOffset + stepLength, swing()), false });
_gateQueue.pushReplace({ Groove::applySwing(stepTick, swing()), true });
_gateQueue.pushReplace({ Groove::applySwing(stepTick + stepLength, swing()), false });
}
}

if (stepGate || _noteTrack.cvUpdateMode() == NoteTrack::CvUpdateMode::Always) {
const auto &scale = evalSequence.selectedScale(_model.project().scale());
int rootNote = evalSequence.selectedRootNote(_model.project().rootNote());
_cvQueue.push({ Groove::applySwing(tick + gateOffset, swing()), evalStepNote(step, _noteTrack.noteProbabilityBias(), scale, rootNote, octave, transpose), step.slide() });
_cvQueue.push({ Groove::applySwing(stepTick, swing()), evalStepNote(step, _noteTrack.noteProbabilityBias(), scale, rootNote, octave, transpose), step.slide() });
}
}

void NoteTrackEngine::triggerStep(uint32_t tick, uint32_t divisor) {
triggerStep(tick, divisor, false);
}

void NoteTrackEngine::recordStep(uint32_t tick, uint32_t divisor) {
if (!_engine.state().recording() || _model.project().recordMode() == Types::RecordMode::StepRecord || _sequenceState.prevStep() < 0) {
return;
Expand Down
2 changes: 2 additions & 0 deletions src/apps/sequencer/engine/NoteTrackEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "SortedQueue.h"
#include "Groove.h"
#include "RecordHistory.h"
#include "model/NoteSequence.h"
#include "StepRecorder.h"

class NoteTrackEngine : public TrackEngine {
Expand Down Expand Up @@ -45,6 +46,7 @@ class NoteTrackEngine : public TrackEngine {
void setMonitorStep(int index);

private:
void triggerStep(uint32_t tick, uint32_t divisor, bool nextStep);
void triggerStep(uint32_t tick, uint32_t divisor);
void recordStep(uint32_t tick, uint32_t divisor);
int noteFromMidiNote(uint8_t midiNote) const;
Expand Down
81 changes: 51 additions & 30 deletions src/apps/sequencer/engine/SequenceState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,74 +8,86 @@ static int randomStep(int firstStep, int lastStep, Random &rng) {
}

void SequenceState::reset() {
_nextStep = -1;
_step = -1;
_prevStep = -1;
_direction = 1;
_iteration = 0;
_nextIteration = 0;
}

void SequenceState::advanceFree(Types::RunMode runMode, int firstStep, int lastStep, Random &rng) {
ASSERT(firstStep <= lastStep, "invalid first/last step");

_prevStep = _step;
if (_nextStep < 0) {
calculateNextStepFree(runMode, firstStep, lastStep, rng);
}

if (_step == -1) {
_iteration = _nextIteration;
_prevStep = _step;
_step = _nextStep;
_nextStep = -1;
}

void SequenceState::calculateNextStepFree(Types::RunMode runMode, int firstStep, int lastStep, Random &rng) {

if (_nextStep == -1) {
// first step
switch (runMode) {
case Types::RunMode::Forward:
case Types::RunMode::Pendulum:
case Types::RunMode::PingPong:
_step = firstStep;
_nextStep = firstStep;
break;
case Types::RunMode::Backward:
_step = lastStep;
_nextStep = lastStep;
break;
case Types::RunMode::Random:
case Types::RunMode::RandomWalk:
_step = randomStep(firstStep, lastStep, rng);
_nextStep = randomStep(firstStep, lastStep, rng);
break;
case Types::RunMode::Last:
break;
}
} else {
// advance step
_step = clamp(int(_step), firstStep, lastStep);
_nextStep = clamp(int(_step), firstStep, lastStep);

switch (runMode) {
case Types::RunMode::Forward:
if (_step >= lastStep) {
_step = firstStep;
++_iteration;
_nextStep = firstStep;
++_nextIteration;
} else {
++_step;
++_nextStep;
}
break;
case Types::RunMode::Backward:
if (_step <= firstStep) {
_step = lastStep;
++_iteration;
_nextStep = lastStep;
++_nextIteration;
} else {
--_step;
--_nextStep;
}
break;
case Types::RunMode::Pendulum:
case Types::RunMode::PingPong:
if (_direction > 0 && _step >= lastStep) {
if (_direction > 0 && _step>= lastStep) {
_direction = -1;
} else if (_direction < 0 && _step <= firstStep) {
_direction = 1;
++_iteration;
++_nextIteration;
} else {
if (runMode == Types::RunMode::Pendulum) {
_step += _direction;
_nextStep += _direction;
}
}
if (runMode == Types::RunMode::PingPong) {
_step += _direction;
_nextStep += _direction;
}
break;
case Types::RunMode::Random:
_step = randomStep(firstStep, lastStep, rng);
_nextStep = randomStep(firstStep, lastStep, rng);
break;
case Types::RunMode::RandomWalk:
advanceRandomWalk(firstStep, lastStep, rng);
Expand All @@ -87,33 +99,42 @@ void SequenceState::advanceFree(Types::RunMode runMode, int firstStep, int lastS
}

void SequenceState::advanceAligned(int absoluteStep, Types::RunMode runMode, int firstStep, int lastStep, Random &rng) {
ASSERT(firstStep <= lastStep, "invalid first/last step");

if (_nextStep < 0) {
calculateNextStepAligned(absoluteStep, runMode, firstStep, lastStep, rng);
}

_iteration = _nextIteration;
_prevStep = _step;
_step = _nextStep;
_nextStep = -1;
}

void SequenceState::calculateNextStepAligned(int absoluteStep, Types::RunMode runMode, int firstStep, int lastStep, Random &rng) {
ASSERT(firstStep <= lastStep, "invalid first/last step");

int stepCount = lastStep - firstStep + 1;

switch (runMode) {
case Types::RunMode::Forward:
_step = firstStep + absoluteStep % stepCount;
_iteration = absoluteStep / stepCount;
_nextStep = firstStep + absoluteStep % stepCount;
break;
case Types::RunMode::Backward:
_step = lastStep - absoluteStep % stepCount;
_iteration = absoluteStep / stepCount;
_nextStep = lastStep - absoluteStep % stepCount;
_nextIteration = absoluteStep / stepCount;
break;
case Types::RunMode::Pendulum:
_iteration = absoluteStep / (2 * stepCount);
_nextIteration = absoluteStep / (2 * stepCount);
absoluteStep %= (2 * stepCount);
_step = (absoluteStep < stepCount) ? (firstStep + absoluteStep) : (lastStep - (absoluteStep - stepCount));
_nextStep = (absoluteStep < stepCount) ? (firstStep + absoluteStep) : (lastStep - (absoluteStep - stepCount));
break;
case Types::RunMode::PingPong:
_iteration = absoluteStep / (2 * stepCount - 2);
_nextIteration = absoluteStep / (2 * stepCount - 2);
absoluteStep %= (2 * stepCount - 2);
_step = (absoluteStep < stepCount) ? (firstStep + absoluteStep) : (lastStep - (absoluteStep - stepCount) - 1);
_nextStep = (absoluteStep < stepCount) ? (firstStep + absoluteStep) : (lastStep - (absoluteStep - stepCount) - 1);
break;
case Types::RunMode::Random:
_step = firstStep + rng.nextRange(stepCount);
_nextStep = firstStep + rng.nextRange(stepCount);
break;
case Types::RunMode::RandomWalk:
advanceRandomWalk(firstStep, lastStep, rng);
Expand All @@ -125,13 +146,13 @@ void SequenceState::advanceAligned(int absoluteStep, Types::RunMode runMode, int

void SequenceState::advanceRandomWalk(int firstStep, int lastStep, Random &rng) {
if (_step == -1) {
_step = randomStep(firstStep, lastStep, rng);
_nextStep = randomStep(firstStep, lastStep, rng);
} else {
int dir = rng.nextRange(2);
if (dir == 0) {
_step = _step == firstStep ? lastStep : _step - 1;
_nextStep = _step == firstStep ? lastStep : _step - 1;
} else {
_step = _step == lastStep ? firstStep : _step + 1;
_nextStep = _step == lastStep ? firstStep : _step + 1;
}
}
}
9 changes: 8 additions & 1 deletion src/apps/sequencer/engine/SequenceState.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,26 @@ class SequenceState {
public:
int step() const { return _step; }
int prevStep() const { return _prevStep; }
int nextStep() const { return _nextStep; }
int direction() const { return _direction; }
uint32_t iteration() const { return _iteration; }


void reset();

void advanceFree(Types::RunMode runMode, int firstStep, int lastStep, Random &rng);
void advanceAligned(int absoluteStep, Types::RunMode runMode, int firstStep, int lastStep, Random &rng);
void calculateNextStepAligned(int absoluteStep, Types::RunMode runMode, int firstStep, int lastStep, Random &rng);

void advanceFree(Types::RunMode runMode, int firstStep, int lastStep, Random &rng);
void calculateNextStepFree(Types::RunMode runMode, int firstStep, int lastStep, Random &rng);

private:
void advanceRandomWalk(int firstStep, int lastStep, Random &rng);

int8_t _step;
int8_t _prevStep;
int8_t _nextStep = -1;
int8_t _direction;
uint32_t _iteration;
uint32_t _nextIteration;
};
4 changes: 1 addition & 3 deletions src/apps/sequencer/model/NoteSequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ Types::LayerRange NoteSequence::layerRange(Layer layer) {
return { 0, 1 };
case Layer::Slide:
return { 0, 1 };
case Layer::GateOffset:
// TODO: allow negative gate delay in the future
return { 0, GateOffset::Max };
CASE(GateOffset)
CASE(GateProbability)
CASE(Retrigger)
CASE(RetriggerProbability)
Expand Down
5 changes: 2 additions & 3 deletions src/apps/sequencer/model/NoteSequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,9 @@ class NoteSequence {

// gateOffset

int gateOffset() const { return GateOffset::Min + _data1.gateOffset; }
int gateOffset() const { return GateOffset::Min + (int) _data1.gateOffset; }
void setGateOffset(int gateOffset) {
// TODO: allow negative gate delay in the future
_data1.gateOffset = std::max(0, GateOffset::clamp(gateOffset)) - GateOffset::Min;
_data1.gateOffset = GateOffset::clamp(gateOffset) - GateOffset::Min;
}

// slide
Expand Down