beep
This commit is contained in:
116
script.js
116
script.js
@@ -2180,35 +2180,61 @@ function initListenerMode() {
|
||||
|
||||
// AudioContext will be created when user enables audio to avoid suspension
|
||||
|
||||
// Create or reuse audio element to handle the MediaSource
|
||||
// ALWAYS create a fresh audio element to avoid MediaSource/MediaElementSource conflicts
|
||||
// This is critical for page refreshes - you can only create MediaElementSource once per element
|
||||
let audio;
|
||||
|
||||
// Clean up old audio element if it exists
|
||||
if (window.listenerAudio) {
|
||||
// Reuse existing audio element from previous initialization
|
||||
audio = window.listenerAudio;
|
||||
console.log('♻️ Reusing existing audio element');
|
||||
|
||||
// Clean up old MediaSource if it exists
|
||||
if (audio.src) {
|
||||
URL.revokeObjectURL(audio.src);
|
||||
audio.removeAttribute('src');
|
||||
audio.load(); // Reset the element
|
||||
console.log('🧹 Cleaning up old audio element and AudioContext nodes');
|
||||
try {
|
||||
window.listenerAudio.pause();
|
||||
if (window.listenerAudio.src) {
|
||||
URL.revokeObjectURL(window.listenerAudio.src);
|
||||
}
|
||||
window.listenerAudio.removeAttribute('src');
|
||||
window.listenerAudio.remove(); // Remove from DOM
|
||||
} catch (e) {
|
||||
console.warn('Error cleaning up old audio:', e);
|
||||
}
|
||||
} else {
|
||||
// Create a new hidden media element.
|
||||
// Note: MSE (MediaSource) support is often more reliable on <video> than <audio>.
|
||||
audio = document.createElement('video');
|
||||
audio.autoplay = false; // Don't autoplay - we use the Enable Audio button
|
||||
audio.muted = false;
|
||||
audio.controls = false;
|
||||
audio.playsInline = true;
|
||||
audio.setAttribute('playsinline', '');
|
||||
audio.style.display = 'none';
|
||||
document.body.appendChild(audio);
|
||||
console.log('🆕 Created new media element (video) for listener');
|
||||
|
||||
// AudioContext will be created later on user interaction
|
||||
// Reset all AudioContext-related nodes
|
||||
if (listenerMediaElementSourceNode) {
|
||||
try {
|
||||
listenerMediaElementSourceNode.disconnect();
|
||||
} catch (e) { }
|
||||
listenerMediaElementSourceNode = null;
|
||||
}
|
||||
if (listenerAnalyserNode) {
|
||||
try {
|
||||
listenerAnalyserNode.disconnect();
|
||||
} catch (e) { }
|
||||
listenerAnalyserNode = null;
|
||||
}
|
||||
if (listenerGainNode) {
|
||||
try {
|
||||
listenerGainNode.disconnect();
|
||||
} catch (e) { }
|
||||
listenerGainNode = null;
|
||||
}
|
||||
|
||||
window.listenerAudio = null;
|
||||
window.listenerMediaSource = null;
|
||||
window.listenerAudioEnabled = false;
|
||||
}
|
||||
|
||||
// Create a new hidden media element.
|
||||
// Note: MSE (MediaSource) support is often more reliable on <video> than <audio>.
|
||||
audio = document.createElement('video');
|
||||
audio.autoplay = false; // Don't autoplay - we use the Enable Audio button
|
||||
audio.muted = false;
|
||||
audio.controls = false;
|
||||
audio.playsInline = true;
|
||||
audio.setAttribute('playsinline', '');
|
||||
audio.style.display = 'none';
|
||||
document.body.appendChild(audio);
|
||||
console.log('🆕 Created fresh media element (video) for listener');
|
||||
|
||||
// Initialize MediaSource for streaming binary chunks
|
||||
const mediaSource = new MediaSource();
|
||||
audio.src = URL.createObjectURL(mediaSource);
|
||||
@@ -2227,7 +2253,7 @@ function initListenerMode() {
|
||||
mediaSource.addEventListener('sourceopen', () => {
|
||||
console.log('📦 MediaSource opened');
|
||||
const mimeType = window.currentStreamMimeType || currentStreamMimeType || 'audio/webm;codecs=opus';
|
||||
|
||||
|
||||
if (!MediaSource.isTypeSupported(mimeType)) {
|
||||
console.error(`❌ Browser does not support ${mimeType}`);
|
||||
const statusEl = document.getElementById('connection-status');
|
||||
@@ -2470,8 +2496,38 @@ async function enableListenerAudio() {
|
||||
return window.listenerAudio.buffered && window.listenerAudio.buffered.length > 0;
|
||||
};
|
||||
|
||||
// Attempt playback IMMEDIATELY to capture user gesture
|
||||
// We do this before waiting for data so we don't lose the "user interaction" token
|
||||
// CRITICAL: Wait for buffered data before calling play()
|
||||
// This prevents NotSupportedError when buffer is empty
|
||||
if (!hasBufferedData()) {
|
||||
console.log('⏳ Waiting for audio data to buffer before playback...');
|
||||
if (audioText) audioText.textContent = 'BUFFERING...';
|
||||
|
||||
// Wait for data with timeout (max 5 seconds)
|
||||
const waitForData = new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
clearInterval(checkInterval);
|
||||
reject(new Error('Timeout waiting for audio data'));
|
||||
}, 5000);
|
||||
|
||||
const checkInterval = setInterval(() => {
|
||||
if (hasBufferedData()) {
|
||||
clearInterval(checkInterval);
|
||||
clearTimeout(timeout);
|
||||
console.log('✅ Audio data buffered, ready to play');
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
try {
|
||||
await waitForData;
|
||||
} catch (e) {
|
||||
console.warn('⚠️ Timeout waiting for buffer data:', e.message);
|
||||
}
|
||||
} else {
|
||||
console.log('✅ Audio already has buffered data');
|
||||
}
|
||||
|
||||
console.log('▶️ Attempting to play audio...');
|
||||
const playPromise = window.listenerAudio.play();
|
||||
|
||||
@@ -2482,13 +2538,13 @@ async function enableListenerAudio() {
|
||||
if (audioText) {
|
||||
audioText.textContent = chunkCount > 0 ? 'BUFFERING...' : 'WAITING FOR STREAM...';
|
||||
}
|
||||
|
||||
|
||||
// Start a background checker to update UI
|
||||
const checkInterval = setInterval(() => {
|
||||
if (hasBufferedData()) {
|
||||
clearInterval(checkInterval);
|
||||
console.log('✅ Audio data buffered');
|
||||
const chunkCount = Number.isFinite(listenerChunksReceived) ? listenerChunksReceived : 0;
|
||||
const chunkCount = Number.isFinite(listenerChunksReceived) ? listenerChunksReceived : 0;
|
||||
} else if (audioText && chunkCount > 0 && audioText.textContent === 'WAITING FOR STREAM...') {
|
||||
audioText.textContent = 'BUFFERING...';
|
||||
}
|
||||
@@ -2532,9 +2588,9 @@ async function enableListenerAudio() {
|
||||
} else if (error.name === 'NotSupportedError') {
|
||||
errorMsg = 'Format not supported or buffer empty (NotSupportedError).';
|
||||
}
|
||||
|
||||
|
||||
stashedStatus.textContent = '⚠️ ' + errorMsg;
|
||||
|
||||
|
||||
if (error.name === 'NotSupportedError') {
|
||||
// Two common causes:
|
||||
// 1) WebM/Opus MSE isn't supported by this browser
|
||||
|
||||
Reference in New Issue
Block a user