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