This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_alsa_audio.c
691 lines (604 loc) · 20.5 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2010 Sam Lantinga
4
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Lesser General Public
7
License as published by the Free Software Foundation; either
8
version 2.1 of the License, or (at your option) any later version.
9
10
11
12
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
13
Lesser General Public License for more details.
14
15
16
17
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19
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
#include <errno.h>
#include <string.h>
30
31
#include "SDL_timer.h"
32
33
34
#include "SDL_audio.h"
#include "../SDL_audiomem.h"
#include "../SDL_audio_c.h"
35
36
#include "SDL_alsa_audio.h"
37
38
39
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
#include "SDL_loadso.h"
#endif
40
41
42
43
/* The tag name used by ALSA audio */
#define DRIVER_NAME "alsa"
44
static int (*ALSA_snd_pcm_open)
45
46
(snd_pcm_t **, const char *, snd_pcm_stream_t, int);
static int (*ALSA_snd_pcm_close) (snd_pcm_t * pcm);
47
static snd_pcm_sframes_t(*ALSA_snd_pcm_writei)
48
(snd_pcm_t *, const void *, snd_pcm_uframes_t);
49
static int (*ALSA_snd_pcm_recover) (snd_pcm_t *, int, int);
50
51
52
53
54
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);
55
56
static void (*ALSA_snd_pcm_hw_params_copy)
(snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *);
57
static int (*ALSA_snd_pcm_hw_params_any) (snd_pcm_t *, snd_pcm_hw_params_t *);
58
static int (*ALSA_snd_pcm_hw_params_set_access)
59
(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
60
static int (*ALSA_snd_pcm_hw_params_set_format)
61
(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
62
static int (*ALSA_snd_pcm_hw_params_set_channels)
63
(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int);
64
65
static int (*ALSA_snd_pcm_hw_params_get_channels)
(const snd_pcm_hw_params_t *, unsigned int *);
66
67
68
69
static int (*ALSA_snd_pcm_hw_params_set_rate_near)
(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
static int (*ALSA_snd_pcm_hw_params_set_period_size_near)
(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
70
71
static int (*ALSA_snd_pcm_hw_params_get_period_size)
(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
72
73
static int (*ALSA_snd_pcm_hw_params_set_periods_near)
(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
74
75
76
77
78
79
static int (*ALSA_snd_pcm_hw_params_get_periods)
(const snd_pcm_hw_params_t *, unsigned int *, int *);
static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)
(snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
static int (*ALSA_snd_pcm_hw_params_get_buffer_size)
(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
80
81
82
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 *);
83
static int (*ALSA_snd_pcm_sw_params_set_start_threshold)
84
85
86
(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);
87
static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
88
89
90
#define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
#define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof
91
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
*addr = SDL_LoadFunction(alsa_handle, fn);
if (*addr == NULL) {
/* Don't call SDL_SetError(): SDL_LoadFunction already did. */
return 0;
104
105
106
107
108
109
110
111
112
113
114
115
}
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
116
117
static int
load_alsa_syms(void)
118
119
120
121
{
SDL_ALSA_SYM(snd_pcm_open);
SDL_ALSA_SYM(snd_pcm_close);
SDL_ALSA_SYM(snd_pcm_writei);
122
SDL_ALSA_SYM(snd_pcm_recover);
123
124
125
126
127
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);
128
SDL_ALSA_SYM(snd_pcm_hw_params_copy);
129
130
131
132
133
134
135
136
137
138
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);
139
140
SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near);
SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size);
141
142
143
144
145
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);
SDL_ALSA_SYM(snd_pcm_nonblock);
146
SDL_ALSA_SYM(snd_pcm_wait);
147
148
return 0;
}
149
150
151
152
#undef SDL_ALSA_SYM
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
153
154
155
156
static void
UnloadALSALibrary(void)
{
157
if (alsa_handle != NULL) {
158
SDL_UnloadObject(alsa_handle);
159
160
alsa_handle = NULL;
}
161
162
}
163
164
165
static int
LoadALSALibrary(void)
{
166
167
int retval = 0;
if (alsa_handle == NULL) {
168
alsa_handle = SDL_LoadObject(alsa_library);
169
170
if (alsa_handle == NULL) {
retval = -1;
171
/* Don't call SDL_SetError(): SDL_LoadObject already did. */
172
173
174
} else {
retval = load_alsa_syms();
if (retval < 0) {
175
176
177
178
179
UnloadALSALibrary();
}
}
}
return retval;
180
181
182
183
}
#else
184
185
186
static void
UnloadALSALibrary(void)
{
187
188
}
189
190
191
static int
LoadALSALibrary(void)
{
192
load_alsa_syms();
193
return 0;
194
195
}
196
#endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
197
198
199
static const char *
get_audio_device(int channels)
200
{
201
202
203
204
const char *device;
device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
if (device == NULL) {
205
206
207
208
209
210
211
212
213
214
215
switch (channels) {
case 6:
device = "plug:surround51";
break;
case 4:
device = "plug:surround40";
break;
default:
device = "default";
break;
}
216
217
}
return device;
218
219
220
221
}
/* This function waits until it is possible to write a full sound buffer */
222
static void
223
ALSA_WaitDevice(_THIS)
224
{
225
/* We're in blocking mode, so there's nothing to do here */
226
227
}
228
229
/* !!! FIXME: is there a channel swizzler in alsalib instead? */
230
231
232
233
234
235
/*
* 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) \
236
T *ptr = (T *) this->hidden->mixbuf; \
237
Uint32 i; \
238
for (i = 0; i < this->spec.samples; i++, ptr += 6) { \
239
240
241
242
243
T tmp; \
tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
}
244
245
246
247
248
static __inline__ void
swizzle_alsa_channels_6_64bit(_THIS)
{
SWIZ6(Uint64);
}
249
250
251
252
253
254
static __inline__ void
swizzle_alsa_channels_6_32bit(_THIS)
{
SWIZ6(Uint32);
}
255
256
257
258
259
260
static __inline__ void
swizzle_alsa_channels_6_16bit(_THIS)
{
SWIZ6(Uint16);
}
261
262
263
264
265
266
static __inline__ void
swizzle_alsa_channels_6_8bit(_THIS)
{
SWIZ6(Uint8);
}
267
268
269
270
271
#undef SWIZ6
/*
272
273
* Called right before feeding this->hidden->mixbuf to the hardware. Swizzle
* channels from Windows/Mac order to the format alsalib will want.
274
*/
275
276
static __inline__ void
swizzle_alsa_channels(_THIS)
277
278
{
if (this->spec.channels == 6) {
279
const Uint16 fmtsize = (this->spec.format & 0xFF); /* bits/channel. */
280
281
282
283
284
285
286
287
288
289
290
291
292
293
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. */
}
294
static void
295
ALSA_PlayDevice(_THIS)
296
{
297
int status;
298
299
300
301
const Uint8 *sample_buf = (const Uint8 *) this->hidden->mixbuf;
const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) *
this->spec.channels;
snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->spec.samples);
302
303
304
swizzle_alsa_channels(this);
305
306
307
while ( frames_left > 0 && this->enabled ) {
/* !!! FIXME: This works, but needs more testing before going live */
/*ALSA_snd_pcm_wait(this->hidden->pcm_handle, -1);*/
308
status = ALSA_snd_pcm_writei(this->hidden->pcm_handle,
309
sample_buf, frames_left);
310
311
312
if (status < 0) {
if (status == -EAGAIN) {
313
314
/* Apparently snd_pcm_recover() doesn't handle this case -
does it assume snd_pcm_wait() above? */
315
316
317
SDL_Delay(1);
continue;
}
318
status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
319
320
if (status < 0) {
/* Hmm, not much we can do - abort */
321
322
fprintf(stderr, "ALSA write failed (unrecoverable): %s\n",
ALSA_snd_strerror(status));
323
324
325
326
327
this->enabled = 0;
return;
}
continue;
}
328
329
sample_buf += status * frame_size;
frames_left -= status;
330
}
331
332
}
333
static Uint8 *
334
ALSA_GetDeviceBuf(_THIS)
335
{
336
return (this->hidden->mixbuf);
337
338
}
339
static void
340
ALSA_CloseDevice(_THIS)
341
{
342
343
344
345
346
347
348
349
350
351
352
353
if (this->hidden != NULL) {
if (this->hidden->mixbuf != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
}
if (this->hidden->pcm_handle) {
ALSA_snd_pcm_drain(this->hidden->pcm_handle);
ALSA_snd_pcm_close(this->hidden->pcm_handle);
this->hidden->pcm_handle = NULL;
}
SDL_free(this->hidden);
this->hidden = NULL;
354
}
355
356
}
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
static int
ALSA_finalize_hardware(_THIS, snd_pcm_hw_params_t *hwparams, int override)
{
int status;
snd_pcm_uframes_t bufsize;
/* "set" the hardware with the desired parameters */
status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
if ( status < 0 ) {
return(-1);
}
/* Get samples for the actual buffer size */
status = ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
if ( status < 0 ) {
return(-1);
}
if ( !override && bufsize != this->spec.samples * 2 ) {
return(-1);
}
/* !!! FIXME: Is this safe to do? */
this->spec.samples = bufsize / 2;
/* This is useful for debugging */
if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) {
snd_pcm_uframes_t persize = 0;
unsigned int periods = 0;
ALSA_snd_pcm_hw_params_get_period_size(hwparams, &persize, NULL);
ALSA_snd_pcm_hw_params_get_periods(hwparams, &periods, NULL);
fprintf(stderr,
"ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
persize, periods, bufsize);
}
return(0);
}
static int
ALSA_set_period_size(_THIS, snd_pcm_hw_params_t *params, int override)
{
const char *env;
int status;
snd_pcm_hw_params_t *hwparams;
snd_pcm_uframes_t frames;
unsigned int periods;
/* Copy the hardware parameters for this setup */
snd_pcm_hw_params_alloca(&hwparams);
ALSA_snd_pcm_hw_params_copy(hwparams, params);
if ( !override ) {
env = SDL_getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
if ( env ) {
override = SDL_atoi(env);
if ( override == 0 ) {
return(-1);
}
}
}
frames = this->spec.samples;
status = ALSA_snd_pcm_hw_params_set_period_size_near(
this->hidden->pcm_handle, hwparams, &frames, NULL);
if ( status < 0 ) {
return(-1);
}
periods = 2;
status = ALSA_snd_pcm_hw_params_set_periods_near(
this->hidden->pcm_handle, hwparams, &periods, NULL);
if ( status < 0 ) {
return(-1);
}
return ALSA_finalize_hardware(this, hwparams, override);
}
static int
ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params, int override)
{
const char *env;
int status;
snd_pcm_hw_params_t *hwparams;
snd_pcm_uframes_t frames;
/* Copy the hardware parameters for this setup */
snd_pcm_hw_params_alloca(&hwparams);
ALSA_snd_pcm_hw_params_copy(hwparams, params);
if ( !override ) {
env = SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
if ( env ) {
override = SDL_atoi(env);
if ( override == 0 ) {
return(-1);
}
}
}
frames = this->spec.samples * 2;
status = ALSA_snd_pcm_hw_params_set_buffer_size_near(
this->hidden->pcm_handle, hwparams, &frames);
if ( status < 0 ) {
return(-1);
}
return ALSA_finalize_hardware(this, hwparams, override);
}
469
static int
470
ALSA_OpenDevice(_THIS, const char *devname, int iscapture)
471
{
472
473
474
475
476
477
int status = 0;
snd_pcm_t *pcm_handle = NULL;
snd_pcm_hw_params_t *hwparams = NULL;
snd_pcm_sw_params_t *swparams = NULL;
snd_pcm_format_t format = 0;
SDL_AudioFormat test_format = 0;
478
479
unsigned int rate = 0;
unsigned int channels = 0;
480
481
482
/* Initialize all variables that we clean on shutdown */
this->hidden = (struct SDL_PrivateAudioData *)
483
SDL_malloc((sizeof *this->hidden));
484
485
486
487
488
if (this->hidden == NULL) {
SDL_OutOfMemory();
return 0;
}
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
489
490
491
/* Open the audio device */
/* Name of device should depend on # channels in spec */
492
493
494
status = ALSA_snd_pcm_open(&pcm_handle,
get_audio_device(this->spec.channels),
SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
495
496
if (status < 0) {
497
498
499
500
ALSA_CloseDevice(this);
SDL_SetError("ALSA: Couldn't open audio device: %s",
ALSA_snd_strerror(status));
return 0;
501
502
}
503
504
this->hidden->pcm_handle = pcm_handle;
505
506
/* Figure out what the hardware is capable of */
snd_pcm_hw_params_alloca(&hwparams);
507
status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
508
if (status < 0) {
509
510
511
512
ALSA_CloseDevice(this);
SDL_SetError("ALSA: Couldn't get hardware config: %s",
ALSA_snd_strerror(status));
return 0;
513
514
515
}
/* SDL only uses interleaved sample output */
516
517
status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams,
SND_PCM_ACCESS_RW_INTERLEAVED);
518
if (status < 0) {
519
520
521
522
ALSA_CloseDevice(this);
SDL_SetError("ALSA: Couldn't set interleaved access: %s",
ALSA_snd_strerror(status));
return 0;
523
524
525
526
}
/* Try for a closest match on audio format */
status = -1;
527
for (test_format = SDL_FirstAudioFormat(this->spec.format);
528
test_format && (status < 0);) {
529
status = 0; /* if we can't support a format, it'll become -1. */
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
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;
549
case AUDIO_S32LSB:
550
format = SND_PCM_FORMAT_S32_LE;
551
552
break;
case AUDIO_S32MSB:
553
format = SND_PCM_FORMAT_S32_BE;
554
555
556
557
558
559
560
break;
case AUDIO_F32LSB:
format = SND_PCM_FORMAT_FLOAT_LE;
break;
case AUDIO_F32MSB:
format = SND_PCM_FORMAT_FLOAT_BE;
break;
561
default:
562
status = -1;
563
564
break;
}
565
if (status >= 0) {
566
567
status = ALSA_snd_pcm_hw_params_set_format(pcm_handle,
hwparams, format);
568
569
570
571
572
573
}
if (status < 0) {
test_format = SDL_NextAudioFormat();
}
}
if (status < 0) {
574
575
576
ALSA_CloseDevice(this);
SDL_SetError("ALSA: Couldn't find any hardware audio formats");
return 0;
577
}
578
this->spec.format = test_format;
579
580
/* Set the number of channels */
581
582
status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
this->spec.channels);
583
channels = this->spec.channels;
584
if (status < 0) {
585
586
status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
if (status < 0) {
587
588
589
ALSA_CloseDevice(this);
SDL_SetError("ALSA: Couldn't set audio channels");
return 0;
590
}
591
this->spec.channels = channels;
592
593
594
}
/* Set the audio rate */
595
rate = this->spec.freq;
596
status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
597
&rate, NULL);
598
if (status < 0) {
599
600
601
602
ALSA_CloseDevice(this);
SDL_SetError("ALSA: Couldn't set audio frequency: %s",
ALSA_snd_strerror(status));
return 0;
603
}
604
this->spec.freq = rate;
605
606
/* Set the buffer size, in samples */
607
608
609
610
611
612
613
614
if ( ALSA_set_period_size(this, hwparams, 0) < 0 &&
ALSA_set_buffer_size(this, hwparams, 0) < 0 ) {
/* Failed to set desired buffer size, do the best you can... */
if ( ALSA_set_period_size(this, hwparams, 1) < 0 ) {
ALSA_CloseDevice(this);
SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
return(-1);
}
615
}
616
617
/* Set the software parameters */
snd_pcm_sw_params_alloca(&swparams);
618
status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
619
if (status < 0) {
620
621
622
623
ALSA_CloseDevice(this);
SDL_SetError("ALSA: Couldn't get software config: %s",
ALSA_snd_strerror(status));
return 0;
624
}
625
status =
626
ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
627
if (status < 0) {
628
629
630
631
ALSA_CloseDevice(this);
SDL_SetError("ALSA: Couldn't set start threshold: %s",
ALSA_snd_strerror(status));
return 0;
632
}
633
status = ALSA_snd_pcm_sw_params(pcm_handle, swparams);
634
if (status < 0) {
635
ALSA_CloseDevice(this);
636
SDL_SetError("Couldn't set software audio parameters: %s",
637
638
ALSA_snd_strerror(status));
return 0;
639
640
641
}
/* Calculate the final parameters for this audio specification */
642
SDL_CalculateAudioSpec(&this->spec);
643
644
/* Allocate mixing buffer */
645
646
647
648
649
650
this->hidden->mixlen = this->spec.size;
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
ALSA_CloseDevice(this);
SDL_OutOfMemory();
return 0;
651
}
652
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
653
654
/* Switch to blocking mode for playback */
655
ALSA_snd_pcm_nonblock(pcm_handle, 0);
656
657
/* We're ready to rock and roll. :-) */
658
659
660
661
662
663
664
return 1;
}
static void
ALSA_Deinitialize(void)
{
UnloadALSALibrary();
665
}
666
667
static int
668
ALSA_Init(SDL_AudioDriverImpl * impl)
669
670
671
672
673
674
675
676
677
678
679
680
{
if (LoadALSALibrary() < 0) {
return 0;
}
/* Set the function pointers */
impl->OpenDevice = ALSA_OpenDevice;
impl->WaitDevice = ALSA_WaitDevice;
impl->GetDeviceBuf = ALSA_GetDeviceBuf;
impl->PlayDevice = ALSA_PlayDevice;
impl->CloseDevice = ALSA_CloseDevice;
impl->Deinitialize = ALSA_Deinitialize;
681
impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: Add device enum! */
682
683
return 1; /* this audio target is available. */
684
685
686
687
}
AudioBootStrap ALSA_bootstrap = {
688
DRIVER_NAME, "ALSA PCM audio", ALSA_Init, 0
689
690
};
691
/* vi: set ts=4 sw=4 expandtab: */