Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't this simple CoreMIDI program produce MIDI output?

Here is an extremely simple CoreMIDI OS X application that sends MIDI data. The problem is that it doesn't work. It compiles fine, and runs. It reports no errors, and does not crash. The Source created becomes visible in MIDI Monitor. However, no MIDI data comes out.

Could somebody let me know what I'm doing wrong here?

#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;
}
like image 391
sixohsix Avatar asked May 13 '12 15:05

sixohsix


3 Answers

I'm just leaving this here for my own reference. It's a full example based 100% on yours, but including the other side (receiving), my bad C code and the accepted answer's corrections (of course).

#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
like image 37
Dan Rosenstark Avatar answered Nov 06 '22 00:11

Dan Rosenstark


You're calling MIDISourceCreate to create a virtual MIDI source.

This means that your source will appear in other apps' MIDI setup UI, and that those apps can choose whether or not to listen to your source. Your MIDI will not get sent to any physical MIDI ports, unless some other app happens to channel it there. It also means that your app has no choice as to where the MIDI it's sending goes. I'm assuming that's what you want.

The documentation for MIDISourceCreate says:

After creating a virtual source, use MIDIReceived to transmit MIDI messages from your virtual source to any clients connected to the virtual source.

So, do two things:

  • Remove the code that creates the output port. You don't need it.
  • change MIDISend(outPort, midiOut, pktList) to: MIDIReceived(midiOut, pktlist).

That should solve your problem.

So what are output ports good for? If you wanted to direct your MIDI data to a specific destination -- maybe a physical MIDI port -- you would NOT create a virtual MIDI source. Instead:

  1. Call MIDIOutputPortCreate() to make an output port
  2. Use MIDIGetNumberOfDestinations() and MIDIGetDestination() to get the list of destinations and find the one you're interested in.
  3. To send MIDI to one destination, call MIDISend(outputPort, destination, packetList).
like image 127
Kurt Revis Avatar answered Nov 06 '22 02:11

Kurt Revis


A little detail that others are skipping: the time parameter of MIDIPacketListAdd is important for some musical apps.

Here is an example of how you can retrieve it:

#import <mach/mach_time.h>
MIDITimeStamp midiTime = mach_absolute_time();

Source: Apple Documentation

And then, applied to the other examples here:

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, sizeof(midiDataToSend), midiDataToSend);
like image 1
gog Avatar answered Nov 06 '22 02:11

gog