SphinxBase  0.6
ad_alsa.c
1 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* ====================================================================
3  * Copyright (c) 1999-2001 Carnegie Mellon University. All rights
4  * reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, 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
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * This work was supported in part by funding from the Defense Advanced
19  * Research Projects Agency and the National Science Foundation of the
20  * United States of America, and the CMU Sphinx Speech Consortium.
21  *
22  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND
23  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
26  * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * ====================================================================
35  *
36  */
37 /* -*- mode:c; indent-tabs-mode:t; c-basic-offset:4; comment-column:40 -*-
38  *
39  * Sphinx II libad (Linux)
40  * ^^^^^^^^^^^^^^^^^^^^^^^
41  * $Id: ad_alsa.c,v 1.6 2001/12/11 00:24:48 lenzo Exp $
42  *
43  * John G. Dorsey (jd5q+@andrew.cmu.edu)
44  * Engineering Design Research Center
45  * Carnegie Mellon University
46  * ***************************************************************************
47  *
48  * REVISION HISTORY
49  *
50  * 18-Mar-2006 David Huggins-Daines <dhuggins@cs.cmu.edu>
51  * Update this to the ALSA 1.0 API.
52  *
53  * 12-Dec-2000 David Huggins-Daines <dhd@cepstral.com> at Cepstral LLC
54  * Make this at least compile with the new ALSA API.
55  *
56  * 05-Nov-1999 Sean Levy (snl@stalphonsos.com) at St. Alphonsos, LLC.
57  * Ported to ALSA so I can actually get working full-duplex.
58  *
59  * 09-Aug-1999 Kevin Lenzo (lenzo@cs.cmu.edu) at Cernegie Mellon University.
60  * Incorporated nickr@cs.cmu.edu's changes (marked below) and
61  * SPS_EPSILON to allow for sample rates that are "close enough".
62  *
63  * 15-Jun-1999 M. K. Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon Univ.
64  * Consolidated all ad functions into
65  * this one file. Added ad_open_sps().
66  * Other cosmetic changes for consistency (e.g., use of err.h).
67  *
68  * 18-May-1999 Kevin Lenzo (lenzo@cs.cmu.edu) added <errno.h>.
69  */
70 
71 
72 #include <fcntl.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <alsa/asoundlib.h>
77 #include <errno.h>
78 #include <config.h>
79 #include <unistd.h>
80 
81 #include "prim_type.h"
82 #include "ad.h"
83 
84 
85 #define AUDIO_FORMAT SND_PCM_SFMT_S16_LE /* 16-bit signed, little endian */
86 #define INPUT_GAIN (85)
87 #define SPS_EPSILON 200
88 
89 static int
90 setparams(int32 sps, snd_pcm_t * handle)
91 {
92  snd_pcm_hw_params_t *hwparams;
93  unsigned int out_sps, buffer_time, period_time;
94  int err;
95 
96  snd_pcm_hw_params_alloca(&hwparams);
97  err = snd_pcm_hw_params_any(handle, hwparams);
98  if (err < 0) {
99  fprintf(stderr, "Can not configure this PCM device: %s\n",
100  snd_strerror(err));
101  return -1;
102  }
103 
104  err =
105  snd_pcm_hw_params_set_access(handle, hwparams,
106  SND_PCM_ACCESS_RW_INTERLEAVED);
107  if (err < 0) {
108  fprintf(stderr,
109  "Failed to set PCM device to interleaved: %s\n",
110  snd_strerror(err));
111  return -1;
112  }
113 
114  err =
115  snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16);
116  if (err < 0) {
117  fprintf(stderr,
118  "Failed to set PCM device to 16-bit signed PCM: %s\n",
119  snd_strerror(err));
120  return -1;
121  }
122 
123  err = snd_pcm_hw_params_set_channels(handle, hwparams, 1);
124  if (err < 0) {
125  fprintf(stderr, "Failed to set PCM device to mono: %s\n",
126  snd_strerror(err));
127  return -1;
128  }
129 
130  out_sps = sps;
131  err =
132  snd_pcm_hw_params_set_rate_near(handle, hwparams, &out_sps, NULL);
133  if (err < 0) {
134  fprintf(stderr, "Failed to set sampling rate: %s\n",
135  snd_strerror(err));
136  return -1;
137  }
138  if (abs(out_sps - sps) > SPS_EPSILON) {
139  fprintf(stderr,
140  "Available samping rate %d is too far from requested %d\n",
141  out_sps, sps);
142  return -1;
143  }
144 
145  /* Set buffer time to the maximum. */
146  err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0);
147  period_time = buffer_time / 4;
148  err = snd_pcm_hw_params_set_period_time_near(handle, hwparams,
149  &period_time, 0);
150  if (err < 0) {
151  fprintf(stderr, "Failed to set period time to %u: %s\n",
152  period_time, snd_strerror(err));
153  return -1;
154  }
155  err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams,
156  &buffer_time, 0);
157  if (err < 0) {
158  fprintf(stderr, "Failed to set buffer time to %u: %s\n",
159  buffer_time, snd_strerror(err));
160  return -1;
161  }
162 
163  err = snd_pcm_hw_params(handle, hwparams);
164  if (err < 0) {
165  fprintf(stderr, "Failed to set hwparams: %s\n", snd_strerror(err));
166  return -1;
167  }
168 
169  err = snd_pcm_nonblock(handle, 1);
170  if (err < 0) {
171  fprintf(stderr, "Failed to set non-blocking mode: %s\n",
172  snd_strerror(err));
173  return -1;
174  }
175  return 0;
176 }
177 
178 static int
179 setlevels(const char *dev)
180 {
181  snd_mixer_t *handle;
182  snd_mixer_selem_id_t *sid;
183  snd_mixer_elem_t *elem;
184  int err;
185  char *mixer_dev, *c;
186 
187  /* Basically we just want to turn on Mic capture. */
188  if ((err = snd_mixer_open(&handle, 0)) < 0) {
189  fprintf(stderr, "Mixer open failed: %s\n", snd_strerror(err));
190  return -1;
191  }
192 
193  mixer_dev = strdup(dev);
194  if (strncmp(mixer_dev, "plug", 4) == 0)
195  memmove(mixer_dev, mixer_dev + 4, strlen(mixer_dev) - 4 + 1);
196  if ((c = strchr(mixer_dev, ',')))
197  *c = '\0';
198  if ((err = snd_mixer_attach(handle, mixer_dev)) < 0) {
199  fprintf(stderr, "Mixer attach to %s failed: %s\n",
200  mixer_dev, snd_strerror(err));
201  free(mixer_dev);
202  snd_mixer_close(handle);
203  return -1;
204  }
205  free(mixer_dev);
206  if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
207  fprintf(stderr, "Mixer register failed: %s\n", snd_strerror(err));
208  snd_mixer_close(handle);
209  return -1;
210  }
211  if ((err = snd_mixer_load(handle)) < 0) {
212  fprintf(stderr, "Mixer load failed: %s\n", snd_strerror(err));
213  snd_mixer_close(handle);
214  return -1;
215  }
216  snd_mixer_selem_id_alloca(&sid);
217  snd_mixer_selem_id_set_name(sid, "Mic");
218  if ((elem = snd_mixer_find_selem(handle, sid)) == NULL) {
219  fprintf(stderr, "Warning: Could not find Mic element\n");
220  }
221  else {
222  if (snd_mixer_selem_has_capture_switch(elem)) {
223  if ((err = snd_mixer_selem_set_capture_switch_all(elem, 1)) < 0) {
224  fprintf(stderr,
225  "Failed to enable microphone capture: %s\n",
226  snd_strerror(err));
227  snd_mixer_close(handle);
228  return -1;
229  }
230  }
231  }
232  snd_mixer_selem_id_set_name(sid, "Capture");
233  if ((elem = snd_mixer_find_selem(handle, sid)) == NULL) {
234  fprintf(stderr, "Warning: Could not find Capture element\n");
235  }
236  else {
237  if (snd_mixer_selem_has_capture_switch(elem)) {
238  if ((err = snd_mixer_selem_set_capture_switch_all(elem, 1)) < 0) {
239  fprintf(stderr,
240  "Failed to enable microphone capture: %s\n",
241  snd_strerror(err));
242  snd_mixer_close(handle);
243  return -1;
244  }
245  }
246  }
247 
248  return 0;
249 }
250 
251 ad_rec_t *
252 ad_open_dev(const char *dev, int32 sps)
253 {
254  ad_rec_t *handle;
255  snd_pcm_t *dspH;
256 
257  int err;
258 
259  if (dev == NULL)
260  dev = DEFAULT_DEVICE;
261 
262  err = snd_pcm_open(&dspH, dev, SND_PCM_STREAM_CAPTURE, 0);
263  if (err < 0) {
264  fprintf(stderr,
265  "Error opening audio device %s for capture: %s\n",
266  dev, snd_strerror(err));
267  return NULL;
268  }
269 
270  if (setparams(sps, dspH) < 0) {
271  return NULL;
272  }
273  if (setlevels(dev) < 0) {
274  return NULL;
275  }
276  if ((handle = (ad_rec_t *) calloc(1, sizeof(ad_rec_t))) == NULL) {
277  fprintf(stderr, "calloc(%d) failed\n", (int)sizeof(ad_rec_t));
278  abort();
279  }
280 
281  handle->dspH = dspH;
282  handle->recording = 0;
283  handle->sps = sps;
284  handle->bps = sizeof(int16);
285 
286  return (handle);
287 }
288 
289 ad_rec_t *
290 ad_open_sps(int32 sps)
291 {
292  return ad_open_dev(DEFAULT_DEVICE, sps);
293 }
294 
295 ad_rec_t *
296 ad_open(void)
297 {
298  return ad_open_sps(DEFAULT_SAMPLES_PER_SEC);
299 }
300 
301 
302 int32
303 ad_close(ad_rec_t * handle)
304 {
305  if (handle->dspH == NULL)
306  return AD_ERR_NOT_OPEN;
307 
308  if (handle->recording) {
309  if (ad_stop_rec(handle) < 0)
310  return AD_ERR_GEN;
311  }
312  snd_pcm_close(handle->dspH);
313  free(handle);
314 
315  return (0);
316 }
317 
318 
319 int32
320 ad_start_rec(ad_rec_t * handle)
321 {
322  int err;
323 
324  if (handle->dspH == NULL)
325  return AD_ERR_NOT_OPEN;
326 
327  if (handle->recording)
328  return AD_ERR_GEN;
329 
330  err = snd_pcm_prepare(handle->dspH);
331  if (err < 0) {
332  fprintf(stderr, "snd_pcm_prepare failed: %s\n", snd_strerror(err));
333  return AD_ERR_GEN;
334  }
335  err = snd_pcm_start(handle->dspH);
336  if (err < 0) {
337  fprintf(stderr, "snd_pcm_start failed: %s\n", snd_strerror(err));
338  return AD_ERR_GEN;
339  }
340  handle->recording = 1;
341 
342  return (0);
343 }
344 
345 
346 int32
347 ad_stop_rec(ad_rec_t * handle)
348 {
349  int err;
350 
351  if (handle->dspH == NULL)
352  return AD_ERR_NOT_OPEN;
353 
354  if (!handle->recording)
355  return AD_ERR_GEN;
356 
357  err = snd_pcm_drop(handle->dspH);
358  if (err < 0) {
359  fprintf(stderr, "snd_pcm_drop failed: %s\n", snd_strerror(err));
360  return AD_ERR_GEN;
361  }
362  handle->recording = 0;
363 
364  return (0);
365 }
366 
367 
368 int32
369 ad_read(ad_rec_t * handle, int16 * buf, int32 max)
370 {
371  int32 length, err;
372 
373  if (!handle->recording) {
374  fprintf(stderr, "Recording is stopped, start recording with ad_start_rec\n");
375  return AD_EOF;
376  }
377 
378  length = snd_pcm_readi(handle->dspH, buf, max);
379  if (length == -EAGAIN) {
380  length = 0;
381  }
382  else if (length == -EPIPE) {
383  fprintf(stderr, "Input overrun, read calls are too rare (non-fatal)\n");
384  err = snd_pcm_prepare(handle->dspH);
385  if (err < 0) {
386  fprintf(stderr, "Can't recover from underrun: %s\n",
387  snd_strerror(err));
388  return AD_ERR_GEN;
389  }
390  length = 0;
391  }
392  else if (length == -ESTRPIPE) {
393  fprintf(stderr, "Resuming sound driver (non-fatal)\n");
394  while ((err = snd_pcm_resume(handle->dspH)) == -EAGAIN)
395  usleep(10000); /* Wait for the driver to wake up */
396  if (err < 0) {
397  err = snd_pcm_prepare(handle->dspH);
398  if (err < 0) {
399  fprintf(stderr, "Can't recover from underrun: %s\n",
400  snd_strerror(err));
401  return AD_ERR_GEN;
402  }
403  }
404  length = 0;
405  }
406  else if (length < 0) {
407  fprintf(stderr, "Audio read error: %s\n",
408  snd_strerror(length));
409  return AD_ERR_GEN;
410  }
411  return length;
412 }
Definition: ad.h:255
int32 sps
Samples/sec.
Definition: ad.h:256
Basic type definitions used in Sphinx.
int32 bps
Bytes/sample.
Definition: ad.h:257
SPHINXBASE_EXPORT ad_rec_t * ad_open(void)
Open the default audio device.
Definition: ad_alsa.c:296
generic live audio interface for recording and playback
SPHINXBASE_EXPORT ad_rec_t * ad_open_dev(const char *dev, int32 samples_per_sec)
Open a specific audio device for recording.
Definition: ad_alsa.c:252
SPHINXBASE_EXPORT ad_rec_t * ad_open_sps(int32 samples_per_sec)
Open the default audio device with a given sampling rate.
Definition: ad_alsa.c:290