簡體   English   中英

AudioKit iOS-接收MIDINoteOn功能

[英]AudioKit iOS - receivedMIDINoteOn function

當音序器播放音符時,我試圖使用ReceivedMIDINoteOn函數來刷新UILabel。 我嘗試使用AKMIDIListener協議沒有成功。 我還做了一個AKMIDISampler的子類,並從音序器中向它發送了midi。 它播放MIDI,但不調用接收的MIDINoteOn。

這就是導體的init()中的內容:

init() {

    [ahSampler, beeSampler, gooSampler,flasher] >>> samplerMixer
    AudioKit.output = samplerMixer
    AudioKit.start()



    let midi = AKMIDI()
    midi.createVirtualPorts()
    midi.openInput("Session 1")
    midi.addListener(self)
}

導體遵循AKMIDIListener協議

這是函數:永不調用

func receivedMIDINoteOn(noteNumber: MIDINoteNumber, velocity: MIDIVelocity, channel: MIDIChannel)
{
    print("got it")
}

這是AKMIDISampler的子類,它獲取midi並播放正弦合成器,但是從不調用receiveMIDINoteOn。

class Flasher: AKMIDISampler
{
    override func receivedMIDINoteOn(noteNumber: MIDINoteNumber, velocity:    MIDIVelocity, channel: MIDIChannel)
    {
        print("Flasher got it!")
    }
}

編輯:我應該一直在使用AKCallbackInstrument類,並覆蓋它的start()函數。

本,

在沒有看到整個項目的情況下,我想如果您的項目能夠接收和觸發MIDI音符,那么僅將其輸出發送到UILabel就是一個問題。 我建議在Conductor類中收到MIDI事件時,使用NotificationCenter通知ViewController。 確保添加DispatchQueue.main.async代碼,否則文本將不會按預期更新。 這是在谷歌AudioKit組注意到這里

例:

DispatchQueue.main.async(execute: {
        nc.post(name: NSNotification.Name(rawValue: "outputMessage"),
                object: nil,
                userInfo: [
                    "message": self.outputMIDIMessage,
                    "midiSignalReceived": self.midiSignalReceived,
                    "midiTypeReceived": self.midiTypeReceived
        ])
})

我還建議以下內容:

let midi = AKMIDI()移到Conductor類頂部的init()外部而不是內部的實例變量中。 看來您正在嘗試在AudioKit.start()之后創建它。

我發布了一個示例項目,演示了如何通過AudioKit接收到MIDI音符編號時如何更改UILabel的顏色:

https://github.com/markjeschke/AKMidiReceiver

導體等級:

import AudioKit

enum MidiEventType: String {
    case
        noteNumber          = "Note Number",
        continuousControl   = "Continuous Control",
        programChange       = "Program Change"
}

class Conductor: AKMIDIListener {

    // Globally accessible
    static let sharedInstance = Conductor()

    // Set the instance variables outside of the init()
    let midi = AKMIDI()

    var demoSampler = SamplerAudioFileLoader()
    var samplerMixer = AKMixer()
    var outputMIDIMessage = ""
    var midiSignalReceived = false
    var midiTypeReceived: MidiEventType = .noteNumber

    init() {

        // Session settings
        AKSettings.bufferLength = .medium
        AKSettings.defaultToSpeaker = true

        // Allow audio to play while the iOS device is muted.
        AKSettings.playbackWhileMuted = true

        do {
            try AKSettings.setSession(category: .playAndRecord, with: [.defaultToSpeaker, .allowBluetooth, .mixWithOthers])
        } catch {
            AKLog("Could not set session category.")
        }

        // File path options are:
        // "TX Brass"
        // "TX LoTine81z"
        // "TX Metalimba"
        // "TX Pluck Bass"
        demoSampler.loadEXS24Sample(filePath: "TX Brass")

        // If you wish to load a wav file, comment the `loadEXS24` method and uncomment this one:
//      demoSampler.loadWavSample(filePath: "Kick") // Load Kick wav file

        [demoSampler] >>> samplerMixer
        AudioKit.output = samplerMixer
        AudioKit.start()

        // MIDI Configure
        midi.createVirtualInputPort(98909, name: "AKMidiReceiver")
        midi.createVirtualOutputPort(97789, name: "AKMidiReceiver")
        midi.openInput()
        midi.openOutput()
        midi.addListener(self)

    }

