AudioKit iOS – receivedMIDINoteOn函数

我正在尝试使用receivedMIDINoteOn函数在音序器播放音符时闪烁UILabel。 我尝试过使用AKMIDIListener协议但没有成功。 我还制作了一个AKMIDISampler的子类,并从音序器发送midi。 它播放midi但未调用receivedMIDINoteOn。

这就是我在指挥的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协议

这是function:永远不会被调用

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

这是AKMIDISampler的子类,它获得midi并播放正弦合成器,但是从不调用receivedMIDINoteOn。

 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 Google Group 在此处注明了这一点。

例:

 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

指挥class:

 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. 点击No Scheme
  2. 单击Manage Scheme
  3. 单击“立即自动创建方案”

根据您初始化闪存器的方式,您可能必须使用名称来运行flasher.enableMIDI()。