Как использовать CoreMIDI на iOS?

Мне не удалось найти много информации о CoreMIDI для iOS. Можно ли даже воспроизвести звук MIDI, отправив сообщение самому устройству. Установлен ли на iPhone или iPad MIDI-устройство или у вас есть устройство, подключенное к интерфейсу?

Ответ 1

Вы должны посмотреть блог pete goodliffe, и он щедро предоставляет примерный проект. Это помогло мне начать программирование CoreMIDI.

Теперь о ваших вопросах, на iOS, в основном используются сетевые сессии CoreMIDI. Участники одной и той же "Сетевой сессии" отправляют сообщения друг другу.

Например, вы настраиваете сетевой сеанс на своем Mac (используя инструмент Audio MIDI Setup), и вы можете подключить к нему устройства iOS. Таким образом, вы можете отправлять сообщения с iOS на ваш хост OSX и наоборот.

Сетевые сеансы CoreMIDI полагаются на протокол RTP для передачи MIDI-сообщений и Bonjour для обнаружения хостов.

Кроме того, CoreMIDI также может обрабатывать MIDI-интерфейс, подключенный к системе, но устройства iOS по умолчанию не имеют физического MIDI-интерфейса. Вы должны купить внешнее оборудование, если хотите напрямую подключить свой iPhone к синтезатору. Однако iPad можно подключить к интерфейсу Midi, совместимому с USB-интерфейсом, через комплект камеры.

Другое дело, что на автономном устройстве iOS вы можете отправить локальный сеанс CoreMIDI для отправки или получения сообщений из другого приложения, совместимого с CoreMIDI.

Ответ 2

Это на пару лет слишком поздно, но это может помочь кому-то другому, как это помогло мне. Этот сайт помог мне прочитать 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 в массиве для использования позже

  • когда status равно 0x90, что означает, что нота была запущена, если она имеет скорость 0, считается, что она не воспроизводится... Мне нужно было добавить этот оператор if, потому что это wasn правильно функционирует ПРИМЕЧАНИЕ. Я обрабатываю только теги key on и key off, поэтому вы должны увеличить оператор switch для обработки большего количества событий MIDI

updateKeyboardButtonAfterKeyPressed Method

  • Это метод, который я использовал для хранения заметок, которые были воспроизведены, и я удаляю заметки из этого массива после того, как ключ был выпущен.

Надеюсь, это поможет.