我一直找不到很多关于iOS版CoreMIDI的信息。甚至可以通过向设备本身发送消息来播放MIDI声音吗?iPhone或iPad是否安装了MIDI设备,或者你必须连接一个设备才能与之接口?
gblwokeq1#
这是几年太晚了,但它可能会帮助别人在那里,就像它帮助我一样。这个website是帮助我从外部MIDI键盘读取MIDI数据的工具。连接是最棘手的部分,但本教程将引导您通过它。这是我创建的类。
#import <Foundation/Foundation.h> @interface MIDIController : NSObject @property NSMutableArray *notes; @end
#import "MIDIController.h" #include <CoreFoundation/CoreFoundation.h> #import <CoreMIDI/CoreMIDI.h> #define SYSEX_LENGTH 1024 #define KEY_ON 1 #define KEY_OFF 0 @implementation MIDIController - (id)init { if (self = [super init]) { _notes = [[NSMutableArray alloc] init]; [self setupMidi]; } return self; } - (void) setupMidi { MIDIClientRef midiClient; checkError(MIDIClientCreate(CFSTR("MIDI client"), NULL, NULL, &midiClient), "MIDI client creation error"); MIDIPortRef inputPort; checkError(MIDIInputPortCreate(midiClient, CFSTR("Input"), midiInputCallback, (__bridge_retained void *)self, &inputPort), "MIDI input port error"); checkError(connectMIDIInputSource(inputPort), "connect MIDI Input Source error"); } OSStatus connectMIDIInputSource(MIDIPortRef inputPort) { unsigned long sourceCount = MIDIGetNumberOfSources(); for (int i = 0; i < sourceCount; ++i) { MIDIEndpointRef endPoint = MIDIGetSource(i); CFStringRef endpointName = NULL; checkError(MIDIObjectGetStringProperty(endPoint, kMIDIPropertyName, &endpointName), "String property not found"); checkError(MIDIPortConnectSource(inputPort, endPoint, NULL), "MIDI not connected"); } return noErr; } void midiInputCallback(const MIDIPacketList *list, void *procRef, void *srcRef) { MIDIController *midiController = (__bridge MIDIController*)procRef; UInt16 nBytes; const MIDIPacket *packet = &list->packet[0]; //gets first packet in list for(unsigned int i = 0; i < list->numPackets; i++) { nBytes = packet->length; //number of bytes in a packet handleMIDIStatus(packet, midiController); packet = MIDIPacketNext(packet); } } void handleMIDIStatus(const MIDIPacket *packet, MIDIController *midiController) { int status = packet->data[0]; //unsigned char messageChannel = status & 0xF; //16 possible MIDI channels switch (status & 0xF0) { case 0x80: updateKeyboardButtonAfterKeyPressed(midiController, packet->data[1], KEY_OFF); break; case 0x90: //data[2] represents the velocity of a note if (packet->data[2] != 0) { updateKeyboardButtonAfterKeyPressed(midiController, packet->data[1], KEY_ON); }//note off also occurs if velocity is 0 else { updateKeyboardButtonAfterKeyPressed(midiController, packet->data[1], KEY_OFF); } break; default: //NSLog(@"Some other message"); break; } } void updateKeyboardButtonAfterKeyPressed(MIDIController *midiController, int key, bool keyStatus) { NSMutableArray *notes = [midiController notes]; //key is being pressed if(keyStatus) { [notes addObject:[NSNumber numberWithInt:key]]; } else {//key has been released for (int i = 0; i < [notes count]; i++) { if ([[notes objectAtIndex:i] integerValue] == key) { [notes removeObjectAtIndex:i]; } } } } void checkError(OSStatus error, const char* task) { if(error == noErr) return; char errorString[20]; *(UInt32 *)(errorString + 1) = CFSwapInt32BigToHost(error); if(isprint(errorString[1]) && isprint(errorString[2]) && isprint(errorString[3]) && isprint(errorString[4])) { errorString[0] = errorString[5] = '\''; errorString[6] = '\0'; } else sprintf(errorString, "%d", (int)error); fprintf(stderr, "Error: %s (%s)\n", task, errorString); exit(1); } @end
midiInputCallback
handleMIDIStatus
status
0x90
key on
key off
希望这能帮上忙。
a8jjtwal2#
你应该看看pete goodliffe's blog,他慷慨地提供了一个示例项目。它对我开始编程CoreMIDI帮助很大。现在关于你的问题,在iOS上,大多使用CoreMIDI网络会话。同一个“网络会话”的参与者互相发送消息。例如,您可以在Mac上配置一个网络会话(使用音频MIDI设置工具),然后将iOS设备连接到该会话。这样,您就可以从iOS向OSX主机发送消息,反之亦然。CoreMIDI网络会话依赖RTP协议传输MIDI消息,依赖Bonjour发现主机。除此之外,CoreMIDI还可以处理连接到系统的MIDI接口,但iOS设备默认没有物理MIDI接口。如果你想直接将iPhone连接到合成器,你必须购买外部硬件。不过,iPad可以通过相机套件连接到USB类兼容的MIDI接口。另一件事,在独立的iOS设备上,你可以使用本地CoreMIDI会话发送或接收来自/到另一个CoreMIDI兼容应用程序的消息。
u0sqgete3#
import UIKit import CoreMIDI class ViewController : UIViewController { // MARK: - Properties - var inputPort : MIDIPortRef = 0 var source : MIDIDeviceRef = 0 var client = MIDIClientRef() var connRefCon : UnsafeMutableRawPointer? var endpoint : MIDIEndpointRef? // MARK: - Lifecycle - override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. print("viewDidLoad") // endpoint self.endpoint = MIDIGetSource(MIDIGetNumberOfSources()-1) // USB Device References let sources = getUSBDeviceReferences() if sources.count > 0 { self.source = sources.first! } print("source: \(source)") // create client DispatchQueue.global().async { self.createClient() } } // MARK: - USB Device References - /// Filters all `MIDIDeviceRef`'s for USB-Devices private func getUSBDeviceReferences() -> [MIDIDeviceRef] { var devices = [MIDIDeviceRef]() for index in 0 ..< MIDIGetNumberOfDevices() { print("index: \(index)") let device = MIDIGetDevice(index) var list : Unmanaged<CFPropertyList>? MIDIObjectGetProperties(device, &list, true) if let list = list { let dict = list.takeRetainedValue() as! NSDictionary print("dict: \(dict)") if dict["USBLocationID"] != nil { print("USB MIDI DEVICE") devices.append(device) } } } return devices } // MARK: - Client - func createClient() { print("createClient") let clientName = "Client" as CFString let err = MIDIClientCreateWithBlock(clientName, &client) { (notificationPtr: UnsafePointer<MIDINotification>) in let notification = notificationPtr.pointee print("notification.messageID: \(notification.messageID)") switch notification.messageID { case .msgSetupChanged: // Can ignore, really break case .msgObjectAdded: let rawPtr = UnsafeRawPointer(notificationPtr) let message = rawPtr.assumingMemoryBound(to: MIDIObjectAddRemoveNotification.self).pointee print("MIDI \(message.childType) added: \(message.child)") case .msgObjectRemoved: let rawPtr = UnsafeRawPointer(notificationPtr) let message = rawPtr.assumingMemoryBound(to: MIDIObjectAddRemoveNotification.self).pointee print("MIDI \(message.childType) removed: \(message.child)") case .msgPropertyChanged: let rawPtr = UnsafeRawPointer(notificationPtr) let message = rawPtr.assumingMemoryBound(to: MIDIObjectPropertyChangeNotification.self).pointee print("MIDI \(message.object) property \(message.propertyName.takeUnretainedValue()) changed.") case .msgThruConnectionsChanged: fallthrough case .msgSerialPortOwnerChanged: print("MIDI Thru connection was created or destroyed") case .msgIOError: let rawPtr = UnsafeRawPointer(notificationPtr) let message = rawPtr.assumingMemoryBound(to: MIDIIOErrorNotification.self).pointee print("MIDI I/O error \(message.errorCode) occurred") default: break } } // createInputPort from client self.createInputPort(midiClient: self.client) if err != noErr { print("Error creating MIDI client: \(err)") } // run on background for connect / disconnect let rl = RunLoop.current while true { rl.run(mode: .default, before: .distantFuture) } } // MARK: - Input Port - func createInputPort(midiClient: MIDIClientRef) { print("createInputPort: midiClient: \(midiClient)") MIDIInputPortCreateWithProtocol( midiClient, "Input Port" as CFString, MIDIProtocolID._1_0, &self.inputPort) { [weak self] eventList, srcConnRefCon in // let midiEventList: MIDIEventList = eventList.pointee //print("srcConnRefCon: \(srcConnRefCon)") //print("midiEventList.protocol: \(midiEventList.protocol)") var packet = midiEventList.packet //print("packet: \(packet)") (0 ..< midiEventList.numPackets).forEach { _ in //print("\(packet)") let words = Mirror(reflecting: packet.words).children words.forEach { word in let uint32 = word.value as! UInt32 guard uint32 > 0 else { return } let midiPacket = MidiPacket( command: UInt8((uint32 & 0xFF000000) >> 24), channel: UInt8((uint32 & 0x00FF0000) >> 16), note: UInt8((uint32 & 0x0000FF00) >> 8), velocity: UInt8(uint32 & 0x000000FF)) print("----------") print("MIDIPACKET") print("----------") midiPacket.printValues() } } } MIDIPortConnectSource(self.inputPort, self.endpoint ?? MIDIGetSource(MIDIGetNumberOfSources()-1), &self.connRefCon) } } class MidiPacket : NSObject { var command : UInt8 = 0 var channel : UInt8 = 0 var note : UInt8 = 0 var velocity : UInt8 = 0 init(command: UInt8, channel: UInt8, note: UInt8, velocity: UInt8) { super.init() self.command = command self.channel = channel self.note = note self.velocity = velocity } func printValues(){ print("command: \(self.command)") print("channel: \(self.channel)") print("note: \(self.note)") print("velocity: \(self.velocity)") } }
3条答案
按热度按时间gblwokeq1#
这是几年太晚了,但它可能会帮助别人在那里,就像它帮助我一样。这个website是帮助我从外部MIDI键盘读取MIDI数据的工具。连接是最棘手的部分,但本教程将引导您通过它。
这是我创建的类。
附加注解
midiInputCallback
是通过MIDI设备(键盘)发生MIDI事件时调用的函数handleMIDIStatus
获取MIDI数据包(其中包含有关播放内容的信息和MIDIController的示例status
为0x90
时,这意味着音符已被触发,如果其力度为0,则视为未播放...我需要添加此if语句,因为它无法正常工作key on
和key off
事件,因此您需要扩充switch语句以处理更多的MIDI事件 *希望这能帮上忙。
a8jjtwal2#
你应该看看pete goodliffe's blog,他慷慨地提供了一个示例项目。它对我开始编程CoreMIDI帮助很大。
现在关于你的问题,在iOS上,大多使用CoreMIDI网络会话。同一个“网络会话”的参与者互相发送消息。
例如,您可以在Mac上配置一个网络会话(使用音频MIDI设置工具),然后将iOS设备连接到该会话。这样,您就可以从iOS向OSX主机发送消息,反之亦然。
CoreMIDI网络会话依赖RTP协议传输MIDI消息,依赖Bonjour发现主机。
除此之外,CoreMIDI还可以处理连接到系统的MIDI接口,但iOS设备默认没有物理MIDI接口。如果你想直接将iPhone连接到合成器,你必须购买外部硬件。不过,iPad可以通过相机套件连接到USB类兼容的MIDI接口。
另一件事,在独立的iOS设备上,你可以使用本地CoreMIDI会话发送或接收来自/到另一个CoreMIDI兼容应用程序的消息。
u0sqgete3#