使用CoreMIDI的MIDISend时,是否忽略MIDIPacket时间戳?(Is the MIDIPacket timestamp ignored when using CoreMIDI'

编程入门 行业动态 更新时间:2024-10-26 04:19:35
使用CoreMIDI的MIDISend时,是否忽略MIDIPacket时间戳?(Is the MIDIPacket timestamp ignored when using CoreMIDI's MIDISend?)

我不清楚时间戳字段是否可以用于在将来调度MIDI事件,即在调用MIDISend之后发生。 以下代码尝试每秒安排10个音符和10个音符关闭。 时间戳应该指定该笔记的笔记在笔记开始后的1/10秒处发生,但是在所有我尝试过的硬件或虚拟目标上,看起来所有事件都在MIDISend被调用时立即发送。 这是一般的行为还是做一些MIDI硬件/做一些虚拟MIDI目标支持使用时间戳值正确安排事件? 这是我的代码:

/* send_midi_port.c * Open connection with destination (rather than setting up MIDI source) and send * timestamped messages */ #include <stdlib.h> #include <stdio.h> #include <CoreMIDI.h> #include <HostTime.h> #include <string.h> #include <CoreServices/CoreServices.h> #include <mach/mach.h> #include <mach/mach_time.h> #include <unistd.h> #define STR_BUFSIZE 200 #define ERR_EXIT(x)\ fprintf(stderr,"Error %s\n",x);\ return -1; typedef void (*sig_t) (int); static volatile int done = 0; static mach_timebase_info_data_t sTimebaseInfo; void int_handle(int signum) { done = 1; } char *CFString_strncpy(char *dest, CFStringRef str, size_t n) { CFStringEncoding encoding = kCFStringEncodingUTF8; if (!CFStringGetCString(str, dest, n, encoding)) { dest = NULL; } return dest; } int CFString_cstr_strncmp(CFStringRef s1, char *s2, size_t n) { char buf[STR_BUFSIZE]; if (CFString_strncpy(buf, s1, STR_BUFSIZE) == NULL) { return -2; } return strncmp(buf,s2,n); } /* Print CFString to stdout */ void CFString_printf(CFStringRef str) { char buffer[STR_BUFSIZE]; CFStringEncoding encoding = kCFStringEncodingUTF8; const char *ptr = CFStringGetCStringPtr(str, encoding); if (ptr == NULL) { if (CFStringGetCString(str, buffer, STR_BUFSIZE, encoding)) { ptr = buffer; } } if (ptr) { printf("%s",ptr); } } /* Get MIDI object's name as CFStringRef */ CFStringRef MIDIObjectRef_get_name(MIDIObjectRef obj) { CFStringRef name = NULL; OSStatus err; err = MIDIObjectGetStringProperty(obj,kMIDIPropertyName,&name); if (err) { return NULL; } return name; } UInt64 nano_to_absolute(UInt64 nano) { return nano * sTimebaseInfo.denom / sTimebaseInfo.numer; } int main(int argc, char **argv) { // Get Timebase info (void) mach_timebase_info(&sTimebaseInfo); char desired_dest_[] = "FastTrack Pro"; char *desired_dest; if (argc < 2) { printf("No device specified, using default output %s.\n", desired_dest_); desired_dest = desired_dest_; } else { desired_dest = argv[1]; } ItemCount n_dests = MIDIGetNumberOfDestinations(); int found = 0; MIDIEndpointRef desired_epr; while (n_dests--) { MIDIEndpointRef dest = MIDIGetDestination(n_dests); CFStringRef name = MIDIObjectRef_get_name(dest); if (CFString_cstr_strncmp(name,desired_dest,strlen(desired_dest)) == 0) { printf(" Name of destination %d: ",(int)n_dests); CFString_printf(name); printf("\n"); found = 1; desired_epr = dest; } } if (!found) { printf("Destination %s not found.\n",desired_dest); return -1; } OSStatus result; MIDIClientRef clientref; result = MIDIClientCreate(CFSTR("default"),NULL,NULL,&clientref); if (result < 0 ) { ERR_EXIT("Creating client."); } MIDIPortRef portref; result = MIDIOutputPortCreate(clientref,CFSTR("hiports"),&portref); if (result < 0 ) { ERR_EXIT("Creating port."); } signal(SIGINT,int_handle); while (!done) { // With multiple and time stamps ByteCount mpdsize = sizeof(MIDIPacketList)+sizeof(MIDIPacket)*20; char mpdata[mpdsize]; memset(mpdata,0,mpdsize); MIDIPacketList *midipackets; midipackets = (MIDIPacketList*)mpdata; MIDIPacket *mp; mp = MIDIPacketListInit(midipackets); int n; UInt64 timeNano, timeScale; for (n = 0; n < 20; n += 2) { Byte noteOnData[3] = {0x90,60+(n/2),100}; Byte noteOffData[3] = {0x80,60+(n/2),0}; // on all the devices I tried, the timestamps seem to be ignored mp = MIDIPacketListAdd(midipackets,mpdsize,mp, nano_to_absolute(((UInt64)n/2) * 100ULL * 1000ULL * 1000ULL), // seems equivalent to 0 // 0, 3,noteOnData); if (!mp) { ERR_EXIT("Adding MIDI packet.\n"); } mp = MIDIPacketListAdd(midipackets,mpdsize,mp, nano_to_absolute((((UInt64)n/2)+1) * 100ULL * 1000ULL * 1000ULL), // seems equivalent to 0 // 0, 3,noteOffData); if (!mp) { ERR_EXIT("Adding MIDI packet.\n"); } } // Check packets printf("Number of packets: %d\n",(int)midipackets->numPackets); MIDIPacket *__p = &midipackets->packet[0]; int i; for (i = 0; i < midipackets->numPackets; ++i) { printf("Timestamp: %llu\n" "Length: %d\n" "Data : ", __p->timeStamp, (int)__p->length); int j; for (j = 0; j < __p->length; j++) { printf("%d ",(int)__p->data[j]); } printf("\n"); __p = MIDIPacketNext(__p); } printf("Sending notes\n"); MIDISend(portref,desired_epr,midipackets); sleep(1); } MIDIPortDispose(portref); MIDIClientDispose(clientref); return 0; }

这可以使用该命令来构建(您的计算机上的框架路径可能不同)

clang send_midi_port.c -o send_midi_port.bin - I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/CoreMIDI.framework/Headers -framework CoreMIDI -g -framework CoreFoundation -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/CoreAudio.framework/Headers -framework CoreAudio -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/CoreServices.framework/Headers -framework CoreServices

It is unclear to me whether the timestamp field can be used to schedule MIDI events in the future, i.e., to happen after the call to MIDISend. The following code attempts to schedule 10 note ons and 10 note offs every second. The timestamps should specify that the note's note off happen 1/10th of a second after the the note on, however on all the hardware or virtual destinations I have tried, it seems all the events are sent immediately when MIDISend is called. Is this the general behaviour or does some MIDI hardware / do some virtual MIDI destinations support the the proper scheduling of events using the timestamp value? Here is my code:

/* send_midi_port.c * Open connection with destination (rather than setting up MIDI source) and send * timestamped messages */ #include <stdlib.h> #include <stdio.h> #include <CoreMIDI.h> #include <HostTime.h> #include <string.h> #include <CoreServices/CoreServices.h> #include <mach/mach.h> #include <mach/mach_time.h> #include <unistd.h> #define STR_BUFSIZE 200 #define ERR_EXIT(x)\ fprintf(stderr,"Error %s\n",x);\ return -1; typedef void (*sig_t) (int); static volatile int done = 0; static mach_timebase_info_data_t sTimebaseInfo; void int_handle(int signum) { done = 1; } char *CFString_strncpy(char *dest, CFStringRef str, size_t n) { CFStringEncoding encoding = kCFStringEncodingUTF8; if (!CFStringGetCString(str, dest, n, encoding)) { dest = NULL; } return dest; } int CFString_cstr_strncmp(CFStringRef s1, char *s2, size_t n) { char buf[STR_BUFSIZE]; if (CFString_strncpy(buf, s1, STR_BUFSIZE) == NULL) { return -2; } return strncmp(buf,s2,n); } /* Print CFString to stdout */ void CFString_printf(CFStringRef str) { char buffer[STR_BUFSIZE]; CFStringEncoding encoding = kCFStringEncodingUTF8; const char *ptr = CFStringGetCStringPtr(str, encoding); if (ptr == NULL) { if (CFStringGetCString(str, buffer, STR_BUFSIZE, encoding)) { ptr = buffer; } } if (ptr) { printf("%s",ptr); } } /* Get MIDI object's name as CFStringRef */ CFStringRef MIDIObjectRef_get_name(MIDIObjectRef obj) { CFStringRef name = NULL; OSStatus err; err = MIDIObjectGetStringProperty(obj,kMIDIPropertyName,&name); if (err) { return NULL; } return name; } UInt64 nano_to_absolute(UInt64 nano) { return nano * sTimebaseInfo.denom / sTimebaseInfo.numer; } int main(int argc, char **argv) { // Get Timebase info (void) mach_timebase_info(&sTimebaseInfo); char desired_dest_[] = "FastTrack Pro"; char *desired_dest; if (argc < 2) { printf("No device specified, using default output %s.\n", desired_dest_); desired_dest = desired_dest_; } else { desired_dest = argv[1]; } ItemCount n_dests = MIDIGetNumberOfDestinations(); int found = 0; MIDIEndpointRef desired_epr; while (n_dests--) { MIDIEndpointRef dest = MIDIGetDestination(n_dests); CFStringRef name = MIDIObjectRef_get_name(dest); if (CFString_cstr_strncmp(name,desired_dest,strlen(desired_dest)) == 0) { printf(" Name of destination %d: ",(int)n_dests); CFString_printf(name); printf("\n"); found = 1; desired_epr = dest; } } if (!found) { printf("Destination %s not found.\n",desired_dest); return -1; } OSStatus result; MIDIClientRef clientref; result = MIDIClientCreate(CFSTR("default"),NULL,NULL,&clientref); if (result < 0 ) { ERR_EXIT("Creating client."); } MIDIPortRef portref; result = MIDIOutputPortCreate(clientref,CFSTR("hiports"),&portref); if (result < 0 ) { ERR_EXIT("Creating port."); } signal(SIGINT,int_handle); while (!done) { // With multiple and time stamps ByteCount mpdsize = sizeof(MIDIPacketList)+sizeof(MIDIPacket)*20; char mpdata[mpdsize]; memset(mpdata,0,mpdsize); MIDIPacketList *midipackets; midipackets = (MIDIPacketList*)mpdata; MIDIPacket *mp; mp = MIDIPacketListInit(midipackets); int n; UInt64 timeNano, timeScale; for (n = 0; n < 20; n += 2) { Byte noteOnData[3] = {0x90,60+(n/2),100}; Byte noteOffData[3] = {0x80,60+(n/2),0}; // on all the devices I tried, the timestamps seem to be ignored mp = MIDIPacketListAdd(midipackets,mpdsize,mp, nano_to_absolute(((UInt64)n/2) * 100ULL * 1000ULL * 1000ULL), // seems equivalent to 0 // 0, 3,noteOnData); if (!mp) { ERR_EXIT("Adding MIDI packet.\n"); } mp = MIDIPacketListAdd(midipackets,mpdsize,mp, nano_to_absolute((((UInt64)n/2)+1) * 100ULL * 1000ULL * 1000ULL), // seems equivalent to 0 // 0, 3,noteOffData); if (!mp) { ERR_EXIT("Adding MIDI packet.\n"); } } // Check packets printf("Number of packets: %d\n",(int)midipackets->numPackets); MIDIPacket *__p = &midipackets->packet[0]; int i; for (i = 0; i < midipackets->numPackets; ++i) { printf("Timestamp: %llu\n" "Length: %d\n" "Data : ", __p->timeStamp, (int)__p->length); int j; for (j = 0; j < __p->length; j++) { printf("%d ",(int)__p->data[j]); } printf("\n"); __p = MIDIPacketNext(__p); } printf("Sending notes\n"); MIDISend(portref,desired_epr,midipackets); sleep(1); } MIDIPortDispose(portref); MIDIClientDispose(clientref); return 0; }

This can be built with the command (the paths to the frameworks might be different on your machine)

clang send_midi_port.c -o send_midi_port.bin - I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/CoreMIDI.framework/Headers -framework CoreMIDI -g -framework CoreFoundation -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/CoreAudio.framework/Headers -framework CoreAudio -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/CoreServices.framework/Headers -framework CoreServices

最满意答案

当您调用MIDIPacketListAdd ,您没有提供正确的时间戳。 您正在从当前时间传递一个偏移量,从第一个事件的0开始,并随着后续事件的增加而增加。 但是你不包括当前时间。

因此,CoreMIDI认为时间戳过去了,所以它立即发送事件。

使用AudioGetCurrentHostTime获取当前时间,并将每个偏移添加到该时间。

UInt64 now = AudioGetCurrentHostTime(); for (n = 0; n < 20; n += 2) { Byte noteOnData[3] = {0x90,60+(n/2),100}; Byte noteOffData[3] = {0x80,60+(n/2),0}; mp = MIDIPacketListAdd(midipackets,mpdsize,mp, now + nano_to_absolute(((UInt64)n/2) * 100ULL * 1000ULL * 1000ULL), 3,noteOnData); mp = MIDIPacketListAdd(midipackets,mpdsize,mp, now + nano_to_absolute((((UInt64)n/2)+1) * 100ULL * 1000ULL * 1000ULL), 3,noteOffData); }

当您使用MIDISend ,无论最终设备是MIDI硬件还是其他应用程序拥有的虚拟MIDI目标,CoreMIDI都会处理调度。

When you call MIDIPacketListAdd, you are not supplying a correct timestamp. You are passing in an offset from the current time, starting at 0 for the first event, and increasing with later events. But you are not including the current time.

As a result, CoreMIDI thinks the timestamps are in the past, so it sends the events immediately.

Use AudioGetCurrentHostTime to get the current time, and add each offset to that.

UInt64 now = AudioGetCurrentHostTime(); for (n = 0; n < 20; n += 2) { Byte noteOnData[3] = {0x90,60+(n/2),100}; Byte noteOffData[3] = {0x80,60+(n/2),0}; mp = MIDIPacketListAdd(midipackets,mpdsize,mp, now + nano_to_absolute(((UInt64)n/2) * 100ULL * 1000ULL * 1000ULL), 3,noteOnData); mp = MIDIPacketListAdd(midipackets,mpdsize,mp, now + nano_to_absolute((((UInt64)n/2)+1) * 100ULL * 1000ULL * 1000ULL), 3,noteOffData); }

When you use MIDISend, CoreMIDI will handle the scheduling, regardless of whether the end device is MIDI hardware, or a virtual MIDI destination owned by some other app.

更多推荐

本文发布于:2023-08-07 08:14:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1463685.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:时间   MIDISend   CoreMIDI   timestamp   MIDIPacket

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!