mirror of
https://github.com/9x/sheetmusictrainer.git
synced 2026-11-03 22:03:54 +01:00
Metronome/phrase click unification: run-driven beats, gated widget
- Phrase Mode (sync to metronome): the run's scheduler is the single click source (with count-in); the metronome widget is muted and its pendulum mirrors the run's beats via the new phraseBeatBus (audio-timestamp scheduled). No more double clicks; clicks only while the run is active (phraseRunBus gates the widget's own ticking). - Schedule entries now carry continuous beat index + accent flag (downbeats accented, count-in beat 1 accented). - 'At your pace': optional click via the metronome toggle (kept explicit). - Metronome widget: gate prop; in gated mode it listens to the beat bus instead of running its own scheduler.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Guitar, Music, Gauge } from 'lucide-react';
|
||||
import { TUNINGS, INSTRUMENT_TUNINGS } from '../music/Tunings';
|
||||
import { INSTRUMENT_DEFINITIONS } from '../music/InstrumentConfigs';
|
||||
@@ -5,7 +6,7 @@ import { INSTRUMENT_DEFINITIONS } from '../music/InstrumentConfigs';
|
||||
import { TuningMeter } from './TuningMeter';
|
||||
import { TargetNoteControls } from './TargetNoteControls';
|
||||
import { MetronomeWidget } from './MetronomeWidget';
|
||||
|
||||
import { phraseRunBus } from '../hooks/phraseRunBus';
|
||||
import { useSettings } from '../context/useSettings';
|
||||
import { type Difficulty, type RhythmSettings } from '../types/SettingsTypes';
|
||||
|
||||
@@ -19,6 +20,10 @@ export const Controls: React.FC<ControlsProps> = ({ currentPitch }) => {
|
||||
const { settings, updateSettings } = useSettings();
|
||||
// Alias to minimize refactor, or just use updateSettings.
|
||||
const onUpdateSettings = updateSettings;
|
||||
// Phrase run gate: the metronome only ticks while the phrase trainer is
|
||||
// counting in / playing (armed by the user toggle, fired by the run).
|
||||
const [phraseRunActive, setPhraseRunActive] = useState(false);
|
||||
useEffect(() => phraseRunBus.subscribe(setPhraseRunActive), []);
|
||||
const handleTuningChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
onUpdateSettings({ ...settings, tuningId: e.target.value });
|
||||
};
|
||||
@@ -176,6 +181,7 @@ export const Controls: React.FC<ControlsProps> = ({ currentPitch }) => {
|
||||
rhythm={settings.rhythm}
|
||||
onUpdate={updateRhythm}
|
||||
showAutoAdvance={!inPhraseMode}
|
||||
gate={inPhraseMode ? phraseRunActive : null}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user