Question

My iOS app sends MIDI bank and program changes to other devices using PGMidi, a Core MIDI library. Some users have reported that the messages sometimes arrive in the wrong order -- the program change followed by the bank change.

For each bank/program change, I'm assembling an NSArray of numeric values, then passing that array to a sendMidiDataInBackground method in a background thread:

int MSBStatus = 0xB0;
int MSBController = 0;
int MSBValue = 1;
NSArray *MSBValues = [NSArray arrayWithObjects:[NSNumber numberWithInt:MSBStatus], [NSNumber numberWithInt:MSBController], [NSNumber numberWithInt:MSBValue], nil];
[self performSelectorInBackground:@selector(sendMidiDataInBackground:) withObject:MSBValues];

int LSBStatus = 0xB0;
int LSBController = 32;
int LSBValue = 2;
NSArray *LSBValues = [NSArray arrayWithObjects:[NSNumber numberWithInt:LSBStatus], [NSNumber numberWithInt:LSBController], [NSNumber numberWithInt:LSBValue], nil];
[self performSelectorInBackground:@selector(sendMidiDataInBackground:) withObject:LSBValues];

int programStatus = 0xC0;
int programValue = 3
NSArray *programValues = [NSArray arrayWithObjects:[NSNumber numberWithInt:programStatus], [NSNumber numberWithInt:programValue], nil];
[self performSelectorInBackground:@selector(sendMidiDataInBackground:) withObject:programValues];

The sendMidiDataInBackground method changes the values to a C array and passes them on to PGMidi's sendBytes method, which assembles them into a packet list and sends them out via MIDISend. I noticed the timestamps are set to 0, which means "now":

- (void) sendBytes:(const UInt8*)bytes size:(UInt32)size {
    Byte packetBuffer[size+100];
    MIDIPacketList *packetList = (MIDIPacketList*)packetBuffer;
    MIDIPacket     *packet     = MIDIPacketListInit(packetList);
    packet = MIDIPacketListAdd(packetList, sizeof(packetBuffer), packet, 0, size, bytes);
    OSStatus s = MIDISend(midi.outputPort, endpoint, packetList);
}

But at some point apparently some messages are getting delayed so they're ending up in the wrong order. Is this because the background threads don't finish in the order they were started? If so, I could combine the MSB, LSB and program arrays and send the combined array to sendMidiDataInBackground rather than starting three separate threads, and that should solve it.

Or could this be happening after the MIDISend function is called? If so, I'd need another solution.

I can't repeat the problem in my own testing, so I'd like to know for sure what the problem is so I have a better chance of actually fixing it.

Was it helpful?

Solution

According to the documentation, -performSelectorInBackground:withObject: creates a new background thread. You should not make any assumptions about how the OS will schedule those threads. You cannot count on them being run in the order you created them.

What makes you think you need to do this in the background? Sending a few short MIDI messages is unlikely to block your main thread for a noticeable amount of time. Creating three separate threads is an incredible amount of overhead for a tiny amount of work.

If you really need to do it in the background, consider using something like GCD's dispatch_async or an NSOperation with a serial queue, and/or do all your work in one method call rather than three.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top