Music_Player3/MusicPlayer/MP/Common/Tool(工具封装)/MPMediaCenterManager.swift
Mr.zhou 96147c5e37 项目:Musicoo
版本:A面 1.0
构建:1.1
更新内容:对项目A面功能的实现,经测试确定各项功能无问题。
更新时间:2024年4月12日 11:20
上传状态:已上传App Connect
2024-04-12 11:19:58 +08:00

621 lines
22 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// MPMediaCenterManager.swift
// MusicPlayer
//
// Created by Mr.Zhou on 2024/4/11.
//
import Foundation
import AVFoundation
import MediaPlayer
///
enum MPPlayerPlayActionType:Int {
///
case Normal = 0
///()
case CountTime = 1
}
///
enum MPPlayerStateType:Int {
///
case Null = 0
///
case Playing = 1
///
case Pause = 2
}
///
enum MPTimerType:Int {
///
case UnActivity = 0
///
case Playing = 1
///
case Suspend = 2
}
///
enum MPCountTimerLevel:Int, CaseIterable {
case OFF = 0
case _10 = 1
case _20 = 2
case _30 = 3
case _60 = 4
case _90 = 5
///
var title:String{
switch self {
case .OFF:
return "OFF"
case ._10:
return "10"
case ._20:
return "20"
case ._30:
return "30"
case ._60:
return "60"
case ._90:
return "90"
}
}
///
var mins:Int{
switch self {
case .OFF:
return 0
case ._10:
return 10
case ._20:
return 20
case ._30:
return 30
case ._60:
return 60
case ._90:
return 90
}
}
}
///
class MPMediaCenterManager {
///
static let shared = MPMediaCenterManager()
//MARK: -
//
private var player:AVPlayer?
//
private var center:MPRemoteCommandCenter?
//GCD
private var countTimer: DispatchSourceTimer?
//()
private var monitor:AVAudioRecorder?
//
private var monitorSetingsDic:[String : Any]?
//GCD
private var monitorTimer: DispatchSourceTimer?
//MARK: -
///
private var music:MusicModel?
///
func getMusic() -> MusicModel?{
return music
}
///
func setMusic(_ music:MusicModel?) {
self.music = music
}
///
private var playActionType:MPPlayerPlayActionType = .Normal
///
private var playerState:MPPlayerStateType = .Null
///
func getPlayerState() -> MPPlayerStateType {
return playerState
}
///
private var countTimeType:MPTimerType = .UnActivity
/// OFF
private var countTimerLevel:MPCountTimerLevel = .OFF
///
func getCountTimerLevel() -> MPCountTimerLevel {
return countTimerLevel
}
///
private var monitorTimerType:MPTimerType = .UnActivity
///
private var isMonitorActivity:Bool = false
//
private init() {
//appidentifier
let lastTitle = UserDefaults.standard.string(forKey: "Last") ?? ""
let last = MusicModel.fetch(.init(format: "identifier==%@", lastTitle)).first
//
music = last
//
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem)
//
monitorSetingsDic =
[
//
AVFormatIDKey:NSNumber(value: kAudioFormatLinearPCM),
//
AVNumberOfChannelsKey: 2,
//
AVEncoderAudioQualityKey : AVAudioQuality.max.rawValue,
//
AVLinearPCMBitDepthKey:NSNumber(value:16),
AVEncoderBitRateKey : 320000,
//
AVSampleRateKey : 44100.0
]
}
//
deinit {
//
NotificationCenter.default.removeObserver(self)
//
player = nil
//
countTimer?.cancel()
countTimer = nil
//
monitor = nil
monitorTimer?.cancel()
monitorTimer = nil
}
//MARK: -
///
/// - Parameters:
/// - music:
/// - actionType: /
/// - countLevel:
func playerStart(_ music:MusicModel, actionType:MPPlayerPlayActionType, countLevel: MPCountTimerLevel = .OFF) {
//
switch countTimeType {
case .UnActivity://
break
case .Playing://
if countTimer != nil {
//rsume
countTimer!.cancel()
countTimer = nil
}
case .Suspend://
if countTimer != nil {
//suspend
countTimer!.resume()
countTimer!.cancel()
countTimer = nil
}
}
//
switch playerState {
case .Null://,
break
case .Playing://
//
if player != nil {
player!.pause()
player = nil
}
//
NotificationCenter.notificationKey.post(notificationName: .stop_music)
case .Pause://
//
if player != nil {
player = nil
}
//
NotificationCenter.notificationKey.post(notificationName: .stop_music)
}
//
playActionType = actionType
//
switch playActionType {
case .Normal://
playMusic(music)
case .CountTime://
//
countTimerLevel = countLevel
countTimerStart(Double(countLevel.mins*60), music: music)
}
}
//
private func countTimerStart(_ totalTimes:TimeInterval, music:MusicModel) {
// //
// switch countTimeType {
// case .UnActivity://
// break
// case .Playing://
// if countTimer != nil {
// //rsume
// countTimer!.cancel()
// countTimer = nil
// }
// case .Suspend://
// if countTimer != nil {
// //suspend
// countTimer!.resume()
// countTimer!.cancel()
// countTimer = nil
// }
// }
//
let queue = DispatchQueue(label: "com.MPCountTimer.queue")
//
countTimer = DispatchSource.makeTimerSource(queue: queue)
//
countTimer!.schedule(deadline: .now(), repeating: .seconds(1))
//times
var times = totalTimes
//
countTimer!.setEventHandler(handler: {
[weak self] in
//
if times > 0 {
//0-1
times -= 1
//
print(setTimesToMinSeconds(times))
//
NotificationCenter.notificationKey.post(notificationName: .time_times, object: times)
}else {
//
self?.playerStop()
}
})
//
countTimeType = .Playing
countTimer!.resume()
print("The CountTimer has started.")
//
playMusic(music)
}
//
private func playMusic(_ music:MusicModel) {
//
self.music = music
player_play(self.music!.isLocal ? musicLocal():musicDocument())
}
//
private func musicLocal() -> URL {
//mp3
let url:URL = .init(fileURLWithPath: Bundle.main.path(forResource: music!.path, ofType: "mp3")!)
return url
}
//
private func musicDocument() -> URL {
var url:URL!
//
let directory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
//
let vedioUrl = URL(fileURLWithPath:URL(fileURLWithPath: directory).appendingPathComponent((music!.path)).path)
//
let authozied = vedioUrl.startAccessingSecurityScopedResource()
if authozied == true {
//访
let fileCoordinator = NSFileCoordinator()
fileCoordinator.coordinate(readingItemAt: vedioUrl, options: .withoutChanges, error: nil) { (newUrl) in
url = newUrl
}
}else {
//
url = vedioUrl
}
//访
vedioUrl.stopAccessingSecurityScopedResource()
return url
}
//
private func player_play(_ url:URL) {
//
if FileManager.default.fileExists(atPath: url.path) == true {
//
//
let playerItem = AVPlayerItem(url: url)
//AVPlayer
player = AVPlayer(playerItem: playerItem)
//
player?.play()
playerState = .Playing
//
setCommandCenter(music!)
//
NotificationCenter.notificationKey.post(notificationName: .play_music)
//
music!.lastTime = Date().timeZone()
MusicModel.save()
UserDefaults.standard.set(music!.identifier, forKey: "Last")
}else {
//
print("Couldn't find the file.")
playerState = .Null
music = nil
//
NotificationCenter.notificationKey.post(notificationName: .null_music)
}
}
///
func playerPause() {
//
guard playerState == .Playing, let player = player else {
//
print("Player is not in playing")
return
}
//,
switch playActionType {
case .Normal://
player_pause(player)
case .CountTime://
//
guard countTimeType == .Playing, let countTimer = countTimer else {
//
print("CountTimer is not playing")
return
}
//,
countTimer.suspend()
countTimeType = .Suspend
//
player_pause(player)
}
}
//
private func player_pause(_ player:AVPlayer) {
//
player.pause()
//
playerState = .Pause
//
NotificationCenter.notificationKey.post(notificationName: .pause_music)
}
///
func playerResume() {
//
guard playerState == .Pause, let player = player else {
//
print("Player is not paused")
return
}
//,
switch playActionType {
case .Normal://
player_resume(player)
case .CountTime://
//
guard countTimeType == .Suspend, let countTimer = countTimer else {
//
print("CountTimer is not paused")
return
}
//
countTimer.resume()
countTimeType = .Playing
//
player_resume(player)
}
}
//
private func player_resume(_ player:AVPlayer) {
//
player.play()
//
playerState = .Playing
//
NotificationCenter.notificationKey.post(notificationName: .resume_music)
}
///
func playerStop() {
//
guard playerState != .Null, let player = player else {
//
print("Player is not started")
return
}
//,
switch playActionType {
case .Normal://
player_stop(player)
case .CountTime://
//
guard countTimeType != .UnActivity, let countTimer = countTimer else {
//
print("CountTimer is not started")
return
}
//
if countTimeType == .Suspend {
countTimer.resume()
}
countTimer.cancel()
self.countTimer = nil
//off
countTimerLevel = .OFF
//
player_stop(player)
}
}
//
private func player_stop(_ player:AVPlayer) {
player.pause()
self.player = nil
playerState = .Null
//
playActionType = .Normal
//
NotificationCenter.notificationKey.post(notificationName: .stop_music)
}
//
@objc private func playerDidFinishPlaying() {
guard playerState == .Playing, let player = player else {
return
}
//
player.seek(to: CMTime.zero)
//
player.play()
}
//MARK: -
private func setCommandCenter(_ music:MusicModel) {
//
center = MPRemoteCommandCenter.shared()
//
//
center!.playCommand.addTarget(handler: { [weak self] (event) in
guard let self = self else { return .noActionableNowPlayingItem}
if self.music != nil {
return .success
}else {
return .noActionableNowPlayingItem
}
})
//
center!.pauseCommand.addTarget(handler: { [weak self] (event) in
guard let self = self else { return .noActionableNowPlayingItem}
if self.music != nil {
return .success
}else {
return .noActionableNowPlayingItem
}
})
//info
var info = [String:Any]()
//
info[MPMediaItemPropertyTitle] = music.title ?? ""
//
// info[MPMediaItemPropertyArtist] = ""
//
// info[MPMediaItemPropertyAlbumTitle] = ""
//
// info[MPMediaItemPropertyPlaybackDuration] = 0
//
if let image = UIImage(data: music.cover) {
info[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size, requestHandler: { size in
return image
})
}
//
MPNowPlayingInfoCenter.default().nowPlayingInfo = info
}
//MARK: -
///
func openMonitor(_ decibels:Double) {
//
if countTimeType != .UnActivity || playerState != .Null {
//
playerStop()
}
//
switch monitorTimerType {
case .UnActivity://
break
case .Playing://
if monitorTimer != nil {
//rsume
monitorTimer!.cancel()
monitorTimer = nil
}
case .Suspend://
if monitorTimer != nil {
//suspend
monitorTimer!.resume()
monitorTimer!.cancel()
monitorTimer = nil
}
}
//
let queue = DispatchQueue(label: "com.MPMonitorTimer.queue")
//
monitorTimer = DispatchSource.makeTimerSource(queue: queue)
//
monitorTimer!.schedule(deadline: .now(), repeating: .seconds(1))
//
monitorTimer!.setEventHandler(handler: {
[weak self] in
guard let self = self else { return }
//
let currentDecibels = checkDecibels()
//
if currentDecibels > decibels {
guard let music = self.music else {
return
}
//
playerStart(music, actionType: .CountTime, countLevel: ._10)
//
stopMonitor()
}
})
//
monitor?.deleteRecording()
monitor = nil
var url:URL?
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/play"
//
if #available(iOS 16.0, *) {
url = URL(filePath: path)
} else {
url = URL(fileURLWithPath: path)
}
//
do {
//
monitor = try AVAudioRecorder(url: url!, settings: monitorSetingsDic!)
//
monitor!.isMeteringEnabled = true
//
monitor!.prepareToRecord()
//
monitor!.record()
//
monitorTimerType = .Playing
isMonitorActivity = true
monitorTimer!.resume()
//
NotificationCenter.notificationKey.post(notificationName: .open_monitor)
print("The monitor has open.")
} catch let error {
print("Monitor initialization failure:\(error.localizedDescription)")
}
}
///
func stopMonitor() {
guard let timer = monitorTimer, let monitor = monitor else { return }
//
if monitorTimerType == .Suspend {
timer.resume()
}
timer.cancel()
self.monitorTimer = nil
monitorTimerType = .UnActivity
isMonitorActivity = false
monitor.stop()
self.monitor = nil
//
NotificationCenter.notificationKey.post(notificationName: .stop_monitor)
print("The monitor has stoped")
}
//
private func checkDecibels() -> Double{
//
monitor!.updateMeters()
let power = monitor!.peakPower(forChannel: 0)
// 0-1 1
let decibels:Double = pow(Double(10), Double(0.05*power))
print("Current decibels: \(decibels)")
return decibels
}
}