GPU‑accelerated MIDI‑driven visualizer and mask generator for generative AI workflows.
Midiphoria has two primary modes:
- Live preview: OpenGL window reacts to incoming MIDI in real time.
- Deterministic export: render fixed‑resolution frames or an MP4 at a chosen FPS from:
- a recorded MIDI session (
.jsonl), or - a MIDI file (
.mid, tempo‑aware).
- a recorded MIDI session (
A mapped MIDI note (or CC) drives a global brightness visual:
- No input → black screen.
- Note‑on → white screen while held.
- Velocity sensitivity optional: brightness follows velocity/value.
- Global ADSR envelope optional (default is instantaneous on/off).
- Python 3.10+
- MIDI backend:
python-rtmidi(for live input)
Optional tools depending on features:
- MP4 export:
ffmpeg - PNG frames:
Pillow(pip install Pillow) - “Audio from MIDI” in MP4:
fluidsynth+ a SoundFont (.sf2)
Create and activate a virtual environment:
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtInstall Midiphoria into the environment (recommended):
pip install -e .If you don’t want editable install, run from the repo root with:
PYTHONPATH=src python -m midiphoria.app --helpList MIDI input ports:
python -m midiphoria.app --list-portsStart the visualizer (auto‑opens the first port if any):
python -m midiphoria.appUseful flags:
# Any note triggers, binary on/off
python -m midiphoria.app --trigger-mode all_notes
# Color-per-note + velocity sensitivity + ADSR
python -m midiphoria.app --color-mode --velocity-sensitive --attack 0.05 --decay 0.1 --sustain 0.7 --release 0.2
# Inverted mode with rotating hues (psychedelic!)
python -m midiphoria.app --color-mode --invert --hue-mode rotate --hue-speed 0.2
# Activity-driven hue shifts (color changes with each note)
python -m midiphoria.app --color-mode --hue-mode activity --trigger-mode all_notes
# Preconfigure a drum note-set
python -m midiphoria.app --trigger-mode note_set --note-set "36,38,42,46"Record incoming MIDI to a JSONL file (includes a snapshot of the startup state: mapping/mode/ADSR/color/velocity):
python -m midiphoria.app --record recordings/take01.jsonlExport frames:
# PPM frames (no extra deps)
python -m midiphoria.app --export-recording recordings/take01.jsonl --fps 24 --width 512 --height 512 --out-dir frames --frame-format ppm
# PNG frames (requires Pillow)
python -m midiphoria.app --export-recording recordings/take01.jsonl --fps 24 --width 512 --height 512 --out-dir frames --frame-format pngExport MP4 (requires ffmpeg):
python -m midiphoria.app --export-recording recordings/take01.jsonl --fps 24 --width 512 --height 512 --mp4 out.mp4State snapshot behavior:
- By default,
--export-recordingapplies the recorded state snapshot. - Use
--ignore-recording-stateto ignore the snapshot and use only CLI flags. - You can also override specific fields on export (e.g.
--color-mode, ADSR flags).
Basic:
python -m midiphoria.app --export-midi-file song.mid --fps 24 --width 512 --height 512 --out-dir frames --frame-format ppmWith visual parameters:
python -m midiphoria.app --export-midi-file song.mid --fps 24 --width 512 --height 512 --mp4 out.mp4 \
--color-mode --velocity-sensitive --attack 0.03 --decay 0.08 --sustain 0.7 --release 0.12Duration control:
--midi-duration events(default): end time is the last note/CC event (+--tail)--midi-duration file: end time is the full MIDI file length (+--tail)
python -m midiphoria.app --export-midi-file song.mid --midi-duration file --fps 24 --width 512 --height 512 --mp4 out.mp4Channel filter (repeatable, 1–16):
python -m midiphoria.app --export-midi-file song.mid --midi-channel 10 --fps 24 --width 512 --height 512 --out-dir framesMP4 containers can’t “play” a raw .mid; MIDI must be synthesized to audio first.
python -m midiphoria.app --export-midi-file song.mid --fps 24 --width 512 --height 512 --mp4 out.mp4 --audio song.wavRequires fluidsynth + a SoundFont .sf2:
python -m midiphoria.app --export-midi-file song.mid --fps 24 --width 512 --height 512 --mp4 out.mp4 \
--audio-from-midi --soundfont /path/to/soundfont.sf2Ubuntu/Debian install:
sudo apt update
sudo apt install fluidsynth fluid-soundfont-gmCommon locations on Ubuntu:
/usr/share/sounds/sf2//usr/share/soundfonts/~/.local/share/soundfonts/
Find SoundFonts:
find /usr/share ~/.local/share -type f \( -iname "*.sf2" -o -iname "*.sf3" \) 2>/dev/nullWhen MIDI events fall between frames, choose how to sample:
--shutter sample: sample envelope at--sample-at start|center|end(defaultend)--shutter max: preserves short hits better at low FPS (--subsamples N)--shutter avg: exposure-like averaging (--subsamples N), may create intermediate grays for binary gates
Ftoggle fullscreenDtoggle debug overlay (shows events/params)Ltoggle learn mode (next note/CC maps to trigger)Ncycle trigger mode: mapped → all notes → note setAtoggle add-to-set learn (next note adds to note set)Cclear note setKtoggle color mode (notes map to hues)Vtoggle velocity sensitivityItoggle invert mode (white background, dark on activity)Hcycle hue mode: static → rotate → activityRreset ADSR to defaults1/2attack −/+3/4decay −/+5/6sustain −/+7/8release −/+9/0hue speed −/+Escquit
ModuleNotFoundError: No module named 'midiphoria': run from repo root afterpip install -e ., or usePYTHONPATH=src.RuntimeError: fluidsynth not found in PATH: install FluidSynth (sudo apt install fluidsynth) or provide audio via--audio.- MP4 export fails: confirm
ffmpegis installed (command -v ffmpeg).
- Trigger modes:
mapped: respond to one learned note or CC.all_notes: any note_on/off drives the gate; overlaps keep the screen on.note_set: only notes in the learned set drive the gate.
- Visual modes:
invert: flip output (white background, darkness on activity) - useful for inverse masks.hue-mode static: each note has a fixed color based on pitch.hue-mode rotate: colors shift continuously over time.hue-mode activity: each note-on nudges the hue forward.
