Arpeggio sequences + auto-start on first note

- Sequence mode: bars (2/4/6/8) with one chord per bar, chosen by
  functional rules (I->IV/V/vi...), diatonic cycle, or random (no immediate
  repeats); deterministic via melody seed. Chord symbols per bar in the
  score model (chordSymbols) rendered above the staff via setSection.
- Rhythm: quarters or eighths (cycling up/down through the pattern).
- Single-chord mode unchanged (whole path, overflow bars if needed).
- Auto-start: 'Start on first note (no count-in)' — step pace begins the
  run as soon as the mic detects the first note; no start press needed.
- 'New' button re-rolls the progression (new seed).
- Tests 174/174.
This commit is contained in:
OpenClaw
2026-09-14 18:44:14 +00:00
parent d76d24ab01
commit 263b6f6b69
7 changed files with 265 additions and 27 deletions

View File

@@ -57,6 +57,10 @@ export interface PhraseSettings {
arpeggioDegree: string; // ArpeggioDegree
arpeggioPattern: 'up' | 'down' | 'updown' | '1235';
arpeggioCoverage: 'one-octave' | 'two-octave';
/** Sequence mode: bars per exercise (1 = single chord). */
arpeggioBars: number;
arpeggioProgression: 'random' | 'functional' | 'diatonic-cycle';
arpeggioRhythm: 'quarters' | 'eighths';
/** Phrase-local fret window (guitar/bass) — overrides the note set. */
fretWindowEnabled: boolean;
fretMin: number;
@@ -71,6 +75,9 @@ export interface PhraseSettings {
* new melody) after a short pause — keeps practice flowing. */
autoContinue: boolean;
inputMode: 'mic' | 'virtual';
/** Begin the run as soon as the first note is played (no count-in,
* step pace only). */
autoStartOnNote: boolean;
}
export const DEFAULT_PHRASE_SETTINGS: PhraseSettings = {
@@ -86,6 +93,9 @@ export const DEFAULT_PHRASE_SETTINGS: PhraseSettings = {
arpeggioDegree: 'I',
arpeggioPattern: 'up',
arpeggioCoverage: 'one-octave',
arpeggioBars: 1,
arpeggioProgression: 'functional',
arpeggioRhythm: 'quarters',
fretWindowEnabled: false,
fretMin: 0,
fretMax: 4,
@@ -96,6 +106,7 @@ export const DEFAULT_PHRASE_SETTINGS: PhraseSettings = {
clickSound: false,
autoContinue: true,
inputMode: 'mic',
autoStartOnNote: false,
};
export function getPhraseSettings(s: AppSettings): PhraseSettings {