Plainsong
A keyboard-first music editor that writes plain-text .track files — composed as single notes on a pitch×time sheet, played back at runtime by game-audio. Built for game leitmotifs, and it can sing.
The whole document is a text file. Every visual edit reserializes it; the Source view lets you edit the text directly and an AI can edit either side. Nothing is hidden in a binary blob.
The Sheet
There is one canvas for everything. Each note is a horizontal line: Y = pitch (rounded to semitones), length = duration, color = instrument (hashed to a hue), and a sloped line means glide. The left pitch gutter is a mini piano keyboard — black keys shaded, every C labelled (C4, C5…).
Placing notes
| Action | Result |
|---|---|
| Click empty grid | Places a snap-length note at the snapped position, and auditions it |
| Drag on empty grid | Places a note and sets its length live as you drag (draws it as a stroke) |
| Ctrl + drag on empty grid | Rubber-band multiselect — selects every note the box touches |
| Drag a note body | Moves it (multi-note moves respect overlap rules) |
| Drag a note's right edge | Resizes its duration |
| Ctrl + drag a note | Duplicates the selection and drags the copy |
| Click overlapping notes | Cycles the selection through the stacked notes under the cursor |
| Shift + click a note | Toggles it in/out of the selection |
Snap & stacking
The Snap combo in the toolbar quantizes placement: Off, 1, 1/2, 1/3 (triplet), 1/4 (default), 1/6 (triplet), 1/8. Hold Shift during any click or drag to override snap for free positioning — Shift also disables pitch rounding, so you can place microtonal pitches between the semitone lanes.
Notes on the same pitch that overlap in time are drawn in parallel sub-lanes so nothing hides behind anything else. Draw order is inactive → active → selected.
Keyboard reference
Everything is reachable from the keyboard. Shortcuts are suppressed while a text field is focused.
| Keys | Action |
|---|---|
| Space | Play / Stop |
| Ctrl+Z · Ctrl+Shift+Z / Ctrl+Y | Undo / Redo |
| Ctrl+C · Ctrl+V | Copy · Paste (at the playhead, keeping relative offsets) |
| Ctrl+D | Duplicate selection (offset +0.5 beat) |
| Delete / Backspace | Delete selection |
| Esc | Exit motif/instance editing, else clear the selection |
| ← / → | Seek one beat back / forward (Shift: one bar) — also while playing |
| 1–9 | Focus layer by index piano off |
| PageUp / PageDown | Focus previous / next layer (wraps) |
| Ctrl++ · Ctrl+- · Ctrl+0 | Zoom in · out · reset (both axes together) |
| Ctrl+wheel · Shift+wheel | Zoom · swap scroll axis |
| Ctrl+N/O/S · Ctrl+Shift+S/O | New · Open · Save · Save As · Reload |
| Ctrl+T · Ctrl+W | New document tab · Close tab (asks before discarding the last unsaved one) |
| Ctrl+PageUp / PageDown | Previous / next document tab |
| Ctrl+Q · Ctrl+Shift+Q | Quit · Force-quit |
Real zoom. Ctrl+wheel scales both pixels-per-beat and semitone height, so notes get physically bigger — not just horizontally stretched. Ctrl+0 resets height and re-fits the song horizontally.
Piano mode
Piano (on by default, toolbar toggle) plays the active sound from the computer keyboard. The layout is isomorphic and uses physical key positions, so it is independent of your keyboard layout. Two rules:
Up one QWERTY row = +1 semitone. Right one key = +3 semitones.
The F key is the base pitch (0). The grid below shows the semitone offset of every key before the octave shift:
When Piano is on, the toolbar shows − Oct +N + to shift the whole keyboard by octaves. (The number-row layer-focus shortcuts are disabled while Piano is on, since those keys now play notes.)
Record-driven composition
Rec is on by default — every piano key you press writes a note at the insert line. Toggle Rec off in the toolbar to play without writing (trying out sounds, hunting for the next note). This is the fast way to compose:
- Without playback, a record clock starts with the first held key: the insert line moves in real time, so keys pressed later start later. Press keys together for a chord; release together and the notes end together.
- A held key's note grows with the line; releasing snaps its duration to the grid.
- When the last key is released the line stops at the snapped note end, ready for the next tone.
- If transport is already playing, recording rides the live playhead instead of its own clock.
Singing voices phoneme-synth
Any sound can become a voice that sings its notes' lyrics. In the sound designer, pick the Voice preset: none (synth), male, female, child, robot, or ghost. A voice sound ignores the harmonics/envelope below it — it is synthesized by phoneme-synth instead.
Four voice knobs fine-tune the timbre, each seeded from the preset with a ↺ reset:
| Knob | Range | What it does |
|---|---|---|
| Formant | 0.5 – 2.0 | Shifts the vocal tract size (smaller = more childlike/chipmunk) |
| Tilt | 0.0 – 1.0 | Spectral tilt — brightness vs. darkness of the voice |
| Breath | 0.0 – 1.0 | Breathiness mixed into the tone |
| Vibrato | 0.0 – 1.0 | Pitch vibrato depth |
Lyrics & phonemes
Each note carries a Lyric (in the note inspector), written in phonetic symbols. New notes on a voice sound default their lyric to la so they always sing something. The lyric is drawn right on the note in the sheet.
The phonetic alphabet:
Vowels
aeiou@äöüVoiced
mnlrjq (ng)Noise
sc (sh) fx (ach) zw (v)Plosives
ptkbdgA lyric can hold more than one syllable: spaces separate words and ' separates syllables within a word — hal'lo du sings a two-syllable word and then another word, all sharing the note's duration. A leading ' ties the note to the previous note on the same sound: the run is sung as one legato phrase (syllables flow into each other), drawn as a small tie arc between the notes in the sheet. Without it every note starts a fresh phrase.
Because lyrics live on ordinary notes, a sung line inherits everything else notes have: glide, volume ramps, layer effects, motif reuse. Auditions sing too — placing or pitch-dragging a voice note sings its lyric instead of falling back to the synth.
Leitmotifs
A motif is a named, reusable group of notes — the core feature for game music. Compose it once, drop transposable instances wherever it recurs, and vary it at runtime in the game.
- + Motif in the library creates one and drops you into edit mode: the song fades out and you place the motif's notes in the sheet exactly like normal notes (inside a labelled
Motif: namebox). - Esc, clicking the motif again in the library, or clicking a layer tab leaves edit mode.
- Place on layer drops an instance at the playhead. Instances render as a ♪ chip outlining their span — drag to reposition and transpose, double-click to edit that instance's overrides.
- The instance inspector sets its sound, start, transpose, and tuning. If the instance's sound is a voice, empty motif-note lyrics fill with
la— so a leitmotif can sing.
from/count) to keep only a contiguous range of the motif's notes; trimmed notes render faded while editing.Custom tunings
Beyond 12-TET, define your own scales. In the Tunings library: + Tuning creates one; the inspector sets:
- period — the repeat ratio (2.0 = octave), range 1.1 – 4.0.
- Degrees (ratios) — the scale steps as frequency ratios. Degree 0 is pinned to
1.0 (root); add degrees with + Degree (inserted at the midpoint to the period) and delete with ×.
Assign a tuning per layer with the Tun combo on each layer card, and set that layer's Root frequency in Hz. Motif instances can override tuning too. Deleting or renaming a tuning cascades to everything that referenced it.
Layers & effects
Layers are mix groups, not composition containers — you compose in the sheet with the active sound, and which layer a note belongs to is just an attribute. New documents start with one layer; the concept only surfaces when you need it.
Each layer card (above the grid) has: name (click to focus, Shift/Ctrl+click to toggle active for editing), M mute, S solo, Vol, Pan, Tun, Root. Inactive layers render faded and non-interactive.
The layer inspector adds the runtime semantics that the game switches:
- Loop — a per-layer
@loopwithlenandskip, for looping ambience under non-looping music. - Effects (in order) — an ordered
@effectchain the game can toggle live: Lowpass, Highpass, Echo, Tremolo, Bitcrusher. These are the environment effects (cave, underwater), distinct from a sound's own musical character.
Move a note to another layer with the Layer combo at the top of the note inspector.
Loop region, playhead & audition
- Drag in the ruler (the thin band above the grid) to set a loop region — playback jumps back to its start when it reaches the end. Double-click the ruler clears it. A single click sets the play position. This is separate from the toolbar Loop checkbox, which loops the whole song.
- Follow-playhead: the view auto-scrolls to keep the white playhead line on screen while playing.
- Audition: notes sound as you place, drag, or cross pitches — and every piano key previews the active sound. The sound designer's Preview plays it at 440 Hz.
Sound designer
Selecting a sound opens a full synth editor (non-voice sounds):
- Harmonics — build the timbre partial by partial: each row is a waveform (
sine, triangle, soft_square, hard_square, saw, hill) × a frequency multiplier × a weight. - distortion, volume, sustain, noise.
- attack / decay envelopes, each with an ease-in / ease-out curve editor.
- vibrato and tremolo modulation (depth, rate, waveform) and a pitch slide (time + amount).
- Preview, Duplicate, Delete. Imported sounds are read-only — duplicate to make a local copy.
Source view & cross-file playback
Toggle Source to open a live text editor beside the sheet. Every visual edit reserializes it; every text edit reparses live (errors show in red and block playback). It is the only place to reach the source-only features:
@jump— jump between songs, including across files. A song with jumps plays as a graph: referenced files auto-open as tabs and the active tab follows the currently playing song.- Time signature (
beats_per_bar) — read for drawing bar lines.
Files open as document tabs in the top bar (Ctrl+PageUp/PageDown to cycle). Drop a .track file to open it; drop an audio file (WAV/MP3/OGG/FLAC) to attach it as a clip on a new layer; drop a .mid to import it (below). External changes auto-reload unless you have unsaved edits.
MIDI import
Drop a .mid / .midi file onto the window — or use Import MIDI in the toolbar — to convert a Standard MIDI File into a new song in its own tab. The conversion (from game-audio) carries over:
- one layer per MIDI track, named after the track;
- General MIDI programs and the channel-10 drum map mapped to the bundled sound-bank names;
- note pitch (relative to A4), timing in beats, and velocity as note volume.
The first tempo wins — later tempo changes are dropped with a warning in the status bar. The imported song pulls its instruments from the shared bank via @import sounds/all.track, so provide that bank next to where you save (or remap the sounds) for playback; until then the sheet still shows every note, colored by instrument. Save as .track like any song.
Audio export
Export Audio in the toolbar renders the whole song to a .wav or .mp3 file — pick the extension in the save dialog. Everything is synthesized offline exactly as it plays, so the export is shareable and usable anywhere, not just in a game running game-audio. The .track file stays the editable source; the audio file is the baked result.
Plainsong is free software. Source: gitlab.com/porky11/plainsong. The runtime is game-audio.