feat(audio): mic packet-loss concealment (jitter buffer + Opus PLC)

The BT mic decode path gained a decoded-frame jitter buffer (8 frames) drained
at a steady 10 ms playout cadence. Bursty BT delivery is smoothed; a dropped
mic frame during an active session is concealed with an Opus PLC frame
(opus_decode(decoder, NULL, 0, ...)) instead of leaving a hole the host hears
as a click/dropout. Playout pre-buffers 3 frames and stops after 300 ms of no
real frames so it never emits comfort noise when the mic is idle. New "Mic PLC:"
Diagnostics counter climbs only when concealment fires (a live link-quality
gauge). Verified on hardware: forced BT loss kept captured audio gap-free
(longest zero-run ~0 ms) while the counter climbed; clean link leaves it idle.
Adds ~30 ms mic latency (the pre-buffer). Design ported from
SundayMoments/DS5_Bridge (credit). Unreleased — batching with the next feature.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MarcelineVPQ
2026-05-24 09:27:11 -06:00
co-authored by Claude Opus 4.7
parent 9920516f52
commit da63e2bb72
5 changed files with 83 additions and 17 deletions
+1
View File
@@ -26,6 +26,7 @@ int32_t audio_mic_last_decoded(); // last opus_decode return — neg = error, 4
uint16_t audio_mic_last_want(); // bytes asked of tud_audio_write
uint16_t audio_mic_last_wrote(); // bytes TinyUSB FIFO actually accepted
uint8_t audio_mic_last_toc(); // first byte of last Opus packet (frame config)
uint32_t audio_mic_plc_frames(); // count of packet-loss-concealment frames generated
// Called from on_bt_data() in main.cpp when the DS5 sends a mic-tagged
// 0x31 input report. Buffer must point at MIC_OPUS_SIZE (71) bytes of