AlarmSound.swift 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. //
  2. // AlarmSound.swift
  3. // scoutwatch
  4. //
  5. // Created by Dirk Hermanns on 03.01.16.
  6. // Copyright © 2016 private. All rights reserved.
  7. //
  8. import Foundation
  9. import AVFoundation
  10. import MediaPlayer
  11. import UIKit
  12. /*
  13. * Class that handles the playing and the volume of the alarm sound.
  14. */
  15. class AlarmSound {
  16. static var isPlaying: Bool {
  17. return self.audioPlayer?.isPlaying == true
  18. }
  19. static var isMuted: Bool {
  20. return self.muted
  21. }
  22. static var whichAlarm: String = "none"
  23. static var isTesting: Bool = false
  24. //static let volumeChangeDetector = VolumeChangeDetector()
  25. static let fadeInTimeInterval = UserDefaultsRepository.fadeInTimeInterval
  26. static let vibrate = UserDefaultsRepository.vibrate
  27. static let overrideSystemOutputVolume = UserDefaultsRepository.overrideSystemOutputVolume
  28. static let systemOutputVolume = UserDefaultsRepository.systemOutputVolume
  29. fileprivate static var systemOutputVolumeBeforeOverride: Float?
  30. fileprivate static var playingTimer: Timer?
  31. fileprivate static let soundURL = Bundle.main.url(forResource: "Indeed", withExtension: "caf")!
  32. fileprivate static var audioPlayer: AVAudioPlayer?
  33. fileprivate static let audioPlayerDelegate = AudioPlayerDelegate()
  34. fileprivate static var muted = false
  35. /*
  36. * Sets the audio volume to 0.
  37. */
  38. static func muteVolume() {
  39. self.audioPlayer?.volume = 0
  40. self.muted = true
  41. self.restoreSystemOutputVolume()
  42. }
  43. /*
  44. * Sets the volume of the alarm back to the volume before it has been muted.
  45. */
  46. static func unmuteVolume() {
  47. if self.fadeInTimeInterval.value > 0 {
  48. self.audioPlayer?.setVolume(1.0, fadeDuration: self.fadeInTimeInterval.value)
  49. } else {
  50. self.audioPlayer?.volume = 1.0
  51. }
  52. self.muted = false
  53. }
  54. static func stop() {
  55. self.playingTimer?.invalidate()
  56. self.playingTimer = nil
  57. self.audioPlayer?.stop()
  58. self.audioPlayer = nil
  59. self.restoreSystemOutputVolume()
  60. }
  61. static func play() {
  62. guard !self.isPlaying else {
  63. return
  64. }
  65. do {
  66. self.audioPlayer = try AVAudioPlayer(contentsOf: self.soundURL)
  67. self.audioPlayer!.delegate = self.audioPlayerDelegate
  68. try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category(rawValue: convertFromAVAudioSessionCategory(AVAudioSession.Category.playback)))
  69. try AVAudioSession.sharedInstance().setActive(true)
  70. // Play endless loops
  71. self.audioPlayer!.numberOfLoops = -1
  72. // init volume before start playing (mute if fade-in)
  73. self.audioPlayer!.volume = (self.muted || (self.fadeInTimeInterval.value > 0)) ? 0.0 : 1.0
  74. if !self.audioPlayer!.prepareToPlay() {
  75. NSLog("AlarmSound - audio player failed preparing to play")
  76. }
  77. if self.audioPlayer!.play() {
  78. if !self.isPlaying {
  79. NSLog("AlarmSound - not playing after calling play")
  80. NSLog("AlarmSound - rate value: \(self.audioPlayer!.rate)")
  81. }
  82. } else {
  83. NSLog("AlarmSound - audio player failed to play")
  84. }
  85. // do fade-in
  86. if !self.muted && (self.fadeInTimeInterval.value > 0) {
  87. self.audioPlayer!.setVolume(1.0, fadeDuration: self.fadeInTimeInterval.value)
  88. }
  89. //self.playingTimer = Timer.schedule(repeatInterval: 1.0, handler: self.onPlayingTimer)
  90. } catch let error {
  91. NSLog("AlarmSound - unable to play sound; error: \(error)")
  92. }
  93. }
  94. fileprivate static func onPlayingTimer(timer: Timer?) {
  95. // player should be playing, not muted!
  96. guard self.isPlaying && !self.isMuted else {
  97. return
  98. }
  99. // application should be in active state!
  100. guard UIApplication.shared.applicationState == .active else {
  101. return
  102. }
  103. if self.overrideSystemOutputVolume.value {
  104. // keep the system output volume before overriding it
  105. if self.systemOutputVolumeBeforeOverride == nil {
  106. // self.systemOutputVolumeBeforeOverride = MPVolumeView.volume
  107. }
  108. // override the system output volume
  109. // if MPVolumeView.volume != self.systemOutputVolume.value {
  110. // self.volumeChangeDetector.isActive = false
  111. // MPVolumeView.volume = self.systemOutputVolume.value
  112. // } else {
  113. // listen to user volume changes
  114. // self.volumeChangeDetector.isActive = true
  115. // }
  116. }
  117. if self.vibrate.value {
  118. AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
  119. }
  120. }
  121. fileprivate static func restoreSystemOutputVolume() {
  122. guard self.overrideSystemOutputVolume.value else {
  123. return
  124. }
  125. // cancel any volume change observations
  126. // self.volumeChangeDetector.isActive = false
  127. // restore system output volume with its value before overriding it
  128. //if let volumeBeforeOverride = self.systemOutputVolumeBeforeOverride {
  129. // MPVolumeView.volume = volumeBeforeOverride
  130. // }
  131. self.systemOutputVolumeBeforeOverride = nil
  132. }
  133. }
  134. class AudioPlayerDelegate: NSObject, AVAudioPlayerDelegate {
  135. /* audioPlayerDidFinishPlaying:successfully: is called when a sound has finished playing. This method is NOT called if the player is stopped due to an interruption. */
  136. func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
  137. NSLog("AlarmRule - audioPlayerDidFinishPlaying (\(flag))")
  138. }
  139. /* if an error occurs while decoding it will be reported to the delegate. */
  140. func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
  141. if let error = error {
  142. NSLog("AlarmRule - audioPlayerDecodeErrorDidOccur: \(error)")
  143. } else {
  144. NSLog("AlarmRule - audioPlayerDecodeErrorDidOccur")
  145. }
  146. }
  147. /* AVAudioPlayer INTERRUPTION NOTIFICATIONS ARE DEPRECATED - Use AVAudioSession instead. */
  148. /* audioPlayerBeginInterruption: is called when the audio session has been interrupted while the player was playing. The player will have been paused. */
  149. func audioPlayerBeginInterruption(_ player: AVAudioPlayer) {
  150. NSLog("AlarmRule - audioPlayerBeginInterruption")
  151. }
  152. /* audioPlayerEndInterruption:withOptions: is called when the audio session interruption has ended and this player had been interrupted while playing. */
  153. /* Currently the only flag is AVAudioSessionInterruptionFlags_ShouldResume. */
  154. func audioPlayerEndInterruption(_ player: AVAudioPlayer, withOptions flags: Int) {
  155. NSLog("AlarmRule - audioPlayerEndInterruption withOptions: \(flags)")
  156. }
  157. }
  158. // Helper function inserted by Swift 4.2 migrator.
  159. fileprivate func convertFromAVAudioSessionCategory(_ input: AVAudioSession.Category) -> String {
  160. return input.rawValue
  161. }