Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

added openal

File size: 23.8 KB
Line 
1/* -*- mode: C; tab-width:8; c-basic-offset:8 -*-
2 * vi:set ts=8:
3 *  alsa.c
4
5   ALSA backend for OpenAL
6
7    Dirk Ehlke
8    EMail: dehlke@mip.informatik.uni-kiel.de
9
10    Multimedia Information Processing
11    Christian-Albrechts-University of Kiel
12
13    2002/06/13
14*/
15
16
17#ifndef _SVID_SOURCE
18#define _SVID_SOURCE
19#endif /* _SVID_SOURCE */
20
21#ifndef _GNU_SOURCE
22#define _GNU_SOURCE
23#endif /* _GNU_SOURCE */
24
25#include "al_siteconfig.h"
26
27#include <AL/alext.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <sys/time.h>
31#include <sys/types.h>
32#include <unistd.h>
33
34#include "backends/alc_backend.h"
35
36#include "al_config.h"
37#include "al_debug.h"
38#include "al_main.h"
39
40#ifdef OPENAL_DLOPEN_ALSA
41#include <dlfcn.h>
42#endif
43
44#include <alsa/asoundlib.h>
45
46static void *alsa_lib_handle = NULL;
47static int (*psnd_pcm_hw_params_malloc)(snd_pcm_hw_params_t **ptr) = NULL;
48static void (*psnd_pcm_hw_params_free)(snd_pcm_hw_params_t *obj) = NULL;
49static const char *(*psnd_strerror)(int errnum) = NULL;
50static size_t (*psnd_pcm_info_sizeof)(void) = NULL;
51static int (*psnd_pcm_close)(snd_pcm_t *pcm) = NULL;
52static int (*psnd_pcm_hw_params)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) = NULL;
53static int (*psnd_pcm_hw_params_any)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) = NULL;
54static int (*psnd_pcm_hw_params_set_access)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t acc) = NULL;
55static int (*psnd_pcm_hw_params_set_buffer_size_near)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) = NULL;
56static int (*psnd_pcm_hw_params_set_channels)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val) = NULL;
57static int (*psnd_pcm_hw_params_set_format)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val) = NULL;
58static int (*psnd_pcm_hw_params_set_periods_near)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) = NULL;
59static int (*psnd_pcm_hw_params_set_rate_near)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) = NULL;
60static size_t (*psnd_pcm_hw_params_sizeof)(void) = NULL;
61static int (*psnd_pcm_prepare)(snd_pcm_t *pcm) = NULL;
62static snd_pcm_sframes_t (*psnd_pcm_readi)(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size) = NULL;
63static int (*psnd_pcm_resume)(snd_pcm_t *pcm) = NULL;
64static snd_pcm_sframes_t (*psnd_pcm_writei)(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size) = NULL;
65static int (*psnd_pcm_hw_params_set_period_size_near)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) = NULL;
66static int (*psnd_pcm_open)(snd_pcm_t **pcm, const char *name,
67                     snd_pcm_stream_t stream, int mode) = NULL;
68static int (*psnd_pcm_nonblock)(snd_pcm_t * pcm, int nonblock) = NULL;
69
70/* !!! FIXME: hhm...this is a problem. */
71#if (SND_LIB_MAJOR == 0)
72static int (*psnd_pcm_hw_params_get_buffer_size)(const snd_pcm_hw_params_t *params) = NULL;
73static int (*psnd_pcm_hw_params_get_channels)(const snd_pcm_hw_params_t *params) = NULL;
74static snd_pcm_sframes_t (*psnd_pcm_hw_params_get_period_size)(const snd_pcm_hw_params_t *params, int *dir) = NULL;
75#else
76static int (*psnd_pcm_hw_params_get_buffer_size)(const snd_pcm_hw_params_t *params,
77                                          snd_pcm_uframes_t *val) = NULL;
78static int (*psnd_pcm_hw_params_get_channels)(const snd_pcm_hw_params_t *params, unsigned int *val) = NULL;
79static int (*psnd_pcm_hw_params_get_period_size)(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir) = NULL;
80#endif
81
82static int openal_load_alsa_library(void)
83{
84#ifdef OPENAL_DLOPEN_ALSA
85        char * error = NULL;
86#endif
87   
88        if (alsa_lib_handle != NULL)
89                return 1;  /* already loaded. */
90
91        /* versioned symbol fetching macro, or NULL if no dlvsym available */
92#ifdef _GNU_SOURCE
93#define AL_DLVSYM dlvsym
94#else
95#define AL_DLVSYM(a,b,c) NULL
96#endif
97
98        /* ALSA uses symbol versioning, which is usually a good thing except
99           that it turns dlsym() into a lottery.  So, we look for known-good
100           symbol versions first before falling back to unversioned symbols. */
101        /* this is our current preferred symbol versioning order:
102           ALSA_0.9.0rc4 > (generic) */
103        #ifdef OPENAL_DLOPEN_ALSA
104                #define OPENAL_LOAD_ALSA_SYMBOL(x) p##x = AL_DLVSYM(alsa_lib_handle, #x, "ALSA_0.9.0rc4"); \
105                                                   error = dlerror(); \
106                                                   if ((error != NULL)||(p##x == NULL)) { \
107                                                           p##x = dlsym(alsa_lib_handle, #x); \
108                                                           error = dlerror(); \
109                                                           if ((error != NULL)||(p##x == NULL)) { \
110                                                                   fprintf(stderr,"Could not resolve ALSA symbol %s: %s\n", #x, ((error!=NULL)?(error):("(null)"))); \
111                                                                   dlclose(alsa_lib_handle); alsa_lib_handle = NULL; \
112                                                                   return 0; } else _alDebug(ALD_MAXIMUS, __FILE__, __LINE__, "got %s", #x); \
113                                                   } else _alDebug(ALD_MAXIMUS, __FILE__, __LINE__, "got %s", #x "@ALSA_0.9rc4");
114                dlerror(); /* clear error state */
115                alsa_lib_handle = dlopen("libasound.so.2", RTLD_LAZY | RTLD_GLOBAL);
116                error = dlerror();
117                if (alsa_lib_handle == NULL) {
118                        fprintf(stderr,"Could not open ALSA library: %s\n",((error!=NULL)?(error):("(null)")));
119                        return 0;
120                }
121        #else
122                #define OPENAL_LOAD_ALSA_SYMBOL(x) p##x = x;
123                alsa_lib_handle = (void *) 0xF00DF00D;
124        #endif
125
126        OPENAL_LOAD_ALSA_SYMBOL(snd_strerror);
127        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_info_sizeof);
128        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_malloc);
129        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_free);
130        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_open);
131        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_close);
132        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params);
133        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_any);
134        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_get_buffer_size);
135        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_get_channels);
136        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_get_period_size);
137        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_set_access);
138        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_set_buffer_size_near);
139        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_set_channels);
140        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_set_format);
141        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_set_period_size_near);
142        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_set_periods_near);
143        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_set_rate_near);
144        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_hw_params_sizeof);
145        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_open);
146        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_nonblock);
147        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_prepare);
148        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_readi);
149        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_resume);
150        OPENAL_LOAD_ALSA_SYMBOL(snd_pcm_writei);
151
152        return 1;
153}
154
155/* alsa stuff */
156#define DEFAULT_DEVICE "plughw:0,0"
157
158/* convert from AL to ALSA format */
159static int AL2ALSAFMT(ALenum format);
160static int FRAMESIZE(snd_pcm_format_t fmt, unsigned int chans);
161
162
163/*
164 * get either the default device name or something the
165 * user specified
166 */
167static void get_in_device_name(char *retref, size_t retsize);
168static void get_out_device_name(char *retref, size_t retsize);
169
170struct alsa_info
171{
172        snd_pcm_t *handle;
173        snd_pcm_format_t format;
174        unsigned int speed;
175        unsigned int channels;
176        unsigned int framesize;
177        unsigned int periods;
178        snd_pcm_uframes_t bufframesize;
179        fd_set fd_set;
180        int setup_read, setup_write;
181};
182
183void release_alsa(void *handle)
184{
185        struct alsa_info *ai = handle;
186        if(handle == NULL)
187          return;
188        psnd_pcm_close(ai->handle);
189        free(ai);
190}
191
192static void *grab_read_alsa( void )
193{
194        struct alsa_info *retval;
195        snd_pcm_t *handle;
196        char card_name[256];
197        int err;
198
199        if (!openal_load_alsa_library())
200                return NULL;
201
202        get_in_device_name(card_name, 256);
203
204        err = psnd_pcm_open(&handle, card_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
205        if(err < 0)
206        {
207                const char *serr = psnd_strerror(err);
208
209                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
210                                "grab_alsa: init failed: %s", serr);
211
212                return NULL;
213        }
214
215        retval = malloc(sizeof *retval);
216        retval->handle      = handle;
217        retval->format      = 0;
218        retval->channels    = 0;
219        retval->speed       = 0;
220        retval->framesize   = 0;
221        retval->bufframesize= 0;
222        retval->periods     = 0;
223        retval->setup_read      = 0;
224        retval->setup_write     = 0;
225
226        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
227                        "grab_alsa: init ok, using %s", card_name);
228
229        return retval;
230}
231
232static void get_out_device_name(char *retref, size_t retsize)
233{
234        Rcvar rcv;
235
236        assert(retref);
237
238        if (!(rcv = rc_lookup("alsa-device")))
239                rcv = rc_lookup("alsa-out-device");
240        if (rcv != NULL)
241        {
242                if(rc_type(rcv) == ALRC_STRING)
243                {
244                        rc_tostr0(rcv, retref, retsize);
245                        return;
246
247                }
248        }
249
250        strncpy(retref, DEFAULT_DEVICE, retsize);
251        retref[retsize - 1] = '\0';
252}
253
254static void get_in_device_name(char *retref, size_t retsize)
255{
256        Rcvar rcv;
257
258        assert(retref);
259
260        if (!(rcv = rc_lookup("alsa-in-device")))
261                rcv = rc_lookup("alsa-device");
262        if (rcv != NULL)
263        {
264                if(rc_type(rcv) == ALRC_STRING)
265                {
266                        rc_tostr0(rcv, retref, retsize);
267                        return;
268
269                }
270        }
271
272        strncpy(retref, DEFAULT_DEVICE, retsize);
273        retref[retsize - 1] = '\0';
274}
275
276static void *grab_write_alsa( void )
277{
278        struct alsa_info *retval;
279        snd_pcm_t *handle;
280        char card_name[256];
281        int err;
282
283        if (!openal_load_alsa_library())
284                return NULL;
285
286        get_out_device_name(card_name, 256);
287
288        /* Try to open the device without blocking, so we can
289         * try other backends even if this would block.
290         */
291        err = psnd_pcm_open(&handle, card_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
292        if(err < 0)
293        {
294                const char *serr = psnd_strerror(err);
295
296                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
297                        "grab_alsa: init failed: %s", serr);
298
299                return NULL;
300        }
301
302        /* Now that we have successfully opened the device,
303         * we can put it into blocking mode.
304         */
305        err = psnd_pcm_nonblock(handle,0);
306        if(err < 0)
307        {
308                const char *serr = psnd_strerror(err);
309
310                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
311                        "grab_alsa: could not put device into blocking mode: %s", serr);
312        }
313       
314        retval = malloc(sizeof *retval);
315        retval->handle      = handle;
316        retval->format      = 0;
317        retval->channels    = 0;
318        retval->speed       = 0;
319        retval->framesize   = 0;
320        retval->bufframesize= 0;
321        retval->periods     = 0;
322        retval->setup_read      = 0;
323        retval->setup_write     = 0;
324
325        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
326                 "grab_alsa: init ok, using %s", card_name);
327
328        return retval;
329}
330
331void *
332alcBackendOpenALSA_( ALC_OpenMode mode )
333{
334        return mode == ALC_OPEN_INPUT_ ? grab_read_alsa() : grab_write_alsa();
335}
336
337static ALboolean set_read_alsa( void *handle,
338                                ALuint *bufsiz,
339                                ALenum *fmt,
340                                ALuint *speed)
341{
342        struct alsa_info *ai = handle;
343        snd_pcm_hw_params_t *setup;
344        snd_pcm_uframes_t buffer_size, period_size;
345        snd_pcm_t *phandle = 0;
346        int err, dir;
347
348        if( (ai == NULL) || (ai->handle == NULL) )
349                return AL_FALSE;
350
351
352        if ((*fmt == AL_FORMAT_QUAD8_LOKI) || (*fmt == AL_FORMAT_STEREO8))
353                *fmt = AL_FORMAT_MONO8;
354        if ((*fmt == AL_FORMAT_QUAD16_LOKI) || (*fmt == AL_FORMAT_STEREO16))
355                *fmt = AL_FORMAT_MONO16;
356
357        ai->channels    = 1;
358        ai->format      = (unsigned int) AL2ALSAFMT(*fmt);
359        ai->speed       = (unsigned int) *speed;
360        ai->framesize   = (unsigned int) FRAMESIZE(ai->format, ai->channels);
361        ai->bufframesize= (snd_pcm_uframes_t) 8192;
362        ai->periods     = 2;
363
364        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
365                        "alsa info (read): channels: %u, format: %u, speed: %u, framesize: %u, bufframesize: %lu,periods: %u",
366                        ai->channels, ai->format, ai->speed, ai->framesize, ai->bufframesize, ai->periods);
367
368        phandle = ai->handle;
369
370        psnd_pcm_hw_params_malloc(&setup);
371        err = psnd_pcm_hw_params_any(phandle, setup);
372        if(err < 0)
373        {
374                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
375                                "set_read_alsa: Could not query parameters: %s",psnd_strerror(err));
376
377                psnd_pcm_hw_params_free(setup);
378                return AL_FALSE;
379        }
380
381        /* set the interleaved read format */
382        err = psnd_pcm_hw_params_set_access(phandle, setup, SND_PCM_ACCESS_RW_INTERLEAVED);
383        if (err < 0) {
384                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
385                                "set_read_alsa: Could not set access type: %s",psnd_strerror(err));
386                psnd_pcm_hw_params_free(setup);
387                return AL_FALSE;
388        }
389
390        /* set format */
391        err = psnd_pcm_hw_params_set_format(phandle, setup, ai->format);
392        if(err < 0)
393        {
394                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
395                                "set_read_alsa: could not set format: %s",psnd_strerror(err));
396
397                psnd_pcm_hw_params_free(setup);
398                return AL_FALSE;
399        }
400
401
402        /* channels */
403        err = psnd_pcm_hw_params_set_channels(phandle, setup, ai->channels);
404        if(err < 0)
405        {
406                unsigned int ch;
407#if (SND_LIB_MAJOR == 0)
408                ch = err = psnd_pcm_hw_params_get_channels(setup);
409#else
410                err = psnd_pcm_hw_params_get_channels(setup, &ch);
411#endif
412
413                if (ch != ai->channels) {
414                        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
415                                        "set_read_alsa: could not set channels: %s",psnd_strerror(err));
416
417                        psnd_pcm_hw_params_free(setup);
418                        return AL_FALSE;
419                }
420        }
421
422
423        /* sampling rate */
424        err = psnd_pcm_hw_params_set_rate_near(phandle, setup, &ai->speed, NULL);
425        if(err < 0)
426        {
427                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
428                                "set_read_alsa: could not set speed: %s",psnd_strerror(err));
429
430                psnd_pcm_hw_params_free(setup);
431                return AL_FALSE;
432        }
433
434        /* Set number of periods. Periods used to be called fragments. */
435        { snd_pcm_uframes_t val = 4096;
436        err = psnd_pcm_hw_params_set_period_size_near(phandle, setup, &val, NULL);
437        if (err < 0) {
438                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
439                                "set_read_alsa: %s", psnd_strerror(err));
440                psnd_pcm_hw_params_free(setup);
441                return AL_FALSE;
442        }
443        }
444
445        err = psnd_pcm_hw_params_set_periods_near(phandle, setup, &ai->periods, 0);
446        if (err < 0) {
447                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
448                                "set_read_alsa: %s", psnd_strerror(err));
449                psnd_pcm_hw_params_free(setup);
450                return AL_FALSE;
451        }
452
453        err = psnd_pcm_hw_params_set_buffer_size_near(phandle, setup, &ai->bufframesize);
454        if (err < 0) {
455                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
456                                "set_read_alsa: %s, size: %lu, speed: %d",
457                                psnd_strerror(err), ai->bufframesize, ai->speed);
458                psnd_pcm_hw_params_free(setup);
459                return AL_FALSE;
460        }
461
462#if (SND_LIB_MAJOR == 0)
463        buffer_size = psnd_pcm_hw_params_get_buffer_size(setup);
464        period_size = psnd_pcm_hw_params_get_period_size(setup, &dir);
465#else
466        psnd_pcm_hw_params_get_buffer_size(setup, &buffer_size);
467        psnd_pcm_hw_params_get_period_size(setup, &period_size, &dir);
468#endif
469
470        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
471                        "set_read_alsa (info): Buffersize = %lu (%u)",buffer_size, *bufsiz);
472        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
473                        "set_read_alsa (info): Periodsize = %lu", period_size);
474        *bufsiz = buffer_size * ai->framesize;
475
476        err = psnd_pcm_hw_params(phandle, setup);
477        if(err < 0)
478        {
479                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
480                                "set_read_alsa: %s", psnd_strerror(err));
481                psnd_pcm_hw_params_free(setup);
482                return AL_FALSE;
483        }
484
485        err = psnd_pcm_prepare(phandle);
486        if(err < 0)
487        {
488                _alDebug(ALD_MAXIMUS,  __FILE__, __LINE__,
489                                "set_read_alsa %s", psnd_strerror(err));
490                psnd_pcm_hw_params_free(setup);
491                return AL_FALSE;
492        }
493
494        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
495                 "set_read_alsa: handle: %p, phandle: %p", handle, (void*)phandle);
496        ai->setup_read = 1;
497
498        psnd_pcm_hw_params_free(setup);
499        return AL_TRUE;
500}
501
502static ALboolean set_write_alsa(void *handle,
503                                ALuint *bufsiz,
504                                ALenum *fmt,
505                                ALuint *speed)
506{
507        struct alsa_info *ai = handle;
508        snd_pcm_hw_params_t *setup;
509        snd_pcm_uframes_t buffer_size, period_size;
510        snd_pcm_t *phandle = 0;
511        int err, dir;
512
513        if( (ai == NULL) || (ai->handle == NULL) )
514                return AL_FALSE;
515
516        ai->channels    = (unsigned int) _alGetChannelsFromFormat(*fmt);
517        ai->format      = (unsigned int) AL2ALSAFMT(*fmt);
518        ai->speed       = (unsigned int) *speed;
519        ai->framesize   = (unsigned int) FRAMESIZE(ai->format, ai->channels);
520        ai->bufframesize= (snd_pcm_uframes_t) *bufsiz / ai->framesize * 4;
521        ai->periods     = 2;
522
523        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
524                        "alsa info (write): channels: %u, format: %u, speed: %u, framesize: %u, bufframesize: %lu, periods: %u",
525                        ai->channels, ai->format, ai->speed, ai->framesize, ai->bufframesize, ai->periods);
526
527        phandle = ai->handle;
528
529        psnd_pcm_hw_params_malloc(&setup);
530        err = psnd_pcm_hw_params_any(phandle, setup);
531        if(err < 0)
532        {
533                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
534                                "set_write_alsa: Could not query parameters: %s",psnd_strerror(err));
535
536                psnd_pcm_hw_params_free(setup);
537                return AL_FALSE;
538        }
539
540        /* set the interleaved write format */
541        err = psnd_pcm_hw_params_set_access(phandle, setup, SND_PCM_ACCESS_RW_INTERLEAVED);
542        if (err < 0) {
543                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
544                                "set_write_alsa: Could not set access type: %s",psnd_strerror(err));
545                psnd_pcm_hw_params_free(setup);
546                return AL_FALSE;
547        }
548
549        /* set format */
550        err = psnd_pcm_hw_params_set_format(phandle, setup, ai->format);
551        if(err < 0)
552        {
553                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
554                                "set_write_alsa: could not set format: %s",psnd_strerror(err));
555
556                psnd_pcm_hw_params_free(setup);
557                return AL_FALSE;
558        }
559
560
561        /* channels */
562        err = psnd_pcm_hw_params_set_channels(phandle, setup, ai->channels);
563        if(err < 0)
564        {
565                unsigned int ch;
566#if (SND_LIB_MAJOR == 0)
567                ch = err = psnd_pcm_hw_params_get_channels(setup);
568#else
569                err = psnd_pcm_hw_params_get_channels(setup, &ch);
570#endif
571
572                if (ch != ai->channels) {
573                        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
574                                        "set_write_alsa: could not set channels: %s",psnd_strerror(err));
575
576                        psnd_pcm_hw_params_free(setup);
577                        return AL_FALSE;
578                }
579        }
580
581
582        /* sampling rate */
583        err = psnd_pcm_hw_params_set_rate_near(phandle, setup, &ai->speed,
584                                               NULL);
585        if(err < 0) {
586                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
587                         "set_write_alsa: could not set speed: %s",
588                         psnd_strerror(err));
589                psnd_pcm_hw_params_free(setup);
590                return AL_FALSE;
591        } else if (err > 0) {
592                /* sampling rate is in 'err' */
593                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
594                         "set_write_alsa: alsa speed returned is %u rather than %u",
595                         (unsigned int) err, ai->speed);
596                ai->speed = (unsigned int) err;
597                if (ai->speed > 200000) {
598                        /* This can happen, and is the precursor to other Bad
599                           Things.  It usually means we dlsym()d a crappy
600                           interface version instead of known-good one, and
601                           our list of preferential interfaces may need
602                           updating. */
603                        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
604                                 "set_write_alsa: hw speed %u not sane.  failing.", ai->speed);
605                        psnd_pcm_hw_params_free(setup);
606                        return AL_FALSE; 
607                }
608        }
609
610
611        /* Set number of periods. Periods used to be called fragments. */
612        err = psnd_pcm_hw_params_set_periods_near(phandle, setup, &ai->periods, 0);
613        if (err < 0) {
614                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
615                                "set_write_alsa: %s", psnd_strerror(err));
616                psnd_pcm_hw_params_free(setup);
617                return AL_FALSE;
618        }
619
620        err = psnd_pcm_hw_params_set_buffer_size_near(phandle, setup, &ai->bufframesize);
621        if (err < 0) {
622                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
623                                "set_write_alsa: %s, size: %lu, speed: %d",
624                                psnd_strerror(err), ai->bufframesize, ai->speed);
625                psnd_pcm_hw_params_free(setup);
626                return AL_FALSE;
627        }
628
629#if (SND_LIB_MAJOR == 0)
630        buffer_size = psnd_pcm_hw_params_get_buffer_size(setup);
631        period_size = psnd_pcm_hw_params_get_period_size(setup, &dir);
632#else
633        psnd_pcm_hw_params_get_buffer_size(setup, &buffer_size);
634        psnd_pcm_hw_params_get_period_size(setup, &period_size, &dir);
635#endif
636
637        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
638                        "set_write_alsa (info): Buffersize = %lu (%u)",buffer_size, *bufsiz);
639        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
640                        "set_write_alsa (info): Periodsize = %lu", period_size);
641
642        err = psnd_pcm_hw_params(phandle, setup);
643        if(err < 0)
644        {
645                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
646                                "set_alsa: %s", psnd_strerror(err));
647                psnd_pcm_hw_params_free(setup);
648                return AL_FALSE;
649        }
650
651        err = psnd_pcm_prepare(phandle);
652        if(err < 0)
653        {
654                _alDebug(ALD_MAXIMUS,  __FILE__, __LINE__,
655                                "set_alsa %s", psnd_strerror(err));
656                psnd_pcm_hw_params_free(setup);
657                return AL_FALSE;
658        }
659
660        _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
661                 "set_write_alsa: handle: %p, phandle: %p", handle, (void*)phandle);
662        ai->setup_write = 1;
663
664        psnd_pcm_hw_params_free(setup);
665        return AL_TRUE;
666}
667
668ALboolean
669alcBackendSetAttributesALSA_( ALC_OpenMode mode, void *handle, ALuint *bufsiz, ALenum *fmt, ALuint *speed)
670{
671        return mode == ALC_OPEN_INPUT_ ?
672                set_read_alsa(handle, bufsiz, fmt, speed) :
673                set_write_alsa(handle, bufsiz, fmt, speed);
674}
675
676void alsa_blitbuffer(void *handle, void *data, int bytes)
677{
678        struct alsa_info *ai = handle;
679        snd_pcm_t *phandle = 0;
680        char *pdata = data;
681        int data_len = bytes;
682        int channels = 0;
683        int err;
684        snd_pcm_uframes_t frames;
685
686        if((ai == NULL) || (ai->handle == NULL) || (!ai->setup_write))
687        {
688                return;
689        }
690
691        phandle = ai->handle;
692        channels= ai->channels;
693        frames  = (snd_pcm_uframes_t) bytes / ai->framesize;
694
695        while(data_len > 0)
696        {
697                err = psnd_pcm_writei(phandle, pdata, frames);
698                switch(err)
699                {
700                        case -EAGAIN:
701                                continue;
702                                break;
703                        case -ESTRPIPE:
704                                do
705                                {
706                                        err = psnd_pcm_resume(phandle);
707                                } while ( err == -EAGAIN );
708                                break;
709                        case -EPIPE:
710                                break;
711                        default:
712                                if (err<0) {
713                                        fprintf(stderr,"alsa_blitbuffer: Could not write audio data to sound device: %s\n",
714                                                psnd_strerror(err));
715                                        break;
716                                }
717                                pdata += err * ai->framesize;
718                                data_len -= err * ai->framesize;
719                                frames -= err;
720                                break;
721                }
722                if(err < 0)
723                {
724                        err = psnd_pcm_prepare(phandle);
725                        if(err < 0)
726                        {
727                                const char *serr = psnd_strerror(err);
728
729                                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
730                                 "alsa_blitbuffer: %s", serr);
731
732                                return;
733                        }
734                }
735        }
736
737        return;
738}
739
740/* capture data from the audio device */
741ALsizei capture_alsa(void *handle,
742                void *capture_buffer,
743                int bufsize)
744{
745        struct alsa_info *ai = handle;
746        snd_pcm_t *phandle = 0;
747        char *pdata = capture_buffer;
748        int ret;
749        snd_pcm_uframes_t frames;
750
751        if ((ai == NULL) || (ai->handle == NULL) || (!ai->setup_read))
752                return 0;
753
754        phandle = ai->handle;
755        frames = (snd_pcm_uframes_t) bufsize / ai->framesize;
756
757grab:
758        ret = psnd_pcm_readi (phandle, pdata, frames);
759        if (ret < 0) {
760                if (ret == -EAGAIN)
761                        return 0;
762                else if (ret == -EPIPE) {
763                        fprintf(stderr, "Error, overrun occurred, trying to recover.\n");
764                        ret = psnd_pcm_prepare(phandle);
765                        if (ret < 0)
766                                fprintf(stderr, "Unable to recover: %d\n", ret);
767                        else
768                                goto grab;
769                } else {
770                        fprintf(stderr, "Unknown error occurred: %d.\n", ret);
771                }
772                return 0;
773        } else
774                return ret * ai->framesize;
775}
776
777
778static int AL2ALSAFMT(ALenum format) {
779        switch(format) {
780                case AL_FORMAT_STEREO8:     return SND_PCM_FORMAT_U8;
781                case AL_FORMAT_MONO8:       return SND_PCM_FORMAT_U8;
782                case AL_FORMAT_QUAD8_LOKI:  return SND_PCM_FORMAT_U8;
783                case AL_FORMAT_STEREO16:    return SND_PCM_FORMAT_S16;
784                case AL_FORMAT_MONO16:      return SND_PCM_FORMAT_S16;
785                case AL_FORMAT_QUAD16_LOKI: return SND_PCM_FORMAT_S16;
786
787                default: break;
788        }
789        return -1;
790}
791
792
793
794static int FRAMESIZE(snd_pcm_format_t fmt, unsigned int chans) {
795        int retval = 0;
796        switch(fmt) {
797                case SND_PCM_FORMAT_U8:
798                        retval = 1;
799                        break;
800                case SND_PCM_FORMAT_S16:
801                        retval = 2;
802                        break;
803                default:
804                        return -1;
805                        break;
806                }
807
808        return(retval*chans);
809}
810
811void
812pause_alsa( UNUSED(void *handle) )
813{
814}
815
816void
817resume_alsa( UNUSED(void *handle) )
818{
819}
820
821ALfloat
822get_alsachannel( UNUSED(void *handle), UNUSED(ALuint channel) )
823{
824        return 0.0;
825}
826
827int
828set_alsachannel( UNUSED(void *handle), UNUSED(ALuint channel), UNUSED(ALfloat volume) )
829{
830        return 0;
831}
832
Note: See TracBrowser for help on using the repository browser.