|
@@ -1,493 +0,0 @@
|
|
|
-//
|
|
|
-// TSBusinessAudioPlayer.swift
|
|
|
-// AIRingtone
|
|
|
-//
|
|
|
-// Created by 100Years on 2025/3/7.
|
|
|
-//
|
|
|
-
|
|
|
-//import AVFoundation
|
|
|
-//class TSBusinessAudioPlayer {
|
|
|
-//
|
|
|
-// static let shared = TSBusinessAudioPlayer()
|
|
|
-//
|
|
|
-// enum PlayerState:Equatable {
|
|
|
-// case play
|
|
|
-// case pause
|
|
|
-// case stop
|
|
|
-// case loading(Float)
|
|
|
-// case volume(Float)
|
|
|
-// case currentTime(Double)
|
|
|
-// }
|
|
|
-//
|
|
|
-// private var audioPlayer: TSAudioPlayer?
|
|
|
-//
|
|
|
-// var stateChangedHandle:((PlayerState) -> Void)?
|
|
|
-// var currentTimeChangedHandle:((Double,Double) -> Void)?
|
|
|
-//
|
|
|
-// var currentPlayerState:PlayerState = .stop
|
|
|
-// var duration:Double{
|
|
|
-// if let audioPlayer = audioPlayer {
|
|
|
-// return audioPlayer.duration
|
|
|
-// }
|
|
|
-// return 0.0
|
|
|
-// }
|
|
|
-//
|
|
|
-// var isPlaying:Bool{
|
|
|
-// if let audioPlayer = audioPlayer {
|
|
|
-// return audioPlayer.isPlaying
|
|
|
-// }
|
|
|
-// return false
|
|
|
-// }
|
|
|
-//
|
|
|
-// var isLoading:Bool{
|
|
|
-// switch currentPlayerState {
|
|
|
-// case .loading(let float):
|
|
|
-// return float < 1.0 ? true : false
|
|
|
-// default:
|
|
|
-// return false
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// var currentTime:Double{
|
|
|
-// if let audioPlayer = audioPlayer {
|
|
|
-// return audioPlayer.currentTime
|
|
|
-// }
|
|
|
-// return 0.0
|
|
|
-// }
|
|
|
-//
|
|
|
-// /// 跳转到指定时间
|
|
|
-// /// - Parameter time: 目标时间(秒)
|
|
|
-// func seek(to time: Double) {
|
|
|
-// audioPlayer?.seek(to: time)
|
|
|
-// }
|
|
|
-//
|
|
|
-// var playProgress:Double{
|
|
|
-// let playProgress = currentTime / duration
|
|
|
-//// logPrint("TSAudioPlayer playProgress = \(playProgress)")
|
|
|
-// return playProgress
|
|
|
-// }
|
|
|
-//
|
|
|
-// //播放器是否可用
|
|
|
-// var playerUsable:Bool {
|
|
|
-// if let audioPlayer = audioPlayer {
|
|
|
-// return audioPlayer.playerUsable
|
|
|
-// }
|
|
|
-// return false
|
|
|
-// }
|
|
|
-// var currentURLString:String = ""
|
|
|
-// var currentLocalURL:URL? = nil
|
|
|
-// var currentIndexPath:IndexPath? = nil
|
|
|
-//
|
|
|
-// //加载音乐可能 2-3 秒有结果,停止加载后播放.
|
|
|
-// private var isStopPlayingAfterLoading:Bool = false
|
|
|
-//
|
|
|
-// func isPlayURLString(string:String,localURL:URL? = nil,indexPath:IndexPath? = nil) -> Bool {
|
|
|
-//
|
|
|
-// if currentURLString == string {
|
|
|
-//
|
|
|
-// if let currentIndexPath = currentIndexPath,
|
|
|
-// let indexPath = indexPath,
|
|
|
-// indexPath != currentIndexPath
|
|
|
-// {
|
|
|
-// return false
|
|
|
-// }else if let currentLocalURL = currentLocalURL,
|
|
|
-// let localURL = localURL,
|
|
|
-// currentLocalURL != localURL
|
|
|
-// {
|
|
|
-// return false
|
|
|
-// }else{
|
|
|
-// return true
|
|
|
-// }
|
|
|
-// }
|
|
|
-// return false
|
|
|
-// }
|
|
|
-//
|
|
|
-// func loadLoactionURL(url:URL){
|
|
|
-// self.audioPlayer = TSAudioPlayer(url: url)
|
|
|
-// }
|
|
|
-//
|
|
|
-// func playUrlString(_ urlString:String?,localURL:URL? = nil,loop:Bool = false,indexPath:IndexPath? = nil) {
|
|
|
-// self.stop()
|
|
|
-// if let urlString = urlString {
|
|
|
-//
|
|
|
-// self.currentURLString = urlString
|
|
|
-// self.currentLocalURL = localURL
|
|
|
-// self.currentIndexPath = indexPath
|
|
|
-//
|
|
|
-// let palyFile:(URL)->Void = { [weak self] url in
|
|
|
-// guard let self = self else { return }
|
|
|
-// logPrint("TSAudioPlayer 正在播放url:\(currentURLString)")
|
|
|
-// logPrint("TSAudioPlayer 正在播放path:\(url)")
|
|
|
-// self.audioPlayer = TSAudioPlayer(url: url)
|
|
|
-// self.audioPlayer?.setLoop(loop)
|
|
|
-//
|
|
|
-// if self.audioPlayer?.volume == 0 {
|
|
|
-// setVolume(volume: 1.0)
|
|
|
-// }
|
|
|
-//
|
|
|
-// self.audioPlayer?.currentTimeChanged = { [weak self] currentTime,duration in
|
|
|
-// guard let self = self else { return }
|
|
|
-// currentTimeChangedHandle?(currentTime,duration)
|
|
|
-// changePlayerState(.currentTime(currentTime))
|
|
|
-// }
|
|
|
-//
|
|
|
-// self.play()
|
|
|
-// logPrint(self.audioPlayer?.duration)
|
|
|
-//
|
|
|
-// self.audioPlayer?.audioPlayerDidFinishHandle = { [weak self] flag in
|
|
|
-// guard let self = self else { return }
|
|
|
-// if flag == true, self.audioPlayer?.isLooping == false{
|
|
|
-// stop()
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// isStopPlayingAfterLoading = false
|
|
|
-//
|
|
|
-// if let path = self.currentLocalURL,ASFileManager.fileExists(at: path){
|
|
|
-// palyFile(path) //播放
|
|
|
-//
|
|
|
-// }else{
|
|
|
-// self.changePlayerState(.loading(0.0))
|
|
|
-//
|
|
|
-// _ = ASDownloadManager.getDownLoadRing(urlString: urlString, progressHandler: { progress in
|
|
|
-// logPrint("ASDownloadManager.etDownLoadRing progress = \(progress)")
|
|
|
-// }, complete: {[weak self] url, success in
|
|
|
-//
|
|
|
-// guard let self = self else { return }
|
|
|
-//
|
|
|
-// self.changePlayerState(.loading(1.0))
|
|
|
-//
|
|
|
-// if isStopPlayingAfterLoading == true || currentURLString != urlString{
|
|
|
-// isStopPlayingAfterLoading = false
|
|
|
-// return
|
|
|
-// }
|
|
|
-//
|
|
|
-// if let url = url {
|
|
|
-// palyFile(url) //播放
|
|
|
-// }else{
|
|
|
-// //暂停
|
|
|
-// self.stop()
|
|
|
-// }
|
|
|
-// })
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-//
|
|
|
-// func play() {
|
|
|
-// self.audioPlayer?.play()
|
|
|
-// changePlayerState(.play)
|
|
|
-// }
|
|
|
-//
|
|
|
-// func stop() {
|
|
|
-// self.audioPlayer?.currentTimeChanged = nil
|
|
|
-// isStopPlayingAfterLoading = true
|
|
|
-// currentURLString = ""
|
|
|
-// self.audioPlayer?.stop()
|
|
|
-// changePlayerState(.stop)
|
|
|
-// }
|
|
|
-//
|
|
|
-// func pause() {
|
|
|
-// isStopPlayingAfterLoading = true
|
|
|
-// self.audioPlayer?.pause()
|
|
|
-// changePlayerState(.pause)
|
|
|
-// }
|
|
|
-//
|
|
|
-// func setVolume(volume:Float){
|
|
|
-// self.audioPlayer?.volume = volume
|
|
|
-// changePlayerState(.volume(volume))
|
|
|
-// }
|
|
|
-//
|
|
|
-//
|
|
|
-// func changeAudioSwitch()->Float {
|
|
|
-// let volume:Float = self.audioPlayer?.volume == 0.0 ? 1.0 : 0.0
|
|
|
-// setVolume(volume: volume)
|
|
|
-// return volume
|
|
|
-// }
|
|
|
-//
|
|
|
-// func changePlayerState(_ state:PlayerState){
|
|
|
-//
|
|
|
-// if case .currentTime(let time) = state {} else {
|
|
|
-// logPrint("TSAudioPlayer changePlayerState=\(state)")
|
|
|
-// }
|
|
|
-// currentPlayerState = state
|
|
|
-// kExecuteOnMainThread{
|
|
|
-// self.stateChangedHandle?(state)
|
|
|
-//// NotificationCenter.default.post(name: .kBusinessAudioStateChange, object: nil, userInfo: ["PlayerState": state])
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// deinit {
|
|
|
-// logPrint("TSAudioPlayer TSBusinessAudioPlayer deinit")
|
|
|
-// }
|
|
|
-//}
|
|
|
-//
|
|
|
-//
|
|
|
-//extension TSBusinessAudioPlayer{
|
|
|
-// struct AudioFileInfo {
|
|
|
-// let sizeInBytes: UInt64? // 文件大小(字节)
|
|
|
-// let durationInSeconds: Double? // 音频时长(秒)
|
|
|
-// let songName: String? // 音频时长(秒)
|
|
|
-// }
|
|
|
-//
|
|
|
-// static func getAudioFileInfo(path: String) -> AudioFileInfo? {
|
|
|
-// // 1. 检查URL有效性
|
|
|
-// guard let url = URL(string: path) else {
|
|
|
-// print("getAudioFileInfo 无效的URL字符串")
|
|
|
-// return nil
|
|
|
-// }
|
|
|
-//
|
|
|
-// // 2. 检查文件是否存在(仅限本地文件)
|
|
|
-// guard FileManager.default.fileExists(atPath: url.path) else {
|
|
|
-// print("getAudioFileInfo 文件不存在或不是本地路径")
|
|
|
-// return nil
|
|
|
-// }
|
|
|
-//
|
|
|
-// // 3. 获取文件大小
|
|
|
-// let fileSize: UInt64? = {
|
|
|
-// do {
|
|
|
-// let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
|
|
|
-// return attributes[.size] as? UInt64
|
|
|
-// } catch {
|
|
|
-// print("获取文件大小失败: \(error.localizedDescription)")
|
|
|
-// return nil
|
|
|
-// }
|
|
|
-// }()
|
|
|
-//
|
|
|
-// // 4. 获取音频时长
|
|
|
-// let duration: Double? = {
|
|
|
-// return getAudioDurationWithAudioFile(url: url)
|
|
|
-// }()
|
|
|
-//
|
|
|
-// // 5. 获取音频名称
|
|
|
-// let songName: String? = {
|
|
|
-// return getSongNameFromLocalFile(fileURL: url)
|
|
|
-// }()
|
|
|
-//
|
|
|
-// return AudioFileInfo(sizeInBytes: fileSize, durationInSeconds: duration,songName: songName)
|
|
|
-// }
|
|
|
-//
|
|
|
-// /// 同步获取音频时长(可能阻塞线程!)
|
|
|
-// /// 使用 AudioFile 同步获取音频时长
|
|
|
-// static func getAudioDurationWithAudioFile(url: URL) -> TimeInterval? {
|
|
|
-// var audioFile: AudioFileID?
|
|
|
-// let status = AudioFileOpenURL(url as CFURL, .readPermission, 0, &audioFile)
|
|
|
-//
|
|
|
-// guard status == noErr, let file = audioFile else {
|
|
|
-// print("⚠️ 打开音频文件失败: \(status)")
|
|
|
-// return nil
|
|
|
-// }
|
|
|
-//
|
|
|
-// // 获取音频时长(单位:秒)
|
|
|
-// var duration: Float64 = 0
|
|
|
-// var propertySize = UInt32(MemoryLayout.size(ofValue: duration))
|
|
|
-// let durationStatus = AudioFileGetProperty(
|
|
|
-// file,
|
|
|
-// kAudioFilePropertyEstimatedDuration,
|
|
|
-// &propertySize,
|
|
|
-// &duration
|
|
|
-// )
|
|
|
-//
|
|
|
-// AudioFileClose(file)
|
|
|
-//
|
|
|
-// return durationStatus == noErr ? duration : nil
|
|
|
-// }
|
|
|
-//
|
|
|
-// //获取音频的名称
|
|
|
-// static func getSongNameFromLocalFile(fileURL: URL) -> String? {
|
|
|
-// // 1. 检查文件是否存在
|
|
|
-// guard FileManager.default.fileExists(atPath: fileURL.path) else {
|
|
|
-// print("文件不存在")
|
|
|
-// return nil
|
|
|
-// }
|
|
|
-//
|
|
|
-// // 2. 创建 AVAsset 对象(代表音频文件)
|
|
|
-// let asset = AVAsset(url: fileURL)
|
|
|
-//
|
|
|
-// // 3. 同步读取元数据(适用于本地文件)
|
|
|
-// var songName: String? = nil
|
|
|
-// let metadataFormats = asset.availableMetadataFormats
|
|
|
-//
|
|
|
-// for format in metadataFormats {
|
|
|
-// let metadata = asset.metadata(forFormat: format)
|
|
|
-//
|
|
|
-// // 4. 遍历元数据项,查找歌曲标题
|
|
|
-// for item in metadata {
|
|
|
-// if item.commonKey == .commonKeyTitle, // 标准键:标题
|
|
|
-// let value = item.value as? String { // 确保值是字符串
|
|
|
-// songName = value
|
|
|
-// break
|
|
|
-// }
|
|
|
-//
|
|
|
-// // 额外检查 ID3 标签(某些 MP3 文件可能用非标准键)
|
|
|
-// if item.key as? String == "TIT2", // ID3v2 标题键
|
|
|
-// let value = item.value as? String {
|
|
|
-// songName = value
|
|
|
-// break
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// if songName != nil { break }
|
|
|
-// }
|
|
|
-//
|
|
|
-// // 5. 返回结果(若未找到,则尝试从文件名推断)
|
|
|
-// return songName ?? fileURL.deletingPathExtension().lastPathComponent
|
|
|
-// }
|
|
|
-//}
|
|
|
-//
|
|
|
-//class TSAudioPlayer: NSObject {
|
|
|
-// private var player: AVPlayer?
|
|
|
-// private var timeObserverToken: Any?
|
|
|
-// var isLooping: Bool = false
|
|
|
-//
|
|
|
-// override init() {
|
|
|
-// super.init()
|
|
|
-// }
|
|
|
-//
|
|
|
-// var isPlaying: Bool {
|
|
|
-//// return player?.rate != 0 && player?.error == nil
|
|
|
-//
|
|
|
-// if let player = player {
|
|
|
-// if #available(iOS 10.0, *) {
|
|
|
-// return player.timeControlStatus == .playing
|
|
|
-// } else {
|
|
|
-// return player.rate > 0
|
|
|
-// }
|
|
|
-// }
|
|
|
-// return false
|
|
|
-// }
|
|
|
-//
|
|
|
-// var duration: Double {
|
|
|
-// if let currentItem = player?.currentItem {
|
|
|
-// return CMTimeGetSeconds(currentItem.asset.duration)
|
|
|
-// }
|
|
|
-// return 0.0
|
|
|
-// }
|
|
|
-//
|
|
|
-// var volume: Float {
|
|
|
-// get {
|
|
|
-// return player?.volume ?? 0.0
|
|
|
-// }
|
|
|
-// set {
|
|
|
-// player?.volume = max(0.0, min(newValue, 1.0))
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// var currentTime: Double {
|
|
|
-// if let currentItem = player?.currentItem {
|
|
|
-// return CMTimeGetSeconds(currentItem.currentTime())
|
|
|
-// }
|
|
|
-// return 0.0
|
|
|
-// }
|
|
|
-//
|
|
|
-// var playerUsable: Bool {
|
|
|
-// return player != nil
|
|
|
-// }
|
|
|
-//
|
|
|
-// var currentTimeChanged: ((Double,Double) -> Void)?
|
|
|
-// var audioPlayerDidFinishHandle: ((Bool) -> Void)?
|
|
|
-//
|
|
|
-// /// 初始化播放器
|
|
|
-// /// - Parameter url: 音频文件的 URL
|
|
|
-// init?(url: URL) {
|
|
|
-// super.init()
|
|
|
-//
|
|
|
-// let playerItem = AVPlayerItem(url: url)
|
|
|
-// player = AVPlayer(playerItem: playerItem)
|
|
|
-//
|
|
|
-// // 监听播放完成事件
|
|
|
-// NotificationCenter.default.addObserver(
|
|
|
-// self,
|
|
|
-// selector: #selector(playerDidFinishPlaying),
|
|
|
-// name: .AVPlayerItemDidPlayToEndTime,
|
|
|
-// object: playerItem
|
|
|
-// )
|
|
|
-//
|
|
|
-// // 设置音频会话
|
|
|
-// do {
|
|
|
-// let audioSession = AVAudioSession.sharedInstance()
|
|
|
-// try audioSession.setCategory(.playback)
|
|
|
-// try audioSession.setActive(true)
|
|
|
-// } catch {
|
|
|
-// print("TSAudioPlayer 音频会话设置失败: \(error.localizedDescription)")
|
|
|
-// return nil
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// deinit {
|
|
|
-// stop()
|
|
|
-// }
|
|
|
-//
|
|
|
-// /// 开始播放音频
|
|
|
-// func play() {
|
|
|
-// guard let player = player else { return }
|
|
|
-//
|
|
|
-// let outputVolume = AVAudioSession.sharedInstance().outputVolume
|
|
|
-// print("TSAudioPlayer outputVolume: \(outputVolume)")
|
|
|
-//
|
|
|
-// if outputVolume == 0.0 {
|
|
|
-// print("Please turn up the volume".localized)
|
|
|
-// }
|
|
|
-//
|
|
|
-// player.play()
|
|
|
-//
|
|
|
-// // 监听播放进度
|
|
|
-// addTimeObserver()
|
|
|
-// }
|
|
|
-//
|
|
|
-// /// 暂停播放音频
|
|
|
-// func pause() {
|
|
|
-// player?.pause()
|
|
|
-// }
|
|
|
-//
|
|
|
-// /// 停止并销毁播放器
|
|
|
-// func stop() {
|
|
|
-// player?.pause()
|
|
|
-// player?.replaceCurrentItem(with: nil)
|
|
|
-// player = nil
|
|
|
-// removeTimeObserver()
|
|
|
-// }
|
|
|
-//
|
|
|
-// /// 设置是否重复播放
|
|
|
-// /// - Parameter loop: 是否循环播放
|
|
|
-// func setLoop(_ loop: Bool) {
|
|
|
-// isLooping = loop
|
|
|
-// }
|
|
|
-//
|
|
|
-// /// 跳转到指定时间
|
|
|
-// /// - Parameter time: 目标时间(秒)
|
|
|
-// func seek(to time: Double) {
|
|
|
-// if let player = player {
|
|
|
-// let targetTime = CMTimeMakeWithSeconds(time, preferredTimescale: 600)
|
|
|
-// player.seek(to: targetTime)
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// /// 监听播放完成
|
|
|
-// @objc private func playerDidFinishPlaying() {
|
|
|
-// if isLooping {
|
|
|
-// seek(to: 0.0)
|
|
|
-// play()
|
|
|
-// }
|
|
|
-// audioPlayerDidFinishHandle?(true)
|
|
|
-// }
|
|
|
-//
|
|
|
-// /// 添加时间观察者
|
|
|
-// private func addTimeObserver() {
|
|
|
-// let interval = CMTime(seconds: 0.1, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
|
|
|
-// timeObserverToken = player?.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in
|
|
|
-// guard let self = self else { return }
|
|
|
-// let currentTime = CMTimeGetSeconds(time)
|
|
|
-// self.currentTimeChanged?(currentTime,duration)
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// /// 移除时间观察者
|
|
|
-// private func removeTimeObserver() {
|
|
|
-// if let token = timeObserverToken {
|
|
|
-// player?.removeTimeObserver(token)
|
|
|
-// timeObserverToken = nil
|
|
|
-// }
|
|
|
-// }
|
|
|
-//}
|