AlarmSound.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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 playOnce() {
  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 = 0
  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 UserDefaultsRepository.overrideSystemOutputVolume.value {
  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 play() {
  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 = -1
  140. // Store existing volume
  141. if self.systemOutputVolumeBeforeOverride == nil {
  142. self.systemOutputVolumeBeforeOverride = AVAudioSession.sharedInstance().outputVolume
  143. }
  144. // init volume before start playing (mute if fade-in)
  145. //self.audioPlayer!.volume = (self.muted || (UserDefaultsRepository.fadeInTimeInterval.value > 0)) ? 0.0 : 1.0
  146. if !self.audioPlayer!.prepareToPlay() {
  147. NSLog("AlarmSound - audio player failed preparing to play")
  148. }
  149. if self.audioPlayer!.play() {
  150. if !self.isPlaying {
  151. NSLog("AlarmSound - not playing after calling play")
  152. NSLog("AlarmSound - rate value: \(self.audioPlayer!.rate)")
  153. }
  154. } else {
  155. NSLog("AlarmSound - audio player failed to play")
  156. }
  157. // do fade-in
  158. //if !self.muted && (UserDefaultsRepository.fadeInTimeInterval.value > 0) {
  159. // self.audioPlayer!.setVolume(1.0, fadeDuration: UserDefaultsRepository.fadeInTimeInterval.value)
  160. //}
  161. if UserDefaultsRepository.overrideSystemOutputVolume.value {
  162. MPVolumeView.setVolume(UserDefaultsRepository.forcedOutputVolume.value)
  163. }
  164. //self.playingTimer = Timer.schedule(repeatInterval: 1.0, handler: self.onPlayingTimer)
  165. } catch let error {
  166. NSLog("AlarmSound - unable to play sound; error: \(error)")
  167. }
  168. }
  169. static func playTerminated() {
  170. guard !self.isPlaying else {
  171. return
  172. }
  173. do {
  174. self.audioPlayer = try AVAudioPlayer(contentsOf: self.soundURL)
  175. self.audioPlayer!.delegate = self.audioPlayerDelegate
  176. try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category(rawValue: convertFromAVAudioSessionCategory(AVAudioSession.Category.playback)))
  177. try AVAudioSession.sharedInstance().setActive(true)
  178. // Play endless loops
  179. self.audioPlayer!.numberOfLoops = 2
  180. // Store existing volume
  181. if self.systemOutputVolumeBeforeOverride == nil {
  182. self.systemOutputVolumeBeforeOverride = AVAudioSession.sharedInstance().outputVolume
  183. }
  184. if !self.audioPlayer!.prepareToPlay() {
  185. NSLog("Terminate AlarmSound - audio player failed preparing to play")
  186. }
  187. if self.audioPlayer!.play() {
  188. if !self.isPlaying {
  189. NSLog("Terminate AlarmSound - not playing after calling play")
  190. NSLog("Terminate AlarmSound - rate value: \(self.audioPlayer!.rate)")
  191. }
  192. } else {
  193. NSLog("Terminate AlarmSound - audio player failed to play")
  194. }
  195. MPVolumeView.setVolume(1.0)
  196. } catch let error {
  197. NSLog("Terminate AlarmSound - unable to play sound; error: \(error)")
  198. }
  199. }
  200. fileprivate static func onPlayingTimer(timer: Timer?) {
  201. // player should be playing, not muted!
  202. guard self.isPlaying && !self.isMuted else {
  203. return
  204. }
  205. // application should be in active state!
  206. guard UIApplication.shared.applicationState == .active else {
  207. return
  208. }
  209. if UserDefaultsRepository.overrideSystemOutputVolume.value {
  210. // keep the system output volume before overriding it
  211. if self.systemOutputVolumeBeforeOverride == nil {
  212. //self.systemOutputVolumeBeforeOverride = MPVolumeView.volume
  213. self.systemOutputVolumeBeforeOverride = AVAudioSession.sharedInstance().outputVolume
  214. }
  215. // override the system output volume
  216. MPVolumeView.setVolume(UserDefaultsRepository.systemOutputVolume.value)
  217. // if MPVolumeView.volume != UserDefaultsRepository.systemOutputVolume.value {
  218. // self.volumeChangeDetector.isActive = false
  219. // MPVolumeView.volume = UserDefaultsRepository.systemOutputVolume.value
  220. // } else {
  221. // listen to user volume changes
  222. // self.volumeChangeDetector.isActive = true
  223. // }
  224. }
  225. if self.vibrate.value {
  226. AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
  227. }
  228. }
  229. fileprivate static func restoreSystemOutputVolume() {
  230. guard UserDefaultsRepository.overrideSystemOutputVolume.value else {
  231. return
  232. }
  233. // cancel any volume change observations
  234. // self.volumeChangeDetector.isActive = false
  235. // restore system output volume with its value before overriding it
  236. if let volumeBeforeOverride = self.systemOutputVolumeBeforeOverride {
  237. MPVolumeView.setVolume(volumeBeforeOverride)
  238. }
  239. self.systemOutputVolumeBeforeOverride = nil
  240. }
  241. }
  242. class AudioPlayerDelegate: NSObject, AVAudioPlayerDelegate {
  243. /* audioPlayerDidFinishPlaying:successfully: is called when a sound has finished playing. This method is NOT called if the player is stopped due to an interruption. */
  244. func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
  245. NSLog("AlarmRule - audioPlayerDidFinishPlaying (\(flag))")
  246. }
  247. /* if an error occurs while decoding it will be reported to the delegate. */
  248. func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
  249. if let error = error {
  250. NSLog("AlarmRule - audioPlayerDecodeErrorDidOccur: \(error)")
  251. } else {
  252. NSLog("AlarmRule - audioPlayerDecodeErrorDidOccur")
  253. }
  254. }
  255. /* AVAudioPlayer INTERRUPTION NOTIFICATIONS ARE DEPRECATED - Use AVAudioSession instead. */
  256. /* audioPlayerBeginInterruption: is called when the audio session has been interrupted while the player was playing. The player will have been paused. */
  257. func audioPlayerBeginInterruption(_ player: AVAudioPlayer) {
  258. NSLog("AlarmRule - audioPlayerBeginInterruption")
  259. }
  260. /* audioPlayerEndInterruption:withOptions: is called when the audio session interruption has ended and this player had been interrupted while playing. */
  261. /* Currently the only flag is AVAudioSessionInterruptionFlags_ShouldResume. */
  262. func audioPlayerEndInterruption(_ player: AVAudioPlayer, withOptions flags: Int) {
  263. NSLog("AlarmRule - audioPlayerEndInterruption withOptions: \(flags)")
  264. }
  265. }
  266. // Helper function inserted by Swift 4.2 migrator.
  267. fileprivate func convertFromAVAudioSessionCategory(_ input: AVAudioSession.Category) -> String {
  268. return input.rawValue
  269. }
  270. extension MPVolumeView {
  271. static func setVolume(_ volume: Float) {
  272. // Need to use the MPVolumeView in order to change volume, but don't care about UI set so frame to .zero
  273. let volumeView = MPVolumeView(frame: .zero)
  274. // Search for the slider
  275. let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider
  276. // Update the slider value with the desired volume.
  277. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
  278. slider?.value = volume
  279. }
  280. // Optional - Remove the HUD
  281. if let app = UIApplication.shared.delegate as? AppDelegate, let window = app.window {
  282. volumeView.alpha = 0.000001
  283. window.addSubview(volumeView)
  284. }
  285. }
  286. }