macos - para - ¿Por qué este simple programa CoreMIDI no produce salida MIDI?
configurar midi mac (3)
Aquí hay una aplicación CoreMIDI OS X extremadamente simple que envía datos MIDI. El problema es que no funciona. Se compila bien, y se ejecuta. No informa de errores, y no se bloquea. La Fuente creada se hace visible en el Monitor MIDI. Sin embargo, no salen datos MIDI .
¿Alguien podría decirme qué estoy haciendo mal aquí?
#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;
}
Estás llamando a MIDISourceCreate
para crear una fuente MIDI virtual.
Esto significa que su fuente aparecerá en la IU de configuración MIDI de otras aplicaciones, y que esas aplicaciones pueden elegir si escuchar o no su fuente. Su MIDI no se enviará a ningún puerto MIDI físico, a menos que alguna otra aplicación lo canalice allí. También significa que su aplicación no tiene ninguna opción en cuanto a dónde va el MIDI que está enviando. Supongo que eso es lo que quieres.
La documentación para MIDISourceCreate dice:
Después de crear una fuente virtual, use MIDIReceived para transmitir mensajes MIDI desde su fuente virtual a cualquier cliente conectado a la fuente virtual.
Entonces, haz dos cosas:
- Eliminar el código que crea el puerto de salida. No lo necesitas.
- cambie
MIDISend(outPort, midiOut, pktList)
a:MIDIReceived(midiOut, pktlist)
.
Eso debería resolver tu problema.
Entonces, ¿para qué sirven los puertos de salida? Si quisiera dirigir sus datos MIDI a un destino específico, tal vez un puerto MIDI físico, NO crearía una fuente MIDI virtual. En lugar:
- Llame a
MIDIOutputPortCreate()
para crear un puerto de salida - Use
MIDIGetNumberOfDestinations()
yMIDIGetDestination()
para obtener la lista de destinos y encontrar el que le interesa. - Para enviar MIDI a un destino, llame a
MIDISend(outputPort, destination, packetList)
.
Solo estoy dejando esto aquí por mi propia referencia. Es un ejemplo completo basado 100% en el suyo, pero incluye la otra parte (recepción), mi código C incorrecto y las correcciones de la respuesta aceptada (por supuesto).
#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
Un pequeño detalle que otros están omitiendo: el parámetro de time
de MIDIPacketListAdd
es importante para algunas aplicaciones musicales.
Aquí hay un ejemplo de cómo puedes recuperarlo:
#import <mach/mach_time.h>
MIDITimeStamp midiTime = mach_absolute_time();
Fuente: Documentación Apple
Y luego, aplicado a los otros ejemplos aquí:
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));