Public-ready: JSON tune specs, first-class loop API, licence, AGENTS.md
- MidiFile.set_loop(): one call writes LOOP_START/END, RPR LOOP START/END (Reaper) and LoopBegin/End markers - every engine finds its dialect; reader now parses markers and the smoke test asserts the round-trip - tools/make_tune.py: declarative JSON spec -> fully arranged MIDI with style presets (dos_adventure, n64_ambient, chiptune_racing); the agent-friendly front door, no Python required - tunes/specs/: demo tune 04 'Tea Break at the Terminal' (Am/112, loop bars 4-20) generated entirely from JSON - AGENTS.md: spec schema, score grammar, verification loop, looping conventions for game BGM - MIT LICENSE; history audited clean of credentials
This commit is contained in:
1 parent
c3cd91d04f
commit
633eb92352
15 files changed
+387
-9
No files matched your search
@@ -16,34 +16,58 @@ hand-edited hex. If a tune is wrong, you fix a bar of text and rebuild.
|
||||
| 01 | Service Station at 2 AM | DOS adventure main theme (LucasArts-meets-OPL) | Dm / 132 | ~58s |
|
||||
| 02 | Kestrel Over The Fens | N64 ambient exploration theme | G / 72 | ~82s |
|
||||
| 03 | The U's Late Equaliser | chiptune sports/racing theme | Em / 152 | ~44s |
|
||||
| 04 | Tea Break at the Terminal | DOS puzzle-game theme (from JSON spec) | Am / 112 | ~51s |
|
||||
|
||||
All files are Standard MIDI File **format 1**, 480 tpq, General MIDI
|
||||
instruments, with `LOOP_START` / `LOOP_END` markers so a game engine can
|
||||
skip the intro and loop the gameplay section.
|
||||
instruments, with loop markers (`LOOP_START/END`, `RPR LOOP START/END`,
|
||||
`LoopBegin/End`) so a game engine can skip the intro and loop the
|
||||
gameplay section. See [Looping](#looping) below.
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
./tools/build_all.sh # regenerate, verify, render WAVs, audio checks
|
||||
python3 tests/smoke_test.py # library self-test
|
||||
python3 tools/make_tune.py tunes/specs/04_tea_break_at_the_terminal.json --render
|
||||
```
|
||||
|
||||
Rendering needs `timidity` + a GM soundfont (`FluidR3_GM.sf2`). Note:
|
||||
Debian's timidity build has no `-O2` mode — use `-Ow -s 44`.
|
||||
|
||||
**AI agents**: see [AGENTS.md](AGENTS.md) — the JSON spec format is the
|
||||
agent-friendly front door; no Python required to make a tune.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
lib/midikit.py SMF writer + reader/verifier (stdlib only)
|
||||
lib/songkit.py composition helpers: bar grids, OPL arps, vibrato, drums
|
||||
tunes/*.py one script per tune; each self-verifies on run
|
||||
tunes/specs/*.json declarative tune specs -> tools/make_tune.py
|
||||
tools/build_all.sh full pipeline: generate -> verify -> render -> check
|
||||
tools/make_tune.py JSON spec -> fully arranged MIDI (style presets)
|
||||
tools/check_wav.py WAV sanity: clipping, loudness, dead air
|
||||
tests/smoke_test.py library round-trip test (cross-checked with mido)
|
||||
midi/ generated .mid files
|
||||
archive/ the April 2026 wreckage (70-byte "working" MIDIs, rest in peace)
|
||||
```
|
||||
|
||||
## Looping
|
||||
|
||||
Game background music convention: the intro bars play once, then the
|
||||
engine jumps back to the loop point. Declare it once and every marker
|
||||
dialect gets written:
|
||||
|
||||
```python
|
||||
mf.set_loop(bar(4), bar(28)) # Python
|
||||
```
|
||||
```json
|
||||
"loop": {"start_bar": 4, "end_bar": 20} // JSON spec
|
||||
```
|
||||
|
||||
Markers written: `LOOP_START/LOOP_END` (ours), `RPR LOOP START/END`
|
||||
(Reaper), `LoopBegin/LoopEnd` (various engines/trackers).
|
||||
|
||||
## Composing a new tune
|
||||
|
||||
Copy a tune script. Melodies are written as bar grids:
|
||||
@@ -79,3 +103,8 @@ on a peak of 32767.
|
||||
(trailing bends/resets), but "Notes lost totally" is not.
|
||||
- A bar-length linter in the score parser catches more composition bugs
|
||||
than any amount of staring at note lists.
|
||||
|
||||
## Licence
|
||||
|
||||
MIT — see [LICENSE](LICENSE). Soundfonts are not bundled (licensing);
|
||||
install FluidR3_GM.sf2 or your favourite GM soundfont separately.
|
||||
Reference in new issue
Block a user