Make a playlist (start next song) in swift - ios

I have created a sound player in swift with AVFoundation. I am trying to start the next song in array when the playing song is finished. I was trying to implement this code
if (audioPlayer.currentTime >= audioPlayer.duration){
var recentSong = songPlaylist[selectedSongNumber + 1]
audioPlayer = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath:
NSBundle.mainBundle().pathForResource(recentSong, ofType: "mp3")!), error: nil)
audioPlayer.play()
}
but I am not being able to implement this code (I do not know where to implement it).Here is my complete code
import UIKit
import AVFoundation
import AVKit
public var audioPlayer = AVPlayer()
public var selectedSongNumber = Int()
public var songPlaylist:[String] = ["song1", "song2"]
public var recentSong = "song1"
let playImage = UIImage(named: "Play.png") as UIImage!
let pauseImage = UIImage(named: "Pause.png") as UIImage!
class FirstViewController: UIViewController {
#IBOutlet weak var musicSlider: UISlider!
#IBOutlet weak var PlayPause: UIButton!
var audioPlayer = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath:
NSBundle.mainBundle().pathForResource(recentSong, ofType: "mp3")!), error: nil)
override func viewDidLoad() {
super.viewDidLoad()
musicSlider.maximumValue = Float(audioPlayer.duration)
var timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("updateMusicSlider"), userInfo: nil, repeats: true)
if (audioPlayer.currentTime >= audioPlayer.duration){
var recentSong = songPlaylist[selectedSongNumber + 1]
audioPlayer = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath:
NSBundle.mainBundle().pathForResource(recentSong, ofType: "mp3")!), error: nil)
audioPlayer.play()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func PlayPauseButton(sender: AnyObject) {
if (audioPlayer.playing == false){
audioPlayer.play()
PlayPause.setImage(pauseImage, forState: .Normal)
}else{
audioPlayer.pause()
PlayPause.setImage(playImage, forState: .Normal)
}
}
#IBAction func StopButton(sender: AnyObject) {
audioPlayer.stop()
audioPlayer.currentTime = 0
PlayPause.setImage(playImage, forState: .Normal)
}
#IBAction func musicSliderAction(sender: UISlider) {
audioPlayer.stop()
audioPlayer.currentTime = NSTimeInterval(musicSlider.value)
audioPlayer.play()
}
func updateMusicSlider(){
musicSlider.value = Float(audioPlayer.currentTime)
}
}

I am updating my code with something different:
import UIKit
import AVFoundation
class ViewController: UIViewController, AVAudioPlayerDelegate {
var counter = 0
var song = ["1","2","3"]
var player = AVAudioPlayer()
#IBOutlet weak var musicSlider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
musicSlider.value = 0.0
}
func updateMusicSlider(){
musicSlider.value = Float(player.currentTime)
}
#IBAction func playSong(sender: AnyObject) {
music()
}
#IBAction func sliderAction(sender: AnyObject) {
player.stop()
player.currentTime = NSTimeInterval(musicSlider.value)
player.play()
}
func music(){
var audioPath = NSBundle.mainBundle().pathForResource("\(song[counter])", ofType: "mp3")!
var error : NSError? = nil
player = AVAudioPlayer(contentsOfURL: NSURL(string: audioPath), error: &error)
musicSlider.maximumValue = Float(player.duration)
var timer = NSTimer.scheduledTimerWithTimeInterval(0.05, target: self, selector: Selector("updateMusicSlider"), userInfo: nil, repeats: true)
player.delegate = self
if error == nil {
player.delegate = self
player.prepareToPlay()
player.play()
}
}
func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool)
{
println("Called")
if flag {
counter++
}
if ((counter + 1) == song.count) {
counter = 0
}
music()
}
}
You can do it this way.
Hope It will help and HERE is sample project for more Info.

You need to implement AVAudioPlayerDelegate Protocol's method:
optional func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer!, successfully flag: Bool)
Documentation link
Play your next music item here.
But I will not recommend, since AVAudioPlayer can only play one item at a time. You need to instantiate again with another music item after completion. I will suggest you to use AVQueuePlayer. Detaled answer has been given here. Hope it helps!

I created a sound player in swift with AVFoundation. I used progressView to time the song and then when it hits 0.98743 it will update to the next song automatically this is the Github link: https://github.com/ryan-wlr/MusicPlayerIOS
func updateProgressView() {
if (progressView.progress > a.advanced(by: 0.98743)) {
audioPlayerDidFinishPlayeing()
}
if audioPlayer.isPlaying {
let progress = Float(audioPlayer.currentTime/audioPlayer.duration)
progressView.setProgress(progress, animated: true)
}
}

Related

swift 3: How to add UISlider?

