root/rat/trunk/auddev_oss.c @ 2107

Revision 2107, 15.7 KB (checked in by ucacoxh, 16 years ago)

- First stab at oss audio interface update. No Linux machine to test
it on for the moment :-(

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