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
89static int
90setparams(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
178static int
179setlevels(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
251ad_rec_t *
252ad_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
289ad_rec_t *
290ad_open_sps(int32 sps)
291{
292 return ad_open_dev(DEFAULT_DEVICE, sps);
293}
294
295ad_rec_t *
297{
298 return ad_open_sps(DEFAULT_SAMPLES_PER_SEC);
299}
300
301
302int32
303ad_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
319int32
320ad_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
346int32
347ad_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
368int32
369ad_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}
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(void)
Open the default audio device.
Definition ad_alsa.c:296
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
Basic type definitions used in Sphinx.
Definition ad.h:255
int32 sps
Samples/sec.
Definition ad.h:256
int32 bps
Bytes/sample.
Definition ad.h:257