如何将输入节点动态连接到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()
}
}
答案 0 :(得分:2)
在断开连接时,detach将从基础AVAudioEngine中分离节点。 AKMicrophone的底层节点是AVAudioEngine的属性,所以最好断开它。
let disconnect = node is AKMicrophone ? disconnectOutput : detach
node.disconnect()
但是将其静音更容易。
mic.volume = 0