Chapter 0 · Setup and Your First Sound

Welcome. By the end of this chapter you’ll have Python and Subsequence installed, your instrument connected, and a four-bar drum loop playing — or saved to a MIDI file you can drop into any DAW. No prior Python experience needed; we’ll explain the few programming ideas as they come up.

If you already have a working Python setup, you can skim to §0.4. If you’re brand new to Python, follow every step in order — it’s about fifteen minutes, once.

0.1 What Subsequence is (and what it isn’t)

Subsequence is an algorithmic sequencer: you describe music with short Python functions, and it turns them into MIDI in real time. The crucial thing to understand before anything else:

Important

Subsequence makes no sound of its own. It sends MIDI — the same note, timing, and controller messages your gear already speaks — to your instrument, which makes the sound. If nothing is connected to receive that MIDI, you’ll hear silence even when everything is working perfectly.

That instrument can be a hardware synth or drum machine, a Eurorack setup, or a software instrument inside a DAW. §0.5 covers both hardware and software routing — pick whichever you have.

You write Subsequence music in plain Python, but you only need a little Python: how to call a function and how to write a list of numbers. Everything else we teach as we go. The payoff for learning that little is large — patterns that evolve, react to the chord underneath them, and never loop quite the same way twice.

0.2 Installing Python and git

Subsequence needs Python 3.10 or newer and git (the installer downloads Subsequence from a git repository). Open a terminal and check what you have:

python3 --version
git --version

If both print a version (Python 3.10+), skip to §0.3. Otherwise, follow the steps for your operating system.

macOS. Install Python from python.org/downloads (avoid the old system Python). If you use Homebrew, brew install python works too. Get git with xcode-select --install.

Windows. Download the installer from python.org/downloads and — this matters — tick “Add python.exe to PATH” on the first screen. Install git from git-scm.com. Windows users can use the py launcher in place of python3 throughout this guide (e.g. py -m venv ...).

Linux. Most distributions ship Python 3. On Debian/Ubuntu also install the venv and pip packages plus git:

sudo apt install python3-venv python3-pip git

Note

On Linux, Subsequence’s MIDI backend may build a small C extension (python-rtmidi) the first time. If installation complains about a missing compiler or headers, install the build tools once: sudo apt install build-essential libasound2-dev libjack-dev.

0.3 A clean workspace: the virtual environment

A virtual environment (venv) is an isolated folder that holds the Python packages for one project, so Subsequence and its dependencies don’t collide with anything else on your system. You make one once.

On macOS / Linux:

python3 -m venv ~/subsequence
source ~/subsequence/bin/activate

On Windows (PowerShell):

py -m venv subsequence
subsequence\Scripts\Activate.ps1

After activating, your prompt shows the environment name (e.g. (subsequence)).

Important

You must activate the venv in every new terminal session before running your music — otherwise Python won’t find Subsequence. The give-away that you forgot is ModuleNotFoundError: No module named 'subsequence'. Just re-run the activate line above.

Note

On Windows, if PowerShell refuses to run the activate script, allow it once with Set-ExecutionPolicy -Scope CurrentUser RemoteSigned and try again.

0.4 Installing Subsequence

With the venv active, upgrade pip and install Subsequence straight from its repository:

python -m pip install --upgrade pip
pip install "subsequence @ git+https://github.com/simonholliday/subsequence@v0.6.2"

This pulls in everything Subsequence needs (the MIDI libraries mido and python-rtmidi, and a few others) automatically. The @v0.6.2 pins the exact version this guide is written for, so the examples match what you see.

Confirm it landed:

python -c "import importlib.metadata as m; print(m.version('subsequence'))"

You should see 0.6.2. If instead you get ModuleNotFoundError, your venv isn’t active — see the box in §0.3.

0.5 Connecting your instrument

Subsequence sends MIDI to a port — a named connection your operating system exposes. The port leads either to a piece of hardware or to a virtual cable that carries MIDI to another app on the same computer. Set up whichever matches your setup; you can always add the other later.

0.5.1 Path A — A hardware synth or drum machine

Connect the instrument by USB or a USB-MIDI interface and switch it on. The operating system lists it as a MIDI port automatically — there’s nothing else to install. Skip ahead to §0.5.3.

0.5.2 Path B — A DAW or software instrument

Software instruments live inside your computer, so you need a virtual MIDI port to carry MIDI from Subsequence to your DAW. Subsequence doesn’t create one itself, so make one at the operating-system level — a one-time setup:

  • macOS: open Audio MIDI SetupWindow ▸ Show MIDI Studio → double-click IAC Driver → tick “Device is online”. The IAC bus now appears as a MIDI port.

  • Windows: install loopMIDI and click + to create a named port.

  • Linux: load the ALSA virtual MIDI module (sudo modprobe snd-virmidi) or wire it up in your JACK/patchbay.

Then, in your DAW, set a track’s MIDI input to that virtual port and arm it, and point its instrument at the track. Subsequence sends to the port; the DAW receives and plays the sound.

