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
34static INT64 getTimeInMicroseconds() {
35 struct timeval tv;
36
37 gettimeofday(&tv, NULL);
38 return (tv.tv_sec * 1000000UL) + tv.tv_usec;
39}
40
41
42const 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
50typedef 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
57static 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
193int 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*/
215static 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
261static 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
271int 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
285void 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
295int 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
316int 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
323int 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
341int getMidiDeviceVersion(int index, char *name, UINT32 nameLength) {
342 getALSAVersion(name, nameLength);
343 return MIDI_SUCCESS;
344}
345
346
347static 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*/
371INT32 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
449INT32 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
472INT64 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 */
482