USB Audio: libusb iso OUT for TX (companion to #83)#84
Merged
Conversation
Mirrors the input-side libusb capture work. The car-dash log from the first end-to-end QSO test caught the symmetric bug on the output side: with Audio Output set to (USB direct), UsbAudioDevice.writeAudio() calls UsbRequest.initialize() on the OUT endpoint, the car-dash kernel returns false (same iso-not-supported behavior as the input path), writeAudio returns immediately, FT8TransmitSignal.playViaUsbAudio short-circuits to afterPlayAudio(), and the PTT releases ~286ms after keying. TX appears to "abort" twice in a row. This commit adds a synchronous nativeWrite() that mirrors the input path's design: - usb_audio_capture.cpp: new OutputSession + onOutputComplete callback, same N=4 transfers × 8 packets pattern as the capture side. Caller blocks on libusb_handle_events_timeout_completed until in-flight drops to zero. Errors propagate back as libusb error codes. - UsbAudioNative.nativeWrite(): synchronous JNI entry point. Takes the fd + endpoint params + pre-formatted PCM byte buffer (Java still does float->int16 + channel duplication, same as before) and returns 0 on success or a negative LIBUSB_ERROR_* on failure. - UsbAudioDevice.writeAudio(): try the libusb path first, fall back to the original UsbRequest loop on non-zero return. Both paths log to debug.log so the next car-dash run will tell us exactly which one took the TX. Build verified: assembleDebug compiles cleanly across all four ABIs, the JNI symbol Java_com_bg7yoz_ft8cn_wave_UsbAudioNative_nativeWrite is present in libft8af_usb.so. NOT YET smoke-tested on the Pixel — the device was unplugged when I tried to install. Logically symmetric with the input path that's already proven working in the field, so the risk profile is the same as the input PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
patrickrb
added a commit
that referenced
this pull request
Jun 1, 2026
This was referenced Jun 1, 2026
jonstacks
pushed a commit
to jonstacks/FT8AF
that referenced
this pull request
Jun 3, 2026
The car-dash TX-abort symptom (PTT keys → 250-300ms → PTT releases, no audio sent) is still happening despite patrickrb#84 shipping the libusb nativeWrite. But the full TX output path is logged only to logcat, so from debug.log there's no way to tell: 1. Which output branch ran (USB direct vs. AudioTrack). 2. If USB direct ran, which step failed before reaching writeAudio (device lookup, USB permission, open, hasOutput, activateOutput). Same instrumentation pattern as the input side that surfaced the real bugs in patrickrb#82 / patrickrb#83. Changes: - playFT8Signal: log the branch decision with audioOutputDeviceId and the saved USB output VID:PID. Log which branch was taken. - playViaUsbAudio: fileLog at each failure point and at the success pivots (device opened + output activated, before writeAudio, writeAudio return value). Every failure path has a distinct message so the failure point is obvious from one log line. After the next car-dash repro we'll know whether the abort is: - audioOutputDeviceId not being -1 at TX time (picker reset?), - device lookup failing (UsbManager state), - permission lost on re-attach, - activateOutput failing on the car-dash kernel, - or actually reaching writeAudio and libusb dying inside. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #83. That PR fixed the input path on the car-dash; this one fixes the output path the same way. The first end-to-end QSO test on the car-dash caught the symmetric bug —
(USB direct)selected for Audio Output causes TX to abort after ~286ms becauseUsbAudioDevice.writeAudio()callsUsbRequest.initialize()on the OUT endpoint, the car-dash kernel returns false (same iso-not-supported behavior as input),writeAudioreturns immediately,FT8TransmitSignal.playViaUsbAudioshort-circuits toafterPlayAudio(), and the PTT releases before any audio is sent.What this gives you
With both #83 (RX) and this PR (TX) shipped,
(USB direct)selected for both Audio Input and Audio Output, the FT8 audio pipeline becomes completely independent of Android's audio framework:AudioTracksession is opened, so Android's audio policy never tries to route TX audio anywhere.What's in it
usb_audio_capture.cpp: newOutputSession+onOutputCompletecallback. SameN=4transfers × 8 packets pattern as the capture side. The caller blocks onlibusb_handle_events_timeout_completeduntil the in-flight counter drops to zero. Errors propagate back as libusb error codes.UsbAudioNative.nativeWrite(): synchronous JNI entry point. Takes the fd + endpoint params + pre-formatted PCM byte buffer (Java still does float→int16 + channel duplication, same as before) and returns 0 on success or a negativeLIBUSB_ERROR_*on failure.UsbAudioDevice.writeAudio(): try the libusb path first, fall back to the originalUsbRequestloop on non-zero return. Both paths log todebug.logso the next car-dash run will tell us exactly which one took the TX.Test plan
assembleDebugcompiles cleanly across all four ABIs. JNI symbolJava_com_bg7yoz_ft8cn_wave_UsbAudioNative_nativeWriteis present inlibft8af_usb.so.installDebugafter pushing). Need to confirm the existing UsbRequest output path doesn't regress on devices where it was already working, AND that the libusb path can drive an iso OUT endpoint on the Pixel.(USB direct), run a full TX cycle. Expected lines indebug.log:(USB direct)selected for output, start Spotify or another music app. Confirm music keeps playing through car speakers while FT8 TX uses the USB radio. This is the user-visible benefit, not just absence of bugs.🤖 Generated with Claude Code