[DRAFT]: Add Sequencer Modes: Step & Pulse#4143
[DRAFT]: Add Sequencer Modes: Step & Pulse#4143solaris76 wants to merge 27 commits intoSynthstromAudible:communityfrom
Conversation
New Features: - Step Sequencer: 16-step analog-style sequencer (SH-101/TB-303 inspired) - Pulse Sequencer: 8-stage euclidean/polyrhythmic sequencer (Metropolix inspired) - Control Pads: 16 configurable performance controls (Clock Div, Octave, Transpose, Direction, Scenes, Generative) - Pattern Save/Load: Mode-specific folders with compact HEX encoding - Song Persistence: All sequencer states preserved in song files Architecture: - RAII design with smart pointers - Factory pattern for mode management - Isolated in src/deluge/model/clip/sequencer/ - Minimal changes to existing Deluge code - Follows Deluge conventions (keyboard layout system, file formats) Status: Alpha 0.6 - Core functionality working, ready for community testing
- Scene XML wasn't properly closing tags
- Fixed closeTag() to closeTag("scene")
- Should resolve E365 parsing error when loading patterns
- Fix duplicated sequencerMode_->readFromFile() calls in instrument_clip.cpp - Simplify XML parsing structure to properly handle mode attribute and delegate child elements - Remove redundant error handling blocks that were causing CI job failures - Clean up unused sequencer mode folder logic in save_pattern_ui.cpp Resolves CI job failure ID: 52646064856
- Fix StepSequencerMode::writeToFile() to specify 'stepSequencer' tag name in closeTag() - Fix PulseSequencerMode::writeToFile() to specify 'pulseSequencer' tag name in closeTag() - Ensures proper XML serialization when saving sequencer mode data
- Fix StepSequencerMode::writeToFile() to use writeAttributeHexBytes() instead of manual writer.write() - Fix PulseSequencerMode::writeToFile() to use proper XML serialization methods - Fix SequencerControlState::writeToFile() to use writeAttributeHexBytes() for padData - Add proper writeOpeningTagEnd() and writeClosingTag() calls - Add missing #include <vector> for dynamic byte arrays These fixes resolve E365 XML corruption errors when saving/loading songs with sequencer mode data. The XML output is now properly formatted with correct tag structure.
- Add sequencer mode processPlayback() call to InstrumentClip::processCurrentPos() - Sequencer modes now replace normal note row processing when active - This enables actual sound generation from step sequencer and pulse sequencer modes - Fixes issue where sequencer modes were not playing any notes The sequencer mode playback integration was missing, causing sequencer modes to render but not generate any audio. Now when a sequencer mode is active, it completely replaces the linear clip playback with pattern-based sequencing.
- Integrate sequencer mode processing into Session::doTickForward() alongside arpeggiator - Add sequencer mode processing loop that calls processPlayback() for active sequencer clips - Include proper timing integration with swungTicksTilNextEvent for efficient scheduling - Add necessary include for SequencerMode class in session.cpp - Skip normal note row processing when sequencer mode is active in InstrumentClip - Remove duplicate sequencer processing from InstrumentClip (now handled at session level) This completes the sequencer playback integration, enabling step sequencer and pulse sequencer modes to generate notes during playback. The architecture follows Deluge patterns by handling sequencer processing in the main session loop similar to how the arpeggiator is processed.
- Add PATTERNS/SEQUENCER/STEP/ and PATTERNS/SEQUENCER/PULSE/ folder support - Update SavePatternUI to detect sequencer modes and save to appropriate folders - Update LoadPatternUI to load from sequencer-specific folders instead of PATTERNS/MELODIC/ - Create sequencer folders automatically when saving patterns - Provide clear titles: 'Save Step Pattern' and 'Save Pulse Pattern' This organizes sequencer patterns separately from regular melodic patterns, making it easier to manage and find sequencer-specific content on the SD card.
- Correct indentation in sequencer mode reading code - Fix misplaced closing brace that was causing format check failure - Ensure proper tab indentation follows Deluge code style This resolves the CI formatting check failure in job 52668798897.
- Fix method signatures and parameter alignment across all sequencer files
- Standardize if-statement formatting: if (condition) action; → if (condition) newline action;
- Fix switch statement formatting with proper case indentation
- Reorder includes alphabetically within groups
- Fix template formatting: template<T> → template <T>
- Standardize else formatting: } else { → } newline else {
- Fix namespace formatting and add closing comments
- Align method call parameters properly
- Fix enum and struct member alignment
- Standardize comment spacing and alignment
- Fix macro indentation in registration code
- Resolve all clang-format violations for CI compliance
These changes ensure the code passes automated formatting tests while
maintaining functionality and compilation compatibility.
…cer data - Add stopAllNotes() method to SequencerMode base class and implementations - Integrate sequencer note cleanup with InstrumentClip::expectNoFurtherTicks() - Enhanced cleanup() methods to properly stop notes during mode switching - Fix XML serialization issues causing E365 errors: - Use self-closing tags for sequencer elements (stepSequencer, pulseSequencer) - Convert scene data to attributes for consistent XML structure - Fix XML/JSON format mismatch in control columns parsing - Proper tag delegation following arpeggiator pattern - Add safety checks for empty padData and large scene data - Preserve both linear note data AND sequencer data in song files - Add robust error handling for hex data parsing - Ensure backward compatibility with existing song files This resolves hung notes when stopping playback and eliminates E365 XML corruption errors when saving/loading songs with sequencer mode data. Both linear clips and sequencer modes now coexist properly.
Normal clips notes no longer play over Sequencer modes. Key fixes: - Implemented proper sequencer mode caching to preserve data - Fixed hasSequencerMode() logic to prevent interference with piano roll - Sequencer instances now cached when switching modes instead of destroyed - Step and pulse sequencer data preserved across mode switches - Updated default patterns to all gates OFF for cleaner user experience - Removed arp-influenced values from default pattern detection - Fixed XML serialization issues and hung note problems - Added comprehensive note cleanup for cached sequencer modes
…equencer data currently) - Modified InstrumentClip::copyBasicsFrom() to preserve sequencer mode type during clip duplication - Duplicated clips maintain their sequencer mode (step/pulse) but get default patterns - Cached sequencer modes are also recreated with defaults - This ensures consistent behavior: duplicated clips have the same mode but clean patterns - Future enhancement: implement proper clone() methods for full pattern duplication
- Move sequencer mode registration from static initialization to lazy initialization - Prevents interference with Deluge's custom memory allocator during startup - Fixes memory corruption in emptySpaces tracking that caused M005 errors - No changes to InstrumentClip core functionality - Sequencer modes (step_sequencer, pulse_seq) now register safely on first access Changes: - sequencer_mode_manager.cpp: Added lazy registration in instance() method - step_sequencer_mode.cpp: Removed static registration lambda - pulse_sequencer_mode.cpp: Removed static registration lambda
|
Really interesting stuff! I spent a bit of time testing the step sequencer yesterday. I think I saw two technical issues:
I find the UX quite hard to approach in this current iteration since they are quite complicated sequencers. I would need to spend a lot more time with them to come up with actional UX feedback, tho. My most immediate dissonance in the step sequencer is that the steps always have a note "pitch" selected, even when they are "off". I would expect to be able to press a note pitch to turn it off and for that column (octave buttons, etc.) to be visually muted to tell them apart more easily. It might also make more sense to have color coded "pages" of the step sequencer to have to columns represent velocity, gate, random of that step in addition to pitch? The <> encoder would be a pretty handy tool for that. Would it make sense to split this PR into separate sequencers to not bundle all the UX work together, blocking each other? |
Awesome, thanks for taking the time to review, much appreciated. You're right re the step seq, I should dim the step if inactive, will take a look. Like the idea of multiple pages too, will see what I can do. Tbh the step sequencer (and the generative one) were extra ones I added so could do with a bit more review/improvement. Thanks again. |
74d985c to
ff1b6a7
Compare
- Dim SKIP steps to 10% brightness (entire column) - Dim OFF step note pads to 10% brightness - Fix red indicator not showing on steps following SKIP steps - Reset to first step when playback starts - Clear white progress column when initializing sequencer modes (step, pulse) - Make octave pads bright white only when step type is ON, dim for OFF/SKIP
ff1b6a7 to
b0b6c0b
Compare
|
Pushed an update that should fix those issues above (removed the generative seq for now), and dimmed the column when steps are skipped so should be a bit clearer. Thanks @MrHaila |
- Add Shift key check in step sequencer handlePadPress to allow synth parameter editing - Add Shift key check in pulse sequencer handlePadPress to allow synth parameter editing - When Shift is pressed, pad presses return false so instrument clip view can handle them
- Move static lastRefreshTick to member variable in pulse sequencer - Handle note slot exhaustion: stop oldest note when all slots full - Complete cleanup: reset all state variables in both sequencers - Clear scale notes array in step sequencer cleanup - Reset all note tracking arrays in pulse sequencer cleanup - Initialize lastRefreshTick in pulse sequencer initialize
- Add include for sequencer_mode.h - Process sequencer modes in arrangement mode, similar to session mode - Fixes sequencer mode clips not working in arrangement mode
|
I pulled the latest and poked around some more with proper thought. Bugs:
Suggestions:
Thinking about the step sequencer... I would suggest moving the octave buttons below the step gate buttons and make the up/down encoder scroll the whole view instead of just the note grid. That way, I could get all my notes of a default scale to be visible at once without scrolling. I think it is more important for me to be able to see where the notes are vs having the octave buttons always on top. Again, thanks for working on this! I would be extremely happy to not have to buy more gear to be able to explore and practice with more sequencer types. So at least I can help alpha test this stuff. I haven't really interrogated the pulse sequencer at all since the UX is so overwhelming. Poking the step sequencer first. |
|
I noticed that the step sequencer does not really play well together with the song view, and in fact has its own concept of scenes. To me, it would feel more intuitive to not have scenes at all and instead use the default Deluge clips feature for the same purpose. I would like to be able to duplicate a currently playing step sequencer in the song view and start editing it like I would a piano roll clip. Currently, it initializes an empty one instead of duplicating the contents. Related bug: duplicating a step sequencer in the song view and hold+select encoder on the newly duplicated row (that is not playing) to change the instryment causes the instrument to go quiet. Same action works on the enabled row that is playing notes, so possibly this has something to do with editing a row that is disabled. Editing the instrument on the enabled row fixes the sounds or stop/play also fixes it. |
|
One more: entering the step sequencer from song view while playing causes the song view play head to get stuck on top of the step sequencer. |
|
Thanks again @MrHaila
This isn't really how an analogue sequencer works which is what this concept is based on (inspired by my Korg SQ1). I also need to see what note I have selected and or whether I have a note on/off. Given we can have up to 12 notes in a scale this would provide poor usability as i can't at a glance see whether a note is selected or not and or what note i have selected is then enabling a step. Eg I may re-enable a step and wonder why nothings playing if i've disabled a note.
I've added shift + vertical to shift octaves. You also have the octave/transpose control column option for this. this way you have much more control. Hold an empty pad on the two rightmost columns, choose Octave with <>, then choose amount +/- with vert encoder (press encoder to set toggle/momentary). You have much more usable control this way and this is mainly based on performance.
Highlighting the step/note in our case makes the most sense to me as the white bar displays where the Deluge is in the clip. The sequencers have their own independent lengths so the white position bar and highlighted notes are not the same. Also, having the white bar jump around on a random order would be a bit odd in my view and also would take up too much of the UI.
Hold the random button and turn the Vertical encoder. You can set the random %. You might want to set one of the pads to Evolve which will only shift notes at lower %'s. As a note, all the right two column pads are configurable. e.g. hold an unlit pad, turn <> encoder to choose action, then vert encoder for value/percentage or press to toggle momentary/toggle. This way you can completely configure the right column to your needs.
Agree. Will Update.
This could work. Maybe hold note pad + <> for Velocity, Vert for gate length and select for note probability?
Don't agree here. I personally think we need to ensure we still know what the step note settings are even if step is skipped. However I have added this functionality when you change the total length of the sequencer using Shift + <>.
I think would add too much complexity to song view personally. You dont see the note probably etc from clip view so think it would be odd to have this here. |
The scenes feature in my view is by far the most useful of the sequencers and really sets them apart from others. You can save a state with random/octave/transpose then go back to where you were, great for trying variations without losing your prior one. You can have up to 8 scenes per sequencer. That said, you dont need to use the scenes like this and can still do as you describe. I do need to look more at song & arrange mode though and the implications of the sequencers there as agree might not be optimal currently. |
- Fix timing issue: advance step at START of boundary, refresh UI before processing - Optimize memory: use smaller integer types (uint8_t/int8_t/int16_t) for step sequencer state - Remove old format fallback handling for numActiveSteps - Simplify step advancement logic to ensure correct red highlight on step after skip
- Restructure step advancement to happen at START of boundary (not end) - Fix UI refresh timing to occur after advancement, ensuring async render sees correct currentStep_ - Prevent double-play of first step by only advancing when position has moved forward - Fix red pad highlight after skip steps by refreshing UI immediately after advancing past skip - Ensure currentStep_ stays correct throughout step duration for proper UI highlighting
|
New commit should fix most of those bugs :) Also added the ability to alter sequencer length holding shift + <> like clips. |
- Add probability field to Step struct (0-100%, default 100%) - Add select encoder control for probability adjustment when note pad held - Improve display formatting: show 'Velocity: 100', 'Gate length: 75', 'Probability: 75%' - Fix encoder handling: only adjust velocity/gate/probability when note pad is actually held - Fix pad release tracking to properly clear held pad state - Add helper functions: isNotePadHeld(), displayValue(), clampValue() - Optimize memory: change heldPadX_/heldPadY_ from int32_t to int8_t - Use probability in playback via shouldPlayBasedOnProbability() - Update file persistence to include probability (6 bytes per step with backward compatibility)
- Restore vertical encoder note scrolling when no pad is held - Vertical encoder now: adjusts velocity when pad held, scrolls notes when not held
|
You can now hold a note pad and adjust note settings the three encoders. vertical: Velocity, <> Gate & Select: Iteration/Probability. I've removed all the performance controls form Pulse sequencer and now uses the control columns for all of its settings, so far less intimidating and aligns well with step sequencer. |
- Replace snprintf with intToString for smaller code size - Create specialized display functions (displayVelocity, displayGateLength, displayProbability) - Make isNotePadHeld() and clampValue() inline for performance - Reduce buffer sizes where possible - Remove redundant cfunctions.h include (already via functions.h)
…l columns) - Removed performance control UI (y0, y1, y2, y5, y6, y7) - now handled by control columns - Kept only stage count (y4) and stage enable/disable (y3) controls - Removed unused PlayOrder enum (using control column direction values 0-7) - Removed unused functions: handleNoteSelection, cycleValue, renderRightSideControls, renderPlaybackIndicator - Removed handler functions: handleClockDividerChange, handlePlayOrderChange, handleTransposeChange, handleOctaveChange, resetPerformanceControls, handleVelocitySpread, handleProbability, handleGateLength - Updated code to use control columns for transpose, octave, clockDivider, direction - Added calculateNoteCode() helper to reduce code duplication - Enabled DIRECTION control type support for pulse sequencer - Reduced binary size by ~2KB
- Remove redundant performance controls, use control columns exclusively (like step sequencer) - Add hold note + encoder functionality: velocity (horizontal), gate (vertical), probability/iterance (select) - Add Shift + horizontal encoder to adjust sequence length (1-8 stages) - Standardize encoder behavior across both sequencers to match piano roll view interaction patterns
- Optimize PulseSequencerMode::StageData: use uint8_t/int8_t instead of int32_t (saves ~224 bytes per instance, ~512 bytes binary size) - Standardize string formatting: replace sprintf/snprintf with intToString + memcpy for consistency - Fix XML save/load: correct offset calculations and array sizes - Add play order state to XML persistence for both sequencers - Fix pulse sequencer rendering: inactive stages set to 0 brightness like step sequencer - Add copyFrom method for proper sequencer data duplication when cloning clips - Ensure duplicated clips can play immediately with initialized_ flag
|
Thanks for all the replies and fixes! Another weekend, another round of testing. I'm trying to focus on digging up bugs and interesting edge cases. Compliments:
Bugs:
UX suggestions:
Of the basic UX now, I think my only major concern is around how the note pitch being displayed while the gate is not "on". I understand you referencing physical gear but I've never actually touched one so unfortunately I can only comment from a synth noob first impressions PoW 🙂 Maybe this is an interesting use case: I jammed around a very simple sequence that combined a few piano roll clips and one step sequencer clip in an infine loop. When switching between the clips after a while, I'm pretty disoriented on what clip is playing what because of the polymeters and how it's easy to forget instrument assignments on the Deluge. In this scenario, seeing only the notes that are active (pic 1)... ...makes it easier for my brain than the same clip but scrolled by two clicks on the up/down encoder (pic 2). Never mind the white pads. That's a stuck playhead from song view. Here, I intentionally shifted all "off" and "skip" notes to the top so they would not be visible (pic 1), leaving only active notes and visually-off pads. I find this simplicity really helpful. In the latter image, I have to look at the top pad of a column and the bottom pad of a column and combine the info from the two to know what the column will sound like, because my eyes read the screen top->bottom instead of bottom->up. So far, I have not found value in having a visible note be assigned at the same time as the step is "off" or "skipped". Maybe that's just something I don't know how to benefit from because I've never had a hardware step sequencer like this? Looking at this with the context that I have of other Deluge UX, I think I would prefer to get the same functional states by:
This way, interacting with the steps would require less button presses as multiple actions can combine into one press in the case of "set this step to 'on' and 'F2' regardless of its current state". I would argue it also requires less learning because that's how the piano roll interactions work. ...but I fully appreciate that this suggestion is not based on previous experiences with these types of sequencers. Please excuse my ignorance 🙏 And again, thanks for the work on this. I'm having a blast just playing around with it! |
…work Integrates the Lanes generative sequencer as a selectable sequencer mode within the Sequencer Modes framework (PR SynthstromAudible#4143), available alongside Step Sequencer and Pulse Sequencer via the Clip Type menu. - LanesSequencerMode wraps LanesEngine and implements SequencerMode interface - 8 independent parameter lanes (trigger, pitch, octave, velocity, gate, interval, retrigger, probability) with polymetric phasing - Performance mode toggle via Cross-Screen button for control column access (transpose, octave, evolve, randomize, reset, scenes) - Adaptive evolve: gentle drift at low %, chaotic mutations at high % - Full playback with gate/retrigger/tie/legato state machine - Undo/redo via ConsequenceLanesChange snapshots - Serialization within the sequencer mode XML format - Lane editor menus (track length, base note, interval range) Based on PR SynthstromAudible#4143 (sequencer-modes-pr) by @seangoodvibes


[DRAFT] Sequencer Modes: Alternative Pattern-Based Sequencing for Deluge
Overview
This PR adds alternative sequencer modes to the Deluge - new ways to create and perform patterns beyond the traditional piano roll. Think of them as different "views" of musical material that complement the existing linear clip system.
Status: Alpha 0.7 - Core functionality working, ready for testing and feedback
What This Adds
Two New Sequencer Modes
1. Step Sequencer 📊
2. Pulse Sequencer 🌀
Performance Controls (Control Pads)
Configurable sidebar columns (x16-x17):
Each pad is individually configurable with Toggle/Momentary modes.
Pattern Save/Load System
Patterns save to mode-specific folders:
PATTERNS/MELODIC/SEQUENCER/STEP/PATTERNS/MELODIC/SEQUENCER/PULSE/PATTERNS/MELODIC/(unchanged, backward compatible)Compact HEX encoding following Deluge conventions (like
noteDataWithSplitProb)Song files preserve all sequencer mode states + piano roll data
How It Works
Mode Selection
Access via Select Encoder menu:
Key Design Principles
Current Implementation Status
✅ Working Features
Core Sequencers:
Control System:
Persistence:
UI/UX:
🚧 Known Limitations
Architecture
RAII Design
All sequencer modes use RAII (Resource Acquisition Is Initialization):
File Structure
Integration Points
Modified files (minimal changes to existing code):
instrument_clip.h/.cpp- Added sequencer mode managementinstrument_clip_view.cpp- Mode rendering/input handlingsave_pattern_ui.cpp- Folder routing for sequencer patternsload_pattern_ui.cpp- Folder routing for loadingNew files (isolated sequencer code):
File Format
Pattern Files
Step Sequencer Pattern:
Encoding:
stepData: 3 bytes per step (noteIndex, octave+3, gate)stageData: 7 bytes per stage (gate, note, octave, pulseCount, velocity, probability, gateLength)padData: 9-10 bytes per pad (y, x, type, valueIndex, mode, active, sceneValid)noteDataWithSplitProbHEX encoding conventionCredits
Design & Implementation: Chris Griggs (@solaris76)
Built on: Deluge Community Firmware
Request for Feedback
This is an alpha release - please test and provide feedback on:
How to provide feedback:
Thank You
Huge thanks to the Deluge Community Firmware team for creating such an extensible platform, and to everyone who tests and provides feedback on this feature!
🎹 Happy sequencing! 🎹