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_PCMUtils.h"
30#include "PLATFORM_API_LinuxOS_ALSA_CommonUtils.h"
31
32
33
34// callback for iteration through devices
35// returns TRUE if iteration should continue
36// NOTE: cardinfo may be NULL (for "default" device)
37typedef int (*DeviceIteratorPtr)(UINT32 deviceID, snd_pcm_info_t* pcminfo,
38 snd_ctl_card_info_t* cardinfo, void *userData);
39
40// for each ALSA device, call iterator. userData is passed to the iterator
41// returns total number of iterations
42int iteratePCMDevices(DeviceIteratorPtr iterator, void* userData) {
43 int count = 0;
44 int subdeviceCount;
45 int card, dev, subDev;
46 char devname[16];
47 int err;
48 snd_ctl_t *handle;
49 snd_pcm_t *pcm;
50 snd_pcm_info_t* pcminfo;
51 snd_ctl_card_info_t *cardinfo, *defcardinfo = NULL;
52 UINT32 deviceID;
53 int doContinue = TRUE;
54
55 snd_pcm_info_malloc(&pcminfo);
56 snd_ctl_card_info_malloc(&cardinfo);
57
58 // 1st try "default" device
59 err = snd_pcm_open(&pcm, ALSA_DEFAULT_DEVICE_NAME,
60 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
61 if (err < 0) {
62 // try with the other direction
63 err = snd_pcm_open(&pcm, ALSA_DEFAULT_DEVICE_NAME,
64 SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
65 }
66 if (err < 0) {
67 ERROR1("ERROR: snd_pcm_open (\"default\"): %s\n", snd_strerror(err));
68 } else {
69 err = snd_pcm_info(pcm, pcminfo);
70 snd_pcm_close(pcm);
71 if (err < 0) {
72 ERROR1("ERROR: snd_pcm_info (\"default\"): %s\n",
73 snd_strerror(err));
74 } else {
75 // try to get card info
76 card = snd_pcm_info_get_card(pcminfo);
77 if (card >= 0) {
78 sprintf(devname, ALSA_HARDWARE_CARD, card);
79 if (snd_ctl_open(&handle, devname, SND_CTL_NONBLOCK) >= 0) {
80 if (snd_ctl_card_info(handle, cardinfo) >= 0) {
81 defcardinfo = cardinfo;
82 }
83 snd_ctl_close(handle);
84 }
85 }
86 // call callback function for the device
87 if (iterator != NULL) {
88 doContinue = (*iterator)(ALSA_DEFAULT_DEVICE_ID, pcminfo,
89 defcardinfo, userData);
90 }
91 count++;
92 }
93 }
94
95 // iterate cards
96 card = -1;
97 while (doContinue) {
98 if (snd_card_next(&card) < 0) {
99 break;
100 }
101 if (card < 0) {
102 break;
103 }
104 sprintf(devname, ALSA_HARDWARE_CARD, card);
105 TRACE1("Opening alsa device \"%s\"...\n", devname);
106 err = snd_ctl_open(&handle, devname, SND_CTL_NONBLOCK);
107 if (err < 0) {
108 ERROR2("ERROR: snd_ctl_open, card=%d: %s\n",
109 card, snd_strerror(err));
110 } else {
111 err = snd_ctl_card_info(handle, cardinfo);
112 if (err < 0) {
113 ERROR2("ERROR: snd_ctl_card_info, card=%d: %s\n",
114 card, snd_strerror(err));
115 } else {
116 dev = -1;
117 while (doContinue) {
118 if (snd_ctl_pcm_next_device(handle, &dev) < 0) {
119 ERROR0("snd_ctl_pcm_next_device\n");
120 }
121 if (dev < 0) {
122 break;
123 }
124 snd_pcm_info_set_device(pcminfo, dev);
125 snd_pcm_info_set_subdevice(pcminfo, 0);
126 snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
127 err = snd_ctl_pcm_info(handle, pcminfo);
128 if (err == -ENOENT) {
129 // try with the other direction
130 snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE);
131 err = snd_ctl_pcm_info(handle, pcminfo);
132 }
133 if (err < 0) {
134 if (err != -ENOENT) {
135 ERROR2("ERROR: snd_ctl_pcm_info, card=%d: %s",
136 card, snd_strerror(err));
137 }
138 } else {
139 subdeviceCount = needEnumerateSubdevices(ALSA_PCM) ?
140 snd_pcm_info_get_subdevices_count(pcminfo) : 1;
141 if (iterator!=NULL) {
142 for (subDev = 0; subDev < subdeviceCount; subDev++) {
143 deviceID = encodeDeviceID(card, dev, subDev);
144 doContinue = (*iterator)(deviceID, pcminfo,
145 cardinfo, userData);
146 count++;
147 if (!doContinue) {
148 break;
149 }
150 }
151 } else {
152 count += subdeviceCount;
153 }
154 }
155 } // of while(doContinue)
156 }
157 snd_ctl_close(handle);
158 }
159 }
160 snd_ctl_card_info_free(cardinfo);
161 snd_pcm_info_free(pcminfo);
162 return count;
163}
164
165int getAudioDeviceCount() {
166 initAlsaSupport();
167 return iteratePCMDevices(NULL, NULL);
168}
169
170int deviceInfoIterator(UINT32 deviceID, snd_pcm_info_t* pcminfo,
171 snd_ctl_card_info_t* cardinfo, void* userData) {
172 char buffer[300];
173 ALSA_AudioDeviceDescription* desc = (ALSA_AudioDeviceDescription*)userData;
174#ifdef ALSA_PCM_USE_PLUGHW
175 int usePlugHw = 1;
176#else
177 int usePlugHw = 0;
178#endif
179
180 initAlsaSupport();
181 if (desc->index == 0) {
182 // we found the device with correct index
183 *(desc->maxSimultaneousLines) = needEnumerateSubdevices(ALSA_PCM) ?
184 1 : snd_pcm_info_get_subdevices_count(pcminfo);
185 *desc->deviceID = deviceID;
186 buffer[0]=' '; buffer[1]='[';
187 // buffer[300] is enough to store the actual device string w/o overrun
188 getDeviceStringFromDeviceID(&buffer[2], deviceID, usePlugHw, ALSA_PCM);
189 strncat(buffer, "]", sizeof(buffer) - strlen(buffer) - 1);
190 strncpy(desc->name,
191 (cardinfo != NULL)
192 ? snd_ctl_card_info_get_id(cardinfo)
193 : snd_pcm_info_get_id(pcminfo),
194 desc->strLen - strlen(buffer));
195 strncat(desc->name, buffer, desc->strLen - strlen(desc->name));
196 strncpy(desc->vendor, "ALSA (http://www.alsa-project.org)", desc->strLen);
197 strncpy(desc->description,
198 (cardinfo != NULL)
199 ? snd_ctl_card_info_get_name(cardinfo)
200 : snd_pcm_info_get_name(pcminfo),
201 desc->strLen);
202 strncat(desc->description, ", ", desc->strLen - strlen(desc->description));
203 strncat(desc->description, snd_pcm_info_get_id(pcminfo), desc->strLen - strlen(desc->description));
204 strncat(desc->description, ", ", desc->strLen - strlen(desc->description));
205 strncat(desc->description, snd_pcm_info_get_name(pcminfo), desc->strLen - strlen(desc->description));
206 getALSAVersion(desc->version, desc->strLen);
207 TRACE4("Returning %s, %s, %s, %s\n", desc->name, desc->vendor, desc->description, desc->version);
208 return FALSE; // do not continue iteration
209 }
210 desc->index--;
211 return TRUE;
212}
213
214// returns 0 if successful
215int openPCMfromDeviceID(int deviceID, snd_pcm_t** handle, int isSource, int hardware) {
216 char buffer[200];
217 int ret;
218
219 initAlsaSupport();
220 getDeviceStringFromDeviceID(buffer, deviceID, !hardware, ALSA_PCM);
221
222 TRACE1("Opening ALSA device %s\n", buffer);
223 ret = snd_pcm_open(handle, buffer,
224 isSource?SND_PCM_STREAM_PLAYBACK:SND_PCM_STREAM_CAPTURE,
225 SND_PCM_NONBLOCK);
226 if (ret != 0) {
227 ERROR1("snd_pcm_open returned error code %d \n", ret);
228 *handle = NULL;
229 }
230 return ret;
231}
232
233
234int getAudioDeviceDescriptionByIndex(ALSA_AudioDeviceDescription* desc) {
235 initAlsaSupport();
236 TRACE1(" getAudioDeviceDescriptionByIndex(mixerIndex = %d\n", desc->index);
237 iteratePCMDevices(&deviceInfoIterator, desc);
238 return (desc->index == 0)?TRUE:FALSE;
239}
240
241// returns 1 if successful
242// enc: 0 for PCM, 1 for ULAW, 2 for ALAW (see DirectAudio.h)
243int getFormatFromAlsaFormat(snd_pcm_format_t alsaFormat,
244 int* sampleSizeInBytes, int* significantBits,
245 int* isSigned, int* isBigEndian, int* enc) {
246
247 *sampleSizeInBytes = (snd_pcm_format_physical_width(alsaFormat) + 7) / 8;
248 *significantBits = snd_pcm_format_width(alsaFormat);
249
250 // defaults
251 *enc = 0; // PCM
252 *isSigned = (snd_pcm_format_signed(alsaFormat) > 0);
253 *isBigEndian = (snd_pcm_format_big_endian(alsaFormat) > 0);
254
255 // non-PCM formats
256 if (alsaFormat == SND_PCM_FORMAT_MU_LAW) { // Mu-Law
257 *sampleSizeInBytes = 8; *enc = 1; *significantBits = *sampleSizeInBytes;
258 }
259 else if (alsaFormat == SND_PCM_FORMAT_A_LAW) { // A-Law
260 *sampleSizeInBytes = 8; *enc = 2; *significantBits = *sampleSizeInBytes;
261 }
262 else if (snd_pcm_format_linear(alsaFormat) < 1) {
263 return 0;
264 }
265 return (*sampleSizeInBytes > 0);
266}
267
268// returns 1 if successful
269int getAlsaFormatFromFormat(snd_pcm_format_t* alsaFormat,
270 int sampleSizeInBytes, int significantBits,
271 int isSigned, int isBigEndian, int enc) {
272 *alsaFormat = SND_PCM_FORMAT_UNKNOWN;
273
274 if (enc == 0) {
275 *alsaFormat = snd_pcm_build_linear_format(significantBits,
276 sampleSizeInBytes * 8,
277 isSigned?0:1,
278 isBigEndian?1:0);
279 }
280 else if ((sampleSizeInBytes == 1) && (significantBits == 8)) {
281 if (enc == 1) { // ULAW
282 *alsaFormat = SND_PCM_FORMAT_MU_LAW;
283 }
284 else if (enc == 2) { // ALAW
285 *alsaFormat = SND_PCM_FORMAT_A_LAW;
286 }
287 }
288 return (*alsaFormat == SND_PCM_FORMAT_UNKNOWN)?0:1;
289}
290
291
292/* end */
293