mirror of
https://github.com/9x/sheetmusictrainer.git
synced 2026-11-03 22:03:54 +01:00
Fix double metronome click in single-note modes
useGameLogic's own scheduler and the footer widget's scheduler both played audible clicks while rhythm.active — assist->learn-notes switch made the second one mount. Now the widget is the single click source in non-zen modes; useGameLogic's scheduler is a silent tick source for auto-advance (full sound only in zen mode, where the widget isn't rendered). Also: widget honors Timer mode (60/seconds) like the trainer, and useMetronome skips audio nodes at volume 0.
This commit is contained in:
@@ -65,8 +65,12 @@ export const MetronomeWidget: React.FC<MetronomeWidgetProps> = ({
|
|||||||
// Free/plain: the widget's own scheduler runs whenever armed.
|
// Free/plain: the widget's own scheduler runs whenever armed.
|
||||||
const running = isSync ? runActive : armed;
|
const running = isSync ? runActive : armed;
|
||||||
|
|
||||||
|
// Timer mode = one beat per N seconds: effective BPM 60/seconds so
|
||||||
|
// clicks and pendulum match the trainer's auto-advance cadence.
|
||||||
|
const effectiveBpm = rhythm.mode === 'bpm' ? rhythm.bpm : 60 / Math.max(1, rhythm.seconds);
|
||||||
|
|
||||||
const { restart } = useMetronome({
|
const { restart } = useMetronome({
|
||||||
bpm: rhythm.bpm,
|
bpm: effectiveBpm,
|
||||||
volume: rhythm.sound ? rhythm.volume : 0,
|
volume: rhythm.sound ? rhythm.volume : 0,
|
||||||
playing: running && !isSync,
|
playing: running && !isSync,
|
||||||
onTick: handleTick,
|
onTick: handleTick,
|
||||||
@@ -82,7 +86,7 @@ export const MetronomeWidget: React.FC<MetronomeWidgetProps> = ({
|
|||||||
});
|
});
|
||||||
}, [isSync, runActive]);
|
}, [isSync, runActive]);
|
||||||
|
|
||||||
const beatMs = 60000 / Math.max(1, rhythm.bpm);
|
const beatMs = 60000 / Math.max(1, effectiveBpm);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||||
|
|||||||
@@ -85,7 +85,11 @@ export const useGameLogic = (
|
|||||||
|
|
||||||
const { restart: restartMetronome } = useMetronome({
|
const { restart: restartMetronome } = useMetronome({
|
||||||
bpm: effectiveBpm,
|
bpm: effectiveBpm,
|
||||||
volume: settings.rhythm.sound ? settings.rhythm.volume : 0,
|
// Audible clicks come from the shared footer widget (Controls) in
|
||||||
|
// non-zen single-note modes — this scheduler is only the tick source
|
||||||
|
// for auto-advance. Sounding it here too doubles every beat. In zen
|
||||||
|
// mode the widget isn't rendered, so this scheduler IS the click.
|
||||||
|
volume: settings.zenMode && settings.rhythm.sound ? settings.rhythm.volume : 0,
|
||||||
playing: settings.rhythm.active,
|
playing: settings.rhythm.active,
|
||||||
onTick: handleTick
|
onTick: handleTick
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export function useMetronome({ bpm, volume, playing, onTick }: MetronomeOptions)
|
|||||||
|
|
||||||
const playClick = useCallback((time: number) => {
|
const playClick = useCallback((time: number) => {
|
||||||
if (!audioContext.current) return;
|
if (!audioContext.current) return;
|
||||||
|
onTickRef.current(currentBeat.current);
|
||||||
|
if (volume <= 0) return; // silent tick source — no audio nodes needed
|
||||||
|
|
||||||
const osc = audioContext.current.createOscillator();
|
const osc = audioContext.current.createOscillator();
|
||||||
const gainNode = audioContext.current.createGain();
|
const gainNode = audioContext.current.createGain();
|
||||||
|
|||||||
Reference in New Issue
Block a user