/
SDL_alsa_audio.c
634 lines (550 loc) · 20.1 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2012 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
#include "SDL_timer.h"
30
31
32
#include "SDL_audio.h"
#include "../SDL_audiomem.h"
#include "../SDL_audio_c.h"
33
34
#include "SDL_alsa_audio.h"
35
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
36
37
38
39
40
41
42
#include "SDL_name.h"
#include "SDL_loadso.h"
#else
#define SDL_NAME(X) X
#endif
43
44
45
46
/* The tag name used by ALSA audio */
#define DRIVER_NAME "alsa"
/* Audio driver functions */
47
48
49
50
51
52
static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec);
static void ALSA_WaitAudio(_THIS);
static void ALSA_PlayAudio(_THIS);
static Uint8 *ALSA_GetAudioBuf(_THIS);
static void ALSA_CloseAudio(_THIS);
53
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
54
55
static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
56
57
58
59
60
61
static void *alsa_handle = NULL;
static int alsa_loaded = 0;
static int (*SDL_NAME(snd_pcm_open))(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);
static int (*SDL_NAME(snd_pcm_close))(snd_pcm_t *pcm);
static snd_pcm_sframes_t (*SDL_NAME(snd_pcm_writei))(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
62
static int (*SDL_NAME(snd_pcm_resume))(snd_pcm_t *pcm);
63
64
65
66
static int (*SDL_NAME(snd_pcm_prepare))(snd_pcm_t *pcm);
static int (*SDL_NAME(snd_pcm_drain))(snd_pcm_t *pcm);
static const char *(*SDL_NAME(snd_strerror))(int errnum);
static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void);
67
static size_t (*SDL_NAME(snd_pcm_sw_params_sizeof))(void);
68
static void (*SDL_NAME(snd_pcm_hw_params_copy))(snd_pcm_hw_params_t *dst, const snd_pcm_hw_params_t *src);
69
70
71
72
static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
static int (*SDL_NAME(snd_pcm_hw_params_set_access))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t access);
static int (*SDL_NAME(snd_pcm_hw_params_set_format))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val);
static int (*SDL_NAME(snd_pcm_hw_params_set_channels))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);
73
static int (*SDL_NAME(snd_pcm_hw_params_get_channels))(const snd_pcm_hw_params_t *params, unsigned int *val);
74
75
static int (*SDL_NAME(snd_pcm_hw_params_set_rate_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
static int (*SDL_NAME(snd_pcm_hw_params_set_period_size_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir);
76
static int (*SDL_NAME(snd_pcm_hw_params_get_period_size))(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir);
77
static int (*SDL_NAME(snd_pcm_hw_params_set_periods_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
78
static int (*SDL_NAME(snd_pcm_hw_params_get_periods))(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
79
static int (*SDL_NAME(snd_pcm_hw_params_set_buffer_size_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val);
80
static int (*SDL_NAME(snd_pcm_hw_params_get_buffer_size))(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val);
81
static int (*SDL_NAME(snd_pcm_hw_params))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
84
static int (*SDL_NAME(snd_pcm_sw_params_set_avail_min))(snd_pcm_t *pcm, snd_pcm_sw_params_t *swparams, snd_pcm_uframes_t val);
85
86
87
static int (*SDL_NAME(snd_pcm_sw_params_current))(snd_pcm_t *pcm, snd_pcm_sw_params_t *swparams);
static int (*SDL_NAME(snd_pcm_sw_params_set_start_threshold))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val);
static int (*SDL_NAME(snd_pcm_sw_params))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
88
static int (*SDL_NAME(snd_pcm_nonblock))(snd_pcm_t *pcm, int nonblock);
89
#define snd_pcm_hw_params_sizeof SDL_NAME(snd_pcm_hw_params_sizeof)
90
#define snd_pcm_sw_params_sizeof SDL_NAME(snd_pcm_sw_params_sizeof)
91
92
93
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
94
95
96
97
static struct {
const char *name;
void **func;
} alsa_functions[] = {
98
99
100
{ "snd_pcm_open", (void**)(char*)&SDL_NAME(snd_pcm_open) },
{ "snd_pcm_close", (void**)(char*)&SDL_NAME(snd_pcm_close) },
{ "snd_pcm_writei", (void**)(char*)&SDL_NAME(snd_pcm_writei) },
101
{ "snd_pcm_resume", (void**)(char*)&SDL_NAME(snd_pcm_resume) },
102
103
104
105
{ "snd_pcm_prepare", (void**)(char*)&SDL_NAME(snd_pcm_prepare) },
{ "snd_pcm_drain", (void**)(char*)&SDL_NAME(snd_pcm_drain) },
{ "snd_strerror", (void**)(char*)&SDL_NAME(snd_strerror) },
{ "snd_pcm_hw_params_sizeof", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_sizeof) },
106
{ "snd_pcm_sw_params_sizeof", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_sizeof) },
107
{ "snd_pcm_hw_params_copy", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_copy) },
108
109
110
111
112
113
114
{ "snd_pcm_hw_params_any", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_any) },
{ "snd_pcm_hw_params_set_access", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_access) },
{ "snd_pcm_hw_params_set_format", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_format) },
{ "snd_pcm_hw_params_set_channels", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_channels) },
{ "snd_pcm_hw_params_get_channels", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_channels) },
{ "snd_pcm_hw_params_set_rate_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_rate_near) },
{ "snd_pcm_hw_params_set_period_size_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_period_size_near) },
115
{ "snd_pcm_hw_params_get_period_size", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_period_size) },
116
{ "snd_pcm_hw_params_set_periods_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_periods_near) },
117
{ "snd_pcm_hw_params_get_periods", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_periods) },
118
{ "snd_pcm_hw_params_set_buffer_size_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_buffer_size_near) },
119
{ "snd_pcm_hw_params_get_buffer_size", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_buffer_size) },
120
{ "snd_pcm_hw_params", (void**)(char*)&SDL_NAME(snd_pcm_hw_params) },
121
{ "snd_pcm_sw_params_set_avail_min", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_set_avail_min) },
122
123
124
{ "snd_pcm_sw_params_current", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_current) },
{ "snd_pcm_sw_params_set_start_threshold", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_set_start_threshold) },
{ "snd_pcm_sw_params", (void**)(char*)&SDL_NAME(snd_pcm_sw_params) },
125
{ "snd_pcm_nonblock", (void**)(char*)&SDL_NAME(snd_pcm_nonblock) },
126
127
128
129
};
static void UnloadALSALibrary(void) {
if (alsa_loaded) {
130
SDL_UnloadObject(alsa_handle);
131
132
133
134
135
136
137
138
alsa_handle = NULL;
alsa_loaded = 0;
}
}
static int LoadALSALibrary(void) {
int i, retval = -1;
139
alsa_handle = SDL_LoadObject(alsa_library);
140
141
142
if (alsa_handle) {
alsa_loaded = 1;
retval = 0;
143
for (i = 0; i < SDL_arraysize(alsa_functions); i++) {
144
*alsa_functions[i].func = SDL_LoadFunction(alsa_handle,alsa_functions[i].name);
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
if (!*alsa_functions[i].func) {
retval = -1;
UnloadALSALibrary();
break;
}
}
}
return retval;
}
#else
static void UnloadALSALibrary(void) {
return;
}
static int LoadALSALibrary(void) {
return 0;
}
165
#endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
166
167
static const char *get_audio_device(int channels)
168
{
169
170
const char *device;
171
device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
172
if ( device == NULL ) {
173
174
175
176
177
178
179
180
181
182
183
switch (channels) {
case 6:
device = "plug:surround51";
break;
case 4:
device = "plug:surround40";
break;
default:
device = "default";
break;
}
184
185
}
return device;
186
187
188
189
190
191
192
}
/* Audio driver bootstrap functions */
static int Audio_Available(void)
{
int available;
193
int status;
194
195
196
snd_pcm_t *handle;
available = 0;
197
198
199
if (LoadALSALibrary() < 0) {
return available;
}
200
status = SDL_NAME(snd_pcm_open)(&handle, get_audio_device(2), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
201
202
if ( status >= 0 ) {
available = 1;
203
SDL_NAME(snd_pcm_close)(handle);
204
}
205
UnloadALSALibrary();
206
207
208
209
210
return(available);
}
static void Audio_DeleteDevice(SDL_AudioDevice *device)
{
211
212
SDL_free(device->hidden);
SDL_free(device);
213
UnloadALSALibrary();
214
215
216
217
218
219
220
}
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
{
SDL_AudioDevice *this;
/* Initialize all variables that we clean on shutdown */
221
LoadALSALibrary();
222
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
223
if ( this ) {
224
SDL_memset(this, 0, (sizeof *this));
225
this->hidden = (struct SDL_PrivateAudioData *)
226
SDL_malloc((sizeof *this->hidden));
227
228
229
230
}
if ( (this == NULL) || (this->hidden == NULL) ) {
SDL_OutOfMemory();
if ( this ) {
231
SDL_free(this);
232
233
234
}
return(0);
}
235
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
236
237
/* Set the function pointers */
238
239
240
241
242
this->OpenAudio = ALSA_OpenAudio;
this->WaitAudio = ALSA_WaitAudio;
this->PlayAudio = ALSA_PlayAudio;
this->GetAudioBuf = ALSA_GetAudioBuf;
this->CloseAudio = ALSA_CloseAudio;
243
244
245
246
247
248
249
this->free = Audio_DeleteDevice;
return this;
}
AudioBootStrap ALSA_bootstrap = {
250
DRIVER_NAME, "ALSA PCM audio",
251
252
253
254
Audio_Available, Audio_CreateDevice
};
/* This function waits until it is possible to write a full sound buffer */
255
static void ALSA_WaitAudio(_THIS)
256
{
257
/* We're in blocking mode, so there's nothing to do here */
258
259
}
260
261
262
263
264
265
266
267
268
/*
* 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; \
Uint32 i; \
269
for (i = 0; i < this->spec.samples; i++, ptr += 6) { \
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
T tmp; \
tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
}
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); }
#undef SWIZ6
/*
* Called right before feeding this->mixbuf to the hardware. Swizzle channels
* from Windows/Mac order to the format alsalib will want.
*/
static __inline__ void swizzle_alsa_channels(_THIS)
{
if (this->spec.channels == 6) {
const Uint16 fmtsize = (this->spec.format & 0xFF); /* bits/channel. */
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. */
}
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/* snd_pcm_recover() is available in alsa-lib >= 1.0.11 */
static int ALSA_pcm_recover(snd_pcm_t *handle, int err, int silent)
{
(void) silent;
if (err == -EINTR) return 0;
if (err == -EPIPE) { /* under-run */
err = SDL_NAME(snd_pcm_prepare)(handle);
return (err < 0)? err : 0;
}
if (err == -ESTRPIPE) {
/* wait until suspend flag is released */
while ((err = SDL_NAME(snd_pcm_resume)(handle)) == -EAGAIN)
SDL_Delay(100);
if (err < 0) err = SDL_NAME(snd_pcm_prepare)(handle);
return (err < 0)? err : 0;
}
return err;
}
324
static void ALSA_PlayAudio(_THIS)
325
{
326
int status;
327
snd_pcm_uframes_t frames_left;
328
const Uint8 *sample_buf = (const Uint8 *) mixbuf;
329
const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) * this->spec.channels;
330
331
332
swizzle_alsa_channels(this);
333
frames_left = ((snd_pcm_uframes_t) this->spec.samples);
334
335
while ( frames_left > 0 && this->enabled ) {
336
status = SDL_NAME(snd_pcm_writei)(pcm_handle, sample_buf, frames_left);
337
if ( status < 0 ) {
338
if ( status == -EAGAIN ) {
339
/* Apparently snd_pcm_recover() doesn't handle this case. Foo. */
340
341
342
SDL_Delay(1);
continue;
}
343
status = ALSA_pcm_recover(pcm_handle, status, 0);
344
if ( status < 0 ) {
345
/* Hmm, not much we can do - abort */
346
fprintf(stderr, "ALSA write failed (unrecoverable): %s\n", SDL_NAME(snd_strerror)(status));
347
348
this->enabled = 0;
return;
349
}
350
continue;
351
}
352
sample_buf += status * frame_size;
353
frames_left -= status;
354
355
356
}
}
357
static Uint8 *ALSA_GetAudioBuf(_THIS)
358
{
359
return(mixbuf);
360
361
}
362
static void ALSA_CloseAudio(_THIS)
363
{
364
365
366
if ( mixbuf != NULL ) {
SDL_FreeAudioMem(mixbuf);
mixbuf = NULL;
367
}
368
if ( pcm_handle ) {
369
370
SDL_NAME(snd_pcm_drain)(pcm_handle);
SDL_NAME(snd_pcm_close)(pcm_handle);
371
pcm_handle = NULL;
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
static int ALSA_finalize_hardware(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *hwparams, int override)
{
int status;
snd_pcm_uframes_t bufsize;
/* "set" the hardware with the desired parameters */
status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams);
if ( status < 0 ) {
return(-1);
}
/* Get samples for the actual buffer size */
status = SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize);
if ( status < 0 ) {
return(-1);
}
if ( !override && bufsize != spec->samples * 2 ) {
return(-1);
}
/* FIXME: Is this safe to do? */
spec->samples = bufsize / 2;
/* This is useful for debugging */
if ( getenv("SDL_AUDIO_ALSA_DEBUG") ) {
400
snd_pcm_uframes_t persize = 0;
401
402
403
404
405
406
407
408
409
410
unsigned int periods = 0;
SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, NULL);
SDL_NAME(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);
}
411
static int ALSA_set_period_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params, int override)
412
413
414
415
416
417
418
419
420
421
422
{
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);
SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
423
424
425
426
427
428
429
if ( !override ) {
env = getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
if ( env ) {
override = SDL_atoi(env);
if ( override == 0 ) {
return(-1);
}
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
}
}
frames = spec->samples;
status = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, &frames, NULL);
if ( status < 0 ) {
return(-1);
}
periods = 2;
status = SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, &periods, NULL);
if ( status < 0 ) {
return(-1);
}
return ALSA_finalize_hardware(this, spec, hwparams, override);
}
448
static int ALSA_set_buffer_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params, int override)
449
450
451
452
453
454
455
456
457
458
{
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);
SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
459
460
461
462
463
464
465
if ( !override ) {
env = getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
if ( env ) {
override = SDL_atoi(env);
if ( override == 0 ) {
return(-1);
}
466
467
468
469
470
471
472
473
474
475
476
477
}
}
frames = spec->samples * 2;
status = SDL_NAME(snd_pcm_hw_params_set_buffer_size_near)(pcm_handle, hwparams, &frames);
if ( status < 0 ) {
return(-1);
}
return ALSA_finalize_hardware(this, spec, hwparams, override);
}
478
static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec)
479
{
480
int status;
481
482
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
483
snd_pcm_format_t format;
484
unsigned int rate;
485
unsigned int channels;
486
Uint16 test_format;
487
488
/* Open the audio device */
489
490
491
/* Name of device should depend on # channels in spec */
status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(spec->channels), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
492
if ( status < 0 ) {
493
SDL_SetError("Couldn't open audio device: %s", SDL_NAME(snd_strerror)(status));
494
495
496
return(-1);
}
497
/* Figure out what the hardware is capable of */
498
499
snd_pcm_hw_params_alloca(&hwparams);
status = SDL_NAME(snd_pcm_hw_params_any)(pcm_handle, hwparams);
500
if ( status < 0 ) {
501
SDL_SetError("Couldn't get hardware config: %s", SDL_NAME(snd_strerror)(status));
502
503
504
ALSA_CloseAudio(this);
return(-1);
}
505
506
/* SDL only uses interleaved sample output */
507
status = SDL_NAME(snd_pcm_hw_params_set_access)(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
508
if ( status < 0 ) {
509
SDL_SetError("Couldn't set interleaved access: %s", SDL_NAME(snd_strerror)(status));
510
511
512
ALSA_CloseAudio(this);
return(-1);
}
513
514
/* Try for a closest match on audio format */
515
status = -1;
516
for ( test_format = SDL_FirstAudioFormat(spec->format);
517
518
test_format && (status < 0); ) {
switch ( test_format ) {
519
case AUDIO_U8:
520
format = SND_PCM_FORMAT_U8;
521
522
break;
case AUDIO_S8:
523
format = SND_PCM_FORMAT_S8;
524
525
break;
case AUDIO_S16LSB:
526
format = SND_PCM_FORMAT_S16_LE;
527
528
break;
case AUDIO_S16MSB:
529
format = SND_PCM_FORMAT_S16_BE;
530
531
break;
case AUDIO_U16LSB:
532
format = SND_PCM_FORMAT_U16_LE;
533
534
break;
case AUDIO_U16MSB:
535
format = SND_PCM_FORMAT_U16_BE;
536
537
break;
default:
538
format = 0;
539
540
break;
}
541
if ( format != 0 ) {
542
status = SDL_NAME(snd_pcm_hw_params_set_format)(pcm_handle, hwparams, format);
543
544
}
if ( status < 0 ) {
545
546
547
test_format = SDL_NextAudioFormat();
}
}
548
if ( status < 0 ) {
549
SDL_SetError("Couldn't find any hardware audio formats");
550
ALSA_CloseAudio(this);
551
552
553
554
return(-1);
}
spec->format = test_format;
555
/* Set the number of channels */
556
status = SDL_NAME(snd_pcm_hw_params_set_channels)(pcm_handle, hwparams, spec->channels);
557
channels = spec->channels;
558
if ( status < 0 ) {
559
560
status = SDL_NAME(snd_pcm_hw_params_get_channels)(hwparams, &channels);
if ( status < 0 ) {
561
562
563
564
SDL_SetError("Couldn't set audio channels");
ALSA_CloseAudio(this);
return(-1);
}
565
spec->channels = channels;
566
}
567
568
/* Set the audio rate */
569
570
571
rate = spec->freq;
status = SDL_NAME(snd_pcm_hw_params_set_rate_near)(pcm_handle, hwparams, &rate, NULL);
572
if ( status < 0 ) {
573
SDL_SetError("Couldn't set audio frequency: %s", SDL_NAME(snd_strerror)(status));
574
575
576
ALSA_CloseAudio(this);
return(-1);
}
577
spec->freq = rate;
578
579
/* Set the buffer size, in samples */
580
581
582
583
if ( ALSA_set_period_size(this, spec, hwparams, 0) < 0 &&
ALSA_set_buffer_size(this, spec, hwparams, 0) < 0 ) {
/* Failed to set desired buffer size, do the best you can... */
if ( ALSA_set_period_size(this, spec, hwparams, 1) < 0 ) {
584
SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status));
585
586
587
ALSA_CloseAudio(this);
return(-1);
}
588
}
589
590
591
592
593
594
595
596
597
/* Set the software parameters */
snd_pcm_sw_params_alloca(&swparams);
status = SDL_NAME(snd_pcm_sw_params_current)(pcm_handle, swparams);
if ( status < 0 ) {
SDL_SetError("Couldn't get software config: %s", SDL_NAME(snd_strerror)(status));
ALSA_CloseAudio(this);
return(-1);
}
598
599
600
601
602
603
status = SDL_NAME(snd_pcm_sw_params_set_avail_min)(pcm_handle, swparams, spec->samples);
if ( status < 0 ) {
SDL_SetError("Couldn't set minimum available samples: %s", SDL_NAME(snd_strerror)(status));
ALSA_CloseAudio(this);
return(-1);
}
604
status = SDL_NAME(snd_pcm_sw_params_set_start_threshold)(pcm_handle, swparams, 1);
605
606
607
608
609
610
if ( status < 0 ) {
SDL_SetError("Couldn't set start threshold: %s", SDL_NAME(snd_strerror)(status));
ALSA_CloseAudio(this);
return(-1);
}
status = SDL_NAME(snd_pcm_sw_params)(pcm_handle, swparams);
611
if ( status < 0 ) {
612
SDL_SetError("Couldn't set software audio parameters: %s", SDL_NAME(snd_strerror)(status));
613
ALSA_CloseAudio(this);
614
615
616
return(-1);
}
617
618
619
620
621
622
623
624
625
/* 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);
626
}
627
SDL_memset(mixbuf, spec->silence, spec->size);
628
629
/* Switch to blocking mode for playback */
630
SDL_NAME(snd_pcm_nonblock)(pcm_handle, 0);
631
632
633
634
/* We're ready to rock and roll. :-) */
return(0);
}