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 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 ??? 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 */ 149 static inline BOOL isInitialized() { return data.threadHandle != NULL; } 150 protected: 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 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 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 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 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 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 247 INT32 MIDI_IN_GetNumDevices() { 248 return (INT32) midiInGetNumDevs(); 249 } 250 251 INT32 getMidiInCaps(INT32 deviceID, MIDIINCAPS* caps, INT32* err) { 252 (*err) = midiInGetDevCaps(deviceID, caps, sizeof(MIDIINCAPS)); 253 return ((*err) == MMSYSERR_NOERROR); 254 } 255 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 270 INT32 MIDI_IN_GetDeviceVendor(INT32 deviceID, char *name, UINT32 nameLength) { 271 return MIDI_NOT_SUPPORTED; 272 } 273 274 275 INT32 MIDI_IN_GetDeviceDescription(INT32 deviceID, char *name, UINT32 nameLength) { 276 return MIDI_NOT_SUPPORTED; 277 } 278 279 280 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 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 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 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 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 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 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 */ 468 INT64 MIDI_IN_GetTimeStamp(MidiDeviceHandle* handle) { 469 return MIDI_GetTimeStamp(handle); 470 } 471 472 473 // read the next message from the queue 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 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