AVAudioEngine выдает на первый взгляд случайные ошибки в разных средах - PullRequest
1 голос
/ 08 февраля 2020

Я пытаюсь написать класс MIDIPlayer, который является оберткой для AVAudioEngine и AVAudioUnitMIDIInstrument. Я написал al oop, который получает имена и ASBD для всех AudioComponents типа 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, помещаю класс и его настройки c в его собственный исходный файл и создаю экземпляр в методе 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

Примечание. На игровой площадке и в приложении командной строки работает SoftSynth.

Некоторые наблюдения, которые могут относиться или не относиться к проблеме:

  1. В этом посте http://www.rockhoppertech.com/blog/multi-timbral-avaudiounitmidiinstrument/ Джин ДеЛиза упоминает, что AVAudioUnitSampler является единственным подкласс абстрактного класса AVAudioUnitMIDIInstrument. Это сообщение с 2016 года, но я не нахожу никакой дополнительной информации об этом. Но, очевидно, DLS MusicDevice, а также сторонний SoftSynth работают - по крайней мере, в некоторых средах.

  2. ASBD, взятый из найденного AudioComponents для обоих блоков Apple, имеет их свойство componentFlags установлено на 2. В документации сказано: должно быть установлено на ноль, если не запрошено известное значение c. Свойство componentFlags SoftSynth равно 0.

  3. У меня все еще есть какая-то проблема, связанная с попыткой захвата звука с входа Как разрешить Xcode получить доступ к микрофону ? - связано с тем, что приложение командной строки показывает другое поведение при запуске из XCode или через терминал, и в обеих проблемах участвует CoreAudio.

Мой вопрос (ы):

Почему я получаю эти ошибки?

Почему сторонний плагин работает на игровой площадке и в приложении командной строки, но не в одном приложении просмотра?

Готов ли AVAudioEngine к размещать другие приборные модули, кроме монотибрального блока SamplerUnit?

Или мне нужно уйти в отставку и использовать приборы прибора напрямую, а не оболочку AVAudioUnitMIDIInstrument?

...