Turns Standard MIDI Files into monome Teletype scenes, and streams MIDI live to the module over WiFi.
The monome Teletype is a scriptable Eurorack sequencer. Its
scenes are plain-text files (tt00.txt … tt31.txt) loaded from a USB stick. Writing them by hand
is slow, and there is no native way to get an existing musical idea out of a DAW and onto the
module. This compiles MIDI files into valid scenes.
The Teletype also has exactly one USB port and no network interface, so the second half of this repo is a bridge: a Raspberry Pi Zero W that pretends to be a USB-MIDI device and feeds the module live MIDI from a laptop over WiFi.
Both halves are hardware-verified on a real Teletype.
Pure Swift, no UI, no dependencies. The pipeline is SMF parser → MusicIR → arranger → scene → serializer/validator. It round-trips byte-identical against monome's eight factory scenes, which is the property the test suite is built around.
- Grid resolutions from whole notes to 1/32, including triplets
- 1–4 voices, mapped to CV/TR pairs
- Output targets:
cvtr, Just Friends over i2c, or W/ tape-retrigger over i2c - Scene validation against the module's real limits (line counts, script sizes, pattern banks)
A SwiftUI workbench in a phosphor-terminal theme.
- Workbench — import
.midor open an existingtt##.txt; grid 1/1 → 1/32 incl. triplets; tempo and tap tempo; voice count; per-voice mixer with mute/solo/drag-to-reroute and a grid editor; scene paging; a compare tab; export straight to a mounted USB volume - Live MIDI out — voices map to MIDI channels 1–4. Note-offs fire at 80% of the step so gates actually close and repeated notes retrigger. PANIC releases everything and sends CC-123 four times. Destination picker plus a synth-mute toggle so only the rack sounds
- Track paging — files with more than four tracks page through the mixer; arrangement, audition, MIDI out and export all follow the visible page
- Remote tab — Pi status, USB identity switching, and a remote typing field that types on the Teletype. Disruptive mode switches arm on first tap and fire on the second
- Synth tab — approximation synth for auditioning before committing to hardware, with swing preview
A Raspberry Pi Zero W appliance: RTP-MIDI in over WiFi, USB-MIDI gadget out, plus an HTTP control API. Three switchable USB identities — MIDI device, HID keyboard, or FAT16 flash drive — which together replace the USB-stick-and-keyboard juggling the module's single port otherwise forces.
- macOS 13+ and Swift 5.9 (Xcode 15+)
xcodegen—brew install xcodegen— to build the app- Optional, for live MIDI: a Raspberry Pi Zero W and a monome Teletype
The translator and CLI need none of the hardware.
Engine and CLI:
cd TeletypeKit
swift build
swift test # 66 testsThe macOS app (the Xcode project is generated, not checked in):
cd app
xcodegen generate
xcodebuild -project TeletypeTranslator.xcodeproj \
-scheme TeletypeTranslator -configuration Release \
-derivedDataPath build CODE_SIGNING_ALLOWED=NO build
# the build is unsigned — ad-hoc sign it, then install
codesign --force -s - build/Build/Products/Release/TeletypeTranslator.app
cp -R build/Build/Products/Release/TeletypeTranslator.app /Applications/There is no notarized release build. You build it yourself, or Gatekeeper will complain.
ttx <input.mid> [--out DIR] [--slot N] [--grid N] [--voices N] [--name NAME] [--target T]
| flag | meaning | default |
|---|---|---|
--out DIR |
output directory | current directory |
--slot N |
first scene slot, 0–31 | 0 |
--grid N |
steps per quarter note; 4 = 1/16, 0.25 = whole, 3 = 1/8T |
4 |
--voices N |
max voices, 1–4 | 4 |
--name NAME |
scene title prefix | derived from the filename |
--target T |
cvtr | jf (Just Friends i2c) | w (W/ tape retrig i2c) |
cvtr |
swift run ttx song.mid --out /Volumes/STICK --slot 9 --grid 4 --voices 4Then on the module: ESC → arrow to the scene → ENTER. Loading is what runs its init script.
Drag in a .mid, set grid/tempo/voices, audition it, then either export to a USB volume or turn
on live MIDI and play it straight to the rack.
Raspberry Pi Zero W running Raspberry Pi OS Lite 32-bit (armhf — see the gotchas). Setup is:
flash the image, fill in bridge/pi/firstrun.sh.template with your own WiFi SSID, password and
SSH public key, copy the scripts and systemd units onto the Pi, and enable the services.
The placeholders to replace are __WLANSSID__, __WLANPW__ and __SSHPUB__. The scripts assume
the stock pi account; change the paths in midibridge.service, tt-disk-mode and ttremote.py
if yours differs.
One USB identity is active at a time:
| mode | what the Teletype sees | command |
|---|---|---|
midi (default) |
class-compliant USB-MIDI device, fed from RTP-MIDI | tt-midi-mode |
keyboard |
USB HID keyboard that can type on the module | tt-keyboard-mode |
disk |
FAT16 flash drive holding tt##.txt |
tt-disk-mode |
| method | endpoint | body | does |
|---|---|---|---|
GET |
/status |
— | returns {"mode": …, "udc": "configured" | …} |
POST |
/mode |
{"mode":"midi"|"keyboard"|"disk"} |
switches USB identity |
POST |
/type |
{"text":"MI.$ 1 1","enter":true} |
types text (keyboard mode only) |
POST |
/key |
{"key":"ALT+ENTER"} |
sends a chord (keyboard mode only) |
udc: "configured" means a host has enumerated the gadget.
TeletypeTranslator.app voices → MIDI channels 1–4, gates close at 80% of step
─▶ Network RTP-MIDI session over WiFi
─▶ Pi Zero W pymidi bridge, UDP 5004/5005 → g_midi gadget
─▶ USB ─▶ Teletype USB host ─▶ MI.* ops in the loaded scene
─▶ CV 1–4 pitch + TR 1–4 gates
bridge/tt10.txt is the four-voice scene that receives it; bridge/tt09.txt is a one-voice
MIDI monitor useful for proving the chain works.
Everything here was learned the expensive way on real hardware. If you are building the bridge, read this section first.
- The Pi Zero W is ARMv6. An arm64 image LED-bootloops with the config untouched. Use armhf.
- 2026 Raspberry Pi images ignore
custom.toml(sshanduserconf.txtstill work). Configure WiFi with afirstrun.shinvoked fromcmdline.txt— andcmdline.txtmust stay a single line. - macOS dials RTP-MIDI over IPv6 first. The bridge is IPv4-only, so you get a session that
connects with 0 ms latency and then sits silent. Disable IPv6 on the Pi. Diagnose with
tcpdump -i wlan0 -n 'udp port 5004 or udp port 5005'. - Do not build a composite (multi-function) USB gadget. MIDI+HID in one device enumerates
(
configured) but the Teletype's USB host polls nothing, MIDI stops flowing, and the host stack wedges until the module is power-cycled. One function at a time is a hard constraint — which is why the mode dance exists rather than being an oversight. - The HID keyboard identity is best-effort. The module only polls a freshly enumerated HID
device and stops within seconds. Roughly one enumeration equals one keypress;
ttsendre-enumerates and retries six times and still sometimes fails. A real keyboard is the reliable way to type on a Teletype. - On the module:
ENTERloads a scene and wipes typed state,ALT+ENTERsaves. Unsaved LIVE state dies on reboot. In the scene browser use the arrow keys — the PARAM knob does nothing there.F1–F8fire scripts 1–8, which is a MIDI-free way to self-test a scene. - One MIDI sender at a time. Two senders fight over the same ops. A two-voice arrangement only ever lights two TRs — mixer rows equal voice count.
TeletypeKit/ swift package — the engine, pure logic
Sources/TeletypeKit/ SMF parser, MusicIR, arranger, scene serializer/validator
Sources/ttx/ command-line translator
Tests/ 66 tests, incl. byte-exact round-trip against the corpus
app/ SwiftUI macOS app (xcodegen project)
bridge/pi/ python appliance: RTP-MIDI → USB gadget, mode scripts, systemd units
bridge/tt09-11.txt Teletype scenes that receive the live MIDI
bridge/*/*.ino Teensy 4.1 sketches — experimental wired alternative
corpus/scenes/ monome's 8 factory scenes, round-trip ground truth (CC-BY, see NOTICE.md)
corpus/midi/ small MIDI fixtures
docs/ design spec, implementation plans, Pi bridge research
site/ icon and brand concepts
PROJECT.html long-form project brief
cd TeletypeKit && swift test66 tests, no hardware or network required. The suite's backbone is a byte-exact round-trip of monome's eight factory scenes: parse → model → re-serialize must reproduce the original file byte for byte.
One optional test sweeps a large MIDI library for parser robustness. It skips unless you point it at one:
TELETYPE_MIDI_SWEEP_DIR=~/Music/MIDI swift test --filter MIDIImportRobustnessTestsVersion 0.1 — a personal project, shared as-is. It works, and it is not a product.
- Just Friends pitch is broken. Driving JF over i2c produces sound and CV/TR respond correctly,
but every note comes out at the same pitch.
JF.NOTE's first argument is pitch relative to C3, which was fixed, and the symptom persisted — so the remaining suspects are on JF's side (its left toggle must be onsound, notshape, and Just Type needs JF 4.0+ firmware). Unresolved. - The Teensy sketches are scaffolding, not a finished bridge.
- No notarized build, no installer, no release binaries.
MIT.
The factory scenes in corpus/scenes/ are monome's, redistributed under CC-BY-4.0 — see
NOTICE.md. The Teletype firmware itself is not vendored here.