1 /*
2 Copyright (C) 2004-2006 Jean-Marc Valin
3 Copyright (C) 2006 Commonwealth Scientific and Industrial Research
4 Organisation (CSIRO) Australia
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are
8 met:
9
10 1. Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16
17 3. The name of the author may not be used to endorse or promote products
18 derived from this software without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
24 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "alsa_device.h"
34 #include <stdlib.h>
35 #include <alsa/asoundlib.h>
36
37 struct AlsaDevice_ {
38 char *device_name;
39 int channels;
40 int period;
41 snd_pcm_t *capture_handle;
42 snd_pcm_t *playback_handle;
43 int readN, writeN;
44 struct pollfd *read_fd, *write_fd;
45 };
46
47 #define PERIODS 3
alsa_device_open(char * device_name,unsigned int rate,int channels,int period)48 AlsaDevice *alsa_device_open(char *device_name, unsigned int rate, int channels, int period)
49 {
50 int dir;
51 int err;
52 snd_pcm_hw_params_t *hw_params;
53 snd_pcm_sw_params_t *sw_params;
54 snd_pcm_uframes_t period_size = period;
55 snd_pcm_uframes_t buffer_size = PERIODS*period;
56 static snd_output_t *jcd_out;
57 AlsaDevice *dev = malloc(sizeof(*dev));
58 if (!dev)
59 return NULL;
60 dev->device_name = malloc(1+strlen(device_name));
61 if (!dev->device_name)
62 {
63 free(dev);
64 return NULL;
65 }
66 strcpy(dev->device_name, device_name);
67 dev->channels = channels;
68 dev->period = period;
69 err = snd_output_stdio_attach(&jcd_out, stdout, 0);
70
71 if ((err = snd_pcm_open (&dev->capture_handle, dev->device_name, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
72 fprintf (stderr, "cannot open audio device %s (%s)\n",
73 dev->device_name,
74 snd_strerror (err));
75 assert(0);
76 }
77
78 if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
79 fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
80 snd_strerror (err));
81 assert(0);
82 }
83
84 if ((err = snd_pcm_hw_params_any (dev->capture_handle, hw_params)) < 0) {
85 fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
86 snd_strerror (err));
87 assert(0);
88 }
89
90 if ((err = snd_pcm_hw_params_set_access (dev->capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
91 fprintf (stderr, "cannot set access type (%s)\n",
92 snd_strerror (err));
93 assert(0);
94 }
95
96 if ((err = snd_pcm_hw_params_set_format (dev->capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
97 fprintf (stderr, "cannot set sample format (%s)\n",
98 snd_strerror (err));
99 assert(0);
100 }
101
102 if ((err = snd_pcm_hw_params_set_rate_near (dev->capture_handle, hw_params, &rate, 0)) < 0) {
103 fprintf (stderr, "cannot set sample rate (%s)\n",
104 snd_strerror (err));
105 assert(0);
106 }
107 /*fprintf (stderr, "rate = %d\n", rate);*/
108
109 if ((err = snd_pcm_hw_params_set_channels (dev->capture_handle, hw_params, channels)) < 0) {
110 fprintf (stderr, "cannot set channel count (%s)\n",
111 snd_strerror (err));
112 assert(0);
113 }
114
115 period_size = period;
116 dir = 0;
117 if ((err = snd_pcm_hw_params_set_period_size_near (dev->capture_handle, hw_params, &period_size, &dir)) < 0) {
118 fprintf (stderr, "cannot set period size (%s)\n",
119 snd_strerror (err));
120 assert(0);
121 }
122
123 if ((err = snd_pcm_hw_params_set_periods (dev->capture_handle, hw_params, PERIODS, 0)) < 0) {
124 fprintf (stderr, "cannot set number of periods (%s)\n",
125 snd_strerror (err));
126 assert(0);
127 }
128
129 buffer_size = period_size * PERIODS;
130 dir=0;
131 if ((err = snd_pcm_hw_params_set_buffer_size_near (dev->capture_handle, hw_params, &buffer_size)) < 0) {
132 fprintf (stderr, "cannot set buffer time (%s)\n",
133 snd_strerror (err));
134 assert(0);
135 }
136
137 if ((err = snd_pcm_hw_params (dev->capture_handle, hw_params)) < 0) {
138 fprintf (stderr, "cannot set capture parameters (%s)\n",
139 snd_strerror (err));
140 assert(0);
141 }
142 /*snd_pcm_dump_setup(dev->capture_handle, jcd_out);*/
143 snd_pcm_hw_params_free (hw_params);
144
145 if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) {
146 fprintf (stderr, "cannot allocate software parameters structure (%s)\n",
147 snd_strerror (err));
148 assert(0);
149 }
150 if ((err = snd_pcm_sw_params_current (dev->capture_handle, sw_params)) < 0) {
151 fprintf (stderr, "cannot initialize software parameters structure (%s)\n",
152 snd_strerror (err));
153 assert(0);
154 }
155 if ((err = snd_pcm_sw_params_set_avail_min (dev->capture_handle, sw_params, period)) < 0) {
156 fprintf (stderr, "cannot set minimum available count (%s)\n",
157 snd_strerror (err));
158 assert(0);
159 }
160 if ((err = snd_pcm_sw_params (dev->capture_handle, sw_params)) < 0) {
161 fprintf (stderr, "cannot set software parameters (%s)\n",
162 snd_strerror (err));
163 assert(0);
164 }
165
166
167 if ((err = snd_pcm_open (&dev->playback_handle, dev->device_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
168 fprintf (stderr, "cannot open audio device %s (%s)\n",
169 dev->device_name,
170 snd_strerror (err));
171 assert(0);
172 }
173
174 if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
175 fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
176 snd_strerror (err));
177 assert(0);
178 }
179
180 if ((err = snd_pcm_hw_params_any (dev->playback_handle, hw_params)) < 0) {
181 fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
182 snd_strerror (err));
183 assert(0);
184 }
185
186 if ((err = snd_pcm_hw_params_set_access (dev->playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
187 fprintf (stderr, "cannot set access type (%s)\n",
188 snd_strerror (err));
189 assert(0);
190 }
191
192 if ((err = snd_pcm_hw_params_set_format (dev->playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
193 fprintf (stderr, "cannot set sample format (%s)\n",
194 snd_strerror (err));
195 assert(0);
196 }
197
198 if ((err = snd_pcm_hw_params_set_rate_near (dev->playback_handle, hw_params, &rate, 0)) < 0) {
199 fprintf (stderr, "cannot set sample rate (%s)\n",
200 snd_strerror (err));
201 assert(0);
202 }
203 /*fprintf (stderr, "rate = %d\n", rate);*/
204
205 if ((err = snd_pcm_hw_params_set_channels (dev->playback_handle, hw_params, channels)) < 0) {
206 fprintf (stderr, "cannot set channel count (%s)\n",
207 snd_strerror (err));
208 assert(0);
209 }
210
211 period_size = period;
212 dir = 0;
213 if ((err = snd_pcm_hw_params_set_period_size_near (dev->playback_handle, hw_params, &period_size, &dir)) < 0) {
214 fprintf (stderr, "cannot set period size (%s)\n",
215 snd_strerror (err));
216 assert(0);
217 }
218 if ((err = snd_pcm_hw_params_set_periods (dev->playback_handle, hw_params, PERIODS, 0)) < 0) {
219 fprintf (stderr, "cannot set number of periods (%s)\n",
220 snd_strerror (err));
221 assert(0);
222 }
223 buffer_size = period_size * PERIODS;
224 dir=0;
225 if ((err = snd_pcm_hw_params_set_buffer_size_near (dev->playback_handle, hw_params, &buffer_size)) < 0) {
226 fprintf (stderr, "cannot set buffer time (%s)\n",
227 snd_strerror (err));
228 assert(0);
229 }
230
231
232 if ((err = snd_pcm_hw_params (dev->playback_handle, hw_params)) < 0) {
233 fprintf (stderr, "cannot set playback parameters (%s)\n",
234 snd_strerror (err));
235 assert(0);
236 }
237
238 /*snd_pcm_dump_setup(dev->playback_handle, jcd_out);*/
239 snd_pcm_hw_params_free (hw_params);
240
241
242 if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) {
243 fprintf (stderr, "cannot allocate software parameters structure (%s)\n",
244 snd_strerror (err));
245 assert(0);
246 }
247 if ((err = snd_pcm_sw_params_current (dev->playback_handle, sw_params)) < 0) {
248 fprintf (stderr, "cannot initialize software parameters structure (%s)\n",
249 snd_strerror (err));
250 assert(0);
251 }
252 if ((err = snd_pcm_sw_params_set_avail_min (dev->playback_handle, sw_params, period)) < 0) {
253 fprintf (stderr, "cannot set minimum available count (%s)\n",
254 snd_strerror (err));
255 assert(0);
256 }
257 if ((err = snd_pcm_sw_params_set_start_threshold (dev->playback_handle, sw_params, period)) < 0) {
258 fprintf (stderr, "cannot set start mode (%s)\n",
259 snd_strerror (err));
260 assert(0);
261 }
262 if ((err = snd_pcm_sw_params (dev->playback_handle, sw_params)) < 0) {
263 fprintf (stderr, "cannot set software parameters (%s)\n",
264 snd_strerror (err));
265 assert(0);
266 }
267
268
269 snd_pcm_link(dev->capture_handle, dev->playback_handle);
270 if ((err = snd_pcm_prepare (dev->capture_handle)) < 0) {
271 fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
272 snd_strerror (err));
273 assert(0);
274 }
275 if ((err = snd_pcm_prepare (dev->playback_handle)) < 0) {
276 fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
277 snd_strerror (err));
278 assert(0);
279 }
280
281 dev->readN = snd_pcm_poll_descriptors_count(dev->capture_handle);
282 dev->writeN = snd_pcm_poll_descriptors_count(dev->playback_handle);
283
284 dev->read_fd = malloc(dev->readN*sizeof(*dev->read_fd));
285 /*printf ("descriptors: %d %d\n", dev->readN, dev->writeN);*/
286 if (snd_pcm_poll_descriptors(dev->capture_handle, dev->read_fd, dev->readN) != dev->readN)
287 {
288 fprintf (stderr, "cannot obtain capture file descriptors (%s)\n",
289 snd_strerror (err));
290 assert(0);
291 }
292
293 dev->write_fd = malloc(dev->writeN*sizeof(*dev->read_fd));
294 if (snd_pcm_poll_descriptors(dev->playback_handle, dev->write_fd, dev->writeN) != dev->writeN)
295 {
296 fprintf (stderr, "cannot obtain playback file descriptors (%s)\n",
297 snd_strerror (err));
298 assert(0);
299 }
300 return dev;
301 }
302
alsa_device_close(AlsaDevice * dev)303 void alsa_device_close(AlsaDevice *dev)
304 {
305 snd_pcm_close(dev->capture_handle);
306 snd_pcm_close(dev->playback_handle);
307 free(dev->device_name);
308 free(dev);
309 }
310
alsa_device_read(AlsaDevice * dev,short * pcm,int len)311 int alsa_device_read(AlsaDevice *dev, short *pcm, int len)
312 {
313 int err;
314 /*fprintf (stderr, "-");*/
315 if ((err = snd_pcm_readi (dev->capture_handle, pcm, len)) != len)
316 {
317 if (err<0)
318 {
319 //fprintf(stderr, "error %d, EPIPE = %d\n", err, EPIPE);
320 if (err == -EPIPE)
321 {
322 fprintf (stderr, "An overrun has occured, reseting capture\n");
323 } else
324 {
325 fprintf (stderr, "read from audio interface failed (%s)\n",
326 snd_strerror (err));
327 }
328 if ((err = snd_pcm_prepare (dev->capture_handle)) < 0)
329 {
330 fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
331 snd_strerror (err));
332 }
333 if ((err = snd_pcm_start (dev->capture_handle)) < 0)
334 {
335 fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
336 snd_strerror (err));
337 }
338 /*alsa_device_read(dev,pcm,len);*/
339 } else {
340 fprintf (stderr, "Couldn't read as many samples as I wanted (%d instead of %d)\n", err, len);
341 }
342 return 1;
343 }
344 return 0;
345 }
346
alsa_device_write(AlsaDevice * dev,const short * pcm,int len)347 int alsa_device_write(AlsaDevice *dev, const short *pcm, int len)
348 {
349 int err;
350 /*fprintf (stderr, "+");*/
351 if ((err = snd_pcm_writei (dev->playback_handle, pcm, len)) != len)
352 {
353 if (err<0)
354 {
355 if (err == -EPIPE)
356 {
357 fprintf (stderr, "An underrun has occured, reseting playback, len=%d\n", len);
358 } else
359 {
360 fprintf (stderr, "write to audio interface failed (%s)\n",
361 snd_strerror (err));
362 }
363 if ((err = snd_pcm_prepare (dev->playback_handle)) < 0)
364 {
365 fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
366 snd_strerror (err));
367 }
368 } else {
369 fprintf (stderr, "Couldn't write as many samples as I wanted (%d instead of %d)\n", err, len);
370 }
371 /*alsa_device_write(dev,pcm,len);*/
372 return 1;
373 }
374 return 0;
375 }
376
alsa_device_capture_ready(AlsaDevice * dev,struct pollfd * pfds,unsigned int nfds)377 int alsa_device_capture_ready(AlsaDevice *dev, struct pollfd *pfds, unsigned int nfds)
378 {
379 unsigned short revents=0;
380 int err;
381 if ((err = snd_pcm_poll_descriptors_revents(dev->capture_handle, pfds, dev->readN, &revents)) < 0)
382 {
383 //cerr << "error in snd_pcm_poll_descriptors_revents for capture: " << snd_strerror (err) << endl;
384 //FIXME: This is a kludge
385 fprintf (stderr, "error in alsa_device_capture_ready: %s\n", snd_strerror (err));
386 return pfds[0].revents & POLLIN;
387 }
388 //cerr << (revents & POLLERR) << endl;
389 return revents & POLLIN;
390 }
391
alsa_device_playback_ready(AlsaDevice * dev,struct pollfd * pfds,unsigned int nfds)392 int alsa_device_playback_ready(AlsaDevice *dev, struct pollfd *pfds, unsigned int nfds)
393 {
394 unsigned short revents=0;
395 int err;
396 if ((err = snd_pcm_poll_descriptors_revents(dev->playback_handle, pfds+dev->readN, dev->writeN, &revents)) < 0)
397 {
398 //cerr << "error in snd_pcm_poll_descriptors_revents for playback: " << snd_strerror (err) << endl;
399 //FIXME: This is a kludge
400 fprintf (stderr, "error in alsa_device_playback_ready: %s\n", snd_strerror (err));
401 return pfds[1].revents & POLLOUT;
402 }
403 //cerr << (revents & POLLERR) << endl;
404 return revents & POLLOUT;
405 }
406
alsa_device_start(AlsaDevice * dev)407 void alsa_device_start(AlsaDevice *dev)
408 {
409 int i;
410 short pcm[dev->period*dev->channels];
411 for (i=0;i<dev->period*dev->channels;i++)
412 pcm[i] = 0;
413 alsa_device_write(dev, pcm, dev->period);
414 alsa_device_write(dev, pcm, dev->period);
415 snd_pcm_start(dev->capture_handle);
416 snd_pcm_start(dev->playback_handle);
417 }
418
alsa_device_nfds(AlsaDevice * dev)419 int alsa_device_nfds(AlsaDevice *dev)
420 {
421 return dev->writeN+dev->readN;
422 }
423
alsa_device_getfds(AlsaDevice * dev,struct pollfd * pfds,unsigned int nfds)424 void alsa_device_getfds(AlsaDevice *dev, struct pollfd *pfds, unsigned int nfds)
425 {
426 int i;
427 assert (nfds >= dev->writeN+dev->readN);
428 for (i=0;i<dev->readN;i++)
429 pfds[i] = dev->read_fd[i];
430 for (i=0;i<dev->writeN;i++)
431 pfds[i+dev->readN] = dev->write_fd[i];
432 }
433