Playing an audio file repeatedly with AVAudioEngine
Asked Answered
P

2

6

I'm working on an iOS app with Swift and Xcode 6. What I would like to do is play an audio file using an AVAudioEngine, and until this point everything OK. But how can I play it without stopping, I mean, that when it ends playing it starts again?

This is my code:

/*==================== CONFIGURATES THE AVAUDIOENGINE ===========*/
    audioEngine.reset() //Resets any previous configuration on AudioEngine

    let audioPlayerNode = AVAudioPlayerNode() //The node that will play the actual sound
    audioEngine.attachNode(audioPlayerNode) //Attachs the node to the audioengine

    audioEngine.connect(audioPlayerNode, to: audioEngine.outputNode, format: nil) //Connects the applause playback node to the sound output
    audioPlayerNode.scheduleFile(applause.applauseFile, atTime: nil, completionHandler: nil)

    audioEngine.startAndReturnError(nil)
    audioPlayerNode.play() //Plays the sound

Before saying me that I should use AVAudioPlayer for this, I can't because later I will have to use some effects and play three audio files at the same time, also repeatedly.

Puente answered 14/2, 2015 at 18:35 Comment(0)
P
10

I found the solution in another question, asked and also auto-answered by @CarveDrone , so I've just copied the code he used:

class aboutViewController: UIViewController {


    var audioEngine: AVAudioEngine = AVAudioEngine()
    var audioFilePlayer: AVAudioPlayerNode = AVAudioPlayerNode()

    override func viewDidLoad() {
        super.viewDidLoad()

        let filePath: String = NSBundle.mainBundle().pathForResource("chimes", ofType: "wav")!
        println("\(filePath)")
        let fileURL: NSURL = NSURL(fileURLWithPath: filePath)!
        let audioFile = AVAudioFile(forReading: fileURL, error: nil)
        let audioFormat = audioFile.processingFormat
        let audioFrameCount = UInt32(audioFile.length)
        let audioFileBuffer = AVAudioPCMBuffer(PCMFormat: audioFormat, frameCapacity: audioFrameCount)
        audioFile.readIntoBuffer(audioFileBuffer, error: nil)

        var mainMixer = audioEngine.mainMixerNode
        audioEngine.attachNode(audioFilePlayer)
        audioEngine.connect(audioFilePlayer, to:mainMixer, format: audioFileBuffer.format)
        audioEngine.startAndReturnError(nil)

        audioFilePlayer.play()
        audioFilePlayer.scheduleBuffer(audioFileBuffer, atTime: nil, options:.Loops, completionHandler: nil)
    }

The only thing you have to change is the filePath constant. Here is the link to the original answer: Having AVAudioEngine repeat a sound

Puente answered 2/3, 2015 at 17:31 Comment(2)
Note that reading a long file into the buffer will take a long time to load. The scheduleFile method streams the file into the player efficiently, beginning immediately. Unfortunately it doesn't loop.Sasha
Mmh, I just translated this code to Swift 5, but I'm getting silence and several kAudioUnitErr_TooManyFramesToProcess in the console...Atony
B
6

Swift 5, thanks at @Guillermo Barreiro

    var audioEngine: AVAudioEngine = AVAudioEngine()
    var audioFilePlayer: AVAudioPlayerNode = AVAudioPlayerNode()

    override func viewDidLoad() {
        super. viewDidLoad()

        guard let filePath: String = Bundle.main.path(forResource: "chimes", ofType: "wav") else{ return }
        print("\(filePath)")
        let fileURL: URL = URL(fileURLWithPath: filePath)
        guard let audioFile = try? AVAudioFile(forReading: fileURL) else{ return }

        let audioFormat = audioFile.processingFormat
        let audioFrameCount = UInt32(audioFile.length)
        guard let audioFileBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: audioFrameCount)  else{ return }
        do{
            try audioFile.read(into: audioFileBuffer)
        } catch{
            print("over")
        }
        let mainMixer = audioEngine.mainMixerNode
        audioEngine.attach(audioFilePlayer)
        audioEngine.connect(audioFilePlayer, to:mainMixer, format: audioFileBuffer.format)
        try? audioEngine.start()
        audioFilePlayer.play()
        audioFilePlayer.scheduleBuffer(audioFileBuffer, at: nil, options:AVAudioPlayerNodeBufferOptions.loops)
    }
Burgos answered 8/10, 2019 at 9:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.