2016-11-22 46 views
0

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

/* 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 

回答

0

当你调用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,CoreMIDI将处理调度,无论终端装置是否在MIDI硬件,或其他一些应用程序拥有的虚拟MIDI目的地。

+0

这可行,但文档是误导。对于MIDIReceived,它表示:_Unlike MIDISend(),0的时间戳不等于“now”;驱动程序或虚拟源负责将正确的时间戳放入数据包中,这意味着对于MIDISend 0意味着“现在”,这显然不正确。 – mondaugen

+0

它的意思是“现在”。如果您传递零时间戳,事件将尽快发送。你有否观察过这总是我见过的行为。我看到你的第一个事件(时间戳0)立即被发送......只是其他事件与其他时间戳也被立即发送。 –

+0

是的,因为我的示例代码不起作用。如果它确实意味着“现在”,那么我的代码将工作,因为现在*/* nano_to_absolute(((UInt64)n/2)* 100ULL * 1000ULL * 1000ULL)'应该发送MIDI消息,在几纳秒未来。 0实际上意味着“迟到”,因为即使添加一些不等于最近调用AudioGetCurrentHostTime()(我只观察到返回非零)的数字仍然立即播放该消息。我当然假设时间戳0被解释为一个时间而不是一个标志,意思是“立即发送”。它似乎被解释为一面旗帜。 – mondaugen

相关问题