0.5.3 Choosing and checking your port

List the output ports Python can see:

python -c "import mido; print(mido.get_output_names())"

You’ll get a list of names, for example ['IAC Driver Bus 1', 'Minilogue']. When you create a Composition, tell it which one to use:

import subsequence
composition = subsequence.Composition(bpm=120, output_device="IAC Driver Bus 1")

Warning

The name must match exactly — Subsequence does not guess from partial names. Copy it verbatim from the list above. If you leave output_device out entirely, Subsequence uses the only port if there’s exactly one, or prompts you to pick when there are several.

Note

On Linux, ALSA port names include trailing numbers (e.g. Minilogue:Minilogue MIDI 1 24:0) that can change between reboots. Re-run the list command if a previously-working name stops matching.

0.6 Your first composition

Here is a complete Subsequence script: a one-bar General MIDI drum loop — four-on-the-floor kick, backbeat snare, and a steady sixteenth-note hi-hat. Save it as hello.py.

import subsequence
import subsequence.constants.instruments.gm_drums as gm_drums

composition = subsequence.Composition(bpm=120)

@composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP)
def drums(p):
    p.hit_steps("kick_1", [0, 4, 8, 12], velocity=100)   # one hit per beat
    p.hit_steps("snare_1", [4, 12], velocity=90)          # backbeat (beats 2 & 4)
    p.hit_steps("hi_hat_closed", range(16), velocity=70)  # every sixteenth

composition.render(bars=4, filename="hello.mid")

Reading it top to bottom:

  • subsequence.Composition(bpm=120) creates the piece and sets the tempo. (To send to a specific instrument, add output_device="..." from §0.5.)

  • @composition.pattern(...) is a decorator — the @ line just above a function. For now, read it as “register the function below as a musical pattern.” Here it says: play on channel 10 (the General MIDI drum channel), make the pattern 4 beats long, and translate drum names via the GM drum map.

  • def drums(p): is your pattern. The p it receives is a builder you paint notes onto.

  • p.hit_steps(name, steps, velocity=...) places a drum hit on specific grid steps. A 4-beat bar is divided into 16 sixteenth-note steps numbered 0–15, so [0, 4, 8, 12] is one kick per beat, [4, 12] is the backbeat, and range(16) is all sixteen hi-hats. velocity is how hard each hit plays (1–127).

  • composition.render(bars=4, filename="hello.mid") writes four bars to a MIDI file — covered next.

Note

Channels are 1-indexed; steps are 0-indexed. MIDI channels run 1–16 to match the labels on hardware (channel 10 = drums). Grid steps, like most things you count in code, start at 0. This trips up everyone once — we’ll come back to it in Chapter 1.

0.7 Rendering it to a file

You don’t need any hardware to check your code works. composition.render() runs the sequencer as fast as possible and writes a standard MIDI file:

composition.render(bars=4, filename="hello.mid")

Run the script:

python hello.py

It produces hello.mid in the current folder. Open that file in any DAW (or a MIDI viewer) and you’ll see the kick, snare, and hi-hat laid out across four bars. render is the quickest way to answer “did my code do what I meant?” — it never waits for real time and never needs an instrument connected.

Tip

Pass bars= to set the length. Without it, render keeps going up to a 60-minute safety cap — handy for generative pieces that would otherwise never end.

0.8 Going live with play()

When you’re ready to hear it through your instrument in real time, swap the render line for play:

composition.play()   # streams MIDI live; press Ctrl-C to stop

play() opens your MIDI port and runs off the wall clock, so the music comes out of your synth or DAW as it happens. Stop it with Ctrl-C. To capture a live take to a file at the same time, start the composition with record=True:

composition = subsequence.Composition(bpm=120, record=True, record_filename="take.mid")

That’s the whole loop you’ll use from here on: write a pattern, render to check it, play to perform it.

0.9 Troubleshooting your first run

Symptom

Fix

ModuleNotFoundError: No module named 'subsequence'

The venv isn’t active. Re-run the activate line from §0.3.

git errors during pip install

git isn’t installed or not on PATH — see §0.2. The install is a git download, not a plain package.

play() hangs with no sound and no prompt

A port was auto-chosen but your instrument isn’t listening. Pass an explicit output_device= (§0.5) and check the DAW track is armed.

It prints a numbered list and waits for input

Several MIDI ports were found. Type the number, or set output_device= to skip the prompt.

No errors, but silence

MIDI is flowing but nothing makes sound. Confirm the port name is exact, the instrument is on the right channel (drums = channel 10), and — for a DAW — the track’s MIDI input is the virtual port and it’s record-armed.

Linux: pip install fails compiling python-rtmidi

Install build headers once: sudo apt install build-essential libasound2-dev libjack-dev.


You now have a working setup and a first loop. In Chapter 1 we’ll stay with this drum pattern and learn the step grid properly — building a groove by hand, shaping velocity, and meeting that 1-indexed-channel / 0-indexed-step distinction head-on.