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 "PLATFORM_API_WinOS_Util.h" 30 31 #if USE_PLATFORM_MIDI_OUT == TRUE 32 33 34 #ifdef USE_ERROR 35 #include <stdio.h> 36 37 /* Use THIS_FILE when it is available. */ 38 #ifndef THIS_FILE 39 #define THIS_FILE __FILE__ 40 #endif 41 42 #define MIDIOUT_CHECK_ERROR { \ 43 if (err != MMSYSERR_NOERROR) \ 44 ERROR3("MIDI OUT Error in %s:%d : %s\n", THIS_FILE, __LINE__, MIDI_OUT_GetErrorStr((INT32) err)); \ 45 } 46 #else 47 #define MIDIOUT_CHECK_ERROR 48 #endif 49 50 /* *************************** MidiOutDeviceProvider implementation *********************************** */ 51 52 /* not thread safe */ 53 static char winMidiOutErrMsg[WIN_MAX_ERROR_LEN]; 54 55 char* MIDI_OUT_GetErrorStr(INT32 err) { 56 winMidiOutErrMsg[0] = 0; 57 midiOutGetErrorText((MMRESULT) err, winMidiOutErrMsg, WIN_MAX_ERROR_LEN); 58 return winMidiOutErrMsg; 59 } 60 61 INT32 MIDI_OUT_GetNumDevices() { 62 // add one for the MIDI_MAPPER 63 // we want to return it first so it'll be the default, so we 64 // decrement each deviceID for these methods.... 65 return (INT32) (midiOutGetNumDevs() + 1); 66 } 67 68 69 INT32 getMidiOutCaps(INT32 deviceID, MIDIOUTCAPS* caps, INT32* err) { 70 if (deviceID == 0) { 71 deviceID = MIDI_MAPPER; 72 } else { 73 deviceID--; 74 } 75 (*err) = (INT32) midiOutGetDevCaps(deviceID, caps, sizeof(MIDIOUTCAPS)); 76 return ((*err) == MMSYSERR_NOERROR); 77 } 78 79 80 INT32 MIDI_OUT_GetDeviceName(INT32 deviceID, char *name, UINT32 nameLength) { 81 MIDIOUTCAPS midiOutCaps; 82 INT32 err; 83 84 if (getMidiOutCaps(deviceID, &midiOutCaps, &err)) { 85 strncpy(name, midiOutCaps.szPname, nameLength-1); 86 name[nameLength-1] = 0; 87 return MIDI_SUCCESS; 88 } 89 MIDIOUT_CHECK_ERROR; 90 return err; 91 } 92 93 94 INT32 MIDI_OUT_GetDeviceVendor(INT32 deviceID, char *name, UINT32 nameLength) { 95 return MIDI_NOT_SUPPORTED; 96 } 97 98 99 INT32 MIDI_OUT_GetDeviceDescription(INT32 deviceID, char *name, UINT32 nameLength) { 100 MIDIOUTCAPS midiOutCaps; 101 char *desc; 102 INT32 err; 103 104 if (getMidiOutCaps(deviceID, &midiOutCaps, &err)) { 105 int tech = (int)midiOutCaps.wTechnology; 106 switch(tech) { 107 case MOD_MIDIPORT: 108 desc = "External MIDI Port"; 109 break; 110 case MOD_SQSYNTH: 111 desc = "Internal square wave synthesizer"; 112 break; 113 case MOD_FMSYNTH: 114 desc = "Internal FM synthesizer"; 115 break; 116 case MOD_SYNTH: 117 desc = "Internal synthesizer (generic)"; 118 break; 119 case MOD_MAPPER: 120 desc = "Windows MIDI_MAPPER"; 121 break; 122 case 7 /* MOD_SWSYNTH*/: 123 desc = "Internal software synthesizer"; 124 break; 125 default: 126 return MIDI_NOT_SUPPORTED; 127 } 128 strncpy(name, desc, nameLength-1); 129 name[nameLength-1] = 0; 130 return MIDI_SUCCESS; 131 } 132 return err; 133 } 134 135 136 INT32 MIDI_OUT_GetDeviceVersion(INT32 deviceID, char *name, UINT32 nameLength) { 137 MIDIOUTCAPS midiOutCaps; 138 INT32 err; 139 140 if (getMidiOutCaps(deviceID, &midiOutCaps, &err) && nameLength>7) { 141 sprintf(name, "%d.%d", (midiOutCaps.vDriverVersion & 0xFF00) >> 8, midiOutCaps.vDriverVersion & 0xFF); 142 return MIDI_SUCCESS; 143 } 144 MIDIOUT_CHECK_ERROR; 145 return err; 146 } 147 148 149 /* *************************** MidiOutDevice implementation ***************************************** */ 150 151 152 INT32 unprepareLongBuffers(MidiDeviceHandle* handle) { 153 SysExQueue* sysex; 154 MMRESULT err = MMSYSERR_NOERROR; 155 int i; 156 157 if (!handle || !handle->deviceHandle || !handle->longBuffers) { 158 ERROR0("MIDI_OUT_unprepareLongBuffers: handle, deviceHandle, or longBuffers == NULL\n"); 159 return MIDI_INVALID_HANDLE; 160 } 161 sysex = (SysExQueue*) handle->longBuffers; 162 for (i = 0; i<sysex->count; i++) { 163 MIDIHDR* hdr = &(sysex->header[i]); 164 if (hdr->dwFlags) { 165 err = midiOutUnprepareHeader((HMIDIOUT) handle->deviceHandle, hdr, sizeof(MIDIHDR)); 166 } 167 } 168 MIDIOUT_CHECK_ERROR; 169 return (INT32) err; 170 } 171 172 INT32 freeLongBuffer(MIDIHDR* hdr, HMIDIOUT deviceHandle, INT32 minToLeaveData) { 173 MMRESULT err = MMSYSERR_NOERROR; 174 175 if (!hdr) { 176 ERROR0("MIDI_OUT_freeLongBuffer: hdr == NULL\n"); 177 return MIDI_INVALID_HANDLE; 178 } 179 if (hdr->dwFlags && deviceHandle) { 180 err = midiOutUnprepareHeader(deviceHandle, hdr, sizeof(MIDIHDR)); 181 } 182 if (hdr->lpData && (((INT32) hdr->dwBufferLength) < minToLeaveData || minToLeaveData < 0)) { 183 free(hdr->lpData); 184 hdr->lpData=NULL; 185 hdr->dwBufferLength=0; 186 } 187 hdr->dwBytesRecorded=0; 188 hdr->dwFlags=0; 189 return (INT32) err; 190 } 191 192 INT32 freeLongBuffers(MidiDeviceHandle* handle) { 193 SysExQueue* sysex; 194 MMRESULT err = MMSYSERR_NOERROR; 195 int i; 196 197 if (!handle || !handle->longBuffers) { 198 ERROR0("MIDI_OUT_freeLongBuffers: handle or longBuffers == NULL\n"); 199 return MIDI_INVALID_HANDLE; 200 } 201 sysex = (SysExQueue*) handle->longBuffers; 202 for (i = 0; i<sysex->count; i++) { 203 err = freeLongBuffer(&(sysex->header[i]), (HMIDIOUT) handle->deviceHandle, -1); 204 } 205 MIDIOUT_CHECK_ERROR; 206 return (INT32) err; 207 } 208 209 INT32 MIDI_OUT_OpenDevice(INT32 deviceID, MidiDeviceHandle** handle) { 210 MMRESULT err; 211 212 TRACE1(">> MIDI_OUT_OpenDevice: deviceID: %d\n", deviceID); 213 214 if (deviceID == 0) { 215 deviceID = MIDI_MAPPER; 216 } else { 217 deviceID--; 218 } 219 #ifdef USE_ERROR 220 setvbuf(stdout, NULL, (int)_IONBF, 0); 221 setvbuf(stderr, NULL, (int)_IONBF, 0); 222 #endif 223 224 (*handle) = (MidiDeviceHandle*) malloc(sizeof(MidiDeviceHandle)); 225 if (!(*handle)) { 226 ERROR0("ERROR: MIDI_OUT_OpenDevice: out of memory\n"); 227 return MIDI_OUT_OF_MEMORY; 228 } 229 memset(*handle, 0, sizeof(MidiDeviceHandle)); 230 231 // create long buffer queue 232 if (!MIDI_WinCreateEmptyLongBufferQueue(*handle, MIDI_OUT_LONG_QUEUE_SIZE)) { 233 ERROR0("ERROR: MIDI_OUT_OpenDevice: could not create long Buffers\n"); 234 free(*handle); 235 (*handle) = NULL; 236 return MIDI_OUT_OF_MEMORY; 237 } 238 239 // create notification event 240 (*handle)->platformData = (void*) CreateEvent(NULL, FALSE /*manual reset*/, FALSE /*signaled*/, NULL); 241 if (!(*handle)->platformData) { 242 ERROR0("ERROR: MIDI_OUT_StartDevice: could not create event\n"); 243 MIDI_WinDestroyLongBufferQueue(*handle); 244 free(*handle); 245 (*handle) = NULL; 246 return MIDI_OUT_OF_MEMORY; 247 } 248 249 // finally open the device 250 err = midiOutOpen((HMIDIOUT*) &((*handle)->deviceHandle), deviceID, 251 (UINT_PTR) (*handle)->platformData, (UINT_PTR) (*handle), CALLBACK_EVENT); 252 253 if ((err != MMSYSERR_NOERROR) || (!(*handle)->deviceHandle)) { 254 /* some devices return non zero, but no error! */ 255 if (midiOutShortMsg((HMIDIOUT) ((*handle)->deviceHandle),0) == MMSYSERR_INVALHANDLE) { 256 MIDIOUT_CHECK_ERROR; 257 CloseHandle((HANDLE) (*handle)->platformData); 258 MIDI_WinDestroyLongBufferQueue(*handle); 259 free(*handle); 260 (*handle) = NULL; 261 return (INT32) err; 262 } 263 } 264 //$$fb enable high resolution time 265 timeBeginPeriod(1); 266 MIDI_SetStartTime(*handle); 267 TRACE0("<< MIDI_OUT_OpenDevice: succeeded\n"); 268 return MIDI_SUCCESS; 269 } 270 271 INT32 MIDI_OUT_CloseDevice(MidiDeviceHandle* handle) { 272 MMRESULT err = MMSYSERR_NOERROR; 273 HANDLE event; 274 275 TRACE0("> MIDI_OUT_CloseDevice\n"); 276 if (!handle) { 277 ERROR0("ERROR: MIDI_OUT_StopDevice: handle is NULL\n"); 278 return MIDI_INVALID_HANDLE; // failure 279 } 280 // encourage MIDI_OUT_SendLongMessage to return soon 281 event = handle->platformData; 282 handle->platformData = NULL; 283 if (event) { 284 SetEvent(event); 285 } else { 286 ERROR0("ERROR: MIDI_OUT_StopDevice: event is NULL\n"); 287 } 288 289 if (handle->deviceHandle) { 290 //$$fb disable high resolution time 291 timeEndPeriod(1); 292 err = midiOutReset((HMIDIOUT) handle->deviceHandle); 293 } else { 294 ERROR0("ERROR: MIDI_OUT_CloseDevice: deviceHandle is NULL\n"); 295 } 296 297 // issue a "SUSTAIN OFF" message to each MIDI channel, 0 to 15. 298 // "CONTROL CHANGE" is 176, "SUSTAIN CONTROLLER" is 64, and the value is 0. 299 // $$fb 2002-04-04: It is responsability of the application developer to 300 // leave the device in a consistent state. So I put this in comments 301 /* 302 for (channel = 0; channel < 16; channel++) 303 MIDI_OUT_SendShortMessage(deviceHandle, (unsigned char)(176 + channel), (unsigned char)64, (unsigned char)0, (UINT32)-1); 304 */ 305 306 if (event) { 307 // wait until MIDI_OUT_SendLongMessage has finished 308 while (handle->isWaiting) Sleep(0); 309 } 310 311 unprepareLongBuffers(handle); 312 313 if (handle->deviceHandle) { 314 err = midiOutClose((HMIDIOUT) handle->deviceHandle); 315 MIDIOUT_CHECK_ERROR; 316 handle->deviceHandle = NULL; 317 } 318 freeLongBuffers(handle); 319 320 if (event) { 321 CloseHandle(event); 322 } 323 MIDI_WinDestroyLongBufferQueue(handle); 324 free(handle); 325 326 TRACE0("< MIDI_OUT_CloseDevice\n"); 327 return (INT32) err; 328 } 329 330 331 /* return time stamp in microseconds */ 332 INT64 MIDI_OUT_GetTimeStamp(MidiDeviceHandle* handle) { 333 return MIDI_GetTimeStamp(handle); 334 } 335 336 337 INT32 MIDI_OUT_SendShortMessage(MidiDeviceHandle* handle, UINT32 packedMsg, UINT32 timestamp) { 338 MMRESULT err = MMSYSERR_NOERROR; 339 340 TRACE2("> MIDI_OUT_SendShortMessage %x, time: %d\n", packedMsg, timestamp); 341 if (!handle) { 342 ERROR0("ERROR: MIDI_OUT_SendShortMessage: handle is NULL\n"); 343 return MIDI_INVALID_HANDLE; // failure 344 } 345 err = midiOutShortMsg((HMIDIOUT) handle->deviceHandle, packedMsg); 346 MIDIOUT_CHECK_ERROR; 347 TRACE0("< MIDI_OUT_SendShortMessage\n"); 348 return (INT32) err; 349 } 350 351 INT32 MIDI_OUT_SendLongMessage(MidiDeviceHandle* handle, UBYTE* data, UINT32 size, UINT32 timestamp) { 352 MMRESULT err; 353 SysExQueue* sysex; 354 MIDIHDR* hdr = NULL; 355 INT32 remainingSize; 356 int i; 357 358 TRACE2("> MIDI_OUT_SendLongMessage size %d, time: %d\n", size, timestamp); 359 if (!handle || !data || !handle->longBuffers) { 360 ERROR0("< ERROR: MIDI_OUT_SendLongMessage: handle, data, or longBuffers is NULL\n"); 361 return MIDI_INVALID_HANDLE; // failure 362 } 363 if (size == 0) { 364 return MIDI_SUCCESS; 365 } 366 367 sysex = (SysExQueue*) handle->longBuffers; 368 remainingSize = size; 369 370 // send in chunks of 512 bytes 371 size = 512; 372 while (remainingSize > 0) { 373 if (remainingSize < (INT32) size) { 374 size = (UINT32) remainingSize; 375 } 376 377 while (!hdr && handle->platformData) { 378 /* find a non-queued header */ 379 for (i = 0; i < sysex->count; i++) { 380 hdr = &(sysex->header[i]); 381 if ((hdr->dwFlags & MHDR_DONE) || (hdr->dwFlags == 0)) { 382 break; 383 } 384 hdr = NULL; 385 } 386 /* wait for a buffer to free up */ 387 if (!hdr && handle->platformData) { 388 DWORD res; 389 TRACE0(" Need to wait for free buffer\n"); 390 handle->isWaiting = TRUE; 391 res = WaitForSingleObject((HANDLE) handle->platformData, 700); 392 handle->isWaiting = FALSE; 393 if (res == WAIT_TIMEOUT) { 394 // break out back to Java if no buffer freed up after 700 milliseconds 395 TRACE0("-> TIMEOUT. Need to go back to Java\n"); 396 break; 397 } 398 } 399 } 400 if (!hdr) { 401 // no free buffer 402 return MIDI_NOT_SUPPORTED; 403 } 404 405 TRACE2("-> sending %d bytes with buffer index=%d\n", (int) size, (int) hdr->dwUser); 406 freeLongBuffer(hdr, handle->deviceHandle, (INT32) size); 407 if (hdr->lpData == NULL) { 408 hdr->lpData = malloc(size); 409 hdr->dwBufferLength = size; 410 } 411 hdr->dwBytesRecorded = size; 412 memcpy(hdr->lpData, data, size); 413 err = midiOutPrepareHeader((HMIDIOUT) handle->deviceHandle, hdr, sizeof(MIDIHDR)); 414 if (err != MMSYSERR_NOERROR) { 415 freeLongBuffer(hdr, handle->deviceHandle, -1); 416 MIDIOUT_CHECK_ERROR; 417 return (INT32) err; 418 } 419 err = midiOutLongMsg((HMIDIOUT) handle->deviceHandle, hdr, sizeof(MIDIHDR)); 420 if (err != MMSYSERR_NOERROR) { 421 freeLongBuffer(hdr, handle->deviceHandle, -1); 422 ERROR0("ERROR: MIDI_OUT_SendLongMessage: midiOutLongMsg returned error:\n"); 423 MIDIOUT_CHECK_ERROR; 424 return (INT32) err; 425 } 426 remainingSize -= size; 427 data += size; 428 } 429 TRACE0("< MIDI_OUT_SendLongMessage success\n"); 430 return MIDI_SUCCESS; 431 } 432 433 #endif // USE_PLATFORM_MIDI_OUT