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