Music_Player3/relax.offline.mp3.music/MP/Common/Tool(工具封装)/MPSideA_MediaCenterManager.swift
2024-07-05 17:25:49 +08:00

708 lines
25 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 MPSideA_PlayerPlayActionType:Int {
///
case Normal = 0
///()
case CountTime = 1
}
///
enum MPSideA_PlayerStateType:Int {
///
case Null = 0
///
case Playing = 1
///
case Pause = 2
}
///
enum MPSideA_TimerType:Int {
///
case UnActivity = 0
///
case Playing = 1
///
case Suspend = 2
}
///
enum MPSideA_CountTimerLevel: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 MPSideA_MediaCenterManager {
///
static let shared = MPSideA_MediaCenterManager()
//MARK: -
//
var player:AVPlayer?
//
var center:MPRemoteCommandCenter?
//GCD
var countTimer: DispatchSourceTimer?
//()
var monitor:AVAudioRecorder?
//
var monitorSetingsDic:[String : Any]?
//GCD
var monitorTimer: DispatchSourceTimer?
//广广
var isAdLate:Bool?{
didSet{
DispatchQueue.main.asyncAfter(deadline: .now()) {
[weak self] in
guard let self = self else {return}
guard let ad = isAdLate, let player = player else {return}
if ad == true {
//广
switch playerState {
case .Null://
//
break
case .Playing://
//
player.pause()
if countTimeType == .Playing {
//
countTimer?.suspend()
}
//
NotificationCenter.notificationKey.post(notificationName: .sideA_pause_music)
case .Pause://
//
break
}
}else {
//广
switch playerState {
case .Null://
//
break
case .Playing://
//
player.play()
if countTimeType == .Playing {
//
countTimer?.resume()
}
//
NotificationCenter.notificationKey.post(notificationName: .sideA_resume_music)
case .Pause://
//
break
}
}
}
}
}
//A
func destroySideA() {
//
NotificationCenter.default.removeObserver(self)
//
player = nil
//
countTimer?.cancel()
countTimer = nil
//
monitor = nil
monitorTimer?.cancel()
monitorTimer = nil
center?.playCommand.removeTarget(self)
center?.pauseCommand.removeTarget(self)
MPNowPlayingInfoCenter.default().nowPlayingInfo = nil
}
//MARK: -
///
var music:MPSideA_MusicModel?
///
func getMusic() -> MPSideA_MusicModel?{
return music
}
///
func setMusic(_ music:MPSideA_MusicModel?) {
self.music = music
}
///
private var playActionType:MPSideA_PlayerPlayActionType = .Normal
///
private var playerState:MPSideA_PlayerStateType = .Null
///
func getPlayerState() -> MPSideA_PlayerStateType {
return playerState
}
///
private var countTimeType:MPSideA_TimerType = .UnActivity
/// OFF
private var countTimerLevel:MPSideA_CountTimerLevel = .OFF
///
func getCountTimerLevel() -> MPSideA_CountTimerLevel {
return countTimerLevel
}
///
private var monitorTimerType:MPSideA_TimerType = .UnActivity
///
private var isMonitorActivity:Bool = false
//
private init() {
//appidentifier
let lastTitle = UserDefaults.standard.string(forKey: "Last") ?? ""
MPSideA_MusicModel.fetch(predicate: .init(format: "identifier==%@", lastTitle)){[weak self] results in
guard let self = self else {return}
let last = results.last
//
// music = last
}
//
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem)
//
NotificationCenter.default.addObserver(self, selector: #selector(handleAudioSessionRouteChangeAction(_ :)), name: AVAudioSession.routeChangeNotification, object: nil)
//
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
}
///
@objc private func handleAudioSessionRouteChangeAction(_ notification: Notification) {
guard let info = notification.userInfo, let reasonValue = info[AVAudioSessionRouteChangeReasonKey] as? UInt, let reason = AVAudioSession.RouteChangeReason(rawValue:reasonValue) else {
return
}
switch reason {
case .newDeviceAvailable://
print("设备已连接")
//
case .oldDeviceUnavailable://
print("设备已断开")
//
playerPause()
default:
break
}
}
//MARK: -
///
/// - Parameters:
/// - music:
/// - actionType: /
/// - countLevel:
func playerStart(_ music:MPSideA_MusicModel, actionType:MPSideA_PlayerPlayActionType, countLevel: MPSideA_CountTimerLevel = .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: .sideA_stop_music)
case .Pause://
//
if player != nil {
player = nil
}
//
NotificationCenter.notificationKey.post(notificationName: .sideA_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:MPSideA_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: .sideA_time_times, object: times)
}else {
//
self?.playerStop()
}
})
//
countTimeType = .Playing
countTimer!.resume()
print("The CountTimer has started.")
//
playMusic(music)
}
//
private func playMusic(_ music:MPSideA_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: .sideA_play_music)
//
music!.lastTime = Date().timeZone()
MPSideA_MusicModel.save()
UserDefaults.standard.set(music!.identifier, forKey: "Last")
}else {
//
print("Couldn't find the file.")
playerState = .Null
music = nil
//
NotificationCenter.notificationKey.post(notificationName: .sideA_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: .sideA_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: .sideA_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: .sideA_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:MPSideA_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: .sideA_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: .sideA_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
}
}