I have ViewController with AVAudioPlayer. Now I have this code
import UIKit
import AVFoundation
var audioPlayer = AVAudioPlayer()
class ViewController: UIViewController {
#IBOutlet weak var slider: UISlider!
#IBOutlet weak var playButton: UIButton!
var index = 0
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
if index == 0{
let url = Bundle.main.url(forResource: "1", withExtension: "mp3")!
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.prepareToPlay()
//audioPlayer.play()
//play(sender:AnyObject.self as AnyObject)
} catch let error {
print(error.localizedDescription)
}
}
}
#IBAction func slide(_ slider: UISlider) {
audioPlayer.currentTime = TimeInterval(slider.value)
}
#IBAction func play(sender: AnyObject) {
if !audioPlayer.isPlaying{
audioPlayer.play()
// **** The line below is the new line ****
timer = Timer(timeInterval: 1.0, target: self, selector: #selector(self.updateSlider), userInfo: nil, repeats: true)
RunLoop.main.add(timer!, forMode: .commonModes)
} else {
audioPlayer.pause()
playButton.setImage(UIImage(named: "pause.png"), for: UIControlState.normal)
timer?.invalidate()
}
}
func updateSlider(_ timer: Timer) {
slider.value = Float(audioPlayer.currentTime)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I want to add UISlider that change scrub through the audio file. How to do it?
First, you have to add a new IBAction from the slider to make sure your class gets notified when the sliders value changes (Look for "Responding to User Interaction" here: https://developer.apple.com/reference/uikit/uislider#2557863)
In this event handler you have to set the audios current time
#IBAction func slide(_ slider: UISlider) {
audioPlayer.currentTime = TimeInterval(slider.value)
}
To make sure the slider updates its value according to the current time, you have to add a timer when you start the player that calls a custom method which updates the sliders value.
#IBAction func play(sender: AnyObject) {
if !audioPlayer.isPlaying{
audioPlayer.play()
// **** The line below is the new line ****
timer = Timer(timeInterval: 1.0, target: self, selector: #selector(self.updateSlider), userInfo: nil, repeats: true)
RunLoop.main.add(timer, forMode: .commonModes)
} else {
audioPlayer.pause()
playButton.setImage(UIImage(named: "pause.png"), for: UIControlState.normal)
timer.invalidate()
}
}
Afterwards you have to create the method which updates the sliders value
func updateSlider(_ timer: Timer) {
slider.value = Float(audioPlayer.currentTime)
}
Then you have to set the sliders maximum value
override func viewDidLoad() {
super.viewDidLoad()
if index == 0{
let url = Bundle.main.url(forResource: "1", withExtension: "mp3")!
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.prepareToPlay()
// **** These two lines below are new ****
slider.maximumValue = Float(audioPlayer.duration)
slider.value = 0.0
} catch let error {
print(error.localizedDescription)
}
}
}
And finally you have to create a property below your IBOutlets which holds the timer
var timer: Timer?

iOS Swift AVAudioPlayer how to reset currentTime to zero when finished playing

My code is supposed to play a C Major scale wave file on a button press. Hitting a separate stop button will stop playback and reset the currentTime to zero.
#IBAction func onPlayButtonClick(sender: UIButton)
{
play.enabled = false
let path = NSBundle.mainBundle().pathForResource("cmajor", ofType: "wav")!
let url = NSURL(fileURLWithPath: path)
do
{
scalePlayer = try AVAudioPlayer(contentsOfURL: url)
scalePlayer.prepareToPlay()
scalePlayer.enableRate = true
scalePlayer.rate = 0.75
scalePlayer.play()
}
catch
{
}
}
It works as intended, but how do I set the currentTime to zero when the file is finished playing? I couldn't find anything on the developer docs.
You have to set a delegate:
scalePlayer.delegate = self
Then you implement this callback
func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) {
//set the current time here
scalePlayer.currentTime = 0
}
Update
Here is an example how you can implement this (based on your code):
class MyViewController: UIViewController, AVAudioPlayerDelegate {
#IBOutlet weak var play: UIButton!
private var scalePlayer: AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
let path = NSBundle.mainBundle().pathForResource("cmajor", ofType: "wav")!
let url = NSURL(fileURLWithPath: path)
scalePlayer = try? AVAudioPlayer(contentsOfURL: url)
scalePlayer?.delegate = self
}
#IBAction func onPlayButtonClick(sender: UIButton) {
play.enabled = false
scalePlayer?.prepareToPlay()
scalePlayer?.enableRate = true
scalePlayer?.rate = 0.75
scalePlayer?.play()
}
func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) {
scalePlayer?.currentTime = 0
}
}

Impossible to stop AVPlayer

