SphinxBase  5prealpha
ad_oss.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 /* Sphinx II libad (Linux)
38  * ^^^^^^^^^^^^^^^^^^^^^^^
39  * $Id: ad_oss.c,v 1.9 2004/07/16 00:57:12 egouvea Exp $
40  *
41  * John G. Dorsey (jd5q+@andrew.cmu.edu)
42  * Engineering Design Research Center
43  * Carnegie Mellon University
44  * ********************************************************************
45  *
46  * REVISION HISTORY
47  *
48  * 09-Aug-1999 Kevin Lenzo (lenzo@cs.cmu.edu) at Cernegie Mellon University.
49  * Incorporated nickr@cs.cmu.edu's changes (marked below) and
50  * SPS_EPSILON to allow for sample rates that are "close enough".
51  *
52  * 15-Jun-1999 M. K. Ravishankar (rkm@cs.cmu.edu) Consolidated all ad functions into
53  * this one file. Added ad_open_sps().
54  * Other cosmetic changes for consistency (e.g., use of err.h).
55  *
56  * 18-May-1999 Kevin Lenzo (lenzo@cs.cmu.edu) added <errno.h>.
57  */
58 
59 #include <fcntl.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <sys/soundcard.h>
64 #include <sys/ioctl.h>
65 #include <errno.h>
66 #include <unistd.h>
67 #include <config.h>
68 
69 #include "prim_type.h"
70 #include "ad.h"
71 
72 #define AUDIO_FORMAT AFMT_S16_LE /* 16-bit signed, little endian */
73 #define INPUT_GAIN (80)
74 
75 #define SPS_EPSILON 200
76 #define SAMPLERATE_TOLERANCE 0.01
77 
78 #define DEFAULT_DEVICE "/dev/dsp"
79 
83 struct ad_rec_s {
84  int32 dspFD; /* Audio device descriptor */
85  int32 recording;
86  int32 sps; /* Samples/sec */
87  int32 bps; /* Bytes/sample */
88 };
89 
90 ad_rec_t *
91 ad_open_dev(const char *dev, int32 sps)
92 {
93  ad_rec_t *handle;
94  int32 dspFD, mixerFD;
95  int32 nonBlocking = 1, sourceMic = SOUND_MASK_MIC, inputGain =
96  INPUT_GAIN, devMask = 0;
97  int32 audioFormat = AUDIO_FORMAT;
98  int32 dspCaps = 0;
99  int32 sampleRate;
100  int32 numberChannels = 1;
101 
102  sampleRate = sps;
103 
104  if (dev == NULL)
105  dev = DEFAULT_DEVICE;
106 
107  /* Used to have O_NDELAY. */
108  if ((dspFD = open(dev, O_RDONLY)) < 0) {
109  if (errno == EBUSY)
110  fprintf(stderr, "%s(%d): Audio device(%s) busy\n",
111  __FILE__, __LINE__, dev);
112  else
113  fprintf(stderr,
114  "%s(%d): Failed to open audio device(%s): %s\n",
115  __FILE__, __LINE__, dev, strerror(errno));
116  return NULL;
117  }
118 
119  if (ioctl(dspFD, SNDCTL_DSP_SYNC, 0) < 0) {
120  fprintf(stderr, "Audio ioctl(SYNC) failed: %s\n", strerror(errno));
121  close(dspFD);
122  return NULL;
123  }
124 
125  if (ioctl(dspFD, SNDCTL_DSP_RESET, 0) < 0) {
126  fprintf(stderr, "Audio ioctl(RESET) failed: %s\n",
127  strerror(errno));
128  close(dspFD);
129  return NULL;
130  }
131 
132  if (ioctl(dspFD, SNDCTL_DSP_SETFMT, &audioFormat) < 0) {
133  fprintf(stderr, "Audio ioctl(SETFMT 0x%x) failed: %s\n",
134  audioFormat, strerror(errno));
135  close(dspFD);
136  return NULL;
137  }
138  if (audioFormat != AUDIO_FORMAT) {
139  fprintf(stderr,
140  "Audio ioctl(SETFMT): 0x%x, expected: 0x%x\n",
141  audioFormat, AUDIO_FORMAT);
142  close(dspFD);
143  return NULL;
144  }
145 
146  if (ioctl(dspFD, SNDCTL_DSP_SPEED, &sampleRate) < 0) {
147  fprintf(stderr, "Audio ioctl(SPEED %d) failed %s\n",
148  sampleRate, strerror(errno));
149  close(dspFD);
150  return NULL;
151  }
152  if (sampleRate != sps) {
153  if (abs(sampleRate - sps) <= (sampleRate * SAMPLERATE_TOLERANCE)) {
154  fprintf(stderr,
155  "Audio ioctl(SPEED) not perfect, but is acceptable. "
156  "(Wanted %d, but got %d)\n", sampleRate, sps);
157  }
158  else {
159  fprintf(stderr,
160  "Audio ioctl(SPEED): %d, expected: %d\n",
161  sampleRate, sps);
162  close(dspFD);
163  return NULL;
164  }
165  }
166 
167  if (ioctl(dspFD, SNDCTL_DSP_CHANNELS, &numberChannels) < 0) {
168  fprintf(stderr, "Audio ioctl(CHANNELS %d) failed %s\n",
169  numberChannels, strerror(errno));
170  close(dspFD);
171  return NULL;
172  }
173 
174  if (ioctl(dspFD, SNDCTL_DSP_NONBLOCK, &nonBlocking) < 0) {
175  fprintf(stderr, "ioctl(NONBLOCK) failed: %s\n", strerror(errno));
176  close(dspFD);
177  return NULL;
178  }
179 
180  if (ioctl(dspFD, SNDCTL_DSP_GETCAPS, &dspCaps) < 0) {
181  fprintf(stderr, "ioctl(GETCAPS) failed: %s\n", strerror(errno));
182  close(dspFD);
183  return NULL;
184  }
185 #if 0
186  printf("DSP Revision %d:\n", dspCaps & DSP_CAP_REVISION);
187  printf("DSP %s duplex capability.\n",
188  (dspCaps & DSP_CAP_DUPLEX) ? "has" : "does not have");
189  printf("DSP %s real time capability.\n",
190  (dspCaps & DSP_CAP_REALTIME) ? "has" : "does not have");
191  printf("DSP %s batch capability.\n",
192  (dspCaps & DSP_CAP_BATCH) ? "has" : "does not have");
193  printf("DSP %s coprocessor capability.\n",
194  (dspCaps & DSP_CAP_COPROC) ? "has" : "does not have");
195  printf("DSP %s trigger capability.\n",
196  (dspCaps & DSP_CAP_TRIGGER) ? "has" : "does not have");
197  printf("DSP %s memory map capability.\n",
198  (dspCaps & DSP_CAP_MMAP) ? "has" : "does not have");
199 #endif
200 
201  if ((dspCaps & DSP_CAP_DUPLEX)
202  && (ioctl(dspFD, SNDCTL_DSP_SETDUPLEX, 0) < 0))
203  fprintf(stderr, "ioctl(SETDUPLEX) failed: %s\n", strerror(errno));
204 
205  /* Patched by N. Roy (nickr@ri.cmu.edu), 99/7/23.
206  Previously, mixer was set through dspFD. This is incorrect. Should
207  be set through mixerFD, /dev/mixer.
208  Also, only the left channel volume was being set.
209  */
210 
211  if ((mixerFD = open("/dev/mixer", O_RDONLY)) < 0) {
212  if (errno == EBUSY) {
213  fprintf(stderr, "%s %d: mixer device busy.\n",
214  __FILE__, __LINE__);
215  fprintf(stderr, "%s %d: Using current setting.\n",
216  __FILE__, __LINE__);
217  }
218  else {
219  fprintf(stderr, "%s %d: %s\n", __FILE__, __LINE__,
220  strerror(errno));
221  exit(1);
222  }
223  }
224 
225  if (mixerFD >= 0) {
226  if (ioctl(mixerFD, SOUND_MIXER_WRITE_RECSRC, &sourceMic) < 0) {
227  if (errno == ENXIO)
228  fprintf(stderr,
229  "%s %d: can't set mic source for this device.\n",
230  __FILE__, __LINE__);
231  else {
232  fprintf(stderr,
233  "%s %d: mixer set to mic: %s\n",
234  __FILE__, __LINE__, strerror(errno));
235  exit(1);
236  }
237  }
238 
239  /* Set the same gain for left and right channels. */
240  inputGain = inputGain << 8 | inputGain;
241 
242  /* Some OSS devices have no input gain control, but do have a
243  recording level control. Find out if this is one of them and
244  adjust accordingly. */
245  if (ioctl(mixerFD, SOUND_MIXER_READ_DEVMASK, &devMask) < 0) {
246  fprintf(stderr,
247  "%s %d: failed to read device mask: %s\n",
248  __FILE__, __LINE__, strerror(errno));
249  exit(1); /* FIXME: not a well-behaved-library thing to do! */
250  }
251  if (devMask & SOUND_MASK_IGAIN) {
252  if (ioctl(mixerFD, SOUND_MIXER_WRITE_IGAIN, &inputGain) < 0) {
253  fprintf(stderr,
254  "%s %d: mixer input gain to %d: %s\n",
255  __FILE__, __LINE__, inputGain, strerror(errno));
256  exit(1);
257  }
258  }
259  else if (devMask & SOUND_MASK_RECLEV) {
260  if (ioctl(mixerFD, SOUND_MIXER_WRITE_RECLEV, &inputGain) < 0) {
261  fprintf(stderr,
262  "%s %d: mixer record level to %d: %s\n",
263  __FILE__, __LINE__, inputGain, strerror(errno));
264  exit(1);
265  }
266  }
267  else {
268  fprintf(stderr,
269  "%s %d: can't set input gain/recording level for this device.\n",
270  __FILE__, __LINE__);
271  }
272 
273  close(mixerFD);
274  }
275 
276  if ((handle = (ad_rec_t *) calloc(1, sizeof(ad_rec_t))) == NULL) {
277  fprintf(stderr, "calloc(%ld) failed\n", sizeof(ad_rec_t));
278  abort();
279  }
280 
281  handle->dspFD = dspFD;
282  handle->recording = 0;
283  handle->sps = sps;
284  handle->bps = sizeof(int16);
285 
286  return (handle);
287 }
288 
289 ad_rec_t *
290 ad_open_sps(int32 sps)
291 {
292  return ad_open_dev(DEFAULT_DEVICE, sps);
293 }
294 
295 ad_rec_t *
296 ad_open(void)
297 {
298  return ad_open_sps(DEFAULT_SAMPLES_PER_SEC);
299 }
300 
301 int32
302 ad_close(ad_rec_t * handle)
303 {
304  if (handle->dspFD < 0)
305  return AD_ERR_NOT_OPEN;
306 
307  if (handle->recording) {
308  if (ad_stop_rec(handle) < 0)
309  return AD_ERR_GEN;
310  }
311 
312  close(handle->dspFD);
313  free(handle);
314 
315  return (0);
316 }
317 
318 int32
319 ad_start_rec(ad_rec_t * handle)
320 {
321  if (handle->dspFD < 0)
322  return AD_ERR_NOT_OPEN;
323 
324  if (handle->recording)
325  return AD_ERR_GEN;
326 
327  /* Sample rate, format, input mix settings, &c. are configured
328  * with ioctl(2) calls under Linux. It makes more sense to handle
329  * these at device open time and consider the state of the device
330  * to be fixed until closed.
331  */
332 
333  handle->recording = 1;
334 
335  /* rkm@cs: This doesn't actually do anything. How do we turn recording on/off? */
336 
337  return (0);
338 }
339 
340 int32
341 ad_stop_rec(ad_rec_t * handle)
342 {
343  if (handle->dspFD < 0)
344  return AD_ERR_NOT_OPEN;
345 
346  if (!handle->recording)
347  return AD_ERR_GEN;
348 
349  if (ioctl(handle->dspFD, SNDCTL_DSP_SYNC, 0) < 0) {
350  fprintf(stderr, "Audio ioctl(SYNC) failed: %s\n", strerror(errno));
351  return AD_ERR_GEN;
352  }
353 
354  handle->recording = 0;
355 
356  return (0);
357 }
358 
359 int32
360 ad_read(ad_rec_t * handle, int16 * buf, int32 max)
361 {
362  int32 length;
363 
364  length = max * handle->bps; /* #samples -> #bytes */
365 
366  if ((length = read(handle->dspFD, buf, length)) > 0) {
367 #if 0
368  if ((length % handle->bps) != 0)
369  fprintf(stderr,
370  "Audio read returned non-integral #sample bytes (%d)\n",
371  length);
372 #endif
373  length /= handle->bps;
374  }
375 
376  if (length < 0) {
377  if (errno != EAGAIN) {
378  fprintf(stderr, "Audio read error");
379  return AD_ERR_GEN;
380  }
381  else {
382  length = 0;
383  }
384  }
385 
386  if ((length == 0) && (!handle->recording))
387  return AD_EOF;
388 
389  return length;
390 }
Audio recording structure.
int32 bps
Bytes/sample.
Definition: ad_alsa.c:94
Basic type definitions used in Sphinx.
SPHINXBASE_EXPORT ad_rec_t * ad_open(void)
Open the default audio device.
Definition: ad_alsa.c:228
generic live audio interface for recording and playback
int32 sps
Samples/sec.
Definition: ad_alsa.c:93
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_sps(int32 samples_per_sec)
Open the default audio device with a given sampling rate.
Definition: ad_alsa.c:222
Audio recording structure.
Definition: ad_alsa.c:90