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>
In-progress work on DualSense microphone capture over BT. Mic-add tap
itself is disabled (was decoding standard input bytes as Opus and
producing INT16_MIN garbage on the USB IN endpoint) but everything
around it is wired and ready to re-enable once we identify the actual
mic transport.
Firmware:
- src/audio.cpp: Opus decoder on core0, mic_fifo queue, audio_loop
mic-in path with decode + mono->stereo + tud_audio_write. Decoder
init in audio_init() (creates 48kHz mono OpusDecoder).
- src/audio.h: exports mic_add_queue() + per-frame diagnostic
accessors (audio_mic_frames, last_decoded, last_want, last_wrote,
last_toc).
- src/main.cpp on_bt_data(): BT-side instrumentation — counts every
INTERRUPT input report, tracks min/max length, OR mask of byte[2],
most recent non-0x31 report ID, hex prefix of last 0x31/other/any
frame, full content of the longest 0x31 frame seen. Mic-tap call
itself stubbed behind `if (false)` pending the real detector.
- src/state_mgr.cpp: state_init_data byte 6 (VolumeMic) 0xFF→0x40
(was out of range), byte 9 (MuteControl) 0x0F→0x00 (clear all
PowerSave bits — AudioPowerSave was muting DSP).
- src/cmd.cpp: two new vendor feature reports — 0xFD returns 32-byte
diagnostic state (counters + prefixes), 0xFE returns the longest
0x31 frame in full (up to 80 bytes). Both queryable via
/dev/hidraw on Linux from the host script.
- src/oled.cpp: Diagnostics screen shows TOC + decode result + USB
wrote/want bytes for live BT-side visibility.
Host-side:
- scripts/mic_diag.sh: subcommands `status`, `capture [secs]`,
`watch`, `bt-trace`. The bt-trace subcommand reads the 0xFD
feature report via hidraw ioctl, decodes counters + recent
prefixes, computes per-second rates. Drastically cuts iteration
time — no OLED relay or per-test flash cycle needed.
Findings to date:
- Upstream/mic's mic-flag bit ((data[2] >> 1) & 1) does NOT match
this DS5 firmware; bit 1 of byte[2] is NEVER set. Bit 0 is the
standard input report type indicator, not a mic tag — confirmed
by stick-bytes appearing as our supposed "Opus prefix".
- DS5 sends both report ID 0x01 and 0x31 over BT; the longest frame
is a standard 79-byte 0x31 input report with sticks/IMU/touchpad
but no audio bytes appended.
- Conclusion in progress: the DS5 firmware on this controller is
not currently streaming mic over BT at all, even with
AllowAudioControl=1, VolumeMic=0x40, AudioPowerSave=0,
MicMute=0. Next investigation step: compare against a USB-mode
DS5 to see what a real mic stream looks like at the UAC1 layer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build now compiles. audio.cpp on top of upstream's state_mgr-based
audio_loop adds three additive pieces:
1. VU peak meters for ch0/1 (spk_max) and ch2/3 (hap_max) tracked
per-sample in the existing loop; written back to g_peak_spk /
g_peak_hap after. Accessors decay 12.5% per read so the OLED
VU bars fall back naturally over a few frames.
2. Monotonic byte-flow counters: g_usb_frames++ after tud_audio_read
(the OLED Diagnostics screen + web emulator compute USB-aud/s
and BT-0x32/s as delta over time). g_bt_packets++ after each
0x36 bt_write.
3. Audio Auto Haptics DSP — 1-pole LP + envelope follower +
modulation + soft-clip per loteran/DS5Dongle 5d6bc2f, with our
added 4th "Fallback" mode that fires only after the game's
native haptic path has been silent for ~1 s (NATIVE_SILENT_TIMEOUT
= 100 audio_loop calls). Preserves native HD haptics (Spider-Man
Remastered) while filling in for games that send no haptic data
(Ghost of Tsushima). Modes: 0=Off, 1=Fallback (default), 2=Mix,
3=Replace.
Crucially the state_mgr-based state_set(pkt+13, 63) call is left
untouched — that's upstream's correct replacement for our prior
state_data restore hack, and it's load-bearing for speaker output.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>