jsloop
2/19/2019 - 3:02 PM

Swift Playground shows how to play multiple wav files on top of background audio file, using AVAudioPlayerNode, AVAudioMixerNode, AVAudioEng

Swift Playground shows how to play multiple wav files on top of background audio file, using AVAudioPlayerNode, AVAudioMixerNode, AVAudioEngine

import AVFoundation

import UIKit
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

class AudioPlayer {
    var backgroundAudioFile:AVAudioFile
    var topAudioFiles: [AVAudioFile] = []
    var engine:AVAudioEngine
    var backgroundAudioNode: AVAudioPlayerNode
    var topAudioAudioNodes = [AVAudioPlayerNode]()
    var mixer: AVAudioMixerNode
    var timer: Timer!
    var urls: [URL] = []
    
    init (_ url: URL, urls: [URL] = []) {
        backgroundAudioFile = try! AVAudioFile(forReading: url)
        topAudioFiles = urls.map { try! AVAudioFile(forReading: $0) }
        
        engine = AVAudioEngine()
        mixer = AVAudioMixerNode()
        
        engine.attach(mixer)
        engine.connect(mixer, to: engine.outputNode, format: nil)
        self.urls = urls
        backgroundAudioNode = AVAudioPlayerNode()
        for _ in topAudioFiles {
            topAudioAudioNodes += [AVAudioPlayerNode()]
        }
    }
    
    func start() {
        engine.attach(backgroundAudioNode)
        engine.connect(backgroundAudioNode, to: mixer, format: nil)
        backgroundAudioNode.scheduleFile(backgroundAudioFile, at: nil, completionHandler: nil)
        
        try! engine.start()
        backgroundAudioNode.play()
        
        for node in topAudioAudioNodes {
            engine.attach(node)
            engine.connect(node, to: mixer, format: nil)
        }
        try! engine.start()
        // simulate rescheduling files played on top of background audio
        DispatchQueue.global().async { [unowned self] in
            for i in 0..<1000 {
                sleep(2)
                let index = i % self.topAudioAudioNodes.count
                let node = self.topAudioAudioNodes[index]
                node.scheduleFile(self.topAudioFiles[index], at: nil, completionHandler: nil)
                node.play()
            }
        }
    }
}

let bundle = Bundle.main
let beepLow = bundle.url(forResource: "beeplow", withExtension: "wav")!
let beepMid = bundle.url(forResource: "beepmid", withExtension: "wav")!
let backgroundAudio = bundle.url(forResource: "backgroundAudio", withExtension: "wav")!
let audioPlayer = AudioPlayer(backgroundAudio, urls: [beepLow, beepMid])
audioPlayer.start()