我正在尝试编写一个MIDIPlayer
类,它是 anAVAudioEngine
和 an的包装器AVAudioUnitMIDIInstrument
。我编写了一个循环,获取所有AudioComponents
类型的名称和 ASBD MusicDevice
,然后根据与字体替换非常相似的列表选择最需要的单元,Apples DLS MusicDevice 作为最终后备。这是我的示例代码:
import AVKit
fileprivate func setupAVAudioEngine(engine: inout AVAudioEngine, instrumentAU: inout AVAudioUnitMIDIInstrument?) {
var instrumentACD = AudioComponentDescription(componentType: kAudioUnitType_MusicDevice, componentSubType: 0, componentManufacturer: 0, componentFlags: 0, componentFlagsMask: 0)
var instrumentComponents: [(AudioComponentDescription, String)] = []
var instrumentComponent: AudioComponent? = nil
repeat {
instrumentComponent = AudioComponentFindNext(instrumentComponent, &instrumentACD)
if instrumentComponent == nil {
break
}
var compDescr = AudioComponentDescription()
var name: Unmanaged<CFString>?
AudioComponentCopyName(instrumentComponent!, &name)
AudioComponentGetDescription(instrumentComponent!, &compDescr)
let nameString = name!.takeRetainedValue() as String
instrumentComponents.append((compDescr, nameString))
name?.release()
} while true
let instrumentComponentSubstitutionList = ["MakeMusic: SmartMusicSoftSynth","Apple: AUMIDISynth","Apple: DLSMusicDevice"]
var found = false
for instrument in instrumentComponentSubstitutionList {
for member in instrumentComponents {
if member.1 == instrument {
instrumentACD = member.0
print("\(member.1) found")
found = true
break
}
if found {break}
}
}
print("Try to create InstrumentNode with ACD: \(instrumentACD)")
instrumentAU = AVAudioUnitMIDIInstrument(audioComponentDescription: instrumentACD)
print("InstrumentNode created: \(instrumentAU!.name)")
print()
engine.attach(instrumentAU!)
engine.connect(instrumentAU!, to: engine.mainMixerNode, format: nil)
}
open class MIDIPlayer {
private var audioEngine: AVAudioEngine
private var instrumentUnit: AVAudioUnitMIDIInstrument
private var mainMixer: AVAudioMixerNode
public init() {
self.audioEngine = AVAudioEngine()
self.mainMixer = audioEngine.mainMixerNode
var instrumentAU: AVAudioUnitMIDIInstrument?
setupAVAudioEngine(engine: &audioEngine, instrumentAU: &instrumentAU)
self.instrumentUnit = instrumentAU!
try! audioEngine.start()
}
public func playMIDINote(_ note: UInt8) {
print("Playing MIDI Note \(note)")
instrumentUnit.startNote(note, withVelocity: 70, onChannel: 0)
sleep(1)
instrumentUnit.stopNote(note, onChannel: 0)
}
}
let midiPlayer = MIDIPlayer()
midiPlayer.playMIDINote(60)
midiPlayer.playMIDINote(62)
midiPlayer.playMIDINote(64)
midiPlayer.playMIDINote(65)
midiPlayer.playMIDINote(67)
该代码在 Xcode 11.3.1 操场上运行得非常好,但是,当我在操场外使用完全相同的代码时,我会遇到不同类型的错误:当我将相同的代码复制到命令行项目并从 XCode 运行它时,它仍然有效,但控制台给了我以下错误:
[AudioHAL_Client] AudioHardware.cpp:666:AudioObjectGetPropertyData: AudioObjectGetPropertyData: no object with given ID 0
当我在没有 Xcode 的情况下运行可执行文件时,不会报告任何错误。
当我创建单个 View 应用程序时,将类及其设置函数放在它自己的源文件中并applicationDidFinishLaunching
在 AppDelegate 的方法中创建一个实例,我收到以下错误:
[AudioHAL_Client] HALC_ShellDriverPlugIn.cpp:104:Open: HALC_ShellDriverPlugIn::Open: opening the plug-in failed, Error: 2003329396 (what)
[AudioHAL_Client] AudioHardware.cpp:666:AudioObjectGetPropertyData: AudioObjectGetPropertyData: no object with given ID 0
这些错误甚至在我的设置函数被调用之前就被写入控制台。但是,代码仍然有效(我听到音符播放),但前提是我更改了instrumentComponentSubstitutionList
以便找到 Apple AU 之一(换句话说:不是 SmartMusicSoftSynth)。当我将 SoftSynth 保留为首选设备时,代码崩溃并出现额外错误:
[avae] AVAEInternal.h:103:_AVAE_CheckNoErr: [AUInterface.mm:461:AUInterfaceBaseV3: (AudioComponentInstanceNew(comp, &_auv2)): error -3000
注意:在 Playground 和命令行应用程序中,SoftSynth 可以工作。
一些可能与该问题相关或无关的观察结果:
在这篇博文http://www.rockhoppertech.com/blog/multi-timbral-avaudiounitmidiinstrument/ Gene DeLisa 提到
AVAudioUnitSampler
是抽象类的唯一子类AVAudioUnitMIDIInstrument
。这篇文章来自 2016 年,但我没有找到任何关于此的更多信息。但是,很明显,DLS MusicDevice 和第 3 方 SoftSynth 都可以工作——至少在某些环境中是这样。从发现
AudioComponents
的两个 Apple 设备中提取的 ASBD 将其componentFlags
属性设置为 2。文档说:必须设置为零,除非请求已知的特定值。SoftSynth的componentFlags
属性为 0。当我尝试从输入捕获音频时,我仍然遇到某种相关的问题如何允许 Xcode 访问麦克风?? - 到目前为止,命令行应用程序在从 XCode 或通过终端运行时显示不同的行为,并且在这两个问题中都涉及 CoreAudio。
我的问题:
为什么我会收到这些错误?
为什么 3rd 方插件可以在 Playground 和命令行应用程序中工作,但不能在单视图应用程序中工作?
AVAudioEngine 是否准备好托管除单音色 SamplerUnit 之外的其他乐器单元?
还是我必须下台并直接使用instrumentAU而不是AVAudioUnitMIDIInstrument
包装器?