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