root/rat/trunk/auddev_oss.c @ 2103

Revision 2103, 17.2 KB (checked in by ucacoxh, 16 years ago)

- First steps towards transparent format conversion in audio device

interface. Audio open now takes input and output formats, device is
opened in greatest common denominator format. Everything except the
post read/write conversion implemented.

- Audio devices need to be changed to accept mulaw if requested by

audio interface and not do anything smart as we now do this in
generic audio interface code. This will save on duplication of effort.

- Renamed some of the audio_format entries num_channels now channels, and

blocksize is now bytes_per_block. [oh]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * FILE:    auddev_oss.c - Open Sound System audio device driver
3 * PROGRAM: RAT
4 * AUTHOR:  Colin Perkins
5 * MODS:    Orion Hodson
6 *
7 * From revision 1.25 of auddev_linux.c
8 *
9 * $Revision$
10 * $Date$
11 *
12 * Copyright (c) 1996-98 University College London
13 * All rights reserved.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, is permitted,` for non-commercial use only, provided
17 * that the following conditions are met:
18 * 1. Redistributions of source code must retain the above copyright
19 *    notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 *    notice, this list of conditions and the following disclaimer in the
22 *    documentation and/or other materials provided with the distribution.
23 * 3. All advertising materials mentioning features or use of this software
24 *    must display the following acknowledgement:
25 *      This product includes software developed by the Computer Science
26 *      Department at University College London
27 * 4. Neither the name of the University nor of the Department may be used
28 *    to endorse or promote products derived from this software without
29 *    specific prior written permission.
30 * Use of this software for commercial purposes is explicitly forbidden
31 * unless prior written permission is obtained from the authors.
32 *
33 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
34 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * SUCH DAMAGE.
44 */
45
46#if defined(OSS)||defined(Linux)
47
48#include "config_unix.h"
49#include "config_win32.h"
50#include "assert.h"
51#include "debug.h"
52#include "memory.h"
53#include "audio_types.h"
54#include "auddev_oss.h"
55
56int     can_read  = FALSE;
57int     can_write = FALSE;
58int     iport     = AUDIO_MICROPHONE;
59int     is_duplex = FALSE;
60int     done_test = FALSE;
61int     bytes_per_block;
62
63/* Magic to get device names from OSS */
64
65#define OSS_MAX_DEVICES 3
66#define OSS_MAX_NAME_LEN 32
67static int ndev;
68char   dev_name[OSS_MAX_DEVICES][OSS_MAX_NAME_LEN];
69
70static char the_dev[] = "/dev/audioX";
71static int audio_fd = -1;
72
73audio_format format;
74
75#define bat_to_device(x)  ((x) * 100 / MAX_AMP)
76#define device_to_bat(x)  ((x) * MAX_AMP / 100)
77
78static int 
79oss_audio_open_rw(audio_desc_t ad, char rw)
80{
81        int  mode     = AFMT_S16_LE;                    /* 16bit linear, little-endian */
82        int  stereo   = format.channels - 1;    /* 0=mono, 1=stereo            */
83        int  speed    = format.sample_rate;
84        int  volume   = (100<<8)|100;
85        int  frag     = 0x7fff0000;                     /* unlimited number of fragments */
86        int  reclb    = 0;
87        char buffer[128];                               /* sigh. */
88
89        /* Calculate the size of the fragments we want... 20ms worth of audio data... */
90        bytes_per_block = DEVICE_BUF_UNIT * (format.sample_rate / 8000) * (format.bits_per_sample / 8);
91        /* Round to the nearest legal frag size (next power of two lower...) */
92        frag |= (int) (log(bytes_per_block)/log(2));
93        debug_msg("frag=%x bytes_per_block=%d\n", frag, bytes_per_block);
94
95        switch (rw) {
96        case O_RDONLY:
97                can_read  = TRUE;
98                can_write = FALSE;
99                break;
100        case O_WRONLY:
101                can_read  = FALSE;
102                can_write = TRUE;
103                break;
104        case O_RDWR  :
105                can_read  = TRUE;
106                can_write = TRUE;
107                break;
108        default :
109                printf("Unknown r/w mode!\n");
110                abort();
111        }
112
113        audio_fd = open(the_dev, rw);
114        if (audio_fd > 0) {
115                /* Note: The order in which the device is set up is important! Don't */
116                /*       reorder this code unless you really know what you're doing! */
117                if ((rw == O_RDWR) && ioctl(audio_fd, SNDCTL_DSP_SETDUPLEX, 0) == -1) {
118                        printf("ERROR: Cannot enable full-duplex mode!\n");
119                        abort();
120                }
121                if ((ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag) == -1)) {
122                        printf("ERROR: Cannot set the fragement size\n");
123                        abort();
124                }
125                if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &mode) == -1) || (mode != AFMT_S16_LE)) {
126                        printf("ERROR: Audio device doesn't support 16bit linear format!\n");
127                        abort();
128                }
129                if ((ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == -1) || (stereo != (format.channels - 1))) {
130                        printf("ERROR: Audio device doesn't support %d channels!\n", format.channels);
131                        abort();
132                }
133                if ((ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed) == -1) || (speed != format.sample_rate)) {
134                        printf("ERROR: Audio device doesn't support %d sampling rate!\n", format.sample_rate);
135                        abort();
136                }
137                /* Set global gain/volume to maximum values. This may fail on */
138                /* some cards, but shouldn't cause any harm when it does..... */ 
139                ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_VOLUME), &volume);
140                ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_RECLEV), &volume);
141                /* Select microphone input. We can't select output source...  */
142                oss_audio_set_iport(audio_fd, iport);
143                /* Turn off loopback from input to output... This only works  */
144                /* on a few cards, but shouldn't cause problems on the others */
145                ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_IMIX), &reclb);
146                /* Device driver bug: we must read some data before the ioctl */
147                /* to tell us how much data is waiting works....              */
148                read(audio_fd, buffer, 128);   
149                /* Okay, now we're done...                                    */
150                UNUSED(ad);
151                return audio_fd;
152        } else {
153                close(audio_fd);
154                can_read  = FALSE;
155                can_write = FALSE;
156                return -1;
157        }
158}
159
160/* Try to open the audio device.              */
161/* Return TRUE if successful FALSE otherwise. */
162int
163oss_audio_open(audio_desc_t ad, audio_format *fmt)
164{
165        int mode;
166
167        UNUSED(ad);
168
169        memcpy(&format, fmt, sizeof(audio_format));
170
171        if (ad <0 || ad>OSS_MAX_DEVICES) {
172                debug_msg("Invalid audio descriptor (%d)", ad);
173                return FALSE;
174        }
175
176        sprintf(the_dev, "/dev/audio%d", ad);
177
178        if (oss_audio_duplex(-1)) {
179                mode = O_RDWR;
180        } else {
181                perror("Half duplex cards not supported\n");
182                exit(-1);
183                mode = O_WRONLY;
184        }
185        audio_fd = oss_audio_open_rw(ad, mode);
186        return (audio_fd > 0) ? TRUE : FALSE;
187}
188
189/* Close the audio device */
190void
191oss_audio_close(audio_desc_t ad)
192{
193        UNUSED(ad); assert(audio_fd > 0);
194        oss_audirain(audio_fd);
195        close(audio_fd);
196        audio_fd = -1;
197}
198
199/* Flush input buffer */
200void
201oss_audio_drain(audio_desc_t ad)
202{
203        sample buf[160];
204
205        UNUSED(ad); assert(audio_fd > 0);
206
207        while(oss_audio_read(audio_fd, buf, 160) == 160);
208}
209
210int
211oss_audio_duplex(audio_desc_t ad)
212{
213        /* Find out if the device supports full-duplex operation. The device must
214         * be open to do this, so if we're passed -1 as a file-descriptor we open
215         * the device, do the ioctl, and then close it again...
216         */
217        int info;
218        int did_open = FALSE;
219
220        UNUSED(ad);
221
222        if (done_test) return is_duplex;
223
224        if (ad == -1) {
225                audio_fd = open(the_dev, O_RDWR | O_NDELAY);
226                did_open = TRUE;
227        }
228
229        if (ioctl(audio_fd, SNDCTL_DSP_SETDUPLEX, 0) == -1) {
230                if (did_open) close(audio_fd);
231                is_duplex = FALSE;
232                done_test = TRUE;
233                return FALSE;
234        }
235        if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &info) == -1) {
236                if (did_open) close(audio_fd);
237                is_duplex = FALSE;
238                done_test = TRUE;
239                return FALSE;
240        }
241
242        if (did_open) {
243                close(audio_fd);
244        }
245
246        is_duplex = info & DSP_CAP_DUPLEX;
247        done_test = TRUE;
248        debug_msg("%s duplex audio\n", is_duplex?"Full":"Half");
249        return is_duplex;
250}
251
252/* Gain and volume values are in the range 0 - MAX_AMP */
253void
254oss_audio_set_gain(audio_desc_t ad, int gain)
255{
256        int volume = bat_to_device(gain) << 8 | bat_to_device(gain);
257
258        UNUSED(ad); assert(audio_fd > 0);
259
260        switch (iport) {
261        case AUDIO_MICROPHONE :
262                if (ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_MIC), &volume) == -1) {
263                        perror("Setting gain");
264                }
265                return;
266        case AUDIO_LINE_IN :
267                if (ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_LINE), &volume) == -1) {
268                        perror("Setting gain");
269                }
270                return;
271        case AUDIO_CD:
272                if (ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_CD), &volume) < 0) {
273                        perror("Setting gain");
274                }
275                return;
276        }
277        printf("ERROR: Unknown iport in audio_set_gain!\n");
278        abort();
279}
280
281int
282oss_audio_get_gain(audio_desc_t ad)
283{
284        int volume;
285
286        UNUSED(ad); assert(audio_fd > 0);
287
288        switch (iport) {
289        case AUDIO_MICROPHONE :
290                if (ioctl(audio_fd, MIXER_READ(SOUND_MIXER_MIC), &volume) == -1) {
291                        perror("Getting gain");
292                }
293                break;
294        case AUDIO_LINE_IN :
295                if (ioctl(audio_fd, MIXER_READ(SOUND_MIXER_LINE), &volume) == -1) {
296                        perror("Getting gain");
297                }
298                break;
299        case AUDIO_CD:
300                if (ioctl(audio_fd, MIXER_READ(SOUND_MIXER_CD), &volume) < 0) {
301                        perror("Getting gain");
302                }
303                break;
304        default :
305                printf("ERROR: Unknown iport in audio_set_gain!\n");
306                abort();
307        }
308        return device_to_bat(volume & 0xff);
309}
310
311void
312oss_audio_set_volume(audio_desc_t ad, int vol)
313{
314        int volume;
315
316        UNUSED(ad); assert(audio_fd > 0);
317
318        volume = vol << 8 | vol;
319        if (ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1) {
320                perror("Setting volume");
321        }
322}
323
324void
325oss_audio_loopback(audio_desc_t ad, int gain)
326{
327        UNUSED(ad); assert(audio_fd > 0);
328
329        gain = gain << 8 | gain;
330        if (ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_IMIX), &gain) == -1) {
331                perror("loopback");
332        }
333}
334
335int
336oss_audio_get_volume(audio_desc_t ad)
337{
338        int volume;
339
340        UNUSED(ad); assert(audio_fd > 0);
341
342        if (ioctl(audio_fd, MIXER_READ(SOUND_MIXER_PCM), &volume) == -1) {
343                perror("Getting volume");
344        }
345        return device_to_bat(volume & 0x000000ff); /* Extract left channel volume */
346}
347
348int
349oss_audio_read(audio_desc_t ad, sample *buf, int samples)
350{
351        UNUSED(ad); assert(audio_fd > 0);
352
353        if (can_read) {
354                int            len, read_len;
355                audio_buf_info info;
356
357                /* Figure out how many bytes we can read before blocking... */
358                ioctl(audio_fd, SNDCTL_DSP_GETISPACE, &info);
359                if (info.bytes > (int) (samples * BYTES_PER_SAMPLE)) {
360                        read_len = (samples * BYTES_PER_SAMPLE);
361                } else {
362                        read_len = info.bytes;
363                }
364                if ((len = read(audio_fd, (char *)buf, read_len)) < 0) {
365                        perror("audio_read");
366                        return 0;
367                }
368                return len / BYTES_PER_SAMPLE;
369        } else {
370                /* The value returned should indicate the time (in audio samples) */
371                /* since the last time read was called.                           */
372                int                   i;
373                int                   diff;
374                static struct timeval last_time;
375                static struct timeval curr_time;
376                static int            first_time = 0;
377
378                if (first_time == 0) {
379                        gettimeofday(&last_time, NULL);
380                        first_time = 1;
381                }
382                gettimeofday(&curr_time, NULL);
383                diff = (((curr_time.tv_sec - last_time.tv_sec) * 1e6) + (curr_time.tv_usec - last_time.tv_usec)) / 125;
384                if (diff > samples) diff = samples;
385                if (diff <      80) diff = 80;
386                xmemchk();
387                for (i=0; i<diff; i++) {
388                        buf[i] = L16_AUDIO_ZERO;
389                }
390                xmemchk();
391                last_time = curr_time;
392                return diff;
393        }
394}
395
396int
397oss_audio_write(audio_desc_t ad, sample *buf, int samples)
398{
399        UNUSED(ad); assert(audio_fd > 0);
400       
401        if (can_write) {
402                int              done, len;
403                char            *p;
404
405                p   = (char *) buf;
406                len = samples * BYTES_PER_SAMPLE;
407                while (1) {
408                        if ((done = write(audio_fd, p, len)) = len) {
409                                break;
410                        }
411                        if (errno != EINTR) {
412                                perror("Error writing device");
413                                return samples - ((len - done) / BYTES_PER_SAMPLE);
414                        }
415                        len -= done;
416                        p   += done;
417                }
418                return samples;
419        } else {
420                return samples;
421        }
422}
423
424/* Set ops on audio device to be non-blocking */
425void
426oss_audio_non_block(audio_desc_t ad)
427{
428        int  on = 1;
429
430        UNUSED(ad); assert(audio_fd > 0);
431
432        if (ioctl(audio_fd, FIONBIO, (char *)&on) < 0) {
433                debug_msg("Failed to set non-blocking mode on audio device!\n");
434        }
435}
436
437/* Set ops on audio device to block */
438void
439oss_audio_block(audio_desc_t ad)
440{
441        int  on = 0;
442
443        UNUSED(ad); assert(audio_fd > 0);
444
445        if (ioctl(audio_fd, FIONBIO, (char *)&on) < 0) {
446                debug_msg("Failed to set blocking mode on audio device!\n");
447        }
448}
449
450void
451oss_audio_set_oport(audio_desc_t ad, int port)
452{
453        /* There appears to be no-way to select this with OSS... */
454        UNUSED(ad); assert(audio_fd > 0);
455        UNUSED(port);
456        return;
457}
458
459int
460oss_audio_get_oport(audio_desc_t ad)
461{
462        /* There appears to be no-way to select this with OSS... */
463        UNUSED(ad); assert(audio_fd > 0);
464        return AUDIO_HEADPHONE;
465}
466
467int
468oss_audio_next_oport(audio_desc_t ad)
469{
470        /* There appears to be no-way to select this with OSS... */
471        UNUSED(ad); assert(audio_fd > 0);
472        return AUDIO_HEADPHONE;
473}
474
475void
476oss_audio_set_iport(audio_desc_t ad, int port)
477{
478        int recmask;
479        int recsrc;
480        int gain;
481
482        UNUSED(ad); assert(audio_fd > 0);
483
484        if (ioctl(audio_fd, MIXER_READ(SOUND_MIXER_RECMASK), &recmask) == -1) {
485                debug_msg("WARNING: Unable to read recording mask!\n");
486                return;
487        }
488        switch (port) {
489        case AUDIO_MICROPHONE :
490                if (recmask & SOUND_MASK_MIC) {
491                        recsrc = SOUND_MASK_MIC;
492                        if ((ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_RECSRC), &recsrc) == -1) && !(recsrc & SOUND_MASK_MIC)) {
493                                debug_msg("WARNING: Unable to select recording source!\n");
494                                return;
495                        }
496                        gain = oss_audio_get_gain(audio_fd);
497                        iport = port;
498                        oss_audio_set_gain(audio_fd, gain);
499                } else {
500                        debug_msg("Audio device doesn't support recording from microphone\n");
501                }
502                break;
503        case AUDIO_LINE_IN :
504                if (recmask & SOUND_MASK_LINE) {
505                        recsrc = SOUND_MASK_LINE;
506                        if ((ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_RECSRC), &recsrc) == -1) && !(recsrc & SOUND_MASK_LINE)){
507                                debug_msg("WARNING: Unable to select recording source!\n");
508                                return;
509                        }
510                        gain = oss_audio_get_gain(audio_fd);
511                        iport = port;
512                        oss_audio_set_gain(audio_fd, gain);
513                } else {
514                        debug_msg("Audio device doesn't support recording from line-input\n");
515                }
516                break;
517        case AUDIO_CD:
518                if (recmask & SOUND_MASK_CD) {
519                        recsrc = SOUND_MASK_CD;
520                        if ((ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_RECSRC), &recsrc) == -1) && !(recsrc & SOUND_MASK_LINE)){
521                                debug_msg("WARNING: Unable to select recording source!\n");
522                                return;
523                        }
524                        gain = oss_audio_get_gain(audio_fd);
525                        iport = port;
526                        oss_audio_set_gain(audio_fd, gain);
527                } else {
528                        debug_msg("Audio device doesn't support recording from CD\n");
529                }
530                break;
531        default :
532                debug_msg("audio_set_port: unknown port!\n");
533                abort();
534        };
535        return;
536}
537
538int
539oss_audio_get_iport(audio_desc_t ad)
540{
541        UNUSED(ad); assert(audio_fd > 0);
542        return iport;
543}
544
545int
546oss_audio_next_iport(audio_desc_t ad)
547{
548        UNUSED(ad); assert(audio_fd > 0);
549
550        switch (iport) {
551        case AUDIO_MICROPHONE :
552                oss_audio_set_iport(audio_fd, AUDIO_LINE_IN);
553                break;
554        case AUDIO_LINE_IN :
555                oss_audio_set_iport(audio_fd, AUDIO_CD);
556                break;
557        case AUDIO_CD :
558                oss_audio_set_iport(audio_fd, AUDIO_MICROPHONE);
559                break;
560        default :
561                debug_msg("Unknown audio source!\n");
562        }
563
564        return iport;
565}
566
567int
568oss_audio_get_bytes_per_block(audio_desc_t ad)
569{
570        UNUSED(ad); assert(audio_fd > 0);
571        return bytes_per_block;
572}
573
574int
575oss_audio_get_channels(audio_desc_t ad)
576{
577        UNUSED(ad); assert(audio_fd > 0);
578        return format.channels;
579}
580
581int
582oss_audio_get_freq(audio_desc_t ad)
583{
584        UNUSED(ad); assert(audio_fd > 0);
585        return format.sample_rate;
586}
587
588static int
589oss_audio_select(audio_desc_t ad, int delay_us)
590{
591        fd_set rfds;
592        struct timeval tv;
593
594        UNUSED(ad); assert(audio_fd > 0);
595       
596        tv.tv_sec = 0;
597        tv.tv_usec = delay_us;
598
599        FD_ZERO(&rfds);
600        FD_SET(audio_fd, &rfds);
601
602        select(audio_fd+1, &rfds, NULL, NULL, &tv);
603
604        return FD_ISSET(audio_fd, &rfds);
605}
606
607void
608oss_audio_wait_for(audio_desc_t ad, int delay_ms)
609{
610        oss_audio_select(ad, delay_ms * 1000);
611}
612
613int 
614oss_audio_is_ready(audio_desc_t ad)
615{
616        return oss_audio_select(ad, 0);
617}
618
619void
620oss_audio_query_devices()
621{
622        FILE *f;
623        char buf[OSS_MAX_NAME_LEN], *name_start;
624        int  found_devices = FALSE;
625       
626        char devices_tag[] = "Audio devices:";
627        int len = strlen(devices_tag);
628
629        f = fopen("/dev/sndstat", "r");
630       
631        if (f) {
632                while(!feof(f)) {
633                        fgets(buf, OSS_MAX_NAME_LEN, f);
634
635                        if (!strncmp(buf, devices_tag, len)) {
636                                found_devices = TRUE;
637                                debug_msg("Found devices entry\n");
638                                continue;
639                        }
640                        if (found_devices) {
641                                if ((name_start = strstr(buf, ":")) && ndev < OSS_MAX_DEVICES) {
642                                        name_start += 2; /* pass colon plus space */
643                                        strcpy(dev_name[ndev], name_start);
644                                        ndev++;
645                                        debug_msg("OSS device found (%s)\n", name_start);
646                                } else {
647                                        break;
648                                }
649                        }
650                }
651                fclose(f);
652        }
653}
654
655int
656oss_get_device_count()
657{
658        return ndev;
659}
660
661char *
662oss_get_device_name(int idx)
663{
664        if (idx >=0 && idx < ndev) {
665                return dev_name[idx];
666        }
667        debug_msg("Invalid index\n");
668        return NULL;
669}
670
671
672#endif /* OSS */
Note: See TracBrowser for help on using the browser.