mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-17 01:50:47 -04:00
gologle
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -11,8 +11,8 @@
|
|||||||
<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-ChF3_rCg.js"></script>
|
<script type="module" crossorigin src="/assets/index-D8STCiny.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-BhESptqT.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-Bz1ixYh7.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ export default function VideoTile({
|
|||||||
const [audioStatus, setAudioStatus] = useState('idle');
|
const [audioStatus, setAudioStatus] = useState('idle');
|
||||||
const [audioDetail, setAudioDetail] = useState(null);
|
const [audioDetail, setAudioDetail] = useState(null);
|
||||||
const [levelIndicator, setLevelIndicator] = useState(null);
|
const [levelIndicator, setLevelIndicator] = useState(null);
|
||||||
|
const [compressorActive, setCompressorActive] = useState(false);
|
||||||
const [restartToken, setRestartToken] = useState(0);
|
const [restartToken, setRestartToken] = useState(0);
|
||||||
const [audioRestartToken, setAudioRestartToken] = useState(0);
|
const [audioRestartToken, setAudioRestartToken] = useState(0);
|
||||||
const [muted, setMuted] = useState(true);
|
const [muted, setMuted] = useState(true);
|
||||||
@@ -415,26 +416,26 @@ export default function VideoTile({
|
|||||||
clearInterval(reductionPollRef.current);
|
clearInterval(reductionPollRef.current);
|
||||||
reductionPollRef.current = null;
|
reductionPollRef.current = null;
|
||||||
if (!audioEl || !audioSessionInfo?.url) {
|
if (!audioEl || !audioSessionInfo?.url) {
|
||||||
|
setCompressorActive(false);
|
||||||
setLevelIndicator(null);
|
setLevelIndicator(null);
|
||||||
logAudio('route/no-audio-url');
|
logAudio('route/no-audio-url');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 1 stabilization: bypass WebAudio compressor path and use plain media element audio.
|
const compressorMode = autoLevelEnabled && autoLevelMode === 'compressor';
|
||||||
// This restores the known-good autoplay/retry behavior while we isolate Chrome-specific issues.
|
|
||||||
const compressorMode = false;
|
|
||||||
const duckMode = autoLevelEnabled && autoLevelMode === 'duck';
|
const duckMode = autoLevelEnabled && autoLevelMode === 'duck';
|
||||||
const graphReady = compressorMode && ensureAudioGraph();
|
const graphReady = compressorMode ? ensureAudioGraph() : false;
|
||||||
|
|
||||||
if (graphReady) {
|
if (graphReady) {
|
||||||
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';
|
||||||
// Keep direct element audio until WebAudio is actually running in Chrome.
|
const canActivate = Boolean(contextRunning && compressorActive);
|
||||||
audioEl.volume = contextRunning ? 0 : effectiveRoverGain;
|
// Strict gating: graph-only output only after context is running and compressor is explicitly activated.
|
||||||
logAudio('route/graph', { contextRunning, elementVolume: audioEl.volume });
|
audioEl.volume = canActivate ? 0 : effectiveRoverGain;
|
||||||
|
logAudio('route/graph', { contextRunning, canActivate, elementVolume: audioEl.volume });
|
||||||
if (gainNode) {
|
if (gainNode) {
|
||||||
const graphGain = contextRunning
|
const graphGain = canActivate
|
||||||
? Math.max(0, Math.min(4, effectiveRoverGain * COMPRESSOR_MAKEUP_GAIN))
|
? Math.max(0, Math.min(4, effectiveRoverGain * COMPRESSOR_MAKEUP_GAIN))
|
||||||
: 0;
|
: 0;
|
||||||
gainNode.gain.value = graphGain;
|
gainNode.gain.value = graphGain;
|
||||||
@@ -447,25 +448,29 @@ export default function VideoTile({
|
|||||||
compressor.attack.value = COMPRESSOR_SETTINGS.attack;
|
compressor.attack.value = COMPRESSOR_SETTINGS.attack;
|
||||||
compressor.release.value = COMPRESSOR_SETTINGS.release;
|
compressor.release.value = COMPRESSOR_SETTINGS.release;
|
||||||
}
|
}
|
||||||
resumeAudioContext();
|
if (canActivate) {
|
||||||
reductionPollRef.current = setInterval(() => {
|
reductionPollRef.current = setInterval(() => {
|
||||||
if (audioContextRef.current?.state !== 'running') {
|
if (audioContextRef.current?.state !== 'running') {
|
||||||
setLevelIndicator(null);
|
setLevelIndicator(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const reduction = compressor?.reduction;
|
const reduction = compressor?.reduction;
|
||||||
if (typeof reduction === 'number' && reduction <= COMPRESSOR_REDUCTION_ACTIVE_DB) {
|
if (typeof reduction === 'number' && reduction <= COMPRESSOR_REDUCTION_ACTIVE_DB) {
|
||||||
const amount = Math.round(Math.abs(reduction) * 10) / 10;
|
const amount = Math.round(Math.abs(reduction) * 10) / 10;
|
||||||
const formatted = Number.isInteger(amount) ? amount.toFixed(0) : amount.toFixed(1);
|
const formatted = Number.isInteger(amount) ? amount.toFixed(0) : amount.toFixed(1);
|
||||||
setLevelIndicator(`Level: ${formatted}dB`);
|
setLevelIndicator(`Level: ${formatted}dB`);
|
||||||
logAudio('compressor/reduction', { reduction, formatted });
|
logAudio('compressor/reduction', { reduction, formatted });
|
||||||
} else {
|
} else {
|
||||||
setLevelIndicator(null);
|
setLevelIndicator(null);
|
||||||
}
|
}
|
||||||
}, 180);
|
}, 180);
|
||||||
|
} else {
|
||||||
|
setLevelIndicator(null);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCompressorActive(false);
|
||||||
audioEl.volume = effectiveRoverGain;
|
audioEl.volume = effectiveRoverGain;
|
||||||
logAudio('route/direct', { elementVolume: audioEl.volume, duckMode, brushOrVacuumActive });
|
logAudio('route/direct', { elementVolume: audioEl.volume, duckMode, brushOrVacuumActive });
|
||||||
if (audioCompressorRef.current) {
|
if (audioCompressorRef.current) {
|
||||||
@@ -489,9 +494,9 @@ export default function VideoTile({
|
|||||||
autoLevelEnabled,
|
autoLevelEnabled,
|
||||||
autoLevelMode,
|
autoLevelMode,
|
||||||
brushOrVacuumActive,
|
brushOrVacuumActive,
|
||||||
|
compressorActive,
|
||||||
effectiveRoverGain,
|
effectiveRoverGain,
|
||||||
ensureAudioGraph,
|
ensureAudioGraph,
|
||||||
resumeAudioContext,
|
|
||||||
logAudio,
|
logAudio,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -517,11 +522,6 @@ export default function VideoTile({
|
|||||||
});
|
});
|
||||||
if (nextStatus === 'playing') {
|
if (nextStatus === 'playing') {
|
||||||
resumeAudioContext();
|
resumeAudioContext();
|
||||||
const target = audioRef.current;
|
|
||||||
if (target) {
|
|
||||||
target.muted = false;
|
|
||||||
target.play().then(() => logAudio('audio/play-ok')).catch((err) => logAudio('audio/play-fail', { error: err?.message }));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (['error', 'failed'].includes(nextStatus)) {
|
if (['error', 'failed'].includes(nextStatus)) {
|
||||||
scheduleAudioRestart();
|
scheduleAudioRestart();
|
||||||
@@ -573,9 +573,18 @@ export default function VideoTile({
|
|||||||
const target = audioRef.current;
|
const target = audioRef.current;
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
target.muted = false;
|
target.muted = false;
|
||||||
|
const wantCompressor = autoLevelEnabled && autoLevelMode === 'compressor';
|
||||||
|
if (wantCompressor) {
|
||||||
|
ensureAudioGraph();
|
||||||
|
await resumeAudioContext();
|
||||||
|
const running = audioContextRef.current?.state === 'running';
|
||||||
|
if (running && !compressorActive) {
|
||||||
|
setCompressorActive(true);
|
||||||
|
logAudio('compressor/activated');
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!target.paused && !target.ended) return;
|
if (!target.paused && !target.ended) return;
|
||||||
logAudio('retry/play-attempt');
|
logAudio('retry/play-attempt');
|
||||||
await resumeAudioContext();
|
|
||||||
target
|
target
|
||||||
.play()
|
.play()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -593,7 +602,16 @@ export default function VideoTile({
|
|||||||
audioPlayInterval.current = setInterval(attemptPlay, AUDIO_RETRY_MS);
|
audioPlayInterval.current = setInterval(attemptPlay, AUDIO_RETRY_MS);
|
||||||
|
|
||||||
return () => clearInterval(audioPlayInterval.current);
|
return () => clearInterval(audioPlayInterval.current);
|
||||||
}, [audioSessionInfo?.url, audioStatus, resumeAudioContext, logAudio]);
|
}, [
|
||||||
|
audioSessionInfo?.url,
|
||||||
|
audioStatus,
|
||||||
|
autoLevelEnabled,
|
||||||
|
autoLevelMode,
|
||||||
|
compressorActive,
|
||||||
|
ensureAudioGraph,
|
||||||
|
resumeAudioContext,
|
||||||
|
logAudio,
|
||||||
|
]);
|
||||||
|
|
||||||
// Reflect audio element events back into status/detail so the HUD stays accurate.
|
// Reflect audio element events back into status/detail so the HUD stays accurate.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user