docs: condense implementation comments
This commit is contained in:
@@ -35,14 +35,7 @@ final class AudioSessionManager {
|
||||
name: AVAudioSession.routeChangeNotification, object: nil)
|
||||
}
|
||||
|
||||
/// The single end-to-end audio recovery path, driven by *intent* (`IOSAudioEngine.isConnected`)
|
||||
/// — not by session bookkeeping flags that can drift out of sync (e.g. an interruption ended
|
||||
/// without `.shouldResume`, which used to leave `isSessionActive` false forever). Safe to call
|
||||
/// speculatively: the underlying calls are idempotent (AVAudioSession.setActive(true),
|
||||
/// `IOSAudioRouter.applyConfiguration` has a re-entrancy guard, `IOSAudioEngine.reconfigure`
|
||||
/// no-ops when not connected). Call this whenever the audio environment changes in a way that
|
||||
/// could have stopped the engine — interruption end, route change, AVAudioEngine
|
||||
/// configuration-change — and we still want audio back.
|
||||
/// Idempotently restores audio after an interruption or external route change.
|
||||
func recoverAudio() {
|
||||
guard IOSAudioEngine.shared.isConnected else { return }
|
||||
do {
|
||||
@@ -158,27 +151,9 @@ final class AudioSessionManager {
|
||||
IOSAudioRouter.shared.refreshRoutes()
|
||||
NotificationCenter.default.post(name: .voiceCatDeviceListChanged, object: nil)
|
||||
|
||||
// Recover audio on every externally-initiated route change. `.categoryChange`,
|
||||
// `.routeConfigurationChange`, and `.override` are fired by our OWN calls:
|
||||
// - `.categoryChange` / `.routeConfigurationChange` ← applyConfiguration()'s
|
||||
// setCategory / setPreferredInput / ...
|
||||
// - `.override` ← applyA2dpSpeakerFallback()'s overrideOutputAudioPort(.speaker),
|
||||
// which fires on every AirPods disconnect (and reconnect) on an A2DP preset.
|
||||
// Acting on any of these would create a tight ping-pong loop with the re-entrancy
|
||||
// guard (handleRouteChange → recoverAudio → applyA2dpSpeakerFallback →
|
||||
// overrideOutputAudioPort → .override routeChange → recoverAudio → ...). The
|
||||
// `.override` skip is what fixes the AirPods-disconnect reinitialize loop: each
|
||||
// iteration also calls IOSAudioEngine.reconfigure() → rebuild() (a full
|
||||
// stop/restart of AVAudioEngine), which is the audible cycling. IOSAudioRouter's
|
||||
// guard is the backstop that bounds it to ONE extra iteration, but skipping these
|
||||
// three reasons avoids even that, so we reconfigure only in response to genuine
|
||||
// environmental changes.
|
||||
//
|
||||
// The recovery set below (oldDeviceUnavailable, newDeviceAvailable, wakeFromSleep,
|
||||
// noSuitableRouteForCategory, unknown) covers headphone/AirPods/wired unplug-replug
|
||||
// — the previously-reported "audio dies when headphones disconnect" bug. If an
|
||||
// override ever actually stops the AVAudioEngine, the
|
||||
// AVAudioEngineConfigurationChange handler in IOSVoiceProcessingEngine catches it.
|
||||
// Ignore notifications caused by our own configuration calls; rebuilding for them
|
||||
// recursively emits more route changes. Engine-configuration notifications remain
|
||||
// the recovery path if a self-initiated change actually stops AVAudioEngine.
|
||||
if reason != .categoryChange && reason != .routeConfigurationChange && reason != .override {
|
||||
recoverAudio()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user