root/rat/trunk/auddev_luigi.c @ 1901

Revision 1901, 12.2 KB (checked in by ucaccsp, 16 years ago)

Split config.h into config_unix.h and config_win32.h
Converted all files to use these and removed all #include's of
system headers into these two files.

  • 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
322/* Gain and volume values are in the range 0 - MAX_AMP */
323void
324audio_set_volume(int audio_fd, int vol)
325{
326        int volume;
327
328        if (audio_fd < 0)
329                return;
330        volume = vol << 8 | vol;
331        if (ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1) {
332                perror("Setting volume");
333        }
334}
335
336int
337audio_get_volume(int audio_fd)
338{
339        int volume;
340
341        if (audio_fd < 0)
342                return (0);
343        if (ioctl(audio_fd, MIXER_READ(SOUND_MIXER_PCM), &volume) == -1) {
344                perror("Getting volume");
345        }
346
347        return device_to_bat(volume & 0xff); /* Extract left channel volume */
348}
349
350void
351audio_set_oport(int audio_fd, int port)
352{
353        /* There appears to be no-way to select this with OSS... */
354        UNUSED(audio_fd);
355        UNUSED(port);
356        return;
357}
358
359int
360audio_get_oport(int audio_fd)
361{
362        /* There appears to be no-way to select this with OSS... */
363        UNUSED(audio_fd);
364        return AUDIO_HEADPHONE;
365}
366
367int
368audio_next_oport(int audio_fd)
369{
370        /* There appears to be no-way to select this with OSS... */
371        UNUSED(audio_fd);
372        return AUDIO_HEADPHONE;
373}
374
375void
376audio_set_gain(int audio_fd, int gain)
377{
378        int volume = bat_to_device(gain) << 8 | bat_to_device(gain);
379
380        if (audio_fd < 0) {
381                return;
382        }
383        switch (iport) {
384        case AUDIO_MICROPHONE:
385                if (ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_MIC), &volume) < 0)
386                        perror("Setting gain");
387                break;
388        case AUDIO_LINE_IN:
389                if (ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_LINE), &volume) < 0)
390                        perror("Setting gain");
391                break;
392        case AUDIO_CD:
393                if (ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_CD), &volume) < 0)
394                        perror("Setting gain");
395                break;
396        }
397        return;
398}
399
400int
401audio_get_gain(int audio_fd)
402{
403        int volume;
404
405        if (audio_fd < 0) {
406                return (0);
407        }
408        switch (iport) {
409        case AUDIO_MICROPHONE:
410                if (ioctl(audio_fd, MIXER_READ(SOUND_MIXER_MIC), &volume) < 0)
411                        perror("Getting gain");
412                break;
413        case AUDIO_LINE_IN:
414                if (ioctl(audio_fd, MIXER_READ(SOUND_MIXER_LINE), &volume) < 0)
415                        perror("Getting gain");
416                break;
417        case AUDIO_CD:
418                if (ioctl(audio_fd, MIXER_READ(SOUND_MIXER_CD), &volume) < 0)
419                        perror("Getting gain");
420                break;
421        default:
422                printf("ERROR: Unknown iport in audio_set_gain!\n");
423                abort();
424        }
425        return (device_to_bat(volume & 0xff));
426}
427
428void
429audio_set_iport(int audio_fd, int port)
430{
431        int recmask, gain, src;
432
433        if (ioctl(audio_fd, MIXER_READ(SOUND_MIXER_RECMASK), &recmask) == -1) {
434                perror("Unable to read recording mask");
435                return;
436        }
437
438        switch (port) {
439        case AUDIO_MICROPHONE:
440                src = SOUND_MASK_MIC;
441                break;
442        case AUDIO_LINE_IN:
443                src = SOUND_MASK_LINE;
444                break;
445        case AUDIO_CD:
446                src = SOUND_MASK_CD;
447                break;
448        }
449
450        gain = audio_get_gain(audio_fd);
451        audio_set_gain(audio_fd, 0);
452        if ((ioctl(audio_fd, MIXER_WRITE(SOUND_MIXER_RECSRC), &src) < 0)) {
453                perror("Unable to select recording source");
454                return;
455        }
456        iport = port;
457        audio_set_gain(audio_fd, gain);
458}
459
460int
461audio_get_iport(int audio_fd)
462{
463        UNUSED(audio_fd);
464        return iport;
465}
466
467int
468audio_next_iport(int audio_fd)
469{
470        switch (iport) {
471        case AUDIO_MICROPHONE:
472                audio_set_iport(audio_fd, AUDIO_LINE_IN);
473                break;
474        case AUDIO_LINE_IN:
475                audio_set_iport(audio_fd, AUDIO_CD);
476                break;
477        case AUDIO_CD:
478                audio_set_iport(audio_fd, AUDIO_MICROPHONE);
479                break;
480        default:
481                printf("Unknown audio source!\n");
482        }
483        return (iport);
484}
485
486void
487audio_switch_out(int audio_fd, struct s_cushion_struct *ap)
488{
489        UNUSED(ap);
490        if (!audio_duplex(audio_fd) && !can_write) {
491                audio_close(audio_fd);
492                audio_open_rw(O_WRONLY);
493        }
494}
495
496void
497audio_switch_in(int audio_fd)
498{
499        if (!audio_duplex(audio_fd) && !can_read) {
500                audio_close(audio_fd);
501                audio_open_rw(O_RDONLY);
502        }
503}
504
505int
506audio_get_blocksize()
507{
508        return format.blocksize;
509}
510
511int
512audio_get_channels()
513{
514        return format.num_channels;
515}
516
517int
518audio_get_freq()
519{
520        return format.sample_rate;
521}
522
523#endif
Note: See TracBrowser for help on using the browser.