0
votes

I'm working on a very simple OSX application that will allow me to play either a song or a folder of songs. I can choose a song and play it and everything is fine. I can choose a folder and create an array of songs and... play the last one.

 var currentSong: NSURL?
 var album: [NSURL] = [] 
 var audioPlayer = AVAudioPlayer()

My playing function is

func playCurrent(){
    do {
        try audioPlayer = AVAudioPlayer(contentsOfURL: currentSong!)
    } catch {
        print("Ignoring errors for now")
    }
    audioPlayer.play()
 }

This works fine whenever I set currentSong to a NSURL. I choose one song, it plays it.

My album function is as follows:

@IBAction func chooseAlbumAndPlay(sender: AnyObject) {
    album = albumFromFile()
    for song in album {
        currentSong = song
        playCurrent()
    }
}

and here I have the problem. albumFromFile opens an NSOpenPanel, lets me choose a folder, and dumps paths to playable items into an array of NSURLs. This part works, I've verified it, so I really have an array with 12 or 20 or whatever correct, playable URLs. If I just run it as is, only the last song in any album gets played. If I set a breakpoint in the playCurrent() function, I can hear that it will actually play a tiny snippet - less than a note in most cases - of all songs but the last. According to the documentation, play() returns a boolean - and it will happily report that it has finished playing every song in this loop.

My - human - opinion is that a song has only finished playing when I have heard all of it.

If I query the duration of the current AVAudioPlayer, they all report perfectly reasonable-sounding values.

I'm completely stumped here. PlayCurrent seems to completely fail to assert itself as a running function. The expected behaviour is that it will not exit until play() has finished playing; observed behaviour is that it will touch every song for the briefest time, go 'been there' and return to the enclosing loop.

How can I force AVAudioPlayer to play the whole of a file before exiting my playCurrent() function? And where would I have found that information? (The class documentation is unhelpful, the mentioned audio guides do not exist - right now, the Developer Library does not mention any basic audio guides for OSX.

1
Hi Marek,the first link only shows how to play a single sound (I can do that); the second link is manually memory-managed iOS code that does not compile under OSX10.11/Xcode 7.2b; given that it's six years old, I have my doubts about its general viability. It also uses the MediaPlayer frameworks' setQueueWithItemCollection method; this isn't available on OSX.green_knight
What about the delegate that notifies you once the song ended. And also google a bit "AVxxx site:github.com". It might help.Marek H

1 Answers

0
votes

The answer is, once you get around to it, very obvious indeed. Unfortunately, the documentation really is no help at all. I could, in theory, have found the solution by looking at the entry for AVAudioPlayer in the AVFoundationFramework reference, but I didn't. (I found it by wildly casting about and a chance mention).

There appear to be no working OSX examples; Apple's iOS example uses the MediaPlayer framework that is not available on OSX.

The solution is as follows: An AVAudioPlayer plays a single sound. If you want to play more than one sound, you need an AVQueuePlayer which can also play a single sound from a URL or a queue of AVPlayerItems (this class has an init(URL:) method). If anyone has an idea why AVAudioPlayer behaves as it does, I'm still interested to hear it.