Initial import to Tizen
[profile/ivi/sphinxbase.git] / src / libsphinxad / 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 ad_rec_t *
79 ad_open_dev(const char *dev, int32 sps)
80 {
81     ad_rec_t *handle;
82     int32 dspFD, mixerFD;
83     int32 nonBlocking = 1, sourceMic = SOUND_MASK_MIC, inputGain =
84         INPUT_GAIN, devMask = 0;
85     int32 audioFormat = AUDIO_FORMAT;
86     int32 dspCaps = 0;
87     int32 sampleRate;
88     int32 numberChannels = 1;
89
90     sampleRate = sps;
91
92     if (dev == NULL)
93         dev = DEFAULT_DEVICE;
94
95     /* Used to have O_NDELAY. */
96     if ((dspFD = open(dev, O_RDONLY)) < 0) {
97         if (errno == EBUSY)
98             fprintf(stderr, "%s(%d): Audio device(%s) busy\n",
99                     __FILE__, __LINE__, dev);
100         else
101             fprintf(stderr,
102                     "%s(%d): Failed to open audio device(%s): %s\n",
103                     __FILE__, __LINE__, dev, strerror(errno));
104         return NULL;
105     }
106
107     if (ioctl(dspFD, SNDCTL_DSP_SYNC, 0) < 0) {
108         fprintf(stderr, "Audio ioctl(SYNC) failed: %s\n", strerror(errno));
109         close(dspFD);
110         return NULL;
111     }
112
113     if (ioctl(dspFD, SNDCTL_DSP_RESET, 0) < 0) {
114         fprintf(stderr, "Audio ioctl(RESET) failed: %s\n",
115                 strerror(errno));
116         close(dspFD);
117         return NULL;
118     }
119
120     if (ioctl(dspFD, SNDCTL_DSP_SETFMT, &audioFormat) < 0) {
121         fprintf(stderr, "Audio ioctl(SETFMT 0x%x) failed: %s\n",
122                 audioFormat, strerror(errno));
123         close(dspFD);
124         return NULL;
125     }
126     if (audioFormat != AUDIO_FORMAT) {
127         fprintf(stderr,
128                 "Audio ioctl(SETFMT): 0x%x, expected: 0x%x\n",
129                 audioFormat, AUDIO_FORMAT);
130         close(dspFD);
131         return NULL;
132     }
133
134     if (ioctl(dspFD, SNDCTL_DSP_SPEED, &sampleRate) < 0) {
135         fprintf(stderr, "Audio ioctl(SPEED %d) failed %s\n",
136                 sampleRate, strerror(errno));
137         close(dspFD);
138         return NULL;
139     }
140     if (sampleRate != sps) {
141         if (abs(sampleRate - sps) <= (sampleRate * SAMPLERATE_TOLERANCE)) {
142             fprintf(stderr,
143                     "Audio ioctl(SPEED) not perfect, but is acceptable. "
144                     "(Wanted %d, but got %d)\n", sampleRate, sps);
145         }
146         else {
147             fprintf(stderr,
148                     "Audio ioctl(SPEED): %d, expected: %d\n",
149                     sampleRate, sps);
150             close(dspFD);
151             return NULL;
152         }
153     }
154
155     if (ioctl(dspFD, SNDCTL_DSP_CHANNELS, &numberChannels) < 0) {
156         fprintf(stderr, "Audio ioctl(CHANNELS %d) failed %s\n",
157                 numberChannels, strerror(errno));
158         close(dspFD);
159         return NULL;
160     }
161
162     if (ioctl(dspFD, SNDCTL_DSP_NONBLOCK, &nonBlocking) < 0) {
163         fprintf(stderr, "ioctl(NONBLOCK) failed: %s\n", strerror(errno));
164         close(dspFD);
165         return NULL;
166     }
167
168     if (ioctl(dspFD, SNDCTL_DSP_GETCAPS, &dspCaps) < 0) {
169         fprintf(stderr, "ioctl(GETCAPS) failed: %s\n", strerror(errno));
170         close(dspFD);
171         return NULL;
172     }
173 #if 0
174     printf("DSP Revision %d:\n", dspCaps & DSP_CAP_REVISION);
175     printf("DSP %s duplex capability.\n",
176            (dspCaps & DSP_CAP_DUPLEX) ? "has" : "does not have");
177     printf("DSP %s real time capability.\n",
178            (dspCaps & DSP_CAP_REALTIME) ? "has" : "does not have");
179     printf("DSP %s batch capability.\n",
180            (dspCaps & DSP_CAP_BATCH) ? "has" : "does not have");
181     printf("DSP %s coprocessor capability.\n",
182            (dspCaps & DSP_CAP_COPROC) ? "has" : "does not have");
183     printf("DSP %s trigger capability.\n",
184            (dspCaps & DSP_CAP_TRIGGER) ? "has" : "does not have");
185     printf("DSP %s memory map capability.\n",
186            (dspCaps & DSP_CAP_MMAP) ? "has" : "does not have");
187 #endif
188
189     if ((dspCaps & DSP_CAP_DUPLEX)
190         && (ioctl(dspFD, SNDCTL_DSP_SETDUPLEX, 0) < 0))
191         fprintf(stderr, "ioctl(SETDUPLEX) failed: %s\n", strerror(errno));
192
193     /* Patched by N. Roy (nickr@ri.cmu.edu), 99/7/23. 
194        Previously, mixer was set through dspFD. This is incorrect. Should
195        be set through mixerFD, /dev/mixer. 
196        Also, only the left channel volume was being set.
197      */
198
199     if ((mixerFD = open("/dev/mixer", O_RDONLY)) < 0) {
200         if (errno == EBUSY) {
201             fprintf(stderr, "%s %d: mixer device busy.\n",
202                     __FILE__, __LINE__);
203             fprintf(stderr, "%s %d: Using current setting.\n",
204                     __FILE__, __LINE__);
205         }
206         else {
207             fprintf(stderr, "%s %d: %s\n", __FILE__, __LINE__,
208                     strerror(errno));
209             exit(1);
210         }
211     }
212
213     if (mixerFD >= 0) {
214         if (ioctl(mixerFD, SOUND_MIXER_WRITE_RECSRC, &sourceMic) < 0) {
215             if (errno == ENXIO)
216                 fprintf(stderr,
217                         "%s %d: can't set mic source for this device.\n",
218                         __FILE__, __LINE__);
219             else {
220                 fprintf(stderr,
221                         "%s %d: mixer set to mic: %s\n",
222                         __FILE__, __LINE__, strerror(errno));
223                 exit(1);
224             }
225         }
226
227         /* Set the same gain for left and right channels. */
228         inputGain = inputGain << 8 | inputGain;
229
230         /* Some OSS devices have no input gain control, but do have a
231            recording level control.  Find out if this is one of them and
232            adjust accordingly. */
233         if (ioctl(mixerFD, SOUND_MIXER_READ_DEVMASK, &devMask) < 0) {
234             fprintf(stderr,
235                     "%s %d: failed to read device mask: %s\n",
236                     __FILE__, __LINE__, strerror(errno));
237             exit(1);            /* FIXME: not a well-behaved-library thing to do! */
238         }
239         if (devMask & SOUND_MASK_IGAIN) {
240             if (ioctl(mixerFD, SOUND_MIXER_WRITE_IGAIN, &inputGain) < 0) {
241                 fprintf(stderr,
242                         "%s %d: mixer input gain to %d: %s\n",
243                         __FILE__, __LINE__, inputGain, strerror(errno));
244                 exit(1);
245             }
246         }
247         else if (devMask & SOUND_MASK_RECLEV) {
248             if (ioctl(mixerFD, SOUND_MIXER_WRITE_RECLEV, &inputGain) < 0) {
249                 fprintf(stderr,
250                         "%s %d: mixer record level to %d: %s\n",
251                         __FILE__, __LINE__, inputGain, strerror(errno));
252                 exit(1);
253             }
254         }
255         else {
256             fprintf(stderr,
257                     "%s %d: can't set input gain/recording level for this device.\n",
258                     __FILE__, __LINE__);
259         }
260
261         close(mixerFD);
262     }
263
264     if ((handle = (ad_rec_t *) calloc(1, sizeof(ad_rec_t))) == NULL) {
265         fprintf(stderr, "calloc(%ld) failed\n", sizeof(ad_rec_t));
266         abort();
267     }
268
269     handle->dspFD = dspFD;
270     handle->recording = 0;
271     handle->sps = sps;
272     handle->bps = sizeof(int16);
273
274     return (handle);
275 }
276
277 ad_rec_t *
278 ad_open_sps(int32 sps)
279 {
280     return ad_open_dev(DEFAULT_DEVICE, sps);
281 }
282
283 ad_rec_t *
284 ad_open(void)
285 {
286     return ad_open_sps(DEFAULT_SAMPLES_PER_SEC);
287 }
288
289 int32
290 ad_close(ad_rec_t * handle)
291 {
292     if (handle->dspFD < 0)
293         return AD_ERR_NOT_OPEN;
294
295     if (handle->recording) {
296         if (ad_stop_rec(handle) < 0)
297             return AD_ERR_GEN;
298     }
299
300     close(handle->dspFD);
301     free(handle);
302
303     return (0);
304 }
305
306 int32
307 ad_start_rec(ad_rec_t * handle)
308 {
309     if (handle->dspFD < 0)
310         return AD_ERR_NOT_OPEN;
311
312     if (handle->recording)
313         return AD_ERR_GEN;
314
315     /* Sample rate, format, input mix settings, &c. are configured
316      * with ioctl(2) calls under Linux. It makes more sense to handle
317      * these at device open time and consider the state of the device
318      * to be fixed until closed.
319      */
320
321     handle->recording = 1;
322
323     /* rkm@cs: This doesn't actually do anything.  How do we turn recording on/off? */
324
325     return (0);
326 }
327
328 int32
329 ad_stop_rec(ad_rec_t * handle)
330 {
331     if (handle->dspFD < 0)
332         return AD_ERR_NOT_OPEN;
333
334     if (!handle->recording)
335         return AD_ERR_GEN;
336
337     if (ioctl(handle->dspFD, SNDCTL_DSP_SYNC, 0) < 0) {
338         fprintf(stderr, "Audio ioctl(SYNC) failed: %s\n", strerror(errno));
339         return AD_ERR_GEN;
340     }
341
342     handle->recording = 0;
343
344     return (0);
345 }
346
347 int32
348 ad_read(ad_rec_t * handle, int16 * buf, int32 max)
349 {
350     int32 length;
351
352     length = max * handle->bps; /* #samples -> #bytes */
353
354     if ((length = read(handle->dspFD, buf, length)) > 0) {
355 #if 0
356         if ((length % handle->bps) != 0)
357             fprintf(stderr,
358                     "Audio read returned non-integral #sample bytes (%d)\n",
359                     length);
360 #endif
361         length /= handle->bps;
362     }
363
364     if (length < 0) {
365         if (errno != EAGAIN) {
366             fprintf(stderr, "Audio read error");
367             return AD_ERR_GEN;
368         }
369         else {
370             length = 0;
371         }
372     }
373
374     if ((length == 0) && (!handle->recording))
375         return AD_EOF;
376
377     return length;
378 }