AlarmSound.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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 soundFile = "Indeed"
  24. static var isTesting: Bool = false
  25. //static let volumeChangeDetector = VolumeChangeDetector()
  26. static let vibrate = UserDefaultsRepository.vibrate
  27. fileprivate static var systemOutputVolumeBeforeOverride: Float?
  28. fileprivate static var playingTimer: Timer?
  29. fileprivate static var soundURL = Bundle.main.url(forResource: "Indeed", withExtension: "caf")!
  30. fileprivate static var audioPlayer: AVAudioPlayer?
  31. fileprivate static let audioPlayerDelegate = AudioPlayerDelegate()
  32. fileprivate static var muted = false
  33. /*
  34. * Sets the audio volume to 0.
  35. */
  36. static func muteVolume() {
  37. self.audioPlayer?.volume = 0
  38. self.muted = true
  39. self.restoreSystemOutputVolume()
  40. }
  41. static func setSoundFile(str: String) {
  42. self.soundURL = Bundle.main.url(forResource: str, withExtension: "caf")!
  43. }
  44. /*
  45. * Sets the volume of the alarm back to the volume before it has been muted.
  46. */
  47. static func unmuteVolume() {
  48. if UserDefaultsRepository.fadeInTimeInterval.value > 0 {
  49. self.audioPlayer?.setVolume(1.0, fadeDuration: UserDefaultsRepository.fadeInTimeInterval.value)
  50. } else {
  51. self.audioPlayer?.volume = 1.0
  52. }
  53. self.muted = false
  54. }
  55. static func stop() {
  56. self.playingTimer?.invalidate()
  57. self.playingTimer = nil
  58. self.audioPlayer?.stop()
  59. self.audioPlayer = nil
  60. self.restoreSystemOutputVolume()
  61. }
  62. static func playTest() {
  63. guard !self.isPlaying else {
  64. return
  65. }
  66. do {
  67. self.audioPlayer = try AVAudioPlayer(contentsOf: self.soundURL)
  68. self.audioPlayer!.delegate = self.audioPlayerDelegate
  69. try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category(rawValue: convertFromAVAudioSessionCategory(AVAudioSession.Category.playback)))
  70. try AVAudioSession.sharedInstance().setActive(true)
  71. self.audioPlayer?.numberOfLoops = 0
  72. // init volume before start playing (mute if fade-in)
  73. //self.audioPlayer!.volume = (self.muted || (UserDefaultsRepository.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. } catch let error {
  86. NSLog("AlarmSound - unable to play sound; error: \(error)")
  87. }
  88. }
  89. static func play(overrideVolume: Bool, numLoops: Int) {
  90. guard !self.isPlaying else {
  91. return
  92. }
  93. do {
  94. self.audioPlayer = try AVAudioPlayer(contentsOf: self.soundURL)
  95. self.audioPlayer!.delegate = self.audioPlayerDelegate
  96. try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category(rawValue: convertFromAVAudioSessionCategory(AVAudioSession.Category.playback)))
  97. try AVAudioSession.sharedInstance().setActive(true)
  98. // Play endless loops
  99. self.audioPlayer!.numberOfLoops = numLoops
  100. // Store existing volume
  101. if self.systemOutputVolumeBeforeOverride == nil {
  102. self.systemOutputVolumeBeforeOverride = AVAudioSession.sharedInstance().outputVolume
  103. }
  104. // init volume before start playing (mute if fade-in)
  105. //self.audioPlayer!.volume = (self.muted || (UserDefaultsRepository.fadeInTimeInterval.value > 0)) ? 0.0 : 1.0
  106. if !self.audioPlayer!.prepareToPlay() {
  107. NSLog("AlarmSound - audio player failed preparing to play")
  108. }
  109. if self.audioPlayer!.play() {
  110. if !self.isPlaying {
  111. NSLog("AlarmSound - not playing after calling play")
  112. NSLog("AlarmSound - rate value: \(self.audioPlayer!.rate)")
  113. }
  114. } else {
  115. NSLog("AlarmSound - audio player failed to play")
  116. }
  117. // do fade-in
  118. //if !self.muted && (UserDefaultsRepository.fadeInTimeInterval.value > 0) {
  119. // self.audioPlayer!.setVolume(1.0, fadeDuration: UserDefaultsRepository.fadeInTimeInterval.value)
  120. //}
  121. if overrideVolume {
  122. MPVolumeView.setVolume(UserDefaultsRepository.forcedOutputVolume.value)
  123. }
  124. //self.playingTimer = Timer.schedule(repeatInterval: 1.0, handler: self.onPlayingTimer)
  125. } catch let error {
  126. NSLog("AlarmSound - unable to play sound; error: \(error)")
  127. }
  128. }
  129. static func playTerminated() {
  130. guard !self.isPlaying else {
  131. return
  132. }
  133. do {
  134. self.audioPlayer = try AVAudioPlayer(contentsOf: self.soundURL)
  135. self.audioPlayer!.delegate = self.audioPlayerDelegate
  136. try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category(rawValue: convertFromAVAudioSessionCategory(AVAudioSession.Category.playback)))
  137. try AVAudioSession.sharedInstance().setActive(true)
  138. // Play endless loops
  139. self.audioPlayer!.numberOfLoops = 2
  140. // Store existing volume
  141. if self.systemOutputVolumeBeforeOverride == nil {
  142. self.systemOutputVolumeBeforeOverride = AVAudioSession.sharedInstance().outputVolume
  143. }
  144. if !self.audioPlayer!.prepareToPlay() {
  145. NSLog("Terminate AlarmSound - audio player failed preparing to play")
  146. }
  147. if self.audioPlayer!.play() {
  148. if !self.isPlaying {
  149. NSLog("Terminate AlarmSound - not playing after calling play")
  150. NSLog("Terminate AlarmSound - rate value: \(self.audioPlayer!.rate)")
  151. }
  152. } else {
  153. NSLog("Terminate AlarmSound - audio player failed to play")
  154. }
  155. MPVolumeView.setVolume(1.0)
  156. } catch let error {
  157. NSLog("Terminate AlarmSound - unable to play sound; error: \(error)")
  158. }
  159. }
  160. fileprivate static func onPlayingTimer(timer: Timer?) {
  161. // player should be playing, not muted!
  162. guard self.isPlaying && !self.isMuted else {
  163. return
  164. }
  165. // application should be in active state!
  166. guard UIApplication.shared.applicationState == .active else {
  167. return
  168. }
  169. if UserDefaultsRepository.overrideSystemOutputVolume.value {
  170. // keep the system output volume before overriding it
  171. if self.systemOutputVolumeBeforeOverride == nil {
  172. //self.systemOutputVolumeBeforeOverride = MPVolumeView.volume
  173. self.systemOutputVolumeBeforeOverride = AVAudioSession.sharedInstance().outputVolume
  174. }
  175. // override the system output volume
  176. MPVolumeView.setVolume(UserDefaultsRepository.systemOutputVolume.value)
  177. // if MPVolumeView.volume != UserDefaultsRepository.systemOutputVolume.value {
  178. // self.volumeChangeDetector.isActive = false
  179. // MPVolumeView.volume = UserDefaultsRepository.systemOutputVolume.value
  180. // } else {
  181. // listen to user volume changes
  182. // self.volumeChangeDetector.isActive = true
  183. // }
  184. }
  185. if self.vibrate.value {
  186. AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
  187. }
  188. }
  189. fileprivate static func restoreSystemOutputVolume() {
  190. guard UserDefaultsRepository.overrideSystemOutputVolume.value else {
  191. return
  192. }
  193. // cancel any volume change observations
  194. // self.volumeChangeDetector.isActive = false
  195. // restore system output volume with its value before overriding it
  196. if let volumeBeforeOverride = self.systemOutputVolumeBeforeOverride {
  197. MPVolumeView.setVolume(volumeBeforeOverride)
  198. }
  199. self.systemOutputVolumeBeforeOverride = nil
  200. }
  201. }
  202. class AudioPlayerDelegate: NSObject, AVAudioPlayerDelegate {
  203. /* audioPlayerDidFinishPlaying:successfully: is called when a sound has finished playing. This method is NOT called if the player is stopped due to an interruption. */
  204. func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
  205. NSLog("AlarmRule - audioPlayerDidFinishPlaying (\(flag))")
  206. }
  207. /* if an error occurs while decoding it will be reported to the delegate. */
  208. func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
  209. if let error = error {
  210. NSLog("AlarmRule - audioPlayerDecodeErrorDidOccur: \(error)")
  211. } else {
  212. NSLog("AlarmRule - audioPlayerDecodeErrorDidOccur")
  213. }
  214. }
  215. /* AVAudioPlayer INTERRUPTION NOTIFICATIONS ARE DEPRECATED - Use AVAudioSession instead. */
  216. /* audioPlayerBeginInterruption: is called when the audio session has been interrupted while the player was playing. The player will have been paused. */
  217. func audioPlayerBeginInterruption(_ player: AVAudioPlayer) {
  218. NSLog("AlarmRule - audioPlayerBeginInterruption")
  219. }
  220. /* audioPlayerEndInterruption:withOptions: is called when the audio session interruption has ended and this player had been interrupted while playing. */
  221. /* Currently the only flag is AVAudioSessionInterruptionFlags_ShouldResume. */
  222. func audioPlayerEndInterruption(_ player: AVAudioPlayer, withOptions flags: Int) {
  223. NSLog("AlarmRule - audioPlayerEndInterruption withOptions: \(flags)")
  224. }
  225. }
  226. // Helper function inserted by Swift 4.2 migrator.
  227. fileprivate func convertFromAVAudioSessionCategory(_ input: AVAudioSession.Category) -> String {
  228. return input.rawValue
  229. }
  230. extension MPVolumeView {
  231. static func setVolume(_ volume: Float) {
  232. // Need to use the MPVolumeView in order to change volume, but don't care about UI set so frame to .zero
  233. let volumeView = MPVolumeView(frame: .zero)
  234. // Search for the slider
  235. let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider
  236. // Update the slider value with the desired volume.
  237. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
  238. slider?.value = volume
  239. }
  240. // Optional - Remove the HUD
  241. if let app = UIApplication.shared.delegate as? AppDelegate, let window = app.window {
  242. volumeView.alpha = 0.000001
  243. window.addSubview(volumeView)
  244. }
  245. }
  246. }