blob: c7379bec24646b56d1b42b3c6f0f32235854aad3 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 getDeviceStringFromDeviceID(&buffer[2], deviceID, usePlugHw, ALSA_RAWMIDI);
233 strcat(buffer, "]");
234 strncpy(desc->name,
235 (cardinfo != NULL)
236 ? snd_ctl_card_info_get_id(cardinfo)
237 : snd_rawmidi_info_get_id(rawmidi_info),
238 desc->strLen - strlen(buffer));
239 strncat(desc->name, buffer, desc->strLen - strlen(desc->name));
240 desc->description[0] = 0;
241 if (cardinfo != NULL) {
242 strncpy(desc->description, snd_ctl_card_info_get_name(cardinfo),
243 desc->strLen);
244 strncat(desc->description, ", ",
245 desc->strLen - strlen(desc->description));
246 }
247 strncat(desc->description, snd_rawmidi_info_get_id(rawmidi_info),
248 desc->strLen - strlen(desc->description));
249 strncat(desc->description, ", ", desc->strLen - strlen(desc->description));
250 strncat(desc->description, snd_rawmidi_info_get_name(rawmidi_info),
251 desc->strLen - strlen(desc->description));
252 TRACE2("Returning %s, %s\n", desc->name, desc->description);
253 return FALSE; // do not continue iteration
254 }
255 desc->index--;
256 return TRUE;
257}
258
259
260static int getMIDIDeviceDescriptionByIndex(snd_rawmidi_stream_t direction,
261 ALSA_MIDIDeviceDescription* desc) {
262 initAlsaSupport();
263 TRACE1(" getMIDIDeviceDescriptionByIndex (index = %d)\n", desc->index);
264 iterateRawmidiDevices(direction, &deviceInfoIterator, desc);
265 return (desc->index == 0) ? MIDI_SUCCESS : MIDI_INVALID_DEVICEID;
266}
267
268
269
270int initMIDIDeviceDescription(ALSA_MIDIDeviceDescription* desc, int index) {
271 int ret = MIDI_SUCCESS;
272 desc->index = index;
273 desc->strLen = 200;
274 desc->name = (char*) calloc(desc->strLen + 1, 1);
275 desc->description = (char*) calloc(desc->strLen + 1, 1);
276 if (! desc->name ||
277 ! desc->description) {
278 ret = MIDI_OUT_OF_MEMORY;
279 }
280 return ret;
281}
282
283
284void freeMIDIDeviceDescription(ALSA_MIDIDeviceDescription* desc) {
285 if (desc->name) {
286 free(desc->name);
287 }
288 if (desc->description) {
289 free(desc->description);
290 }
291}
292
293
294int getMidiDeviceName(snd_rawmidi_stream_t direction, int index, char *name,
295 UINT32 nameLength) {
296 ALSA_MIDIDeviceDescription desc;
297 int ret;
298
299 TRACE1("getMidiDeviceName: nameLength: %d\n", (int) nameLength);
300 ret = initMIDIDeviceDescription(&desc, index);
301 if (ret == MIDI_SUCCESS) {
302 TRACE0("getMidiDeviceName: initMIDIDeviceDescription() SUCCESS\n");
303 ret = getMIDIDeviceDescriptionByIndex(direction, &desc);
304 if (ret == MIDI_SUCCESS) {
305 TRACE1("getMidiDeviceName: desc.name: %s\n", desc.name);
306 strncpy(name, desc.name, nameLength - 1);
307 name[nameLength - 1] = 0;
308 }
309 }
310 freeMIDIDeviceDescription(&desc);
311 return ret;
312}
313
314
315int getMidiDeviceVendor(int index, char *name, UINT32 nameLength) {
316 strncpy(name, ALSA_VENDOR, nameLength - 1);
317 name[nameLength - 1] = 0;
318 return MIDI_SUCCESS;
319}
320
321
322int getMidiDeviceDescription(snd_rawmidi_stream_t direction,
323 int index, char *name, UINT32 nameLength) {
324 ALSA_MIDIDeviceDescription desc;
325 int ret;
326
327 ret = initMIDIDeviceDescription(&desc, index);
328 if (ret == MIDI_SUCCESS) {
329 ret = getMIDIDeviceDescriptionByIndex(direction, &desc);
330 if (ret == MIDI_SUCCESS) {
331 strncpy(name, desc.description, nameLength - 1);
332 name[nameLength - 1] = 0;
333 }
334 }
335 freeMIDIDeviceDescription(&desc);
336 return ret;
337}
338
339
340int getMidiDeviceVersion(int index, char *name, UINT32 nameLength) {
341 getALSAVersion(name, nameLength);
342 return MIDI_SUCCESS;
343}
344
345
346static int getMidiDeviceID(snd_rawmidi_stream_t direction, int index,
347 UINT32* deviceID) {
348 ALSA_MIDIDeviceDescription desc;
349 int ret;
350
351 ret = initMIDIDeviceDescription(&desc, index);
352 if (ret == MIDI_SUCCESS) {
353 ret = getMIDIDeviceDescriptionByIndex(direction, &desc);
354 if (ret == MIDI_SUCCESS) {
355 // TRACE1("getMidiDeviceName: desc.name: %s\n", desc.name);
356 *deviceID = desc.deviceID;
357 }
358 }
359 freeMIDIDeviceDescription(&desc);
360 return ret;
361}
362
363
364/*
365 direction has to be either SND_RAWMIDI_STREAM_INPUT or
366 SND_RAWMIDI_STREAM_OUTPUT.
367 Returns 0 on success. Otherwise, MIDI_OUT_OF_MEMORY, MIDI_INVALID_ARGUMENT
368 or a negative ALSA error code is returned.
369*/
370INT32 openMidiDevice(snd_rawmidi_stream_t direction, INT32 deviceIndex,
371 MidiDeviceHandle** handle) {
372 snd_rawmidi_t* native_handle;
373 snd_midi_event_t* event_parser = NULL;
374 int err;
375 UINT32 deviceID;
376 char devicename[100];
377#ifdef ALSA_MIDI_USE_PLUGHW
378 int usePlugHw = 1;
379#else
380 int usePlugHw = 0;
381#endif
382
383 TRACE0("> openMidiDevice()\n");
384
385 (*handle) = (MidiDeviceHandle*) calloc(sizeof(MidiDeviceHandle), 1);
386 if (!(*handle)) {
387 ERROR0("ERROR: openDevice: out of memory\n");
388 return MIDI_OUT_OF_MEMORY;
389 }
390
391 // TODO: iterate to get dev ID from index
392 err = getMidiDeviceID(direction, deviceIndex, &deviceID);
393 TRACE1(" openMidiDevice(): deviceID: %d\n", (int) deviceID);
394 getDeviceStringFromDeviceID(devicename, deviceID,
395 usePlugHw, ALSA_RAWMIDI);
396 TRACE1(" openMidiDevice(): deviceString: %s\n", devicename);
397
398 // finally open the device
399 if (direction == SND_RAWMIDI_STREAM_INPUT) {
400 err = snd_rawmidi_open(&native_handle, NULL, devicename,
401 SND_RAWMIDI_NONBLOCK);
402 } else if (direction == SND_RAWMIDI_STREAM_OUTPUT) {
403 err = snd_rawmidi_open(NULL, &native_handle, devicename,
404 SND_RAWMIDI_NONBLOCK);
405 } else {
406 ERROR0(" ERROR: openMidiDevice(): direction is neither SND_RAWMIDI_STREAM_INPUT nor SND_RAWMIDI_STREAM_OUTPUT\n");
407 err = MIDI_INVALID_ARGUMENT;
408 }
409 if (err < 0) {
410 ERROR1("< ERROR: openMidiDevice(): snd_rawmidi_open() returned %d\n", err);
411 free(*handle);
412 (*handle) = NULL;
413 return err;
414 }
415 /* We opened with non-blocking behaviour to not get hung if the device
416 is used by a different process. Writing, however, should
417 be blocking. So we change it here. */
418 if (direction == SND_RAWMIDI_STREAM_OUTPUT) {
419 err = snd_rawmidi_nonblock(native_handle, 0);
420 if (err < 0) {
421 ERROR1(" ERROR: openMidiDevice(): snd_rawmidi_nonblock() returned %d\n", err);
422 snd_rawmidi_close(native_handle);
423 free(*handle);
424 (*handle) = NULL;
425 return err;
426 }
427 }
428 if (direction == SND_RAWMIDI_STREAM_INPUT) {
429 err = snd_midi_event_new(EVENT_PARSER_BUFSIZE, &event_parser);
430 if (err < 0) {
431 ERROR1(" ERROR: openMidiDevice(): snd_midi_event_new() returned %d\n", err);
432 snd_rawmidi_close(native_handle);
433 free(*handle);
434 (*handle) = NULL;
435 return err;
436 }
437 }
438
439 (*handle)->deviceHandle = (void*) native_handle;
440 (*handle)->startTime = getTimeInMicroseconds();
441 (*handle)->platformData = event_parser;
442 TRACE0("< openMidiDevice(): succeeded\n");
443 return err;
444}
445
446
447
448INT32 closeMidiDevice(MidiDeviceHandle* handle) {
449 int err;
450
451 TRACE0("> closeMidiDevice()\n");
452 if (!handle) {
453 ERROR0("< ERROR: closeMidiDevice(): handle is NULL\n");
454 return MIDI_INVALID_HANDLE;
455 }
456 if (!handle->deviceHandle) {
457 ERROR0("< ERROR: closeMidiDevice(): native handle is NULL\n");
458 return MIDI_INVALID_HANDLE;
459 }
460 err = snd_rawmidi_close((snd_rawmidi_t*) handle->deviceHandle);
461 TRACE1(" snd_rawmidi_close() returns %d\n", err);
462 if (handle->platformData) {
463 snd_midi_event_free((snd_midi_event_t*) handle->platformData);
464 }
465 free(handle);
466 TRACE0("< closeMidiDevice: succeeded\n");
467 return err;
468}
469
470
471INT64 getMidiTimestamp(MidiDeviceHandle* handle) {
472 if (!handle) {
473 ERROR0("< ERROR: closeMidiDevice(): handle is NULL\n");
474 return MIDI_INVALID_HANDLE;
475 }
476 return getTimeInMicroseconds() - handle->startTime;
477}
478
479
480/* end */