This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_alsa_audio.c
622 lines (550 loc) · 18.1 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2004 Sam Lantinga
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
20
slouken@libsdl.org
21
*/
22
#include "SDL_config.h"
23
24
25
26
/* Allow access to a raw mixing buffer */
#include <sys/types.h>
27
#include <signal.h> /* For kill() */
28
29
30
#include <dlfcn.h>
#include <errno.h>
#include <string.h>
31
32
#include "SDL_timer.h"
33
34
35
#include "SDL_audio.h"
#include "../SDL_audiomem.h"
#include "../SDL_audio_c.h"
36
37
#include "SDL_alsa_audio.h"
38
39
40
41
/* The tag name used by ALSA audio */
#define DRIVER_NAME "alsa"
42
/* The default ALSA audio driver */
43
#define DEFAULT_DEVICE "default"
44
45
/* Audio driver functions */
46
static int ALSA_OpenAudio(_THIS, SDL_AudioSpec * spec);
47
48
49
50
51
static void ALSA_WaitAudio(_THIS);
static void ALSA_PlayAudio(_THIS);
static Uint8 *ALSA_GetAudioBuf(_THIS);
static void ALSA_CloseAudio(_THIS);
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
static int (*ALSA_snd_pcm_open)
(snd_pcm_t **, const char *, snd_pcm_stream_t, int);
static int (*ALSA_snd_pcm_close)(snd_pcm_t * pcm);
static snd_pcm_sframes_t(*ALSA_snd_pcm_writei)
(snd_pcm_t *,const void *, snd_pcm_uframes_t);
static int (*ALSA_snd_pcm_resume)(snd_pcm_t *);
static int (*ALSA_snd_pcm_prepare)(snd_pcm_t *);
static int (*ALSA_snd_pcm_drain)(snd_pcm_t *);
static const char *(*ALSA_snd_strerror)(int);
static size_t(*ALSA_snd_pcm_hw_params_sizeof)(void);
static size_t(*ALSA_snd_pcm_sw_params_sizeof)(void);
static int (*ALSA_snd_pcm_hw_params_any)(snd_pcm_t *, snd_pcm_hw_params_t *);
static int (*ALSA_snd_pcm_hw_params_set_access)
(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
static int (*ALSA_snd_pcm_hw_params_set_format)
(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
static int (*ALSA_snd_pcm_hw_params_set_channels)
(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int);
static int (*ALSA_snd_pcm_hw_params_get_channels)(const snd_pcm_hw_params_t *);
static unsigned int (*ALSA_snd_pcm_hw_params_set_rate_near)
(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int, int *);
static snd_pcm_uframes_t (*ALSA_snd_pcm_hw_params_set_period_size_near)
(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t, int *);
static snd_pcm_sframes_t (*ALSA_snd_pcm_hw_params_get_period_size)
(const snd_pcm_hw_params_t *);
static unsigned int (*ALSA_snd_pcm_hw_params_set_periods_near)
(snd_pcm_t *,snd_pcm_hw_params_t *, unsigned int, int *);
static int (*ALSA_snd_pcm_hw_params_get_periods)(snd_pcm_hw_params_t *);
static int (*ALSA_snd_pcm_hw_params)(snd_pcm_t *, snd_pcm_hw_params_t *);
static int (*ALSA_snd_pcm_sw_params_current)(snd_pcm_t*, snd_pcm_sw_params_t*);
static int (*ALSA_snd_pcm_sw_params_set_start_threshold)
(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
static int (*ALSA_snd_pcm_sw_params_set_avail_min)
(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
static int (*ALSA_snd_pcm_sw_params)(snd_pcm_t *, snd_pcm_sw_params_t *);
static int (*ALSA_snd_pcm_nonblock)(snd_pcm_t *, int);
#define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
#define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof
92
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
93
94
static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
95
96
static void *alsa_handle = NULL;
97
98
static int
load_alsa_sym(const char *fn, void **addr)
99
{
100
101
102
103
#if HAVE_DLVSYM
*addr = dlvsym(alsa_handle, fn, "ALSA_0.9");
if (*addr == NULL)
#endif
104
{
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
*addr = dlsym(alsa_handle, fn);
if (*addr == NULL) {
return 0;
}
}
return 1;
}
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
#define SDL_ALSA_SYM(x) \
if (!load_alsa_sym(#x, (void **) (char *) &ALSA_##x)) return -1
#else
#define SDL_ALSA_SYM(x) ALSA_##x = x
#endif
static int load_alsa_syms(void)
{
SDL_ALSA_SYM(snd_pcm_open);
SDL_ALSA_SYM(snd_pcm_close);
SDL_ALSA_SYM(snd_pcm_writei);
SDL_ALSA_SYM(snd_pcm_resume);
SDL_ALSA_SYM(snd_pcm_prepare);
SDL_ALSA_SYM(snd_pcm_drain);
SDL_ALSA_SYM(snd_strerror);
SDL_ALSA_SYM(snd_pcm_hw_params_sizeof);
SDL_ALSA_SYM(snd_pcm_sw_params_sizeof);
SDL_ALSA_SYM(snd_pcm_hw_params_any);
SDL_ALSA_SYM(snd_pcm_hw_params_set_access);
SDL_ALSA_SYM(snd_pcm_hw_params_set_format);
SDL_ALSA_SYM(snd_pcm_hw_params_set_channels);
SDL_ALSA_SYM(snd_pcm_hw_params_get_channels);
SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near);
SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near);
SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size);
SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_near);
SDL_ALSA_SYM(snd_pcm_hw_params_get_periods);
SDL_ALSA_SYM(snd_pcm_hw_params);
SDL_ALSA_SYM(snd_pcm_sw_params_current);
SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold);
SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min);
SDL_ALSA_SYM(snd_pcm_sw_params);
SDL_ALSA_SYM(snd_pcm_nonblock);
return 0;
}
150
151
152
153
static void
UnloadALSALibrary(void)
{
154
if (alsa_handle != NULL) {
155
156
157
dlclose(alsa_handle);
alsa_handle = NULL;
}
158
159
}
160
161
162
163
static int
LoadALSALibrary(void)
{
int i, retval = -1;
164
165
alsa_handle = dlopen(alsa_library, RTLD_NOW);
166
167
168
169
170
171
172
if (alsa_handle == NULL) {
SDL_SetError("ALSA: dlopen('%s') failed: %s\n",
alsa_library, strerror(errno));
} else {
retval = load_alsa_syms();
if (retval < 0) {
UnloadALSALibrary();
173
174
175
}
}
return retval;
176
177
178
179
}
#else
180
181
182
183
static void
UnloadALSALibrary(void)
{
return;
184
185
}
186
187
188
static int
LoadALSALibrary(void)
{
189
load_alsa_syms();
190
return 0;
191
192
}
193
#endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
194
195
196
static const char *
get_audio_device(int channels)
197
{
198
199
200
201
202
203
204
205
206
207
208
209
const char *device;
device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
if (device == NULL) {
if (channels == 6)
device = "surround51";
else if (channels == 4)
device = "surround40";
else
device = DEFAULT_DEVICE;
}
return device;
210
211
212
213
}
/* Audio driver bootstrap functions */
214
215
static int
Audio_Available(void)
216
{
217
218
219
220
221
222
223
224
int available;
int status;
snd_pcm_t *handle;
available = 0;
if (LoadALSALibrary() < 0) {
return available;
}
225
226
status = ALSA_snd_pcm_open(&handle, get_audio_device(2),
SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
227
228
if (status >= 0) {
available = 1;
229
ALSA_snd_pcm_close(handle);
230
231
232
}
UnloadALSALibrary();
return (available);
233
234
}
235
236
static void
Audio_DeleteDevice(SDL_AudioDevice * device)
237
{
238
239
240
SDL_free(device->hidden);
SDL_free(device);
UnloadALSALibrary();
241
242
}
243
244
static SDL_AudioDevice *
Audio_CreateDevice(int devindex)
245
{
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
SDL_AudioDevice *this;
/* Initialize all variables that we clean on shutdown */
LoadALSALibrary();
this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice));
if (this) {
SDL_memset(this, 0, (sizeof *this));
this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc((sizeof *this->hidden));
}
if ((this == NULL) || (this->hidden == NULL)) {
SDL_OutOfMemory();
if (this) {
SDL_free(this);
}
return (0);
}
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
/* Set the function pointers */
this->OpenAudio = ALSA_OpenAudio;
this->WaitAudio = ALSA_WaitAudio;
this->PlayAudio = ALSA_PlayAudio;
this->GetAudioBuf = ALSA_GetAudioBuf;
this->CloseAudio = ALSA_CloseAudio;
this->free = Audio_DeleteDevice;
return this;
275
276
277
}
AudioBootStrap ALSA_bootstrap = {
278
DRIVER_NAME, "ALSA 0.9 PCM audio",
279
Audio_Available, Audio_CreateDevice, 0
280
281
282
};
/* This function waits until it is possible to write a full sound buffer */
283
284
static void
ALSA_WaitAudio(_THIS)
285
{
286
287
288
289
290
291
292
293
294
295
296
297
/* Check to see if the thread-parent process is still alive */
{
static int cnt = 0;
/* Note that this only works with thread implementations
that use a different process id for each thread.
*/
if (parent && (((++cnt) % 10) == 0)) { /* Check every 10 loops */
if (kill(parent, 0) < 0) {
this->enabled = 0;
}
}
}
298
299
}
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/*
* http://bugzilla.libsdl.org/show_bug.cgi?id=110
* "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
* and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
*/
#define SWIZ6(T) \
T *ptr = (T *) mixbuf; \
const Uint32 count = (this->spec.samples / 6); \
Uint32 i; \
for (i = 0; i < count; i++, ptr += 6) { \
T tmp; \
tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
}
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
static __inline__ void
swizzle_alsa_channels_6_64bit(_THIS)
{
SWIZ6(Uint64);
}
static __inline__ void
swizzle_alsa_channels_6_32bit(_THIS)
{
SWIZ6(Uint32);
}
static __inline__ void
swizzle_alsa_channels_6_16bit(_THIS)
{
SWIZ6(Uint16);
}
static __inline__ void
swizzle_alsa_channels_6_8bit(_THIS)
{
SWIZ6(Uint8);
}
336
337
338
339
340
341
342
343
#undef SWIZ6
/*
* Called right before feeding this->mixbuf to the hardware. Swizzle channels
* from Windows/Mac order to the format alsalib will want.
*/
344
345
static __inline__ void
swizzle_alsa_channels(_THIS)
346
347
{
if (this->spec.channels == 6) {
348
const Uint16 fmtsize = (this->spec.format & 0xFF); /* bits/channel. */
349
350
351
352
353
354
355
356
357
358
359
360
361
362
if (fmtsize == 16)
swizzle_alsa_channels_6_16bit(this);
else if (fmtsize == 8)
swizzle_alsa_channels_6_8bit(this);
else if (fmtsize == 32)
swizzle_alsa_channels_6_32bit(this);
else if (fmtsize == 64)
swizzle_alsa_channels_6_64bit(this);
}
/* !!! FIXME: update this for 7.1 if needed, later. */
}
363
364
static void
ALSA_PlayAudio(_THIS)
365
{
366
367
368
369
370
371
372
373
374
375
int status;
int sample_len;
signed short *sample_buf;
swizzle_alsa_channels(this);
sample_len = this->spec.samples;
sample_buf = (signed short *) mixbuf;
while (sample_len > 0) {
376
status = ALSA_snd_pcm_writei(pcm_handle, sample_buf, sample_len);
377
378
379
380
381
382
383
384
if (status < 0) {
if (status == -EAGAIN) {
SDL_Delay(1);
continue;
}
if (status == -ESTRPIPE) {
do {
SDL_Delay(1);
385
status = ALSA_snd_pcm_resume(pcm_handle);
386
387
388
} while (status == -EAGAIN);
}
if (status < 0) {
389
status = ALSA_snd_pcm_prepare(pcm_handle);
390
391
392
393
394
395
396
397
398
399
400
}
if (status < 0) {
/* Hmm, not much we can do - abort */
this->enabled = 0;
return;
}
continue;
}
sample_buf += status * this->spec.channels;
sample_len -= status;
}
401
402
}
403
404
static Uint8 *
ALSA_GetAudioBuf(_THIS)
405
{
406
return (mixbuf);
407
408
}
409
410
static void
ALSA_CloseAudio(_THIS)
411
{
412
413
414
415
416
if (mixbuf != NULL) {
SDL_FreeAudioMem(mixbuf);
mixbuf = NULL;
}
if (pcm_handle) {
417
418
ALSA_snd_pcm_drain(pcm_handle);
ALSA_snd_pcm_close(pcm_handle);
419
420
pcm_handle = NULL;
}
421
422
}
423
424
static int
ALSA_OpenAudio(_THIS, SDL_AudioSpec * spec)
425
{
426
427
428
429
430
int status;
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
snd_pcm_format_t format;
snd_pcm_uframes_t frames;
431
SDL_AudioFormat test_format;
432
433
434
/* Open the audio device */
/* Name of device should depend on # channels in spec */
435
436
437
status = ALSA_snd_pcm_open(&pcm_handle,
get_audio_device(spec->channels),
SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
438
439
440
if (status < 0) {
SDL_SetError("Couldn't open audio device: %s",
441
ALSA_snd_strerror(status));
442
443
444
445
446
return (-1);
}
/* Figure out what the hardware is capable of */
snd_pcm_hw_params_alloca(&hwparams);
447
status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
448
449
if (status < 0) {
SDL_SetError("Couldn't get hardware config: %s",
450
ALSA_snd_strerror(status));
451
452
453
454
455
ALSA_CloseAudio(this);
return (-1);
}
/* SDL only uses interleaved sample output */
456
457
status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams,
SND_PCM_ACCESS_RW_INTERLEAVED);
458
459
if (status < 0) {
SDL_SetError("Couldn't set interleaved access: %s",
460
ALSA_snd_strerror(status));
461
462
463
464
465
466
467
468
ALSA_CloseAudio(this);
return (-1);
}
/* Try for a closest match on audio format */
status = -1;
for (test_format = SDL_FirstAudioFormat(spec->format);
test_format && (status < 0);) {
469
status = 0; /* if we can't support a format, it'll become -1. */
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
switch (test_format) {
case AUDIO_U8:
format = SND_PCM_FORMAT_U8;
break;
case AUDIO_S8:
format = SND_PCM_FORMAT_S8;
break;
case AUDIO_S16LSB:
format = SND_PCM_FORMAT_S16_LE;
break;
case AUDIO_S16MSB:
format = SND_PCM_FORMAT_S16_BE;
break;
case AUDIO_U16LSB:
format = SND_PCM_FORMAT_U16_LE;
break;
case AUDIO_U16MSB:
format = SND_PCM_FORMAT_U16_BE;
break;
489
case AUDIO_S32LSB:
490
format = SND_PCM_FORMAT_S32_LE;
491
492
break;
case AUDIO_S32MSB:
493
format = SND_PCM_FORMAT_S32_BE;
494
495
496
497
498
499
500
break;
case AUDIO_F32LSB:
format = SND_PCM_FORMAT_FLOAT_LE;
break;
case AUDIO_F32MSB:
format = SND_PCM_FORMAT_FLOAT_BE;
break;
501
default:
502
status = -1;
503
504
break;
}
505
if (status >= 0) {
506
507
status = ALSA_snd_pcm_hw_params_set_format(pcm_handle,
hwparams, format);
508
509
510
511
512
513
514
515
516
517
518
519
520
}
if (status < 0) {
test_format = SDL_NextAudioFormat();
}
}
if (status < 0) {
SDL_SetError("Couldn't find any hardware audio formats");
ALSA_CloseAudio(this);
return (-1);
}
spec->format = test_format;
/* Set the number of channels */
521
522
status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
spec->channels);
523
if (status < 0) {
524
status = ALSA_snd_pcm_hw_params_get_channels(hwparams);
525
526
527
528
529
530
531
532
533
if ((status <= 0) || (status > 2)) {
SDL_SetError("Couldn't set audio channels");
ALSA_CloseAudio(this);
return (-1);
}
spec->channels = status;
}
/* Set the audio rate */
534
535
status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
spec->freq, NULL);
536
537
if (status < 0) {
ALSA_CloseAudio(this);
538
539
SDL_SetError("Couldn't set audio frequency: %s",
ALSA_snd_strerror(status));
540
541
542
543
544
545
return (-1);
}
spec->freq = status;
/* Set the buffer size, in samples */
frames = spec->samples;
546
547
frames = ALSA_snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams,
frames, NULL);
548
spec->samples = frames;
549
ALSA_snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, 2, NULL);
550
551
/* "set" the hardware with the desired parameters */
552
status = ALSA_snd_pcm_hw_params(pcm_handle, hwparams);
553
554
if (status < 0) {
ALSA_CloseAudio(this);
555
556
SDL_SetError("Couldn't set hardware audio parameters: %s",
ALSA_snd_strerror(status));
557
558
return (-1);
}
559
560
/* This is useful for debugging... */
561
/*
562
{ snd_pcm_sframes_t bufsize; int fragments;
563
564
bufsize = ALSA_snd_pcm_hw_params_get_period_size(hwparams);
fragments = ALSA_snd_pcm_hw_params_get_periods(hwparams);
565
566
567
568
569
fprintf(stderr, "ALSA: bufsize = %ld, fragments = %d\n", bufsize, fragments);
}
*/
570
571
/* Set the software parameters */
snd_pcm_sw_params_alloca(&swparams);
572
status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
573
574
if (status < 0) {
SDL_SetError("Couldn't get software config: %s",
575
ALSA_snd_strerror(status));
576
577
578
ALSA_CloseAudio(this);
return (-1);
}
579
status = ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle,swparams,0);
580
581
if (status < 0) {
SDL_SetError("Couldn't set start threshold: %s",
582
ALSA_snd_strerror(status));
583
584
585
ALSA_CloseAudio(this);
return (-1);
}
586
status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, frames);
587
if (status < 0) {
588
SDL_SetError("Couldn't set avail min: %s", ALSA_snd_strerror(status));
589
590
591
ALSA_CloseAudio(this);
return (-1);
}
592
status = ALSA_snd_pcm_sw_params(pcm_handle, swparams);
593
594
if (status < 0) {
SDL_SetError("Couldn't set software audio parameters: %s",
595
ALSA_snd_strerror(status));
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
ALSA_CloseAudio(this);
return (-1);
}
/* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(spec);
/* Allocate mixing buffer */
mixlen = spec->size;
mixbuf = (Uint8 *) SDL_AllocAudioMem(mixlen);
if (mixbuf == NULL) {
ALSA_CloseAudio(this);
return (-1);
}
SDL_memset(mixbuf, spec->silence, spec->size);
/* Get the parent process id (we're the parent of the audio thread) */
parent = getpid();
/* Switch to blocking mode for playback */
616
ALSA_snd_pcm_nonblock(pcm_handle, 0);
617
618
619
/* We're ready to rock and roll. :-) */
return (0);
620
}
621
622
/* vi: set ts=4 sw=4 expandtab: */