    // Capture the MIDI Text within a DispatchQueue, so that it's on the main thread.
    // Otherwise, it won't display.
    func captureMIDIText() {
        let nc = NotificationCenter.default
        DispatchQueue.main.async(execute: {
            nc.post(name: NSNotification.Name(rawValue: "outputMessage"),
                    object: nil,
                    userInfo: [
                        "message": self.outputMIDIMessage,
                        "midiSignalReceived": self.midiSignalReceived,
                        "midiTypeReceived": self.midiTypeReceived
                ])
        })
    }

    // MARK: MIDI received

    // Note On Number + Velocity + MIDI Channel
    func receivedMIDINoteOn(noteNumber: MIDINoteNumber, velocity: MIDIVelocity, channel: MIDIChannel) {
        midiTypeReceived = .noteNumber
        outputMIDIMessage = "\(midiTypeReceived.rawValue)\nChannel: \(channel+1)  noteOn: \(noteNumber)  velocity: \(velocity)"
        print(outputMIDIMessage)
        midiSignalReceived = true
        captureMIDIText()
        playNote(note: noteNumber, velocity: velocity, channel: channel)
    }

    // Note Off Number + Velocity + MIDI Channel
    func receivedMIDINoteOff(noteNumber: MIDINoteNumber, velocity: MIDIVelocity, channel: MIDIChannel) {
        midiTypeReceived = .noteNumber
        outputMIDIMessage = "\(midiTypeReceived.rawValue)\nChannel: \(channel+1)  noteOff: \(noteNumber)  velocity: \(velocity)"
        print(outputMIDIMessage)
        midiSignalReceived = false
        captureMIDIText()
        stopNote(note: noteNumber, channel: channel)
    }

    // Controller Number + Value + MIDI Channel
    func receivedMIDIController(_ controller: MIDIByte, value: MIDIByte, channel: MIDIChannel) {
        // If the controller value reaches 127 or above, then trigger the `demoSampler` note.
        // If the controller value is less, then stop the note.
        // This creates an on/off type of "momentary" MIDI messaging.
        if value >= 127 {
            playNote(note: 30 + controller, velocity: 80, channel: channel)
        } else {
            stopNote(note: 30 + controller, channel: channel)
        }
        midiTypeReceived = .continuousControl
        outputMIDIMessage = "\(midiTypeReceived.rawValue)\nChannel: \(channel+1)  controller: \(controller)  value: \(value)"
        midiSignalReceived = true
        captureMIDIText()
    }

    // Program Change Number + MIDI Channel
    func receivedMIDIProgramChange(_ program: MIDIByte, channel: MIDIChannel) {
        // Trigger the `demoSampler` note and release it after half a second (0.5), since program changes don't have a note off release.
        triggerSamplerNote(program, channel: channel)
        midiTypeReceived = .programChange
        outputMIDIMessage = "\(midiTypeReceived.rawValue)\nChannel: \(channel+1)  programChange: \(program)"
        midiSignalReceived = true
        captureMIDIText()
    }

    func receivedMIDISetupChange() {
        print("midi setup change")
        print("midi.inputNames: \(midi.inputNames)")

        let listInputNames = midi.inputNames

        for inputNames in listInputNames {
            print("inputNames: \(inputNames)")
            midi.openInput(inputNames)
        }
    }

    func playNote(note: MIDINoteNumber, velocity: MIDIVelocity, channel: MIDIChannel) {
        demoSampler.play(noteNumber: note, velocity: velocity, channel: channel)
    }

