Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/openal-0.0.8/src/backends/alc_backend_irix.c @ 17

Last change on this file since 17 was 17, checked in by landauf, 16 years ago

added openal

File size: 17.9 KB
Line 
1/* -*- mode: C; tab-width:8; c-basic-offset:8 -*-
2 * vi:set ts=8:
3 *
4 * iris.c
5 *
6 * functions related to the aquisition and management of the DMedia
7 * audio on IRIX.
8 *
9 * This file is in the Public Domain and comes with no warranty.
10 * Erik Hofman <erik@ehofman.com>
11 *
12 * For more information, please visit:
13 * http://www.sgi.com/tech/faq/audio/general.html
14 *
15 */
16
17/*
18 * Only Indy, Indogo2 and Onyx Audio/Serial Option (ASO) can
19 * natively handle 4 channel (surround sound) audio.
20 *
21 * Adding 4 channel audio support is possible by adding the following
22 * definition to your ~/.openalrc file:
23 *     (define speaker-num 4)
24 *
25 * It is also possible to specify the default output port by adding
26 * the following definition:
27 *     (define dmedia-out-device "Analog Out")
28 *
29 * When your system doesn't support four channel audio by default, but
30 * it does contain (at least) two different output ports you could
31 * enable four channel audio by difining two separate ports:
32 *     (define dmedia-out-device "Analog Out")
33 *     (define dmedia-rear-out-device "Analog Out 2")
34 *
35 * or alternatively by selecting two different interfaces:
36 *     (define dmedia-out-device "A3.Speaker")
37 *     (define dmedia-rear-out-device "A3.LineOut2")
38 *
39 * see "man 3dm alResources" for more information.
40 */
41
42/*
43 * Known problems:
44 *
45 * 1 channel audio doesn't work as expected.
46 */
47
48#include <stdio.h>
49#include <stdlib.h>
50#include <dmedia/audio.h>
51#include <errno.h>
52
53/* dmedia complains */
54#define SGI_AL_CHANNELS AL_CHANNELS
55#define SGI_AL_GAIN     AL_GAIN
56#undef AL_CHANNELS
57#undef AL_GAIN
58#undef AL_VERSION
59#undef AL_INVALID_VALUE
60
61#include "al_siteconfig.h"
62
63#include <AL/al.h>
64#include <AL/alext.h>
65
66#include "backends/alc_backend.h"
67
68#include "al_config.h"
69#include "al_main.h"
70#include "al_debug.h"
71
72#include "alc/alc_context.h"
73
74/*
75 * Type definitions
76 */
77typedef struct {
78    ALport port;
79    ALconfig config;
80
81    int device;
82    char *name;
83    unsigned int sampleWidth;
84    unsigned int sampleRate;
85    unsigned int numChannels;
86    stamp_t offset;
87
88    unsigned int _numChannels_init;
89    unsigned int _numChannels_avail;
90} _ALdevice;
91
92typedef struct {
93    _ALdevice input;
94    _ALdevice *output;
95    unsigned int numOutputPorts;
96} _ALhandle;
97
98/* Temporary storage buffer size */
99#define BUF_SIZE        64
100#define MAX_DELTA       8
101#define CHECK_FRAMES    4
102#define MAX_DEVICES     2
103
104/*
105 * Local funtion foreward declaration.
106 */
107static void check_sync(void *handle);
108static void sync_ports(void *handle);
109static int grab_device_byname(const char *name);
110
111
112/*
113 * Fallback function
114 */
115ALC_BackendPrivateData *alcBackendOpenNative_ (ALC_OpenMode mode)
116{
117   return NULL;
118}
119
120
121/*
122 * Driver functions
123 */
124
125static void *grab_read_dmedia(void)
126{
127    _ALhandle *alh;
128
129    alh = (_ALhandle *)calloc(1, sizeof(_ALhandle));
130    if (alh == NULL)
131    {
132        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__, "Insufficient memory.\n");
133        return NULL;
134    }
135
136
137    alh->input.name = "dmedia-in-device";
138    alh->input.device = AL_DEFAULT_INPUT;
139    alh->input.sampleWidth = 16;
140    alh->input._numChannels_avail = 2;
141    alh->input.numChannels = 2;
142
143    alh->input.device = grab_device_byname(alh->input.name);
144    if (alh->input.device <= 0)
145    {
146        release_dmedia(alh);
147        return NULL;
148    }
149
150    return NULL; /* (void *)alh; */
151}
152
153static void *grab_write_dmedia(void)
154{
155    _ALhandle *alh;
156    ALpv params[2];
157    int i, res;
158
159    alh = (_ALhandle *)calloc(1, sizeof(_ALhandle));
160    if (alh == NULL)
161    {
162        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__, "Insufficient memory.\n");
163        return NULL;
164    }
165
166
167    /*
168     * Read the configuration file and request the proper device(s) or
169     * interface(s).
170     */
171    alh->numOutputPorts = 1;
172    alh->output = (_ALdevice *)calloc(MAX_DEVICES, sizeof(_ALdevice));
173    alh->output[0].name = "dmedia-out-device";
174    alh->output[0].sampleWidth = 16;
175    alh->output[0]._numChannels_avail = 2;
176    alh->output[0].numChannels = 2;
177
178    alh->output[0].device = grab_device_byname(alh->output[0].name);
179    if (alh->output[0].device <= 0)
180    {
181       if (alh->output[0].device < 0)
182       {
183           release_dmedia(alh);
184           return NULL;
185       }
186
187        alh->output[0].device = AL_DEFAULT_OUTPUT;
188    }
189
190#if MAX_DEVICES >= 2
191    alh->output[1].name = "dmedia-rear-out-device";
192    alh->output[1].device = grab_device_byname(alh->output[1].name);
193    if (alh->output[1].device < 0)
194    {
195       return AL_FALSE;
196    }
197    if (alh->output[1].device > 0)
198        alh->numOutputPorts++;
199#endif
200
201    for (i=0; i < alh->numOutputPorts; i++)
202    {
203        /*
204         * Store the default configuration to restore it when finished.
205         */
206        params[0].param = AL_CHANNEL_MODE;
207        res = alGetParams(alh->output[i].device, params, 1);
208        if ((res >= 0) && (params[0].sizeOut >= 0))
209            alh->output[i]._numChannels_init = params[0].value.i;
210
211        /*
212         * find out how many channels the device is.
213         */
214        params[0].param = SGI_AL_CHANNELS;
215        res = alGetParams(alh->output[i].device, params, 1);
216        if ((res >= 0) && (params[0].sizeOut >= 0))
217        {
218            alh->output[i]._numChannels_avail = params[0].value.i;
219            alh->output[i].numChannels = params[0].value.i;
220        }
221    }
222
223
224    /*
225     * Create a new config that remains available until exit.
226     * Only the master port has a configurtion assigned to it.
227     * All slave ports share the same config.
228     */
229   
230    alh->output[0].config = alNewConfig();
231    if (alh->output[0].config == NULL)
232    {
233        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__, alGetErrorString(oserror()));
234        return AL_FALSE;
235    }
236
237    return (void *)alh;
238}
239
240void *
241alcBackendOpenDMedia_( ALC_OpenMode mode )
242{
243        return mode == ALC_OPEN_INPUT_ ? grab_read_dmedia() : grab_write_dmedia();
244}
245
246void dmedia_blitbuffer(void *handle,
247                      void *dataptr,
248                      int bytes_to_write)
249{
250    static int check_ = CHECK_FRAMES;
251    _ALhandle *alh = (_ALhandle *)handle;
252    int sample_width = (alh->output[0].sampleWidth / 8);
253    int frame_size = sample_width * alh->output[0].numChannels;
254    int frames_to_write, frame_no, i;
255    char **buf, *ptr;
256
257    if (alh->numOutputPorts == 1)
258    {
259        alWriteFrames(alh->output[0].port, dataptr, bytes_to_write/frame_size);
260        return;
261    }
262
263#if MAX_DEVICES >= 2
264    /*
265     * Setup a buffer for each audio port.
266     */
267    frames_to_write = bytes_to_write / alh->numOutputPorts;
268
269    buf = (char **)calloc(alh->numOutputPorts, sizeof(char *));
270    for (i=0; i < alh->numOutputPorts; i++)
271        buf[i] = malloc(frames_to_write);
272
273    /*
274     * Fill the buffers in the appropriate format.
275     */
276    ptr = dataptr;
277    for(frame_no = 0; frame_no < frames_to_write; frame_no += frame_size)
278    {
279        for (i=0; i < alh->numOutputPorts; i++)
280        {
281            memcpy(&buf[i][frame_no], ptr, frame_size);
282            ptr += frame_size;
283        }
284       
285    }
286
287
288    /*
289     * Sent the data to the appropriate output port and clean up the buffers.
290     */
291    for (i=0; i < alh->numOutputPorts; i++)
292    {
293        alWriteFrames(alh->output[i].port, buf[i], frames_to_write/frame_size);
294        free(buf[i]);
295    }
296    free(buf);
297
298    if (! --check_)
299    {
300        check_ = CHECK_FRAMES;
301        check_sync(handle);
302    }
303#endif
304}
305
306void release_dmedia(void *handle)
307{
308    _ALhandle *alh = (_ALhandle *)handle;
309    ALpv params;
310    int i;
311
312    if (alh->output[0].config != NULL) {
313        alFreeConfig(alh->output[0].config);
314        alh->output[0].config = NULL;
315    }
316
317    for (i=0; i < alh->numOutputPorts; i++)
318    {
319        params.param = AL_CHANNEL_MODE;
320        params.value.i = alh->output[i]._numChannels_init;
321        if(alh->output[i]._numChannels_init > 0)
322            alSetParams(alh->output[i].device, &params, 1);
323
324        if (alh->output[i].port != NULL) {
325            alClosePort(alh->output[i].port);
326            alh->output[i].port = NULL;
327        }
328    }
329
330    if (alh->output != NULL) {
331        free(alh->output);
332        alh->output = NULL;
333    }
334
335    if (alh->input.config != NULL) {
336        alFreeConfig(alh->input.config);
337        alh->input.config = NULL;
338    }
339
340    if (alh->input.port != NULL) {
341        alClosePort(alh->input.port);
342        alh->input.port = NULL;
343    }
344
345    if (alh != NULL) {
346        free(alh);
347        alh = NULL;
348    }
349}
350
351int set_dmediachannel(UNUSED(void *handle),
352                      UNUSED(ALuint channel),
353                      UNUSED(ALfloat volume))
354{
355    return 0;
356}
357
358void pause_dmedia(void *handle)
359{
360    _ALhandle *alh = (_ALhandle *)handle;
361    ALpv params;
362    int i;
363
364    params.param = AL_RATE;
365    params.value.i = 0;
366    alSetParams(alh->input.device, &params, 1);
367
368    for (i=0; i < alh->numOutputPorts; i++)
369        alSetParams(alh->output[i].device, &params, 1);
370
371    if (alh->numOutputPorts > 1)
372        sync_ports(handle);
373
374    return;
375}
376
377void resume_dmedia(void *handle)
378{
379    _ALhandle *alh = (_ALhandle *)handle;
380    ALpv params;
381    int i;
382
383    if (alh->numOutputPorts > 1)
384        sync_ports(handle);
385
386    params.param = AL_INPUT_RATE;
387    params.value.i = alh->input.sampleRate;
388    alSetParams(alh->input.device, &params, 1);
389
390    params.param = AL_OUTPUT_RATE;
391    params.value.i = alh->output[0].sampleRate;
392    for (i=0; i < alh->numOutputPorts; i++)
393        alSetParams(alh->output[i].device, &params, 1);
394
395    return;
396}
397
398ALfloat get_dmediachannel(UNUSED(void *handle), UNUSED(ALuint channel))
399{
400    return 0.0;
401}
402
403/* capture data from the audio device */
404ALsizei capture_dmedia(UNUSED(void *handle),
405                             UNUSED(void *capture_buffer),
406                             UNUSED(int bufsiz))
407{
408    return 0;
409}
410
411static ALboolean set_write_dmedia(void *handle,
412                                  unsigned int *bufsiz,
413                                  ALenum *fmt,
414                                  unsigned int *speed)
415{
416    _ALhandle *alh = (_ALhandle *)handle;
417    ALuint channels = _alGetChannelsFromFormat(*fmt);
418    unsigned int data_format;
419    ALpv params[2];
420    int i, result;
421
422    if (alh->output[0]._numChannels_avail >= channels)
423        alh->numOutputPorts = 1;
424
425    switch(*fmt) {
426    case AL_FORMAT_MONO8:
427    case AL_FORMAT_STEREO8:
428    case AL_FORMAT_QUAD8_LOKI:
429        data_format = AL_SAMPLE_8;
430        alh->output[0].sampleWidth = 8;
431        break;
432    case AL_FORMAT_MONO16:
433    case AL_FORMAT_STEREO16:
434    case AL_FORMAT_QUAD16_LOKI:
435        data_format = AL_SAMPLE_16;
436        alh->output[0].sampleWidth = 16;
437        break;
438    default:
439        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
440                 "Unsuported audio format: %d\n", *fmt);
441        return AL_FALSE;
442    }
443
444    /*
445     * Change the playback sample rate
446     */
447    alh->output[0].sampleRate = *speed;
448    params[0].param = AL_MASTER_CLOCK;
449    params[0].value.i = AL_MCLK_TYPE;
450    params[1].param = AL_RATE;
451    params[1].value.i = alh->output[0].sampleRate;
452    for(i=0; i < alh->numOutputPorts; i++)
453        result = alSetParams(alh->output[i].device, params, 2);
454
455
456    if (alh->output[0].config != NULL)
457    {
458        alFreeConfig(alh->output[0].config);
459        alh->output[0].config = alNewConfig();
460        if (alh->output[0].config == NULL)
461        {
462            _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
463                     alGetErrorString(oserror()));
464            return AL_FALSE;
465        }
466    }
467
468
469    if (alh->output[0].port == NULL)    /* Only if port not initialized */
470    {
471        alh->output[0].numChannels = channels/alh->numOutputPorts;
472        if (alh->output[0].numChannels <= 1)
473            alh->output[0].numChannels = 2;
474
475        for (i=0; i < alh->numOutputPorts; i++)
476        {
477            params[0].param = AL_CHANNEL_MODE;
478            params[0].value.i = alh->output[0].numChannels;
479            alSetParams(alh->output[i].device,params, 1);
480        }
481
482        /*
483         * Change the size of the audio queue
484         * and the number of audio channels.
485         */
486        result = alSetChannels(alh->output[0].config, alh->output[0].numChannels);
487        alSetQueueSize(alh->output[0].config, alh->output[0].sampleRate*alh->output[0].numChannels/10);
488    }
489
490    alSetSampFmt(alh->output[0].config, AL_SAMPFMT_TWOSCOMP);
491    alSetWidth(alh->output[0].config, data_format);
492
493    /*
494     * Alter configuration parameters, if possible
495     */
496    if (alh->output[0].port == NULL)
497    {
498
499        for (i=0; i < alh->numOutputPorts; i++)
500        {
501            alSetDevice(alh->output[0].config, alh->output[i].device);
502            /*
503             * Now attempt to open an audio output port using this config
504             */
505            alh->output[i].port = alOpenPort(alh->output[i].name, "w",
506                                             alh->output[0].config);
507
508            if (alh->output[i].port == NULL)
509            {
510                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
511                         alGetErrorString(oserror()));
512                return AL_FALSE;
513            }
514        }
515    }
516    else
517    {
518
519        for (i=0; i < alh->numOutputPorts; i++)
520        {
521            alSetDevice(alh->output[0].config, alh->output[i].device);
522            result = alSetConfig(alh->output[i].port, alh->output[0].config);
523            if (result == NULL)
524            {
525                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
526                         alGetErrorString(oserror()));
527                return AL_FALSE;
528            }
529        }
530    }
531
532    if (alh->numOutputPorts > 1)
533        sync_ports(alh);
534
535    return AL_TRUE;
536}
537
538static ALboolean set_read_dmedia(UNUSED(void *handle),
539                                 UNUSED(unsigned int *bufsiz),
540                                 UNUSED(ALenum *fmt),
541                                 UNUSED(unsigned int *speed))
542{
543    return AL_FALSE;
544}
545
546ALboolean
547alcBackendSetAttributesDMedia_(ALC_OpenMode mode, void *handle, ALuint *bufsiz, ALenum *fmt, ALuint *speed)
548{
549        return mode == ALC_OPEN_INPUT_ ?
550                set_read_dmedia(handle, bufsiz, fmt, speed) :
551                set_write_dmedia(handle, bufsiz, fmt, speed);
552}
553
554/*
555 * Local function definitions
556 */
557static void check_sync(void *handle)
558{
559    _ALhandle *alh = (_ALhandle *)handle;
560    stamp_t msc[MAX_DEVICES];
561    int need_sync = 0;
562    stamp_t buf_delta_msc;
563    stamp_t ref_delta_msc;
564    int i;
565
566    if (alh->numOutputPorts == 1)
567        return;
568
569    /*
570     * Get the sample frame number associated with
571     * each port.
572     */
573    for (i = 0; i < alh->numOutputPorts; i++) {
574        alGetFrameNumber(alh->output[i].port, &msc[i]);
575    }
576
577    ref_delta_msc = msc[0];
578
579    for (i = 0; i < alh->numOutputPorts; i++) {
580        /*
581         * For each port, see how far ahead or behind
582         * the first port we are, and keep track of the
583         * maximum. in a moment, we'll bring all the ports to this
584         * number.
585         */
586        buf_delta_msc = msc[i] - alh->output[i].offset;
587        if (abs(buf_delta_msc - ref_delta_msc) > MAX_DELTA) {
588            need_sync++;
589        }
590    }
591
592    if (need_sync) {
593        sync_ports(handle);
594    }
595}
596
597static void sync_ports(void *handle)
598{
599    _ALhandle *alh = (_ALhandle *)handle;
600    double nanosec_per_frame = (1000000000.0)/alh->output[0].sampleRate;
601    stamp_t buf_delta_msc;
602    stamp_t msc[MAX_DEVICES];
603    stamp_t ust[MAX_DEVICES];
604    int corrected;
605    int i;
606
607    /*
608     * Get UST/MSC (time & sample frame number) pairs for the
609     * device associated with each port.
610     */
611    for (i = 0; i < alh->numOutputPorts; i++) {
612        alGetFrameTime(alh->output[i].port, &msc[i], &ust[i]);
613    }
614
615    /*
616     * We consider the first device to be the "master" and we
617     * calculate, for each other device, the sample frame number
618     * on that device which has the same time as ust[0] (the time
619     * at which sample frame msc[0] went out on device 0).
620     *
621     * From that, we calculate the sample frame offset between
622     * contemporaneous samples on the two devices. This remains
623     * constant as long as the devices don't drift.
624     *
625     * If the port i is connected to the same device as port 0, you should
626     * see offset[i] = 0.
627     */
628
629    /* alh->output[0].offset = 0;         / * by definition */
630    for (i = 0; i < alh->numOutputPorts; i++) {
631        stamp_t msc0 = 
632           msc[i] + (stamp_t)((double) (ust[0] - ust[i]) / (nanosec_per_frame));
633        alh->output[i].offset = msc0 - msc[0];
634    }
635
636    do {
637        stamp_t max_delta_msc;
638        corrected = 0;
639
640        /*
641         * Now get the sample frame number associated with
642         * each port.
643         */
644        for (i = 0; i < alh->numOutputPorts; i++) {
645            alGetFrameNumber(alh->output[i].port, &msc[i]);
646        }
647
648        max_delta_msc = msc[0];
649        for (i = 0; i < alh->numOutputPorts; i++) {
650            /*
651             * For each port, see how far ahead or behind
652             * the furthest port we are, and keep track of the
653             * minimum. in a moment, we'll bring all the ports to this
654             * number.
655             */
656            buf_delta_msc = msc[i] - alh->output[i].offset;
657            if (max_delta_msc < buf_delta_msc) {
658                max_delta_msc = buf_delta_msc;
659            }
660        }
661
662        for (i = 0; i < alh->numOutputPorts; i++) {
663            buf_delta_msc = msc[i] - alh->output[i].offset;
664            if (abs(buf_delta_msc - max_delta_msc) > MAX_DELTA ) {
665                alDiscardFrames(alh->output[i].port,
666                                (int)(max_delta_msc-buf_delta_msc));
667                corrected++;
668            }
669        }
670    } while (corrected);
671}
672
673static int grab_device_byname(const char *name)
674{
675    int device = 0;
676    Rcvar rcv;
677
678    if ((rcv = rc_lookup(name)) != NULL)
679    {
680        char buf[BUF_SIZE];
681        ALpv params[2];
682
683        rc_tostr0(rcv, buf, BUF_SIZE);
684        device = alGetResourceByName(AL_SYSTEM, buf, AL_DEVICE_TYPE);
685        if (device)
686        {
687            int res;
688            /*
689             * if the name refers to an interface, select that interface
690             * on the device
691             */
692            if (res = alGetResourceByName(AL_SYSTEM, buf, AL_INTERFACE_TYPE))
693            {
694                params[0].param = AL_INTERFACE;
695                params[0].value.i = res;
696                alSetParams(device, params, 1);
697            }
698        }
699        else
700        {
701            device = -1;
702            _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
703                     alGetErrorString(oserror()));
704        }
705
706    }
707
708    return device;
709}
Note: See TracBrowser for help on using the repository browser.