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 /*****************************************************************************/
  27 /*
  28 **      Native functions for interfacing Java with the native implementation
  29 **      of PlatformMidi.h's functions.
  30 */
  31 /*****************************************************************************/
  32 
  33 #define USE_ERROR
  34 #define USE_TRACE
  35 
  36 
  37 #include <jni.h>
  38 /* for memcpy */
  39 #include <string.h>
  40 #include "SoundDefs.h"
  41 #include "PlatformMidi.h"
  42 #include "com_sun_media_sound_MidiInDevice.h"
  43 
  44 
  45 JNIEXPORT jlong JNICALL
  46 Java_com_sun_media_sound_MidiInDevice_nOpen(JNIEnv* e, jobject thisObj, jint index) {
  47 
  48     MidiDeviceHandle* deviceHandle = NULL;
  49     INT32 err = MIDI_NOT_SUPPORTED;
  50 
  51     TRACE1("> Java_com_sun_media_sound_MidiInDevice_nOpen: index: %d\n", index);
  52 
  53 #if USE_PLATFORM_MIDI_IN == TRUE
  54     err = MIDI_IN_OpenDevice((INT32) index, &deviceHandle);
  55 #endif
  56 
  57     /* $$mp 2003-08-28:
  58        So far, the return value (err) hasn't been taken into account.
  59        Now, it is also expected to be MIDI_SUCCESS (0).
  60        This works for Linux, but has to be checked on other platforms.
  61 
  62        It would be better to settle on one method of signaling error:
  63        either returned error codes or a NULL handle. If the latter is used,
  64        the return value should be removed from the signature of
  65        MIDI_IN_OpenDevice.
  66     */
  67     // if we didn't get a valid handle, throw a MidiUnavailableException
  68     if (!deviceHandle || err != MIDI_SUCCESS) {
  69         deviceHandle = NULL;
  70         ERROR0("Java_com_sun_media_sound_MidiInDevice_nOpen: ");
  71         ThrowJavaMessageException(e, JAVA_MIDI_PACKAGE_NAME"/MidiUnavailableException",
  72                                   MIDI_IN_InternalGetErrorString(err));
  73     } else {
  74         TRACE0("< Java_com_sun_media_sound_MidiInDevice_nOpen succeeded\n");
  75     }
  76     return (jlong) (UINT_PTR) deviceHandle;
  77 }
  78 
  79 
  80 JNIEXPORT void JNICALL
  81 Java_com_sun_media_sound_MidiInDevice_nClose(JNIEnv* e, jobject thisObj, jlong deviceHandle) {
  82 
  83     TRACE0("> Java_com_sun_media_sound_MidiInDevice_nClose.\n");
  84 
  85 #if USE_PLATFORM_MIDI_IN == TRUE
  86     MIDI_IN_CloseDevice((MidiDeviceHandle*) (UINT_PTR) deviceHandle);
  87 #endif
  88 
  89     TRACE0("< Java_com_sun_media_sound_MidiInDevice_nClose succeeded\n");
  90 }
  91 
  92 
  93 JNIEXPORT void JNICALL
  94 Java_com_sun_media_sound_MidiInDevice_nStart(JNIEnv* e, jobject thisObj, jlong deviceHandle) {
  95 
  96     INT32 err = MIDI_NOT_SUPPORTED;
  97 
  98     TRACE0("> Java_com_sun_media_sound_MidiInDevice_nStart.\n");
  99 
 100 #if USE_PLATFORM_MIDI_IN == TRUE
 101     err = MIDI_IN_StartDevice((MidiDeviceHandle*) (UINT_PTR) deviceHandle);
 102 #endif
 103 
 104     if (err != MIDI_SUCCESS) {
 105         ERROR0("Java_com_sun_media_sound_MidiInDevice_nStart: ");
 106         ThrowJavaMessageException(e, JAVA_MIDI_PACKAGE_NAME"/MidiUnavailableException",
 107                                   MIDI_IN_InternalGetErrorString(err));
 108     } else {
 109         TRACE0("< Java_com_sun_media_sound_MidiInDevice_nStart succeeded\n");
 110     }
 111 }
 112 
 113 
 114 JNIEXPORT void JNICALL
 115 Java_com_sun_media_sound_MidiInDevice_nStop(JNIEnv* e, jobject thisObj, jlong deviceHandle) {
 116 
 117     TRACE0("> Java_com_sun_media_sound_MidiInDevice_nStop.\n");
 118 
 119 #if USE_PLATFORM_MIDI_IN == TRUE
 120     // stop the device and remove all queued events for this device handle
 121     MIDI_IN_StopDevice((MidiDeviceHandle*) (UINT_PTR) deviceHandle);
 122 #endif
 123 
 124     TRACE0("< Java_com_sun_media_sound_MidiInDevice_nStop succeeded\n");
 125 }
 126 
 127 JNIEXPORT jlong JNICALL
 128 Java_com_sun_media_sound_MidiInDevice_nGetTimeStamp(JNIEnv* e, jobject thisObj, jlong deviceHandle) {
 129 
 130     jlong ret = -1;
 131 
 132     TRACE0("Java_com_sun_media_sound_MidiInDevice_nGetTimeStamp.\n");
 133 
 134 #if USE_PLATFORM_MIDI_IN == TRUE
 135     ret = (jlong) MIDI_IN_GetTimeStamp((MidiDeviceHandle*) (UINT_PTR) deviceHandle);
 136 #endif
 137 
 138     /* Handle error codes. */
 139     if (ret < -1) {
 140         ERROR1("Java_com_sun_media_sound_MidiInDevice_nGetTimeStamp: MIDI_IN_GetTimeStamp returned %lld\n", ret);
 141         ret = -1;
 142     }
 143     return ret;
 144 }
 145 
 146 
 147 JNIEXPORT void JNICALL
 148 Java_com_sun_media_sound_MidiInDevice_nGetMessages(JNIEnv* e, jobject thisObj, jlong deviceHandle) {
 149 
 150 #if USE_PLATFORM_MIDI_IN == TRUE
 151     MidiMessage* pMessage;
 152     jclass javaClass = NULL;
 153     jmethodID callbackShortMessageMethodID = NULL;
 154     jmethodID callbackLongMessageMethodID = NULL;
 155 #endif
 156 
 157     TRACE0("> Java_com_sun_media_sound_MidiInDevice_nGetMessages\n");
 158 
 159 #if USE_PLATFORM_MIDI_IN == TRUE
 160     while ((pMessage = MIDI_IN_GetMessage((MidiDeviceHandle*) (UINT_PTR) deviceHandle))) {
 161         if ((javaClass == NULL) || (callbackShortMessageMethodID == NULL)) {
 162             if (!thisObj) {
 163                 ERROR0("MidiInDevice: Java_com_sun_media_sound_MidiInDevice_nGetMessages: thisObj is NULL\n");
 164                 return;
 165             }
 166 
 167             if (javaClass == NULL) {
 168                 javaClass = (*e)->GetObjectClass(e, thisObj);
 169                 if (javaClass == NULL) {
 170                     ERROR0("MidiInDevice: Java_com_sun_media_sound_MidiInDevice_nGetMessages: javaClass is NULL\n");
 171                     return;
 172                 }
 173             }
 174 
 175             if (callbackShortMessageMethodID == NULL) {
 176                 // save the callbackShortMessage callback method id.
 177                 // this is valid as long as the class is not unloaded.
 178                 callbackShortMessageMethodID = (*e)->GetMethodID(e, javaClass, "callbackShortMessage", "(IJ)V");
 179                 if (callbackShortMessageMethodID == 0) {
 180                     ERROR0("MidiInDevice: Java_com_sun_media_sound_MidiInDevice_nGetMessages: callbackShortMessageMethodID is 0\n");
 181                     return;
 182                 }
 183             }
 184             if (callbackLongMessageMethodID == NULL) {
 185                 // save the callbackLongMessage callback method id.
 186                 // this is valid as long as the class is not unloaded.
 187                 callbackLongMessageMethodID = (*e)->GetMethodID(e, javaClass, "callbackLongMessage", "([BJ)V");
 188                 if (callbackLongMessageMethodID == 0) {
 189                     ERROR0("MidiInDevice: Java_com_sun_media_sound_MidiInDevice_nGetMessages: callbackLongMessageMethodID is 0\n");
 190                     return;
 191                 }
 192             }
 193         }
 194 
 195         switch ((int)pMessage->type) {
 196         case SHORT_MESSAGE: {
 197             jint msg = (jint)pMessage->data.s.packedMsg;
 198             jlong ts = (jlong)pMessage->timestamp;
 199             TRACE0("Java_com_sun_media_sound_MidiInDevice_nGetMessages: got SHORT_MESSAGE\n");
 200             // now we can put this message object back in the queue
 201             MIDI_IN_ReleaseMessage((MidiDeviceHandle*) (UINT_PTR) deviceHandle, pMessage);
 202             // and notify Java space
 203             (*e)->CallVoidMethod(e, thisObj, callbackShortMessageMethodID, msg, ts);
 204             break;
 205         }
 206 
 207         case LONG_MESSAGE: {
 208             jlong ts = (jlong)pMessage->timestamp;
 209             jbyteArray jData;
 210             UBYTE* data;
 211             int isSXCont = 0;
 212             TRACE0("Java_com_sun_media_sound_MidiInDevice_nGetMessages: got LONG_MESSAGE\n");
 213             if ((*(pMessage->data.l.data) != 0xF0)
 214                 && (*(pMessage->data.l.data) != 0xF7)) {
 215                 // this is a continued sys ex message
 216                 // need to prepend 0xF7
 217                 isSXCont = 1;
 218             }
 219             jData = (*e)->NewByteArray(e, pMessage->data.l.size + isSXCont);
 220             if (!jData) {
 221                 ERROR0("Java_com_sun_media_sound_MidiInDevice_nGetMessages: cannot create long byte array.\n");
 222                 break;
 223             }
 224             data = (UBYTE*) ((*e)->GetByteArrayElements(e, jData, NULL));
 225             if (!data) {
 226                 ERROR0("MidiInDevice: Java_com_sun_media_sound_MidiInDevice_nGetMessages: array data is NULL\n");
 227                 break;
 228             }
 229             // finally copy the long message
 230             memcpy(data + isSXCont, pMessage->data.l.data, pMessage->data.l.size);
 231 
 232             // now we can put this message object back in the queue
 233             MIDI_IN_ReleaseMessage((MidiDeviceHandle*) (UINT_PTR) deviceHandle, pMessage);
 234 
 235             // if this is a patched continued sys ex message, prepend 0xF7
 236             if (isSXCont) {
 237                 *data = 0xF7;
 238             }
 239 
 240             // commit the byte array
 241             (*e)->ReleaseByteArrayElements(e, jData, (jbyte*) data, (jint) 0);
 242 
 243             (*e)->CallVoidMethod(e, thisObj, callbackLongMessageMethodID, jData, ts);
 244             // release local reference to array: not needed anymore.
 245             (*e)->DeleteLocalRef(e, jData);
 246             break;
 247         }
 248 
 249         default:
 250             // put this message object back in the queue
 251             MIDI_IN_ReleaseMessage((MidiDeviceHandle*) (UINT_PTR) deviceHandle, pMessage);
 252             ERROR1("Java_com_sun_media_sound_MidiInDevice_nGetMessages: got unsupported message, type %d\n", pMessage->type);
 253             break;
 254         } // switch
 255     }
 256 
 257 #endif // USE_PLATFORM_MIDI_IN
 258 
 259     TRACE0("< Java_com_sun_media_sound_MidiInDevice_nGetMessages returning\n");
 260 }