From 615c992f9ceaa6dc7afa0d38895907b5c9c72b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bj=C3=B6rkert?= Date: Sun, 1 Nov 2026 15:32:01 +0100 Subject: [PATCH] Release the audio session after alarms and spoken BG so other audio returns to full volume --- LoopFollow/Controllers/AlarmSound.swift | 22 +++++++++++++++++++ .../ViewControllers/MainViewController.swift | 14 +----------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/LoopFollow/Controllers/AlarmSound.swift b/LoopFollow/Controllers/AlarmSound.swift index fafd2bf6f..6f6d8cdec 100644 --- a/LoopFollow/Controllers/AlarmSound.swift +++ b/LoopFollow/Controllers/AlarmSound.swift @@ -100,6 +100,27 @@ class AlarmSound { audioPlayer = nil restoreSystemOutputVolume() + releaseAudioSession() + } + + /// Hands the audio session back once LoopFollow has nothing left to play, so + /// audio from other apps that was ducked or interrupted returns to full volume. + /// While Silent Tune's loop holds the session, it stays active with the + /// mixable category that loop uses. + static func releaseAudioSession() { + guard !isPlaying else { return } + + let session = AVAudioSession.sharedInstance() + do { + if MainViewController.shared?.backgroundTask.isPlaying == true { + try session.setCategory(.playback, mode: .default, options: .mixWithOthers) + } else { + try session.setActive(false, options: .notifyOthersOnDeactivation) + } + } catch { + let nsError = error as NSError + LogManager.shared.log(category: .alarm, message: "AlarmSound - audio session release failed [code \(nsError.code)]: \(error.localizedDescription)") + } } static func playTest() { @@ -318,6 +339,7 @@ class AudioPlayerDelegate: NSObject, AVAudioPlayerDelegate { AlarmSound.playNextWithDelay() } else { Observable.shared.alarmSoundPlaying.value = false + AlarmSound.releaseAudioSession() } } diff --git a/LoopFollow/ViewControllers/MainViewController.swift b/LoopFollow/ViewControllers/MainViewController.swift index dce0b6611..025ecb6f3 100644 --- a/LoopFollow/ViewControllers/MainViewController.swift +++ b/LoopFollow/ViewControllers/MainViewController.swift @@ -1266,18 +1266,6 @@ class MainViewController: UIViewController, UNUserNotificationCenterDelegate { extension MainViewController: AVSpeechSynthesizerDelegate { func speechSynthesizer(_: AVSpeechSynthesizer, didFinish _: AVSpeechUtterance) { - let appState = UIApplication.shared.applicationState - let isSilentTuneMode = Storage.shared.backgroundRefreshType.value == .silentTune - - if isSilentTuneMode, appState == .background { - LogManager.shared.log(category: .general, message: "Silent tune active in background; not deactivating session.", isDebug: true) - } else { - do { - try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation) - LogManager.shared.log(category: .general, message: "Audio session deactivated after speech.", isDebug: true) - } catch { - LogManager.shared.log(category: .alarm, message: "Failed to deactivate audio session: \(error)") - } - } + AlarmSound.releaseAudioSession() } }