Почему эта простая программа CoreMIDI не выдает MIDI-выход?
Вот очень простое приложение CoreMIDI OS X, которое отправляет MIDI-данные. Проблема в том, что она не работает. Он компилируется отлично и работает. Он не сообщает об ошибках и не падает. Созданный источник становится видимым в MIDI Monitor. Однако не выводится информация MIDI.
Может ли кто-нибудь сообщить мне, что я здесь делаю неправильно?
#include <CoreMIDI/CoreMIDI.h>
int main(int argc, char *args[])
{
MIDIClientRef theMidiClient;
MIDIEndpointRef midiOut;
MIDIPortRef outPort;
char pktBuffer[1024];
MIDIPacketList* pktList = (MIDIPacketList*) pktBuffer;
MIDIPacket *pkt;
Byte midiDataToSend[] = {0x91, 0x3c, 0x40};
int i;
MIDIClientCreate(CFSTR("Magical MIDI"), NULL, NULL,
&theMidiClient);
MIDISourceCreate(theMidiClient, CFSTR("Magical MIDI Source"),
&midiOut);
MIDIOutputPortCreate(theMidiClient, CFSTR("Magical MIDI Out Port"),
&outPort);
pkt = MIDIPacketListInit(pktList);
pkt = MIDIPacketListAdd(pktList, 1024, pkt, 0, 3, midiDataToSend);
for (i = 0; i < 100; i++) {
if (pkt == NULL || MIDISend(outPort, midiOut, pktList)) {
printf("failed to send the midi.\n");
} else {
printf("sent!\n");
}
sleep(1);
}
return 0;
}
Ответы
Ответ 1
Вы вызываете MIDISourceCreate
для создания виртуального MIDI-источника.
Это означает, что ваш источник появится в другом интерфейсе настройки MIDI-приложений, и эти приложения могут выбрать, следует ли слушать ваш источник. Ваш MIDI-интерфейс не будет отправлен на любые физические MIDI-порты, если только какое-то другое приложение не будет направлять его туда. Это также означает, что у вашего приложения нет выбора, куда идет передача MIDI-сообщений. Я предполагаю, что вы хотите.
Документация для MIDISourceCreate говорит:
После создания виртуального источника используйте MIDIReceived для передачи MIDI-сообщений из вашего виртуального источника любым клиентам, подключенным к виртуальному источнику.
Итак, выполните две вещи:
- Удалите код, создающий выходной порт. Вам это не нужно.
- измените
MIDISend(outPort, midiOut, pktList)
на: MIDIReceived(midiOut, pktlist)
.
Это должно решить вашу проблему.
Итак, для чего нужны выходные порты? Если вы хотите направить свои MIDI-данные на конкретный пункт назначения - возможно, на физический MIDI-порт - вы бы не создали виртуальный MIDI-источник. Вместо этого:
- Вызов
MIDIOutputPortCreate()
для создания выходного порта
- Используйте
MIDIGetNumberOfDestinations()
и MIDIGetDestination()
, чтобы получить список получателей и найти тот, который вас интересует.
- Чтобы отправить MIDI в один пункт назначения, вызовите
MIDISend(outputPort, destination, packetList)
.
Ответ 2
Я просто оставляю это здесь для своих собственных ссылок. Это полный пример, основанный на 100% на вашем, но включающий в себя другую сторону (получение), мой плохой код C и принятые ответы (конечно).
#import "AppDelegate.h"
@implementation AppDelegate
@synthesize window = _window;
#define NSLogError(c,str) do{if (c) NSLog(@"Error (%@): %u:%@", str, (unsigned int)c,[NSError errorWithDomain:NSMachErrorDomain code:c userInfo:nil]); }while(false)
static void spit(Byte* values, int length, BOOL useHex) {
NSMutableString *thing = [@"" mutableCopy];
for (int i=0; i<length; i++) {
if (useHex)
[thing appendFormat:@"0x%X ", values[i]];
else
[thing appendFormat:@"%d ", values[i]];
}
NSLog(@"Length=%d %@", length, thing);
}
- (void) startSending {
MIDIEndpointRef midiOut;
char pktBuffer[1024];
MIDIPacketList* pktList = (MIDIPacketList*) pktBuffer;
MIDIPacket *pkt;
Byte midiDataToSend[] = {0x91, 0x3c, 0x40};
int i;
MIDISourceCreate(theMidiClient, CFSTR("Magical MIDI Source"),
&midiOut);
pkt = MIDIPacketListInit(pktList);
pkt = MIDIPacketListAdd(pktList, 1024, pkt, 0, 3, midiDataToSend);
for (i = 0; i < 100; i++) {
if (pkt == NULL || MIDIReceived(midiOut, pktList)) {
printf("failed to send the midi.\n");
} else {
printf("sent!\n");
}
sleep(1);
}
}
void ReadProc(const MIDIPacketList *packetList, void *readProcRefCon, void *srcConnRefCon)
{
const MIDIPacket *packet = &packetList->packet[0];
for (int i = 0; i < packetList->numPackets; i++)
{
NSData *data = [NSData dataWithBytes:packet->data length:packet->length];
spit((Byte*)data.bytes, data.length, YES);
packet = MIDIPacketNext(packet);
}
}
- (void) setupReceiver {
OSStatus s;
MIDIEndpointRef virtualInTemp;
NSString *inName = [NSString stringWithFormat:@"Magical MIDI Destination"];
s = MIDIDestinationCreate(theMidiClient, (__bridge CFStringRef)inName, ReadProc, (__bridge void *)self, &virtualInTemp);
NSLogError(s, @"Create virtual MIDI in");
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
MIDIClientCreate(CFSTR("Magical MIDI"), NULL, NULL,
&theMidiClient);
[self setupReceiver];
[self startSending];
}
@end
Ответ 3
Небольшая информация, которую другие пропускают: параметр time
MIDIPacketListAdd
важен для некоторых музыкальных приложений.
Вот пример того, как вы можете его получить:
#import <mach/mach_time.h>
MIDITimeStamp midiTime = mach_absolute_time();
Источник: Документация Apple
И затем применим к другим примерам здесь:
pktBuffer[1024];
MIDIPacketList *pktList = (MIDIPacketList*)pktBuffer;
MIDIPacket *pktPtr = MIDIPacketListInit(pktList);
MIDITimeStamp midiTime = mach_absolute_time();
Byte midiDataToSend[] = {0x91, 0x3c, 0x40};
pktPtr = MIDIPacketListAdd(pktList, sizeof(pktList), pktPtr, midiTime, midiDataToSend, sizeof(midiDataToSend));