AlarmSound.swift 12 KB

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