diff --git a/src/apps/sequencer/engine/CurveTrackEngine.cpp b/src/apps/sequencer/engine/CurveTrackEngine.cpp index f1220fc5..fb69dbfa 100644 --- a/src/apps/sequencer/engine/CurveTrackEngine.cpp +++ b/src/apps/sequencer/engine/CurveTrackEngine.cpp @@ -55,7 +55,7 @@ void CurveTrackEngine::restart() { _currentStepFraction = 0.f; } -void CurveTrackEngine::tick(uint32_t tick) { +TrackEngine::TickResult CurveTrackEngine::tick(uint32_t tick) { ASSERT(_sequence != nullptr, "invalid sequence"); const auto &sequence = *_sequence; const auto *linkData = _linkedTrackEngine ? _linkedTrackEngine->linkData() : nullptr; @@ -106,13 +106,18 @@ void CurveTrackEngine::tick(uint32_t tick) { _linkData.sequenceState = &_sequenceState; } + TickResult result = TickResult::NoUpdate; + while (!_gateQueue.empty() && tick >= _gateQueue.front().tick) { + result |= TickResult::GateUpdate; _activity = _gateQueue.front().gate; _gateOutput = (!mute() || fill()) && _activity; _gateQueue.pop(); _engine.midiOutputEngine().sendGate(_track.trackIndex(), _gateOutput); } + + return result; } void CurveTrackEngine::update(float dt) { diff --git a/src/apps/sequencer/engine/CurveTrackEngine.h b/src/apps/sequencer/engine/CurveTrackEngine.h index fd6fc161..99aaebda 100644 --- a/src/apps/sequencer/engine/CurveTrackEngine.h +++ b/src/apps/sequencer/engine/CurveTrackEngine.h @@ -20,7 +20,7 @@ class CurveTrackEngine : public TrackEngine { virtual void reset() override; virtual void restart() override; - virtual void tick(uint32_t tick) override; + virtual TickResult tick(uint32_t tick) override; virtual void update(float dt) override; virtual void changePattern() override; diff --git a/src/apps/sequencer/engine/Engine.cpp b/src/apps/sequencer/engine/Engine.cpp index 7a426d7c..bee41346 100644 --- a/src/apps/sequencer/engine/Engine.cpp +++ b/src/apps/sequencer/engine/Engine.cpp @@ -131,8 +131,17 @@ void Engine::update() { // update play state updatePlayState(true); - for (auto trackEngine : _trackEngines) { - trackEngine->tick(tick); + // tick track engines + for (size_t trackIndex = 0; trackIndex < CONFIG_TRACK_COUNT; ++trackIndex) { + auto &trackEngine = _trackEngines[trackIndex]; + uint32_t result = trackEngine->tick(tick); + // update track outputs and routings if tick results in updating the track's CV output + if (result &= TrackEngine::TickResult::CvUpdate && _trackUpdateReducers[trackIndex].update()) { + trackEngine->update(0.f); + updateTrackOutputs(); + updateOverrides(); + _routingEngine.update(); + } } // update midi outputs, force sending CC on first tick diff --git a/src/apps/sequencer/engine/Engine.h b/src/apps/sequencer/engine/Engine.h index 327fda34..966e2971 100644 --- a/src/apps/sequencer/engine/Engine.h +++ b/src/apps/sequencer/engine/Engine.h @@ -15,6 +15,7 @@ #include "MidiPort.h" #include "MidiLearn.h" #include "CvGateToMidiConverter.h" +#include "UpdateReducer.h" #include "model/Model.h" @@ -35,6 +36,7 @@ class Engine : private Clock::Listener { typedef Container TrackEngineContainer; typedef std::array TrackEngineContainerArray; typedef std::array TrackEngineArray; + typedef std::array, CONFIG_TRACK_COUNT> TrackUpdateReducerArray; typedef std::function MidiReceiveHandler; @@ -193,6 +195,7 @@ class Engine : private Clock::Listener { TrackEngineContainerArray _trackEngineContainers; TrackEngineArray _trackEngines; + TrackUpdateReducerArray _trackUpdateReducers; MidiOutputEngine _midiOutputEngine; diff --git a/src/apps/sequencer/engine/MidiCvTrackEngine.cpp b/src/apps/sequencer/engine/MidiCvTrackEngine.cpp index ba387895..a7fb1161 100644 --- a/src/apps/sequencer/engine/MidiCvTrackEngine.cpp +++ b/src/apps/sequencer/engine/MidiCvTrackEngine.cpp @@ -20,10 +20,12 @@ void MidiCvTrackEngine::reset() { void MidiCvTrackEngine::restart() { } -void MidiCvTrackEngine::tick(uint32_t tick) { +TrackEngine::TickResult MidiCvTrackEngine::tick(uint32_t tick) { if (_arpeggiatorEnabled) { tickArpeggiator(tick); } + + return TickResult::NoUpdate; } void MidiCvTrackEngine::update(float dt) { diff --git a/src/apps/sequencer/engine/MidiCvTrackEngine.h b/src/apps/sequencer/engine/MidiCvTrackEngine.h index a472e0cf..8b6f30d9 100644 --- a/src/apps/sequencer/engine/MidiCvTrackEngine.h +++ b/src/apps/sequencer/engine/MidiCvTrackEngine.h @@ -19,7 +19,7 @@ class MidiCvTrackEngine : public TrackEngine { virtual void reset() override; virtual void restart() override; - virtual void tick(uint32_t tick) override; + virtual TickResult tick(uint32_t tick) override; virtual void update(float dt) override; virtual bool receiveMidi(MidiPort port, const MidiMessage &message) override; diff --git a/src/apps/sequencer/engine/NoteTrackEngine.cpp b/src/apps/sequencer/engine/NoteTrackEngine.cpp index dce5bd09..74bffcc9 100644 --- a/src/apps/sequencer/engine/NoteTrackEngine.cpp +++ b/src/apps/sequencer/engine/NoteTrackEngine.cpp @@ -102,7 +102,7 @@ void NoteTrackEngine::restart() { _currentStep = -1; } -void NoteTrackEngine::tick(uint32_t tick) { +TrackEngine::TickResult NoteTrackEngine::tick(uint32_t tick) { ASSERT(_sequence != nullptr, "invalid sequence"); const auto &sequence = *_sequence; const auto *linkData = _linkedTrackEngine ? _linkedTrackEngine->linkData() : nullptr; @@ -156,7 +156,10 @@ void NoteTrackEngine::tick(uint32_t tick) { auto &midiOutputEngine = _engine.midiOutputEngine(); + TickResult result = TickResult::NoUpdate; + while (!_gateQueue.empty() && tick >= _gateQueue.front().tick) { + result |= TickResult::GateUpdate; _activity = _gateQueue.front().gate; _gateOutput = (!mute() || fill()) && _activity; _gateQueue.pop(); @@ -166,6 +169,7 @@ void NoteTrackEngine::tick(uint32_t tick) { while (!_cvQueue.empty() && tick >= _cvQueue.front().tick) { if (!mute() || _noteTrack.cvUpdateMode() == NoteTrack::CvUpdateMode::Always) { + result |= TickResult::CvUpdate; _cvOutputTarget = _cvQueue.front().cv; _slideActive = _cvQueue.front().slide; @@ -174,6 +178,8 @@ void NoteTrackEngine::tick(uint32_t tick) { } _cvQueue.pop(); } + + return result; } void NoteTrackEngine::update(float dt) { diff --git a/src/apps/sequencer/engine/NoteTrackEngine.h b/src/apps/sequencer/engine/NoteTrackEngine.h index aea665e4..46bd9202 100644 --- a/src/apps/sequencer/engine/NoteTrackEngine.h +++ b/src/apps/sequencer/engine/NoteTrackEngine.h @@ -19,7 +19,7 @@ class NoteTrackEngine : public TrackEngine { virtual void reset() override; virtual void restart() override; - virtual void tick(uint32_t tick) override; + virtual TickResult tick(uint32_t tick) override; virtual void update(float dt) override; virtual void changePattern() override; diff --git a/src/apps/sequencer/engine/TrackEngine.h b/src/apps/sequencer/engine/TrackEngine.h index d3e96ec3..2764fb9a 100644 --- a/src/apps/sequencer/engine/TrackEngine.h +++ b/src/apps/sequencer/engine/TrackEngine.h @@ -8,6 +8,7 @@ #include "model/Model.h" #include "core/midi/MidiMessage.h" +#include "core/utils/EnumUtils.h" #include @@ -28,6 +29,13 @@ struct TrackLinkData { class TrackEngine { public: + // Set of updates resulting from calling tick(). + enum TickResult { + NoUpdate = 0, + CvUpdate = (1<<0), + GateUpdate = (1<<1), + }; + TrackEngine(Engine &engine, const Model &model, Track &track, const TrackEngine *linkedTrackEngine) : _engine(engine), _model(model), @@ -61,7 +69,7 @@ class TrackEngine { virtual void reset() = 0; virtual void restart() = 0; - virtual void tick(uint32_t tick) = 0; + virtual TickResult tick(uint32_t tick) = 0; virtual void update(float dt) = 0; virtual void changePattern() {} @@ -98,4 +106,6 @@ class TrackEngine { const TrackEngine *_linkedTrackEngine; }; +ENUM_CLASS_OPERATORS(TrackEngine::TickResult) + #undef SANITIZE_TRACK_MODE diff --git a/src/apps/sequencer/engine/UpdateReducer.h b/src/apps/sequencer/engine/UpdateReducer.h new file mode 100644 index 00000000..e663bbd4 --- /dev/null +++ b/src/apps/sequencer/engine/UpdateReducer.h @@ -0,0 +1,21 @@ +#pragma once + +#include "os/os.h" + +#include + +template +class UpdateReducer { +public: + bool update() { + uint32_t currentTick = os::ticks(); + if (currentTick >= _lastUpdate + Interval) { + _lastUpdate = currentTick; + return true; + } + return false; + } + +private: + uint32_t _lastUpdate = 0; +}; diff --git a/src/core/utils/EnumUtils.h b/src/core/utils/EnumUtils.h new file mode 100644 index 00000000..a2f9aa48 --- /dev/null +++ b/src/core/utils/EnumUtils.h @@ -0,0 +1,8 @@ +#pragma once + +#define ENUM_CLASS_OPERATORS(e_) \ + inline e_ operator&(e_ a, e_ b) { return static_cast(static_cast(a) & static_cast(b)); } \ + inline e_ operator|(e_ a, e_ b) { return static_cast(static_cast(a) | static_cast(b)); } \ + inline e_ &operator|=(e_& a, e_ b) { a = a | b; return a; }; \ + inline e_ &operator&=(e_& a, e_ b) { a = a & b; return a; }; \ + inline e_ operator~(e_ a) { return static_cast(~static_cast(a)); } diff --git a/src/platform/sim/os/os.h b/src/platform/sim/os/os.h index e68c3787..158fd0ba 100644 --- a/src/platform/sim/os/os.h +++ b/src/platform/sim/os/os.h @@ -142,10 +142,10 @@ namespace os { }; namespace time { - inline uint32_t us(uint32_t us) { + constexpr inline uint32_t us(uint32_t us) { return us / 1000; } - inline uint32_t ms(uint32_t ms) { + constexpr inline uint32_t ms(uint32_t ms) { return ms; } }; diff --git a/src/platform/stm32/os/os.h b/src/platform/stm32/os/os.h index 54075982..5924b72e 100644 --- a/src/platform/stm32/os/os.h +++ b/src/platform/stm32/os/os.h @@ -236,10 +236,10 @@ namespace os { }; namespace time { - inline uint32_t us(uint32_t us) { + constexpr inline uint32_t us(uint32_t us) { return (us * configTICK_RATE_HZ) / 1000000; } - inline uint32_t ms(uint32_t ms) { + constexpr inline uint32_t ms(uint32_t ms) { return (ms * configTICK_RATE_HZ) / 1000; } };