src/java.desktop/unix/native/libjsound/PLATFORM_API_LinuxOS_ALSA_MidiIn.c
changeset 49289 148e29df1644
parent 49288 6e2d71029781
child 49290 07779973cbe2
equal deleted inserted replaced
49288:6e2d71029781 49289:148e29df1644
     1 /*
       
     2  * Copyright (c) 2003, 2010, 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 #if USE_PLATFORM_MIDI_IN == TRUE
       
    30 
       
    31 
       
    32 #include <alsa/asoundlib.h>
       
    33 #include "PlatformMidi.h"
       
    34 #include "PLATFORM_API_LinuxOS_ALSA_MidiUtils.h"
       
    35 #if defined(i586)
       
    36 #include <sys/utsname.h>
       
    37 #endif
       
    38 
       
    39 /*
       
    40  * Helper methods
       
    41  */
       
    42 
       
    43 static inline UINT32 packMessage(int status, int data1, int data2) {
       
    44     return ((status & 0xFF) | ((data1 & 0xFF) << 8) | ((data2 & 0xFF) << 16));
       
    45 }
       
    46 
       
    47 
       
    48 static void setShortMessage(MidiMessage* message,
       
    49                             int status, int data1, int data2) {
       
    50     message->type = SHORT_MESSAGE;
       
    51     message->data.s.packedMsg = packMessage(status, data1, data2);
       
    52 }
       
    53 
       
    54 
       
    55 static void setRealtimeMessage(MidiMessage* message, int status) {
       
    56     setShortMessage(message, status, 0, 0);
       
    57 }
       
    58 
       
    59 
       
    60 static void set14bitMessage(MidiMessage* message, int status, int value) {
       
    61     TRACE3("14bit value: %d, lsb: %d, msb: %d\n", value, value & 0x7F, (value >> 7) & 0x7F);
       
    62     value &= 0x3FFF;
       
    63     TRACE3("14bit value (2): %d, lsb: %d, msb: %d\n", value, value & 0x7F, (value >> 7) & 0x7F);
       
    64     setShortMessage(message, status,
       
    65                     value & 0x7F,
       
    66                     (value >> 7) & 0x7F);
       
    67 }
       
    68 
       
    69 
       
    70 /*
       
    71  * implementation of the platform-dependent
       
    72  * MIDI in functions declared in PlatformMidi.h
       
    73  */
       
    74 
       
    75 char* MIDI_IN_GetErrorStr(INT32 err) {
       
    76     return (char*) getErrorStr(err);
       
    77 }
       
    78 
       
    79 INT32 MIDI_IN_GetNumDevices() {
       
    80 /* Workaround for 6842956: 32bit app on 64bit linux
       
    81  * gets assertion failure trying to open midiIn ports.
       
    82  * Untill the issue is fixed in ALSA
       
    83  * (https://bugtrack.alsa-project.org/alsa-bug/view.php?id=4807)
       
    84  * report no midi in devices in the configuration.
       
    85  */
       
    86 #if defined(i586)
       
    87     static int jre32onlinux64 = -1;
       
    88     if (jre32onlinux64 < 0) {
       
    89         jre32onlinux64 = 0;
       
    90         /* The workaround may be disabled setting "JAVASOUND_ENABLE_MIDIIN"
       
    91          * environment variable.
       
    92          */
       
    93         if (getenv("JAVASOUND_ENABLE_MIDIIN") == NULL) {
       
    94             struct utsname u;
       
    95             jre32onlinux64 = 0;
       
    96             if (uname(&u) == 0) {
       
    97                 if (strstr(u.machine, "64") != NULL) {
       
    98                     TRACE0("jre32 on linux64 detected - report no midiIn devices\n");
       
    99                     jre32onlinux64 = 1;
       
   100                 }
       
   101             }
       
   102         }
       
   103     }
       
   104     if (jre32onlinux64) {
       
   105         return 0;
       
   106     }
       
   107 #endif
       
   108 
       
   109     TRACE0("MIDI_IN_GetNumDevices()\n");
       
   110 
       
   111     return getMidiDeviceCount(SND_RAWMIDI_STREAM_INPUT);
       
   112 }
       
   113 
       
   114 
       
   115 INT32 MIDI_IN_GetDeviceName(INT32 deviceIndex, char *name, UINT32 nameLength) {
       
   116     int ret = getMidiDeviceName(SND_RAWMIDI_STREAM_INPUT, deviceIndex,
       
   117                                 name, nameLength);
       
   118     return ret;
       
   119 }
       
   120 
       
   121 
       
   122 INT32 MIDI_IN_GetDeviceVendor(INT32 deviceIndex, char *name, UINT32 nameLength) {
       
   123     int ret = getMidiDeviceVendor(deviceIndex, name, nameLength);
       
   124     return ret;
       
   125 }
       
   126 
       
   127 
       
   128 INT32 MIDI_IN_GetDeviceDescription(INT32 deviceIndex, char *name, UINT32 nameLength) {
       
   129     int ret = getMidiDeviceDescription(SND_RAWMIDI_STREAM_INPUT, deviceIndex,
       
   130                                        name, nameLength);
       
   131     return ret;
       
   132 }
       
   133 
       
   134 
       
   135 INT32 MIDI_IN_GetDeviceVersion(INT32 deviceIndex, char *name, UINT32 nameLength) {
       
   136     int ret = getMidiDeviceVersion(deviceIndex, name, nameLength);
       
   137     return ret;
       
   138 }
       
   139 
       
   140 /*************************************************************************/
       
   141 
       
   142 INT32 MIDI_IN_OpenDevice(INT32 deviceIndex, MidiDeviceHandle** handle) {
       
   143     INT32 ret;
       
   144     TRACE0("> MIDI_IN_OpenDevice\n");
       
   145     ret = openMidiDevice(SND_RAWMIDI_STREAM_INPUT, deviceIndex, handle);
       
   146     TRACE1("< MIDI_IN_OpenDevice: returning %d\n", (int) ret);
       
   147     return ret;
       
   148 }
       
   149 
       
   150 
       
   151 INT32 MIDI_IN_CloseDevice(MidiDeviceHandle* handle) {
       
   152     INT32 ret;
       
   153     TRACE0("> MIDI_IN_CloseDevice\n");
       
   154     ret = closeMidiDevice(handle);
       
   155     TRACE1("< MIDI_IN_CloseDevice: returning %d\n", (int) ret);
       
   156     return ret;
       
   157 }
       
   158 
       
   159 
       
   160 INT32 MIDI_IN_StartDevice(MidiDeviceHandle* handle) {
       
   161     TRACE0("MIDI_IN_StartDevice\n");
       
   162     return MIDI_SUCCESS;
       
   163 }
       
   164 
       
   165 
       
   166 INT32 MIDI_IN_StopDevice(MidiDeviceHandle* handle) {
       
   167     TRACE0("MIDI_IN_StopDevice\n");
       
   168     return MIDI_SUCCESS;
       
   169 }
       
   170 
       
   171 
       
   172 INT64 MIDI_IN_GetTimeStamp(MidiDeviceHandle* handle) {
       
   173     return getMidiTimestamp(handle);
       
   174 }
       
   175 
       
   176 
       
   177 /* read the next message from the queue */
       
   178 MidiMessage* MIDI_IN_GetMessage(MidiDeviceHandle* handle) {
       
   179     snd_seq_event_t alsa_message;
       
   180     MidiMessage* jdk_message;
       
   181     int err;
       
   182     char buffer[1];
       
   183     int status;
       
   184 
       
   185     TRACE0("> MIDI_IN_GetMessage\n");
       
   186     if (!handle) {
       
   187         ERROR0("< ERROR: MIDI_IN_GetMessage(): handle is NULL\n");
       
   188         return NULL;
       
   189     }
       
   190     if (!handle->deviceHandle) {
       
   191         ERROR0("< ERROR: MIDI_IN_GetMessage(): native handle is NULL\n");
       
   192         return NULL;
       
   193     }
       
   194     if (!handle->platformData) {
       
   195         ERROR0("< ERROR: MIDI_IN_GetMessage(): platformData is NULL\n");
       
   196         return NULL;
       
   197     }
       
   198 
       
   199     /* For MIDI In, the device is left in non blocking mode. So if there is
       
   200        no data from the device, snd_rawmidi_read() returns with -11 (EAGAIN).
       
   201        This results in jumping back to the Java layer. */
       
   202     while (TRUE) {
       
   203         TRACE0("before snd_rawmidi_read()\n");
       
   204         err = snd_rawmidi_read((snd_rawmidi_t*) handle->deviceHandle, buffer, 1);
       
   205         TRACE0("after snd_rawmidi_read()\n");
       
   206         if (err != 1) {
       
   207             ERROR2("< ERROR: MIDI_IN_GetMessage(): snd_rawmidi_read() returned %d : %s\n", err, snd_strerror(err));
       
   208             return NULL;
       
   209         }
       
   210         // printf("received byte: %d\n", buffer[0]);
       
   211         err = snd_midi_event_encode_byte((snd_midi_event_t*) handle->platformData,
       
   212                                          (int) buffer[0],
       
   213                                          &alsa_message);
       
   214         if (err == 1) {
       
   215             break;
       
   216         } else if (err < 0) {
       
   217             ERROR1("< ERROR: MIDI_IN_GetMessage(): snd_midi_event_encode_byte() returned %d\n", err);
       
   218             return NULL;
       
   219         }
       
   220     }
       
   221     jdk_message = (MidiMessage*) calloc(sizeof(MidiMessage), 1);
       
   222     if (!jdk_message) {
       
   223         ERROR0("< ERROR: MIDI_IN_GetMessage(): out of memory\n");
       
   224         return NULL;
       
   225     }
       
   226     // TODO: tra
       
   227     switch (alsa_message.type) {
       
   228     case SND_SEQ_EVENT_NOTEON:
       
   229     case SND_SEQ_EVENT_NOTEOFF:
       
   230     case SND_SEQ_EVENT_KEYPRESS:
       
   231         status = (alsa_message.type == SND_SEQ_EVENT_KEYPRESS) ? 0xA0 :
       
   232             (alsa_message.type == SND_SEQ_EVENT_NOTEON) ? 0x90 : 0x80;
       
   233         status |= alsa_message.data.note.channel;
       
   234         setShortMessage(jdk_message, status,
       
   235                         alsa_message.data.note.note,
       
   236                         alsa_message.data.note.velocity);
       
   237         break;
       
   238 
       
   239     case SND_SEQ_EVENT_CONTROLLER:
       
   240         status = 0xB0 | alsa_message.data.control.channel;
       
   241         setShortMessage(jdk_message, status,
       
   242                         alsa_message.data.control.param,
       
   243                         alsa_message.data.control.value);
       
   244         break;
       
   245 
       
   246     case SND_SEQ_EVENT_PGMCHANGE:
       
   247     case SND_SEQ_EVENT_CHANPRESS:
       
   248         status = (alsa_message.type == SND_SEQ_EVENT_PGMCHANGE) ? 0xC0 : 0xD0;
       
   249         status |= alsa_message.data.control.channel;
       
   250         setShortMessage(jdk_message, status,
       
   251                         alsa_message.data.control.value, 0);
       
   252         break;
       
   253 
       
   254     case SND_SEQ_EVENT_PITCHBEND:
       
   255         status = 0xE0 | alsa_message.data.control.channel;
       
   256         // $$mp 2003-09-23:
       
   257         // possible hack to work around a bug in ALSA. Necessary for
       
   258         // ALSA 0.9.2. May be fixed in newer versions of ALSA.
       
   259         // alsa_message.data.control.value ^= 0x2000;
       
   260         // TRACE1("pitchbend value: %d\n", alsa_message.data.control.value);
       
   261         set14bitMessage(jdk_message, status,
       
   262                         alsa_message.data.control.value);
       
   263         break;
       
   264 
       
   265         /* System exclusive messages */
       
   266 
       
   267     case SND_SEQ_EVENT_SYSEX:
       
   268         jdk_message->type = LONG_MESSAGE;
       
   269         jdk_message->data.l.size = alsa_message.data.ext.len;
       
   270         jdk_message->data.l.data = malloc(alsa_message.data.ext.len);
       
   271         if (jdk_message->data.l.data == NULL) {
       
   272             ERROR0("< ERROR: MIDI_IN_GetMessage(): out of memory\n");
       
   273             free(jdk_message);
       
   274             jdk_message = NULL;
       
   275         } else {
       
   276             memcpy(jdk_message->data.l.data, alsa_message.data.ext.ptr, alsa_message.data.ext.len);
       
   277         }
       
   278         break;
       
   279 
       
   280         /* System common messages */
       
   281 
       
   282     case SND_SEQ_EVENT_QFRAME:
       
   283         setShortMessage(jdk_message, 0xF1,
       
   284                         alsa_message.data.control.value & 0x7F, 0);
       
   285         break;
       
   286 
       
   287     case SND_SEQ_EVENT_SONGPOS:
       
   288         set14bitMessage(jdk_message, 0xF2,
       
   289                         alsa_message.data.control.value);
       
   290         break;
       
   291 
       
   292     case SND_SEQ_EVENT_SONGSEL:
       
   293         setShortMessage(jdk_message, 0xF3,
       
   294                         alsa_message.data.control.value & 0x7F, 0);
       
   295         break;
       
   296 
       
   297     case SND_SEQ_EVENT_TUNE_REQUEST:
       
   298         setRealtimeMessage(jdk_message, 0xF6);
       
   299         break;
       
   300 
       
   301         /* System realtime messages */
       
   302 
       
   303     case SND_SEQ_EVENT_CLOCK:
       
   304         setRealtimeMessage(jdk_message, 0xF8);
       
   305         break;
       
   306 
       
   307     case SND_SEQ_EVENT_START:
       
   308         setRealtimeMessage(jdk_message, 0xFA);
       
   309         break;
       
   310 
       
   311     case SND_SEQ_EVENT_CONTINUE:
       
   312         setRealtimeMessage(jdk_message, 0xFB);
       
   313         break;
       
   314 
       
   315     case SND_SEQ_EVENT_STOP:
       
   316         setRealtimeMessage(jdk_message, 0xFC);
       
   317         break;
       
   318 
       
   319     case SND_SEQ_EVENT_SENSING:
       
   320         setRealtimeMessage(jdk_message, 0xFE);
       
   321         break;
       
   322 
       
   323     case SND_SEQ_EVENT_RESET:
       
   324         setRealtimeMessage(jdk_message, 0xFF);
       
   325         break;
       
   326 
       
   327     default:
       
   328         ERROR0("< ERROR: MIDI_IN_GetMessage(): unhandled ALSA MIDI message type\n");
       
   329         free(jdk_message);
       
   330         jdk_message = NULL;
       
   331 
       
   332     }
       
   333 
       
   334     // set timestamp
       
   335     if (jdk_message != NULL) {
       
   336         jdk_message->timestamp = getMidiTimestamp(handle);
       
   337     }
       
   338     TRACE1("< MIDI_IN_GetMessage: returning %p\n", jdk_message);
       
   339     return jdk_message;
       
   340 }
       
   341 
       
   342 
       
   343 void MIDI_IN_ReleaseMessage(MidiDeviceHandle* handle, MidiMessage* msg) {
       
   344     if (!msg) {
       
   345         ERROR0("< ERROR: MIDI_IN_ReleaseMessage(): message is NULL\n");
       
   346         return;
       
   347     }
       
   348     if (msg->type == LONG_MESSAGE && msg->data.l.data) {
       
   349         free(msg->data.l.data);
       
   350     }
       
   351     free(msg);
       
   352 }
       
   353 
       
   354 #endif /* USE_PLATFORM_MIDI_IN */