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