2

我正在尝试编写一个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 可以工作。

一些可能与该问题相关或无关的观察结果:

  1. 在这篇博文http://www.rockhoppertech.com/blog/multi-timbral-avaudiounitmidiinstrument/ Gene DeLisa 提到AVAudioUnitSampler是抽象类的唯一子类AVAudioUnitMIDIInstrument。这篇文章来自 2016 年,但我没有找到任何关于此的更多信息。但是,很明显,DLS MusicDevice 和第 3 方 SoftSynth 都可以工作——至少在某些环境中是这样。

  2. 从发现AudioComponents的两个 Apple 设备中提取的 ASBD 将其componentFlags属性设置为 2。文档说:必须设置为零,除非请求已知的特定值。SoftSynth的componentFlags属性为 0。

  3. 当我尝试从输入捕获音频时,我仍然遇到某种相关的问题如何允许 Xcode 访问麦克风?? - 到目前为止,命令行应用程序在从 XCode 或通过终端运行时显示不同的行为,并且在这两个问题中都涉及 CoreAudio。

我的问题:

为什么我会收到这些错误?

为什么 3rd 方插件可以在 Playground 和命令行应用程序中工作,但不能在单视图应用程序中工作?

AVAudioEngine 是否准备好托管除单音色 SamplerUnit 之外的其他乐器单元?

还是我必须下台并直接使用instrumentAU而不是AVAudioUnitMIDIInstrument包装器?

4

0 回答 0