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