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