root/rat/trunk/auddev_oss.c @ 1904

Revision 1904, 13.8 KB (checked in by ucacoxh, 16 years ago)

- Added loopback for OSS and Luigi's driver. This may not work on all cards.

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