1 /* 2 * Copyright (c) 2003, 2014, 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_LinuxOS_ALSA_MidiUtils.h" 30 #include "PLATFORM_API_LinuxOS_ALSA_CommonUtils.h" 31 #include <string.h> 32 #include <sys/time.h> 33 34 static INT64 getTimeInMicroseconds() { 35 struct timeval tv; 36 37 gettimeofday(&tv, NULL); 38 return (tv.tv_sec * 1000000UL) + tv.tv_usec; 39 } 40 41 42 const char* getErrorStr(INT32 err) { 43 return snd_strerror((int) err); 44 } 45 46 47 48 // callback for iteration through devices 49 // returns TRUE if iteration should continue 50 typedef int (*DeviceIteratorPtr)(UINT32 deviceID, 51 snd_rawmidi_info_t* rawmidi_info, 52 snd_ctl_card_info_t* cardinfo, 53 void *userData); 54 55 // for each ALSA device, call iterator. userData is passed to the iterator 56 // returns total number of iterations 57 static int iterateRawmidiDevices(snd_rawmidi_stream_t direction, 58 DeviceIteratorPtr iterator, 59 void* userData) { 60 int count = 0; 61 int subdeviceCount; 62 int card, dev, subDev; 63 char devname[16]; 64 int err; 65 snd_ctl_t *handle; 66 snd_rawmidi_t *rawmidi; 67 snd_rawmidi_info_t *rawmidi_info; 68 snd_ctl_card_info_t *card_info, *defcardinfo = NULL; 69 UINT32 deviceID; 70 int doContinue = TRUE; 71 72 snd_rawmidi_info_malloc(&rawmidi_info); 73 snd_ctl_card_info_malloc(&card_info); 74 75 // 1st try "default" device 76 if (direction == SND_RAWMIDI_STREAM_INPUT) { 77 err = snd_rawmidi_open(&rawmidi, NULL, ALSA_DEFAULT_DEVICE_NAME, 78 SND_RAWMIDI_NONBLOCK); 79 } else if (direction == SND_RAWMIDI_STREAM_OUTPUT) { 80 err = snd_rawmidi_open(NULL, &rawmidi, ALSA_DEFAULT_DEVICE_NAME, 81 SND_RAWMIDI_NONBLOCK); 82 } else { 83 ERROR0("ERROR: iterateRawmidiDevices(): direction is neither" 84 " SND_RAWMIDI_STREAM_INPUT nor SND_RAWMIDI_STREAM_OUTPUT\n"); 85 err = MIDI_INVALID_ARGUMENT; 86 } 87 if (err < 0) { 88 ERROR1("ERROR: snd_rawmidi_open (\"default\"): %s\n", 89 snd_strerror(err)); 90 } else { 91 err = snd_rawmidi_info(rawmidi, rawmidi_info); 92 93 snd_rawmidi_close(rawmidi); 94 if (err < 0) { 95 ERROR1("ERROR: snd_rawmidi_info (\"default\"): %s\n", 96 snd_strerror(err)); 97 } else { 98 // try to get card info 99 card = snd_rawmidi_info_get_card(rawmidi_info); 100 if (card >= 0) { 101 sprintf(devname, ALSA_HARDWARE_CARD, card); 102 if (snd_ctl_open(&handle, devname, SND_CTL_NONBLOCK) >= 0) { 103 if (snd_ctl_card_info(handle, card_info) >= 0) { 104 defcardinfo = card_info; 105 } 106 snd_ctl_close(handle); 107 } 108 } 109 // call calback function for the device 110 if (iterator != NULL) { 111 doContinue = (*iterator)(ALSA_DEFAULT_DEVICE_ID, rawmidi_info, 112 defcardinfo, userData); 113 } 114 count++; 115 } 116 } 117 118 // iterate cards 119 card = -1; 120 TRACE0("testing for cards...\n"); 121 if (snd_card_next(&card) >= 0) { 122 TRACE1("Found card %d\n", card); 123 while (doContinue && (card >= 0)) { 124 sprintf(devname, ALSA_HARDWARE_CARD, card); 125 TRACE1("Opening control for alsa rawmidi device \"%s\"...\n", devname); 126 err = snd_ctl_open(&handle, devname, SND_CTL_NONBLOCK); 127 if (err < 0) { 128 ERROR2("ERROR: snd_ctl_open, card=%d: %s\n", card, snd_strerror(err)); 129 } else { 130 TRACE0("snd_ctl_open() SUCCESS\n"); 131 err = snd_ctl_card_info(handle, card_info); 132 if (err < 0) { 133 ERROR2("ERROR: snd_ctl_card_info, card=%d: %s\n", card, snd_strerror(err)); 134 } else { 135 TRACE0("snd_ctl_card_info() SUCCESS\n"); 136 dev = -1; 137 while (doContinue) { 138 if (snd_ctl_rawmidi_next_device(handle, &dev) < 0) { 139 ERROR0("snd_ctl_rawmidi_next_device\n"); 140 } 141 TRACE0("snd_ctl_rawmidi_next_device() SUCCESS\n"); 142 if (dev < 0) { 143 break; 144 } 145 snd_rawmidi_info_set_device(rawmidi_info, dev); 146 snd_rawmidi_info_set_subdevice(rawmidi_info, 0); 147 snd_rawmidi_info_set_stream(rawmidi_info, direction); 148 err = snd_ctl_rawmidi_info(handle, rawmidi_info); 149 TRACE0("after snd_ctl_rawmidi_info()\n"); 150 if (err < 0) { 151 if (err != -ENOENT) { 152 ERROR2("ERROR: snd_ctl_rawmidi_info, card=%d: %s", card, snd_strerror(err)); 153 } 154 } else { 155 TRACE0("snd_ctl_rawmidi_info() SUCCESS\n"); 156 subdeviceCount = needEnumerateSubdevices(ALSA_RAWMIDI) 157 ? snd_rawmidi_info_get_subdevices_count(rawmidi_info) 158 : 1; 159 if (iterator!=NULL) { 160 for (subDev = 0; subDev < subdeviceCount; subDev++) { 161 TRACE3(" Iterating %d,%d,%d\n", card, dev, subDev); 162 deviceID = encodeDeviceID(card, dev, subDev); 163 doContinue = (*iterator)(deviceID, rawmidi_info, 164 card_info, userData); 165 count++; 166 TRACE0("returned from iterator\n"); 167 if (!doContinue) { 168 break; 169 } 170 } 171 } else { 172 count += subdeviceCount; 173 } 174 } 175 } // of while(doContinue) 176 } 177 snd_ctl_close(handle); 178 } 179 if (snd_card_next(&card) < 0) { 180 break; 181 } 182 } 183 } else { 184 ERROR0("No cards found!\n"); 185 } 186 snd_ctl_card_info_free(card_info); 187 snd_rawmidi_info_free(rawmidi_info); 188 return count; 189 } 190 191 192 193 int getMidiDeviceCount(snd_rawmidi_stream_t direction) { 194 int deviceCount; 195 TRACE0("> getMidiDeviceCount()\n"); 196 initAlsaSupport(); 197 deviceCount = iterateRawmidiDevices(direction, NULL, NULL); 198 TRACE0("< getMidiDeviceCount()\n"); 199 return deviceCount; 200 } 201 202 203 204 /* 205 userData is assumed to be a pointer to ALSA_MIDIDeviceDescription. 206 ALSA_MIDIDeviceDescription->index has to be set to the index of the device 207 we want to get information of before this method is called the first time via 208 iterateRawmidiDevices(). On each call of this method, 209 ALSA_MIDIDeviceDescription->index is decremented. If it is equal to zero, 210 we have reached the desired device, so action is taken. 211 So after successful completion of iterateRawmidiDevices(), 212 ALSA_MIDIDeviceDescription->index is zero. If it isn't, this is an 213 indication of an error. 214 */ 215 static int deviceInfoIterator(UINT32 deviceID, snd_rawmidi_info_t *rawmidi_info, 216 snd_ctl_card_info_t *cardinfo, void *userData) { 217 char buffer[300]; 218 ALSA_MIDIDeviceDescription* desc = (ALSA_MIDIDeviceDescription*)userData; 219 #ifdef ALSA_MIDI_USE_PLUGHW 220 int usePlugHw = 1; 221 #else 222 int usePlugHw = 0; 223 #endif 224 225 TRACE0("deviceInfoIterator\n"); 226 initAlsaSupport(); 227 if (desc->index == 0) { 228 // we found the device with correct index 229 desc->deviceID = deviceID; 230 231 buffer[0]=' '; buffer[1]='['; 232 // buffer[300] is enough to store the actual device string w/o overrun 233 getDeviceStringFromDeviceID(&buffer[2], deviceID, usePlugHw, ALSA_RAWMIDI); 234 strncat(buffer, "]", sizeof(buffer) - strlen(buffer) - 1); 235 strncpy(desc->name, 236 (cardinfo != NULL) 237 ? snd_ctl_card_info_get_id(cardinfo) 238 : snd_rawmidi_info_get_id(rawmidi_info), 239 desc->strLen - strlen(buffer)); 240 strncat(desc->name, buffer, desc->strLen - strlen(desc->name)); 241 desc->description[0] = 0; 242 if (cardinfo != NULL) { 243 strncpy(desc->description, snd_ctl_card_info_get_name(cardinfo), 244 desc->strLen); 245 strncat(desc->description, ", ", 246 desc->strLen - strlen(desc->description)); 247 } 248 strncat(desc->description, snd_rawmidi_info_get_id(rawmidi_info), 249 desc->strLen - strlen(desc->description)); 250 strncat(desc->description, ", ", desc->strLen - strlen(desc->description)); 251 strncat(desc->description, snd_rawmidi_info_get_name(rawmidi_info), 252 desc->strLen - strlen(desc->description)); 253 TRACE2("Returning %s, %s\n", desc->name, desc->description); 254 return FALSE; // do not continue iteration 255 } 256 desc->index--; 257 return TRUE; 258 } 259 260 261 static int getMIDIDeviceDescriptionByIndex(snd_rawmidi_stream_t direction, 262 ALSA_MIDIDeviceDescription* desc) { 263 initAlsaSupport(); 264 TRACE1(" getMIDIDeviceDescriptionByIndex (index = %d)\n", desc->index); 265 iterateRawmidiDevices(direction, &deviceInfoIterator, desc); 266 return (desc->index == 0) ? MIDI_SUCCESS : MIDI_INVALID_DEVICEID; 267 } 268 269 270 271 int initMIDIDeviceDescription(ALSA_MIDIDeviceDescription* desc, int index) { 272 int ret = MIDI_SUCCESS; 273 desc->index = index; 274 desc->strLen = 200; 275 desc->name = (char*) calloc(desc->strLen + 1, 1); 276 desc->description = (char*) calloc(desc->strLen + 1, 1); 277 if (! desc->name || 278 ! desc->description) { 279 ret = MIDI_OUT_OF_MEMORY; 280 } 281 return ret; 282 } 283 284 285 void freeMIDIDeviceDescription(ALSA_MIDIDeviceDescription* desc) { 286 if (desc->name) { 287 free(desc->name); 288 } 289 if (desc->description) { 290 free(desc->description); 291 } 292 } 293 294 295 int getMidiDeviceName(snd_rawmidi_stream_t direction, int index, char *name, 296 UINT32 nameLength) { 297 ALSA_MIDIDeviceDescription desc; 298 int ret; 299 300 TRACE1("getMidiDeviceName: nameLength: %d\n", (int) nameLength); 301 ret = initMIDIDeviceDescription(&desc, index); 302 if (ret == MIDI_SUCCESS) { 303 TRACE0("getMidiDeviceName: initMIDIDeviceDescription() SUCCESS\n"); 304 ret = getMIDIDeviceDescriptionByIndex(direction, &desc); 305 if (ret == MIDI_SUCCESS) { 306 TRACE1("getMidiDeviceName: desc.name: %s\n", desc.name); 307 strncpy(name, desc.name, nameLength - 1); 308 name[nameLength - 1] = 0; 309 } 310 } 311 freeMIDIDeviceDescription(&desc); 312 return ret; 313 } 314 315 316 int getMidiDeviceVendor(int index, char *name, UINT32 nameLength) { 317 strncpy(name, ALSA_VENDOR, nameLength - 1); 318 name[nameLength - 1] = 0; 319 return MIDI_SUCCESS; 320 } 321 322 323 int getMidiDeviceDescription(snd_rawmidi_stream_t direction, 324 int index, char *name, UINT32 nameLength) { 325 ALSA_MIDIDeviceDescription desc; 326 int ret; 327 328 ret = initMIDIDeviceDescription(&desc, index); 329 if (ret == MIDI_SUCCESS) { 330 ret = getMIDIDeviceDescriptionByIndex(direction, &desc); 331 if (ret == MIDI_SUCCESS) { 332 strncpy(name, desc.description, nameLength - 1); 333 name[nameLength - 1] = 0; 334 } 335 } 336 freeMIDIDeviceDescription(&desc); 337 return ret; 338 } 339 340 341 int getMidiDeviceVersion(int index, char *name, UINT32 nameLength) { 342 getALSAVersion(name, nameLength); 343 return MIDI_SUCCESS; 344 } 345 346 347 static int getMidiDeviceID(snd_rawmidi_stream_t direction, int index, 348 UINT32* deviceID) { 349 ALSA_MIDIDeviceDescription desc; 350 int ret; 351 352 ret = initMIDIDeviceDescription(&desc, index); 353 if (ret == MIDI_SUCCESS) { 354 ret = getMIDIDeviceDescriptionByIndex(direction, &desc); 355 if (ret == MIDI_SUCCESS) { 356 // TRACE1("getMidiDeviceName: desc.name: %s\n", desc.name); 357 *deviceID = desc.deviceID; 358 } 359 } 360 freeMIDIDeviceDescription(&desc); 361 return ret; 362 } 363 364 365 /* 366 direction has to be either SND_RAWMIDI_STREAM_INPUT or 367 SND_RAWMIDI_STREAM_OUTPUT. 368 Returns 0 on success. Otherwise, MIDI_OUT_OF_MEMORY, MIDI_INVALID_ARGUMENT 369 or a negative ALSA error code is returned. 370 */ 371 INT32 openMidiDevice(snd_rawmidi_stream_t direction, INT32 deviceIndex, 372 MidiDeviceHandle** handle) { 373 snd_rawmidi_t* native_handle; 374 snd_midi_event_t* event_parser = NULL; 375 int err; 376 UINT32 deviceID = 0; 377 char devicename[100]; 378 #ifdef ALSA_MIDI_USE_PLUGHW 379 int usePlugHw = 1; 380 #else 381 int usePlugHw = 0; 382 #endif 383 384 TRACE0("> openMidiDevice()\n"); 385 386 (*handle) = (MidiDeviceHandle*) calloc(sizeof(MidiDeviceHandle), 1); 387 if (!(*handle)) { 388 ERROR0("ERROR: openDevice: out of memory\n"); 389 return MIDI_OUT_OF_MEMORY; 390 } 391 392 // TODO: iterate to get dev ID from index 393 err = getMidiDeviceID(direction, deviceIndex, &deviceID); 394 TRACE1(" openMidiDevice(): deviceID: %d\n", (int) deviceID); 395 getDeviceStringFromDeviceID(devicename, deviceID, 396 usePlugHw, ALSA_RAWMIDI); 397 TRACE1(" openMidiDevice(): deviceString: %s\n", devicename); 398 399 // finally open the device 400 if (direction == SND_RAWMIDI_STREAM_INPUT) { 401 err = snd_rawmidi_open(&native_handle, NULL, devicename, 402 SND_RAWMIDI_NONBLOCK); 403 } else if (direction == SND_RAWMIDI_STREAM_OUTPUT) { 404 err = snd_rawmidi_open(NULL, &native_handle, devicename, 405 SND_RAWMIDI_NONBLOCK); 406 } else { 407 ERROR0(" ERROR: openMidiDevice(): direction is neither SND_RAWMIDI_STREAM_INPUT nor SND_RAWMIDI_STREAM_OUTPUT\n"); 408 err = MIDI_INVALID_ARGUMENT; 409 } 410 if (err < 0) { 411 ERROR1("< ERROR: openMidiDevice(): snd_rawmidi_open() returned %d\n", err); 412 free(*handle); 413 (*handle) = NULL; 414 return err; 415 } 416 /* We opened with non-blocking behaviour to not get hung if the device 417 is used by a different process. Writing, however, should 418 be blocking. So we change it here. */ 419 if (direction == SND_RAWMIDI_STREAM_OUTPUT) { 420 err = snd_rawmidi_nonblock(native_handle, 0); 421 if (err < 0) { 422 ERROR1(" ERROR: openMidiDevice(): snd_rawmidi_nonblock() returned %d\n", err); 423 snd_rawmidi_close(native_handle); 424 free(*handle); 425 (*handle) = NULL; 426 return err; 427 } 428 } 429 if (direction == SND_RAWMIDI_STREAM_INPUT) { 430 err = snd_midi_event_new(EVENT_PARSER_BUFSIZE, &event_parser); 431 if (err < 0) { 432 ERROR1(" ERROR: openMidiDevice(): snd_midi_event_new() returned %d\n", err); 433 snd_rawmidi_close(native_handle); 434 free(*handle); 435 (*handle) = NULL; 436 return err; 437 } 438 } 439 440 (*handle)->deviceHandle = (void*) native_handle; 441 (*handle)->startTime = getTimeInMicroseconds(); 442 (*handle)->platformData = event_parser; 443 TRACE0("< openMidiDevice(): succeeded\n"); 444 return err; 445 } 446 447 448 449 INT32 closeMidiDevice(MidiDeviceHandle* handle) { 450 int err; 451 452 TRACE0("> closeMidiDevice()\n"); 453 if (!handle) { 454 ERROR0("< ERROR: closeMidiDevice(): handle is NULL\n"); 455 return MIDI_INVALID_HANDLE; 456 } 457 if (!handle->deviceHandle) { 458 ERROR0("< ERROR: closeMidiDevice(): native handle is NULL\n"); 459 return MIDI_INVALID_HANDLE; 460 } 461 err = snd_rawmidi_close((snd_rawmidi_t*) handle->deviceHandle); 462 TRACE1(" snd_rawmidi_close() returns %d\n", err); 463 if (handle->platformData) { 464 snd_midi_event_free((snd_midi_event_t*) handle->platformData); 465 } 466 free(handle); 467 TRACE0("< closeMidiDevice: succeeded\n"); 468 return err; 469 } 470 471 472 INT64 getMidiTimestamp(MidiDeviceHandle* handle) { 473 if (!handle) { 474 ERROR0("< ERROR: closeMidiDevice(): handle is NULL\n"); 475 return MIDI_INVALID_HANDLE; 476 } 477 return getTimeInMicroseconds() - handle->startTime; 478 } 479 480 481 /* end */