I am currently testing the use of AVPlayer with audio streaming url, using Swift. There are play() and pause() methods, but the problem is that, pausing only, the stream remains cached in the device.
Here is my test code :
import UIKit
import AVFoundation
class ViewController: UIViewController {
let player = AVPlayer(URL: NSURL(string: "http://streaming.radio.rtl.fr/rtl-1-48-192")!)
#IBOutlet weak var btnPlay: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnPress(sender: AnyObject) {
if (btnPlay.titleLabel?.text == "Play") {
initPlayer()
btnPlay.setTitle("Stop", forState: UIControlState.Normal)
} else {
stopPlayer()
btnPlay.setTitle("Play", forState: UIControlState.Normal)
}
}
func initPlayer() {
player.play()
}
func stopPlayer() {
// player.currentItem = nil // Last thing I tried, but generate an error
player.pause()
}
}
Here are the issues when trying somethings :
player = nil : "Cannot assign a value of type 'NilLiteralCOnvertible' to a value of type 'AVPlayer'"
player.currentItem = nil : "Cannot assign to property: 'currentItem' is a get-only property"
I tried everything, even through AVQueuePlayer without any effective result. (obviously, since I only have one item in my case).
How to stop AVPlayer or destroy his instance ?
From this post I found the best solution to completely stop AVPlayer before you leave or start a new player:
videoPlayer.replaceCurrentItemWithPlayerItem(nil)
[Update] For SWIFT 3:
player.replaceCurrentItem(with: nil)
If you declare player as an optional variable, you can then set the player to nil to deallocate it.
Silly example but it shows what happens:
import UIKit
import AVFoundation
class ViewController: UIViewController {
#IBOutlet weak var btnPlay: UIButton!
var player:AVPlayer?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btnPress(sender: AnyObject) {
if (btnPlay.titleLabel?.text == "Play") {
initPlayer()
btnPlay.setTitle("Stop", forState: UIControlState.Normal)
} else {
stopPlayer()
btnPlay.setTitle("Play", forState: UIControlState.Normal)
}
}
func initPlayer() {
if let play = player {
print("playing")
play.play()
} else {
print("player allocated")
player = AVPlayer(URL: NSURL(string: "http://streaming.radio.rtl.fr/rtl-1-48-192")!)
print("playing")
player!.play()
}
}
func stopPlayer() {
if let play = player {
print("stopped")
play.pause()
player = nil
print("player deallocated")
} else {
print("player was already deallocated")
}
}
}
SWIFT 3 Version:
player.replaceCurrentItem(with: nil)

(iOS) Action when music file stops

When I open a view the music starts playing using AVFoundation and then when it finishes, how do I create an action when it stops? So when the music file finishes, I want the "Pause" button turn into "Play". I know already how to change the text programmatically by using btnPausePlay.setTitle("Pause", forState: UIControlState.Normal)but how do I create the function when the music stops playing?
Note: I'm using swift
You can use delegate functions for that and here is your complete code:
import UIKit
import AVFoundation
class ViewController: UIViewController, AVAudioPlayerDelegate {
var player = AVAudioPlayer()
#IBOutlet weak var musicSlider: UISlider!
#IBOutlet weak var btnPausePlay: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
musicSlider.value = 0.0
music()
}
func updateMusicSlider(){
musicSlider.value = Float(player.currentTime)
}
#IBAction func sliderAction(sender: AnyObject) {
player.stop()
player.currentTime = NSTimeInterval(musicSlider.value)
player.play()
}
func music(){
var audioPath = NSBundle.mainBundle().pathForResource("1", ofType: "mp3")!
var error : NSError? = nil
player = AVAudioPlayer(contentsOfURL: NSURL(string: audioPath), error: &error)
musicSlider.maximumValue = Float(player.duration)
var timer = NSTimer.scheduledTimerWithTimeInterval(0.05, target: self, selector: Selector("updateMusicSlider"), userInfo: nil, repeats: true)
player.delegate = self
if error == nil {
player.delegate = self
player.prepareToPlay()
player.play()
}
}
//This delegate method will call when your player finish playing.
func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool)
{
btnPausePlay.setTitle("Play", forState: .Normal)
}
}
And HERE is your sample project for more Info.

How to play audio in background with Swift?

