Skip to content

Commit

Permalink
Merge pull request #51 from mebitek/feature_48
Browse files Browse the repository at this point in the history
Feature 48
  • Loading branch information
mebitek authored Jan 30, 2024
2 parents f4240c8 + 9fceb0e commit 826d2e3
Show file tree
Hide file tree
Showing 62 changed files with 724 additions and 204 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

# 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)
- 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
- 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
- random generator: random seed just on init method
Expand Down
2 changes: 1 addition & 1 deletion src/apps/sequencer/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/apps/sequencer/engine/Clock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 5 additions & 2 deletions src/apps/sequencer/engine/CurveTrackEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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, _currentStepFraction);
float value = evalStepShape(step, _shapeVariation || fillVariation, fillInvert, _currentStepFraction, _sequenceState.direction());
value = range.denormalize(value);
_cvOutputTarget = value;
}
Expand Down
4 changes: 4 additions & 0 deletions src/apps/sequencer/engine/CurveTrackEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 14 additions & 0 deletions src/apps/sequencer/engine/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "core/Debug.h"
#include "core/midi/MidiMessage.h"
#include "ui/ControllerManager.h"

#include "os/os.h"

Expand Down Expand Up @@ -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)) {
Expand Down
7 changes: 6 additions & 1 deletion src/apps/sequencer/engine/Engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -259,4 +262,6 @@ class Engine : private Clock::Listener {
std::array<float, CvOutput::Channels> _cvOutputOverrideValues;

MessageHandler _messageHandler;

bool _deviceConnected = false;
};
15 changes: 15 additions & 0 deletions src/apps/sequencer/engine/NoteTrackEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
18 changes: 18 additions & 0 deletions src/apps/sequencer/engine/SequenceState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ void SequenceState::calculateNextStepFree(Types::RunMode runMode, int firstStep,
} else {
++_nextStep;
}
_direction = 1;
break;
case Types::RunMode::Backward:
if (_step <= firstStep) {
Expand All @@ -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:
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
}
}
2 changes: 1 addition & 1 deletion src/apps/sequencer/engine/SequenceState.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }


Expand Down
4 changes: 2 additions & 2 deletions src/apps/sequencer/engine/generators/Generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ 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<CONFIG_STEP_COUNT> &selected) {
switch (mode) {
case Mode::InitLayer:
initLayer(builder);
return nullptr;
case Mode::Euclidean:
return generatorContainer.create<EuclideanGenerator>(builder, euclideanParams);
case Mode::Random:
return generatorContainer.create<RandomGenerator>(builder, randomParams);
return generatorContainer.create<RandomGenerator>(builder, randomParams, selected);
case Mode::Last:
break;
}
Expand Down
2 changes: 1 addition & 1 deletion src/apps/sequencer/engine/generators/Generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<CONFIG_STEP_COUNT> &selected);

protected:
SequenceBuilder &_builder;
Expand Down
32 changes: 22 additions & 10 deletions src/apps/sequencer/engine/generators/RandomGenerator.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#include "RandomGenerator.h"

#include "core/utils/Random.h"
#include <bitset>
#include <cstddef>
#include <ctime>


RandomGenerator::RandomGenerator(SequenceBuilder &builder, Params &params) :
RandomGenerator::RandomGenerator(SequenceBuilder &builder, Params &params, std::bitset<CONFIG_STEP_COUNT> &selected) :
Generator(builder),
_params(params)
_params(params),
_selected(selected)
{
update();
}
Expand Down Expand Up @@ -59,26 +61,36 @@ 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;
}
}
}

int bias = (_params.bias * 255) / 10;
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));
}
}
}
4 changes: 3 additions & 1 deletion src/apps/sequencer/engine/generators/RandomGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Generator.h"

#include "core/math/Math.h"
#include <bitset>

class RandomGenerator : public Generator {
public:
Expand All @@ -23,7 +24,7 @@ class RandomGenerator : public Generator {
uint8_t scale = 5;
};

RandomGenerator(SequenceBuilder &builder, Params &params);
RandomGenerator(SequenceBuilder &builder, Params &params, std::bitset<CONFIG_STEP_COUNT> &selected);

Mode mode() const override { return Mode::Random; }

Expand Down Expand Up @@ -64,4 +65,5 @@ class RandomGenerator : public Generator {
private:
Params &_params;
GeneratorPattern _pattern;
std::bitset<CONFIG_STEP_COUNT> &_selected;
};
23 changes: 15 additions & 8 deletions src/apps/sequencer/model/BaseTrackPatternFollow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Types::PatternFollow>((static_cast<int>(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) {
Expand Down
Loading

0 comments on commit 826d2e3

Please sign in to comment.