mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
gawa
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@
|
|||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
||||||
<title>Multi Roomba Rover</title>
|
<title>Multi Roomba Rover</title>
|
||||||
<script type="module" crossorigin src="/assets/index-Ck-dlrgH.js"></script>
|
<script type="module" crossorigin src="/assets/index-BkBPhgTa.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-Bz1ixYh7.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-Bz1ixYh7.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -133,8 +133,9 @@ export default function VideoTile({
|
|||||||
if (!debugAudio) return;
|
if (!debugAudio) return;
|
||||||
const audioEl = audioRef.current;
|
const audioEl = audioRef.current;
|
||||||
const ctx = audioContextRef.current;
|
const ctx = audioContextRef.current;
|
||||||
console.log('[AudioDebug]', {
|
const payload = {
|
||||||
event,
|
event,
|
||||||
|
ts: Date.now(),
|
||||||
roverLabel: label || null,
|
roverLabel: label || null,
|
||||||
hasDedicatedAudio,
|
hasDedicatedAudio,
|
||||||
audioUrl: audioSessionInfo?.url || null,
|
audioUrl: audioSessionInfo?.url || null,
|
||||||
@@ -159,7 +160,12 @@ export default function VideoTile({
|
|||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
...meta,
|
...meta,
|
||||||
});
|
};
|
||||||
|
try {
|
||||||
|
console.log(`[AudioDebug] ${event} ${JSON.stringify(payload)}`);
|
||||||
|
} catch {
|
||||||
|
console.log('[AudioDebug]', event, payload);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
debugAudio,
|
debugAudio,
|
||||||
@@ -265,25 +271,75 @@ export default function VideoTile({
|
|||||||
logAudio('context/resume-result');
|
logAudio('context/resume-result');
|
||||||
}, [logAudio]);
|
}, [logAudio]);
|
||||||
|
|
||||||
|
const forceUnlockCompressorAudio = useCallback(async () => {
|
||||||
|
if (!audioSessionInfo?.url || !autoLevelEnabled || autoLevelMode !== 'compressor') return;
|
||||||
|
const graphReady = ensureAudioGraph();
|
||||||
|
if (!graphReady) {
|
||||||
|
logAudio('unlock/no-graph');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await resumeAudioContext();
|
||||||
|
const target = audioRef.current;
|
||||||
|
const gainNode = audioGainRef.current;
|
||||||
|
if (gainNode) {
|
||||||
|
gainNode.gain.value = effectiveRoverGain;
|
||||||
|
}
|
||||||
|
if (!target) {
|
||||||
|
logAudio('unlock/no-audio-element');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Compressor mode uses graph-only output.
|
||||||
|
target.volume = 0;
|
||||||
|
target.muted = false;
|
||||||
|
target
|
||||||
|
.play()
|
||||||
|
.then(() => logAudio('unlock/force-play-ok'))
|
||||||
|
.catch((err) => logAudio('unlock/force-play-fail', { error: err?.message }));
|
||||||
|
}, [
|
||||||
|
audioSessionInfo?.url,
|
||||||
|
autoLevelEnabled,
|
||||||
|
autoLevelMode,
|
||||||
|
ensureAudioGraph,
|
||||||
|
resumeAudioContext,
|
||||||
|
effectiveRoverGain,
|
||||||
|
logAudio,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!audioSessionInfo?.url || !autoLevelEnabled || autoLevelMode !== 'compressor') {
|
if (!audioSessionInfo?.url || !autoLevelEnabled || autoLevelMode !== 'compressor') {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const unlock = () => {
|
const unlock = () => {
|
||||||
logAudio('unlock/gesture');
|
logAudio('unlock/gesture');
|
||||||
resumeAudioContext();
|
forceUnlockCompressorAudio();
|
||||||
const target = audioRef.current;
|
|
||||||
if (!target) return;
|
|
||||||
target.muted = false;
|
|
||||||
target.play().then(() => logAudio('unlock/play-ok')).catch((err) => logAudio('unlock/play-fail', { error: err?.message }));
|
|
||||||
};
|
};
|
||||||
window.addEventListener('pointerdown', unlock, { passive: true });
|
window.addEventListener('pointerdown', unlock, { passive: true, capture: true });
|
||||||
|
window.addEventListener('touchstart', unlock, { passive: true, capture: true });
|
||||||
|
window.addEventListener('click', unlock, { passive: true, capture: true });
|
||||||
window.addEventListener('keydown', unlock);
|
window.addEventListener('keydown', unlock);
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('pointerdown', unlock);
|
window.removeEventListener('pointerdown', unlock);
|
||||||
|
window.removeEventListener('touchstart', unlock);
|
||||||
|
window.removeEventListener('click', unlock);
|
||||||
window.removeEventListener('keydown', unlock);
|
window.removeEventListener('keydown', unlock);
|
||||||
};
|
};
|
||||||
}, [audioSessionInfo?.url, autoLevelEnabled, autoLevelMode, resumeAudioContext, logAudio]);
|
}, [audioSessionInfo?.url, autoLevelEnabled, autoLevelMode, forceUnlockCompressorAudio, logAudio]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!audioSessionInfo?.url || !autoLevelEnabled || autoLevelMode !== 'compressor') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
const ctx = audioContextRef.current;
|
||||||
|
const statusActive = ['connecting', 'connected', 'playing', 'paused'].includes(audioStatus);
|
||||||
|
if (!statusActive) return;
|
||||||
|
if (!ctx || ctx.state !== 'running') {
|
||||||
|
logAudio('unlock/interval');
|
||||||
|
forceUnlockCompressorAudio();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [audioSessionInfo?.url, autoLevelEnabled, autoLevelMode, audioStatus, forceUnlockCompressorAudio, logAudio]);
|
||||||
|
|
||||||
const ensurePlayback = useCallback(async () => {
|
const ensurePlayback = useCallback(async () => {
|
||||||
const video = videoRef.current;
|
const video = videoRef.current;
|
||||||
@@ -426,11 +482,11 @@ export default function VideoTile({
|
|||||||
const compressor = audioCompressorRef.current;
|
const compressor = audioCompressorRef.current;
|
||||||
const gainNode = audioGainRef.current;
|
const gainNode = audioGainRef.current;
|
||||||
const contextRunning = audioContextRef.current?.state === 'running';
|
const contextRunning = audioContextRef.current?.state === 'running';
|
||||||
// If Chrome has not unlocked WebAudio yet, keep direct element volume so audio still works.
|
// Compressor mode is graph-only: do not route direct element output.
|
||||||
audioEl.volume = contextRunning ? 0 : effectiveRoverGain;
|
audioEl.volume = 0;
|
||||||
logAudio('route/graph', { contextRunning, elementVolume: audioEl.volume });
|
logAudio('route/graph', { contextRunning, elementVolume: audioEl.volume });
|
||||||
if (gainNode) {
|
if (gainNode) {
|
||||||
gainNode.gain.value = effectiveRoverGain;
|
gainNode.gain.value = contextRunning ? effectiveRoverGain : 0;
|
||||||
}
|
}
|
||||||
if (compressor) {
|
if (compressor) {
|
||||||
compressor.threshold.value = COMPRESSOR_SETTINGS.threshold;
|
compressor.threshold.value = COMPRESSOR_SETTINGS.threshold;
|
||||||
@@ -458,9 +514,8 @@ export default function VideoTile({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasGraph = Boolean(audioSourceNodeRef.current && audioGainRef.current);
|
audioEl.volume = effectiveRoverGain;
|
||||||
audioEl.volume = hasGraph ? 0 : effectiveRoverGain;
|
logAudio('route/direct', { elementVolume: audioEl.volume, duckMode, brushOrVacuumActive });
|
||||||
logAudio('route/direct', { hasGraph, elementVolume: audioEl.volume, duckMode, brushOrVacuumActive });
|
|
||||||
if (audioCompressorRef.current) {
|
if (audioCompressorRef.current) {
|
||||||
audioCompressorRef.current.threshold.value = 0;
|
audioCompressorRef.current.threshold.value = 0;
|
||||||
audioCompressorRef.current.knee.value = 0;
|
audioCompressorRef.current.knee.value = 0;
|
||||||
@@ -469,7 +524,8 @@ export default function VideoTile({
|
|||||||
audioCompressorRef.current.release.value = 0.25;
|
audioCompressorRef.current.release.value = 0.25;
|
||||||
}
|
}
|
||||||
if (audioGainRef.current) {
|
if (audioGainRef.current) {
|
||||||
audioGainRef.current.gain.value = effectiveRoverGain;
|
// In direct route, silence graph output to avoid duplicate path.
|
||||||
|
audioGainRef.current.gain.value = 0;
|
||||||
}
|
}
|
||||||
if (duckMode && brushOrVacuumActive) {
|
if (duckMode && brushOrVacuumActive) {
|
||||||
setLevelIndicator('Level: ducking');
|
setLevelIndicator('Level: ducking');
|
||||||
@@ -509,6 +565,7 @@ export default function VideoTile({
|
|||||||
});
|
});
|
||||||
if (nextStatus === 'playing') {
|
if (nextStatus === 'playing') {
|
||||||
resumeAudioContext();
|
resumeAudioContext();
|
||||||
|
forceUnlockCompressorAudio();
|
||||||
const target = audioRef.current;
|
const target = audioRef.current;
|
||||||
if (target) {
|
if (target) {
|
||||||
target.muted = false;
|
target.muted = false;
|
||||||
@@ -539,7 +596,15 @@ export default function VideoTile({
|
|||||||
active = false;
|
active = false;
|
||||||
player?.stop();
|
player?.stop();
|
||||||
};
|
};
|
||||||
}, [audioSessionInfo?.url, audioSessionInfo?.token, audioRestartToken, scheduleAudioRestart, resumeAudioContext, logAudio]);
|
}, [
|
||||||
|
audioSessionInfo?.url,
|
||||||
|
audioSessionInfo?.token,
|
||||||
|
audioRestartToken,
|
||||||
|
scheduleAudioRestart,
|
||||||
|
resumeAudioContext,
|
||||||
|
forceUnlockCompressorAudio,
|
||||||
|
logAudio,
|
||||||
|
]);
|
||||||
|
|
||||||
// Keep nudging the audio element to play in case autoplay was blocked.
|
// Keep nudging the audio element to play in case autoplay was blocked.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user