    func stopNote(note: MIDINoteNumber, channel: MIDIChannel) {
        demoSampler.stop(noteNumber: note, channel: channel)
    }

    func triggerSamplerNote(_ program: MIDIByte, channel: MIDIChannel) {
        playNote(note: 60 + program, velocity: 80, channel: channel)
        let releaseNoteDelay = DispatchTime.now() + 0.5 // Change 0.5 to desired number of seconds
        DispatchQueue.main.asyncAfter(deadline: releaseNoteDelay) {
            self.stopNote(note: 60 + program, channel: channel)
            self.midiSignalReceived = false
        }
    }

}

帶UILabel的ViewController:

import UIKit
import AudioKit

class ViewController: UIViewController {

    @IBOutlet weak var outputTextLabel: UILabel!

    var conductor = Conductor.sharedInstance
    var midiSignalReceived = false
    var midiTypeReceived: MidiEventType = .noteNumber

    override func viewDidLoad() {
        super.viewDidLoad()

        let nc = NotificationCenter.default
        nc.addObserver(forName:NSNotification.Name(rawValue: "outputMessage"), object:nil, queue:nil, using:catchNotification)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        flashBackgroundColor()
        midiSignalReceived = false
        self.outputTextLabel.text = "Listening for MIDI events..."
    }

    @objc func catchNotification(notification:Notification) -> Void {
        guard
            let userInfo = notification.userInfo,
            let message  = userInfo["message"] as? String,
            let midiSignalReceived = userInfo["midiSignalReceived"] as? Bool,
            let midiTypeReceived = userInfo["midiTypeReceived"] as? MidiEventType else {
                print("No userInfo found in notification")
                return
        }
        DispatchQueue.main.async(execute: {
            self.outputTextLabel.text = message
            self.midiSignalReceived = midiSignalReceived
            self.midiTypeReceived = midiTypeReceived
            self.flashBackgroundColor()
        })
    }

    @objc func flashBackgroundColor() {
        if midiSignalReceived {
            self.outputTextLabel.backgroundColor = UIColor.green
            self.view.backgroundColor = UIColor.lightGray
            if midiTypeReceived != .noteNumber {
                self.perform(#selector(dismissFlashBackgroundColor), with: nil, afterDelay: 0.5)
            }
        } else {
            dismissFlashBackgroundColor()
        }
    }

    @objc func dismissFlashBackgroundColor() {
        UIView.animate(withDuration: 0.5) {
            self.outputTextLabel.backgroundColor = UIColor.clear
            self.view.backgroundColor = UIColor.white
            self.midiSignalReceived = false
            self.conductor.midiSignalReceived = false
        }
    }

    deinit {
        NotificationCenter.default.removeObserver(self,
                                                  name: NSNotification.Name(rawValue: "outputMessage"),
                                                  object: nil)
    }

}

SamplerAudioFileLoader.swift:

import AudioKit

class SamplerAudioFileLoader: AKMIDISampler {

    internal func loadWavSample(filePath: String) {
        do {
            try self.loadWav("Sounds/\(filePath)")
        } catch {
            print("Could not locate the Wav file.")
        }
    }

    internal func loadEXS24Sample(filePath: String) {
        do {
            try self.loadEXS24("Sounds/Sampler Instruments/\(filePath)")
        } catch {
            print("Could not locate the EXS24 file.")
        }
    }

}

我希望這有幫助。 如果您對此有任何疑問,請告訴我。

照顧自己,
標記

PS如果克隆這個AKMidiReceiver例如 ,打開工作區,並沒有出現計划在Xcode項目,請按照中發現,這些步驟在這里

  1. 點擊無計划
  2. 點擊管理方案
  3. 立即點擊自動創建方案

根據初始化flasher的方式,可能必須運行帶有名稱的flasher.enableMIDI()(可選)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM