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