1 /*
   2  * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27 **
  28 **    Overview:
  29 **      Implementation of the functions used for both MIDI in and MIDI out.
  30 **
  31 **      Java package com.sun.media.sound defines the AbstractMidiDevice class
  32 **      which encapsulates functionalities shared by both MidiInDevice and
  33 **      MidiOutDevice classes in the same package.
  34 **
  35 **      The Java layer classes MidiInDevice and MidiOutDevice in turn map to
  36 **      the MIDIEndpointRef data type in the CoreMIDI framework, which
  37 **      represents a source or destination for a standard 16-channel MIDI data
  38 **      stream.
  39 */
  40 /*****************************************************************************/
  41 
  42 //#define USE_ERROR
  43 //#define USE_TRACE
  44 
  45 #if (USE_PLATFORM_MIDI_IN == TRUE) || (USE_PLATFORM_MIDI_OUT == TRUE)
  46 
  47 #include "PLATFORM_API_MacOSX_MidiUtils.h"
  48 #include <pthread.h>
  49 #include <assert.h>
  50 
  51 // Constant character string definitions of CoreMIDI's corresponding error codes.
  52 
  53 static const char* strMIDIInvalidClient =
  54                         "An invalid MIDIClientRef was passed.";
  55 static const char* strMIDIInvalidPort =
  56                         "An invalid MIDIPortRef was passed.";
  57 static const char* strMIDIWrongEndpointType =
  58                         "A source endpoint was passed to a function expecting a destination, or vice versa.";
  59 static const char* strMIDINoConnection =
  60                         "Attempt to close a non-existant connection.";
  61 static const char* strMIDIUnknownEndpoint =
  62                         "An invalid MIDIEndpointRef was passed.";
  63 static const char* strMIDIUnknownProperty =
  64                         "Attempt to query a property not set on the object.";
  65 static const char* strMIDIWrongPropertyType =
  66                         "Attempt to set a property with a value not of the correct type.";
  67 static const char* strMIDINoCurrentSetup =
  68                         "Internal error; there is no current MIDI setup object.";
  69 static const char* strMIDIMessageSendErr =
  70                         "Communication with MIDIServer failed.";
  71 static const char* strMIDIServerStartErr =
  72                         "Unable to start MIDIServer.";
  73 static const char* strMIDISetupFormatErr =
  74                         "Unable to read the saved state.";
  75 static const char* strMIDIWrongThread =
  76                         "A driver is calling a non-I/O function in the server from a thread other than"
  77                         "the server's main thread.";
  78 static const char* strMIDIObjectNotFound =
  79                         "The requested object does not exist.";
  80 static const char* strMIDIIDNotUnique =
  81                         "Attempt to set a non-unique kMIDIPropertyUniqueID on an object.";
  82 
  83 static const char* midi_strerror(int err) {
  84 /*
  85     @enum           Error Constants
  86     @abstract       The error constants unique to Core MIDI.
  87     @discussion     These are the error constants that are unique to Core MIDI. Note that Core MIDI
  88                     functions may return other codes that are not listed here.
  89 */
  90     const char* strerr;
  91 
  92     switch (err) {
  93     case kMIDIInvalidClient:
  94         strerr = strMIDIInvalidClient;
  95         break;
  96     case kMIDIInvalidPort:
  97         strerr = strMIDIInvalidPort;
  98         break;
  99     case kMIDIWrongEndpointType:
 100         strerr = strMIDIWrongEndpointType;
 101         break;
 102     case kMIDINoConnection:
 103         strerr = strMIDINoConnection;
 104         break;
 105     case kMIDIUnknownEndpoint:
 106         strerr = strMIDIUnknownEndpoint;
 107         break;
 108     case kMIDIUnknownProperty:
 109         strerr = strMIDIUnknownProperty;
 110         break;
 111     case kMIDIWrongPropertyType:
 112         strerr = strMIDIWrongPropertyType;
 113         break;
 114     case kMIDINoCurrentSetup:
 115         strerr = strMIDINoCurrentSetup;
 116         break;
 117     case kMIDIMessageSendErr:
 118         strerr = strMIDIMessageSendErr;
 119         break;
 120     case kMIDIServerStartErr:
 121         strerr = strMIDIServerStartErr;
 122         break;
 123     case kMIDISetupFormatErr:
 124         strerr = strMIDISetupFormatErr;
 125         break;
 126     case kMIDIWrongThread:
 127         strerr = strMIDIWrongThread;
 128         break;
 129     case kMIDIObjectNotFound:
 130         strerr = strMIDIObjectNotFound;
 131         break;
 132     case kMIDIIDNotUnique:
 133         strerr = strMIDIIDNotUnique;
 134         break;
 135     default:
 136         strerr = "Unknown error.";
 137         break;
 138     }
 139     return strerr;
 140 }
 141 
 142 const char* MIDI_Utils_GetErrorMsg(int err) {
 143     return midi_strerror(err);
 144 }
 145 
 146 
 147 void MIDI_Utils_PrintError(int err) {
 148 #ifdef USE_ERROR
 149     const char* s = MIDI_Utils_GetErrorMsg(err);
 150     if (s != NULL) {
 151         fprintf(stderr, "%s\n", s);
 152     }
 153 #endif
 154 }
 155 
 156 
 157 // Note direction is either MIDI_IN or MIDI_OUT.
 158 INT32 MIDI_Utils_GetNumDevices(int direction) {
 159     int num_endpoints;
 160     if (direction == MIDI_IN) {
 161         num_endpoints = MIDIGetNumberOfSources();
 162     //fprintf(stdout, "MIDIGetNumberOfSources() returns %d\n", num_endpoints);
 163     } else if (direction == MIDI_OUT) {
 164         num_endpoints = MIDIGetNumberOfDestinations();
 165         //printf(stdout, "MIDIGetNumberOfDestinations() returns %d\n", num_endpoints);
 166     } else {
 167         assert((direction == MIDI_IN || direction == MIDI_OUT));
 168         num_endpoints = 0;
 169     }
 170     return (INT32) num_endpoints;
 171 }
 172 
 173 // Wraps calls to CFStringGetCStringPtr and CFStringGetCString to make sure
 174 // we extract the c characters into the buffer and null-terminate it.
 175 static void CFStringExtractCString(CFStringRef cfs, char* buffer, UINT32 bufferSize, CFStringEncoding encoding) {
 176     const char* ptr = CFStringGetCStringPtr(cfs, encoding);
 177     if (ptr) {
 178         strlcpy(buffer, ptr, bufferSize);
 179     } else {
 180         if (! CFStringGetCString(cfs, buffer, bufferSize, encoding)) {
 181             // There's an error in conversion, make sure we null-terminate the buffer.
 182             buffer[bufferSize - 1] = '\0';
 183         }
 184     }
 185 }
 186 
 187 //
 188 // @see com.sun.media.sound.AbstractMidiDeviceProvider.getDeviceInfo().
 189 static int getEndpointProperty(int direction, INT32 deviceID, char *buffer, int bufferLength, CFStringRef propertyID) {
 190 
 191     if (deviceID < 0) {
 192         return MIDI_INVALID_DEVICEID;
 193     }
 194 
 195     MIDIEndpointRef endpoint;
 196 
 197     if (direction == MIDI_IN) {
 198         endpoint = MIDIGetSource(deviceID);
 199     } else if (direction == MIDI_OUT) {
 200         endpoint = MIDIGetDestination(deviceID);
 201     } else {
 202         return MIDI_INVALID_ARGUMENT;
 203     }
 204 
 205     if (!endpoint) {
 206         return MIDI_INVALID_DEVICEID;
 207     }
 208 
 209     int status = MIDI_SUCCESS;
 210     if (propertyID == kMIDIPropertyDriverVersion) {
 211         SInt32 driverVersion;
 212         status = MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyDriverVersion, &driverVersion);
 213         if (status != MIDI_SUCCESS) return status;
 214         snprintf(buffer,
 215                  bufferLength,
 216                  "%d",
 217                  (int) driverVersion);
 218     }
 219     else {
 220         CFStringRef pname;
 221         status = MIDIObjectGetStringProperty(endpoint, propertyID, &pname);
 222         if (status != MIDI_SUCCESS) return status;
 223         CFStringExtractCString(pname, buffer, bufferLength, 0);
 224     }
 225     return MIDI_ERROR_NONE;
 226 }
 227 
 228 // A simple utility which encapsulates CoreAudio's HostTime APIs.
 229 // It returns the current host time in nanoseconds which when subtracted from
 230 // a previous getCurrentTimeInNanos() result produces the delta in nanos.
 231 static UInt64 getCurrentTimeInNanos() {
 232     UInt64 hostTime = AudioGetCurrentHostTime();
 233     UInt64 nanos = AudioConvertHostTimeToNanos(hostTime);
 234     return nanos;
 235 }
 236 
 237 
 238 INT32 MIDI_Utils_GetDeviceName(int direction, INT32 deviceID, char *name, UINT32 bufferLength) {
 239     return getEndpointProperty(direction, deviceID, name, bufferLength, kMIDIPropertyName);
 240 }
 241 
 242 
 243 INT32 MIDI_Utils_GetDeviceVendor(int direction, INT32 deviceID, char *name, UINT32 bufferLength) {
 244     return getEndpointProperty(direction, deviceID, name, bufferLength, kMIDIPropertyManufacturer);
 245 }
 246 
 247 
 248 INT32 MIDI_Utils_GetDeviceDescription(int direction, INT32 deviceID, char *name, UINT32 bufferLength) {
 249     return getEndpointProperty(direction, deviceID, name, bufferLength, kMIDIPropertyDisplayName);
 250 }
 251 
 252 
 253 INT32 MIDI_Utils_GetDeviceVersion(int direction, INT32 deviceID, char *name, UINT32 bufferLength) {
 254     return getEndpointProperty(direction, deviceID, name, bufferLength, kMIDIPropertyDriverVersion);
 255 }
 256 
 257 
 258 static MIDIClientRef client = (MIDIClientRef) NULL;
 259 static MIDIPortRef inPort = (MIDIPortRef) NULL;
 260 static MIDIPortRef outPort = (MIDIPortRef) NULL;
 261 
 262 // Each MIDIPacket can contain more than one midi messages.
 263 // This function processes the packet and adds the messages to the specified message queue.
 264 // @see also src/share/native/com/sun/media/sound/PlatformMidi.h.
 265 static void processMessagesForPacket(const MIDIPacket* packet, MacMidiDeviceHandle* handle) {
 266     const UInt8* data;
 267     UInt16 length;
 268     UInt8 byte;
 269     UInt8 pendingMessageStatus;
 270     UInt8 pendingData[2];
 271     UInt16 pendingDataIndex, pendingDataLength;
 272     UINT32 packedMsg;
 273     MIDITimeStamp ts = packet->timeStamp;
 274 
 275     pendingMessageStatus = 0;
 276     pendingDataIndex = pendingDataLength = 0;
 277 
 278     data = packet->data;
 279     length = packet->length;
 280     while (length--) {
 281         bool byteIsInvalid = FALSE;
 282 
 283         byte = *data++;
 284         packedMsg = byte;
 285 
 286         if (byte >= 0xF8) {
 287             // Each RealTime Category message (ie, Status of 0xF8 to 0xFF) consists of only 1 byte, the Status.
 288             // Except that 0xFD is an invalid status code.
 289             //
 290             // 0xF8 -> Midi clock
 291             // 0xF9 -> Midi tick
 292             // 0xFA -> Midi start
 293             // 0xFB -> Midi continue
 294             // 0xFC -> Midi stop
 295             // 0xFE -> Active sense
 296             // 0xFF -> Reset
 297             if (byte == 0xFD) {
 298                 byteIsInvalid = TRUE;
 299             } else {
 300                 pendingDataLength = 0;
 301             }
 302         } else {
 303             if (byte < 0x80) {
 304                 // Not a status byte -- check our history.
 305                 if (handle->readingSysExData) {
 306                     CFDataAppendBytes(handle->readingSysExData, &byte, 1);
 307 
 308                 } else if (pendingDataIndex < pendingDataLength) {
 309                     pendingData[pendingDataIndex] = byte;
 310                     pendingDataIndex++;
 311 
 312                     if (pendingDataIndex == pendingDataLength) {
 313                         // This message is now done -- do the final processing.
 314                         if (pendingDataLength == 2) {
 315                             packedMsg = pendingMessageStatus | pendingData[0] << 8 | pendingData[1] << 16;
 316                         } else if (pendingDataLength == 1) {
 317                             packedMsg = pendingMessageStatus | pendingData[0] << 8;
 318                         } else {
 319                             fprintf(stderr, "%s: %d->internal error: pendingMessageStatus=0x%X, pendingDataLength=%d\n",
 320                                     __FILE__, __LINE__, pendingMessageStatus, pendingDataLength);
 321                             byteIsInvalid = TRUE;
 322                         }
 323                         pendingDataLength = 0;
 324                     }
 325                 } else {
 326                     // Skip this byte -- it is invalid.
 327                     byteIsInvalid = TRUE;
 328                 }
 329             } else {
 330                 if (handle->readingSysExData /* && (byte == 0xF7) */) {
 331                     // We have reached the end of system exclusive message -- send it finally.
 332                     const UInt8* bytes = CFDataGetBytePtr(handle->readingSysExData);
 333                     CFIndex size = CFDataGetLength(handle->readingSysExData);
 334                     MIDI_QueueAddLong(handle->h.queue,
 335                                       (UBYTE*) bytes,
 336                                       (UINT32) size,
 337                                       0, // Don't care, windowish porting only.
 338                                       (INT64) (AudioConvertHostTimeToNanos(ts) + 500) / 1000,
 339                                       TRUE);
 340                     CFRelease(handle->readingSysExData);
 341                     handle->readingSysExData = NULL;
 342                 }
 343 
 344                 pendingMessageStatus = byte;
 345                 pendingDataLength = 0;
 346                 pendingDataIndex = 0;
 347 
 348                 switch (byte & 0xF0) {
 349                     case 0x80:    // Note off
 350                     case 0x90:    // Note on
 351                     case 0xA0:    // Aftertouch
 352                     case 0xB0:    // Controller
 353                     case 0xE0:    // Pitch wheel
 354                         pendingDataLength = 2;
 355                         break;
 356 
 357                     case 0xC0:    // Program change
 358                     case 0xD0:    // Channel pressure
 359                         pendingDataLength = 1;
 360                         break;
 361 
 362                     case 0xF0: {
 363                         // System common message
 364                         switch (byte) {
 365                         case 0xF0:
 366                             // System exclusive
 367                             // Allocates a CFMutableData reference to accumulate the SysEx data until EOX (0xF7) is reached.
 368                             handle->readingSysExData = CFDataCreateMutable(NULL, 0);
 369                             break;
 370 
 371                         case 0xF7:
 372                             // System exclusive ends--already handled above.
 373                             // But if this is showing up outside of sysex, it's invalid.
 374                             byteIsInvalid = TRUE;
 375                             break;
 376 
 377                         case 0xF1:    // MTC quarter frame message
 378                         case 0xF3:    // Song select
 379                             pendingDataLength = 1;
 380                             break;
 381 
 382                         case 0xF2:    // Song position pointer
 383                             pendingDataLength = 2;
 384                             break;
 385 
 386                         case 0xF6:    // Tune request
 387                             pendingDataLength = 0;
 388                             break;
 389 
 390                         default:
 391                             // Invalid message
 392                             byteIsInvalid = TRUE;
 393                             break;
 394                         }
 395                         break;
 396                     }
 397 
 398                     default:
 399                         // This can't happen, but handle it anyway.
 400                         byteIsInvalid = TRUE;
 401                         break;
 402                 }
 403             }
 404         }
 405         if (byteIsInvalid) continue;
 406 
 407         // If the byte is valid and pendingDataLength is 0, we are ready to send the message.
 408         if (pendingDataLength == 0) {
 409             MIDI_QueueAddShort(handle->h.queue, packedMsg, (INT64) (AudioConvertHostTimeToNanos(ts) + 500) / 1000, TRUE);
 410         }
 411     }
 412 }
 413 
 414 static void midiReadProc(const MIDIPacketList* packetList, void* refCon, void* connRefCon) {
 415     unsigned int i;
 416     const MIDIPacket* packet;
 417     MacMidiDeviceHandle* handle = (MacMidiDeviceHandle*) connRefCon;
 418 
 419     packet = packetList->packet;
 420     for (i = 0; i < packetList->numPackets; ++i) {
 421         processMessagesForPacket(packet, handle);
 422         packet = MIDIPacketNext(packet);
 423     }
 424 
 425     // Notify the waiting thread that there's data available.
 426     if (handle) {
 427         MIDI_SignalConditionVariable(handle->h.platformData);
 428     }
 429 }
 430 
 431 static void midiInit() {
 432     if (client) {
 433         return;
 434     }
 435 
 436     OSStatus err = noErr;
 437 
 438     err = MIDIClientCreate(CFSTR("MIDI Client"), NULL, NULL, &client);
 439     if (err != noErr) { goto Exit; }
 440 
 441     // This just creates an input port through which the client may receive
 442     // incoming MIDI messages from any MIDI source.
 443     err = MIDIInputPortCreate(client, CFSTR("MIDI Input Port"), midiReadProc, NULL, &inPort);
 444     if (err != noErr) { goto Exit; }
 445 
 446     err = MIDIOutputPortCreate(client, CFSTR("MIDI Output Port"), &outPort);
 447     if (err != noErr) { goto Exit; }
 448 
 449 Exit:
 450     if (err != noErr) {
 451         const char* s = MIDI_Utils_GetErrorMsg(err);
 452         if (s != NULL) {
 453             printf("%s\n", s);
 454         }
 455     }
 456 }
 457 
 458 
 459 INT32 MIDI_Utils_OpenDevice(int direction, INT32 deviceID, MacMidiDeviceHandle** handle,
 460                             int num_msgs, int num_long_msgs,
 461                             size_t lm_size)
 462 {
 463     midiInit();
 464 
 465     int err = MIDI_ERROR_NONE;
 466     MIDIEndpointRef endpoint = (MIDIEndpointRef) NULL;
 467 
 468     TRACE0("MIDI_Utils_OpenDevice\n");
 469 
 470     (*handle) = (MacMidiDeviceHandle*) malloc(sizeof(MacMidiDeviceHandle));
 471     if (!(*handle)) {
 472         ERROR0("ERROR: MIDI_Utils_OpenDevice: out of memory\n");
 473         return MIDI_OUT_OF_MEMORY;
 474     }
 475     memset(*handle, 0, sizeof(MacMidiDeviceHandle));
 476 
 477     // Create the infrastructure for MIDI in/out, and after that,
 478     // get the device's endpoint.
 479     if (direction == MIDI_IN) {
 480         // Create queue and the pthread condition variable.
 481         (*handle)->h.queue = MIDI_CreateQueue(num_msgs);
 482         (*handle)->h.platformData = MIDI_CreateConditionVariable();
 483         if (!(*handle)->h.queue || !(*handle)->h.platformData) {
 484             ERROR0("< ERROR: MIDI_IN_OpenDevice: could not create queue or condition variable\n");
 485             free(*handle);
 486             (*handle) = NULL;
 487             return MIDI_OUT_OF_MEMORY;
 488         }
 489         endpoint = MIDIGetSource(deviceID);
 490         (*handle)->port = inPort;
 491     } else if (direction == MIDI_OUT) {
 492         endpoint = MIDIGetDestination(deviceID);
 493         (*handle)->port = outPort;
 494     }
 495 
 496     if (!endpoint) {
 497         // An error occurred.
 498         free(*handle);
 499         return MIDI_INVALID_DEVICEID;
 500     }
 501     (*handle)->h.deviceHandle = (void*) (intptr_t) endpoint;
 502     (*handle)->h.startTime = getCurrentTimeInNanos();
 503     (*handle)->direction = direction;
 504     (*handle)->deviceID = deviceID;
 505 
 506     TRACE0("MIDI_Utils_OpenDevice: succeeded\n");
 507     return err;
 508 }
 509 
 510 
 511 INT32 MIDI_Utils_CloseDevice(MacMidiDeviceHandle* handle) {
 512     int err = MIDI_ERROR_NONE;
 513     bool midiIn = (handle->direction == MIDI_IN);
 514 
 515     TRACE0("> MIDI_Utils_CloseDevice\n");
 516     if (!handle) {
 517         ERROR0("< ERROR: MIDI_Utils_CloseDevice: handle is NULL\n");
 518         return MIDI_INVALID_HANDLE;
 519     }
 520     if (!handle->h.deviceHandle) {
 521         ERROR0("< ERROR: MIDI_Utils_CloseDevice: native handle is NULL\n");
 522         return MIDI_INVALID_HANDLE;
 523     }
 524     handle->isStarted = FALSE;
 525     handle->h.deviceHandle = NULL;
 526 
 527     if (midiIn) {
 528         if (handle->h.queue != NULL) {
 529             MidiMessageQueue* queue = handle->h.queue;
 530             handle->h.queue = NULL;
 531             MIDI_DestroyQueue(queue);
 532         }
 533         if (handle->h.platformData) {
 534             MIDI_DestroyConditionVariable(handle->h.platformData);
 535         }
 536     }
 537     free(handle);
 538 
 539     TRACE0("< MIDI_Utils_CloseDevice: succeeded\n");
 540     return err;
 541 }
 542 
 543 
 544 INT32 MIDI_Utils_StartDevice(MacMidiDeviceHandle* handle) {
 545     OSStatus err = noErr;
 546 
 547     if (!handle || !handle->h.deviceHandle) {
 548         ERROR0("ERROR: MIDI_Utils_StartDevice: handle or native is NULL\n");
 549         return MIDI_INVALID_HANDLE;
 550     }
 551 
 552     // Clears all the events from the queue.
 553     MIDI_QueueClear(handle->h.queue);
 554 
 555     if (!handle->isStarted) {
 556         /* set the flag that we can now receive messages */
 557         handle->isStarted = TRUE;
 558 
 559         if (handle->direction == MIDI_IN) {
 560             // The handle->h.platformData field contains the (pthread_cond_t*)
 561             // associated with the source of the MIDI input stream, and is
 562             // used in the CoreMIDI's callback to signal the arrival of new
 563             // data.
 564             //
 565             // Similarly, handle->h.queue is used in the CoreMDID's callback
 566             // to dispatch the incoming messages to the appropriate queue.
 567             //
 568             err = MIDIPortConnectSource(inPort, (MIDIEndpointRef) (intptr_t) (handle->h.deviceHandle), (void*) handle);
 569         } else if (handle->direction == MIDI_OUT) {
 570             // Unschedules previous-sent packets.
 571             err = MIDIFlushOutput((MIDIEndpointRef) (intptr_t) handle->h.deviceHandle);
 572         }
 573 
 574         MIDI_CHECK_ERROR;
 575     }
 576     return MIDI_SUCCESS; /* don't fail */
 577 }
 578 
 579 
 580 INT32 MIDI_Utils_StopDevice(MacMidiDeviceHandle* handle) {
 581     OSStatus err = noErr;
 582 
 583     if (!handle || !handle->h.deviceHandle) {
 584         ERROR0("ERROR: MIDI_Utils_StopDevice: handle or native handle is NULL\n");
 585         return MIDI_INVALID_HANDLE;
 586     }
 587 
 588     if (handle->isStarted) {
 589         /* set the flag that we don't want to receive messages anymore */
 590         handle->isStarted = FALSE;
 591 
 592         if (handle->direction == MIDI_IN) {
 593             err = MIDIPortDisconnectSource(inPort, (MIDIEndpointRef) (intptr_t) (handle->h.deviceHandle));
 594         } else if (handle->direction == MIDI_OUT) {
 595             // Unschedules previously-sent packets.
 596             err = MIDIFlushOutput((MIDIEndpointRef) (intptr_t) handle->h.deviceHandle);
 597         }
 598 
 599         MIDI_CHECK_ERROR;
 600     }
 601     return MIDI_SUCCESS;
 602 }
 603 
 604 
 605 INT64 MIDI_Utils_GetTimeStamp(MacMidiDeviceHandle* handle) {
 606 
 607     if (!handle || !handle->h.deviceHandle) {
 608         ERROR0("ERROR: MIDI_Utils_GetTimeStamp: handle or native handle is NULL\n");
 609         return (INT64) -1; /* failure */
 610     }
 611 
 612     UInt64 delta = getCurrentTimeInNanos() - handle->h.startTime;
 613     return (INT64) ((delta + 500) / 1000);
 614 }
 615 
 616 
 617 /***************************************************************************/
 618 /*            Condition Variable Support for Mac OS X Port                 */
 619 /*                                                                         */
 620 /* This works with the Native Locking Support defined below.  We are using */
 621 /* POSIX pthread_cond_t/pthread_mutex_t to do locking and synchronization. */
 622 /*                                                                         */
 623 /* For MidiDeviceHandle* handle, the mutex reference is stored as handle-> */
 624 /* queue->lock while the condition variabale reference is stored as handle */
 625 /* ->platformData.                                                         */
 626 /***************************************************************************/
 627 
 628 // Called from Midi_Utils_Opendevice(...) to create a condition variable
 629 // used to synchronize between the receive thread created by the CoreMIDI
 630 // and the Java-initiated MidiInDevice run loop.
 631 void* MIDI_CreateConditionVariable() {
 632     pthread_cond_t* cond = (pthread_cond_t*) malloc(sizeof(pthread_cond_t));
 633     pthread_cond_init(cond, NULL);
 634     return (void*) cond;
 635 }
 636 
 637 void MIDI_DestroyConditionVariable(void* cond) {
 638     while (pthread_cond_destroy((pthread_cond_t*) cond) == EBUSY) {
 639         pthread_cond_broadcast((pthread_cond_t*) cond);
 640         sched_yield();
 641     }
 642     return;
 643 }
 644 
 645 // Called from MIDI_IN_GetMessage(...) to wait for MIDI messages to become
 646 // available via delivery from the CoreMIDI receive thread
 647 void MIDI_WaitOnConditionVariable(void* cond, void* lock) {
 648     if (cond && lock) {
 649         pthread_mutex_lock(lock);
 650         pthread_cond_wait((pthread_cond_t*) cond, (pthread_mutex_t*) lock);
 651         pthread_mutex_unlock(lock);
 652     }
 653     return;
 654 }
 655 
 656 // Called from midiReadProc(...) to notify the waiting thread to unblock on
 657 // the condition variable.
 658 void MIDI_SignalConditionVariable(void* cond) {
 659     if (cond) {
 660         pthread_cond_signal((pthread_cond_t*) cond);
 661     }
 662     return;
 663 }
 664 
 665 
 666 /**************************************************************************/
 667 /*                     Native Locking Support                             */
 668 /*                                                                        */
 669 /* @see src/share/natve/com/sun/media/sound/PlatformMidi.c which contains */
 670 /* utility functions for platform midi support where the section of code  */
 671 /* for MessageQueue implementation calls out to these functions.          */
 672 /**************************************************************************/
 673 
 674 void* MIDI_CreateLock() {
 675     pthread_mutex_t* lock = (pthread_mutex_t*) malloc(sizeof(pthread_mutex_t));
 676     pthread_mutex_init(lock, NULL);
 677     TRACE0("MIDI_CreateLock\n");
 678     return (void *)lock;
 679 }
 680 
 681 void MIDI_DestroyLock(void* lock) {
 682     if (lock) {
 683         pthread_mutex_destroy((pthread_mutex_t*) lock);
 684         free(lock);
 685         TRACE0("MIDI_DestroyLock\n");
 686     }
 687 }
 688 
 689 void MIDI_Lock(void* lock) {
 690     if (lock) {
 691         pthread_mutex_lock((pthread_mutex_t*) lock);
 692     }
 693 }
 694 
 695 void MIDI_Unlock(void* lock) {
 696     if (lock) {
 697         pthread_mutex_unlock((pthread_mutex_t*) lock);
 698     }
 699 }
 700 
 701 
 702 #endif // USE_PLATFORM_MIDI_IN || USE_PLATFORM_MIDI_OUT