root/rat/trunk/auddev_luigi.c @ 1904

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