185 lines
5.5 KiB
Swift
185 lines
5.5 KiB
Swift
//
|
|
// SAPlayer.swift
|
|
// SwiftAudioPlayer
|
|
//
|
|
// Created by Tanha Kabir on 2019-01-29.
|
|
// Copyright © 2019 Tanha Kabir, Jon Mercer
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
import Foundation
|
|
import AVFoundation
|
|
|
|
public class SAPlayer {
|
|
public static let shared: SAPlayer = SAPlayer()
|
|
private var presenter: SAPlayerPresenter!
|
|
private var player: AudioEngine?
|
|
|
|
public var skipForwardSeconds: Double = 30
|
|
public var skipBackwardSeconds: Double = 15
|
|
|
|
public var rate: Double = 1.0 {
|
|
didSet {
|
|
presenter.handleSetSpeed(withMultiple: rate)
|
|
}
|
|
}
|
|
|
|
public var duration: Double {
|
|
get {
|
|
return presenter.duration ?? 0.0
|
|
}
|
|
}
|
|
|
|
public var prettyDuration: String {
|
|
get {
|
|
return SAPlayer.prettifyTimestamp(duration)
|
|
}
|
|
}
|
|
|
|
public var elapsedTime: Double {
|
|
get {
|
|
return presenter.needle ?? 0
|
|
}
|
|
}
|
|
|
|
public var prettyElapsedTime: String {
|
|
get {
|
|
return SAPlayer.prettifyTimestamp(elapsedTime)
|
|
}
|
|
}
|
|
|
|
public var mediaInfo: SALockScreenInfo? = nil {
|
|
didSet {
|
|
if let info = mediaInfo {
|
|
presenter.handleLockscreenInfo(info: info)
|
|
}
|
|
}
|
|
}
|
|
|
|
private init() {
|
|
presenter = SAPlayerPresenter(delegate: self)
|
|
}
|
|
|
|
public static func prettifyTimestamp(_ timestamp: Double) -> String {
|
|
let hours = Int(timestamp / 60 / 60)
|
|
let minutes = Int((timestamp - Double(hours * 60)) / 60)
|
|
|
|
let secondsLeft = Int(timestamp) - (minutes * 60)
|
|
|
|
return "\(hours):\(String(format: "%02d", minutes)):\(String(format: "%02d", secondsLeft))"
|
|
}
|
|
|
|
func getUrl(forKey key: Key) -> URL? {
|
|
return presenter.getUrl(forKey: key)
|
|
}
|
|
|
|
func addUrlToMapping(url: URL) {
|
|
presenter.addUrlToKeyMap(url)
|
|
}
|
|
}
|
|
|
|
//MARK: - External Player Controls
|
|
extension SAPlayer {
|
|
public func togglePlayAndPause() {
|
|
presenter.handleTogglePlayingAndPausing()
|
|
}
|
|
|
|
public func play() {
|
|
presenter.handlePlay()
|
|
}
|
|
|
|
public func pause() {
|
|
presenter.handlePause()
|
|
}
|
|
|
|
public func skipBackwards() {
|
|
presenter.handleSkipBackward()
|
|
}
|
|
|
|
public func skipForward() {
|
|
presenter.handleSkipForward()
|
|
}
|
|
|
|
public func seekTo(seconds: Double) {
|
|
presenter.handleSeek(toNeedle: seconds)
|
|
}
|
|
|
|
public func initializeSavedAudio(withSavedUrl url: URL, mediaInfo: SALockScreenInfo? = nil) {
|
|
self.mediaInfo = mediaInfo
|
|
presenter.handlePlaySavedAudio(withSavedUrl: url)
|
|
}
|
|
|
|
public func initializeAudio(withRemoteUrl url: URL, mediaInfo: SALockScreenInfo? = nil) {
|
|
self.mediaInfo = mediaInfo
|
|
presenter.handlePlayStreamedAudio(withRemoteUrl: url)
|
|
}
|
|
}
|
|
|
|
|
|
//MARK: - Internal implementation of delegate
|
|
extension SAPlayer: SAPlayerDelegate {
|
|
func startAudioDownloaded(withSavedUrl url: AudioURL) {
|
|
player?.pause()
|
|
player?.invalidate()
|
|
player = AudioDiskEngine(withSavedUrl: url, delegate: presenter)
|
|
}
|
|
|
|
func startAudioStreamed(withRemoteUrl url: AudioURL) {
|
|
player?.pause()
|
|
player?.invalidate()
|
|
player = AudioStreamEngine(withRemoteUrl: url, delegate: presenter)
|
|
}
|
|
|
|
func playEngine() {
|
|
becomeDeviceAudioPlayer()
|
|
player?.play()
|
|
}
|
|
|
|
//Start taking control as the device's player
|
|
private func becomeDeviceAudioPlayer() {
|
|
do {
|
|
if #available(iOS 11.0, *) {
|
|
// try AVAudioSession.sharedInstance().setCategory(.playback, mode: .spokenAudio, policy: .longForm, options: [])
|
|
} else {
|
|
// Fallback on earlier versions
|
|
}
|
|
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, mode: AVAudioSessionModeDefault, options: .allowAirPlay)
|
|
|
|
try AVAudioSession.sharedInstance().setActive(true, with: .notifyOthersOnDeactivation)
|
|
} catch {
|
|
Log.monitor("Problem setting up AVAudioSession to play in:: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
func pauseEngine() {
|
|
player?.pause()
|
|
}
|
|
|
|
func seekEngine(toNeedle needle: Needle) {
|
|
var seekToNeedle = needle < 0 ? 0 : needle
|
|
seekToNeedle = needle > Needle(duration) ? Needle(duration) : needle
|
|
player?.seek(toNeedle: seekToNeedle)
|
|
}
|
|
|
|
func setSpeedEngine(withMultiple multiple: Double) {
|
|
player?.setSpeed(speed: multiple)
|
|
}
|
|
}
|
|
|