mastodon-ios/Mastodon/Service/AudioPlayer.swift

143 lines
4.4 KiB
Swift
Raw Normal View History

2021-03-08 04:42:10 +01:00
//
// AudioPlayer.swift
// Mastodon
//
// Created by sxiaojian on 2021/3/8.
//
import AVFoundation
import Combine
import CoreDataStack
import Foundation
import UIKit
final class AudioPlayer: NSObject {
var disposeBag = Set<AnyCancellable>()
var player = AVPlayer()
var timeObserver: Any?
var statusObserver: Any?
var attachment: Attachment?
2021-03-08 04:42:10 +01:00
let session = AVAudioSession.sharedInstance()
let playbackState = CurrentValueSubject<PlaybackState, Never>(PlaybackState.unknown)
// MARK: - singleton
2021-03-08 04:42:10 +01:00
public static let shared = AudioPlayer()
let currentTimeSubject = CurrentValueSubject<TimeInterval, Never>(0)
private override init() {
2021-03-08 04:42:10 +01:00
super.init()
addObserver()
}
}
extension AudioPlayer {
func playAudio(audioAttachment: Attachment) {
guard let url = URL(string: audioAttachment.url) else {
return
}
do {
try session.setCategory(.playback)
} catch {
print(error)
return
}
if audioAttachment == attachment {
if self.playbackState.value == .stopped {
self.seekToTime(time: .zero)
}
2021-03-08 04:42:10 +01:00
player.play()
self.playbackState.value = .playing
2021-03-08 04:42:10 +01:00
return
}
2021-03-09 10:13:17 +01:00
player.pause()
2021-03-08 04:42:10 +01:00
let playerItem = AVPlayerItem(url: url)
player.replaceCurrentItem(with: playerItem)
attachment = audioAttachment
player.play()
playbackState.value = .playing
2021-03-08 04:42:10 +01:00
}
func addObserver() {
UIDevice.current.isProximityMonitoringEnabled = true
NotificationCenter.default.publisher(for: UIDevice.proximityStateDidChangeNotification, object: nil)
.sink { [weak self] _ in
guard let self = self else { return }
if UIDevice.current.proximityState == true {
do {
try self.session.setCategory(.playAndRecord)
} catch {
print(error)
return
}
} else {
do {
try self.session.setCategory(.playback)
} catch {
print(error)
return
}
}
}
.store(in: &disposeBag)
2021-03-08 04:42:10 +01:00
timeObserver = player.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: CMTimeScale(NSEC_PER_SEC)), queue: DispatchQueue.main, using: { [weak self] time in
guard let self = self else { return }
self.currentTimeSubject.value = time.seconds
})
player.publisher(for: \.status, options: .new)
.sink(receiveValue: { [weak self] status in
guard let self = self else { return }
2021-03-08 04:42:10 +01:00
switch status {
case .failed:
self.playbackState.value = .failed
case .readyToPlay:
self.playbackState.value = .readyToPlay
case .unknown:
self.playbackState.value = .unknown
@unknown default:
assertionFailure()
2021-03-08 04:42:10 +01:00
}
})
.store(in: &disposeBag)
NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime, object: nil)
.sink { [weak self] _ in
guard let self = self else { return }
self.player.seek(to: .zero)
self.playbackState.value = .stopped
self.currentTimeSubject.value = 0
2021-03-08 04:42:10 +01:00
}
.store(in: &disposeBag)
NotificationCenter.default.publisher(for: AVAudioSession.interruptionNotification, object: nil)
.sink { [weak self] _ in
guard let self = self else { return }
self.pause()
}
.store(in: &disposeBag)
2021-03-08 04:42:10 +01:00
}
func isPlaying() -> Bool {
return self.playbackState.value == .readyToPlay || self.playbackState.value == .playing
}
2021-03-08 04:42:10 +01:00
func resume() {
player.play()
playbackState.value = .playing
2021-03-08 04:42:10 +01:00
}
func pause() {
player.pause()
playbackState.value = .paused
2021-03-08 04:42:10 +01:00
}
func pauseIfNeed() {
if isPlaying() {
pause()
}
}
2021-03-08 04:42:10 +01:00
func seekToTime(time: TimeInterval) {
player.seek(to: CMTimeMake(value:Int64(time), timescale: 1))
}
}