动态重新连接AVAudioEngine节点而不停止声音

时间:2019-09-05 15:13:32

标签: avaudioengine avaudiounit avaudionode

我正在尝试动态修改连接到AVAudioEngine的节点图,但是该图在每次重新连接后都会输出静默。

在下面的示例中,我想将player动态连接到engine.mainMixerNode,但是每当我打电话给toggleBypass时,我都会保持沉默。

是否可以在不暂停播放AVAudioPlayerNode的情况下进行重新布线?

class Sample: UIViewController {

    let engine = AVAudioEngine()
    let player = AVAudioPlayerNode()
    let effectNode = AVAudioUnitDelay()

    @objc func toggleBypass() {
        if effectNode.numberOfInputs == 0 {
            engine.connect(player, to: effectNode, format: file.processingFormat)
            engine.connect(effectNode, to: engine.mainMixerNode, format: file.processingFormat)
        } else {
            engine.connect(player, to: engine.mainMixerNode, format: file.processingFormat)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .red

        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(toggleBypass)))

        try! AVAudioSession.sharedInstance().setCategory(.playback)
        try! AVAudioSession.sharedInstance().setPreferredIOBufferDuration(0.005)
        try! AVAudioSession.sharedInstance().setActive(true, options: [])

        do {

            engine.attach(player)
            engine.attach(effectNode)

            engine.connect(player, to: effectNode, format: file.processingFormat)
            engine.connect(effectNode, to: engine.mainMixerNode, format: file.processingFormat)

            player.scheduleBuffer(buffer, at: nil, options: .loops, completionHandler: nil)

            engine.prepare()

            try engine.start()

            player.play()

        } catch {
            assertionFailure(String(describing: error))
        }
    }

    lazy var file: AVAudioFile = {
        let fileURL = Bundle.main.url(forResource: "beber", withExtension: "mp3")!
        return try! AVAudioFile(forReading: fileURL)
    }()

    lazy var buffer: AVAudioPCMBuffer = {
        let buffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, frameCapacity: UInt32(file.length))!
        try! file.read(into: buffer)
        return buffer
    }()

}

(是的,我知道我可以更新效果的bypass属性,但是我很想知道动态重新连接图形的步骤)

1 个答案:

答案 0 :(得分:0)

经过进一步研究,我意识到(从iOS 13开始)在不暂停AVAudioPlayerNode播放的情况下进行重新布线是不可能的。

使用connect(_:to:fromBus:toBus:format:)方法重新连接播放器节点,该方法首先将节点与先前连接到的节点断开连接,从而停止播放。

但是,您可以使用其他连接方法(connect(_:to:fromBus:format:)  将播放器连接到几种效果以及通过以下方式播放的混音器:

let engine = AVAudioEngine()
let player = AVAudioPlayerNode()
let effectOne = AVAudioUnitTimePitch()
let effectTwo = AVAudioUnitReverb()
let mixerThru = AVAudioMixerNode()
let mixerOne = AVAudioMixerNode()
let mixerTwo = AVAudioMixerNode()

engine.attach(player)
engine.attach(effectOne)
engine.attach(effectTwo)
engine.attach(mixerThru)
engine.attach(mixerOne)
engine.attach(mixerTwo)

player.scheduleBuffer(buffer, at: nil, options: .loops, completionHandler: nil)

let effectThruConnectionPoint = AVAudioConnectionPoint(node: mixerThru, bus: 0)
let effectOneConnectionPoint = AVAudioConnectionPoint(node: effectOne, bus: 0)
let effectTwoConnectionPoint = AVAudioConnectionPoint(node: effectTwo, bus: 0)

engine.connect(player, to: [effectThruConnectionPoint, effectOneConnectionPoint, effectTwoConnectionPoint], fromBus: 0, format: nil)

engine.connect(effectOne, to: mixerOne, format: file.processingFormat)
engine.connect(effectTwo, to: mixerTwo, format: file.processingFormat)
engine.connect(mixerThru, to: engine.mainMixerNode, format: file.processingFormat)
engine.connect(mixerOne, to: engine.mainMixerNode, format: file.processingFormat)
engine.connect(mixerTwo, to: engine.mainMixerNode, format: file.processingFormat)

mixerTwo.outputVolume = 0
mixerThru.outputVolume = 0

(向RH提出建议和代码)