0

If a person presses the button 10 times, then they will hear 10 different lists of songs being played continuously. I want it to be that if a person presses 10 times, they will only be listening to one list of songs. I'm basically trying to create a reset button.

var myQueuePlayer: AVQueuePlayer?
var avItems: [AVPlayerItem] = []


func audio () {

    var items: [String] = ["one", "two", "three", "four", "five"]
    items.shuffle()

    for clip in items {
        guard let url = Bundle.main.url(forResource: clip, withExtension: ".mp3") else {
            // mp3 file not found in bundle - so crash!
            fatalError("Could not load \(clip).mp3")
        }
        avItems.append(AVPlayerItem(url: url))
        //button.isHidden = true
    }    
}


@IBAction func didTapButton() {

    audio()
    if myQueuePlayer == nil {   
        // instantiate the AVQueuePlayer with all avItems
        myQueuePlayer = AVQueuePlayer(items: avItems)

    } else {
        // stop the player and remove all avItems
        myQueuePlayer?.removeAllItems()
        // add all avItems back to the player

        avItems.forEach {
                myQueuePlayer?.insert($0, after: nil)
            }
    }
    // seek to .zero (in case we added items back in)
    myQueuePlayer?.seek(to: .zero)
    // start playing
    myQueuePlayer?.play()    
}
  • Where’s the part where you stop the player? – matt Jun 17 '20 at 04:04
  • @matt I have tried to stop it everywhere but that doesn't solve the problem. The problem seems to be when I'm adding back avItems back which if a person presses the button 10 times then avItems will be added 10 times. I have spent a day and a half trying to tackle it from different ends but have not been able to solve it. – Yumel Hernandez Jun 17 '20 at 04:11
  • @LeoDabus I have tried declaring it outside but the problem still persists. – Yumel Hernandez Jun 17 '20 at 04:12
  • @YumelHernandez my bad they are not the same – Leo Dabus Jun 17 '20 at 04:13
  • So you are basically saying that `removeAllItems` does nothing? – matt Jun 17 '20 at 04:16
  • @matt I don't think its doing the job unless I'm missing something – Yumel Hernandez Jun 17 '20 at 04:27
  • @YumelHernandez - I gave you that code for your other question (which, apparently, you didn't think answered your question) ... although, it ***does*** in fact, stop and restart the sequence of audio files. What you're now doing in this code is calling `audio()` every time the button is tapped -- `audio()` then shuffles your 5 audio names and ***appends*** them to the array. So, after 10 taps, you have 10 sets of shuffled names, for a total of 50 elements in `avItems`. Do you want the button to be a "Play / Stop / Restart" button? – DonMag Jun 17 '20 at 12:20
  • @DongMag Yes, I want it to be a restart button. I already have figured out how to make it play/pause button but not a restart one. That's essentially what I have ben trying to do, a restart button but the audio files shuffle when you restart. – Yumel Hernandez Jun 17 '20 at 14:50
  • @YumelHernandez - for future reference, try to be a bit more precise when posting a question. Saves the time and trouble of a bunch of back-and-forth comments to figure out what you're really trying to do. – DonMag Jun 17 '20 at 17:56

1 Answers1

0

Here is a very simple example.

This assumes 5 .mp3 files in your bundle, and 4 buttons:

  • Play / Restart
  • Pause
  • Resume
  • Shuffle and Play

connected to the @IBAction funcs:

class TestAVQueuViewController: UIViewController {

    var myQueuePlayer: AVQueuePlayer?

    var avItemsArray: [AVPlayerItem] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        // assuming I have 5 .mp3 files in the bundle, named:
        let mySongs: [String] = [
            "clip1", "clip2", "clip3", "clip4", "clip5",
        ]

        // build the array of URLs for the song files
        for clip in mySongs {
            if let url = Bundle.main.url(forResource: clip, withExtension: ".mp3") {
                avItemsArray.append(AVPlayerItem(url: url))
            } else {
                print("Could not get URL for \(clip).mp3")
            }
        }
        if avItemsArray.count == 0 {
            fatalError("Failed to get URL for ANY songs!")
        }

    }

    func playQueue() -> Void {

        // if first time
        if myQueuePlayer == nil {
            // instantiate the AVQueuePlayer
            myQueuePlayer = AVQueuePlayer()
        }

        guard let player = myQueuePlayer else {
            // I suppose it's possible that AVQueuePlayer() failed to instantiate
            //  so print a message to debug console and return
            print("AVQueuePlayer failed to instantiate!")
            return
        }

        // this will make sure the player is stopped and remove any remaining avItems
        player.removeAllItems()

        // every time this is called,
        //  loop through and reset the time on each avItem
        for item in avItemsArray {
            item.seek(to: .zero, completionHandler: nil)
        }

        // add avItems to the player
        avItemsArray.forEach {
            player.insert($0, after: nil)
        }

        // start playing
        player.play()
    }

    @IBAction func playRestartTapped(_ sender: Any) {
        playQueue()
    }

    @IBAction func pauseTapped(_ sender: Any) {
        guard let player = myQueuePlayer, player.items().count > 0 else {
            return
        }
        player.pause()
    }

    @IBAction func resumeTapped(_ sender: Any) {
        guard let player = myQueuePlayer, player.items().count > 0 else {
            return
        }
        player.play()
    }

    @IBAction func restartShuffledTapped(_ sender: Any) {
        // shuffle the items
        avItemsArray.shuffle()
        playQueue()
    }

}
DonMag
  • 69,424
  • 5
  • 50
  • 86
  • So I added a pause in between each item and want it to play in the background but it doesn't let me. Any turnaround? Any way to actually do this? (I asked a question about this but no one seems to know). After this I'm finally done with this app! – Yumel Hernandez Jul 08 '20 at 23:06
  • I have tested a bunch of things out and the problem seems to be when I call this notification to know if an audio has finished playing: NotificationCenter.default.addObserver(self, selector: #selector(self.playerDidFinishPlaying(sender:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: myQueauPlayer) When this is called, I dispatch the queau for a few seconds and that seems to be the very problem of why the audio can't run in the background – Yumel Hernandez Jul 08 '20 at 23:53