我正在开发一个小程序来监控NI Maschine 2的midi输出。(想法是将程序更改消息添加到NI机器中的某些部件并使用它们来触发VJ效应和其他一些东西)
以下测试用例给出了问题。 NI机器中的测试部分在第一拍上有一个音符和两个程序变更信息,在第16拍后有第二个音符。
当我点击开始并使用MIDI监视器工具捕获输出时,我看到:
这是对的。正是Maschine正在发送的东西。 - 注意两个Control Changes和下一个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监听器的输出:
从我的代码输出:
没有解决方案,但现在收到的时钟具有相同的时间戳。
此测试中的速度设置为50 BPM(每50 ms一个时钟) 我检查了是否有丢失的时钟脉冲但事实并非如此。收到的所有时钟都大约是预期的时间戳。
答案 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)让我走上正轨。
对于这个测试,我在第一个条上放置了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