cgcardona / muse public
midi-domain.md markdown
1151 lines 31.6 KB
1f0a7cd9 docs: rename demo-midi.md → midi-demo.md and add full MIDI domain reference Gabriel Cardona <gabriel@tellurstori.com> 15h ago
1 # MIDI Domain — Complete Reference
2
3 > **Engine:** `muse/plugins/music/` · **Dependency:** `mido` for MIDI I/O
4 > **Scope:** Every command, module, type, and protocol in the MIDI domain plugin
5
6 ---
7
8 ## Overview
9
10 The MIDI domain plugin treats a composition as a **content-addressed graph of note events** — not as a binary blob. Every note becomes a `NoteInfo` record with a stable SHA-256 content ID derived from its five fields. This unlocks operations that are structurally impossible in Git:
11
12 - Read a track as musical notation, not binary bytes.
13 - Attribute any bar to the exact commit (and agent) that wrote it.
14 - Detect recurring melodic motifs independent of transposition.
15 - Partition a composition for parallel agent work with zero note-level conflicts.
16 - Enforce harmonic quality gates (no parallel fifths, proper cadences) in CI.
17 - Cherry-pick a harmonic transformation from one branch to another.
18
19 ---
20
21 ## Contents
22
23 1. [Note Identity Model](#1-note-identity-model)
24 2. [Notation & Visualization Commands](#2-notation--visualization-commands)
25 3. [Pitch, Harmony & Scale Commands](#3-pitch-harmony--scale-commands)
26 4. [Rhythm & Dynamics Commands](#4-rhythm--dynamics-commands)
27 5. [Structure & Voice-Leading Commands](#5-structure--voice-leading-commands)
28 6. [History & Attribution Commands](#6-history--attribution-commands)
29 7. [Multi-Agent Intelligence Commands](#7-multi-agent-intelligence-commands)
30 8. [Transformation Commands](#8-transformation-commands)
31 9. [Invariants & Quality Gate Commands](#9-invariants--quality-gate-commands)
32 10. [Type Reference](#10-type-reference)
33
34 ---
35
36 ## 1. Note Identity Model
37
38 Every note is a `NoteInfo` NamedTuple carrying five fields:
39
40 | Field | Type | Description |
41 |---|---|---|
42 | `pitch` | `int` | MIDI note number (0–127; middle C = 60) |
43 | `velocity` | `int` | Note-on velocity (0–127) |
44 | `start_tick` | `int` | Onset in MIDI ticks from the beginning of the file |
45 | `duration_ticks` | `int` | Duration in MIDI ticks |
46 | `channel` | `int` | MIDI channel (0–15) |
47
48 The content ID of a note is `SHA-256(pitch | velocity | start_tick | duration_ticks | channel)`.
49 Two notes with identical content IDs are the same note regardless of which file or commit they appear in.
50
51 ### Bar Conversion
52
53 MIDI ticks are converted to bar/beat positions using the file header's `ticks_per_beat` and an assumed 4/4 time signature.
54
55 ```
56 bar = (start_tick // ticks_per_beat // 4) + 1
57 beat = ((start_tick // ticks_per_beat) % 4) + 1.0 + fraction_within_beat
58 ```
59
60 All commands accept `--bars N` or `--bar N` to filter output to a specific bar or range.
61
62 ---
63
64 ## 2. Notation & Visualization Commands
65
66 ### `muse midi notes TRACK`
67
68 List every note in a MIDI track as human-readable musical notation.
69
70 ```
71 muse midi notes tracks/melody.mid
72 muse midi notes tracks/melody.mid --commit HEAD~10
73 muse midi notes tracks/melody.mid --bar 4
74 muse midi notes tracks/melody.mid --json
75 ```
76
77 **Flags**
78
79 | Flag | Default | Description |
80 |---|---|---|
81 | `--commit TEXT` | working tree | Resolve the track from a historical commit |
82 | `--bar INTEGER` | all | Filter to a single bar |
83 | `--json` | false | Emit JSON instead of text |
84
85 **Text output columns:** `Bar`, `Beat`, `Pitch`, `Vel`, `Dur(beats)`, `Channel`
86
87 **JSON output**
88
89 ```json
90 {
91 "track": "tracks/melody.mid",
92 "commit": "cb4afaed",
93 "note_count": 23,
94 "bar_count": 8,
95 "key_estimate": "G major",
96 "notes": [
97 {
98 "bar": 1, "beat": 1.0, "pitch_name": "G4", "pitch": 67,
99 "velocity": 80, "duration_beats": 1.0, "channel": 0
100 }
101 ]
102 }
103 ```
104
105 ---
106
107 ### `muse midi piano-roll TRACK`
108
109 ASCII piano roll visualization — pitch on the Y-axis, time on the X-axis, bar lines included.
110
111 ```
112 muse midi piano-roll tracks/melody.mid
113 muse midi piano-roll tracks/melody.mid --bars 1-4
114 muse midi piano-roll tracks/melody.mid --commit HEAD~5 --resolution 4
115 ```
116
117 **Flags**
118
119 | Flag | Default | Description |
120 |---|---|---|
121 | `--commit TEXT` | working tree | Historical snapshot |
122 | `--bars TEXT` | all | Bar range (`1-8`, `3-6`) |
123 | `--resolution INTEGER` | 2 | Cells per beat (2 = eighth-note, 4 = sixteenth-note) |
124
125 Each cell represents one time slice. `════` indicates a sustained note; the pitch label appears at the onset.
126
127 ---
128
129 ### `muse midi instrumentation TRACK`
130
131 Per-channel note count, pitch range, register classification, and mean velocity.
132
133 ```
134 muse midi instrumentation tracks/full_score.mid
135 muse midi instrumentation tracks/full_score.mid --commit HEAD --json
136 ```
137
138 **Flags**
139
140 | Flag | Default | Description |
141 |---|---|---|
142 | `--commit TEXT` | working tree | Historical snapshot |
143 | `--json` | false | Machine-readable output |
144
145 **Register classification:** `bass` (MIDI < 48), `mid` (48–71), `treble` (≥ 72).
146
147 **JSON output**
148
149 ```json
150 {
151 "track": "tracks/full_score.mid",
152 "channel_count": 3,
153 "total_notes": 106,
154 "channels": [
155 {
156 "channel": 0, "note_count": 34,
157 "pitch_min": 36, "pitch_max": 43,
158 "pitch_min_name": "C2", "pitch_max_name": "G2",
159 "register": "bass", "mean_velocity": 84.2
160 }
161 ]
162 }
163 ```
164
165 ---
166
167 ## 3. Pitch, Harmony & Scale Commands
168
169 ### `muse midi harmony TRACK`
170
171 Bar-by-bar chord detection and key signature estimation using the Krumhansl-Schmuckler key-finding algorithm.
172
173 ```
174 muse midi harmony tracks/melody.mid
175 muse midi harmony tracks/melody.mid --commit HEAD~3
176 muse midi harmony tracks/melody.mid --json
177 ```
178
179 **Flags**
180
181 | Flag | Default | Description |
182 |---|---|---|
183 | `--commit TEXT` | working tree | Historical snapshot |
184 | `--json` | false | Machine-readable output |
185
186 **Text output:** key estimate, table of `Bar → Chord → Notes → Pitch classes`, pitch class distribution histogram.
187
188 **JSON output**
189
190 ```json
191 {
192 "track": "tracks/melody.mid",
193 "key_estimate": "G major",
194 "total_notes": 23,
195 "bar_count": 8,
196 "bars": [
197 {"bar": 1, "chord": "Gmaj", "pitch_classes": ["G", "B", "D"], "note_count": 4}
198 ],
199 "pitch_class_distribution": {"G": 8, "B": 3, "D": 4}
200 }
201 ```
202
203 ---
204
205 ### `muse midi scale TRACK`
206
207 Scale and mode detection across 15 scale types and all 12 chromatic roots, ranked by confidence.
208
209 **Supported scales:** major, natural minor, harmonic minor, melodic minor, dorian, phrygian, lydian, mixolydian, locrian, major pentatonic, minor pentatonic, blues, whole-tone, diminished, chromatic.
210
211 ```
212 muse midi scale tracks/lead.mid
213 muse midi scale tracks/lead.mid --top 5
214 muse midi scale tracks/melody.mid --json
215 ```
216
217 **Flags**
218
219 | Flag | Default | Description |
220 |---|---|---|
221 | `--commit TEXT` | working tree | Historical snapshot |
222 | `--top INTEGER` | 3 | Number of best-match results to display |
223 | `--json` | false | Machine-readable output |
224
225 **JSON output**
226
227 ```json
228 {
229 "track": "tracks/lead.mid",
230 "matches": [
231 {
232 "rank": 1, "root": "E", "scale": "natural minor",
233 "confidence": 0.971, "out_of_scale_count": 0
234 }
235 ]
236 }
237 ```
238
239 ---
240
241 ### `muse midi contour TRACK`
242
243 Melodic contour analysis: shape classification, pitch range, direction-change count, and interval sequence.
244
245 **Shape types:** `ascending`, `descending`, `arch`, `valley`, `wave`, `flat`.
246
247 ```
248 muse midi contour tracks/lead.mid
249 muse midi contour tracks/lead.mid --commit HEAD~5 --json
250 ```
251
252 **Flags**
253
254 | Flag | Default | Description |
255 |---|---|---|
256 | `--commit TEXT` | working tree | Historical snapshot |
257 | `--json` | false | Machine-readable output |
258
259 **JSON output**
260
261 ```json
262 {
263 "track": "tracks/lead.mid",
264 "shape": "arch",
265 "pitch_range_semitones": 35,
266 "pitch_min_name": "D3",
267 "pitch_max_name": "C6",
268 "direction_changes": 6,
269 "avg_interval_size": 2.43,
270 "intervals": [2, 3, 2, -1, 4, -3, 2]
271 }
272 ```
273
274 ---
275
276 ### `muse midi tension TRACK`
277
278 Harmonic tension curve: dissonance score per bar on a 0 (consonant) → 1 (maximally tense) scale.
279
280 Tension is computed by summing the interval-dissonance weights for every simultaneous note pair in a bar. The weight table treats unisons and octaves as 0.0, perfect fifths as 0.1, major thirds as 0.2, and minor seconds as 1.0.
281
282 ```
283 muse midi tension tracks/epiano.mid
284 muse midi tension tracks/epiano.mid --commit HEAD~2 --json
285 ```
286
287 **Flags**
288
289 | Flag | Default | Description |
290 |---|---|---|
291 | `--commit TEXT` | working tree | Historical snapshot |
292 | `--json` | false | Machine-readable output |
293
294 **Tension labels:** `consonant` (< 0.15), `mild` (0.15–0.40), `tense` (0.40–0.65), `very tense` (≥ 0.65).
295
296 **JSON output**
297
298 ```json
299 {
300 "track": "tracks/epiano.mid",
301 "bars": [
302 {"bar": 1, "tension": 0.08, "label": "consonant"},
303 {"bar": 3, "tension": 0.67, "label": "tense"}
304 ]
305 }
306 ```
307
308 ---
309
310 ### `muse midi cadence TRACK`
311
312 Cadence detection at phrase boundaries: authentic, deceptive, half, and plagal.
313
314 A cadence is detected when the last chord of a phrase (determined by note density minima) moves to another chord matching one of the four cadence patterns.
315
316 ```
317 muse midi cadence tracks/epiano.mid
318 muse midi cadence tracks/epiano.mid --strict
319 muse midi cadence tracks/epiano.mid --json
320 ```
321
322 **Flags**
323
324 | Flag | Default | Description |
325 |---|---|---|
326 | `--commit TEXT` | working tree | Historical snapshot |
327 | `--strict` | false | Exit 1 if no cadences are found (CI gate) |
328 | `--json` | false | Machine-readable output |
329
330 **JSON output**
331
332 ```json
333 {
334 "track": "tracks/epiano.mid",
335 "cadence_count": 2,
336 "cadences": [
337 {"bar": 5, "cadence_type": "half", "from_chord": "Em", "to_chord": "Bdom7"},
338 {"bar": 9, "cadence_type": "authentic", "from_chord": "Bdom7", "to_chord": "Em"}
339 ]
340 }
341 ```
342
343 ---
344
345 ## 4. Rhythm & Dynamics Commands
346
347 ### `muse midi rhythm TRACK`
348
349 Rhythmic analysis: syncopation score, swing ratio, quantisation accuracy, and dominant subdivision.
350
351 ```
352 muse midi rhythm tracks/drums.mid
353 muse midi rhythm tracks/drums.mid --commit HEAD~3
354 muse midi rhythm tracks/bass.mid --json
355 ```
356
357 **Flags**
358
359 | Flag | Default | Description |
360 |---|---|---|
361 | `--commit TEXT` | working tree | Historical snapshot |
362 | `--json` | false | Machine-readable output |
363
364 **Computed fields:**
365
366 | Field | Range | Meaning |
367 |---|---|---|
368 | `quantization_score` | 0–1 | 1.0 = every note perfectly on the grid |
369 | `syncopation_score` | 0–1 | 0 = no off-beat notes; 1 = fully syncopated |
370 | `swing_ratio` | ≥ 1.0 | 1.0 = straight; > 1.3 = noticeable swing |
371 | `dominant_subdivision` | string | The most common rhythmic grid in the track |
372
373 **JSON output**
374
375 ```json
376 {
377 "track": "tracks/drums.mid",
378 "note_count": 64,
379 "bar_count": 8,
380 "notes_per_bar_avg": 8.0,
381 "dominant_subdivision": "sixteenth",
382 "quantization_score": 0.942,
383 "syncopation_score": 0.382,
384 "swing_ratio": 1.003
385 }
386 ```
387
388 ---
389
390 ### `muse midi tempo TRACK`
391
392 BPM estimation via inter-onset interval (IOI) voting.
393
394 Onset times are converted to seconds using `ticks_per_beat`. The most common IOI is detected and inverted to beats-per-minute. Confidence is rated based on how many onsets agree with the estimate.
395
396 ```
397 muse midi tempo tracks/drums.mid
398 muse midi tempo tracks/drums.mid --json
399 ```
400
401 **Flags**
402
403 | Flag | Default | Description |
404 |---|---|---|
405 | `--commit TEXT` | working tree | Historical snapshot |
406 | `--json` | false | Machine-readable output |
407
408 **JSON output**
409
410 ```json
411 {
412 "track": "tracks/drums.mid",
413 "bpm": 96.0,
414 "ticks_per_beat": 480,
415 "confidence": "high",
416 "method": "ioi_voting"
417 }
418 ```
419
420 ---
421
422 ### `muse midi density TRACK`
423
424 Notes-per-beat per bar — the textural arc of a composition.
425
426 ```
427 muse midi density tracks/drums.mid
428 muse midi density tracks/full.mid --commit HEAD~5 --json
429 ```
430
431 **Flags**
432
433 | Flag | Default | Description |
434 |---|---|---|
435 | `--commit TEXT` | working tree | Historical snapshot |
436 | `--json` | false | Machine-readable output |
437
438 **JSON output**
439
440 ```json
441 {
442 "track": "tracks/drums.mid",
443 "bar_count": 8,
444 "peak_bar": 5,
445 "peak_density": 6.25,
446 "avg_density": 5.1,
447 "bars": [
448 {"bar": 1, "notes": 16, "density": 4.0},
449 {"bar": 5, "notes": 25, "density": 6.25}
450 ]
451 }
452 ```
453
454 ---
455
456 ### `muse midi velocity-profile TRACK`
457
458 Dynamic range, RMS velocity, and histogram across the standard dynamic markings (ppp–fff).
459
460 ```
461 muse midi velocity-profile tracks/melody.mid
462 muse midi velocity-profile tracks/melody.mid --by-bar
463 muse midi velocity-profile tracks/melody.mid --commit HEAD~2 --json
464 ```
465
466 **Flags**
467
468 | Flag | Default | Description |
469 |---|---|---|
470 | `--commit TEXT` | working tree | Historical snapshot |
471 | `--by-bar` | false | Show per-bar average velocity as a histogram |
472 | `--json` | false | Machine-readable output |
473
474 **JSON output**
475
476 ```json
477 {
478 "track": "tracks/melody.mid",
479 "note_count": 23,
480 "velocity_min": 48,
481 "velocity_max": 96,
482 "velocity_mean": 78.3,
483 "velocity_rms": 79.1,
484 "dynamic_character": "mf",
485 "histogram": {
486 "ppp": 0, "pp": 0, "p": 0, "mp": 2,
487 "mf": 12, "f": 8, "ff": 1, "fff": 0
488 }
489 }
490 ```
491
492 ---
493
494 ## 5. Structure & Voice-Leading Commands
495
496 ### `muse midi motif TRACK`
497
498 Recurring interval-pattern detection. Scans the interval sequence between consecutive notes for repeated sub-sequences of length ≥ `--min-length`. Motifs are identified by their interval vector, making detection transposition-invariant.
499
500 ```
501 muse midi motif tracks/lead.mid
502 muse midi motif tracks/melody.mid --min-length 4 --min-occurrences 3
503 muse midi motif tracks/theme.mid --commit HEAD~5 --json
504 ```
505
506 **Flags**
507
508 | Flag | Default | Description |
509 |---|---|---|
510 | `--commit TEXT` | working tree | Historical snapshot |
511 | `--min-length INTEGER` | 3 | Minimum number of intervals (notes − 1) in a motif |
512 | `--min-occurrences INTEGER` | 2 | Minimum number of times a pattern must appear |
513 | `--json` | false | Machine-readable output |
514
515 **JSON output**
516
517 ```json
518 {
519 "track": "tracks/lead.mid",
520 "motif_count": 2,
521 "motifs": [
522 {
523 "intervals": [2, 2, -3],
524 "occurrences": 3,
525 "first_pitch_name": "E4",
526 "bars": [1, 5, 9]
527 }
528 ]
529 }
530 ```
531
532 ---
533
534 ### `muse midi voice-leading TRACK`
535
536 Classical counterpoint lint: parallel fifths, parallel octaves, and large leaps in the top voice.
537
538 Voice-leading issues are detected by comparing successive simultaneous note pairs across MIDI channels. "Parallel" motion means two voices move in the same direction by the same interval class.
539
540 ```
541 muse midi voice-leading tracks/strings.mid
542 muse midi voice-leading tracks/choir.mid --strict
543 muse midi voice-leading tracks/strings.mid --json
544 ```
545
546 **Flags**
547
548 | Flag | Default | Description |
549 |---|---|---|
550 | `--commit TEXT` | working tree | Historical snapshot |
551 | `--strict` | false | Exit 1 if any issues are found (CI gate) |
552 | `--json` | false | Machine-readable output |
553
554 **Issue types:** `parallel_fifths`, `parallel_octaves`, `large_leap` (> 7 semitones in the top voice).
555
556 **JSON output**
557
558 ```json
559 {
560 "track": "tracks/strings.mid",
561 "issue_count": 2,
562 "issues": [
563 {"bar": 6, "issue_type": "parallel_fifths", "description": "voices 0–1: parallel perfect fifths"},
564 {"bar": 9, "issue_type": "large_leap", "description": "top voice: leap of 11 semitones"}
565 ]
566 }
567 ```
568
569 ---
570
571 ### `muse midi compare TRACK COMMIT_A COMMIT_B`
572
573 Semantic diff between two commits: side-by-side comparison of key, density, rhythm, and structure dimensions.
574
575 ```
576 muse midi compare tracks/epiano.mid HEAD~2 HEAD
577 muse midi compare tracks/melody.mid main feat/variation --json
578 ```
579
580 **Flags**
581
582 | Flag | Default | Description |
583 |---|---|---|
584 | `--json` | false | Machine-readable output |
585
586 **Compared dimensions:** note count, bar count, key estimate, density (avg notes/beat), swing ratio, syncopation score, quantisation score, dominant subdivision.
587
588 **JSON output**
589
590 ```json
591 {
592 "track": "tracks/epiano.mid",
593 "commit_a": "1b3c8f02",
594 "commit_b": "3f0b5c8d",
595 "dimensions": {
596 "note_count": {"a": 18, "b": 32, "delta": 14},
597 "bar_count": {"a": 4, "b": 8, "delta": 4},
598 "key_estimate": {"a": "E minor", "b": "E minor", "delta": "="},
599 "density_avg": {"a": 4.5, "b": 5.1, "delta": 0.6},
600 "swing_ratio": {"a": 1.0, "b": 1.0, "delta": 0.0},
601 "syncopation_score": {"a": 0.11, "b": 0.38, "delta": 0.27},
602 "quantization_score": {"a": 0.97, "b": 0.94, "delta": -0.03},
603 "dominant_subdivision":{"a": "quarter", "b": "sixteenth", "delta": "changed"}
604 }
605 }
606 ```
607
608 ---
609
610 ## 6. History & Attribution Commands
611
612 ### `muse midi note-log TRACK`
613
614 Note-level commit history: every commit that touched this track, with its note insertions and deletions expressed as musical notation.
615
616 ```
617 muse midi note-log tracks/melody.mid
618 muse midi note-log tracks/melody.mid --limit 10
619 muse midi note-log tracks/melody.mid --json
620 ```
621
622 **Flags**
623
624 | Flag | Default | Description |
625 |---|---|---|
626 | `--limit INTEGER` | all | Maximum number of commits to display |
627 | `--json` | false | Machine-readable output |
628
629 **Text output format:** `+ pitch vel @beat dur ch` for insertions, `- pitch vel @beat dur ch (removed)` for deletions.
630
631 ---
632
633 ### `muse midi note-blame TRACK`
634
635 Per-bar attribution: which commit (and author) introduced the notes in a given bar.
636
637 ```
638 muse midi note-blame tracks/melody.mid --bar 4
639 muse midi note-blame tracks/melody.mid --json
640 ```
641
642 **Flags**
643
644 | Flag | Default | Description |
645 |---|---|---|
646 | `--bar INTEGER` | 1 | Bar number to inspect |
647 | `--json` | false | Machine-readable output |
648
649 **JSON output**
650
651 ```json
652 {
653 "track": "tracks/melody.mid",
654 "bar": 4,
655 "note_count": 5,
656 "commit_id": "cb4afaed",
657 "committed_at": "2026-03-16T10:00:00+00:00",
658 "author": "alice",
659 "message": "Add D7 arpeggiation in bar 4"
660 }
661 ```
662
663 ---
664
665 ### `muse midi hotspots`
666
667 Bar-level churn leaderboard: which bars have accumulated the most note insertions and deletions across all commits.
668
669 ```
670 muse midi hotspots
671 muse midi hotspots --top 10
672 muse midi hotspots --track tracks/melody.mid
673 muse midi hotspots --from HEAD~20 --top 5
674 muse midi hotspots --json
675 ```
676
677 **Flags**
678
679 | Flag | Default | Description |
680 |---|---|---|
681 | `--top INTEGER` | 10 | Number of bars to display |
682 | `--track TEXT` | all | Restrict to a single MIDI file |
683 | `--from TEXT` | initial commit | Start of the commit range |
684 | `--json` | false | Machine-readable output |
685
686 **JSON output**
687
688 ```json
689 {
690 "commit_count": 47,
691 "hotspots": [
692 {"rank": 1, "track": "tracks/melody.mid", "bar": 8, "changes": 12},
693 {"rank": 2, "track": "tracks/melody.mid", "bar": 4, "changes": 9}
694 ]
695 }
696 ```
697
698 ---
699
700 ## 7. Multi-Agent Intelligence Commands
701
702 ### `muse midi agent-map TRACK`
703
704 Bar-level blame showing which author and commit last edited each bar. The musical equivalent of `git blame` at bar granularity.
705
706 ```
707 muse midi agent-map tracks/lead.mid
708 muse midi agent-map tracks/lead.mid --depth 100
709 muse midi agent-map tracks/bass.mid --json
710 ```
711
712 **Flags**
713
714 | Flag | Default | Description |
715 |---|---|---|
716 | `--depth INTEGER` | 50 | Maximum number of commits to walk |
717 | `--json` | false | Machine-readable output |
718
719 **JSON output**
720
721 ```json
722 {
723 "track": "tracks/lead.mid",
724 "bars": [
725 {"bar": 1, "author": "agent-melody", "commit_id": "3f0b5c8d", "message": "Groove: full arrangement"},
726 {"bar": 3, "author": "agent-harmony", "commit_id": "4e2c91aa", "message": "Harmony: modal interchange"}
727 ]
728 }
729 ```
730
731 ---
732
733 ### `muse midi find-phrase TRACK --query QUERY_FILE`
734
735 Phrase similarity search: find commits where the content of `TRACK` most closely resembles `QUERY_FILE`, using pitch-class histogram and interval fingerprint similarity. Detection is transposition-invariant — the same motif in a different key is still found.
736
737 ```
738 muse midi find-phrase tracks/lead.mid --query query/motif.mid
739 muse midi find-phrase tracks/lead.mid --query query/motif.mid --depth 20
740 muse midi find-phrase tracks/lead.mid --query query/motif.mid --json
741 ```
742
743 **Flags**
744
745 | Flag | Default | Description |
746 |---|---|---|
747 | `--query TEXT` | required | Path to the query MIDI file |
748 | `--depth INTEGER` | 20 | Maximum number of commits to scan |
749 | `--json` | false | Machine-readable output |
750
751 **Similarity score:** `0.0` = no resemblance; `1.0` = identical. Composed from 60% interval fingerprint + 40% pitch-class histogram.
752
753 **JSON output**
754
755 ```json
756 {
757 "track": "tracks/lead.mid",
758 "query": "query/motif.mid",
759 "results": [
760 {"score": 0.934, "commit_id": "3f0b5c8d", "author": "agent-melody", "message": "Groove: full arrangement"},
761 {"score": 0.812, "commit_id": "4e2c91aa", "author": "agent-harmony", "message": "Harmony: modal interchange"}
762 ]
763 }
764 ```
765
766 ---
767
768 ### `muse midi shard TRACK --shards N`
769
770 Partition a MIDI track into N non-overlapping bar-range shards for parallel agent work. Agents working on different shards can commit independently and merge with zero note-level conflicts — identical to `muse coord shard` for code.
771
772 ```
773 muse midi shard tracks/full.mid --shards 4
774 muse midi shard tracks/symphony.mid --bars-per-shard 32 --output-dir agents/
775 muse midi shard tracks/full.mid --shards 8 --dry-run
776 ```
777
778 **Flags**
779
780 | Flag | Default | Description |
781 |---|---|---|
782 | `--shards INTEGER` | required | Number of shards to produce |
783 | `--bars-per-shard INTEGER` | auto | Fixed bar count per shard (overrides `--shards`) |
784 | `--output-dir TEXT` | `shards/` | Directory for output shard files |
785 | `--dry-run` | false | Print shard plan without writing files |
786
787 **Shard file naming:** `{stem}_shard_{n}.mid` in `--output-dir`.
788
789 **Workflow:** shard → assign shards to agents → agents commit → `muse midi mix` to recombine.
790
791 ---
792
793 ### `muse midi query TRACK`
794
795 MIDI DSL predicate query over note data and commit history.
796
797 ```
798 muse midi query tracks/melody.mid "bar=4"
799 muse midi query tracks/melody.mid "pitch=G4 velocity>70"
800 muse midi query tracks/melody.mid "bar>=2 bar<=5 channel=0"
801 muse midi query tracks/melody.mid "agent=agent-melody" --commit HEAD~10
802 ```
803
804 **DSL predicates**
805
806 | Predicate | Example | Description |
807 |---|---|---|
808 | `bar=N` | `bar=4` | Match notes in exactly bar N |
809 | `bar>=N`, `bar<=N` | `bar>=2 bar<=5` | Bar range |
810 | `pitch=X` | `pitch=G4` | Match by pitch name or MIDI number |
811 | `velocity>N` | `velocity>70` | Velocity threshold |
812 | `channel=N` | `channel=0` | MIDI channel filter |
813 | `agent=X` | `agent=alice` | Last-author filter (walks blame history) |
814
815 **Flags**
816
817 | Flag | Default | Description |
818 |---|---|---|
819 | `--commit TEXT` | working tree | Historical snapshot |
820 | `--json` | false | Machine-readable note list |
821
822 ---
823
824 ## 8. Transformation Commands
825
826 All transformation commands modify the working tree. Run `muse status` after, then `muse commit` to record the note-level delta.
827
828 ### `muse midi transpose TRACK --semitones N`
829
830 Shift all notes by N semitones (positive = up, negative = down).
831
832 ```
833 muse midi transpose tracks/melody.mid --semitones 7
834 muse midi transpose tracks/bass.mid --semitones -12
835 muse midi transpose tracks/melody.mid --semitones 5 --clamp
836 muse midi transpose tracks/melody.mid --semitones 7 --dry-run
837 ```
838
839 **Flags**
840
841 | Flag | Default | Description |
842 |---|---|---|
843 | `--semitones INTEGER` | required | Shift amount; negative = down |
844 | `--clamp` | false | Clamp out-of-range pitches to 0–127 instead of skipping |
845 | `--dry-run` | false | Preview without writing |
846
847 ---
848
849 ### `muse midi quantize TRACK --grid GRID`
850
851 Snap note onset times to a rhythmic grid with adjustable strength.
852
853 ```
854 muse midi quantize tracks/piano.mid --grid 16th
855 muse midi quantize tracks/piano.mid --grid triplet-8th --strength 0.5 --dry-run
856 ```
857
858 **Flags**
859
860 | Flag | Default | Description |
861 |---|---|---|
862 | `--grid TEXT` | required | Grid value: `whole`, `half`, `quarter`, `8th`, `16th`, `32nd`, `triplet-8th`, `triplet-16th` |
863 | `--strength FLOAT` | 1.0 | Quantisation strength 0.0–1.0; < 1.0 preserves human feel |
864 | `--dry-run` | false | Preview without writing |
865
866 ---
867
868 ### `muse midi humanize TRACK`
869
870 Add controlled randomness to onset times and velocities to simulate human performance.
871
872 ```
873 muse midi humanize tracks/piano.mid
874 muse midi humanize tracks/piano.mid --timing 0.015 --velocity 10 --seed 42
875 ```
876
877 **Flags**
878
879 | Flag | Default | Description |
880 |---|---|---|
881 | `--timing FLOAT` | 0.01 | Max timing jitter in beats |
882 | `--velocity INTEGER` | 8 | Max velocity jitter (±) |
883 | `--seed INTEGER` | none | RNG seed for reproducible agent pipelines |
884
885 ---
886
887 ### `muse midi invert TRACK --pivot PITCH`
888
889 Melodic inversion: every upward interval becomes downward and vice versa, reflected around the pivot pitch.
890
891 ```
892 muse midi invert tracks/melody.mid --pivot E4
893 muse midi invert tracks/melody.mid --pivot E4 --dry-run
894 ```
895
896 **Flags**
897
898 | Flag | Default | Description |
899 |---|---|---|
900 | `--pivot TEXT` | required | Pivot pitch name (`E4`) or MIDI number (`64`) |
901 | `--dry-run` | false | Preview without writing |
902
903 The pivot pitch maps to itself; all other pitches are reflected symmetrically.
904
905 ---
906
907 ### `muse midi retrograde TRACK`
908
909 Reverse the pitch order of all notes while preserving their timing, velocities, and durations.
910
911 ```
912 muse midi retrograde tracks/melody.mid
913 muse midi retrograde tracks/theme.mid --dry-run
914 ```
915
916 **Flags**
917
918 | Flag | Default | Description |
919 |---|---|---|
920 | `--dry-run` | false | Preview without writing |
921
922 Classic twelve-tone row operation. Combine with `invert` to produce the retrograde-inversion.
923
924 ---
925
926 ### `muse midi arpeggiate TRACK --rate RATE`
927
928 Convert simultaneous chord voicings to sequential arpeggio notes.
929
930 ```
931 muse midi arpeggiate tracks/epiano.mid --rate 8th --order up-down
932 muse midi arpeggiate tracks/piano.mid --rate 16th --order random --seed 7
933 ```
934
935 **Flags**
936
937 | Flag | Default | Description |
938 |---|---|---|
939 | `--rate TEXT` | `8th` | Rhythmic rate: `quarter`, `8th`, `16th`, `32nd` |
940 | `--order TEXT` | `up` | Arpeggio direction: `up`, `down`, `up-down`, `random` |
941 | `--seed INTEGER` | none | RNG seed (used with `--order random`) |
942
943 Simultaneous notes (onset within 10 ticks of each other) are grouped into chord clusters and distributed sequentially at the chosen rate.
944
945 ---
946
947 ### `muse midi normalize TRACK`
948
949 Linearly rescale all note velocities to a target dynamic range while preserving relative dynamics.
950
951 ```
952 muse midi normalize tracks/lead.mid --min 50 --max 100
953 muse midi normalize tracks/drums.mid --min 40 --max 120
954 ```
955
956 **Flags**
957
958 | Flag | Default | Description |
959 |---|---|---|
960 | `--min INTEGER` | 40 | Target minimum velocity |
961 | `--max INTEGER` | 110 | Target maximum velocity |
962
963 Essential when integrating tracks from multiple agents recorded at different volume levels.
964
965 ---
966
967 ### `muse midi mix TRACK_A TRACK_B --output OUTPUT`
968
969 Combine notes from two MIDI tracks into a single output file.
970
971 ```
972 muse midi mix tracks/melody.mid tracks/harmony.mid \
973 --output tracks/full.mid \
974 --channel-a 0 \
975 --channel-b 1
976 ```
977
978 **Flags**
979
980 | Flag | Default | Description |
981 |---|---|---|
982 | `--output TEXT` | required | Output file path |
983 | `--channel-a INTEGER` | 0 | MIDI channel to assign to TRACK_A notes in output |
984 | `--channel-b INTEGER` | 1 | MIDI channel to assign to TRACK_B notes in output |
985
986 The `--channel-a` / `--channel-b` flags ensure instruments can be differentiated in the mixed output and individual parts can be extracted later.
987
988 ---
989
990 ## 9. Invariants & Quality Gate Commands
991
992 ### `muse midi check`
993
994 Enforce MIDI invariant rules defined in `.museattributes` or inline flags.
995
996 ```
997 muse midi check
998 muse midi check --track tracks/melody.mid
999 muse midi check --key "G major" --max-polyphony 4 --strict
1000 ```
1001
1002 **Flags**
1003
1004 | Flag | Default | Description |
1005 |---|---|---|
1006 | `--track TEXT` | all | Restrict check to a single MIDI file |
1007 | `--key TEXT` | none | Assert that the detected key matches this value |
1008 | `--max-polyphony INTEGER` | none | Assert that no bar exceeds N simultaneous notes |
1009 | `--strict` | false | Exit 1 on any violation (CI gate) |
1010 | `--json` | false | Machine-readable results |
1011
1012 **Use in CI:** add `muse midi check --strict` as a pre-commit hook or CI step to prevent agents from committing tracks that violate compositional rules.
1013
1014 ---
1015
1016 ## 10. Type Reference
1017
1018 ### `NoteInfo`
1019
1020 ```python
1021 class NoteInfo(NamedTuple):
1022 pitch: int # MIDI note number 0–127
1023 velocity: int # note-on velocity 0–127
1024 start_tick: int # onset in MIDI ticks
1025 duration_ticks: int # duration in MIDI ticks
1026 channel: int # MIDI channel 0–15
1027 ```
1028
1029 ### Analysis TypedDicts (from `muse/plugins/midi/_analysis.py`)
1030
1031 ```python
1032 class ScaleMatch(TypedDict):
1033 root: str
1034 scale: str
1035 confidence: float
1036 out_of_scale_count: int
1037
1038 class RhythmAnalysis(TypedDict):
1039 quantization_score: float
1040 syncopation_score: float
1041 swing_ratio: float
1042 dominant_subdivision: str
1043
1044 class ContourAnalysis(TypedDict):
1045 shape: str # ascending | descending | arch | valley | wave | flat
1046 pitch_range_semitones: int
1047 direction_changes: int
1048 avg_interval_size: float
1049 intervals: list[int]
1050
1051 class BarDensity(TypedDict):
1052 bar: int
1053 notes: int
1054 density: float
1055
1056 class BarTension(TypedDict):
1057 bar: int
1058 tension: float
1059 label: str
1060
1061 class Cadence(TypedDict):
1062 bar: int
1063 cadence_type: str # authentic | deceptive | half | plagal
1064 from_chord: str
1065 to_chord: str
1066
1067 class Motif(TypedDict):
1068 intervals: list[int]
1069 occurrences: int
1070 first_pitch_name: str
1071 bars: list[int]
1072
1073 class VoiceLeadingIssue(TypedDict):
1074 bar: int
1075 issue_type: str # parallel_fifths | parallel_octaves | large_leap
1076 description: str
1077
1078 class TempoEstimate(TypedDict):
1079 bpm: float
1080 ticks_per_beat: int
1081 confidence: str # high | medium | low
1082 method: str
1083
1084 class ChannelInfo(TypedDict):
1085 channel: int
1086 note_count: int
1087 pitch_min: int
1088 pitch_max: int
1089 pitch_min_name: str
1090 pitch_max_name: str
1091 register: str # bass | mid | treble
1092 mean_velocity: float
1093
1094 class BarAttribution(TypedDict):
1095 bar: int
1096 author: str
1097 commit_id: str
1098 message: str
1099 ```
1100
1101 ### `PhraseMatch`
1102
1103 ```python
1104 class PhraseMatch(TypedDict):
1105 score: float # 0.0–1.0 similarity
1106 commit_id: str
1107 author: str
1108 message: str
1109 ```
1110
1111 ---
1112
1113 ## Command Quick-Reference
1114
1115 | Group | Command | One-line description |
1116 |---|---|---|
1117 | **Notation** | `muse midi notes` | Every note as musical notation |
1118 | **Notation** | `muse midi piano-roll` | ASCII piano roll |
1119 | **Notation** | `muse midi instrumentation` | Per-channel range, register, velocity |
1120 | **Harmony** | `muse midi harmony` | Bar-by-bar chord detection + key |
1121 | **Harmony** | `muse midi scale` | Scale/mode detection (15 types × 12 roots) |
1122 | **Harmony** | `muse midi contour` | Melodic contour shape + interval sequence |
1123 | **Harmony** | `muse midi tension` | Dissonance score per bar (0–1) |
1124 | **Harmony** | `muse midi cadence` | Cadence detection at phrase boundaries |
1125 | **Rhythm** | `muse midi rhythm` | Syncopation, swing, quantisation, subdivision |
1126 | **Rhythm** | `muse midi tempo` | BPM estimation via IOI voting |
1127 | **Rhythm** | `muse midi density` | Notes-per-beat per bar — textural arc |
1128 | **Rhythm** | `muse midi velocity-profile` | Dynamic range and histogram (ppp–fff) |
1129 | **Structure** | `muse midi motif` | Transposition-invariant motif detection |
1130 | **Structure** | `muse midi voice-leading` | Parallel fifths/octaves + large leaps lint |
1131 | **Structure** | `muse midi compare` | Semantic diff across musical dimensions |
1132 | **History** | `muse midi note-log` | Note-level commit history |
1133 | **History** | `muse midi note-blame` | Per-bar: which commit wrote these notes |
1134 | **History** | `muse midi hotspots` | Bar-level churn leaderboard |
1135 | **Multi-agent** | `muse midi agent-map` | Bar-level blame: which agent last edited |
1136 | **Multi-agent** | `muse midi find-phrase` | Phrase similarity search across history |
1137 | **Multi-agent** | `muse midi shard` | Partition into N bar-range shards |
1138 | **Multi-agent** | `muse midi query` | MIDI DSL predicate query |
1139 | **Transform** | `muse midi transpose` | Shift all pitches by N semitones |
1140 | **Transform** | `muse midi invert` | Melodic inversion around a pivot |
1141 | **Transform** | `muse midi retrograde` | Reverse pitch order |
1142 | **Transform** | `muse midi quantize` | Snap onsets to a rhythmic grid |
1143 | **Transform** | `muse midi humanize` | Add timing/velocity jitter |
1144 | **Transform** | `muse midi arpeggiate` | Chords → arpeggios |
1145 | **Transform** | `muse midi normalize` | Rescale velocities to target range |
1146 | **Transform** | `muse midi mix` | Combine two MIDI tracks into one |
1147 | **Invariants** | `muse midi check` | Enforce MIDI invariant rules |
1148
1149 ---
1150
1151 *See also: [Demo walkthrough](../demo/midi-demo.md) · [CLI Tiers Reference](cli-tiers.md) · [Plugin Authoring Guide](../guide/plugin-authoring-guide.md)*