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 /* Sphinx II libad (Linux) 00038 * ^^^^^^^^^^^^^^^^^^^^^^^ 00039 * $Id: ad_oss.c,v 1.9 2004/07/16 00:57:12 egouvea Exp $ 00040 * 00041 * John G. Dorsey (jd5q+@andrew.cmu.edu) 00042 * Engineering Design Research Center 00043 * Carnegie Mellon University 00044 * ******************************************************************** 00045 * 00046 * REVISION HISTORY 00047 * 00048 * 09-Aug-1999 Kevin Lenzo (lenzo@cs.cmu.edu) at Cernegie Mellon University. 00049 * Incorporated nickr@cs.cmu.edu's changes (marked below) and 00050 * SPS_EPSILON to allow for sample rates that are "close enough". 00051 * 00052 * 15-Jun-1999 M. K. Ravishankar (rkm@cs.cmu.edu) Consolidated all ad functions into 00053 * this one file. Added ad_open_sps(). 00054 * Other cosmetic changes for consistency (e.g., use of err.h). 00055 * 00056 * 18-May-1999 Kevin Lenzo (lenzo@cs.cmu.edu) added <errno.h>. 00057 */ 00058 00059 #include <fcntl.h> 00060 #include <stdio.h> 00061 #include <stdlib.h> 00062 #include <string.h> 00063 #include <sys/soundcard.h> 00064 #include <sys/ioctl.h> 00065 #include <errno.h> 00066 #include <unistd.h> 00067 #include <config.h> 00068 00069 #include "prim_type.h" 00070 #include "ad.h" 00071 00072 #define AUDIO_FORMAT AFMT_S16_LE /* 16-bit signed, little endian */ 00073 #define INPUT_GAIN (80) 00074 00075 #define SPS_EPSILON 200 00076 #define SAMPLERATE_TOLERANCE 0.01 00077 00078 ad_rec_t * 00079 ad_open_dev(const char *dev, int32 sps) 00080 { 00081 ad_rec_t *handle; 00082 int32 dspFD, mixerFD; 00083 int32 nonBlocking = 1, sourceMic = SOUND_MASK_MIC, inputGain = 00084 INPUT_GAIN, devMask = 0; 00085 int32 audioFormat = AUDIO_FORMAT; 00086 int32 dspCaps = 0; 00087 int32 sampleRate; 00088 int32 numberChannels = 1; 00089 00090 sampleRate = sps; 00091 00092 if (dev == NULL) 00093 dev = DEFAULT_DEVICE; 00094 00095 /* Used to have O_NDELAY. */ 00096 if ((dspFD = open(dev, O_RDONLY)) < 0) { 00097 if (errno == EBUSY) 00098 fprintf(stderr, "%s(%d): Audio device(%s) busy\n", 00099 __FILE__, __LINE__, dev); 00100 else 00101 fprintf(stderr, 00102 "%s(%d): Failed to open audio device(%s): %s\n", 00103 __FILE__, __LINE__, dev, strerror(errno)); 00104 return NULL; 00105 } 00106 00107 if (ioctl(dspFD, SNDCTL_DSP_SYNC, 0) < 0) { 00108 fprintf(stderr, "Audio ioctl(SYNC) failed: %s\n", strerror(errno)); 00109 close(dspFD); 00110 return NULL; 00111 } 00112 00113 if (ioctl(dspFD, SNDCTL_DSP_RESET, 0) < 0) { 00114 fprintf(stderr, "Audio ioctl(RESET) failed: %s\n", 00115 strerror(errno)); 00116 close(dspFD); 00117 return NULL; 00118 } 00119 00120 if (ioctl(dspFD, SNDCTL_DSP_SETFMT, &audioFormat) < 0) { 00121 fprintf(stderr, "Audio ioctl(SETFMT 0x%x) failed: %s\n", 00122 audioFormat, strerror(errno)); 00123 close(dspFD); 00124 return NULL; 00125 } 00126 if (audioFormat != AUDIO_FORMAT) { 00127 fprintf(stderr, 00128 "Audio ioctl(SETFMT): 0x%x, expected: 0x%x\n", 00129 audioFormat, AUDIO_FORMAT); 00130 close(dspFD); 00131 return NULL; 00132 } 00133 00134 if (ioctl(dspFD, SNDCTL_DSP_SPEED, &sampleRate) < 0) { 00135 fprintf(stderr, "Audio ioctl(SPEED %d) failed %s\n", 00136 sampleRate, strerror(errno)); 00137 close(dspFD); 00138 return NULL; 00139 } 00140 if (sampleRate != sps) { 00141 if (abs(sampleRate - sps) <= (sampleRate * SAMPLERATE_TOLERANCE)) { 00142 fprintf(stderr, 00143 "Audio ioctl(SPEED) not perfect, but is acceptable. " 00144 "(Wanted %d, but got %d)\n", sampleRate, sps); 00145 } 00146 else { 00147 fprintf(stderr, 00148 "Audio ioctl(SPEED): %d, expected: %d\n", 00149 sampleRate, sps); 00150 close(dspFD); 00151 return NULL; 00152 } 00153 } 00154 00155 if (ioctl(dspFD, SNDCTL_DSP_CHANNELS, &numberChannels) < 0) { 00156 fprintf(stderr, "Audio ioctl(CHANNELS %d) failed %s\n", 00157 numberChannels, strerror(errno)); 00158 close(dspFD); 00159 return NULL; 00160 } 00161 00162 if (ioctl(dspFD, SNDCTL_DSP_NONBLOCK, &nonBlocking) < 0) { 00163 fprintf(stderr, "ioctl(NONBLOCK) failed: %s\n", strerror(errno)); 00164 close(dspFD); 00165 return NULL; 00166 } 00167 00168 if (ioctl(dspFD, SNDCTL_DSP_GETCAPS, &dspCaps) < 0) { 00169 fprintf(stderr, "ioctl(GETCAPS) failed: %s\n", strerror(errno)); 00170 close(dspFD); 00171 return NULL; 00172 } 00173 #if 0 00174 printf("DSP Revision %d:\n", dspCaps & DSP_CAP_REVISION); 00175 printf("DSP %s duplex capability.\n", 00176 (dspCaps & DSP_CAP_DUPLEX) ? "has" : "does not have"); 00177 printf("DSP %s real time capability.\n", 00178 (dspCaps & DSP_CAP_REALTIME) ? "has" : "does not have"); 00179 printf("DSP %s batch capability.\n", 00180 (dspCaps & DSP_CAP_BATCH) ? "has" : "does not have"); 00181 printf("DSP %s coprocessor capability.\n", 00182 (dspCaps & DSP_CAP_COPROC) ? "has" : "does not have"); 00183 printf("DSP %s trigger capability.\n", 00184 (dspCaps & DSP_CAP_TRIGGER) ? "has" : "does not have"); 00185 printf("DSP %s memory map capability.\n", 00186 (dspCaps & DSP_CAP_MMAP) ? "has" : "does not have"); 00187 #endif 00188 00189 if ((dspCaps & DSP_CAP_DUPLEX) 00190 && (ioctl(dspFD, SNDCTL_DSP_SETDUPLEX, 0) < 0)) 00191 fprintf(stderr, "ioctl(SETDUPLEX) failed: %s\n", strerror(errno)); 00192 00193 /* Patched by N. Roy (nickr@ri.cmu.edu), 99/7/23. 00194 Previously, mixer was set through dspFD. This is incorrect. Should 00195 be set through mixerFD, /dev/mixer. 00196 Also, only the left channel volume was being set. 00197 */ 00198 00199 if ((mixerFD = open("/dev/mixer", O_RDONLY)) < 0) { 00200 if (errno == EBUSY) { 00201 fprintf(stderr, "%s %d: mixer device busy.\n", 00202 __FILE__, __LINE__); 00203 fprintf(stderr, "%s %d: Using current setting.\n", 00204 __FILE__, __LINE__); 00205 } 00206 else { 00207 fprintf(stderr, "%s %d: %s\n", __FILE__, __LINE__, 00208 strerror(errno)); 00209 exit(1); 00210 } 00211 } 00212 00213 if (mixerFD >= 0) { 00214 if (ioctl(mixerFD, SOUND_MIXER_WRITE_RECSRC, &sourceMic) < 0) { 00215 if (errno == ENXIO) 00216 fprintf(stderr, 00217 "%s %d: can't set mic source for this device.\n", 00218 __FILE__, __LINE__); 00219 else { 00220 fprintf(stderr, 00221 "%s %d: mixer set to mic: %s\n", 00222 __FILE__, __LINE__, strerror(errno)); 00223 exit(1); 00224 } 00225 } 00226 00227 /* Set the same gain for left and right channels. */ 00228 inputGain = inputGain << 8 | inputGain; 00229 00230 /* Some OSS devices have no input gain control, but do have a 00231 recording level control. Find out if this is one of them and 00232 adjust accordingly. */ 00233 if (ioctl(mixerFD, SOUND_MIXER_READ_DEVMASK, &devMask) < 0) { 00234 fprintf(stderr, 00235 "%s %d: failed to read device mask: %s\n", 00236 __FILE__, __LINE__, strerror(errno)); 00237 exit(1); /* FIXME: not a well-behaved-library thing to do! */ 00238 } 00239 if (devMask & SOUND_MASK_IGAIN) { 00240 if (ioctl(mixerFD, SOUND_MIXER_WRITE_IGAIN, &inputGain) < 0) { 00241 fprintf(stderr, 00242 "%s %d: mixer input gain to %d: %s\n", 00243 __FILE__, __LINE__, inputGain, strerror(errno)); 00244 exit(1); 00245 } 00246 } 00247 else if (devMask & SOUND_MASK_RECLEV) { 00248 if (ioctl(mixerFD, SOUND_MIXER_WRITE_RECLEV, &inputGain) < 0) { 00249 fprintf(stderr, 00250 "%s %d: mixer record level to %d: %s\n", 00251 __FILE__, __LINE__, inputGain, strerror(errno)); 00252 exit(1); 00253 } 00254 } 00255 else { 00256 fprintf(stderr, 00257 "%s %d: can't set input gain/recording level for this device.\n", 00258 __FILE__, __LINE__); 00259 } 00260 00261 close(mixerFD); 00262 } 00263 00264 if ((handle = (ad_rec_t *) calloc(1, sizeof(ad_rec_t))) == NULL) { 00265 fprintf(stderr, "calloc(%ld) failed\n", sizeof(ad_rec_t)); 00266 abort(); 00267 } 00268 00269 handle->dspFD = dspFD; 00270 handle->recording = 0; 00271 handle->sps = sps; 00272 handle->bps = sizeof(int16); 00273 00274 return (handle); 00275 } 00276 00277 ad_rec_t * 00278 ad_open_sps(int32 sps) 00279 { 00280 return ad_open_dev(DEFAULT_DEVICE, sps); 00281 } 00282 00283 ad_rec_t * 00284 ad_open(void) 00285 { 00286 return ad_open_sps(DEFAULT_SAMPLES_PER_SEC); 00287 } 00288 00289 int32 00290 ad_close(ad_rec_t * handle) 00291 { 00292 if (handle->dspFD < 0) 00293 return AD_ERR_NOT_OPEN; 00294 00295 if (handle->recording) { 00296 if (ad_stop_rec(handle) < 0) 00297 return AD_ERR_GEN; 00298 } 00299 00300 close(handle->dspFD); 00301 free(handle); 00302 00303 return (0); 00304 } 00305 00306 int32 00307 ad_start_rec(ad_rec_t * handle) 00308 { 00309 if (handle->dspFD < 0) 00310 return AD_ERR_NOT_OPEN; 00311 00312 if (handle->recording) 00313 return AD_ERR_GEN; 00314 00315 /* Sample rate, format, input mix settings, &c. are configured 00316 * with ioctl(2) calls under Linux. It makes more sense to handle 00317 * these at device open time and consider the state of the device 00318 * to be fixed until closed. 00319 */ 00320 00321 handle->recording = 1; 00322 00323 /* rkm@cs: This doesn't actually do anything. How do we turn recording on/off? */ 00324 00325 return (0); 00326 } 00327 00328 int32 00329 ad_stop_rec(ad_rec_t * handle) 00330 { 00331 if (handle->dspFD < 0) 00332 return AD_ERR_NOT_OPEN; 00333 00334 if (!handle->recording) 00335 return AD_ERR_GEN; 00336 00337 if (ioctl(handle->dspFD, SNDCTL_DSP_SYNC, 0) < 0) { 00338 fprintf(stderr, "Audio ioctl(SYNC) failed: %s\n", strerror(errno)); 00339 return AD_ERR_GEN; 00340 } 00341 00342 handle->recording = 0; 00343 00344 return (0); 00345 } 00346 00347 int32 00348 ad_read(ad_rec_t * handle, int16 * buf, int32 max) 00349 { 00350 int32 length; 00351 00352 length = max * handle->bps; /* #samples -> #bytes */ 00353 00354 if ((length = read(handle->dspFD, buf, length)) > 0) { 00355 #if 0 00356 if ((length % handle->bps) != 0) 00357 fprintf(stderr, 00358 "Audio read returned non-integral #sample bytes (%d)\n", 00359 length); 00360 #endif 00361 length /= handle->bps; 00362 } 00363 00364 if (length < 0) { 00365 if (errno != EAGAIN) { 00366 fprintf(stderr, "Audio read error"); 00367 return AD_ERR_GEN; 00368 } 00369 else { 00370 length = 0; 00371 } 00372 } 00373 00374 if ((length == 0) && (!handle->recording)) 00375 return AD_EOF; 00376 00377 return length; 00378 }