Files

70 lines
2.8 KiB
Swift

#if canImport(AVFoundation)
import AVFoundation
import Foundation
enum AudioSession {
typealias Token = any NSObjectProtocol
#if os(iOS) || os(watchOS)
static var outputLatency: TimeInterval {
AVAudioSession.sharedInstance().outputLatency
}
static func activate() async throws {
let session = AVAudioSession.sharedInstance()
#if os(watchOS)
try session.setCategory(.playback, mode: .default, policy: .longFormAudio)
_ = try await session.activate()
#else
try session.setCategory(.playback, mode: .default)
try session.setActive(true)
#endif
}
static func observe(
interrupted: @escaping @Sendable (Bool) -> Void,
routeLost: @escaping @Sendable () -> Void
) -> [Token] {
let center = NotificationCenter.default
let session = AVAudioSession.sharedInstance()
let interruption = center.addObserver(forName: AVAudioSession.interruptionNotification, object: session, queue: nil) {
guard let raw = $0.userInfo?[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: raw)
else { return }
switch type {
case .began:
interrupted(true)
case .ended:
let options = $0.userInfo?[AVAudioSessionInterruptionOptionKey] as? UInt ?? 0
if AVAudioSession.InterruptionOptions(rawValue: options).contains(.shouldResume) {
interrupted(false)
}
@unknown default:
break
}
}
let route = center.addObserver(forName: AVAudioSession.routeChangeNotification, object: session, queue: nil) {
let raw = $0.userInfo?[AVAudioSessionRouteChangeReasonKey] as? UInt
if raw.flatMap(AVAudioSession.RouteChangeReason.init) == .oldDeviceUnavailable {
routeLost()
}
}
return [interruption, route]
}
#else
static var outputLatency: TimeInterval {
0
}
static func activate() async throws {}
static func observe(
interrupted _: @escaping @Sendable (Bool) -> Void,
routeLost _: @escaping @Sendable () -> Void
) -> [Token] {
[]
}
#endif
}
#endif