Home > Software engineering >  Swift iOS, Play tracks sequentially from array using AVAudioPlayer, with some function in between ea
Swift iOS, Play tracks sequentially from array using AVAudioPlayer, with some function in between ea

Time:01-17

I have an array of audio instructions that I would like to randomize, play (one at a time), and do some data recording in between each instruction.

With the below code, I have a button press that triggers beginTest(), but the instruction tracks play simultaneously. It never gets to startRecord().

I've tried placing startRecord() inside audioPlayerDidFinishPlaying; in that case it does get to startRecord() for the first track, but doesn't continue to the next iteration of the loop inside beginTest() where playScript() is called.

func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool)
{
    self.player = nil
    print("finished playing this file")

}

func playScript(c: String)
{
    let path = Bundle.main.path(forResource: c, ofType:"m4a")!
    let url = URL(fileURLWithPath: path)

    do {
        self.player = try AVAudioPlayer(contentsOf: url)
        self.player?.delegate = self
        self.player?.play()
        print(c)
        print("playing script for "   c)
    } catch {
        print("couldn't load script")
    }
}

func beginTest()
{
    for c in conditions.shuffled()
    {
        playScript(c: c)
        
        if self.player == nil
        {
            startRecord()
            print("started data recording")
        }
    }
    
    exportData()
}

I'm guessing that beginTest() keeps moving along the for loop before audioPlayerDidFinishPlaying resets the player to nil, but I don't know how to stall this.

CodePudding user response:

The approach using audioPlayerDidFinishPlaying(_:,successfully:) is correct. In your code when you call playScript(c:) you will iterate through all audio files from conditions array and call startRecord method really quickly, while first audio is playing. All sounds will be placed in a queue and play one by one. I don't know contents of startRecord method, but I don't think that it was supposed to be called like that.

In order to avoid those problems you need to call playScript(c:) and then wait for method startRecord() to finish, and then call playScript(c:) again. So you can try something like this:

import AVFoundation

class SomeClass: NSObject, AVAudioPlayerDelegate {
    
    var player: AVAudioPlayer?
    var conditions = [String]()
    var instructions: [() -> ()] = []
        
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        print("finished playing \(player.url)")
        startRecord {
            playNextInstruction()
        }
    }

    func playScript(c: String)
    {
        let path = Bundle.main.path(forResource: c, ofType:"m4a")!
        let url = URL(fileURLWithPath: path)

        do {
            self.player = try AVAudioPlayer(contentsOf: url)
            self.player?.delegate = self
            self.player?.play()
            print(c)
            print("playing script for "   c)
        } catch {
            print("couldn't load script")
        }
    }

    func beginTest() {
        instructions = conditions.shuffled().map { [unowned self] condition in
            return {
                self.playScript(c: condition)
            }
        }
        playNextInstruction()
    }
    
    func playNextInstruction() {
        if instructions.count != 0 {
            instructions.remove(at: 0)()
        } else {
            exportData()
        }
    }
    
    func exportData() {
    
    }
    
    func startRecord(completion: (() -> ())) {
        
    }
        
}

If it won't work then I suggest to try searching problem somewhere else in your code.

  •  Tags:  
  • Related