386 lines
14 KiB
Swift
386 lines
14 KiB
Swift
//
|
|
// ViewController.swift
|
|
// SwiftAudioPlayer
|
|
//
|
|
// Created by tanhakabir on 01/28/2019.
|
|
// Copyright (c) 2019 tanhakabir. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import SwiftAudioPlayer
|
|
import AVFoundation
|
|
|
|
class ViewController: UIViewController {
|
|
var selectedAudio: AudioInfo = AudioInfo(index: 0)
|
|
|
|
var freq:[Int] = [0,0,0,0,0,0,0,0,0,0]
|
|
@IBOutlet weak var currentUrlLocationLabel: UILabel!
|
|
@IBOutlet weak var bufferProgress: UIProgressView!
|
|
@IBOutlet weak var scrubberSlider: UISlider!
|
|
|
|
@IBOutlet weak var playPauseButton: UIButton!
|
|
@IBOutlet weak var skipBackwardButton: UIButton!
|
|
@IBOutlet weak var skipForwardButton: UIButton!
|
|
|
|
@IBOutlet weak var audioSelector: UISegmentedControl!
|
|
@IBOutlet weak var streamButton: UIButton!
|
|
@IBOutlet weak var downloadButton: UIButton!
|
|
@IBOutlet weak var rateSlider: UISlider!
|
|
|
|
@IBOutlet weak var rateLabel: UILabel!
|
|
|
|
@IBOutlet weak var reverbLabel: UILabel!
|
|
@IBOutlet weak var reverbSlider: UISlider!
|
|
@IBOutlet weak var durationLabel: UILabel!
|
|
@IBOutlet weak var currentTimestampLabel: UILabel!
|
|
|
|
var isDownloading: Bool = false
|
|
var isStreaming: Bool = false
|
|
var beingSeeked: Bool = false
|
|
var loopEnabled = false
|
|
|
|
|
|
var downloadId: UInt?
|
|
var durationId: UInt?
|
|
var bufferId: UInt?
|
|
var playingStatusId: UInt?
|
|
var queueId: UInt?
|
|
var elapsedId: UInt?
|
|
|
|
var duration: Double = 0.0
|
|
var playbackStatus: SAPlayingStatus = .paused
|
|
|
|
var lastPlayedAudioIndex: Int?
|
|
|
|
var isPlayable: Bool = false {
|
|
didSet {
|
|
if isPlayable {
|
|
playPauseButton.isEnabled = true
|
|
skipBackwardButton.isEnabled = true
|
|
skipForwardButton.isEnabled = true
|
|
} else {
|
|
playPauseButton.isEnabled = false
|
|
skipBackwardButton.isEnabled = false
|
|
skipForwardButton.isEnabled = false
|
|
}
|
|
}
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
SAPlayer.Downloader.allowUsingCellularData = true
|
|
SAPlayer.shared.HTTPHeaderFields = ["User-Agent": "foobar"]
|
|
|
|
// SAPlayer.shared.DEBUG_MODE = true
|
|
|
|
isPlayable = false
|
|
checkIfAudioDownloaded()
|
|
selectAudio(atIndex: 0)
|
|
|
|
// addRandomModifiers()
|
|
|
|
subscribeToChanges()
|
|
}
|
|
|
|
func addRandomModifiers() {
|
|
let node = AVAudioUnitReverb()
|
|
SAPlayer.shared.audioModifiers.append(node)
|
|
node.wetDryMix = 300
|
|
let frequency:[Int] = [60,170,310,600,1000,3000,6000,12000,14000,16000]
|
|
let node2 = AVAudioUnitEQ(numberOfBands:frequency.count)
|
|
node2.globalGain = 1
|
|
for i in 0...(node2.bands.count-1) {
|
|
node2.bands[i].frequency = Float(frequency[i])
|
|
node2.bands[i].gain = 0
|
|
node2.bands[i].bypass = false
|
|
node2.bands[i].filterType = .parametric
|
|
}
|
|
SAPlayer.shared.audioModifiers.append(node2)
|
|
}
|
|
|
|
override func didReceiveMemoryWarning() {
|
|
super.didReceiveMemoryWarning()
|
|
// Dispose of any resources that can be recreated.
|
|
}
|
|
|
|
@IBAction func audioSelected(_ sender: Any) {
|
|
let selected = audioSelector.selectedSegmentIndex
|
|
|
|
selectAudio(atIndex: selected)
|
|
}
|
|
|
|
func selectAudio(atIndex i: Int) {
|
|
selectedAudio.setIndex(i)
|
|
|
|
if selectedAudio.savedUrl != nil {
|
|
downloadButton.isEnabled = true
|
|
downloadButton.setTitle("Delete downloaded", for: .normal)
|
|
streamButton.isEnabled = false
|
|
} else {
|
|
downloadButton.isEnabled = true
|
|
downloadButton.setTitle("Download", for: .normal)
|
|
streamButton.isEnabled = true
|
|
}
|
|
}
|
|
|
|
func checkIfAudioDownloaded() {
|
|
for i in 0...2 {
|
|
if let savedUrl = SAPlayer.Downloader.getSavedUrl(forRemoteUrl: selectedAudio.getUrl(atIndex: i)) {
|
|
selectedAudio.addSavedUrl(savedUrl, atIndex: i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func subscribeToChanges() {
|
|
durationId = SAPlayer.Updates.Duration.subscribe { [weak self] (duration) in
|
|
guard let self = self else { return }
|
|
self.durationLabel.text = SAPlayer.prettifyTimestamp(duration)
|
|
self.duration = duration
|
|
}
|
|
|
|
elapsedId = SAPlayer.Updates.ElapsedTime.subscribe { [weak self] (position) in
|
|
guard let self = self else { return }
|
|
|
|
self.currentTimestampLabel.text = SAPlayer.prettifyTimestamp(position)
|
|
|
|
guard self.duration != 0 else { return }
|
|
|
|
self.scrubberSlider.value = Float(position/self.duration)
|
|
}
|
|
|
|
downloadId = SAPlayer.Updates.AudioDownloading.subscribe { [weak self] (url, progress) in
|
|
guard let self = self else { return }
|
|
guard url == self.selectedAudio.url else { return }
|
|
|
|
if self.isDownloading {
|
|
DispatchQueue.main.async {
|
|
UIView.performWithoutAnimation {
|
|
self.downloadButton.setTitle("Cancel \(String(format: "%.2f", (progress * 100)))%", for: .normal)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bufferId = SAPlayer.Updates.StreamingBuffer.subscribe{ [weak self] (buffer) in
|
|
guard let self = self else { return }
|
|
|
|
self.bufferProgress.progress = Float(buffer.bufferingProgress)
|
|
|
|
if buffer.bufferingProgress >= 0.99 {
|
|
self.streamButton.isEnabled = false
|
|
} else {
|
|
self.streamButton.isEnabled = true
|
|
}
|
|
|
|
self.isPlayable = buffer.isReadyForPlaying
|
|
}
|
|
|
|
playingStatusId = SAPlayer.Updates.PlayingStatus.subscribe { [weak self] (playing) in
|
|
guard let self = self else { return }
|
|
|
|
self.playbackStatus = playing
|
|
|
|
switch playing {
|
|
case .playing:
|
|
self.isPlayable = true
|
|
self.playPauseButton.setTitle("Pause", for: .normal)
|
|
return
|
|
case .paused:
|
|
self.isPlayable = true
|
|
self.playPauseButton.setTitle("Play", for: .normal)
|
|
return
|
|
case .buffering:
|
|
self.isPlayable = false
|
|
self.playPauseButton.setTitle("Loading", for: .normal)
|
|
return
|
|
case .ended:
|
|
if !self.loopEnabled {
|
|
self.isPlayable = false
|
|
self.playPauseButton.setTitle("Done", for: .normal)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
queueId = SAPlayer.Updates.AudioQueue.subscribe { [weak self] forthcomingPlaybackUrl in
|
|
guard let self = self else { return }
|
|
/// we update the selected audio. this is a little contrived, but allows us to update outlets
|
|
if let indexFound = self.selectedAudio.getIndex(forURL: forthcomingPlaybackUrl) {
|
|
self.selectAudio(atIndex: indexFound)
|
|
}
|
|
|
|
self.currentUrlLocationLabel.text = "\(forthcomingPlaybackUrl.absoluteString)"
|
|
}
|
|
}
|
|
|
|
func unsubscribeFromChanges() {
|
|
guard let durationId = self.durationId,
|
|
let elapsedId = self.elapsedId,
|
|
let downloadId = self.downloadId,
|
|
let queueId = self.queueId,
|
|
let bufferId = self.bufferId,
|
|
let playingStatusId = self.playingStatusId else { return }
|
|
|
|
SAPlayer.Updates.Duration.unsubscribe(durationId)
|
|
SAPlayer.Updates.ElapsedTime.unsubscribe(elapsedId)
|
|
SAPlayer.Updates.AudioDownloading.unsubscribe(downloadId)
|
|
SAPlayer.Updates.AudioQueue.unsubscribe(queueId)
|
|
SAPlayer.Updates.StreamingBuffer.unsubscribe(bufferId)
|
|
SAPlayer.Updates.PlayingStatus.unsubscribe(playingStatusId)
|
|
}
|
|
|
|
|
|
@IBAction func scrubberStartedSeeking(_ sender: UISlider) {
|
|
beingSeeked = true
|
|
}
|
|
|
|
@IBAction func scrubberSeeked(_ sender: Any) {
|
|
let value = Double(scrubberSlider.value) * duration
|
|
SAPlayer.shared.seekTo(seconds: value)
|
|
beingSeeked = false
|
|
}
|
|
|
|
|
|
@IBAction func rateChanged(_ sender: Any) {
|
|
let speed = rateSlider.value
|
|
rateLabel.text = "rate: \(speed)x"
|
|
|
|
if skipSilencesSwitch.isOn {
|
|
SAPlayer.Features.SkipSilences.setRateSafely(speed) // if using Skip Silences, we need use this version of setting rate to safely change the rate with the feature enabled.
|
|
} else {
|
|
SAPlayer.shared.rate = speed
|
|
}
|
|
}
|
|
@IBAction func reverbChanged(_ sender: Any) {
|
|
let reverb = reverbSlider.value
|
|
reverbLabel.text = "reverb: \(reverb)"
|
|
if let node = SAPlayer.shared.audioModifiers[1] as? AVAudioUnitReverb {
|
|
node.wetDryMix = reverb
|
|
}
|
|
}
|
|
@IBAction func queueTouched(_ sender: Any) {
|
|
if let savedUrl = selectedAudio.savedUrl {
|
|
SAPlayer.shared.queueSavedAudio(withSavedUrl: savedUrl)
|
|
} else {
|
|
SAPlayer.shared.queueRemoteAudio(withRemoteUrl: selectedAudio.url)
|
|
}
|
|
|
|
print("queue: \(SAPlayer.shared.audioQueued)")
|
|
}
|
|
|
|
@IBAction func downloadTouched(_ sender: Any) {
|
|
if !isDownloading {
|
|
if let savedUrl = SAPlayer.Downloader.getSavedUrl(forRemoteUrl: selectedAudio.url) {
|
|
SAPlayer.Downloader.deleteDownloaded(withSavedUrl: savedUrl)
|
|
selectedAudio.deleteSavedUrl()
|
|
downloadButton.setTitle("Download", for: .normal)
|
|
streamButton.isEnabled = true
|
|
isDownloading = false
|
|
} else {
|
|
downloadButton.setTitle("Cancel 0%", for: .normal)
|
|
isDownloading = true
|
|
SAPlayer.Downloader.downloadAudio(withRemoteUrl: selectedAudio.url, completion: { [weak self] (url, error) in
|
|
guard let self = self else { return }
|
|
guard error == nil else {
|
|
DispatchQueue.main.async {
|
|
self.currentUrlLocationLabel.text = "ERROR! \(error!.localizedDescription)"
|
|
}
|
|
return
|
|
}
|
|
|
|
DispatchQueue.main.async {
|
|
self.currentUrlLocationLabel.text = "saved to: \(url.lastPathComponent)"
|
|
self.selectedAudio.addSavedUrl(url)
|
|
}
|
|
})
|
|
streamButton.isEnabled = false
|
|
}
|
|
} else {
|
|
SAPlayer.Downloader.cancelDownload(withRemoteUrl: selectedAudio.url)
|
|
downloadButton.setTitle("Download", for: .normal)
|
|
streamButton.isEnabled = true
|
|
isDownloading = false
|
|
}
|
|
}
|
|
|
|
@IBAction func streamTouched(_ sender: Any) {
|
|
if !isStreaming {
|
|
self.currentUrlLocationLabel.text = "remote url: \(selectedAudio.url.absoluteString)"
|
|
if selectedAudio.index == 2 { // radio
|
|
SAPlayer.shared.startRemoteAudio(withRemoteUrl: selectedAudio.url, bitrate: .low, mediaInfo: selectedAudio.lockscreenInfo)
|
|
} else {
|
|
SAPlayer.shared.startRemoteAudio(withRemoteUrl: selectedAudio.url, mediaInfo: selectedAudio.lockscreenInfo)
|
|
}
|
|
|
|
lastPlayedAudioIndex = selectedAudio.index
|
|
streamButton.setTitle("Cancel streaming", for: .normal)
|
|
downloadButton.isEnabled = false
|
|
isStreaming = true
|
|
} else {
|
|
SAPlayer.shared.stopStreamingRemoteAudio()
|
|
streamButton.setTitle("Stream", for: .normal)
|
|
downloadButton.isEnabled = true
|
|
isStreaming = false
|
|
}
|
|
}
|
|
|
|
@IBAction func playPauseTouched(_ sender: Any) {
|
|
SAPlayer.shared.togglePlayAndPause()
|
|
}
|
|
|
|
@IBAction func skipBackwardTouched(_ sender: Any) {
|
|
SAPlayer.shared.skipBackwards()
|
|
}
|
|
|
|
@IBAction func skipForwardTouched(_ sender: Any) {
|
|
SAPlayer.shared.skipForward()
|
|
}
|
|
@IBAction func setEqualizerValue(_ sender: Any) {
|
|
if let slider = sender as? UISlider{
|
|
print("slider of index:", slider.tag, "is changed to", slider.value)
|
|
freq[slider.tag] = Int(slider.value)
|
|
print("current frequency : ",freq)
|
|
if let node = SAPlayer.shared.audioModifiers[2] as? AVAudioUnitEQ{
|
|
for i in 0...(node.bands.count - 1){
|
|
node.bands[i].gain = Float(freq[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
@IBOutlet weak var skipSilencesSwitch: UISwitch!
|
|
|
|
@IBAction func skipSilencesSwitched(_ sender: Any) {
|
|
if skipSilencesSwitch.isOn {
|
|
_ = SAPlayer.Features.SkipSilences.enable()
|
|
} else {
|
|
_ = SAPlayer.Features.SkipSilences.disable()
|
|
}
|
|
}
|
|
@IBOutlet weak var sleepSwitch: UISwitch!
|
|
|
|
@IBAction func sleepSwitched(_ sender: Any) {
|
|
if sleepSwitch.isOn {
|
|
_ = SAPlayer.Features.SleepTimer.enable(afterDelay: 5.0)
|
|
} else {
|
|
_ = SAPlayer.Features.SleepTimer.disable()
|
|
}
|
|
}
|
|
|
|
@IBOutlet weak var loopSwitch: UISwitch!
|
|
|
|
@IBAction func loopSwitched(_ sender: Any) {
|
|
loopEnabled = loopSwitch.isOn
|
|
|
|
if loopSwitch.isOn {
|
|
SAPlayer.Features.Loop.enable()
|
|
} else {
|
|
SAPlayer.Features.Loop.disable()
|
|
}
|
|
|
|
}
|
|
}
|
|
|