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