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
90struct ad_rec_s {
91 snd_pcm_t *dspH;
92 int32 recording;
93 int32 sps;
94 int32 bps;
95};
96
97static int
98setparams(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
186ad_rec_t *
187ad_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
221ad_rec_t *
222ad_open_sps(int32 sps)
223{
224 return ad_open_dev(DEFAULT_DEVICE, sps);
225}
226
227ad_rec_t *
229{
230 return ad_open_sps(DEFAULT_SAMPLES_PER_SEC);
231}
232
233
234int32
235ad_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
251int32
252ad_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
278int32
279ad_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
300int32
301ad_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}
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:187
SPHINXBASE_EXPORT ad_rec_t * ad_open(void)
Open the default audio device.
Definition: ad_alsa.c:228
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
Basic type definitions used in Sphinx.
Audio recording structure.
Definition: ad_alsa.c:90
int32 sps
Samples/sec.
Definition: ad_alsa.c:93
int32 bps
Bytes/sample.
Definition: ad_alsa.c:94
Audio recording structure.