AudioKit iOS:如何将输入节点动态连接到活动链中的混音器?

时间:2018-06-14 17:35:59

标签: ios swift audiokit

如何将输入节点动态连接到AudioKit iOS中活动链中的混音器?

环境:AudioKit 4.3,Swift 4.1,Xcode 9.4.1,iOS 11.4。

问题

我正在使用由一系列AKNode对象组成的动态模块构建应用程序。这些模块根据请求动态连接到正在运行的AudioKit引擎的专用AKMixer节点或从中分离。这种方法很有效,除非在尝试连接包含输入节点(如AKMicrophone或AKStereoInput)的任何模块时导致崩溃:

  

2018-06-14 10:13:33.696384-0700 MyApp [3440:2578936] [mcmx] 338:输入   公交车0采样率为0 2018-06-14 10:13:33.696749-0700   MyApp [3440:2578936] [avae] AVAEInternal.h:103:_AVAE_CheckNoErr:   [AVAudioEngineGraph.mm:3632:UpdateGraphAfterReconfig:   (AUGraphParser :: InitializeActiveNodesInOutputChain(ThisGraph,   kOutputChainFullTraversal,* conn.srcNode,isChainActive)):错误   -10875 2018-06-14 10:13:33.700474-0700 DynamicMic [3440:2578936] ***因未捕获的异常而终止应用   ' com.apple.coreaudio.avfaudio',原因:'错误-10875'

或者,调用AudioKit.stop(),然后执行有问题的连接,然后调用AudioKit.start()无法启动AudioKit,但它可以避免崩溃:

  

AKMicrophone.swift:init():45:混音器输入8 2018-06-14   10:16:09.532277-0700 MyApp [3443:2580588] [mcmx] 338:输入总线0   采样率为0 2018-06-14 10:16:09.532603-0700 MyApp [3443:2580588]   [avae] AVAEInternal.h:103:_AVAE_CheckNoErr:   [AVAudioEngineGraph.mm:1265:Initialize :(错误=   AUGraphParser :: InitializeActiveNodesInOutputChain(ThisGraph,   kOutputChainOptimizedTraversal,* GetOutputNode(),   isOutputChainActive)):错误-10875 2018-06-14 10:16:09.532654-0700   MyApp [3443:2580588] [avae] AVAudioEngine.mm:149:-[AVAudioEngine   准备]:引擎@ 0x1c0008010:无法初始化,错误= -10875   2018-06-14 10:16:09.651495-0700 MyApp [3443:2580588] [mcmx] 338:输入   公交车0采样率为0 2018-06-14 10:16:09.651549-0700   MyApp [3443:2580588] [avae] AVAEInternal.h:103:_AVAE_CheckNoErr:   [AVAudioEngineGraph.mm:1265:Initialize :(错误=   AUGraphParser :: InitializeActiveNodesInOutputChain(ThisGraph,   kOutputChainOptimizedTraversal,* GetOutputNode(),   isOutputChainActive)):错误-10875

唯一有效的方法是以静态方式制作整个音频节点图,包括AKMicrophone节点,设置输出节点,然后仅启动一次AudioKit。但是,这种方法无法满足我的应用程序所需的动态音频节点图的要求。

代码

这是受管理的AudioKit类的精简版本。理想情况下,{I}会在入口点调用AudioEngine.start(),例如AppDelegate didFinishLaunchingWithOptions方法。

import Foundation
import AudioKit

class AudioEngine {

    private static var _mainMixer: AKMixer = AKMixer()

    // Connected main mixer input nodes.
    private static var _mainMixerNodes = [AKNode]()

    private static var _isInited = false
    private static var _isStarted = false

    static func start() {
        if !_isInited {
            // Clean tempFiles !
            AKAudioFile.cleanTempDirectory()

            // Session settings
            AKSettings.bufferLength = .medium

            do {
                try AKSettings.setSession(category: .playAndRecord, with: .allowBluetoothA2DP)
            } catch {
                AKLog("Could not set session category.")
            }

            AKSettings.defaultToSpeaker = true
            _isInited = true
        }
        if !_isStarted {
            AudioKit.output = _mainMixer
            print("AudioEngine start: just set output to global mixer")
            do {
                try AudioKit.start()
                AKLog("AudioEngine: AudioKit started")
                _isStarted = true
            } catch {
                AKLog("AudioEngine: AudioKit could not start")
            }
        }
    }

    static func stop() {
        if _isStarted {
            AudioKit.output = nil
            do {
                try AudioKit.stop()
                AKLog("AudioEngine: AudioKit stopped")
                _isStarted = false
            } catch {
                AKLog("AudioEngine: AudioKit could not stop")
            }
        }
    }

    static func connect(_ node: AKNode) {
        if !_mainMixerNodes.contains(node) {
            _mainMixer.connect(input: node)
            _mainMixerNodes.append(node)
        }
    }

    static func disconnect(_ node: AKNode) {
        if let nodeIndex = _mainMixerNodes.index(of: node) {
            node.detach()
            _mainMixerNodes.remove(at: nodeIndex)
        }
    }

}

稍后在应用流程中,会打开一个自定义视图,该视图通过输入节点(AKMicrophone)使用麦克风。这是问题发生的地方。这是一个稀疏版本:

import UIKit

import AudioKit

class MicViewController: UIViewController {

    let mic = AKMicrophone()

    override func viewDidLoad() {
        super.viewDidLoad()

        // Approach 1: Causes a crash.
        AudioEngine.connect(mic)

        // Approach 2: Stop engine, connect, start engine again. Does not work.
        // AudioEngine.stop()
        // AudioEngine.connect(mic)
        // AudioEngine.start()
    }

}

1 个答案:

答案 0 :(得分:2)

在断开连接时,detach将从基础AVAudioEngine中分离节点。 AKMicrophone的底层节点是AVAudioEngine的属性,所以最好断开它。

let disconnect = node is AKMicrophone ? disconnectOutput : detach
node.disconnect()

但是将其静音更容易。

mic.volume = 0