mirror of
https://github.com/9x/sheetmusictrainer.git
synced 2026-11-03 13:53:55 +01:00
Add Phrase Mode requirements & implementation plan (draft)
This commit is contained in:
300
PHRASE_MODE_PLAN.md
Normal file
300
PHRASE_MODE_PLAN.md
Normal file
@@ -0,0 +1,300 @@
|
||||
# Phrase Mode — Requirements & Implementation Plan
|
||||
|
||||
**Status: DRAFT — awaiting approval ("Go") before implementation.**
|
||||
|
||||
This document describes a proposed third game mode ("Phrase Mode") for the Sheet
|
||||
Music Trainer. It is written as stakeholder requirements: what the mode does,
|
||||
what it deliberately does **not** do, how it will be built, and which decisions
|
||||
need the owner's sign-off. Section 8 lists the open decisions together with the
|
||||
defaults I will assume if no feedback arrives in time — so an autonomous
|
||||
overnight run can proceed without blocking on any of them.
|
||||
|
||||
---
|
||||
|
||||
## 1. Vision
|
||||
|
||||
Today the app trains **single notes**: one note appears, you play it, the app
|
||||
confirms, repeat. This is great for building note-lookup reflexes, but real
|
||||
sight-reading is about reading **sequences** — a bar or a few bars of connected
|
||||
music, played in order, ideally in time.
|
||||
|
||||
Phrase Mode adds exactly that, without disturbing the minimal feel of the
|
||||
existing modes:
|
||||
|
||||
- The staff shows **1–8 bars of single-voice music** (notes with real durations,
|
||||
bar lines, key/time signature).
|
||||
- You **play the notes one by one**; the app tracks your position and gives the
|
||||
same kind of quiet, unobtrusive feedback as today.
|
||||
- Two paces: **Step mode** (beginner — play each note correctly, no clock) and
|
||||
**Tempo mode** (metronome — the phrase moves on the beat, hit or miss).
|
||||
- Material comes from two sources: **rule-based generated melodies** (in a key,
|
||||
scale or fretboard position of your choosing) and a **bundled library of fixed
|
||||
exercises** (e.g. Giuliani studies, folk tunes) parsed at runtime from a
|
||||
simple text file format.
|
||||
|
||||
Zen mode, mic handling, virtual instruments, themes and all existing modes stay
|
||||
as they are. Phrase Mode is *additive*: a third toggle next to "Sight Reading"
|
||||
and "Ear Training".
|
||||
|
||||
## 2. User stories
|
||||
|
||||
| # | Story |
|
||||
|---|--------|
|
||||
| U1 | As a guitar student, I want the app to generate short melodies within a key and a fretboard position, so I practice reading connected lines instead of isolated notes. |
|
||||
| U2 | As a beginner, I want a mode where the phrase waits for me: the next note is only "armed" after I played the current one, so I can go at my own pace. |
|
||||
| U3 | As a more advanced student, I want the phrase to advance on the metronome, marking notes I hit in time vs. missed, so I practice real sight-reading under tempo. |
|
||||
| U4 | As a classical player, I want to practice fixed material (e.g. Giuliani right-hand studies) from a library, so I rehearse real repertoire. |
|
||||
| U5 | As a future contributor, I want exercises stored in a simple text format in the repo, so more pieces can be added later — found somewhere else, converted, or generated. |
|
||||
| U6 | As a user of the existing modes, I expect Zen mode, hints, virtual instruments and themes to keep working unchanged in the new mode. |
|
||||
|
||||
## 3. Requirements
|
||||
|
||||
### 3.1 Core (MUST)
|
||||
|
||||
- **R1 — Third game mode.** `gameMode: 'phrase'` alongside the existing two.
|
||||
Header shows three toggles. Switching modes must not regress existing modes.
|
||||
- **R2 — Multi-bar rendering.** The staff renders a phrase of 1–8 bars with
|
||||
correct notation: key signature (reusing existing key machinery), time
|
||||
signature, note durations (whole/half/quarter/eighth, dots if the library
|
||||
needs them), bar lines. Reuses the per-instrument clef + guitar transpose
|
||||
logic, incl. grand staff for piano. Notes fit the width; narrow screens show
|
||||
fewer bars per system (wrapping into multiple systems).
|
||||
- **R3 — Cursor & feedback.** The note you currently have to play is visually
|
||||
marked (e.g. colored). Played notes get subtle pass/fail coloring. No big
|
||||
popups per note — the "minimal feeling" of the existing modes must be kept.
|
||||
- **R4 — Step mode (beginner).** The next note is armed only after the current
|
||||
one was matched (same flicker-tolerant matching as today: `MatchTracker`,
|
||||
50 ms hold). Wrong notes simply don't advance (plus the existing "skip"
|
||||
affordance to bail out of a note).
|
||||
- **R5 — Tempo mode.** Metronome at the existing BPM setting; 1-bar count-in;
|
||||
the cursor advances at each note's notated duration regardless of hit/miss.
|
||||
A note counts as *hit* if the correct pitch is detected and held for the
|
||||
match threshold at any point within its window. Missed notes are marked but
|
||||
never stop the run. At the end: a compact summary (X/Y notes) with
|
||||
*Retry* / *Next phrase*.
|
||||
- **R6 — Generated melodies.** Rule-based generator producing short
|
||||
single-voice melodies: all pitches within the chosen key/scale **and** the
|
||||
instrument's playable note set (existing difficulty ranges — incl. custom
|
||||
fret windows, i.e. "position playing" comes for free via the existing
|
||||
custom_fret difficulty), durations summing exactly to full bars, sane
|
||||
musical rules (see 4.1), deterministic under a seed → fully unit-testable.
|
||||
- **R7 — Exercise library.** A bundled set of fixed exercises parsed at
|
||||
runtime from the chosen file format (see 5), plus a picker in the mode's
|
||||
UI (title list, grouped by source/difficulty).
|
||||
- **R8 — Play-phrase preview.** A "Play" button (and keyboard shortcut) plays
|
||||
the phrase through the existing `AudioEngine` so the user can hear it before
|
||||
reading it — the phrase analog of the existing "Play Note".
|
||||
- **R9 — Zen mode compatibility.** Phrase Mode honors Zen mode (hide chrome,
|
||||
keep staff + minimal controls) and works in landscape/mobile layouts.
|
||||
- **R10 — No regressions.** Existing tests pass; new logic is unit-tested;
|
||||
build and lint stay green; README and TECHNICAL_REALIZATION.md updated.
|
||||
|
||||
### 3.2 Nice to have in v1 (SHOULD — done only if time permits after the core)
|
||||
|
||||
- S1 — End-of-phrase auto-advance ("practice loop"): after the summary, the
|
||||
same phrase repeats (Retry) or the next one loads automatically.
|
||||
- S2 — Scale drill material: runs/arpeggios in a selected key and position,
|
||||
as either generated material or tiny bundled exercises (this is the
|
||||
"scales in different positions" ask expressed in the simplest useful form).
|
||||
- S3 — Repeat marks / looping a sub-range of a longer exercise.
|
||||
- S4 — Ear-training variant of phrases ("listen to the phrase, then play it
|
||||
back") — deliberately deferred, see 3.3.
|
||||
|
||||
### 3.3 Explicitly out of scope for v1 (WON'T)
|
||||
|
||||
- **Chords / multiple simultaneous voices.** The matching pipeline is
|
||||
monophonic by design (single pitch per frame). The data model will keep a
|
||||
`voice`-shaped structure so a polyphonic future is not blocked, but v1
|
||||
renders and matches single voice only.
|
||||
- **Polyphonic pitch detection** (fundamental research territory).
|
||||
- **Scoring history / statistics persistence.**
|
||||
- **Importing arbitrary user files via file picker** — the library lives in
|
||||
the repo; a user-facing importer (file picker → localStorage) is a small
|
||||
follow-up once the format is proven.
|
||||
- **MIDI import.** See 5 for the reasoning; can be added later as a
|
||||
build-time/offline converter into the same internal model.
|
||||
|
||||
## 4. Functional specification
|
||||
|
||||
### 4.1 Melody generator (rule set for v1)
|
||||
|
||||
Configurable via settings: key/mode, number of bars, rhythm complexity level,
|
||||
and the existing difficulty range (which constrains the playable pitch set,
|
||||
including fretboard position via the custom fret window).
|
||||
|
||||
Rules (v1 — deliberately simple, testable invariants in parentheses):
|
||||
|
||||
1. Pitch pool = scale of the chosen key/mode ∩ playable notes of the
|
||||
instrument/difficulty. *(invariant: every generated pitch ∈ pool)*
|
||||
2. Start on a stable degree (1, 3 or 5 of the scale); end on the tonic
|
||||
(or dominant), placed on a strong beat.
|
||||
3. Mostly stepwise motion (≈70% steps of a 2nd); leaps mostly 3rd/4th,
|
||||
rare 5th/6th/octave; a leap is followed by a step in the opposite
|
||||
direction (classic counterpoint heuristic — produces melodic, not
|
||||
chaotic, lines). *(invariant: no two consecutive leaps > 4th without
|
||||
an intervening step in opposite direction)*
|
||||
4. No immediate repetition of the same pitch more than twice in a row.
|
||||
5. Rhythm: per-bar patterns drawn from a complexity-tiered pool
|
||||
(level 1: quarters; level 2: + paired eighths / half notes; level 3:
|
||||
+ dotted quarters & mixed). No rests in v1. Final note ≥ half note.
|
||||
*(invariant: durations per bar sum to the bar exactly)*
|
||||
6. Motivic shape: 4-bar phrases built as 2 bars of motif + 2 bars answering
|
||||
(transposed/varied motif) — cheap to implement, dramatically improves
|
||||
"sounds like a melody" impression. Optional at difficulty level 3 only.
|
||||
7. v1 keys: all majors and minors from the existing key-signature list plus
|
||||
major/minor **pentatonic**; additional modes (Dorian, Mixolydian, …) are
|
||||
trivial follow-ups of the same scale machinery.
|
||||
|
||||
### 4.2 Playback state machine
|
||||
|
||||
```
|
||||
IDLE → (start) → COUNT_IN (tempo mode only) → PLAYING → DONE → (retry|next) → …
|
||||
In PLAYING: cursor at note i,
|
||||
step mode: wait for match(i) → color green → cursor i+1
|
||||
tempo mode: window(i) = [t_i, t_i + dur_i); at window end evaluate
|
||||
MatchTracker state → green/red → cursor i+1
|
||||
```
|
||||
|
||||
Matching reuses `MatchTracker` unchanged (hold 50 ms, 150 ms flicker grace).
|
||||
The mic is gated during speaker output exactly as today (`audioEngine.
|
||||
isAudible()`), so the preview button and virtual instruments can't self-match.
|
||||
|
||||
### 4.3 UI concept
|
||||
|
||||
- Header: third toggle **"Phrases"** (naming: see 8.1).
|
||||
- Main stage (non-zen): phrase staff; below it a compact status line
|
||||
(step mode: "Play the highlighted note" / tempo mode: bar count-in
|
||||
indicator + running hit/miss count); controls: Play (preview),
|
||||
Restart, New phrase / Next exercise, plus the existing hint/virtual
|
||||
instrument buttons which keep working (hint marks the current note's
|
||||
fretboard position).
|
||||
- End of phrase: one-line summary + Retry / Next. No modal, no confetti.
|
||||
- Zen mode: staff + exit button only, as today.
|
||||
|
||||
## 5. Fixed-material file format — recommendation
|
||||
|
||||
The internal representation is a plain TypeScript model (notes with midi +
|
||||
duration in beats, metadata) that both the generator and the library feed
|
||||
into. The open question is the ** interchange format** for the library.
|
||||
|
||||
| Option | Pros | Cons |
|
||||
|--------|------|------|
|
||||
| **ABC notation** ✅ recommended | Text: easy to bundle in git, to author by hand, to LLM-generate, to review in diffs; huge public-domain corpus (folk tunes in ABC everywhere); encodes bars, durations, key, **and note spelling** directly — maps 1:1 onto VexFlow; a subset parser is ~300 lines, fully unit-testable | Little pre-existing classical-pedagogy material in ABC (Giuliani studies must be transcribed — but arpeggio studies are extremely regular and transcribe quickly) |
|
||||
| MIDI | Abundant files; import of anything | Binary; durations need quantization (grid inference); no spelling — must be re-derived; repeats/structure opaque; parser + quantizer ≈ same size as ABC parser with worse output |
|
||||
| MusicXML | Notation-perfect | Heavy, verbose, no light-weight parser; overkill for monophonic v1 |
|
||||
|
||||
**Recommendation:** bundle exercises as small `.abc` files in the repo
|
||||
(`src/exercises/library/`), parsed at runtime by a small subset parser
|
||||
(inline notes, durations, octaves, accidentals, bar lines, key/meter/time
|
||||
headers, simple `|:` `:|` repeats expanded at parse time; no ties/grace
|
||||
notes/tuplets in v1). Seed library (public domain — Giuliani died 1829):
|
||||
|
||||
- Giuliani Op. 1 right-hand arpeggio studies (selection, transcribed)
|
||||
- 1–2 Sor/Carcassi-style étude excerpts if the arpeggios go fast
|
||||
- A handful of well-known public-domain folk melodies
|
||||
- Scale/run patterns as tiny generated "built-ins" if useful
|
||||
|
||||
MIDI stays possible later: an offline converter (Node script) MIDI → same
|
||||
model can be added without touching the app.
|
||||
|
||||
## 6. Technical plan
|
||||
|
||||
New code, mirroring the existing layout:
|
||||
|
||||
```
|
||||
src/
|
||||
├── music/
|
||||
│ ├── scales.ts # scale/mode definitions, key→pitch-class sets
|
||||
│ ├── melodyGenerator.ts # rules → Phrase model (seeded RNG, pure)
|
||||
│ └── NoteUtils.ts # extended: duration→VexFlow glyph mapping (shared)
|
||||
├── exercises/
|
||||
│ ├── model.ts # Phrase / PhraseNote / ExerciseInfo types
|
||||
│ ├── abcParser.ts # ABC subset → Phrase (pure, tested)
|
||||
│ ├── library.ts # bundled .abc index + metadata
|
||||
│ └── library/*.abc # the exercise files themselves
|
||||
├── hooks/
|
||||
│ ├── usePhraseTrainer.ts # state machine from 4.2 (uses MatchTracker)
|
||||
│ └── usePhraseClock.ts # audio-clock lookahead scheduler (tempo mode),
|
||||
│ # patterned after the existing useMetronome
|
||||
├── components/
|
||||
│ ├── PhraseSheetMusic.tsx # multi-bar VexFlow renderer w/ cursor+colors
|
||||
│ ├── PhraseControls.tsx # mode-local buttons/status line
|
||||
│ └── ExercisePicker.tsx # library picker (R7)
|
||||
└── types/SettingsTypes.ts # new phrase-related settings
|
||||
```
|
||||
|
||||
Integration approach in `App.tsx`: pitch detection stays exactly as-is and
|
||||
produces `pitchData` once. If `gameMode === 'phrase'`, App renders the
|
||||
`PhraseTrainer` subtree (staff + controls) fed by the same `pitchData`, mic
|
||||
gate and audio engine; the existing single-note subtree is untouched. This
|
||||
keeps `useGameLogic` clean and guarantees the existing modes are unregressed
|
||||
by construction.
|
||||
|
||||
Settings additions (all optional, with defaults — existing installs keep
|
||||
working):
|
||||
|
||||
```ts
|
||||
phrase?: {
|
||||
material: 'generated' | 'library';
|
||||
libraryId?: string; // selected exercise
|
||||
key: string; // generator key/mode (e.g. 'G', 'Am', 'C-penta-maj')
|
||||
bars: number; // 1–8
|
||||
rhythmLevel: 1 | 2 | 3;
|
||||
pace: 'step' | 'tempo';
|
||||
bpm: number; // tempo mode; defaults to existing rhythm.bpm
|
||||
}
|
||||
```
|
||||
|
||||
Tempo-mode scheduling follows the proven metronome pattern (lookahead timer
|
||||
scheduling against `AudioContext.currentTime`) so cursor movement doesn't
|
||||
drift; note windows are derived from the same schedule, not from `Date.now()`.
|
||||
|
||||
### Testing
|
||||
|
||||
- `melodyGenerator.test.ts` — invariants from 4.1 (pool membership, bar sums,
|
||||
leap rules, determinism under seed).
|
||||
- `abcParser.test.ts` — golden strings for each syntax feature; error cases.
|
||||
- `usePhraseTrainer` logic — extract pure reducer/clock math and test the
|
||||
state machine transitions with a fake clock (step & tempo mode, hit/miss).
|
||||
- Renderer gets a thin smoke test; visual QA happens in the browser.
|
||||
- Full `npm run lint && npm run build && npm test` must pass before finishing.
|
||||
|
||||
### Execution / cost control
|
||||
|
||||
- Core architecture, integration and the clock/trainer logic: implemented
|
||||
directly (these are the parts where mistakes are expensive).
|
||||
- Delegate to cheaper subagents (pi supports spawning agents with a cheaper
|
||||
model, e.g. GLM-5.3-Flash): ABC transcriptions of the Giuliani studies,
|
||||
the parser/generator test corpora, the exercise picker UI and other
|
||||
template-like UI work — each with tight, verifiable specs, and their output
|
||||
re-checked by the main agent before merging.
|
||||
- Each phase ends in a green build + commit, so the night produces usable
|
||||
checkpoints, not one big risky blob.
|
||||
|
||||
## 7. Implementation phases
|
||||
|
||||
| Phase | Contents | Exit criterion |
|
||||
|-------|----------|-----------------|
|
||||
| **P1** | Model + scales + generator (rules 1–5) + multi-bar renderer + Step mode end-to-end (R1–R4, R6 core) | Playable generated melodies in step mode, no clock |
|
||||
| **P2** | Tempo mode: phrase clock, count-in, per-window hit/miss, summary (R5) | Full generated-melody experience |
|
||||
| **P3** | ABC parser + bundled library + picker (R7), Play-preview (R8) | Fixed exercises playable in both paces |
|
||||
| **P4** | Motifs/pentatonic (rule 6–7), S1/S2 if time, Zen-mode polish, keyboard shortcuts, help text, README/TECH docs, final lint/build/tests, push | Night done |
|
||||
|
||||
If time runs short, P1–P3 are the must-have spine; everything in P4 is
|
||||
incremental and can be dropped mid-phase without leaving the app broken.
|
||||
|
||||
## 8. Open decisions — defaults if no feedback
|
||||
|
||||
| # | Decision | Default I'll assume |
|
||||
|---|----------|---------------------|
|
||||
| 8.1 | Mode name / labels | Third toggle labeled **"Phrases"**; existing two keep their current names ("Sight Reading", "Ear Training"). If you'd rather rename the pair (e.g. "Notes" / "Ear" / "Phrases"), say so — cosmetic change. |
|
||||
| 8.2 | Library format | **ABC** (rationale in 5). MIDI deferred to a later offline converter. *If you feel strongly about MIDI as the primary format, this is the decision to veto.* |
|
||||
| 8.3 | Tempo-mode strictness | Lenient: misses are marked, never abort. No per-note gating beyond the existing 50 ms hold. |
|
||||
| 8.4 | Generator keys in v1 | Majors + relative minors from the existing key-signature list + major/minor pentatonic. Church modes: later. |
|
||||
| 8.5 | Phrase length default | 4 bars, 4/4 only in v1 (3/4 comes free with the model; generator stays 4/4 unless trivial to add). |
|
||||
| 8.6 | Hints in phrase mode | Reuse existing hint toggle: fretboard/piano hint marks the **current** note only. |
|
||||
| 8.7 | Publishing | Repo `main` is currently 4 commits ahead of `origin`. Pushing `main` triggers the GitHub Pages deploy. Default: at the end of the night, push **everything** (your 4 commits + mine) after a green build, so the work isn't lost. Say "don't push" if you'd rather review first. |
|
||||
| 8.8 | Seed library size | ~10–15 items (5–8 Giuliani arpeggio studies, 3–5 folk tunes, 1–2 technical patterns). Quality-checked, not bulk-dumped. |
|
||||
|
||||
**Approval:** reply "Go" (optionally with edits/vetoes on the table above) and
|
||||
I'll implement autonomously overnight following P1→P4.
|
||||
Reference in New Issue
Block a user