EuclidianSequencer vs Sequencer8 vs TuringMachine: Understanding the Difference Between These Nallely MIDI Sequencers
EuclidianSequencer generates Euclidean rhythm patterns using a computed deque of 0/1 values based on length and density parameters, Sequencer8 operates as a traditional 8-step step-sequencer with individually activatable steps, and TuringMachine implements a probabilistic 8-bit rotating tape for evolving algorithmic sequences.
The dr-schlange/nallely-midi library provides three distinct virtual sequencer devices for modular MIDI and CV generation. Understanding the difference between EuclidianSequencer, Sequencer8, and TuringMachine is essential for selecting the right rhythmic control strategy, whether you need mathematical rhythm distribution, traditional step programming, or generative evolution.
Core Architecture and Design Philosophy
EuclidianSequencer – Euclidean Rhythm Generation
EuclidianSequencer, defined in nallely/sequencer.py (lines 93–184), implements the Bjorklund algorithm to distribute a specified number of hits evenly across a configurable length. The pattern is computed once—or whenever the length, hits, or shift parameters change—and stored in a deque of binary values. This produces mathematically even rhythmic distributions ideal for drum patterns.
Sequencer8 – Fixed Step Sequencing
Sequencer8, located in the same file (lines 130–214), functions as a traditional analog-style step-sequencer with exactly 8 steps. Step values are stored as direct attributes (step0 through step7), and each step includes an activeN boolean flag for per-step muting. Unlike the algorithmic generation of the other two devices, this sequencer relies on manual value entry and activation control.
TuringMachine – Probabilistic Tape Rotation
TuringMachine, found at lines 15–189 in nallely/sequencer.py, models a finite state machine with an 8-bit memory tape implemented as a deque. On each clock trigger, the tape rotates and optionally mutates based on a probability parameter, creating evolving binary patterns without explicit step programming.
Clock Input and Trigger Behavior
EuclidianSequencer responds to the clock_cv input, advancing an internal step counter on rising edges. The on_clock_rising() method outputs gate and trigger signals whenever the current step value equals 1.
Sequencer8 uses two distinct inputs: play_cv to start or stop the sequence and trigger_cv to advance to the next step. The next_step() method checks current_step_active() (lines 62–64) to determine whether to emit outputs for the current position.
TuringMachine treats trigger_cv as the master clock. Every rising edge executes on_trigger_rising(), which rotates the self.memory deque and potentially flips the newest bit based on the mutation probability setting.
Data Structures and Pattern Storage
- EuclidianSequencer: Stores the computed pattern in a
dequeof 0/1 values. Thecompute_sequence()method (lines 38–47) recalculates the pattern using a floor-based algorithm when parameters change. - Sequencer8: Maintains step values as individual float attributes alongside boolean activation flags. The pattern is not computed but directly stored in object attributes.
- TuringMachine: Uses a
dequerepresenting an 8-bit tape. The memory rotates rather than advancing a playhead, creating a circular shift register effect where bits circulate through the output positions.
Length Configuration and Step Activation
EuclidianSequencer supports length parameters from 1–128 steps and hits from 1–128, allowing sparse or dense rhythmic patterns. No per-step muting exists; the pattern is deterministic based on the Euclidean algorithm.
Sequencer8 is constrained to 8 steps maximum, though the length_cv input (1–8) can shorten the active playback range. Individual steps can be disabled via activeN_cv inputs (lines 55–62), causing the sequencer to skip gates for those positions while maintaining the step values in memory.
TuringMachine has a fixed 8-bit tape length. Instead of step activation, it offers a mutation probability (0.0–1.0) to randomly invert the newest bit when the tape rotates, creating generative variations of the initial pattern.
Output Parameters and Signal Types
EuclidianSequencer provides three primary outputs:
step_out_cv: Current step indexgate_out_cv: High when step value is 1trigger_out_cv: Pulse when step value is 1
Sequencer8 provides extensive I/O for step control:
current_step_cv: Playback position indexedit_step_cv: Editing position indexoutput_cv: CV value of the current steptrig_out_cv: Trigger pulse outputactive0_cvthroughactive7_cv: Per-step activation states
TuringMachine provides binary and integer outputs:
out_main_cv: Current bit value (0 or 1)gate_out_cv: Gate signal for the current bittape_out_cv: 8-bit integer representing the entire tape stateout0_cvthroughout7_cv: Individual bit outputs for each memory cell
Practical Implementation Examples
# EuclidianSequencer – 16-step pattern with 5 hits, shifted by 2 steps
euclid = EuclidianSequencer(length=16, hits=5, shift=2)
for _ in range(20):
for out in euclid.on_clock_rising(1, None):
print(out) # Yields step index, gate, and trigger pulse
# Sequencer8 – 8-step melodic sequence with step 3 muted
seq8 = Sequencer8()
seq8.step0 = 60 # C4
seq8.step1 = 62 # D4
seq8.step2 = 64 # E4
seq8.step3 = 65 # F4 (muted)
seq8.step4 = 67 # G4
seq8.active3 = 0 # Disable step 3
for _ in range(10):
for out in seq8.on_trigger_rising(1, None):
print(out) # Yields step index, CV value, and trigger
# TuringMachine – 8-bit tape with 30% mutation probability
tm = TuringMachine(mutation=0.3)
tm.on_random_rising(1, None) # Seed with random byte
for _ in range(16):
for out in tm.on_trigger_rising(1, None):
print(out) # Yields tape integer, current bit, and individual outputs
Summary
- EuclidianSequencer computes mathematical rhythm patterns using the Euclidean algorithm, ideal for drum sequencing with configurable density and length up to 128 steps.
- Sequencer8 provides traditional step-sequencing with 8 discrete steps and per-step muting, suitable for melodic lines and CV automation where manual control is preferred.
- TuringMachine generates evolving binary patterns through rotating memory and probabilistic mutation, perfect for generative music and algorithmic composition that changes over time.
Frequently Asked Questions
Can I change the sequence length dynamically in all three sequencers?
Only EuclidianSequencer supports runtime length changes up to 128 steps via the length parameter. Sequencer8 allows shortening the active range to 1–8 steps using length_cv but maintains 8 underlying storage slots. TuringMachine has a fixed 8-bit tape that cannot be resized.
Which sequencer supports per-step muting or disabling?
Sequencer8 uniquely provides individual activeN boolean flags for each of its 8 steps, allowing you to mute specific positions without deleting their values. EuclidianSequencer outputs the full computed pattern without per-step gating, while TuringMachine always outputs the complete tape state as a rotating shift register.
How does the TuringMachine create variations without traditional step parameters?
The TuringMachine uses a mutation probability parameter (0.0–1.0) in its on_trigger_rising() method. When triggered, it rotates the 8-bit memory deque and randomly flips the newest bit based on this probability, creating evolving sequences through probabilistic bit manipulation rather than manual step editing.
Where are these sequencers defined in the nallely-midi source code?
All three classes reside in nallely/sequencer.py: TuringMachine is defined at lines 15–189, EuclidianSequencer at lines 93–184, and Sequencer8 at lines 130–214. They inherit from VirtualDevice defined in nallely/core.py and utilize helper functions from nallely/utils.py.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →