我在iOS的CoreMIDI上找不到太多信息。是否可以通过向设备本身发送消息来播放MIDI声音。iPhone或iPad是否安装了MIDI设备,或者必须将设备连接到接口?
发布于 2013-01-01 19:57:56
你应该看看pete goodliffe's blog,他慷慨地提供了一个示例项目。开始编写CoreMIDI对我有很大的帮助。
现在关于您的问题,在iOS上,主要使用CoreMIDI网络会话。同一“网络会话”的参与者互相发送消息。
例如,您可以在Mac上配置一个网络会话(使用Audio MIDI设置工具),您可以将iOS设备连接到该会话。这样,您就可以将消息从iOS发送到您的OSX主机,反之亦然。
CoreMIDI网络会话依赖RTP协议来传输MIDI消息,而Bonjour协议来发现主机。
此外,CoreMIDI还可以处理连接到系统的MIDI接口,但iOS设备默认没有物理MIDI接口。如果你想直接将你的iPhone连接到合成器上,你必须购买外部硬件。但是,iPad可以通过相机套件连接到符合USB的Midi接口。
另一件事是,在独立的iOS设备上,您可以使用本地CoreMIDI会话发送消息到另一个兼容CoreMIDI的应用程序,或者从另一个兼容的应用程序接收消息。
发布于 2015-07-17 16:05:05
这已经晚了几年了,但它可能会帮助其他人,就像它帮助了我一样。这个website帮助我从外部MIDI键盘读取MIDI数据。连接是最棘手的部分,但本教程将带您完成它。
这是我创建的类。
MIDIController.h
#import <Foundation/Foundation.h>
@interface MIDIController : NSObject
@property NSMutableArray *notes;
@endMIDIController.m
#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函数
midiInputCallback是通过MIDI设备(键盘)发生MIDI事件时调用的函数注意:这是您可以开始处理MIDI信息的地方
handleMIDIStatus函数
handleMIDIStatus获取MIDI包(其中包含有关播放的内容和MIDIController实例的信息注意:您需要引用MIDIController,以便可以填充class...in的属性my case我按MIDI编号将所有播放的音符存储在一个数组中,以便稍后在
status为0x90时使用,这意味着已经触发了一个音符,如果它的速度为0,则认为它没有播放...我需要添加这个if语句,因为它没有正常工作注意:我只处理key on和key off事件,所以您可以扩充switch语句来处理更多的MIDI事件
updateKeyboardButtonAfterKeyPressed方法
,我就从这个数组中删除音符
我希望这能帮到你。
发布于 2021-08-20 07:30:01
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)")
}
}https://stackoverflow.com/questions/13952151
复制相似问题