As you see I'm streaming an audio broadcast. But when I press the home button and exit the app streaming stops or I cannot hear. How can I continue streaming in background and listen it from lock screen?
ViewController.Swift
import UIKit
import AVFoundation
import MediaPlayer
import GoogleMobileAds
class ViewController: UIViewController, GADInterstitialDelegate {
#IBOutlet weak var exitMapButton: UIButton!
#IBOutlet weak var radarMap: UIWebView!
var interstitial: GADInterstitial!
func createAndLoadInterstitial() -> GADInterstitial {
var interstitial = GADInterstitial(adUnitID: "adUnitID-XXXX")
interstitial.delegate = self
interstitial.loadRequest(GADRequest())
return interstitial
}
func getAd(){
if (self.interstitial.isReady)
{
self.interstitial.presentFromRootViewController(self)
self.interstitial = self.createAndLoadInterstitial()
}
}
#IBOutlet weak var ataturkButton: UIButton!
#IBOutlet weak var sabihaButton: UIButton!
#IBOutlet weak var esenbogaButton: UIButton!
#IBOutlet weak var weatherButton: UIButton!
#IBOutlet weak var statusLabel: UILabel!
#IBOutlet weak var playButton: UIButton!
#IBOutlet weak var webViewButton: UIButton!
var googleBannerView: GADBannerView!
override func viewDidLoad() {
super.viewDidLoad()
}
class PlayerAv {
var audioLink: String?
var player: AVPlayer
init(link: String) {
self.audioLink = link
self.player = AVPlayer(URL: NSURL(string: link))
}
}
var myPlayer = PlayerAv(link: "http://somewebsite.com/abc.pls")
var setTowerState = ""
#IBAction func sliderValueChanged(sender: UISlider) {
var currentValue = Float(sender.value)
println(currentValue)
myPlayer.player.volume = currentValue
}
#IBAction func getWeatherWindow(sender: AnyObject) {
UIApplication.sharedApplication().openURL(NSURL(string: "http://somewebpage.com")!)
println("Directed to weather page")
}
#IBAction func changeToAtaturk() {
myPlayer.player.pause()
myPlayer = PlayerAv(link: "http://somewebsite.com/abc.pls")
myPlayer.audioLink == ""
println("\(myPlayer.audioLink!)--a")
playButton.setTitle("Pause", forState: UIControlState.Normal)
myPlayer.player.play()
setTowerState = "ataturk"
statusLabel.text = "Status: Playing, LTBA"
}
#IBAction func changeToEsenboga() {
myPlayer.player.pause()
myPlayer = PlayerAv(link: "http://somewebsite.com/def.pls")
println("\(myPlayer.audioLink!)--a")
playButton.setTitle("Pause", forState: UIControlState.Normal)
myPlayer.player.play()
setTowerState = "esenboga"
statusLabel.text = "Status: Playing, LTAC"
}
#IBAction func changeToSabiha() {
myPlayer.player.pause()
myPlayer = PlayerAv(link: "http://somewebsite.com/efg.pls")
println("\(myPlayer.audioLink!)--a")
playButton.setTitle("Pause", forState: UIControlState.Normal)
myPlayer.player.play()
setTowerState = "sabiha"
statusLabel.text = "Status: Playing, LTFJ"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func playButtonPressed(sender: AnyObject) {
toggle()
}
func toggle() {
if playButton.titleLabel?.text == "Play" {
playRadio()
println("Playing")
statusLabel.text = "Status: Playing"
} else {
pauseRadio()
println("Paused")
statusLabel.text = "Status: Paused"
}
}
func playRadio() {
myPlayer.player.play()
playButton.setTitle("Pause", forState: UIControlState.Normal)
}
func pauseRadio() {
myPlayer.player.pause()
playButton.setTitle("Play", forState: UIControlState.Normal)
}
}
You need to set your app Capabilities Background Modes (Audio and AirPlay) and set your AVAudioSession category to AVAudioSessionCategoryPlayback and set it active
From Xcode 11.4 • Swift 5.2
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers, .allowAirPlay])
print("Playback OK")
try AVAudioSession.sharedInstance().setActive(true)
print("Session is Active")
} catch {
print(error)
}
Xcode 10.2.1 Swift 4
Please add the following code in your AppDelegate
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, mode: AVAudioSessionModeDefault, options: [.mixWithOthers, .allowAirPlay])
print("Playback OK")
try AVAudioSession.sharedInstance().setActive(true)
print("Session is Active")
} catch {
print(error)
}
return true
}
Note: - Please configure options as required. E.g to stop a background audio while a video file being played add
options: [.allowAirPlay, .defaultToSpeaker]
And don't forget to enable audio and airplay in Background mode
Only paste on the viewDidload
let path = Bundle.main.path(forResource:"Bismallah", ofType: "mp3")
do{
try playerr = AVAudioPlayer(contentsOf: URL(fileURLWithPath: path!))
} catch {
print("File is not Loaded")
}
let session = AVAudioSession.sharedInstance()
do{
try session.setCategory(AVAudioSessionCategoryPlayback)
}
catch{
}
player.play()
Swift 5 Xcode 11.2.1
Add this code where you have initialized the AudioPlayer.
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
let audioSession = AVAudioSession.sharedInstance()
do{
try audioSession.setCategory(AVAudioSession.Category.playback)
}
catch{
fatalError("playback failed")
}

Resources