1 /*
2  * Copyright (c) 1999, 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 #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 ???
MIDI_IN_PutMessage(HMIDIIN hMidiIn,UINT wMsg,UINT_PTR dwInstance,UINT_PTR dwParam1,UINT_PTR dwParam2)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 */
isInitialized()149     static inline BOOL isInitialized() { return data.threadHandle != NULL; }
150 protected:
MidiIn_OpenHelper()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 
Data()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 
~Data()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 
ThreadProc(void * param)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 
midiInOpen(INT32 deviceID,MidiDeviceHandle * handle)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 
MIDI_IN_GetErrorStr(INT32 err)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 
MIDI_IN_GetNumDevices()247 INT32 MIDI_IN_GetNumDevices() {
248     return (INT32) midiInGetNumDevs();
249 }
250 
getMidiInCaps(INT32 deviceID,MIDIINCAPS * caps,INT32 * err)251 INT32 getMidiInCaps(INT32 deviceID, MIDIINCAPS* caps, INT32* err) {
252     (*err) = midiInGetDevCaps(deviceID, caps, sizeof(MIDIINCAPS));
253     return ((*err) == MMSYSERR_NOERROR);
254 }
255 
MIDI_IN_GetDeviceName(INT32 deviceID,char * name,UINT32 nameLength)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 
MIDI_IN_GetDeviceVendor(INT32 deviceID,char * name,UINT32 nameLength)270 INT32 MIDI_IN_GetDeviceVendor(INT32 deviceID, char *name, UINT32 nameLength) {
271     return MIDI_NOT_SUPPORTED;
272 }
273 
274 
MIDI_IN_GetDeviceDescription(INT32 deviceID,char * name,UINT32 nameLength)275 INT32 MIDI_IN_GetDeviceDescription(INT32 deviceID, char *name, UINT32 nameLength) {
276     return MIDI_NOT_SUPPORTED;
277 }
278 
279 
280 
MIDI_IN_GetDeviceVersion(INT32 deviceID,char * name,UINT32 nameLength)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 
prepareBuffers(MidiDeviceHandle * handle)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 
unprepareBuffers(MidiDeviceHandle * handle)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 
MIDI_IN_OpenDevice(INT32 deviceID,MidiDeviceHandle ** handle)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 
MIDI_IN_CloseDevice(MidiDeviceHandle * handle)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 
MIDI_IN_StartDevice(MidiDeviceHandle * handle)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 
MIDI_IN_StopDevice(MidiDeviceHandle * handle)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 */
MIDI_IN_GetTimeStamp(MidiDeviceHandle * handle)468 INT64 MIDI_IN_GetTimeStamp(MidiDeviceHandle* handle) {
469         return MIDI_GetTimeStamp(handle);
470 }
471 
472 
473 // read the next message from the queue
MIDI_IN_GetMessage(MidiDeviceHandle * handle)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 
MIDI_IN_ReleaseMessage(MidiDeviceHandle * handle,MidiMessage * msg)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
515