MSeq
mseq is a lightweight MIDI sequencer framework written in Rust. It provides a flexible core for building sequencers that can run in standalone, master, or slave mode, with synchronization over standard MIDI clock and transport messages.
Features
- Real-time MIDI clock generation and synchronization
- Master/slave transport control with Start/Stop/Continue handling
- Flexible [
Conductor] trait for defining sequencer logic - Easy-to-implement tracks via the [
Track] trait - Thread-safe, minimal core designed for real-time responsiveness
- Step-based deterministic tracks with [
DeteTrack]
Overview
The sequencer is driven by a user-provided [Conductor] implementation, which defines how the sequencer initializes, progresses at each clock tick, and reacts to external MIDI messages.
- No input → runs standalone with its internal clock and transport, generating MIDI clock and transport messages but ignoring external MIDI input.
- Master mode → runs with its internal clock while also processing incoming MIDI events (except for external clock/transport).
- Slave mode → synchronizes playback to an external MIDI clock and responds to Start/Stop/Continue messages, dynamically adjusting BPM to match the clock source.
Conductor Trait
A Conductor defines how your sequencer behaves:
- [
Conductor::init] → called once at startup to initialize state and produce initial [Instruction]s (e.g., send program changes or reset messages). - [
Conductor::update] → called at every clock tick to advance the sequencer state and emit the instructions for that tick (e.g., note on/off events). - [
Conductor::handle_input] → called when a new [MidiMessage] arrives, allowing the conductor to react to external inputs in real time.
Tracks
Sequencers can also be built around the [Track] trait, which provides a simple interface for describing step-based musical patterns. Each track produces a set of [Instruction]s at a given step.
The provided [DeteTrack] implements a deterministic looping track:
use ;
let mut track = default;
// On each tick, play the instructions for the current step
let instructions: = track.play_step;
This makes it easy to implement custom track types, from simple step sequencers to more complex algorithmic patterns.
Usage
The entry point of the crate is the [run] function:
use ;
;
Examples
You can find ready-to-run examples in the examples directory. directory. They demonstrate various usage patterns, from simple standalone sequencers to multi-track setups.