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