TSAudioPlayer.swift 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. //
  2. // TSAudioPlayer.swift
  3. // Girly
  4. //
  5. // Created by 100Years on 2025/1/9.
  6. //
  7. import AVFoundation
  8. class TSAudioPlayer: NSObject {
  9. private var player: AVPlayer?
  10. private var timeObserverToken: Any?
  11. var isLooping: Bool = false
  12. override init() {
  13. super.init()
  14. }
  15. var isPlaying: Bool {
  16. // return player?.rate != 0 && player?.error == nil
  17. if let player = player {
  18. if #available(iOS 10.0, *) {
  19. return player.timeControlStatus == .playing
  20. } else {
  21. return player.rate > 0
  22. }
  23. }
  24. return false
  25. }
  26. var duration: Double {
  27. if let currentItem = player?.currentItem {
  28. return CMTimeGetSeconds(currentItem.asset.duration)
  29. }
  30. return 0.0
  31. }
  32. var volume: Float {
  33. get {
  34. return player?.volume ?? 0.0
  35. }
  36. set {
  37. player?.volume = max(0.0, min(newValue, 1.0))
  38. }
  39. }
  40. var currentTime: Double {
  41. if let currentItem = player?.currentItem {
  42. return CMTimeGetSeconds(currentItem.currentTime())
  43. }
  44. return 0.0
  45. }
  46. var playerUsable: Bool {
  47. return player != nil
  48. }
  49. var currentTimeChanged: ((Double,Double) -> Void)?
  50. var audioPlayerDidFinishHandle: ((Bool) -> Void)?
  51. /// 初始化播放器
  52. /// - Parameter url: 音频文件的 URL
  53. init?(url: URL) {
  54. super.init()
  55. let playerItem = AVPlayerItem(url: url)
  56. player = AVPlayer(playerItem: playerItem)
  57. // 监听播放完成事件
  58. NotificationCenter.default.addObserver(
  59. self,
  60. selector: #selector(playerDidFinishPlaying),
  61. name: .AVPlayerItemDidPlayToEndTime,
  62. object: playerItem
  63. )
  64. // 设置音频会话
  65. do {
  66. let audioSession = AVAudioSession.sharedInstance()
  67. try audioSession.setCategory(.playback)
  68. try audioSession.setActive(true)
  69. } catch {
  70. print("TSAudioPlayer 音频会话设置失败: \(error.localizedDescription)")
  71. return nil
  72. }
  73. }
  74. deinit {
  75. stop()
  76. }
  77. /// 开始播放音频
  78. func play() {
  79. guard let player = player else { return }
  80. let outputVolume = AVAudioSession.sharedInstance().outputVolume
  81. print("TSAudioPlayer outputVolume: \(outputVolume)")
  82. if outputVolume == 0.0 {
  83. print("Please turn up the volume".localized)
  84. }
  85. player.play()
  86. // 监听播放进度
  87. addTimeObserver()
  88. }
  89. /// 暂停播放音频
  90. func pause() {
  91. player?.pause()
  92. }
  93. /// 停止并销毁播放器
  94. func stop() {
  95. player?.pause()
  96. player?.replaceCurrentItem(with: nil)
  97. player = nil
  98. removeTimeObserver()
  99. }
  100. /// 设置是否重复播放
  101. /// - Parameter loop: 是否循环播放
  102. func setLoop(_ loop: Bool) {
  103. isLooping = loop
  104. }
  105. /// 跳转到指定时间
  106. /// - Parameter time: 目标时间(秒)
  107. func seek(to time: Double) {
  108. if let player = player {
  109. let targetTime = CMTimeMakeWithSeconds(time, preferredTimescale: 600)
  110. player.seek(to: targetTime)
  111. }
  112. }
  113. /// 监听播放完成
  114. @objc private func playerDidFinishPlaying() {
  115. if isLooping {
  116. seek(to: 0.0)
  117. play()
  118. }
  119. audioPlayerDidFinishHandle?(true)
  120. }
  121. /// 添加时间观察者
  122. private func addTimeObserver() {
  123. let interval = CMTime(seconds: 0.1, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
  124. timeObserverToken = player?.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in
  125. guard let self = self else { return }
  126. let currentTime = CMTimeGetSeconds(time)
  127. self.currentTimeChanged?(currentTime,duration)
  128. }
  129. }
  130. /// 移除时间观察者
  131. private func removeTimeObserver() {
  132. if let token = timeObserverToken {
  133. player?.removeTimeObserver(token)
  134. timeObserverToken = nil
  135. }
  136. }
  137. }
  138. //class TSAudioPlayer : NSObject, AVAudioPlayerDelegate{
  139. // private var audioPlayer: AVAudioPlayer?
  140. // var isLooping: Bool = false
  141. //
  142. // override init() {
  143. // super.init()
  144. // }
  145. //
  146. // var isPlaying:Bool{
  147. // if let audioPlayer = audioPlayer {
  148. // return audioPlayer.isPlaying
  149. // }
  150. // return false
  151. // }
  152. //
  153. // var duration:Double{
  154. // if let audioPlayer = audioPlayer {
  155. // return audioPlayer.duration
  156. // }
  157. // return 0.0
  158. // }
  159. //
  160. // var volume:Float{
  161. // if let audioPlayer = audioPlayer {
  162. // return audioPlayer.volume
  163. // }
  164. // return 0
  165. // }
  166. //
  167. // var currentTime:Double{
  168. // if let audioPlayer = audioPlayer {
  169. // return audioPlayer.currentTime
  170. // }
  171. // return 0.0
  172. // }
  173. //
  174. // //播放器是否可用
  175. // var playerUsable:Bool {
  176. // if audioPlayer != nil {
  177. // return true
  178. // }
  179. // return false
  180. // }
  181. //
  182. // var timer:GCDTimer = GCDTimer()
  183. //
  184. // var currentTimeChanged:((Double)->Void)?
  185. //
  186. // /// 初始化播放器
  187. // /// - Parameter url: 音频文件的 URL
  188. // init?(url: URL) {
  189. // super.init()
  190. // do {
  191. // audioPlayer = try AVAudioPlayer(contentsOf: url)
  192. // audioPlayer?.prepareToPlay()
  193. // audioPlayer?.delegate = self
  194. // let audioSession = AVAudioSession.sharedInstance()
  195. // try audioSession.setCategory(.playback) // 设置类别为 playback
  196. // try audioSession.setActive(true) // 激活音频会话
  197. //
  198. // } catch {
  199. // dePrint("TSAudioPlayer 音频文件加载失败: \(error.localizedDescription)")
  200. // return nil
  201. // }
  202. // }
  203. //
  204. // /// 开始播放音频
  205. // func play() {
  206. // guard let audioPlayer = audioPlayer else { return }
  207. // let outputVolume = AVAudioSession.sharedInstance().outputVolume
  208. // dePrint("TSAudioPlayer outputVolume\(outputVolume)")
  209. //
  210. // if outputVolume == 0.0 {
  211. // TSToastShared.showToast(text: "Please turn up the volume".localized)
  212. // }
  213. //
  214. // audioPlayer.numberOfLoops = isLooping ? -1 : 0
  215. // audioPlayer.play()
  216. //
  217. // timer.start(interval: 0.5, repeats: true) { [weak self] in
  218. // guard let self = self else { return }
  219. //
  220. // let currentTime = audioPlayer.currentTime
  221. // dePrint("TSAudioPlayer Current playback time: \(currentTime) seconds")
  222. // currentTimeChanged?(currentTime)
  223. // }
  224. // }
  225. //
  226. // /// 暂停播放音频
  227. // func pause() {
  228. // audioPlayer?.pause()
  229. // }
  230. //
  231. // /// 停止并销毁播放器
  232. // func stop() {
  233. // audioPlayer?.stop()
  234. // audioPlayer = nil
  235. // timer.stop()
  236. // }
  237. //
  238. // /// 设置是否重复播放
  239. // /// - Parameter loop: 是否循环播放
  240. // func setLoop(_ loop: Bool) {
  241. // isLooping = loop
  242. // audioPlayer?.numberOfLoops = loop ? -1 : 0
  243. // }
  244. //
  245. // /// 设置音量
  246. // /// - Parameter volume: 音量大小 (0.0 静音, 1.0 最大)
  247. // func setVolume(_ volume: Float) {
  248. // audioPlayer?.volume = max(0.0, min(volume, 1.0))
  249. // }
  250. //
  251. // deinit {
  252. // stop()
  253. // }
  254. //
  255. // var audioPlayerDidFinishHandle:((Bool)->Void)?
  256. // // 实现 AVAudioPlayerDelegate 方法
  257. // func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
  258. // if flag {
  259. // print("TSAudioPlayer 音频播放完成")
  260. //
  261. // } else {
  262. // print("TSAudioPlayer 音频播放中断")
  263. // }
  264. // audioPlayerDidFinishHandle?(flag)
  265. // }
  266. //}