Hold exclusive audio focus during AudioTrack TX and Tune#597
Conversation
The AudioTrack TX path plays through Android's shared mixer, so sounds from other apps (touch clicks, notifications, media) routed to the same output device were mixed into the audio feeding the rig and transmitted on air, corrupting the FT8 waveform. Add TxAudioFocus, which requests AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE for the duration of each transmission (and the Tune carrier) and abandons it on teardown. The system suppresses notification sounds and asks other apps to pause while it is held. Focus is advisory, so a denied request is log-only and TX always proceeds. The CAT and direct-USB (libusb) paths never acquire focus; release in afterPlayAudio() is a no-op there. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #597 +/- ##
============================================
+ Coverage 29.36% 29.41% +0.04%
Complexity 197 197
============================================
Files 179 179
Lines 24068 24097 +29
Branches 3263 3268 +5
============================================
+ Hits 7067 7087 +20
- Misses 16780 16787 +7
- Partials 221 223 +2
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR adds transient-exclusive audio focus management to the Android AudioTrack transmit paths so that other apps’ sounds (notifications/media/etc.) are less likely to be mixed into the over-the-air TX audio during FT8 transmissions and Tune carrier playback.
Changes:
- Introduces a new
TxAudioFocushelper to acquire/releaseAUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVEaround TX playback windows. - Integrates focus acquisition/release into
FT8TransmitSignalfor the AudioTrack FT8 TX branch and the Tune worker. - Adds Robolectric unit tests covering focus acquisition/denial/idempotency and release semantics.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| ft8af/app/src/main/java/com/k1af/ft8af/ft8transmit/TxAudioFocus.java | New helper to request/abandon transient-exclusive audio focus during TX. |
| ft8af/app/src/main/java/com/k1af/ft8af/ft8transmit/FT8TransmitSignal.java | Acquires audio focus for AudioTrack FT8 TX and Tune, and releases on teardown. |
| ft8af/app/src/test/java/com/k1af/ft8af/ft8transmit/TxAudioFocusTest.java | New Robolectric tests validating focus request parameters and lifecycle behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| request = new AudioFocusRequest.Builder( | ||
| AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) | ||
| .setAudioAttributes(new AudioAttributes.Builder() | ||
| .setUsage(AudioAttributes.USAGE_MEDIA) | ||
| .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) | ||
| .build()) | ||
| .setOnAudioFocusChangeListener(focusChangeListener) | ||
| .build(); |
| @Test | ||
| public void acquire_requestsTransientExclusiveGain() { | ||
| focus.acquire(context); | ||
|
|
||
| assertThat(shadowAudioManager.getLastAudioFocusRequest().durationHint) | ||
| .isEqualTo(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE); | ||
| } |
Problem
When the radio's USB sound card is used as an Android output device (the AudioTrack TX path), the transmit waveform goes through Android's shared mixer. Any sound another app makes during the 12.6 s transmission — touch/keyboard clicks, notification dings, media — is mixed into the same stream and transmitted on air. Field symptom: using other apps while FT8AF transmits makes the radio "click" with phone activity, and those sounds corrupt the FT8 waveform.
Fix
New
TxAudioFocushelper requestsAUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVEfor the duration of playback and abandons it on teardown:playFT8SignalAudioTrack branch): acquired just before the track is created, released inafterPlayAudio(). The CAT and direct-USB (libusb) branches never acquire it, and release is a safe no-op there.playTuneTone): its own instance, acquired in the worker and released in the existingfinallyteardown, so tune and TX can never release each other's focus.While held, the system suppresses notification sounds and asks other apps to pause; between cycles the phone behaves normally. Focus is advisory — a denied request is logged to
debug.logand TX proceeds regardless. Grant/deny outcome is also logged per cycle for field diagnosis.Known limits (documented in the class javadoc): focus cannot block system touch sounds or apps that ignore focus; the direct-USB output mode remains the bulletproof option since it bypasses the mixer entirely.
Testing
TxAudioFocusTest(9 Robolectric tests): grant/deny/null-context acquire, transient-exclusive gain type, idempotent acquire, release/abandon semantics, no-op release, re-acquire after release.testDebugUnitTestsuite passes.🤖 Generated with Claude Code