|
@@ -0,0 +1,192 @@
|
|
|
+//
|
|
|
+// BFAudioPlayer.swift
|
|
|
+// PQSpeed
|
|
|
+//
|
|
|
+// Created by ak on 2020/8/14.
|
|
|
+// Copyright © 2020 BytesFlow. All rights reserved.
|
|
|
+// 功能:播放音频文件
|
|
|
+/* e.g.
|
|
|
+
|
|
|
+ BFAudioPlayer.shared.configPlayer(audioPathURL: audioPath)
|
|
|
+ BFAudioPlayer.shared.delegate = self
|
|
|
+ BFAudioPlayer.shared.playOrPause()
|
|
|
+ */
|
|
|
+
|
|
|
+import Foundation
|
|
|
+
|
|
|
+import AVFoundation
|
|
|
+import UIKit
|
|
|
+
|
|
|
+@objc protocol BFAudioPlayerDelegate: NSObjectProtocol {
|
|
|
+ // 播放完成
|
|
|
+ @objc optional func audioPlayerDidFinish()
|
|
|
+
|
|
|
+ // 播放开始
|
|
|
+ @objc optional func audioPlayerStart()
|
|
|
+ // 播放进度
|
|
|
+ @objc optional func playProgress(currentTime: TimeInterval, progress: Double)
|
|
|
+}
|
|
|
+
|
|
|
+class BFAudioPlayer: NSObject {
|
|
|
+ // MARK: - Variables
|
|
|
+
|
|
|
+ var player: AVAudioPlayer?
|
|
|
+ var title: String = ""
|
|
|
+ var artist: String = ""
|
|
|
+ var album: String = ""
|
|
|
+ var artwork: UIImage?
|
|
|
+
|
|
|
+ private var timer: Timer?
|
|
|
+
|
|
|
+ weak var delegate: BFAudioPlayerDelegate?
|
|
|
+
|
|
|
+ // MARK: - Constructor
|
|
|
+
|
|
|
+ override func copy() -> Any {
|
|
|
+ return self
|
|
|
+ }
|
|
|
+
|
|
|
+ override func mutableCopy() -> Any {
|
|
|
+ return self
|
|
|
+ }
|
|
|
+
|
|
|
+ static let shared = BFAudioPlayer()
|
|
|
+
|
|
|
+ override private init() {
|
|
|
+ super.init()
|
|
|
+ }
|
|
|
+
|
|
|
+ func configPlayer(audioPathURL: URL) {
|
|
|
+ BFLog(message: "audioPath is \(audioPathURL.relativePath)")
|
|
|
+ loadMetaDataFromAudioFile(audioPathURL)
|
|
|
+ if !(player != nil && player?.url?.absoluteString == audioPathURL.absoluteString) {
|
|
|
+ do {
|
|
|
+ try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
|
|
|
+ try AVAudioSession.sharedInstance().setActive(true)
|
|
|
+
|
|
|
+ // Prepare player
|
|
|
+ let data: Data = try Data(contentsOf: audioPathURL)
|
|
|
+ player = try AVAudioPlayer(data: data)
|
|
|
+
|
|
|
+ } catch _ {
|
|
|
+ BFLog(message: "播放失败 向上看 系统LOG ")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ player?.currentTime = 0
|
|
|
+ player?.delegate = self
|
|
|
+ player?.enableRate = true
|
|
|
+ player?.prepareToPlay()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // MARK: - Internal methods
|
|
|
+
|
|
|
+ func playOrPause() {
|
|
|
+ if player?.isPlaying ?? false {
|
|
|
+ stopTimer()
|
|
|
+ player?.pause()
|
|
|
+ BFLog(message: "停止播放了")
|
|
|
+ } else {
|
|
|
+ if delegate != nil {
|
|
|
+ delegate?.audioPlayerStart!()
|
|
|
+ }
|
|
|
+ startTimer()
|
|
|
+ player?.play()
|
|
|
+ BFLog(message: "playOrPause开始播放了")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func pause() {
|
|
|
+ if player != nil, player?.isPlaying ?? false {
|
|
|
+ BFLog(message: "停止播放了")
|
|
|
+ stopTimer()
|
|
|
+ player?.pause()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func play() {
|
|
|
+ if delegate != nil {
|
|
|
+ delegate?.audioPlayerStart!()
|
|
|
+ }
|
|
|
+ BFLog(message: "开始播放了")
|
|
|
+ player?.play()
|
|
|
+ startTimer()
|
|
|
+ }
|
|
|
+
|
|
|
+ func stop() {
|
|
|
+ stopTimer()
|
|
|
+ if player != nil {
|
|
|
+ player?.stop()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func setRate(_ rate: Float) {
|
|
|
+ player?.rate = rate
|
|
|
+ }
|
|
|
+
|
|
|
+ func setPlayingTime(_ pos: Double) {
|
|
|
+ player?.currentTime = pos
|
|
|
+ }
|
|
|
+
|
|
|
+ // MARK: - Private methods
|
|
|
+
|
|
|
+ func loadMetaDataFromAudioFile(_ url: URL) {
|
|
|
+ let asset: AVAsset = AVURLAsset(url: url, options: avAssertOptions)
|
|
|
+ let metadata: Array = asset.commonMetadata
|
|
|
+
|
|
|
+ for item in metadata {
|
|
|
+ switch item.commonKey {
|
|
|
+ case AVMetadataKey.commonKeyTitle:
|
|
|
+ title = item.stringValue!
|
|
|
+ case AVMetadataKey.commonKeyAlbumName:
|
|
|
+ album = item.stringValue!
|
|
|
+ case AVMetadataKey.commonKeyArtist:
|
|
|
+ artist = item.stringValue!
|
|
|
+ case AVMetadataKey.commonKeyArtwork:
|
|
|
+ artwork = UIImage(data: item.dataValue!)
|
|
|
+ default:
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 开始计时
|
|
|
+ func startTimer() {
|
|
|
+ if timer == nil {
|
|
|
+ timer = PQWeakTimer.scheduledTimer(timeInterval: 1 / 60, target: self, repeats: true) { [weak self] _ in
|
|
|
+
|
|
|
+// BFLog(message: "播放进度 \(String(describing: self?.player?.currentTime)) 总时长:\(String(describing: self?.player?.duration))")
|
|
|
+ // currentTime 有可能不是从0开始
|
|
|
+ var progress = 0.0
|
|
|
+ if self?.player?.duration ?? 0 > 0 {
|
|
|
+ progress = Double((self?.player?.currentTime ?? 0) / (self?.player?.duration ?? 1))
|
|
|
+ }
|
|
|
+// BFLog(message: "progress is \(progress) currentTime\(self?.player?.currentTime)")
|
|
|
+ if self?.delegate != nil {
|
|
|
+ self!.delegate!.playProgress!(currentTime: self?.player?.currentTime ?? 0, progress: progress)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 停止计时
|
|
|
+ func stopTimer() {
|
|
|
+ if timer != nil {
|
|
|
+ timer?.invalidate() // 销毁timer
|
|
|
+ timer = nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension BFAudioPlayer: AVAudioPlayerDelegate {
|
|
|
+ func audioPlayerDidFinishPlaying(_: AVAudioPlayer, successfully _: Bool) {
|
|
|
+ if delegate != nil {
|
|
|
+ delegate?.audioPlayerDidFinish!()
|
|
|
+ }
|
|
|
+ stop()
|
|
|
+
|
|
|
+ // Send notification
|
|
|
+ let notification = Notification(name: Notification.Name(rawValue: "stop"), object: self)
|
|
|
+ NotificationCenter.default.post(notification)
|
|
|
+ }
|
|
|
+}
|