2016-11-22 6 views
0

Es ist mir unklar, ob das Timestamp-Feld verwendet werden kann, um MIDI-Ereignisse in der Zukunft zu planen, d. H. Nach dem Aufruf von MIDISend. Der folgende Code versucht, jede Sekunde 10 Note Ons und 10 Note Offs zu planen. Die Zeitstempel sollten angeben, dass der Note Note Off eine Zehntelsekunde nach der Note ausgeführt wird, aber auf allen Hardware- oder virtuellen Zielen, die ich versucht habe, scheint es, dass alle Events sofort gesendet werden, wenn MIDISend aufgerufen wird. Ist dies das allgemeine Verhalten oder unterstützt eine gewisse MIDI-Hardware/unterstützen einige virtuelle MIDI-Ziele die korrekte Planung von Ereignissen unter Verwendung des Zeitstempelwerts? Hier ist mein Code:Wird der MIDIPacket-Zeitstempel ignoriert, wenn CoreMIDIs MIDISend verwendet wird?

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

Diese mit dem Befehl gebaut werden kann (die Pfade zu den Rahmenbedingungen könnte auf Ihrem Computer unterschiedlich sein)

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 

Antwort

0

Wenn Sie MIDIPacketListAdd rufen Sie liefern keine korrekte Zeitstempel. Sie geben einen Offset von der aktuellen Zeit ein, beginnend mit 0 für das erste Ereignis, und erhöhen mit späteren Ereignissen. Aber Sie berücksichtigen nicht die aktuelle Zeit.

Daher denkt CoreMIDI, dass die Zeitstempel in der Vergangenheit liegen, und sendet die Ereignisse sofort.

Verwenden Sie AudioGetCurrentHostTime, um die aktuelle Uhrzeit abzurufen, und fügen Sie jeden Offset hinzu.

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); 
    } 

Wenn Sie MIDISend verwenden, Coremidi die Planung behandeln, unabhängig davon, ob das Endgerät ist MIDI-Hardware oder ein virtuelles MIDI-Ziel durch eine andere App gehörte.

+0

Das funktioniert, aber die Dokumentation ist irreführend. Für MIDIRempfangen heißt es: _Im Gegensatz zu MIDISend() ist ein Zeitstempel von 0 nicht äquivalent zu "jetzt"; der Treiber oder die virtuelle Quelle ist dafür verantwortlich, richtige Zeitstempel in die Pakete zu schreiben, was bedeutet, dass für MIDISend 0 "jetzt" bedeutet, was eindeutig nicht wahr ist. – mondaugen

+0

Es bedeutet "jetzt". Wenn Sie einen Null-Zeitstempel übergeben, wird das Ereignis so schnell wie möglich gesendet. Hast du das anders beobachtet? Das ist immer das Verhalten, das ich gesehen habe. Ich habe gesehen, dass dein erstes Event (mit Timestamp 0) sofort gesendet wurde ... es ist nur so, dass die anderen Events mit anderen Timestamps * auch * sofort gesendet wurden. –

+0

Ja, weil mein Beispielcode nicht funktioniert. Wenn es tatsächlich "jetzt" bedeutet, dann würde mein Code funktionieren, weil '0/* jetzt */+ nano_to_absolute (((UInt64) n/2) * 100ULL * 1000ULL * 1000ULL)' die MIDI-Nachricht senden sollte, die viele Nanosekunden in der Zukunft. 0 bedeutet eigentlich "spät", weil sogar das Hinzufügen einer Nummer, die nicht dem letzten Aufruf von AudioGetCurrentHostTime() entspricht (die ich nur beobachtet habe, dass sie nicht Null ist), die Nachricht sofort abspielt. Ich nehme natürlich an, dass ein Zeitstempel von 0 als eine Zeit interpretiert wird und nicht als eine Flagge, die "Jetzt senden" bedeutet. Es scheint als eine Flagge interpretiert zu werden. – mondaugen

Verwandte Themen