forked from computertech/techdj
beep
This commit is contained in:
Binary file not shown.
90
script.js
90
script.js
@@ -2180,20 +2180,49 @@ function initListenerMode() {
|
|||||||
|
|
||||||
// AudioContext will be created when user enables audio to avoid suspension
|
// 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;
|
let audio;
|
||||||
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
|
// Clean up old audio element if it exists
|
||||||
if (audio.src) {
|
if (window.listenerAudio) {
|
||||||
URL.revokeObjectURL(audio.src);
|
console.log('🧹 Cleaning up old audio element and AudioContext nodes');
|
||||||
audio.removeAttribute('src');
|
try {
|
||||||
audio.load(); // Reset the element
|
window.listenerAudio.pause();
|
||||||
|
if (window.listenerAudio.src) {
|
||||||
|
URL.revokeObjectURL(window.listenerAudio.src);
|
||||||
}
|
}
|
||||||
} else {
|
window.listenerAudio.removeAttribute('src');
|
||||||
|
window.listenerAudio.remove(); // Remove from DOM
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Error cleaning up old audio:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
// Create a new hidden media element.
|
||||||
// Note: MSE (MediaSource) support is often more reliable on <video> than <audio>.
|
// Note: MSE (MediaSource) support is often more reliable on <video> than <audio>.
|
||||||
audio = document.createElement('video');
|
audio = document.createElement('video');
|
||||||
@@ -2204,10 +2233,7 @@ function initListenerMode() {
|
|||||||
audio.setAttribute('playsinline', '');
|
audio.setAttribute('playsinline', '');
|
||||||
audio.style.display = 'none';
|
audio.style.display = 'none';
|
||||||
document.body.appendChild(audio);
|
document.body.appendChild(audio);
|
||||||
console.log('🆕 Created new media element (video) for listener');
|
console.log('🆕 Created fresh media element (video) for listener');
|
||||||
|
|
||||||
// AudioContext will be created later on user interaction
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize MediaSource for streaming binary chunks
|
// Initialize MediaSource for streaming binary chunks
|
||||||
const mediaSource = new MediaSource();
|
const mediaSource = new MediaSource();
|
||||||
@@ -2470,8 +2496,38 @@ async function enableListenerAudio() {
|
|||||||
return window.listenerAudio.buffered && window.listenerAudio.buffered.length > 0;
|
return window.listenerAudio.buffered && window.listenerAudio.buffered.length > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Attempt playback IMMEDIATELY to capture user gesture
|
// CRITICAL: Wait for buffered data before calling play()
|
||||||
// We do this before waiting for data so we don't lose the "user interaction" token
|
// 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...');
|
console.log('▶️ Attempting to play audio...');
|
||||||
const playPromise = window.listenerAudio.play();
|
const playPromise = window.listenerAudio.play();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user