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