SphinxBase  5prealpha
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 #define AUDIO_FORMAT SND_PCM_SFMT_S16_LE /* 16-bit signed, little endian */
85 #define INPUT_GAIN 85
86 #define SPS_EPSILON 200
87 
88 #define DEFAULT_DEVICE "default"
89 
90 struct ad_rec_s {
91  snd_pcm_t *dspH;
92  int32 recording;
93  int32 sps;
94  int32 bps;
95 };
96 
97 static int
98 setparams(int32 sps, snd_pcm_t * handle)
99 {
100  snd_pcm_hw_params_t *hwparams;
101  unsigned int out_sps, buffer_time, period_time;
102  int err;
103 
104  snd_pcm_hw_params_alloca(&hwparams);
105  err = snd_pcm_hw_params_any(handle, hwparams);
106  if (err < 0) {
107  fprintf(stderr, "Can not configure this PCM device: %s\n",
108  snd_strerror(err));
109  return -1;
110  }
111 
112  err =
113  snd_pcm_hw_params_set_access(handle, hwparams,
114  SND_PCM_ACCESS_RW_INTERLEAVED);
115  if (err < 0) {
116  fprintf(stderr,
117  "Failed to set PCM device to interleaved: %s\n",
118  snd_strerror(err));
119  return -1;
120  }
121 
122  err =
123  snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16);
124  if (err < 0) {
125  fprintf(stderr,
126  "Failed to set PCM device to 16-bit signed PCM: %s\n",
127  snd_strerror(err));
128  return -1;
129  }
130 
131  err = snd_pcm_hw_params_set_channels(handle, hwparams, 1);
132  if (err < 0) {
133  fprintf(stderr, "Failed to set PCM device to mono: %s\n",
134  snd_strerror(err));
135  return -1;
136  }
137 
138  out_sps = sps;
139  err =
140  snd_pcm_hw_params_set_rate_near(handle, hwparams, &out_sps, NULL);
141  if (err < 0) {
142  fprintf(stderr, "Failed to set sampling rate: %s\n",
143  snd_strerror(err));
144  return -1;
145  }
146  if (abs(out_sps - sps) > SPS_EPSILON) {
147  fprintf(stderr,
148  "Available samping rate %d is too far from requested %d\n",
149  out_sps, sps);
150  return -1;
151  }
152 
153  /* Set buffer time to the maximum. */
154  err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0);
155  period_time = buffer_time / 4;
156  err = snd_pcm_hw_params_set_period_time_near(handle, hwparams,
157  &period_time, 0);
158  if (err < 0) {
159  fprintf(stderr, "Failed to set period time to %u: %s\n",
160  period_time, snd_strerror(err));
161  return -1;
162  }
163  err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams,
164  &buffer_time, 0);
165  if (err < 0) {
166  fprintf(stderr, "Failed to set buffer time to %u: %s\n",
167  buffer_time, snd_strerror(err));
168  return -1;
169  }
170 
171  err = snd_pcm_hw_params(handle, hwparams);
172  if (err < 0) {
173  fprintf(stderr, "Failed to set hwparams: %s\n", snd_strerror(err));
174  return -1;
175  }
176 
177  err = snd_pcm_nonblock(handle, 1);
178  if (err < 0) {
179  fprintf(stderr, "Failed to set non-blocking mode: %s\n",
180  snd_strerror(err));
181  return -1;
182  }
183  return 0;
184 }
185 
186 ad_rec_t *
187 ad_open_dev(const char *dev, int32 sps)
188 {
189  ad_rec_t *handle;
190  snd_pcm_t *dspH;
191 
192  int err;
193 
194  if (dev == NULL)
195  dev = DEFAULT_DEVICE;
196 
197  err = snd_pcm_open(&dspH, dev, SND_PCM_STREAM_CAPTURE, 0);
198  if (err < 0) {
199  fprintf(stderr,
200  "Error opening audio device %s for capture: %s\n",
201  dev, snd_strerror(err));
202  return NULL;
203  }
204 
205  if (setparams(sps, dspH) < 0) {
206  return NULL;
207  }
208  if ((handle = (ad_rec_t *) calloc(1, sizeof(ad_rec_t))) == NULL) {
209  fprintf(stderr, "calloc(%d) failed\n", (int)sizeof(ad_rec_t));
210  abort();
211  }
212 
213  handle->dspH = dspH;
214  handle->recording = 0;
215  handle->sps = sps;
216  handle->bps = sizeof(int16);
217 
218  return (handle);
219 }
220 
221 ad_rec_t *
222 ad_open_sps(int32 sps)
223 {
224  return ad_open_dev(DEFAULT_DEVICE, sps);
225 }
226 
227 ad_rec_t *
228 ad_open(void)
229 {
230  return ad_open_sps(DEFAULT_SAMPLES_PER_SEC);
231 }
232 
233 
234 int32
235 ad_close(ad_rec_t * handle)
236 {
237  if (handle->dspH == NULL)
238  return AD_ERR_NOT_OPEN;
239 
240  if (handle->recording) {
241  if (ad_stop_rec(handle) < 0)
242  return AD_ERR_GEN;
243  }
244  snd_pcm_close(handle->dspH);
245  free(handle);
246 
247  return (0);
248 }
249 
250 
251 int32
252 ad_start_rec(ad_rec_t * handle)
253 {
254  int err;
255 
256  if (handle->dspH == NULL)
257  return AD_ERR_NOT_OPEN;
258 
259  if (handle->recording)
260  return AD_ERR_GEN;
261 
262  err = snd_pcm_prepare(handle->dspH);
263  if (err < 0) {
264  fprintf(stderr, "snd_pcm_prepare failed: %s\n", snd_strerror(err));
265  return AD_ERR_GEN;
266  }
267  err = snd_pcm_start(handle->dspH);
268  if (err < 0) {
269  fprintf(stderr, "snd_pcm_start failed: %s\n", snd_strerror(err));
270  return AD_ERR_GEN;
271  }
272  handle->recording = 1;
273 
274  return (0);
275 }
276 
277 
278 int32
279 ad_stop_rec(ad_rec_t * handle)
280 {
281  int err;
282 
283  if (handle->dspH == NULL)
284  return AD_ERR_NOT_OPEN;
285 
286  if (!handle->recording)
287  return AD_ERR_GEN;
288 
289  err = snd_pcm_drop(handle->dspH);
290  if (err < 0) {
291  fprintf(stderr, "snd_pcm_drop failed: %s\n", snd_strerror(err));
292  return AD_ERR_GEN;
293  }
294  handle->recording = 0;
295 
296  return (0);
297 }
298 
299 
300 int32
301 ad_read(ad_rec_t * handle, int16 * buf, int32 max)
302 {
303  int32 length, err;
304 
305  if (!handle->recording) {
306  fprintf(stderr, "Recording is stopped, start recording with ad_start_rec\n");
307  return AD_EOF;
308  }
309 
310  length = snd_pcm_readi(handle->dspH, buf, max);
311  if (length == -EAGAIN) {
312  length = 0;
313  }
314  else if (length == -EPIPE) {
315  fprintf(stderr, "Input overrun, read calls are too rare (non-fatal)\n");
316  err = snd_pcm_prepare(handle->dspH);
317  if (err < 0) {
318  fprintf(stderr, "Can't recover from underrun: %s\n",
319  snd_strerror(err));
320  return AD_ERR_GEN;
321  }
322  length = 0;
323  }
324  else if (length == -ESTRPIPE) {
325  fprintf(stderr, "Resuming sound driver (non-fatal)\n");
326  while ((err = snd_pcm_resume(handle->dspH)) == -EAGAIN)
327  usleep(10000); /* Wait for the driver to wake up */
328  if (err < 0) {
329  err = snd_pcm_prepare(handle->dspH);
330  if (err < 0) {
331  fprintf(stderr, "Can't recover from underrun: %s\n",
332  snd_strerror(err));
333  return AD_ERR_GEN;
334  }
335  }
336  length = 0;
337  }
338  else if (length < 0) {
339  fprintf(stderr, "Audio read error: %s\n",
340  snd_strerror(length));
341  return AD_ERR_GEN;
342  }
343  return length;
344 }
Audio recording structure.
int32 bps
Bytes/sample.
Definition: ad_alsa.c:94
Basic type definitions used in Sphinx.
SPHINXBASE_EXPORT ad_rec_t * ad_open(void)
Open the default audio device.
Definition: ad_alsa.c:228
generic live audio interface for recording and playback
int32 sps
Samples/sec.
Definition: ad_alsa.c:93
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:187
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:222
Audio recording structure.
Definition: ad_alsa.c:90