source: trunk/third/esound/audio_solaris.c @ 17228

Revision 17228, 15.6 KB checked in by ghudson, 22 years ago (diff)
Fix botched merge.
Line 
1/*
2 * Taken mainly from xmp, (C) 1996-98 Claudio Matsuoka and Hipolito Carraro Jr
3 *
4 * Completely untested.. so fix it
5 */
6
7#ifdef HAVE_SYS_AUDIOIO_H
8#include <sys/audioio.h>
9#elif defined(HAVE_SYS_AUDIO_IO_H)
10#include <sys/audio.io.h>
11#elif defined(HAVE_SUN_AUDIOIO_H)
12#include <sun/audioio.h>
13#endif
14#include <stropts.h>
15#include <errno.h>
16#include <alloca.h>
17
18
19/* if you want to confirm proper device setup, uncomment the following line */
20/* #define ESDBG_DRIVER */
21
22static char *default_device = "/dev/audio";
23
24static char *my_ports = NULL;
25
26static void
27free_the_crap(void)
28{
29    if(my_ports)
30        free(my_ports);
31}
32
33#define ARCH_esd_audio_devices
34const char *esd_audio_devices()
35{
36    /*
37     * I don't know what's the use for this crappy format. Are we supposed
38     * to return everything the OS can possibly support or just the
39     * devices we have available? If the later, shouldn't we make a difference
40     * between play and record devices?
41     *
42     * Let's assume we want to return available devices. In that case we
43     * first have to make a difference between device (something living in
44     * the file system, which can be opened in order to talk to the audio
45     * driver, which essentialy correspond to audio card) and audio ports
46     * (which are things like speaker, lineout etc.). It seems esd uses
47     * "device" for our "ports". But how does it make a difference between
48     * different audio cards? They don't have to support the same ports.
49     *
50     * Anyway, the original code was just returning the static string.
51     * We'll try to return the available ports. But we need to calculate
52     * the stupid string, which means we have to free it at some point.
53     * This thing is a library and it can be dlclosed() at any time. Oh, joy.
54     *
55     * We could free the string at esd_audio_close() time, but it doesn't
56     * make much sense, because that's not library unload and the
57     * calculated string won't be different when another esd_audio_devices()
58     * call comes. So we'll use atexit() to register the clean-up function.
59     * On Solaris 8 that frees the string at library unload time.
60     * On earlier releases it doesn't. So you'll have a small memory
61     * leak. If somebody explains me what's the use for this function, I
62     * might even want to fix it.  -- dave@arsdigita.com
63     */
64
65    char *device, *ctl;
66    int fd, err;
67    struct audio_info auinfo;
68    unsigned int avail_ports;
69
70    if (my_ports)
71        return my_ports + 2;
72
73    /* Use the control device, because opening the normal device might block */
74    if (!(device = getenv("AUDIODEV")))
75    {
76        device = default_device;
77    }
78    ctl = alloca(strlen(device) + 4);
79    strcpy(ctl, device);
80    strcat(ctl, "ctl");
81
82    /* Try to open non-blocking. We don't want to block on this shit. */
83
84    do {
85        fd = open(ctl, O_WRONLY | O_NONBLOCK);
86    } while(fd == -1 && errno == EINTR);
87    if (fd == -1)
88    {
89        if(errno == EBUSY)
90            /* Just return some default crap. */
91            goto hell;
92        /* Otherwise we don't have available audio device, so no ports. */
93        return "";
94    }
95
96    /* Get the available ports */
97    do {
98        err = ioctl(fd, AUDIO_GETINFO, &auinfo);
99    } while (err == -1 && errno == EINTR);
100    close(fd);
101    if (err == -1)
102        goto hell;
103    avail_ports = auinfo.record.avail_ports | auinfo.play.avail_ports;
104    if (!(my_ports = malloc(100))) /* XXX hardcoded string size */
105        goto hell;
106    atexit(free_the_crap);
107
108    /* Now build the string. Everybody's returning only output ports, so... */
109
110    my_ports[0] = 0;
111    if (avail_ports & AUDIO_SPEAKER)
112        strcat(my_ports, ", speaker");
113    if (avail_ports & AUDIO_HEADPHONE)
114        strcat(my_ports, ", headphone");
115    if (avail_ports & AUDIO_LINE_OUT)
116        strcat(my_ports, ", lineout");
117#ifdef AUDIO_SPDIF_OUT
118    if (avail_ports & AUDIO_SPDIF_OUT)
119        strcat(my_ports, ", spdif");
120#endif
121#ifdef AUDIO_AUX1_OUT
122    if (avail_ports & AUDIO_AUX1_OUT)
123        strcat(my_ports, ", aux1");
124#endif
125#ifdef AUDIO_AUX2_OUT
126    if (avail_ports & AUDIO_AUX2_OUT)
127        strcat(my_ports, ", aux2");
128#endif
129    return my_ports + 2;
130
131hell:
132    return "speaker, lineout, headphone";
133}
134
135
136void dump_audio_info(audio_info_t *t, int play)
137{
138    if( play )
139    {
140        char *enc, aports[200], ports[200];
141
142        switch( t->play.encoding )
143        {
144        case AUDIO_ENCODING_NONE:
145            enc = "AUDIO_ENCODING_NONE";
146            break;
147        case AUDIO_ENCODING_ULAW:
148            enc = "AUDIO_ENCODING_ULAW";
149            break;
150        case AUDIO_ENCODING_ALAW:
151            enc = "AUDIO_ENCODING_ALAW";
152            break;
153        case AUDIO_ENCODING_LINEAR:
154            enc = "AUDIO_ENCODING_LINEAR";
155            break;
156        }
157
158        ports[0] = 0;
159        if( t->play.port & AUDIO_SPEAKER )
160            strcat(ports, " & AUDIO_SPEAKER");
161        if( t->play.port & AUDIO_HEADPHONE )
162            strcat(ports, " & AUDIO_HEADPHONE");
163        if( t->play.port & AUDIO_LINE_OUT)
164            strcat(ports, " & AUDIO_LINE_OUT");
165#ifdef AUDIO_SPDIF_OUT
166        if ( t->play.port & AUDIO_SPDIF_OUT )
167            strcat(ports, " & AUDIO_SPDIF_OUT");
168#endif
169#ifdef AUDIO_AUX1_OUT
170        if ( t->play.port & AUDIO_AUX1_OUT )
171            strcat(ports, " & AUDIO_AUX1_OUT");
172#endif
173#ifdef AUDIO_AUX2_OUT
174        if ( t->play.port & AUDIO_AUX2_OUT )
175            strcat(ports, " & AUDIO_AUX2_OUT");
176#endif
177
178        aports[0] = 0;
179        if( t->play.port & AUDIO_SPEAKER )
180            strcat(aports, " & AUDIO_SPEAKER");
181        if( t->play.port & AUDIO_HEADPHONE )
182            strcat(aports, " & AUDIO_HEADPHONE");
183        if( t->play.port & AUDIO_LINE_OUT)
184            strcat(aports, " & AUDIO_LINE_OUT");
185#ifdef AUDIO_SPDIF_OUT
186        if ( t->play.port & AUDIO_SPDIF_OUT )
187            strcat(aports, " & AUDIO_SPDIF_OUT");
188#endif
189#ifdef AUDIO_AUX1_OUT
190        if ( t->play.port & AUDIO_AUX1_OUT )
191            strcat(aports, " & AUDIO_AUX1_OUT");
192#endif
193#ifdef AUDIO_AUX2_OUT
194        if ( t->play.port & AUDIO_AUX2_OUT )
195            strcat(aports, " & AUDIO_AUX2_OUT");
196#endif
197               
198        printf("Play Info:\n");
199        printf(" Sample Rate: %d\n", t->play.sample_rate);
200        printf(" Channels:    %d\n", t->play.channels);
201        printf(" Bits/sample: %d\n", t->play.precision);
202        printf(" Encoding:    %s\n", enc);
203        printf(" Gain:        %d\n", t->play.gain);
204        printf(" Port:        %s\n", ports + 3);
205        printf(" availPorts:  %s\n", aports + 3);
206        printf(" Mon Gain:    %d\n", t->monitor_gain);
207        printf(" o/p Muted:   %d\n", t->output_muted);
208        printf(" Balance:     %d\n", t->play.balance);
209    }
210
211    return;
212}
213
214
215/*
216 * To anybody hacking at this:
217 *
218 *   return -1 means something failed, but the main library will try again
219 *             with the different options
220 *   return -2 means big trouble and the main library won't try again
221 *
222 * -2 is the appropriate value if we detected audio device we don't support
223 * or if we can't even open the device or something like that.
224 *
225 * -1 is intended for conditions when the device doesn't support audio
226 * parameters. In that case the main library can try again, but it needs
227 * to call us again to open the device with another set of audio
228 * parameters.
229 *
230 * Don't just put "return -1" at random places. If everything fails (which
231 * is -2 situation), the user will get 10 or more error messages.
232 * That sucks.
233 *
234 * -- dave@arsdigita.com
235 */
236
237#define ARCH_esd_audio_open
238int esd_audio_open()
239{
240    int afd = -1, cafd = -1;
241    audio_device_t adev;
242    char *device, *devicectl;
243    int err;
244
245    /*
246     * Recording code needs access to the control device, but the playing
247     * code currently doesn't. Control device is the name of audio device
248     * with appended "ctl". So we'll just alloca() memory for that, because
249     * it can't be very large and it will allow us to just return and not
250     * care about freeing the memory.  -- dave@arsdigita.com
251     */
252
253    if ((device = getenv("AUDIODEV")) == NULL)
254        device = default_device;
255    devicectl = alloca(strlen(device) + 4);
256    strcpy(devicectl, device);
257    strcat(devicectl, "ctl");
258   
259    if ((esd_audio_format & ESD_MASK_FUNC) == ESD_RECORD) {
260        audio_info_t ainfo;
261               
262        AUDIO_INITINFO(&ainfo);
263        do {
264            cafd = open(devicectl, O_RDWR);
265        } while (cafd == -1 && errno == EINTR);
266        if( cafd == -1 )
267        {
268            fprintf(stderr,"esd: Could not open ctl device for recording\n");
269            esd_audio_fd = -1;
270            return -1;
271        }
272        do {
273            err = ioctl(cafd, AUDIO_GETDEV, &adev);
274        } while (err == -1 && errno == EINTR);
275        if (err == -1) {
276            fprintf(stderr, "esd: ioctl(\"%s\", AUDIO_GETDEV failed: %s\n",
277                    devicectl, strerror(errno));
278            close(cafd);
279            esd_audio_fd = -1;
280            return -1;
281        }
282
283        /*
284         * Let's check the device name to see if we know how to handle
285         * it. The problem with this is that the new audio devices will
286         * become available over time and it will happen that this program
287         * can use it, but the device name won't be known to it. This just
288         * happened with the Blade machines which have SUNW,audiots.
289         * In attempt to prevent this from happening again, we'll try
290         * the following approach:
291         *  - First check if it's one we know we can't handle. If so,
292         *    print the error message and return.
293         *  - Then check if it's one we know about. If so, everything's
294         *    peachy keen.
295         *  - If it's a device we don't know about, assume that we can
296         *    use it and just print a warning.
297         *
298         *  -- dave@arsdigita.com
299         */
300
301        if ( !(strcmp(adev.name, "SUNW,am79c30")))
302        {
303             fprintf(stderr, "esd: Cannot handle device `%s'.\n",
304                     adev.name);
305             esd_audio_fd = -1;
306             return -2;
307        }
308        if ( (strcmp(adev.name, "SUNW,CS4231") != 0)
309             && (strcmp(adev.name, "SUNW,audiots") != 0)
310             && (strcmp(adev.name, "SUNW,sb16")  != 0)
311             && (strcmp(adev.name, "SUNW,sbpro") != 0)
312             && (strcmp(adev.name, "SUNW,dbri") != 0)  )
313         {
314            fprintf(stderr, "esd: Unknown device `%s', but will try anyway\n",
315                    adev.name);
316         }
317        /* SUNW,CS4231 and most others */
318        {
319            int gain = 255; /* Range: 0 - 255 */
320            int port = AUDIO_MICROPHONE;
321            int bsize = 8180;
322                       
323            ainfo.record.sample_rate = esd_audio_rate;
324           
325            if ((esd_audio_format & ESD_MASK_CHAN) == ESD_STEREO)
326                ainfo.record.channels = 2;
327            else
328                ainfo.record.channels = 1;
329                       
330            if ((esd_audio_format & ESD_MASK_BITS) == ESD_BITS16)
331                ainfo.record.precision = 16;
332            else
333                ainfo.record.precision = 8;
334                       
335            ainfo.record.encoding = AUDIO_ENCODING_LINEAR;
336            ainfo.record.gain = gain;
337            ainfo.record.port = port;
338            ainfo.record.balance = AUDIO_MID_BALANCE;
339            ainfo.record.buffer_size = bsize;
340            /* actually, it doesn't look like we need to set any
341               settings here-- they always seem to be the default, no
342               matter what else was spec. 
343            fprintf( stderr, "record set up: "
344                     "rate=%d, channels=%d, precision=%d, gain=%d, port=%x\n",
345                     ainfo.record.sample_rate, ainfo.record.channels,
346                     ainfo.record.precision, ainfo.record.gain, ainfo.record.port);
347            */
348        }
349               
350        do {
351            afd = open(device, O_RDONLY);
352        } while (afd == -1 && errno == EINTR);
353        if (afd == -1) {
354            fprintf(stderr, "esd: Opening %s device failed: %s\n",
355                    device, strerror(errno));
356            esd_audio_fd = -1;
357            return -1;
358        }
359 
360        do {
361            err = ioctl(afd, AUDIO_SETINFO, &ainfo);
362        } while (err == -1 && errno == EINTR);
363        if (err == -1)
364        {
365            fprintf(stderr, "esd: ioctl(\"%s\", AUDIO_SETINFO) failed: %s\n",
366                    device, strerror(errno));
367            esd_audio_fd = -1;
368            return -1;
369        }
370        esd_audio_fd = afd;
371        return afd;
372    }
373    /* implied else: if ( (esd_audio_format & ESD_MASK_FUNC) != ESD_RECORD ) */
374   
375    do {
376        afd = open(device, O_WRONLY);
377    } while (afd == -1 && errno == EINTR);
378    if (afd == -1) {
379       if(errno != EACCES && errno != ENOENT)
380           fprintf(stderr, "esd: Opening %s device failed: %s\n", device,
381                   strerror(errno));
382       /*
383        * Don't spit errors in other cases; the user is likely to know
384        * that he doesn't have audio device or that he doesn't have the
385        * permission to use it (by default only the user logged at the
386        * console can use audio device. All other users on the server can't.)
387        * So every program which links to this library doesn't have to
388        * tell him that.  -- dave@arsdigita.com
389        */
390       esd_audio_fd = -1;
391       return -2;
392    }
393
394    do {
395        err = ioctl(afd, AUDIO_GETDEV, &adev);
396    } while (err == -1 && errno == EINTR);
397    if (err == -1) {
398        fprintf(stderr, "esd: ioctl(\"%s\", AUDIO_GETDEV) failed: %s\n",
399                device, strerror(errno));
400        close(afd);
401        esd_audio_fd = -1;
402
403        /*
404         * Whatever this was, it's probably going to come again, so there's
405         * no point in trying to reopen the device with another setting.
406         * We didn't even come to that.  -- dave@arsdigita.com
407         */
408
409        return -2;
410    }
411
412    /*
413     * Device check as above: assume that unknown devices are new hardware
414     * and that we can handle them just fine.  -- dave@arsdigita.com
415     */
416    if ( !strcmp(adev.name, "SUNW,am79c30"))
417    {
418        fprintf(stderr, "esd: Cannot handle device `%s'.\n", adev.name);
419        close(afd);
420        esd_audio_fd = -1;
421        return -1;
422    }
423    if ( (strcmp(adev.name, "SUNW,CS4231") != 0)
424        && (strcmp(adev.name, "SUNW,audiots") != 0)
425        && (strcmp(adev.name, "SUNW,sb16")  != 0)
426        && (strcmp(adev.name, "SUNW,sbpro")  != 0)
427        && (strcmp(adev.name, "SUNW,dbri") != 0)  )
428    {
429        fprintf(stderr, "esd: Unknown device `%s', but will try anyway.\n",
430                adev.name);
431    }
432
433    {
434        /*
435         * Volume, balance and output device should be controlled by
436         * an external program - that way the user can set his preferences
437         * for all players  -- dave@arsdigita.com
438         */
439        /* int gain = 64;       /* Range: 0 - 255 */
440        int port;
441        int bsize = 8180;
442        audio_info_t ainfo;
443     
444        if ( esd_audio_device == NULL )
445            /* Don't change the output device unless specificaly requested */
446            port = 0;
447        else if ( !strcmp( esd_audio_device, "lineout" ) )
448            port = AUDIO_LINE_OUT;
449        else if ( !strcmp( esd_audio_device, "speaker" ) )
450            port = AUDIO_SPEAKER;
451        else if ( !strcmp( esd_audio_device, "headphone" ) )
452            port = AUDIO_HEADPHONE;
453        /*
454         * The #ifdefs below are for the older OS releases. They won't have
455         * these things defined, so the compilation would break without them.
456         */
457#ifdef AUDIO_SPDIF_OUT
458        else if ( !strcmp( esd_audio_device, "spdif" ) )
459            port = AUDIO_SPDIF_OUT;
460#endif
461#ifdef AUDIO_AUX1_OUT
462        else if ( !strcmp( esd_audio_device, "aux1" ) )
463            port = AUDIO_AUX1_OUT;
464#endif
465#ifdef AUDIO_AUX2_OUT
466        else if ( !strcmp( esd_audio_device, "aux2" ) )
467            port = AUDIO_AUX2_OUT;
468#endif
469        else {
470            fprintf(stderr, "esd: Unknown output device `%s'.\n",
471                    esd_audio_device);
472            close(afd);
473            esd_audio_fd = -1;
474            return -1;
475        }
476
477        AUDIO_INITINFO(&ainfo);
478     
479        ainfo.play.sample_rate = esd_audio_rate;
480     
481        if ((esd_audio_format & ESD_MASK_CHAN) == ESD_STEREO)
482            ainfo.play.channels = 2;
483        else
484            ainfo.play.channels = 1;
485     
486        if ((esd_audio_format & ESD_MASK_BITS) == ESD_BITS16)
487            ainfo.play.precision = 16;
488        else
489            ainfo.play.precision = 8;
490     
491        ainfo.play.encoding = AUDIO_ENCODING_LINEAR;
492        /* ainfo.play.gain = gain; */
493        if(port)
494            ainfo.play.port = port;
495        /* ainfo.play.balance = AUDIO_MID_BALANCE; */
496        ainfo.play.buffer_size = bsize;
497        ainfo.output_muted = 0;
498     
499#ifdef ESDBG_DRIVER
500        dump_audio_info(&ainfo,1);
501#endif
502     
503        do {
504            err = ioctl(afd, AUDIO_SETINFO, &ainfo);
505        } while (err == -1 && errno == EINTR);
506        if (err == -1)
507        {
508            fprintf(stderr, "esd: ioctl(\"%s\", AUDIO_SETINFO) failed: %s\n",
509                    device, strerror(errno));
510            close(afd);
511            esd_audio_fd = -1;
512            return -1;
513        }
514    }
515   
516    esd_audio_fd = afd;
517    return afd;
518}
519
520#define ARCH_esd_audio_flush
521void
522esd_audio_flush()
523{
524    if (esd_audio_format & (ESD_PLAY | ESD_RECORD) == ESD_PLAY | ESD_RECORD)
525        ioctl(esd_audio_fd, I_FLUSH, FLUSHRW);
526    else
527        if (esd_audio_format & ESD_PLAY == ESD_PLAY)
528            ioctl(esd_audio_fd, I_FLUSH, FLUSHW);
529        else
530            if (esd_audio_format & ESD_RECORD == ESD_RECORD)
531                ioctl(esd_audio_fd, I_FLUSH, FLUSHR);
532}
533
534#define ARCH_esd_audio_close
535void
536esd_audio_close()
537{
538    /* esd_audio_flush();  Should we flush here or not? */
539    close(esd_audio_fd);
540}
Note: See TracBrowser for help on using the repository browser.