如何在iOS上使用CoreMIDI?

时间:2012-12-19 12:06:38

标签: ios coremidi

我无法在CoreMIDI for iOS上找到太多信息。甚至可以通过向设备本身发送消息来播放MIDI声音。 iPhone或iPad是否安装了MIDI设备,或者您是否必须将设备连接到界面?

2 个答案:

答案 0 :(得分:8)

这已经太晚了几年,但它可能会帮助那些帮助我的人。这website帮助我从外部MIDI键盘读取MIDI数据。连接是最棘手的部分,但本教程将引导您完成它。

这是我创建的课程。

MIDIController.h

#import <Foundation/Foundation.h>

@interface MIDIController : NSObject

@property NSMutableArray *notes;

@end

MIDIController.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的引用,以便您可以填充类的属性...在我的情况下,我将所有已播放的音符按MIDI编号存储在一个数组中,以便稍后使用

  • status0x90时,表示已触发音符,如果速度为0,则认为未播放...我需要添加此项声明,因为它没有正常运作 注意:我只处理key onkey off事件,因此您可以扩充switch语句以处理更多MIDI事件

updateKeyboardButtonAfterKeyPressed方法

  • 这是一种用于存储已播放音符的方法,一旦键被释放,我会从该数组中删除音符

我希望这会有所帮助。

答案 1 :(得分:6)

你应该看看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的应用程序发送或接收消息。