SphinxBase 0.6
|
00001 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */ 00002 /* ==================================================================== 00003 * Copyright (c) 1999-2001 Carnegie Mellon University. All rights 00004 * reserved. 00005 * 00006 * Redistribution and use in source and binary forms, with or without 00007 * modification, are permitted provided that the following conditions 00008 * are met: 00009 * 00010 * 1. Redistributions of source code must retain the above copyright 00011 * notice, this list of conditions and the following disclaimer. 00012 * 00013 * 2. Redistributions in binary form must reproduce the above copyright 00014 * notice, this list of conditions and the following disclaimer in 00015 * the documentation and/or other materials provided with the 00016 * distribution. 00017 * 00018 * This work was supported in part by funding from the Defense Advanced 00019 * Research Projects Agency and the National Science Foundation of the 00020 * United States of America, and the CMU Sphinx Speech Consortium. 00021 * 00022 * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND 00023 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 00024 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00025 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY 00026 * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00027 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00028 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00029 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00030 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00031 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00032 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00033 * 00034 * ==================================================================== 00035 * 00036 */ 00037 /* -*- mode:c; indent-tabs-mode:t; c-basic-offset:4; comment-column:40 -*- 00038 * 00039 * Sphinx II libad (Linux) 00040 * ^^^^^^^^^^^^^^^^^^^^^^^ 00041 * $Id: ad_alsa.c,v 1.6 2001/12/11 00:24:48 lenzo Exp $ 00042 * 00043 * John G. Dorsey (jd5q+@andrew.cmu.edu) 00044 * Engineering Design Research Center 00045 * Carnegie Mellon University 00046 * *************************************************************************** 00047 * 00048 * REVISION HISTORY 00049 * 00050 * 18-Mar-2006 David Huggins-Daines <dhuggins@cs.cmu.edu> 00051 * Update this to the ALSA 1.0 API. 00052 * 00053 * 12-Dec-2000 David Huggins-Daines <dhd@cepstral.com> at Cepstral LLC 00054 * Make this at least compile with the new ALSA API. 00055 * 00056 * 05-Nov-1999 Sean Levy (snl@stalphonsos.com) at St. Alphonsos, LLC. 00057 * Ported to ALSA so I can actually get working full-duplex. 00058 * 00059 * 09-Aug-1999 Kevin Lenzo (lenzo@cs.cmu.edu) at Cernegie Mellon University. 00060 * Incorporated nickr@cs.cmu.edu's changes (marked below) and 00061 * SPS_EPSILON to allow for sample rates that are "close enough". 00062 * 00063 * 15-Jun-1999 M. K. Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon Univ. 00064 * Consolidated all ad functions into 00065 * this one file. Added ad_open_sps(). 00066 * Other cosmetic changes for consistency (e.g., use of err.h). 00067 * 00068 * 18-May-1999 Kevin Lenzo (lenzo@cs.cmu.edu) added <errno.h>. 00069 */ 00070 00071 00072 #include <fcntl.h> 00073 #include <stdio.h> 00074 #include <stdlib.h> 00075 #include <string.h> 00076 #include <alsa/asoundlib.h> 00077 #include <errno.h> 00078 #include <config.h> 00079 #include <unistd.h> 00080 00081 #include "prim_type.h" 00082 #include "ad.h" 00083 00084 00085 #define AUDIO_FORMAT SND_PCM_SFMT_S16_LE /* 16-bit signed, little endian */ 00086 #define INPUT_GAIN (85) 00087 #define SPS_EPSILON 200 00088 00089 static int 00090 setparams(int32 sps, snd_pcm_t * handle) 00091 { 00092 snd_pcm_hw_params_t *hwparams; 00093 unsigned int out_sps, buffer_time, period_time; 00094 int err; 00095 00096 snd_pcm_hw_params_alloca(&hwparams); 00097 err = snd_pcm_hw_params_any(handle, hwparams); 00098 if (err < 0) { 00099 fprintf(stderr, "Can not configure this PCM device: %s\n", 00100 snd_strerror(err)); 00101 return -1; 00102 } 00103 00104 err = 00105 snd_pcm_hw_params_set_access(handle, hwparams, 00106 SND_PCM_ACCESS_RW_INTERLEAVED); 00107 if (err < 0) { 00108 fprintf(stderr, 00109 "Failed to set PCM device to interleaved: %s\n", 00110 snd_strerror(err)); 00111 return -1; 00112 } 00113 00114 err = 00115 snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16); 00116 if (err < 0) { 00117 fprintf(stderr, 00118 "Failed to set PCM device to 16-bit signed PCM: %s\n", 00119 snd_strerror(err)); 00120 return -1; 00121 } 00122 00123 err = snd_pcm_hw_params_set_channels(handle, hwparams, 1); 00124 if (err < 0) { 00125 fprintf(stderr, "Failed to set PCM device to mono: %s\n", 00126 snd_strerror(err)); 00127 return -1; 00128 } 00129 00130 out_sps = sps; 00131 err = 00132 snd_pcm_hw_params_set_rate_near(handle, hwparams, &out_sps, NULL); 00133 if (err < 0) { 00134 fprintf(stderr, "Failed to set sampling rate: %s\n", 00135 snd_strerror(err)); 00136 return -1; 00137 } 00138 if (abs(out_sps - sps) > SPS_EPSILON) { 00139 fprintf(stderr, 00140 "Available samping rate %d is too far from requested %d\n", 00141 out_sps, sps); 00142 return -1; 00143 } 00144 00145 /* Set buffer time to the maximum. */ 00146 err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0); 00147 period_time = buffer_time / 4; 00148 err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, 00149 &period_time, 0); 00150 if (err < 0) { 00151 fprintf(stderr, "Failed to set period time to %u: %s\n", 00152 period_time, snd_strerror(err)); 00153 return -1; 00154 } 00155 err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, 00156 &buffer_time, 0); 00157 if (err < 0) { 00158 fprintf(stderr, "Failed to set buffer time to %u: %s\n", 00159 buffer_time, snd_strerror(err)); 00160 return -1; 00161 } 00162 00163 err = snd_pcm_hw_params(handle, hwparams); 00164 if (err < 0) { 00165 fprintf(stderr, "Failed to set hwparams: %s\n", snd_strerror(err)); 00166 return -1; 00167 } 00168 00169 err = snd_pcm_nonblock(handle, 1); 00170 if (err < 0) { 00171 fprintf(stderr, "Failed to set non-blocking mode: %s\n", 00172 snd_strerror(err)); 00173 return -1; 00174 } 00175 return 0; 00176 } 00177 00178 static int 00179 setlevels(const char *dev) 00180 { 00181 snd_mixer_t *handle; 00182 snd_mixer_selem_id_t *sid; 00183 snd_mixer_elem_t *elem; 00184 int err; 00185 char *mixer_dev, *c; 00186 00187 /* Basically we just want to turn on Mic capture. */ 00188 if ((err = snd_mixer_open(&handle, 0)) < 0) { 00189 fprintf(stderr, "Mixer open failed: %s\n", snd_strerror(err)); 00190 return -1; 00191 } 00192 00193 mixer_dev = strdup(dev); 00194 if (strncmp(mixer_dev, "plug", 4) == 0) 00195 memmove(mixer_dev, mixer_dev + 4, strlen(mixer_dev) - 4 + 1); 00196 if ((c = strchr(mixer_dev, ','))) 00197 *c = '\0'; 00198 if ((err = snd_mixer_attach(handle, mixer_dev)) < 0) { 00199 fprintf(stderr, "Mixer attach to %s failed: %s\n", 00200 mixer_dev, snd_strerror(err)); 00201 free(mixer_dev); 00202 snd_mixer_close(handle); 00203 return -1; 00204 } 00205 free(mixer_dev); 00206 if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { 00207 fprintf(stderr, "Mixer register failed: %s\n", snd_strerror(err)); 00208 snd_mixer_close(handle); 00209 return -1; 00210 } 00211 if ((err = snd_mixer_load(handle)) < 0) { 00212 fprintf(stderr, "Mixer load failed: %s\n", snd_strerror(err)); 00213 snd_mixer_close(handle); 00214 return -1; 00215 } 00216 snd_mixer_selem_id_alloca(&sid); 00217 snd_mixer_selem_id_set_name(sid, "Mic"); 00218 if ((elem = snd_mixer_find_selem(handle, sid)) == NULL) { 00219 fprintf(stderr, "Warning: Could not find Mic element\n"); 00220 } 00221 else { 00222 if (snd_mixer_selem_has_capture_switch(elem)) { 00223 if ((err = snd_mixer_selem_set_capture_switch_all(elem, 1)) < 0) { 00224 fprintf(stderr, 00225 "Failed to enable microphone capture: %s\n", 00226 snd_strerror(err)); 00227 snd_mixer_close(handle); 00228 return -1; 00229 } 00230 } 00231 } 00232 snd_mixer_selem_id_set_name(sid, "Capture"); 00233 if ((elem = snd_mixer_find_selem(handle, sid)) == NULL) { 00234 fprintf(stderr, "Warning: Could not find Capture element\n"); 00235 } 00236 else { 00237 if (snd_mixer_selem_has_capture_switch(elem)) { 00238 if ((err = snd_mixer_selem_set_capture_switch_all(elem, 1)) < 0) { 00239 fprintf(stderr, 00240 "Failed to enable microphone capture: %s\n", 00241 snd_strerror(err)); 00242 snd_mixer_close(handle); 00243 return -1; 00244 } 00245 } 00246 } 00247 00248 return 0; 00249 } 00250 00251 ad_rec_t * 00252 ad_open_dev(const char *dev, int32 sps) 00253 { 00254 ad_rec_t *handle; 00255 snd_pcm_t *dspH; 00256 00257 int err; 00258 00259 if (dev == NULL) 00260 dev = DEFAULT_DEVICE; 00261 00262 err = snd_pcm_open(&dspH, dev, SND_PCM_STREAM_CAPTURE, 0); 00263 if (err < 0) { 00264 fprintf(stderr, 00265 "Error opening audio device %s for capture: %s\n", 00266 dev, snd_strerror(err)); 00267 return NULL; 00268 } 00269 00270 if (setparams(sps, dspH) < 0) { 00271 return NULL; 00272 } 00273 if (setlevels(dev) < 0) { 00274 return NULL; 00275 } 00276 if ((handle = (ad_rec_t *) calloc(1, sizeof(ad_rec_t))) == NULL) { 00277 fprintf(stderr, "calloc(%d) failed\n", (int)sizeof(ad_rec_t)); 00278 abort(); 00279 } 00280 00281 handle->dspH = dspH; 00282 handle->recording = 0; 00283 handle->sps = sps; 00284 handle->bps = sizeof(int16); 00285 00286 return (handle); 00287 } 00288 00289 ad_rec_t * 00290 ad_open_sps(int32 sps) 00291 { 00292 return ad_open_dev(DEFAULT_DEVICE, sps); 00293 } 00294 00295 ad_rec_t * 00296 ad_open(void) 00297 { 00298 return ad_open_sps(DEFAULT_SAMPLES_PER_SEC); 00299 } 00300 00301 00302 int32 00303 ad_close(ad_rec_t * handle) 00304 { 00305 if (handle->dspH == NULL) 00306 return AD_ERR_NOT_OPEN; 00307 00308 if (handle->recording) { 00309 if (ad_stop_rec(handle) < 0) 00310 return AD_ERR_GEN; 00311 } 00312 snd_pcm_close(handle->dspH); 00313 free(handle); 00314 00315 return (0); 00316 } 00317 00318 00319 int32 00320 ad_start_rec(ad_rec_t * handle) 00321 { 00322 int err; 00323 00324 if (handle->dspH == NULL) 00325 return AD_ERR_NOT_OPEN; 00326 00327 if (handle->recording) 00328 return AD_ERR_GEN; 00329 00330 err = snd_pcm_prepare(handle->dspH); 00331 if (err < 0) { 00332 fprintf(stderr, "snd_pcm_prepare failed: %s\n", snd_strerror(err)); 00333 return AD_ERR_GEN; 00334 } 00335 err = snd_pcm_start(handle->dspH); 00336 if (err < 0) { 00337 fprintf(stderr, "snd_pcm_start failed: %s\n", snd_strerror(err)); 00338 return AD_ERR_GEN; 00339 } 00340 handle->recording = 1; 00341 00342 return (0); 00343 } 00344 00345 00346 int32 00347 ad_stop_rec(ad_rec_t * handle) 00348 { 00349 int err; 00350 00351 if (handle->dspH == NULL) 00352 return AD_ERR_NOT_OPEN; 00353 00354 if (!handle->recording) 00355 return AD_ERR_GEN; 00356 00357 err = snd_pcm_drop(handle->dspH); 00358 if (err < 0) { 00359 fprintf(stderr, "snd_pcm_drop failed: %s\n", snd_strerror(err)); 00360 return AD_ERR_GEN; 00361 } 00362 handle->recording = 0; 00363 00364 return (0); 00365 } 00366 00367 00368 int32 00369 ad_read(ad_rec_t * handle, int16 * buf, int32 max) 00370 { 00371 int32 length, err; 00372 00373 if (!handle->recording) { 00374 fprintf(stderr, "Recording is stopped, start recording with ad_start_rec\n"); 00375 return AD_EOF; 00376 } 00377 00378 length = snd_pcm_readi(handle->dspH, buf, max); 00379 if (length == -EAGAIN) { 00380 length = 0; 00381 } 00382 else if (length == -EPIPE) { 00383 fprintf(stderr, "Input overrun, read calls are too rare (non-fatal)\n"); 00384 err = snd_pcm_prepare(handle->dspH); 00385 if (err < 0) { 00386 fprintf(stderr, "Can't recover from underrun: %s\n", 00387 snd_strerror(err)); 00388 return AD_ERR_GEN; 00389 } 00390 length = 0; 00391 } 00392 else if (length == -ESTRPIPE) { 00393 fprintf(stderr, "Resuming sound driver (non-fatal)\n"); 00394 while ((err = snd_pcm_resume(handle->dspH)) == -EAGAIN) 00395 usleep(10000); /* Wait for the driver to wake up */ 00396 if (err < 0) { 00397 err = snd_pcm_prepare(handle->dspH); 00398 if (err < 0) { 00399 fprintf(stderr, "Can't recover from underrun: %s\n", 00400 snd_strerror(err)); 00401 return AD_ERR_GEN; 00402 } 00403 } 00404 length = 0; 00405 } 00406 else if (length < 0) { 00407 fprintf(stderr, "Audio read error: %s\n", 00408 snd_strerror(length)); 00409 return AD_ERR_GEN; 00410 } 00411 return length; 00412 }