RecordingEnvironmentDetector.swift 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. import Foundation
  2. import AVFoundation
  3. import Combine
  4. /// Detects system environment issues before starting a recording.
  5. /// Checks background audio, media volume, and microphone permissions.
  6. @Observable
  7. final class RecordingEnvironmentDetector {
  8. // MARK: - Published Warnings
  9. var isOtherAudioPlaying: Bool = false
  10. var isVolumeTooHigh: Bool = false
  11. var micPermissionStatus: AVAudioApplication.recordPermission = .undetermined
  12. /// List of human-readable warnings based on current environment state.
  13. var activeWarnings: [String] = []
  14. init() {
  15. checkEnvironment()
  16. // Listen to volume changes or other audio notifications if necessary
  17. NotificationCenter.default.addObserver(
  18. self,
  19. selector: #selector(handleAudioRouteChanged),
  20. name: AVAudioSession.routeChangeNotification,
  21. object: nil
  22. )
  23. }
  24. /// Runs environment checks and updates warnings.
  25. func checkEnvironment() {
  26. let session = AVAudioSession.sharedInstance()
  27. // 1. Check if other audio is playing in the background
  28. self.isOtherAudioPlaying = session.isOtherAudioPlaying
  29. // 2. Check if output volume is too high (above 70%)
  30. self.isVolumeTooHigh = session.outputVolume > 0.7
  31. // 3. Check microphone permission status
  32. self.micPermissionStatus = AVAudioApplication.shared.recordPermission
  33. // Compile warning strings
  34. var warnings: [String] = []
  35. if isOtherAudioPlaying {
  36. warnings.append("⚠️ 检测到后台有其他音频播放中,建议关闭以防混入录音。")
  37. }
  38. if isVolumeTooHigh {
  39. warnings.append("⚠️ 系统媒体音量过高 (%.0f%%),录音时建议降低音量。" .replacingOccurrences(of: "%.0f%%", with: String(format: "%.0f%%", session.outputVolume * 100)))
  40. }
  41. if micPermissionStatus == .denied {
  42. warnings.append("⚠️ 麦克风访问权限被禁用,请前往系统设置开启麦克风。")
  43. }
  44. self.activeWarnings = warnings
  45. }
  46. @objc private func handleAudioRouteChanged() {
  47. // Run checks on background thread to avoid blocking main loop, then dispatch updates
  48. DispatchQueue.main.async { [weak self] in
  49. self?.checkEnvironment()
  50. }
  51. }
  52. }