1 /*
   2  * Copyright (c) 1999, 2007, 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 #define USE_ERROR
  27 #define USE_TRACE
  28 
  29 /* include Java Sound specific headers as C code */
  30 extern "C" {
  31 #include "PLATFORM_API_WinOS_Util.h"
  32 }
  33 
  34 #if USE_PLATFORM_MIDI_IN == TRUE
  35 
  36 #ifdef USE_ERROR
  37 #include <stdio.h>
  38 
  39 /* Use THIS_FILE when it is available. */
  40 #ifndef THIS_FILE
  41     #define THIS_FILE __FILE__
  42 #endif
  43 
  44 #define MIDIIN_CHECK_ERROR { \
  45         if (err != MMSYSERR_NOERROR) \
  46             ERROR3("MIDI IN Error in %s:%d : %s\n", THIS_FILE, __LINE__, MIDI_IN_GetErrorStr((INT32) err)); \
  47     }
  48 #else
  49 #define MIDIIN_CHECK_ERROR
  50 #endif
  51 
  52 /*
  53  * Callback from the MIDI device for all messages.
  54  */
  55 //$$fb dwParam1 holds a pointer for long messages. How can that be a DWORD then ???
  56 void CALLBACK MIDI_IN_PutMessage( HMIDIIN hMidiIn, UINT wMsg, UINT_PTR dwInstance, UINT_PTR dwParam1, UINT_PTR dwParam2 ) {
  57 
  58     MidiDeviceHandle* handle = (MidiDeviceHandle*) dwInstance;
  59 
  60     TRACE3("> MIDI_IN_PutMessage, hMidiIn: %x, wMsg: %x, dwInstance: %x\n", hMidiIn, wMsg, dwInstance);
  61     TRACE2("                      dwParam1: %x, dwParam2: %x\n", dwParam1, dwParam2);
  62 
  63     switch(wMsg) {
  64 
  65     case MIM_OPEN:
  66         TRACE0("< MIDI_IN_PutMessage: MIM_OPEN\n");
  67         break;
  68 
  69     case MIM_CLOSE:
  70         TRACE0("< MIDI_IN_PutMessage: MIM_CLOSE\n");
  71         break;
  72 
  73     case MIM_MOREDATA:
  74     case MIM_DATA:
  75         TRACE3("  MIDI_IN_PutMessage: MIM_MOREDATA or MIM_DATA. status=%x  data1=%x  data2=%x\n",
  76                dwParam1 & 0xFF, (dwParam1 & 0xFF00)>>8, (dwParam1 & 0xFF0000)>>16);
  77         if (handle!=NULL && handle->queue!=NULL && handle->platformData) {
  78             MIDI_QueueAddShort(handle->queue,
  79                                // queue stores packedMsg in big endian
  80                                //(dwParam1 << 24) | ((dwParam1 << 8) & 0xFF0000) | ((dwParam1 >> 8) & 0xFF00),
  81                                (UINT32) dwParam1,
  82                                // queue uses microseconds
  83                                ((INT64) dwParam2)*1000,
  84                                // overwrite if queue is full
  85                                TRUE);
  86             SetEvent((HANDLE) handle->platformData);
  87         }
  88         TRACE0("< MIDI_IN_PutMessage\n");
  89         break;
  90 
  91     case MIM_LONGDATA:
  92         TRACE1("  MIDI_IN_PutMessage: MIM_LONGDATA (%d bytes recorded)\n", (int) (((MIDIHDR*) dwParam1)->dwBytesRecorded));
  93         if (handle!=NULL && handle->queue!=NULL && handle->platformData) {
  94             MIDIHDR* hdr = (MIDIHDR*) dwParam1;
  95             TRACE2("  MIDI_IN_PutMessage: Adding to queue: index %d, %d bytes\n", (INT32) hdr->dwUser, hdr->dwBytesRecorded);
  96             MIDI_QueueAddLong(handle->queue,
  97                               (UBYTE*) hdr->lpData,
  98                               (UINT32) hdr->dwBytesRecorded,
  99                               // sysex buffer index
 100                               (INT32) hdr->dwUser,
 101                               // queue uses microseconds
 102                               ((INT64) dwParam2)*1000,
 103                               // overwrite if queue is full
 104                               TRUE);
 105             SetEvent((HANDLE) handle->platformData);
 106         }
 107         TRACE0("< MIDI_IN_PutMessage\n");
 108         break;
 109 
 110     case MIM_ERROR:
 111         ERROR0("< MIDI_IN_PutMessage: MIM_ERROR!\n");
 112         break;
 113 
 114     case MIM_LONGERROR:
 115         if (dwParam1 != 0) {
 116             MIDIHDR* hdr = (MIDIHDR*) dwParam1;
 117 #ifdef USE_TRACE
 118             if (hdr->dwBytesRecorded > 0) {
 119                 TRACE2("  MIDI_IN_PutMessage: MIM_LONGERROR! recorded: %d bytes with status 0x%2x\n",
 120                         hdr->dwBytesRecorded, (int) (*((UBYTE*) hdr->lpData)));
 121             }
 122 #endif
 123             // re-add hdr to device query
 124             hdr->dwBytesRecorded = 0;
 125             midiInAddBuffer((HMIDIIN)handle->deviceHandle, hdr, sizeof(MIDIHDR));
 126         }
 127         ERROR0("< MIDI_IN_PutMessage: MIM_LONGERROR!\n");
 128         break;
 129 
 130     default:
 131         ERROR1("< MIDI_IN_PutMessage: ERROR unknown message %d!\n", wMsg);
 132         break;
 133 
 134     } // switch (wMsg)
 135 }
 136 
 137 
 138 /*
 139 ** data/routines for opening MIDI input (MidiIn) device by separate thread
 140 ** (joint into MidiIn_OpenHelper class)
 141 ** see 6415669 - MidiIn device stops work and crushes JVM after exiting
 142 ** from thread that has open the device (it looks like WinMM bug).
 143 */
 144 class MidiIn_OpenHelper {
 145 public:
 146     /* opens MidiIn device  */
 147     static MMRESULT midiInOpen(INT32 deviceID, MidiDeviceHandle* handle);
 148     /* checks for initialization success */
 149     static inline BOOL isInitialized() { return data.threadHandle != NULL; }
 150 protected:
 151     MidiIn_OpenHelper() {}  // no need to create an instance
 152 
 153     /* data class */
 154     class Data {
 155     public:
 156         Data();
 157         ~Data();
 158         // public data to access from parent class
 159         CRITICAL_SECTION crit_sect;
 160         volatile HANDLE threadHandle;
 161         volatile HANDLE doEvent;    // event to resume thread
 162         volatile HANDLE doneEvent;  // processing has been completed
 163         volatile MMRESULT err;      // processing result
 164         // data to process; (handle == null) is command to thread terminating
 165         volatile INT32 deviceID;
 166         volatile MidiDeviceHandle* handle;
 167     } static data;
 168 
 169     /* StartThread function */
 170     static DWORD WINAPI __stdcall ThreadProc(void *param);
 171 };
 172 
 173 /* MidiIn_OpenHelper class implementation
 174 */
 175 MidiIn_OpenHelper::Data MidiIn_OpenHelper::data;
 176 
 177 MidiIn_OpenHelper::Data::Data() {
 178     threadHandle = NULL;
 179     ::InitializeCriticalSection(&crit_sect);
 180     doEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
 181     doneEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
 182     if (doEvent != NULL && doneEvent != NULL)
 183         threadHandle = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
 184 }
 185 
 186 MidiIn_OpenHelper::Data::~Data() {
 187     ::EnterCriticalSection(&crit_sect);
 188     if (threadHandle != NULL) {
 189         // terminate thread
 190         handle = NULL;
 191         ::SetEvent(doEvent);
 192         ::CloseHandle(threadHandle);
 193         threadHandle = NULL;
 194     }
 195     ::LeaveCriticalSection(&crit_sect);
 196     // won't delete doEvent/doneEvent/crit_sect
 197     // - Windows will do during process shutdown
 198 }
 199 
 200 DWORD WINAPI __stdcall MidiIn_OpenHelper::ThreadProc(void *param) {
 201     while (1) {
 202         // wait for something to do
 203         ::WaitForSingleObject(data.doEvent, INFINITE);
 204         if (data.handle == NULL) {
 205             // (data.handle == NULL) is a signal to terminate thread
 206             break;
 207         }
 208 
 209         data.err = ::midiInOpen((HMIDIIN*)&(data.handle->deviceHandle),
 210                                 data.deviceID, (UINT_PTR)&(MIDI_IN_PutMessage),
 211                                 (UINT_PTR)data.handle,
 212                                 CALLBACK_FUNCTION|MIDI_IO_STATUS);
 213 
 214         ::SetEvent(data.doneEvent);
 215     }
 216     return 0;
 217 }
 218 
 219 MMRESULT MidiIn_OpenHelper::midiInOpen(INT32 deviceID, MidiDeviceHandle* handle) {
 220     MMRESULT err;
 221     ::EnterCriticalSection(&data.crit_sect);
 222     if (!isInitialized()) {
 223         ::LeaveCriticalSection(&data.crit_sect);
 224         return MMSYSERR_ERROR;
 225     }
 226     data.deviceID = deviceID;
 227     data.handle = handle;
 228     ::SetEvent(data.doEvent);
 229     ::WaitForSingleObject(data.doneEvent, INFINITE);
 230     err = data.err;
 231     ::LeaveCriticalSection(&data.crit_sect);
 232     return err;
 233 }
 234 
 235 
 236 // PLATFORM_MIDI_IN method implementations
 237 
 238 /* not thread safe */
 239 static char winMidiInErrMsg[WIN_MAX_ERROR_LEN];
 240 
 241 char* MIDI_IN_GetErrorStr(INT32 err) {
 242     winMidiInErrMsg[0] = 0;
 243     midiInGetErrorText((MMRESULT) err, winMidiInErrMsg, WIN_MAX_ERROR_LEN);
 244     return winMidiInErrMsg;
 245 }
 246 
 247 INT32 MIDI_IN_GetNumDevices() {
 248     return (INT32) midiInGetNumDevs();
 249 }
 250 
 251 INT32 getMidiInCaps(INT32 deviceID, MIDIINCAPS* caps, INT32* err) {
 252     (*err) = midiInGetDevCaps(deviceID, caps, sizeof(MIDIINCAPS));
 253     return ((*err) == MMSYSERR_NOERROR);
 254 }
 255 
 256 INT32 MIDI_IN_GetDeviceName(INT32 deviceID, char *name, UINT32 nameLength) {
 257     MIDIINCAPS midiInCaps;
 258     INT32 err;
 259 
 260     if (getMidiInCaps(deviceID, &midiInCaps, &err)) {
 261         strncpy(name, midiInCaps.szPname, nameLength-1);
 262         name[nameLength-1] = 0;
 263         return MIDI_SUCCESS;
 264     }
 265     MIDIIN_CHECK_ERROR;
 266     return err;
 267 }
 268 
 269 
 270 INT32 MIDI_IN_GetDeviceVendor(INT32 deviceID, char *name, UINT32 nameLength) {
 271     return MIDI_NOT_SUPPORTED;
 272 }
 273 
 274 
 275 INT32 MIDI_IN_GetDeviceDescription(INT32 deviceID, char *name, UINT32 nameLength) {
 276     return MIDI_NOT_SUPPORTED;
 277 }
 278 
 279 
 280 
 281 INT32 MIDI_IN_GetDeviceVersion(INT32 deviceID, char *name, UINT32 nameLength) {
 282     MIDIINCAPS midiInCaps;
 283     INT32 err = MIDI_NOT_SUPPORTED;
 284 
 285     if (getMidiInCaps(deviceID, &midiInCaps, &err) && (nameLength>7)) {
 286         sprintf(name, "%d.%d", (midiInCaps.vDriverVersion & 0xFF00) >> 8, midiInCaps.vDriverVersion & 0xFF);
 287         return MIDI_SUCCESS;
 288     }
 289     MIDIIN_CHECK_ERROR;
 290     return err;
 291 }
 292 
 293 
 294 INT32 prepareBuffers(MidiDeviceHandle* handle) {
 295     SysExQueue* sysex;
 296     MMRESULT err = MMSYSERR_NOERROR;
 297     int i;
 298 
 299     if (!handle || !handle->longBuffers || !handle->deviceHandle) {
 300         ERROR0("MIDI_IN_prepareBuffers: handle, or longBuffers, or deviceHandle==NULL\n");
 301         return MIDI_INVALID_HANDLE;
 302     }
 303     sysex = (SysExQueue*) handle->longBuffers;
 304     for (i = 0; i<sysex->count; i++) {
 305         MIDIHDR* hdr = &(sysex->header[i]);
 306         midiInPrepareHeader((HMIDIIN) handle->deviceHandle, hdr, sizeof(MIDIHDR));
 307         err = midiInAddBuffer((HMIDIIN) handle->deviceHandle, hdr, sizeof(MIDIHDR));
 308     }
 309     MIDIIN_CHECK_ERROR;
 310     return (INT32) err;
 311 }
 312 
 313 INT32 unprepareBuffers(MidiDeviceHandle* handle) {
 314     SysExQueue* sysex;
 315     MMRESULT err = MMSYSERR_NOERROR;
 316     int i;
 317 
 318     if (!handle || !handle->longBuffers || !handle->deviceHandle) {
 319         ERROR0("MIDI_IN_unprepareBuffers: handle, or longBuffers, or deviceHandle==NULL\n");
 320         return MIDI_INVALID_HANDLE;
 321     }
 322     sysex = (SysExQueue*) handle->longBuffers;
 323     for (i = 0; i<sysex->count; i++) {
 324         err = midiInUnprepareHeader((HMIDIIN) handle->deviceHandle, &(sysex->header[i]), sizeof(MIDIHDR));
 325     }
 326     MIDIIN_CHECK_ERROR;
 327     return (INT32) err;
 328 }
 329 
 330 INT32 MIDI_IN_OpenDevice(INT32 deviceID, MidiDeviceHandle** handle) {
 331     MMRESULT err;
 332 
 333     TRACE0("> MIDI_IN_OpenDevice\n");
 334 #ifdef USE_ERROR
 335     setvbuf(stdout, NULL, (int)_IONBF, 0);
 336     setvbuf(stderr, NULL, (int)_IONBF, 0);
 337 #endif
 338 
 339     (*handle) = (MidiDeviceHandle*) malloc(sizeof(MidiDeviceHandle));
 340     if (!(*handle)) {
 341         ERROR0("< ERROR: MIDI_IN_OpenDevice: out of memory\n");
 342         return MIDI_OUT_OF_MEMORY;
 343     }
 344     memset(*handle, 0, sizeof(MidiDeviceHandle));
 345 
 346     // create queue
 347     (*handle)->queue = MIDI_CreateQueue(MIDI_IN_MESSAGE_QUEUE_SIZE);
 348     if (!(*handle)->queue) {
 349         ERROR0("< ERROR: MIDI_IN_OpenDevice: could not create queue\n");
 350         free(*handle);
 351         (*handle) = NULL;
 352         return MIDI_OUT_OF_MEMORY;
 353     }
 354 
 355     // create long buffer queue
 356     if (!MIDI_WinCreateLongBufferQueue(*handle, MIDI_IN_LONG_QUEUE_SIZE, MIDI_IN_LONG_MESSAGE_SIZE, NULL)) {
 357         ERROR0("< ERROR: MIDI_IN_OpenDevice: could not create long Buffers\n");
 358         MIDI_DestroyQueue((*handle)->queue);
 359         free(*handle);
 360         (*handle) = NULL;
 361         return MIDI_OUT_OF_MEMORY;
 362     }
 363 
 364     // finally open the device
 365     err = MidiIn_OpenHelper::midiInOpen(deviceID, *handle);
 366 
 367     if ((err != MMSYSERR_NOERROR) || (!(*handle)->deviceHandle)) {
 368         MIDIIN_CHECK_ERROR;
 369         MIDI_WinDestroyLongBufferQueue(*handle);
 370         MIDI_DestroyQueue((*handle)->queue);
 371         free(*handle);
 372         (*handle) = NULL;
 373         return (INT32) err;
 374     }
 375 
 376     prepareBuffers(*handle);
 377         MIDI_SetStartTime(*handle);
 378     TRACE0("< MIDI_IN_OpenDevice: midiInOpen succeeded\n");
 379     return MIDI_SUCCESS;
 380 }
 381 
 382 
 383 INT32 MIDI_IN_CloseDevice(MidiDeviceHandle* handle) {
 384     MMRESULT err;
 385 
 386     TRACE0("> MIDI_IN_CloseDevice: midiInClose\n");
 387     if (!handle) {
 388         ERROR0("ERROR: MIDI_IN_CloseDevice: handle is NULL\n");
 389         return MIDI_INVALID_HANDLE;
 390     }
 391     midiInReset((HMIDIIN) handle->deviceHandle);
 392     unprepareBuffers(handle);
 393     err = midiInClose((HMIDIIN) handle->deviceHandle);
 394     handle->deviceHandle=NULL;
 395     MIDIIN_CHECK_ERROR;
 396     MIDI_WinDestroyLongBufferQueue(handle);
 397 
 398     if (handle->queue!=NULL) {
 399         MidiMessageQueue* queue = handle->queue;
 400         handle->queue = NULL;
 401         MIDI_DestroyQueue(queue);
 402     }
 403     free(handle);
 404 
 405     TRACE0("< MIDI_IN_CloseDevice: midiInClose succeeded\n");
 406     return (INT32) err;
 407 }
 408 
 409 
 410 INT32 MIDI_IN_StartDevice(MidiDeviceHandle* handle) {
 411     MMRESULT err;
 412 
 413     if (!handle || !handle->deviceHandle || !handle->queue) {
 414         ERROR0("ERROR: MIDI_IN_StartDevice: handle or queue is NULL\n");
 415         return MIDI_INVALID_HANDLE;
 416     }
 417 
 418     // clear all the events from the queue
 419     MIDI_QueueClear(handle->queue);
 420 
 421     handle->platformData = (void*) CreateEvent(NULL, FALSE /*manual reset*/, FALSE /*signaled*/, NULL);
 422     if (!handle->platformData) {
 423         ERROR0("ERROR: MIDI_IN_StartDevice: could not create event\n");
 424         return MIDI_OUT_OF_MEMORY;
 425     }
 426 
 427     err = midiInStart((HMIDIIN) handle->deviceHandle);
 428         /* $$mp 200308-11: This method is already called in ...open(). It is
 429            unclear why is is called again. The specification says that
 430            MidiDevice.getMicrosecondPosition() returns the time since the
 431            device was opened (the spec doesn't know about start/stop).
 432            So I guess this call is obsolete. */
 433         MIDI_SetStartTime(handle);
 434 
 435     MIDIIN_CHECK_ERROR;
 436     TRACE0("MIDI_IN_StartDevice: midiInStart finished\n");
 437     return (INT32) err;
 438 }
 439 
 440 
 441 INT32 MIDI_IN_StopDevice(MidiDeviceHandle* handle) {
 442     MMRESULT err;
 443     HANDLE event;
 444 
 445     TRACE0("> MIDI_IN_StopDevice: midiInStop \n");
 446     if (!handle || !handle->platformData) {
 447         ERROR0("ERROR: MIDI_IN_StopDevice: handle or event is NULL\n");
 448         return MIDI_INVALID_HANDLE;
 449     }
 450     // encourage MIDI_IN_GetMessage to return soon
 451     event = handle->platformData;
 452     handle->platformData = NULL;
 453     SetEvent(event);
 454 
 455     err = midiInStop((HMIDIIN) handle->deviceHandle);
 456 
 457     // wait until the Java thread has exited
 458     while (handle->isWaiting) Sleep(0);
 459     CloseHandle(event);
 460 
 461     MIDIIN_CHECK_ERROR;
 462     TRACE0("< MIDI_IN_StopDevice: midiInStop finished\n");
 463     return (INT32) err;
 464 }
 465 
 466 
 467 /* return time stamp in microseconds */
 468 INT64 MIDI_IN_GetTimeStamp(MidiDeviceHandle* handle) {
 469         return MIDI_GetTimeStamp(handle);
 470 }
 471 
 472 
 473 // read the next message from the queue
 474 MidiMessage* MIDI_IN_GetMessage(MidiDeviceHandle* handle) {
 475     if (handle == NULL) {
 476         return NULL;
 477     }
 478     while (handle->queue!=NULL && handle->platformData!=NULL) {
 479         MidiMessage* msg = MIDI_QueueRead(handle->queue);
 480         DWORD res;
 481         if (msg != NULL) {
 482             //fprintf(stdout, "GetMessage returns index %d\n", msg->data.l.index); fflush(stdout);
 483             return msg;
 484         }
 485         TRACE0("MIDI_IN_GetMessage: before waiting\n");
 486         handle->isWaiting = TRUE;
 487         res = WaitForSingleObject((HANDLE) handle->platformData, 2000);
 488         handle->isWaiting = FALSE;
 489         if (res == WAIT_TIMEOUT) {
 490             // break out back to Java from time to time - just to be sure
 491             TRACE0("MIDI_IN_GetMessage: waiting finished with timeout\n");
 492             break;
 493         }
 494         TRACE0("MIDI_IN_GetMessage: waiting finished\n");
 495     }
 496     return NULL;
 497 }
 498 
 499 void MIDI_IN_ReleaseMessage(MidiDeviceHandle* handle, MidiMessage* msg) {
 500     SysExQueue* sysex;
 501     if (handle == NULL || handle->queue == NULL) {
 502         return;
 503     }
 504     sysex = (SysExQueue*) handle->longBuffers;
 505     if (msg->type == LONG_MESSAGE && sysex) {
 506         MIDIHDR* hdr = &(sysex->header[msg->data.l.index]);
 507         //fprintf(stdout, "ReleaseMessage index %d\n", msg->data.l.index); fflush(stdout);
 508         hdr->dwBytesRecorded = 0;
 509         midiInAddBuffer((HMIDIIN) handle->deviceHandle, hdr, sizeof(MIDIHDR));
 510     }
 511     MIDI_QueueRemove(handle->queue, TRUE /*onlyLocked*/);
 512 }
 513 
 514 #endif // USE_PLATFORM_MIDI_IN