我正在尝试通过按特定命令的长度推进数据包索引来使我的midi输入循环处理在单个数据包中发送的多个消息。我在单个数据包中收到多个midi消息,这是尝试从下一个消息而不是下一个数据包启动循环。
它不是从下一条消息开始,而是在下一个数据包到达之前跳过剩余的数据包内容并丢失所有消息。您可以看到底部附近我已经将命令长度添加到packetContentsIndex以尝试让循环从下一条消息重新开始,但它不起作用。任何人都可以看到我搞砸了吗?
- (void) midiInputThreadProc:(id)arg
{
@autoreleasepool {
unsigned int maxPacketLength = 0x100;
unsigned char* data = malloc(maxPacketLength);
UInt16 length;
while (semaphore_wait(midiReceivedSemaphore) == KERN_SUCCESS) {
midi_packet_buffer_next_packet_length(midiPacketBuffer, &length);
if( length > 0) {
length = midi_packet_buffer_read(midiPacketBuffer, data, length);
for (unsigned int packetContentsIndex = 0; packetContentsIndex < length; packetContentsIndex++) {
Byte command = data[packetContentsIndex+0];
switch (command){
case MIDI_NOTEON:
commandLength = 3;
{//sending midi message to delegate in here}
break;
case MIDI_CONTROLCHANGE:
commandLength = 3;
{//sending midi message to delegate in here}
break;
case MIDI_SYSEX:
commandLength = length;
{//sending midi message to delegate in here}
break;
}
packetContentsIndex += commandLength;
}
}
else{
break;
}
}
free(data);
}
}
答案 0 :(得分:2)
每次循环时,您都会将packetContentsIndex递增1,而对于音符开启和控制消息,也递增3。这会导致你将packetContentsIndex增加4个字节,但是一个音符消息只有3个字节长。
做这样的事可能更清楚:
unsigned int packetContentsIndex = 0;
while (packetContentsIndex < length) {
Byte command = data[packetContentsIndex]; // No reason to say +0
unsigned int commandLength = 1; // the byte we're looking at now
switch (command) {
case MIDI_NOTEON:
commandLength += 2; // 2 additional bytes after the 1 you just saw
break;
case MIDI_CONTROLCHANGE:
commandLength += 2;
break;
}
packetContentsIndex += commandLength;
}
但是,这仍然是解析MIDI的一种非常天真的方式。就在我的头顶:
您无法使用简单的大小写比较来查找音符开启和控制变更消息,因为频道编号包含在该字节的后4位中。在比较之前,您需要屏蔽该部分。
您可能会收到速度为0的音符消息,而不是正确的音符消息,应将其视为注释消息。
您可能会在任何消息中间散布其他单字节实时消息(如MIDI时钟),甚至是简单的双字节或三字节消息,尤其是sysex消息。
< / LI>一旦sysex消息启动,您就无法保证它将在当前数据包的末尾完成。它可能跨越多个数据包或数据包列表,它可能非常大(需要几秒或几分钟才能传输),甚至可能不会以正确的F7字节结束。