doc update
[speexdsp.git] / speexclient / alsa_device.c
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 AlsaDevice *alsa_device_open(char *device_name, unsigned int rate, int channels, int period)
48 {
49    int dir;
50    int err;
51    snd_pcm_hw_params_t *hw_params;
52    snd_pcm_sw_params_t *sw_params;
53    snd_pcm_uframes_t period_size = period;
54    snd_pcm_uframes_t buffer_size = 2*period;
55    static snd_output_t *jcd_out;
56    AlsaDevice *dev = malloc(sizeof(*dev));
57    if (!dev)
58       return NULL;
59    dev->device_name = malloc(1+strlen(device_name));
60    if (!dev->device_name)
61    {
62       free(dev);
63       return NULL;
64    }
65    strcpy(dev->device_name, device_name);
66    dev->channels = channels;
67    dev->period = period;
68    err = snd_output_stdio_attach(&jcd_out, stdout, 0);
69    
70    if ((err = snd_pcm_open (&dev->capture_handle, dev->device_name, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
71       fprintf (stderr, "cannot open audio device %s (%s)\n",
72                dev->device_name,
73                snd_strerror (err));
74       assert(0);
75    }
76
77    if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
78       fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
79                snd_strerror (err));
80       assert(0);
81    }
82
83    if ((err = snd_pcm_hw_params_any (dev->capture_handle, hw_params)) < 0) {
84       fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
85                snd_strerror (err));
86       assert(0);
87    }
88
89    if ((err = snd_pcm_hw_params_set_access (dev->capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
90       fprintf (stderr, "cannot set access type (%s)\n",
91                snd_strerror (err));
92       assert(0);
93    }
94
95    if ((err = snd_pcm_hw_params_set_format (dev->capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
96       fprintf (stderr, "cannot set sample format (%s)\n",
97                snd_strerror (err));
98       assert(0);
99    }
100
101    if ((err = snd_pcm_hw_params_set_rate_near (dev->capture_handle, hw_params, &rate, 0)) < 0) {
102       fprintf (stderr, "cannot set sample rate (%s)\n",
103                snd_strerror (err));
104       assert(0);
105    }
106    /*fprintf (stderr, "rate = %d\n", rate);*/
107
108    if ((err = snd_pcm_hw_params_set_channels (dev->capture_handle, hw_params, channels)) < 0) {
109       fprintf (stderr, "cannot set channel count (%s)\n",
110                snd_strerror (err));
111       assert(0);
112    }
113    
114    period_size = period;
115    dir = 0;
116    if ((err = snd_pcm_hw_params_set_period_size_near (dev->capture_handle, hw_params, &period_size, &dir)) < 0) {
117       fprintf (stderr, "cannot set period size (%s)\n",
118                snd_strerror (err));
119       assert(0);
120    }
121    
122    if ((err = snd_pcm_hw_params_set_periods (dev->capture_handle, hw_params, 2, 0)) < 0) {
123       fprintf (stderr, "cannot set number of periods (%s)\n",
124                snd_strerror (err));
125       assert(0);
126    }
127    
128    buffer_size = period_size * 2;
129    dir=0;
130    if ((err = snd_pcm_hw_params_set_buffer_size_near (dev->capture_handle, hw_params, &buffer_size)) < 0) {
131       fprintf (stderr, "cannot set buffer time (%s)\n",
132                snd_strerror (err));
133       assert(0);
134    }
135    
136    if ((err = snd_pcm_hw_params (dev->capture_handle, hw_params)) < 0) {
137       fprintf (stderr, "cannot set capture parameters (%s)\n",
138                snd_strerror (err));
139       assert(0);
140    }
141    /*snd_pcm_dump_setup(dev->capture_handle, jcd_out);*/
142    snd_pcm_hw_params_free (hw_params);
143
144    if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) {
145       fprintf (stderr, "cannot allocate software parameters structure (%s)\n",
146                snd_strerror (err));
147       assert(0);
148    }
149    if ((err = snd_pcm_sw_params_current (dev->capture_handle, sw_params)) < 0) {
150       fprintf (stderr, "cannot initialize software parameters structure (%s)\n",
151                snd_strerror (err));
152       assert(0);
153    }
154    if ((err = snd_pcm_sw_params_set_avail_min (dev->capture_handle, sw_params, period)) < 0) {
155       fprintf (stderr, "cannot set minimum available count (%s)\n",
156                snd_strerror (err));
157       assert(0);
158    }
159    if ((err = snd_pcm_sw_params (dev->capture_handle, sw_params)) < 0) {
160       fprintf (stderr, "cannot set software parameters (%s)\n",
161                snd_strerror (err));
162       assert(0);
163    }   
164    
165    
166    if ((err = snd_pcm_open (&dev->playback_handle, dev->device_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
167       fprintf (stderr, "cannot open audio device %s (%s)\n",
168                dev->device_name,
169                snd_strerror (err));
170       assert(0);
171    }
172    
173    if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
174       fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
175                snd_strerror (err));
176       assert(0);
177    }
178
179    if ((err = snd_pcm_hw_params_any (dev->playback_handle, hw_params)) < 0) {
180       fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
181                snd_strerror (err));
182       assert(0);
183    }
184
185    if ((err = snd_pcm_hw_params_set_access (dev->playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
186       fprintf (stderr, "cannot set access type (%s)\n",
187                snd_strerror (err));
188       assert(0);
189    }
190
191    if ((err = snd_pcm_hw_params_set_format (dev->playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
192       fprintf (stderr, "cannot set sample format (%s)\n",
193                snd_strerror (err));
194       assert(0);
195    }
196
197    if ((err = snd_pcm_hw_params_set_rate_near (dev->playback_handle, hw_params, &rate, 0)) < 0) {
198       fprintf (stderr, "cannot set sample rate (%s)\n",
199                snd_strerror (err));
200       assert(0);
201    }
202    /*fprintf (stderr, "rate = %d\n", rate);*/
203
204    if ((err = snd_pcm_hw_params_set_channels (dev->playback_handle, hw_params, channels)) < 0) {
205       fprintf (stderr, "cannot set channel count (%s)\n",
206                snd_strerror (err));
207       assert(0);
208    }
209    
210    period_size = period;
211    dir = 0;
212    if ((err = snd_pcm_hw_params_set_period_size_near (dev->playback_handle, hw_params, &period_size, &dir)) < 0) {
213       fprintf (stderr, "cannot set period size (%s)\n",
214                snd_strerror (err));
215       assert(0);
216    }
217    if ((err = snd_pcm_hw_params_set_periods (dev->playback_handle, hw_params, 2, 0)) < 0) {
218       fprintf (stderr, "cannot set number of periods (%s)\n",
219                snd_strerror (err));
220       assert(0);
221    }
222    buffer_size = period_size * 2;
223    dir=0;
224    if ((err = snd_pcm_hw_params_set_buffer_size_near (dev->playback_handle, hw_params, &buffer_size)) < 0) {
225       fprintf (stderr, "cannot set buffer time (%s)\n",
226                snd_strerror (err));
227       assert(0);
228    }
229
230
231    if ((err = snd_pcm_hw_params (dev->playback_handle, hw_params)) < 0) {
232       fprintf (stderr, "cannot set playback parameters (%s)\n",
233                snd_strerror (err));
234       assert(0);
235    }
236
237    /*snd_pcm_dump_setup(dev->playback_handle, jcd_out);*/
238    snd_pcm_hw_params_free (hw_params);
239
240    
241    if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) {
242       fprintf (stderr, "cannot allocate software parameters structure (%s)\n",
243                snd_strerror (err));
244       assert(0);
245    }
246    if ((err = snd_pcm_sw_params_current (dev->playback_handle, sw_params)) < 0) {
247       fprintf (stderr, "cannot initialize software parameters structure (%s)\n",
248                snd_strerror (err));
249       assert(0);
250    }
251    if ((err = snd_pcm_sw_params_set_avail_min (dev->playback_handle, sw_params, period)) < 0) {
252       fprintf (stderr, "cannot set minimum available count (%s)\n",
253                snd_strerror (err));
254       assert(0);
255    }
256    if ((err = snd_pcm_sw_params_set_start_threshold (dev->playback_handle, sw_params, period)) < 0) {
257       fprintf (stderr, "cannot set start mode (%s)\n",
258                snd_strerror (err));
259       assert(0);
260    }
261    if ((err = snd_pcm_sw_params (dev->playback_handle, sw_params)) < 0) {
262       fprintf (stderr, "cannot set software parameters (%s)\n",
263                snd_strerror (err));
264       assert(0);
265    }
266   
267    
268    snd_pcm_link(dev->capture_handle, dev->playback_handle);
269    if ((err = snd_pcm_prepare (dev->capture_handle)) < 0) {
270       fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
271                snd_strerror (err));
272       assert(0);
273    }
274    if ((err = snd_pcm_prepare (dev->playback_handle)) < 0) {
275       fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
276                snd_strerror (err));
277       assert(0);
278    }
279    
280    dev->readN = snd_pcm_poll_descriptors_count(dev->capture_handle);
281    dev->writeN = snd_pcm_poll_descriptors_count(dev->playback_handle);
282
283    dev->read_fd = malloc(dev->readN*sizeof(*dev->read_fd));
284    /*printf ("descriptors: %d %d\n", dev->readN, dev->writeN);*/
285    if (snd_pcm_poll_descriptors(dev->capture_handle, dev->read_fd, dev->readN) != dev->readN)
286    {
287       fprintf (stderr, "cannot obtain capture file descriptors (%s)\n",
288                snd_strerror (err));
289       assert(0);
290    }
291    
292    dev->write_fd = malloc(dev->writeN*sizeof(*dev->read_fd));
293    if (snd_pcm_poll_descriptors(dev->playback_handle, dev->write_fd, dev->writeN) != dev->writeN)
294    {
295       fprintf (stderr, "cannot obtain playback file descriptors (%s)\n",
296                snd_strerror (err));
297       assert(0);
298    }
299    return dev;
300 }
301
302 void alsa_device_close(AlsaDevice *dev)
303 {
304    snd_pcm_close(dev->capture_handle);
305    snd_pcm_close(dev->playback_handle);
306    free(dev->device_name);
307    free(dev);
308 }
309
310 int alsa_device_read(AlsaDevice *dev, short *pcm, int len)
311 {
312    int err;
313    /*fprintf (stderr, "-");*/
314    if ((err = snd_pcm_readi (dev->capture_handle, pcm, len)) != len)
315    {
316       if (err<0)
317       {
318          //fprintf(stderr, "error %d, EPIPE = %d\n", err, EPIPE);
319          if (err == -EPIPE)
320          {
321             fprintf (stderr, "An overrun has occured, reseting capture\n");
322          } else
323          {
324             fprintf (stderr, "read from audio interface failed (%s)\n",
325                      snd_strerror (err));
326          }
327          if ((err = snd_pcm_prepare (dev->capture_handle)) < 0)
328          {
329             fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
330                      snd_strerror (err));
331          }
332          if ((err = snd_pcm_start (dev->capture_handle)) < 0)
333          {
334             fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
335                      snd_strerror (err));
336          }
337          /*alsa_device_read(dev,pcm,len);*/
338       } else {
339          fprintf (stderr, "Couldn't read as many samples as I wanted (%d instead of %d)\n", err, len);
340       }
341       return 1;
342    }
343    return 0;
344 }
345
346 int alsa_device_write(AlsaDevice *dev, const short *pcm, int len)
347 {
348    int err;
349    /*fprintf (stderr, "+");*/
350    if ((err = snd_pcm_writei (dev->playback_handle, pcm, len)) != len)
351    {
352       if (err<0)
353       {
354          if (err == -EPIPE)
355          {
356             fprintf (stderr, "An underrun has occured, reseting playback, len=%d\n", len);
357          } else
358          {
359             fprintf (stderr, "write to audio interface failed (%s)\n",
360                      snd_strerror (err));
361          }
362          if ((err = snd_pcm_prepare (dev->playback_handle)) < 0)
363          {
364             fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
365                      snd_strerror (err));
366          }
367       } else {
368          fprintf (stderr, "Couldn't write as many samples as I wanted (%d instead of %d)\n", err, len);
369       }
370       /*alsa_device_write(dev,pcm,len);*/
371       return 1;
372    }
373    return 0;
374 }
375
376 int alsa_device_capture_ready(AlsaDevice *dev, struct pollfd *pfds, unsigned int nfds)
377 {
378    unsigned short revents=0;
379    int err;
380    if ((err = snd_pcm_poll_descriptors_revents(dev->capture_handle, pfds, dev->readN, &revents)) < 0)
381    {
382       //cerr << "error in snd_pcm_poll_descriptors_revents for capture: " << snd_strerror (err) << endl;
383       //FIXME: This is a kludge
384       fprintf (stderr, "error in alsa_device_capture_ready: %s\n", snd_strerror (err));
385       return pfds[0].revents & POLLIN;
386    }
387    //cerr << (revents & POLLERR) << endl;
388    return revents & POLLIN;
389 }
390
391 int alsa_device_playback_ready(AlsaDevice *dev, struct pollfd *pfds, unsigned int nfds)
392 {
393    unsigned short revents=0;
394    int err;
395    if ((err = snd_pcm_poll_descriptors_revents(dev->playback_handle, pfds+dev->readN, dev->writeN, &revents)) < 0)
396    {
397       //cerr << "error in snd_pcm_poll_descriptors_revents for playback: " << snd_strerror (err) << endl;
398       //FIXME: This is a kludge
399       fprintf (stderr, "error in alsa_device_playback_ready: %s\n", snd_strerror (err));
400       return pfds[1].revents & POLLOUT;
401    }
402    //cerr << (revents & POLLERR) << endl;
403    return revents & POLLOUT;
404 }
405
406 void alsa_device_start(AlsaDevice *dev)
407 {
408    int i;
409    short pcm[dev->period*dev->channels];
410    for (i=0;i<dev->period*dev->channels;i++)
411       pcm[i] = 0;
412    alsa_device_write(dev, pcm, dev->period);
413    alsa_device_write(dev, pcm, dev->period);
414    snd_pcm_start(dev->capture_handle);
415    snd_pcm_start(dev->playback_handle);
416 }
417
418 int alsa_device_nfds(AlsaDevice *dev)
419 {
420    return dev->writeN+dev->readN;
421 }
422
423 void alsa_device_getfds(AlsaDevice *dev, struct pollfd *pfds, unsigned int nfds)
424 {
425    int i;
426    assert (nfds >= dev->writeN+dev->readN);
427    for (i=0;i<dev->readN;i++)
428       pfds[i] = dev->read_fd[i];
429    for (i=0;i<dev->writeN;i++)
430       pfds[i+dev->readN] = dev->write_fd[i];
431 }