CoreMIDI:midiReadProc仅接收具有相同时间戳的{3个数据包的第一个数据包

时间:2015-06-02 21:52:38

标签: macos coremidi

我正在开发一个小程序来监控NI Maschine 2的midi输出。(想法是将程序更改消息添加到NI机器中的某些部件并使用它们来触发VJ效应和其他一些东西)

以下测试用例给出了问题。 NI机器中的测试部分在第一拍上有一个音符和两个程序变更信息,在第16拍后有第二个音符。

当我点击开始并使用MIDI监视器工具捕获输出时,我看到: results from MIDI Monitor

这是对的。正是Maschine正在发送的东西。 - 注意两个Control Changes和下一个Note On数据包具有相同的时间戳。

当我对我的简单虚拟客户端执行相同操作时(请参阅下面的代码),我得到: Results from my Virtual Client

  • 您可以看到第二个Control Change和Note On数据包丢失了!

另请注意第二行第三行(歌曲位置和继续)也具有相同的时间戳,并且在两种情况下均已收到。

如果你麻烦地阅读,直到这一点你就会理解这个问题。 我知道我的simpel虚拟客户端和MIDI监视器程序之间的最大区别是使用CoreMidi服务插件来“监视”midi输出。 这只是CoreMidi的限制还是我错过了什么?

在虚拟客户端的代码下面: 它被剥夺了从NI Mashine接收东西的基本需求。 init设置虚拟客户端,目标并设置UniqueID。 readproc正在生成带有丢失数据包的NSLOG消息,如上所示。

非常感谢任何建议

//
//  VirtualClient.m
//  testMidiReadProc
//
//  Created by Rob Keeris on 02/06/15.
//  Copyright (c) 2015 Connector. All rights reserved.
//

#import "VirtualClient.h"

@implementation VirtualClient


SInt32          virtualinUniqueId = 1234567893;
MIDIClientRef   client;
MIDIEndpointRef virtualIn;

NSString * midiTypeName(Byte midiType){
    switch (midiType) {
        case 0x80: return @"Note Off";
        case 0x90: return @"Note On";
        case 0xB0: return @"ControlChange";
        case 0xC0: return @"ProgramChange";
        case 0xF2: return @"SongPosition";
        case 0xF8: return @"Clock";
        case 0xFA: return @"Start";
        case 0xFB: return @"Continue";
        case 0xFC: return @"Stop";
        case 0x00: return @"InvalidType";
        default: return [NSString stringWithFormat:@"Unlisted midiType 0x%02x",midiType];
    }
}

void midiReadProc (const MIDIPacketList *list, void *procRef, void *srcRef) {

    const MIDIPacket *packet = &list->packet[0];  // ?defined as const to avoid compiler warnings?
    for (int i = 0; i < list->numPackets; i++) {

        if (packet->data[0] != 0xF8){ // filter out Clock messages

            NSLog(@"%llu packet(%i of %i) %@(0x%02x) 0x%02x 0x%02x",
                  packet->timeStamp,i+1,list->numPackets,midiTypeName(packet->data[0]),packet->data[0],packet->data[1],packet->data[2]);
        }

        packet = MIDIPacketNext (packet);
    }
}

- (id)init{
    OSStatus result;

    self = [super init];
    if (self) {
        // Create the client
        result = MIDIClientCreate(CFSTR("myVirtualClient"), NULL, NULL, &client);
        if (result !=0) NSLog(@"MIDIClientCreate error %i",result);
        // create the destination
        result = MIDIDestinationCreate(client, CFSTR("myVirtualDestination"), midiReadProc,(__bridge void *)(self),&virtualIn);
        if (result !=0) NSLog(@"MIDIClientCreate error %i",result);
        // set the UniqueId so i dont have to toggele the output in NI Maschine
        result = MIDIObjectSetIntegerProperty(virtualIn, kMIDIPropertyUniqueID, virtualinUniqueId);
        if (result !=0) NSLog(@"MIDIClientCreate error %i",result);

    }
    return self;
}

@end

额外信息 参加这个额外的测试,以回应Gene的提示,看看会发生什么 删除0xF8过滤器并使用printf而不是NSLog();

MIDI监听器的输出: enter image description here

从我的代码输出:

enter image description here

没有解决方案,但现在收到的时钟具有相同的时间戳。

此测试中的速度设置为50 BPM(每50 ms一个时钟) 我检查了是否有丢失的时钟脉冲但事实并非如此。收到的所有时钟都大约是预期的时间戳。

2 个答案:

答案 0 :(得分:0)

这里的小评论。 既然你这样做了:

const MIDIPacket *packet = &list->packet[0];  // ?defined as const to avoid compiler warnings?

for (int i = 0; i < list->numPackets; i++) {

应该是

for (int i = 0; i < list->numPackets; ++i) {

但这并不能解决您的问题。 删除过滤“if”(除了获取大量F8)后会发生什么? 在黑暗中拍摄:尝试printf以查看NSLog是否达不到。

答案 1 :(得分:0)

我想我发现问题是。 数据包不会丢失,但会放在第一个数据包的数据[0]中。 另一篇文章(正确使用midipacketlistadd-coremidi)让我走上正轨。

enter image description here

对于这个测试,我在第一个条上放置了3个音符和2个控制变化信息的和弦,而在第16个条上没有一个控制变化信息和一个控制变化。

好吧,它清楚控制变化包含2个控制变化和3个NoteOn消息。 NoteOf消息包含3 NoteOf。

这是Coremidi中的错误还是另一方程序员的不良行为?

修改 这解决了。 正如Kurt指出的那样正常,你必须为一个数据包中的多个消息做好准备。所以你必须做下面的事情。

感谢所有输入。

#import "VirtualClient.h"

@implementation VirtualClient


SInt32          virtualinUniqueId = 1234567893;
MIDIClientRef   client;
MIDIEndpointRef virtualIn;
MIDITimeStamp previousTimeStamp;

struct MIDIMessage
{
   char* description;
   int   messageLength;
};
typedef struct MIDIMessage MIDIMessage;

// incomplete list of possible messages but suffucient for this test
const MIDIMessage x00 = {"Error! or Unlisted",0};
const MIDIMessage x80 = {"Note Off",3};
const MIDIMessage x90 = {"Note On",3};
const MIDIMessage xB0 = {"ControlChange",3};
const MIDIMessage xC0 = {"ProgramChange",2};
const MIDIMessage xF2 = {"SongPosition",3};
const MIDIMessage xF8 = {"Clock",1};
const MIDIMessage xFA = {"Start",1};
const MIDIMessage xFB = {"Continue",1};
const MIDIMessage xFC = {"Stop",1};

MIDIMessage midiType(Byte midiType){

    switch (midiType) {

        case 0x80: return x80;
        case 0x90: return x90;
        case 0xB0: return xB0;
        case 0xC0: return xC0;

        case 0xF2: return xF2;
        case 0xF8: return xF8;
        case 0xFA: return xFA;
        case 0xFB: return xFB;
        case 0xFC: return xFC;

        default: return x00;
    }

}

void midiReadProc (const MIDIPacketList *list, void *procRef, void *srcRef) {

int index;
int messageLength;
const MIDIPacket *packet = &list->packet[0];

// handle packets
for (int i =0;i< list->numPackets;++i){

    // handle messages in each packet
    index = 0;
    while (index < packet->length){

        messageLength = midiType(packet->data[index]).messageLength;

        if (messageLength){

            printf("%llu packet(%i of %i) %s", packet->timeStamp,i+1,list->numPackets,midiType(packet->data[index]).description);
            for (int x =1; x< messageLength;x++)
                printf(" data[%i]:0x%02x",x,packet->data[index+x]);
            printf("\n");
            index+=messageLength;

        }
        else{

            printf("Unlisted comand! printing rest of bytes");
            for (int i =index; i < packet->length;i++)
                printf("0x%02x ",packet->data[i]);
            printf("\n");
            break;

        }

    }
    packet = MIDIPacketNext (packet);
    }
}

- (id)init{
    OSStatus result;
    self = [super init];
    if (self) {
        // Create the client
        result = MIDIClientCreate(CFSTR("myVirtualClient"), NULL, NULL, &client);
        if (result !=0) NSLog(@"MIDIClientCreate error %i",result);
        // create the destination
        result = MIDIDestinationCreate(client, CFSTR("myVirtualDestination"), midiReadProc,(__bridge void *)(self),&virtualIn);
        if (result !=0) NSLog(@"MIDIClientCreate error %i",result);
        // set the UniqueId so i dont have to toggele the output in NI Maschine
        result = MIDIObjectSetIntegerProperty(virtualIn, kMIDIPropertyUniqueID, virtualinUniqueId);
     if (result !=0) NSLog(@"MIDIClientCreate error %i",result);

    }
    return self;
}

@end