diff --git a/src/components/PhraseSheetMusic.tsx b/src/components/PhraseSheetMusic.tsx index d74ec50..c85ab60 100644 --- a/src/components/PhraseSheetMusic.tsx +++ b/src/components/PhraseSheetMusic.tsx @@ -272,6 +272,14 @@ export const PhraseSheetMusic: React.FC = ({ if (r === 0) treble.addTimeSignature(`${score.meter.numerator}/${score.meter.denominator}`); treble.setMeasure(measure.number); } + // Chord symbol above the staff (bar-indexed), drawn via the + // stave's annotation area so it shifts with the clef offset. + const chordSym = score.chordSymbols?.[m]; + if (chordSym) { + try { + treble.setSection(chordSym, 0); + } catch { /* courtesy label — never fatal */ } + } let bass: Stave | null = null; if (isGrand) { bass = new Stave(x, y + 130, barWidth); diff --git a/src/components/PhraseTrainer.tsx b/src/components/PhraseTrainer.tsx index 9f8ff30..49c16ab 100644 --- a/src/components/PhraseTrainer.tsx +++ b/src/components/PhraseTrainer.tsx @@ -210,6 +210,10 @@ export const PhraseTrainer = forwardRef( pattern: phrase.arpeggioPattern, coverage: phrase.arpeggioCoverage, meter: { numerator: phrase.meterNumerator, denominator: 4 }, + bars: phrase.arpeggioBars, + progression: phrase.arpeggioProgression, + rhythm: phrase.arpeggioRhythm, + seed: melodySeed, }, pool); } if (phrase.material === 'library') { @@ -225,6 +229,7 @@ export const PhraseTrainer = forwardRef( }, [phrase.material, phrase.keyTonic, phrase.keyMode, phrase.bars, phrase.meterNumerator, phrase.rhythmLevel, phrase.scaleDirection, phrase.scaleCoverage, phrase.libraryId, phrase.arpeggioDegree, phrase.arpeggioPattern, phrase.arpeggioCoverage, + phrase.arpeggioBars, phrase.arpeggioProgression, phrase.arpeggioRhythm, melodySeed, melodySeed, pool, importedScore]); const fullScore: Result | null = useMemo(() => { @@ -252,6 +257,7 @@ export const PhraseTrainer = forwardRef( clickSound: phrase.clickSound, inputMode: phrase.inputMode, previewVolume: settings.autoPlayVolume ?? 0.4, + autoStartOnNote: phrase.autoStartOnNote, }, listening, micError, @@ -310,7 +316,7 @@ export const PhraseTrainer = forwardRef( }, [scoreOk, fixedMaterial, pool, fullScore]); const nextAction = useCallback(() => { - if (phrase.material === 'melody') { + if (phrase.material === 'melody' || (phrase.material === 'arpeggio' && phrase.arpeggioBars > 1)) { setMelodySeed(s => (s + 1) % 999983); } else if (fixedMaterial && scoreOk) { const total = fullScore!.value.measures.length; @@ -499,7 +505,7 @@ export const PhraseTrainer = forwardRef( Retry - {(phrase.material === 'melody' || fixedMaterial) && ( + {(phrase.material === 'melody' || phrase.material === 'arpeggio' || fixedMaterial) && ( - {(phrase.material === 'melody' || fixedMaterial) && ( + {(phrase.material === 'melody' || phrase.material === 'arpeggio' || fixedMaterial) && (