Fix Listening Post audio issues

- Add auto-reconnect on audio player errors/stalls
- Fix tuneToFrequency to properly wait between stop/start
- Improve ffmpeg encoding: 96k bitrate, 44.1kHz output, low latency flags
- Increase stream chunk size for smoother playback

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-01-08 12:26:55 +00:00
parent 45c7c0e416
commit ae2499c869
2 changed files with 51 additions and 6 deletions
+11 -3
View File
@@ -375,13 +375,19 @@ def _start_audio_stream(frequency: float, modulation: str):
encoder_cmd = [ encoder_cmd = [
ffmpeg_path, ffmpeg_path,
'-hide_banner',
'-loglevel', 'error',
'-f', 's16le', '-f', 's16le',
'-ar', str(resample_rate), '-ar', str(resample_rate),
'-ac', '1', '-ac', '1',
'-i', 'pipe:0', '-i', 'pipe:0',
'-acodec', 'libmp3lame',
'-f', 'mp3', '-f', 'mp3',
'-b:a', '64k', '-b:a', '96k',
'-ar', '44100', # Resample to standard rate for browser compatibility
'-flush_packets', '1', '-flush_packets', '1',
'-fflags', '+nobuffer',
'-flags', '+low_delay',
'pipe:1' 'pipe:1'
] ]
@@ -754,12 +760,14 @@ def stream_audio() -> Response:
return Response(b'', mimetype='audio/mpeg', status=204) return Response(b'', mimetype='audio/mpeg', status=204)
def generate(): def generate():
chunk_size = 4096 chunk_size = 8192 # Larger chunks for smoother streaming
try: try:
while audio_running and audio_process and audio_process.poll() is None: while audio_running and audio_process and audio_process.poll() is None:
chunk = audio_process.stdout.read(chunk_size) chunk = audio_process.stdout.read(chunk_size)
if not chunk: if not chunk:
break # Small wait before checking again to avoid busy loop
time.sleep(0.01)
continue
yield chunk yield chunk
except Exception as e: except Exception as e:
logger.error(f"Audio stream error: {e}") logger.error(f"Audio stream error: {e}")
+40 -3
View File
@@ -8521,15 +8521,17 @@
document.getElementById('scannerHitCount').textContent = `${tbody.children.length} signals found`; document.getElementById('scannerHitCount').textContent = `${tbody.children.length} signals found`;
} }
function tuneToFrequency(freq, mod) { async function tuneToFrequency(freq, mod) {
// Stop scanner if running // Stop scanner if running
if (isScannerRunning) { if (isScannerRunning) {
stopScanner(); stopScanner();
} }
// Stop any current audio // Stop any current audio and wait for it to complete
if (isAudioPlaying) { if (isAudioPlaying) {
stopAudio(); stopAudio();
// Wait for audio to fully stop
await new Promise(resolve => setTimeout(resolve, 300));
} }
// Set frequency in manual audio form // Set frequency in manual audio form
@@ -8539,7 +8541,10 @@
document.getElementById('audioModulation').value = mod; 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(); startAudio();
showNotification('Tuned', `Now listening to ${freq.toFixed(3)} MHz`); showNotification('Tuned', `Now listening to ${freq.toFixed(3)} MHz`);
} }
@@ -8604,6 +8609,38 @@
let isAudioPlaying = false; let isAudioPlaying = false;
let audioToolsAvailable = { rtl_fm: false, ffmpeg: 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 // Web Audio API for visualization
let visualizerContext = null; let visualizerContext = null;