Skip to content

Fix USB audio death-loop; show real version in splash & About#82

Merged
patrickrb merged 1 commit into
mainfrom
fix/usb-audio-loop
May 29, 2026
Merged

Fix USB audio death-loop; show real version in splash & About#82
patrickrb merged 1 commit into
mainfrom
fix/usb-audio-loop

Conversation

@patrickrb
Copy link
Copy Markdown
Owner

Summary

Three things bundled because they ship together to the same Play release.

1. USB audio death-loop fix (the main one)

The car-dash debug.log from #81 revealed that openUsbAudioInput was firing hundreds of times per second on the tablet, each call succeeding. Root cause: UsbAudioDevice.captureLoop blocks on connection.requestWait(), which returns null on the first iteration on that host (the kernel/USB stack apparently can't deliver isochronous URBs to userspace via UsbRequest). The capture thread exits, the finally block fires onCaptureStopped(), MicRecorder unconditionally calls reinitialize(), which reopens the USB handle and starts capture again, which dies immediately, ... ~30ms per cycle, forever.

This PR doesn't fix the underlying isochronous-transfer issue (we still need more data to know whether it's worth retrying at a different sample rate or claiming a different alt-setting), but it does stop the spin and produce clear diagnostic output:

  • MicRecorder rate-limits self-rebind to one per 2s.
  • After 3 consecutive failures without ever delivering audio data, it gives up on the raw USB path and force-falls back to AudioRecord+mic. App is degraded but stops thrashing the USB bus.
  • Tracks usbAudioSawData — first valid callback resets the failure counter so intermittent hiccups don't accumulate.
  • UsbAudioDevice.captureLoop now writes its exit reason to debug.log (requestWait returned null after N iterations, or exception class + message) plus device params (sample rate, channels, packet size, ratio). The next car-dash repro will tell us exactly when and why the loop dies.

2. Splash version is dynamic

Replaces hardcoded "v1.0 · Forked from FT8CN" with "v${VERSION} · build ${VERSION_CODE}". CI bumps versionCode per release (GITHUB_RUN_NUMBER + 100), so each shipped build shows a unique identifier matching what Play displays.

3. About dialog tagline

Removed the FT8CN attribution line from the About dialog. The fork credit and BG7YOZ thanks remain in README.md:5,52 and LICENSE. Replaced with "FT8, made easy." plus a slightly cleaner description blurb.

Test plan

  • installDebug builds + installs clean on Pixel 8.
  • Splash shows v1.0.2 · build 4 (local build; CI will assign the real code).
  • About dialog body reflects new tagline.
  • Car-dash Android 11 tablet (after Play update): re-pick (USB direct), open Debug screen, share log. Expected outcome: hundreds of SUCCESS at 48000 Hz lines replaced with a single UsbAudio.captureLoop: requestWait returned null after 0 iterations + a couple of throttled reinits + final giving up on raw USB ... forcing fallback to AudioRecord. App ends up on the mic but stops spinning. The captureLoop log line will tell us the next move.

🤖 Generated with Claude Code

…bout

Three things bundled because they ship together to the same Play release.

1. USB audio death-loop fix. On the car-dash Android 11 tablet, the
   isochronous capture loop in UsbAudioDevice exits immediately —
   connection.requestWait() returns null on the first iteration — and
   the onCaptureStopped callback was unconditionally reinitializing the
   recorder. That triggered open -> activate -> start -> die ->
   reinit -> ... at ~30ms per cycle. Symptom in debug.log: hundreds of
   `openUsbAudioInput: SUCCESS at 48000 Hz` lines per second.

   - MicRecorder now rate-limits self-rebind to one per 2s.
   - After 3 consecutive failures without ever delivering audio data,
     it gives up on the raw USB path and falls back to AudioRecord+mic
     so the app stops thrashing the USB bus. Degraded but functional.
   - UsbAudioDevice.captureLoop now writes the reason (requestWait
     null vs exception) and iteration count to debug.log so the next
     car-dash capture tells us why isochronous transfers are failing.

2. Splash screen version is now dynamic — reads
   GeneralVariables.VERSION + VERSION_CODE so each shipped release
   shows a unique identifier. Replaces the hardcoded "v1.0 · Forked
   from FT8CN" line. CI bumps versionCode from GITHUB_RUN_NUMBER + 100.

3. About dialog: removed the "Based on FT8CN by BG7YOZ" line and
   replaced it with the tagline "FT8, made easy." Upstream
   attribution remains in README.md and LICENSE so this is purely a
   UI cleanup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@patrickrb patrickrb merged commit d20a39d into main May 29, 2026
2 checks passed
@patrickrb patrickrb deleted the fix/usb-audio-loop branch May 29, 2026 19:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant