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