root/rat/trunk/auddev_oss.c @ 2237

Revision 2237, 17.7 KB (checked in by ucacoxh, 15 years ago)

Many changes here. The really important deal is the audio device
setup and reconfiguration code in audio.c. Pretty much a complete
rewrite. It works okay for changing device, everything is there, but
have not yet tested / debugged mono input inconjunction with stereo
output as 3d renderer needs. We are getting very close now,
probably...

Large number of trivial changes using sed:

- audio_unbias now bias_remove
- audio_{get,set}_gain now audio_{get,set}_igain
- audio_{get,set}_volume now audio_{get,set}_ogain

And all audio interfaces changed accordingly so sparc_get_gain is now
sparc_get_igain, etc.

Finally, audio.h was included all over the shop - removed in quite
a few places where not necessary.

  • 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 "debug.h"
51#include "memory.h"
52#include "audio_types.h"
53#include "audio_fmt.h"
54#include "auddev_oss.h"
55#include "util.h"
56
57int     iport     = AUDIO_MICROPHONE;
58int     bytes_per_block;
59
60/* Magic to get device names from OSS */
61
62#define OSS_MAX_DEVICES 3
63#define OSS_MAX_NAME_LEN 64
64
65static int ndev;
66char   dev_name[OSS_MAX_DEVICES][OSS_MAX_NAME_LEN];
67
68static char the_dev[] = "/dev/dspX";
69static int audio_fd[OSS_MAX_DEVICES];
70
71
72#define OSS_MAX_SUPPORTED_FORMATS 8
73/* == 8k,16k,32,48k * mono, stereo */
74static u_char       have_probed[OSS_MAX_DEVICES];
75static audio_format af_sup[OSS_MAX_DEVICES][OSS_MAX_SUPPORTED_FORMATS];
76static int          n_af_sup[OSS_MAX_DEVICES];
77
78static oss_probe_formats(audio_desc_t);
79
80audio_format format;
81
82#define bat_to_device(x)  ((x) * 100 / MAX_AMP)
83#define device_to_bat(x)  ((x) * MAX_AMP / 100)
84
85static int
86deve2oss(deve_e encoding)
87{
88        switch(encoding) {
89        case DEV_PCMU: return AFMT_MU_LAW;
90        case DEV_S8:   return AFMT_S8;
91        case DEV_S16return AFMT_S16_LE;
92        }
93        abort();
94}
95
96/* Try to open the audio device.              */
97/* Return TRUE if successful FALSE otherwise. */
98int
99oss_audio_open(audio_desc_t ad, audio_format *ifmt, audio_format *ofmt)
100{
101        int  mode, stereo, speed;
102        int  volume   = (100<<8)|100;
103        int  frag     = 0x7fff0000;                     /* unlimited number of fragments */
104        int  reclb    = 0;
105        char buffer[128];                               /* sigh. */
106
107        if (ad <0 || ad>OSS_MAX_DEVICES) {
108                debug_msg("Invalid audio descriptor (%d)", ad);
109                return FALSE;
110        }
111
112        sprintf(the_dev, "/dev/dsp%d", ad);
113
114        audio_fd[ad] = open(the_dev, O_RDWR | O_NDELAY);
115        if (audio_fd[ad] > 0) {
116                /* Note: The order in which the device is set up is important! Don't */
117                /*       reorder this code unless you really know what you're doing! */
118                if (ioctl(audio_fd[ad], SNDCTL_DSP_SETDUPLEX, 0) == -1) {
119                        fprintf(stderr, "ERROR: Cannot enable full-duplex mode!\n");
120                        return FALSE;
121                }
122
123                mode = deve2oss(ifmt->encoding);
124               
125                if ((ioctl(audio_fd[ad], SNDCTL_DSP_SETFMT, &mode) == -1)) {
126                        if (ifmt->encoding == DEV_S16) {
127                                audio_format_change_encoding(ifmt, DEV_PCMU);
128                                audio_format_change_encoding(ofmt, DEV_PCMU);
129                                if ((ioctl(audio_fd[ad], SNDCTL_DSP_SETFMT, &mode) == -1)) {
130                                        oss_audio_close(ad);
131                                        return FALSE;
132                                }
133                                debug_msg("Using mu-law\n");
134                        }
135                }
136
137                if (!have_probed[ad]) {
138                        oss_probe_formats(ad);
139                        have_probed[ad] = TRUE;
140                }
141
142                /* 20 ms blocksize - only modulates read sizes */
143                bytes_per_block = 20 * (ifmt->sample_rate / 1000) * (ifmt->bits_per_sample / 8);
144                /* Round to the nearest legal frag size (next power of two lower...) */
145                frag |= (int) (log(bytes_per_block)/log(2));
146                debug_msg("frag=%x bytes_per_block=%d\n", frag, bytes_per_block);
147                if ((ioctl(audio_fd[ad], SNDCTL_DSP_SETFRAGMENT, &frag) == -1)) {
148                        fprintf(stderr, "ERROR: Cannot set the fragement size\n");
149                }
150
151                stereo = ifmt->channels - 1;
152                assert(stereo == 0 || stereo == 1);
153                if ((ioctl(audio_fd[ad], SNDCTL_DSP_STEREO, &stereo) == -1) || (stereo != (ifmt->channels - 1))) {
154                        printf("ERROR: Audio device doesn't support %d channels!\n", ifmt->channels);
155                        oss_audio_close(ad);
156                        return FALSE;
157                }
158
159                speed = ifmt->sample_rate;
160                if ((ioctl(audio_fd[ad], SNDCTL_DSP_SPEED, &speed) == -1) || (speed != ifmt->sample_rate)) {
161                        printf("ERROR: Audio device doesn't support %d sampling rate!\n", ifmt->sample_rate);
162                        oss_audio_close(ad);
163                        return FALSE;
164                }
165
166                /* Set global gain/volume to maximum values. This may fail on */
167                /* some cards, but shouldn't cause any harm when it does..... */ 
168                ioctl(audio_fd[ad], MIXER_WRITE(SOUND_MIXER_VOLUME), &volume);
169                ioctl(audio_fd[ad], MIXER_WRITE(SOUND_MIXER_RECLEV), &volume);
170                /* Select microphone input. We can't select output source...  */
171                oss_audio_set_iport(ad, iport);
172                /* Turn off loopback from input to output... This only works  */
173                /* on a few cards, but shouldn't cause problems on the others */
174                ioctl(audio_fd[ad], MIXER_WRITE(SOUND_MIXER_IMIX), &reclb);
175                /* Device driver bug: we must read some data before the ioctl */
176                /* to tell us how much data is waiting works....              */
177                read(audio_fd[ad], buffer, 128);       
178                return audio_fd[ad];
179        } else {
180                oss_audio_close(ad);
181                return -1;
182        }
183}
184
185/* Close the audio device */
186void
187oss_audio_close(audio_desc_t ad)
188{
189        assert(audio_fd[ad] > 0);
190        oss_audio_drain(ad);
191        close(audio_fd[ad]);
192        audio_fd[ad] = -1;
193}
194
195/* Flush input buffer */
196void
197oss_audio_drain(audio_desc_t ad)
198{
199        u_char buf[160];
200
201        assert(ad < OSS_MAX_DEVICES);
202        assert(audio_fd[ad] > 0);
203
204        while(oss_audio_read(ad, buf, 160) == 160);
205}
206
207int
208oss_audio_duplex(audio_desc_t ad)
209{
210        /* We don't open device if not full duplex. */
211        UNUSED(ad);
212        return TRUE;
213}
214
215/* Gain and volume values are in the range 0 - MAX_AMP */
216void
217oss_audio_set_igain(audio_desc_t ad, int gain)
218{
219
220        int volume = bat_to_device(gain) << 8 | bat_to_device(gain);
221
222        assert(ad < OSS_MAX_DEVICES);
223        assert(audio_fd[ad] > 0);
224
225        switch (iport) {
226        case AUDIO_MICROPHONE :
227                if (ioctl(audio_fd[ad], MIXER_WRITE(SOUND_MIXER_MIC), &volume) == -1) {
228                        perror("Setting gain");
229                }
230                return;
231        case AUDIO_LINE_IN :
232                if (ioctl(audio_fd[ad], MIXER_WRITE(SOUND_MIXER_LINE), &volume) == -1) {
233                        perror("Setting gain");
234                }
235                return;
236        case AUDIO_CD:
237                if (ioctl(audio_fd[ad], MIXER_WRITE(SOUND_MIXER_CD), &volume) < 0) {
238                        perror("Setting gain");
239                }
240                return;
241        }
242        printf("ERROR: Unknown iport in audio_set_igain!\n");
243        abort();
244}
245
246int
247oss_audio_get_igain(audio_desc_t ad)
248{
249        int volume;
250
251        UNUSED(ad); assert(audio_fd[ad] > 0); assert(ad < OSS_MAX_DEVICES);
252
253        switch (iport) {
254        case AUDIO_MICROPHONE :
255                if (ioctl(audio_fd[ad], MIXER_READ(SOUND_MIXER_MIC), &volume) == -1) {
256                        perror("Getting gain");
257                }
258                break;
259        case AUDIO_LINE_IN :
260                if (ioctl(audio_fd[ad], MIXER_READ(SOUND_MIXER_LINE), &volume) == -1) {
261                        perror("Getting gain");
262                }
263                break;
264        case AUDIO_CD:
265                if (ioctl(audio_fd[ad], MIXER_READ(SOUND_MIXER_CD), &volume) < 0) {
266                        perror("Getting gain");
267                }
268                break;
269        default :
270                printf("ERROR: Unknown iport in audio_set_igain!\n");
271                abort();
272        }
273        return device_to_bat(volume & 0xff);
274}
275
276void
277oss_audio_set_ogain(audio_desc_t ad, int vol)
278{
279        int volume;
280
281        UNUSED(ad); assert(audio_fd[ad] > 0);
282
283        volume = vol << 8 | vol;
284        if (ioctl(audio_fd[ad], MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1) {
285                perror("Setting volume");
286        }
287}
288
289void
290oss_audio_loopback(audio_desc_t ad, int gain)
291{
292        UNUSED(ad); assert(audio_fd[ad] > 0);
293
294        gain = gain << 8 | gain;
295        if (ioctl(audio_fd[ad], MIXER_WRITE(SOUND_MIXER_IMIX), &gain) == -1) {
296                perror("loopback");
297        }
298}
299
300int
301oss_audio_get_ogain(audio_desc_t ad)
302{
303        int volume;
304
305        UNUSED(ad); assert(audio_fd[ad] > 0);
306
307        if (ioctl(audio_fd[ad], MIXER_READ(SOUND_MIXER_PCM), &volume) == -1) {
308                perror("Getting volume");
309        }
310        return device_to_bat(volume & 0x000000ff); /* Extract left channel volume */
311}
312
313int
314oss_audio_read(audio_desc_t ad, u_char *buf, int read_bytes)
315{
316        int read_len;
317        audio_buf_info info;
318
319        assert(audio_fd[ad] > 0);       
320
321        /* Figure out how many bytes we can read before blocking... */
322        ioctl(audio_fd[ad], SNDCTL_DSP_GETISPACE, &info);
323
324        read_len = min(info.bytes, read_bytes);
325        if ((read_len = read(audio_fd[ad], (char *)buf, read_len)) < 0) {
326                perror("audio_read");
327                        return 0;
328        }
329
330        return read_len;
331}
332
333int
334oss_audio_write(audio_desc_t ad, u_char *buf, int write_bytes)
335{
336        int              done, len;
337        char            *p;
338
339        assert(audio_fd[ad] > 0);
340       
341        p   = (char *) buf;
342        len = write_bytes;
343        while (1) {
344                if ((done = write(audio_fd[ad], p, len)) == len) {
345                        break;
346                }
347                if (errno != EINTR) {
348                                perror("Error writing device");
349                                return write_bytes - (len - done);
350                }
351                len -= done;
352                p   += done;
353        }
354        return write_bytes;
355}
356
357/* Set ops on audio device to be non-blocking */
358void
359oss_audio_non_block(audio_desc_t ad)
360{
361        int  on = 1;
362
363        UNUSED(ad); assert(audio_fd[ad] > 0);
364
365        if (ioctl(audio_fd[ad], FIONBIO, (char *)&on) < 0) {
366                debug_msg("Failed to set non-blocking mode on audio device!\n");
367        }
368}
369
370/* Set ops on audio device to block */
371void
372oss_audio_block(audio_desc_t ad)
373{
374        int  on = 0;
375
376        UNUSED(ad); assert(audio_fd[ad] > 0);
377
378        if (ioctl(audio_fd[ad], FIONBIO, (char *)&on) < 0) {
379                debug_msg("Failed to set blocking mode on audio device!\n");
380        }
381}
382
383void
384oss_audio_set_oport(audio_desc_t ad, int port)
385{
386        /* There appears to be no-way to select this with OSS... */
387        UNUSED(ad); assert(audio_fd[ad] > 0);
388        UNUSED(port);
389        return;
390}
391
392int
393oss_audio_get_oport(audio_desc_t ad)
394{
395        /* There appears to be no-way to select this with OSS... */
396        UNUSED(ad); assert(audio_fd[ad] > 0);
397        return AUDIO_HEADPHONE;
398}
399
400int
401oss_audio_next_oport(audio_desc_t ad)
402{
403        /* There appears to be no-way to select this with OSS... */
404        UNUSED(ad); assert(audio_fd[ad] > 0);
405        return AUDIO_HEADPHONE;
406}
407
408void
409oss_audio_set_iport(audio_desc_t ad, int port)
410{
411        int recmask;
412        int recsrc;
413        int gain;
414
415        UNUSED(ad); assert(audio_fd[ad] > 0);
416
417        if (ioctl(audio_fd[ad], MIXER_READ(SOUND_MIXER_RECMASK), &recmask) == -1) {
418                debug_msg("WARNING: Unable to read recording mask!\n");
419                return;
420        }
421        switch (port) {
422        case AUDIO_MICROPHONE :
423                if (recmask & SOUND_MASK_MIC) {
424                        recsrc = SOUND_MASK_MIC;
425                        if ((ioctl(audio_fd[ad], MIXER_WRITE(SOUND_MIXER_RECSRC), &recsrc) == -1) && !(recsrc & SOUND_MASK_MIC)) {
426                                debug_msg("WARNING: Unable to select recording source!\n");
427                                return;
428                        }
429                        gain = oss_audio_get_igain(ad);
430                        iport = port;
431                        oss_audio_set_igain(ad, gain);
432                } else {
433                        debug_msg("Audio device doesn't support recording from microphone\n");
434                }
435                break;
436        case AUDIO_LINE_IN :
437                if (recmask & SOUND_MASK_LINE) {
438                        recsrc = SOUND_MASK_LINE;
439                        if ((ioctl(audio_fd[ad], MIXER_WRITE(SOUND_MIXER_RECSRC), &recsrc) == -1) && !(recsrc & SOUND_MASK_LINE)){
440                                debug_msg("WARNING: Unable to select recording source!\n");
441                                return;
442                        }
443                        gain = oss_audio_get_igain(ad);
444                        iport = port;
445                        oss_audio_set_igain(ad, gain);
446                } else {
447                        debug_msg("Audio device doesn't support recording from line-input\n");
448                }
449                break;
450        case AUDIO_CD:
451                if (recmask & SOUND_MASK_CD) {
452                        recsrc = SOUND_MASK_CD;
453                        if ((ioctl(audio_fd[ad], MIXER_WRITE(SOUND_MIXER_RECSRC), &recsrc) == -1) && !(recsrc & SOUND_MASK_LINE)){
454                                debug_msg("WARNING: Unable to select recording source!\n");
455                                return;
456                        }
457                        gain = oss_audio_get_igain(ad);
458                        iport = port;
459                        oss_audio_set_igain(ad, gain);
460                } else {
461                        debug_msg("Audio device doesn't support recording from CD\n");
462                }
463                break;
464        default :
465                debug_msg("audio_set_port: unknown port!\n");
466                abort();
467        };
468        return;
469}
470
471int
472oss_audio_get_iport(audio_desc_t ad)
473{
474        UNUSED(ad); assert(audio_fd[ad] > 0);
475        return iport;
476}
477
478int
479oss_audio_next_iport(audio_desc_t ad)
480{
481        UNUSED(ad); assert(audio_fd[ad] > 0);
482
483        switch (iport) {
484        case AUDIO_MICROPHONE :
485                oss_audio_set_iport(ad, AUDIO_LINE_IN);
486                break;
487        case AUDIO_LINE_IN :
488                oss_audio_set_iport(ad, AUDIO_CD);
489                break;
490        case AUDIO_CD :
491                oss_audio_set_iport(ad, AUDIO_MICROPHONE);
492                break;
493        default :
494                debug_msg("Unknown audio source!\n");
495        }
496
497        return iport;
498}
499
500static int
501oss_audio_select(audio_desc_t ad, int delay_us)
502{
503        fd_set rfds;
504        struct timeval tv;
505
506        UNUSED(ad); assert(audio_fd[ad] > 0);
507       
508        tv.tv_sec = 0;
509        tv.tv_usec = delay_us;
510
511        FD_ZERO(&rfds);
512        FD_SET(audio_fd[ad], &rfds);
513
514        select(audio_fd[ad]+1, &rfds, NULL, NULL, &tv);
515
516        return FD_ISSET(audio_fd[ad], &rfds);
517}
518
519void
520oss_audio_wait_for(audio_desc_t ad, int delay_ms)
521{
522        oss_audio_select(ad, delay_ms * 1000);
523}
524
525int 
526oss_audio_is_ready(audio_desc_t ad)
527{
528        return oss_audio_select(ad, 0);
529}
530
531static int
532oss_set_mode(audio_desc_t ad, int speed, int stereo)
533{
534        int sp, st, success;
535       
536        ioctl(audio_fd[ad], SNDCTL_DSP_RESET, 0);
537        success = TRUE;
538       
539        /* We might want to set sample type here */
540
541        st = stereo;
542        if (ioctl(audio_fd[ad], SNDCTL_DSP_STEREO, &st) == -1 || st != stereo) {
543                success = FALSE;
544        }
545
546        sp = speed;
547        if (ioctl(audio_fd[ad], SNDCTL_DSP_SPEED, &sp) == -1 || sp != speed) {
548                success = FALSE;
549        }
550       
551        return success;
552}
553
554static int
555oss_probe_formats(audio_desc_t ad)
556{
557        int speed, stereo;
558        for (speed = 8000; speed <= 48000; speed += 8000) {
559                if (speed == 24000 || speed == 40000) continue;
560                for (stereo = 0; stereo < 2; stereo++) {
561                        if (!oss_set_mode(ad, speed, stereo)) continue; /* Failed */
562                        af_sup[ad][n_af_sup[ad]].sample_rate = speed;
563                        af_sup[ad][n_af_sup[ad]].channels    = stereo + 1;
564                        n_af_sup[ad]++;
565                }
566        }
567        return TRUE;
568}
569
570int
571oss_audio_supports(audio_desc_t ad, audio_format *fmt)
572{
573        int i;
574
575        for(i = 0; i < n_af_sup[ad]; i++) {
576                if (af_sup[ad][i].channels    == fmt->channels &&
577                    af_sup[ad][i].sample_rate == fmt->sample_rate) return TRUE;
578        }
579        return FALSE;
580}
581
582int
583oss_audio_query_devices(void)
584{
585        FILE    *f;
586        char     buf[OSS_MAX_NAME_LEN];
587        char    *name_start;
588        int      found_devices = FALSE;
589        char     devices_tag[] = "Audio devices:";
590        int      len           = strlen(devices_tag);
591
592        ndev = 0;
593
594        f = fopen("/dev/sndstat", "r");
595        if (f) {
596                while(!feof(f)) {
597                        fgets(buf, OSS_MAX_NAME_LEN, f);
598                        if (!strncmp(buf, devices_tag, len)) {
599                                found_devices = TRUE;
600                                debug_msg("Found devices entry\n");
601                                continue;
602                        }
603                        if (found_devices) {
604                                if ((name_start = strstr(buf, ":")) && ndev < OSS_MAX_DEVICES) {
605                                        name_start += 2; /* pass colon plus space */
606                                        strncpy(dev_name[ndev], name_start, OSS_MAX_NAME_LEN);
607                                        purge_chars(dev_name[ndev],"\n");
608                                        ndev++;
609                                        debug_msg("OSS device found (%s)\n", name_start);
610                                } else {
611                                        break;
612                                }
613                        }
614                }
615                fclose(f);
616        } else {
617                debug_msg("Cannot open /dev/sndstat\n");
618        }
619        return ndev;
620}
621
622int
623oss_get_device_count()
624{
625        return ndev;
626}
627
628const char *
629oss_get_device_name(audio_desc_t idx)
630{
631        if (idx >=0 && idx < ndev) {
632                return dev_name[idx];
633        }
634        debug_msg("Invalid index\n");
635        return NULL;
636}
637
638
639#endif /* OSS */
Note: See TracBrowser for help on using the browser.