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
83struct ad_rec_s {
84 int32 dspFD; /* Audio device descriptor */
85 int32 recording;
86 int32 sps; /* Samples/sec */
87 int32 bps; /* Bytes/sample */
88};
89
91ad_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
289ad_rec_t *
290ad_open_sps(int32 sps)
291{
292 return ad_open_dev(DEFAULT_DEVICE, sps);
293}
294
295ad_rec_t *
296ad_open(void)
297{
298 return ad_open_sps(DEFAULT_SAMPLES_PER_SEC);
299}
300
301int32
302ad_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
318int32
319ad_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
340int32
341ad_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
359int32
360ad_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}
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.