AVAudioUnitMidiInstrument может изменить программу в симуляторе, но не на устройстве? - PullRequest
0 голосов
/ 16 апреля 2020

TL; DR: я могу менять MIDI-инструменты в своем приложении iOS при работе в симуляторе Xcode, но не при работе на устройстве.

Я следил за несколькими из Джина Де Лизы посты, чтобы узнать, как использовать MIDI в приложении iOS. Я продвинулся дальше, следуя мультитембральному AVAudioUnitMIDIInstrument для подкласса AVAudioUnitMIDIInstrument и используя свойство kMusicDeviceProperty_SoundBankURL, чтобы указать на файл звукового шрифта sf2.

В симуляторе Xcode , это прекрасно работает. Я могу sendProgramChange переключиться на любой стандартный MIDI-инструмент. Но когда я загружаю свое приложение на iPhone, только пианино, MIDI-инструмент 1, воспроизводит любой звук. Кто-нибудь еще видел такое поведение?

Код, который я использую, основан на шаблоне "iOS Game" в Xcode 11.4 с четырьмя изменениями.

Первое изменение - настройка аудио сессия для приложения правильно. Мой новый AppDelegate.application:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let audioSession = AVAudioSession.sharedInstance()
        do {
            try audioSession.setCategory(.playback, options: .duckOthers)
        } catch let error as NSError {
            print("Unable to set audio category: \(error.localizedDescription)")
        }

        do {
            try audioSession.setActive(true)
        } catch let error as NSError {
            print("Unable to activate audio session: \(error.localizedDescription)")
        }
        return true
    }

Второй - мой подкласс AVAudioUnitMIDIInstrument:

class MyMIDIInstrument: AVAudioUnitMIDIInstrument {
    init(soundBankURL: URL) throws {
        let description = AudioComponentDescription(
            componentType: kAudioUnitType_MusicDevice, componentSubType: kAudioUnitSubType_MIDISynth, componentManufacturer: kAudioUnitManufacturer_Apple, componentFlags: 0, componentFlagsMask: 0
        )
        super.init(audioComponentDescription: description)

        var bankURL = soundBankURL

        let status = AudioUnitSetProperty(
            self.audioUnit,
            AudioUnitPropertyID(kMusicDeviceProperty_SoundBankURL),
            AudioUnitScope(kAudioUnitScope_Global),
            0,
            &bankURL,
            UInt32(MemoryLayout<URL>.size))
        if (status != OSStatus(noErr)) {
            throw NSError(domain: "MyMIDIInstrument", code: Int(status), userInfo: [NSLocalizedDescriptionKey: "Could not set soundbank property"])
        }
    }
}

Последнее изменение кода - добавление инициализации и воспроизведение в GameScene:

class GameScene: SKScene {

    var avEngine = AVAudioEngine()
    var midi: MyMIDIInstrument?

    override func didMove(to view: SKView) {
        guard let soundBankURL = Bundle.main.url(forResource: "FluidR3_GM", withExtension: "sf2")
            else {
                print("Failed to get url for sound bank")
                exit(-1)
        }

        do {
            try midi = MyMIDIInstrument(soundBankURL: soundBankURL)
        } catch let error as NSError {
            print("Failed to init MIDI: \(error.code) - \(error.localizedDescription)")
            exit(-1)
        }

        avEngine.attach(midi!)
        avEngine.connect(midi!, to: avEngine.mainMixerNode, format: nil)
        do {
            try avEngine.start()
        } catch let error as NSError {
            print("Failed to start AVEngine: \(error.localizedDescription)")
        }

        // Case 1: do not send program change
        //
        // Results:
        //   Simulator: piano sounds
        //      iPhone: piano sounds

        // Case 2: send program change to 0=piano, without bank
        // midi!.sendProgramChange(UInt8(0), onChannel: UInt8(0))
        // Results:
        //   Simulator: piano sounds
        //      iPhone: piano sounds

        // Case 3: send program change to 0=piano, with bank=melodic
        // midi!.sendProgramChange(UInt8(0), bankMSB: UInt8(kAUSampler_DefaultMelodicBankMSB), bankLSB: UInt8(kAUSampler_DefaultBankLSB), onChannel: UInt8(0))
        // Results:
        //   Simulator: no sound
        //      iPhone: no sound

        // Case 4: send program change to 0=piano, with bank=0
        // midi!.sendProgramChange(UInt8(0), bankMSB: UInt8(0), bankLSB: UInt8(kAUSampler_DefaultBankLSB), onChannel: UInt8(0))
        // Results:
        //   Simulator: piano sounds
        //      iPhone: piano sounds

        // Case 5: send program change to 12=marimba, without bank
        // midi!.sendProgramChange(UInt8(12), onChannel: UInt8(0))
        // Results:
        //   Simulator: marimba sounds
        //      iPhone: no sound

        // Case 6: send program change to 12=marimba, with bank=melodic
        // midi!.sendProgramChange(UInt8(12), bankMSB: UInt8(kAUSampler_DefaultMelodicBankMSB), bankLSB: UInt8(kAUSampler_DefaultBankLSB), onChannel: UInt8(0))
        // Results:
        //   Simulator: no sound
        //      iPhone: no sound

        // Case 7: send program change to 12=marimba, with bank=0
        // midi!.sendProgramChange(UInt8(12), bankMSB: UInt8(0), bankLSB: UInt8(kAUSampler_DefaultBankLSB), onChannel: UInt8(0))
        // Results:
        //   Simulator: marimba sounds
        //      iPhone: no sound
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("playing note")
        midi!.startNote(64, withVelocity: 64, onChannel: 0)
    }
}

И последнее изменение - добавить в проект звуковой шрифт "FluidR3_GM.sf2" .

В GameScene.didMove(toView:) вы можете увидеть различные варианты поведения, которые я ' видел. Я нахожу несколько интересных вещей:

  1. Я могу получить звуки фортепиано как в симуляторе, так и на устройстве, любым из трех способов: никогда не вызывая sendProgramChange, звоня sendProgramChange без указания банка MSB / LSB или путем вызова sendProgramChange с MSB, установленным в ноль. Однако, если я позвоню sendProgramChange, указав MSB как kAUSampler_DefaultMelodicBankMSB, как и все инструкции, которые я видел, говорят, что вы должны, я не получу звука.

  2. Я могу получить звуки маримбы из симулятор, позвонив по номеру sendProgramChange любым способом, который помог получить звуки фортепиано. Но ни один из этих методов не работает на устройстве.

Я пытался провести тот же эксперимент со звуковым шрифтом GeneralUser GS , с теми же результатами.

Итак, кто-нибудь имеет представление о том, чего мне не хватает?

...