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