Files
pluriwave/openspec/changes/archive/2026-07-10-eq-audiofocus-reapply/proposal.md
T
FreeTLab 0ab63731d0
Build & Deploy PluriWave / Análisis de código (push) Successful in 41s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m33s
fix(eq): re-apply equalizer when the native audio session rotates
ExoPlayer assigns a new audio session id after transient audio-focus
interruptions (navigation prompts, radar warnings), leaving the
AndroidEqualizer attached to the dead session so playback resumed
without equalization until the next station switch. The session-id
listener now detects genuine rotations through a dedicated guard and
re-activates the equalizer with the current preset, gated on EQ
availability to stay clear of player teardown/rebuild.
2026-07-10 18:51:45 +02:00

64 lines
3.7 KiB
Markdown

# Proposal: EQ Re-Apply After Audio-Focus Ducking
## Intent
When another app interrupts playback (Radarbot warnings, Google Maps directions), the native Android audio session id rotates on the audio-focus transition. ExoPlayer orphans the effect attached to the dead session (documented upstream, issue #5302), so `just_audio`'s `AndroidEqualizer` keeps its gain state but never re-pushes it to the new session. Music resumes flat/un-equalized. Re-attachment currently only fires on explicit station switch (`_cambiarFuente` -> `_activarEcualizador`, `servicio_audio.dart:449/526`). The user requires the music to ALWAYS stay equalized. This is Part B; disconnect-revert (Part A) is the separate `eq-device-disconnect-revert` change.
## Scope
### In Scope
- Subscribe to `androidAudioSessionIdStream` inside `PluriWaveAudioHandler` for EQ purposes
- On a changed + non-null session id, re-attach and re-push current gains via existing `_activarEcualizador()` / `aplicarPreset(_presetActual)`
- Change-guard + `_eqDisponible` gate so it never re-fires redundantly nor races `_recrearPlayer()` teardown
- New tests covering re-apply after session-id rotation, first-activation no-op, and teardown race
### Out of Scope
- Part A device disconnect/cold-start revert (`eq-device-disconnect-revert`)
- Any `ServicioAudioSession` interruption/duck/pause logic change
- `EstadoEcualizador` / multi-device toggle logic
- Kotlin/platform-channel changes
- `VisualizadorAudio` (reference pattern only)
## Capabilities
### New Capabilities
- `eq-audiofocus-reapply`: Equalizer re-attaches and re-applies current gains whenever the native audio session id rotates mid-playback
### Modified Capabilities
- None (no existing equalizer spec)
## Approach
Extend the existing `_androidAudioSessionIdSub` listener (`servicio_audio.dart:258-265`), which today only stores/broadcasts the id. After broadcast, if the id changed and is non-null AND `_eqDisponible` is true, call the idempotent choke-point to re-enable and re-write band gains for `_presetActual`. This mirrors `VisualizadorAudio._onSessionId` (`visualizador_audio.dart:82-88`) — the proven in-repo reaction pattern. The fix lives entirely below the multi-device layer (`_eqMultiDeviceEnabled` is an `EstadoEcualizador` concern, absent from this file), so it applies regardless of that toggle.
## Affected Areas
| Area | Impact | Description |
|------|--------|-------------|
| `lib/servicios/servicio_audio.dart` | Modified | Add EQ re-apply to `_androidAudioSessionIdSub` listener |
| `test/servicios/` | New | Session-id rotation re-apply + guard/race coverage |
## Risks
| Risk | Likelihood | Mitigation |
|------|------------|------------|
| Redundant re-fire on first/legit activation | Med | Change-guard (`== _lastSessionId` return), mirrors visualizer |
| Race with `_recrearPlayer()` rebuild | Med | Gate on `_eqDisponible == true`; teardown sets it false + nulls id |
| Regress station-switch / manual slider path | Low | Different call sites unchanged; full `flutter test` regression |
## Rollback Plan
Change is a self-contained addition to one listener block plus new tests. Revert the commit to restore prior (broken) behavior — no persistence, schema, or API surface change, so no data migration or downstream impact.
## Dependencies
- None. `androidAudioSessionIdStream`, `_activarEcualizador()`, `aplicarPreset()` all already exist.
## Success Criteria
- [ ] After another app ducks/interrupts playback, resumed music stays equalized
- [ ] EQ re-applies on every changed non-null session id, regardless of `_eqMultiDeviceEnabled`
- [ ] No redundant re-apply on first activation or during `_recrearPlayer()` teardown
- [ ] Station-switch and manual slider (`setBanda`) paths unchanged
- [ ] All new behavior covered by passing tests (Strict TDD)