/* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* ==================================================================== * Copyright (c) 1999-2001 Carnegie Mellon University. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * This work was supported in part by funding from the Defense Advanced * Research Projects Agency and the National Science Foundation of the * United States of America, and the CMU Sphinx Speech Consortium. * * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ==================================================================== * */ /* -*- mode:c; indent-tabs-mode:t; c-basic-offset:4; comment-column:40 -*- * * Sphinx II libad (Linux) * ^^^^^^^^^^^^^^^^^^^^^^^ * $Id: ad_alsa.c,v 1.6 2001/12/11 00:24:48 lenzo Exp $ * * John G. Dorsey (jd5q+@andrew.cmu.edu) * Engineering Design Research Center * Carnegie Mellon University * *************************************************************************** * * REVISION HISTORY * * 18-Mar-2006 David Huggins-Daines * Update this to the ALSA 1.0 API. * * 12-Dec-2000 David Huggins-Daines at Cepstral LLC * Make this at least compile with the new ALSA API. * * 05-Nov-1999 Sean Levy (snl@stalphonsos.com) at St. Alphonsos, LLC. * Ported to ALSA so I can actually get working full-duplex. * * 09-Aug-1999 Kevin Lenzo (lenzo@cs.cmu.edu) at Cernegie Mellon University. * Incorporated nickr@cs.cmu.edu's changes (marked below) and * SPS_EPSILON to allow for sample rates that are "close enough". * * 15-Jun-1999 M. K. Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon Univ. * Consolidated all ad functions into * this one file. Added ad_open_sps(). * Other cosmetic changes for consistency (e.g., use of err.h). * * 18-May-1999 Kevin Lenzo (lenzo@cs.cmu.edu) added . */ #include #include #include #include #include #include #include #include #include "prim_type.h" #include "ad.h" #define AUDIO_FORMAT SND_PCM_SFMT_S16_LE /* 16-bit signed, little endian */ #define INPUT_GAIN 85 #define SPS_EPSILON 200 #define DEFAULT_DEVICE "default" struct ad_rec_s { snd_pcm_t *dspH; int32 recording; int32 sps; int32 bps; }; static int setparams(int32 sps, snd_pcm_t * handle) { snd_pcm_hw_params_t *hwparams; unsigned int out_sps, buffer_time, period_time; int err; snd_pcm_hw_params_alloca(&hwparams); err = snd_pcm_hw_params_any(handle, hwparams); if (err < 0) { fprintf(stderr, "Can not configure this PCM device: %s\n", snd_strerror(err)); return -1; } err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { fprintf(stderr, "Failed to set PCM device to interleaved: %s\n", snd_strerror(err)); return -1; } err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16); if (err < 0) { fprintf(stderr, "Failed to set PCM device to 16-bit signed PCM: %s\n", snd_strerror(err)); return -1; } err = snd_pcm_hw_params_set_channels(handle, hwparams, 1); if (err < 0) { fprintf(stderr, "Failed to set PCM device to mono: %s\n", snd_strerror(err)); return -1; } out_sps = sps; err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &out_sps, NULL); if (err < 0) { fprintf(stderr, "Failed to set sampling rate: %s\n", snd_strerror(err)); return -1; } if (abs(out_sps - sps) > SPS_EPSILON) { fprintf(stderr, "Available samping rate %d is too far from requested %d\n", out_sps, sps); return -1; } /* Set buffer time to the maximum. */ err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0); period_time = buffer_time / 4; err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0); if (err < 0) { fprintf(stderr, "Failed to set period time to %u: %s\n", period_time, snd_strerror(err)); return -1; } err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0); if (err < 0) { fprintf(stderr, "Failed to set buffer time to %u: %s\n", buffer_time, snd_strerror(err)); return -1; } err = snd_pcm_hw_params(handle, hwparams); if (err < 0) { fprintf(stderr, "Failed to set hwparams: %s\n", snd_strerror(err)); return -1; } err = snd_pcm_nonblock(handle, 1); if (err < 0) { fprintf(stderr, "Failed to set non-blocking mode: %s\n", snd_strerror(err)); return -1; } return 0; } ad_rec_t * ad_open_dev(const char *dev, int32 sps) { ad_rec_t *handle; snd_pcm_t *dspH; int err; if (dev == NULL) dev = DEFAULT_DEVICE; err = snd_pcm_open(&dspH, dev, SND_PCM_STREAM_CAPTURE, 0); if (err < 0) { fprintf(stderr, "Error opening audio device %s for capture: %s\n", dev, snd_strerror(err)); return NULL; } if (setparams(sps, dspH) < 0) { return NULL; } if ((handle = (ad_rec_t *) calloc(1, sizeof(ad_rec_t))) == NULL) { fprintf(stderr, "calloc(%d) failed\n", (int)sizeof(ad_rec_t)); abort(); } handle->dspH = dspH; handle->recording = 0; handle->sps = sps; handle->bps = sizeof(int16); return (handle); } ad_rec_t * ad_open_sps(int32 sps) { return ad_open_dev(DEFAULT_DEVICE, sps); } ad_rec_t * ad_open(void) { return ad_open_sps(DEFAULT_SAMPLES_PER_SEC); } int32 ad_close(ad_rec_t * handle) { if (handle->dspH == NULL) return AD_ERR_NOT_OPEN; if (handle->recording) { if (ad_stop_rec(handle) < 0) return AD_ERR_GEN; } snd_pcm_close(handle->dspH); free(handle); return (0); } int32 ad_start_rec(ad_rec_t * handle) { int err; if (handle->dspH == NULL) return AD_ERR_NOT_OPEN; if (handle->recording) return AD_ERR_GEN; err = snd_pcm_prepare(handle->dspH); if (err < 0) { fprintf(stderr, "snd_pcm_prepare failed: %s\n", snd_strerror(err)); return AD_ERR_GEN; } err = snd_pcm_start(handle->dspH); if (err < 0) { fprintf(stderr, "snd_pcm_start failed: %s\n", snd_strerror(err)); return AD_ERR_GEN; } handle->recording = 1; return (0); } int32 ad_stop_rec(ad_rec_t * handle) { int err; if (handle->dspH == NULL) return AD_ERR_NOT_OPEN; if (!handle->recording) return AD_ERR_GEN; err = snd_pcm_drop(handle->dspH); if (err < 0) { fprintf(stderr, "snd_pcm_drop failed: %s\n", snd_strerror(err)); return AD_ERR_GEN; } handle->recording = 0; return (0); } int32 ad_read(ad_rec_t * handle, int16 * buf, int32 max) { int32 length, err; if (!handle->recording) { fprintf(stderr, "Recording is stopped, start recording with ad_start_rec\n"); return AD_EOF; } length = snd_pcm_readi(handle->dspH, buf, max); if (length == -EAGAIN) { length = 0; } else if (length == -EPIPE) { fprintf(stderr, "Input overrun, read calls are too rare (non-fatal)\n"); err = snd_pcm_prepare(handle->dspH); if (err < 0) { fprintf(stderr, "Can't recover from underrun: %s\n", snd_strerror(err)); return AD_ERR_GEN; } length = 0; } else if (length == -ESTRPIPE) { fprintf(stderr, "Resuming sound driver (non-fatal)\n"); while ((err = snd_pcm_resume(handle->dspH)) == -EAGAIN) usleep(10000); /* Wait for the driver to wake up */ if (err < 0) { err = snd_pcm_prepare(handle->dspH); if (err < 0) { fprintf(stderr, "Can't recover from underrun: %s\n", snd_strerror(err)); return AD_ERR_GEN; } } length = 0; } else if (length < 0) { fprintf(stderr, "Audio read error: %s\n", snd_strerror(length)); return AD_ERR_GEN; } return length; }