[英]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項目,請按照中發現,這些步驟在這里 :
根據初始化flasher的方式,可能必須運行帶有名稱的flasher.enableMIDI()(可選)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.