diff --git a/routes/listening_post.py b/routes/listening_post.py index 9b33b33..88a07f0 100644 --- a/routes/listening_post.py +++ b/routes/listening_post.py @@ -375,13 +375,19 @@ def _start_audio_stream(frequency: float, modulation: str): encoder_cmd = [ ffmpeg_path, + '-hide_banner', + '-loglevel', 'error', '-f', 's16le', '-ar', str(resample_rate), '-ac', '1', '-i', 'pipe:0', + '-acodec', 'libmp3lame', '-f', 'mp3', - '-b:a', '64k', + '-b:a', '96k', + '-ar', '44100', # Resample to standard rate for browser compatibility '-flush_packets', '1', + '-fflags', '+nobuffer', + '-flags', '+low_delay', 'pipe:1' ] @@ -754,12 +760,14 @@ def stream_audio() -> Response: return Response(b'', mimetype='audio/mpeg', status=204) def generate(): - chunk_size = 4096 + chunk_size = 8192 # Larger chunks for smoother streaming try: while audio_running and audio_process and audio_process.poll() is None: chunk = audio_process.stdout.read(chunk_size) if not chunk: - break + # Small wait before checking again to avoid busy loop + time.sleep(0.01) + continue yield chunk except Exception as e: logger.error(f"Audio stream error: {e}") diff --git a/templates/index.html b/templates/index.html index b1336fb..3fcb950 100644 --- a/templates/index.html +++ b/templates/index.html @@ -8521,15 +8521,17 @@ document.getElementById('scannerHitCount').textContent = `${tbody.children.length} signals found`; } - function tuneToFrequency(freq, mod) { + async function tuneToFrequency(freq, mod) { // Stop scanner if running if (isScannerRunning) { stopScanner(); } - // Stop any current audio + // Stop any current audio and wait for it to complete if (isAudioPlaying) { stopAudio(); + // Wait for audio to fully stop + await new Promise(resolve => setTimeout(resolve, 300)); } // Set frequency in manual audio form @@ -8539,7 +8541,10 @@ document.getElementById('audioModulation').value = mod; } - // Start playing immediately + // Small delay before starting to ensure backend is ready + await new Promise(resolve => setTimeout(resolve, 100)); + + // Start playing startAudio(); showNotification('Tuned', `Now listening to ${freq.toFixed(3)} MHz`); } @@ -8604,6 +8609,38 @@ let isAudioPlaying = false; let audioToolsAvailable = { rtl_fm: false, ffmpeg: false }; + let audioReconnectAttempts = 0; + const MAX_AUDIO_RECONNECT = 3; + + // Set up audio player error handling + document.addEventListener('DOMContentLoaded', function() { + const audioPlayer = document.getElementById('audioPlayer'); + if (audioPlayer) { + audioPlayer.addEventListener('error', function(e) { + console.warn('Audio player error:', e); + if (isAudioPlaying && audioReconnectAttempts < MAX_AUDIO_RECONNECT) { + audioReconnectAttempts++; + console.log(`Reconnecting audio (attempt ${audioReconnectAttempts})...`); + setTimeout(() => { + audioPlayer.src = '/listening/audio/stream?' + Date.now(); + audioPlayer.play().catch(() => {}); + }, 500); + } + }); + + audioPlayer.addEventListener('stalled', function() { + console.warn('Audio stalled, attempting recovery...'); + if (isAudioPlaying) { + audioPlayer.load(); + audioPlayer.play().catch(() => {}); + } + }); + + audioPlayer.addEventListener('playing', function() { + audioReconnectAttempts = 0; // Reset on successful play + }); + } + }); // Web Audio API for visualization let visualizerContext = null;