comparison src/audio/pulseaudio/SDL_pulseaudio.c @ 3672:878d8adf2fde

Merged r4210:5510 from branches/SDL-1.2/src/audio/pulse: PulseAudio updates. This pulls all the PulseAudio reworking that was done for 1.2.14 into 1.3. Fixes Bugzilla #801.
author Ryan C. Gordon <icculus@icculus.org>
date Sun, 17 Jan 2010 06:14:04 +0000
parents b21348d47cab
children c9d5bd1b4e18
comparison
equal deleted inserted replaced
3671:0d6f520c0eb9 3672:878d8adf2fde
35 #include <signal.h> 35 #include <signal.h>
36 #endif 36 #endif
37 #include <unistd.h> 37 #include <unistd.h>
38 #include <sys/types.h> 38 #include <sys/types.h>
39 #include <errno.h> 39 #include <errno.h>
40 #include <pulse/pulseaudio.h>
40 #include <pulse/simple.h> 41 #include <pulse/simple.h>
41 42
42 #include "SDL_timer.h" 43 #include "SDL_timer.h"
43 #include "SDL_audio.h" 44 #include "SDL_audio.h"
44 #include "../SDL_audiomem.h" 45 #include "../SDL_audiomem.h"
54 55
55 /* The tag name used by pulse audio */ 56 /* The tag name used by pulse audio */
56 #define PULSEAUDIO_DRIVER_NAME "pulseaudio" 57 #define PULSEAUDIO_DRIVER_NAME "pulseaudio"
57 58
58 #ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC 59 #ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC
60
61 #if (PA_API_VERSION < 12)
62 /** Return non-zero if the passed state is one of the connected states */
63 static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
64 return
65 x == PA_CONTEXT_CONNECTING ||
66 x == PA_CONTEXT_AUTHORIZING ||
67 x == PA_CONTEXT_SETTING_NAME ||
68 x == PA_CONTEXT_READY;
69 }
70 /** Return non-zero if the passed state is one of the connected states */
71 static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
72 return
73 x == PA_STREAM_CREATING ||
74 x == PA_STREAM_READY;
75 }
76 #endif /* pulseaudio <= 0.9.10 */
59 77
60 static const char *pulse_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC; 78 static const char *pulse_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC;
61 static void *pulse_handle = NULL; 79 static void *pulse_handle = NULL;
62 80
63 /* !!! FIXME: I hate this SDL_NAME clutter...it makes everything so messy! */ 81 /* !!! FIXME: I hate this SDL_NAME clutter...it makes everything so messy! */
69 const pa_sample_spec * ss, 87 const pa_sample_spec * ss,
70 const pa_channel_map * map, 88 const pa_channel_map * map,
71 const pa_buffer_attr * attr, 89 const pa_buffer_attr * attr,
72 int *error); 90 int *error);
73 static void (*SDL_NAME(pa_simple_free)) (pa_simple * s); 91 static void (*SDL_NAME(pa_simple_free)) (pa_simple * s);
74 static int (*SDL_NAME(pa_simple_drain)) (pa_simple * s, int *error);
75 static int (*SDL_NAME(pa_simple_write)) (pa_simple * s,
76 const void *data,
77 size_t length, int *error);
78 static pa_channel_map *(*SDL_NAME(pa_channel_map_init_auto)) (pa_channel_map * 92 static pa_channel_map *(*SDL_NAME(pa_channel_map_init_auto)) (pa_channel_map *
79 m, 93 m,
80 unsigned 94 unsigned
81 channels, 95 channels,
82 pa_channel_map_def_t 96 pa_channel_map_def_t
83 def); 97 def);
84 static const char *(*SDL_NAME(pa_strerror)) (int error); 98 static const char *(*SDL_NAME(pa_strerror)) (int error);
99 static pa_mainloop * (*SDL_NAME(pa_mainloop_new))(void);
100 static pa_mainloop_api * (*SDL_NAME(pa_mainloop_get_api))(pa_mainloop *m);
101 static int (*SDL_NAME(pa_mainloop_iterate))(pa_mainloop *m, int block, int *retval);
102 static void (*SDL_NAME(pa_mainloop_free))(pa_mainloop *m);
103
104 static pa_operation_state_t (*SDL_NAME(pa_operation_get_state))(pa_operation *o);
105 static void (*SDL_NAME(pa_operation_cancel))(pa_operation *o);
106 static void (*SDL_NAME(pa_operation_unref))(pa_operation *o);
107
108 static pa_context * (*SDL_NAME(pa_context_new))(
109 pa_mainloop_api *m, const char *name);
110 static int (*SDL_NAME(pa_context_connect))(
111 pa_context *c, const char *server,
112 pa_context_flags_t flags, const pa_spawn_api *api);
113 static pa_context_state_t (*SDL_NAME(pa_context_get_state))(pa_context *c);
114 static void (*SDL_NAME(pa_context_disconnect))(pa_context *c);
115 static void (*SDL_NAME(pa_context_unref))(pa_context *c);
116
117 static pa_stream * (*SDL_NAME(pa_stream_new))(pa_context *c,
118 const char *name, const pa_sample_spec *ss, const pa_channel_map *map);
119 static int (*SDL_NAME(pa_stream_connect_playback))(pa_stream *s, const char *dev,
120 const pa_buffer_attr *attr, pa_stream_flags_t flags,
121 pa_cvolume *volume, pa_stream *sync_stream);
122 static pa_stream_state_t (*SDL_NAME(pa_stream_get_state))(pa_stream *s);
123 static size_t (*SDL_NAME(pa_stream_writable_size))(pa_stream *s);
124 static int (*SDL_NAME(pa_stream_write))(pa_stream *s, const void *data, size_t nbytes,
125 pa_free_cb_t free_cb, int64_t offset, pa_seek_mode_t seek);
126 static pa_operation * (*SDL_NAME(pa_stream_drain))(pa_stream *s,
127 pa_stream_success_cb_t cb, void *userdata);
128 static int (*SDL_NAME(pa_stream_disconnect))(pa_stream *s);
129 static void (*SDL_NAME(pa_stream_unref))(pa_stream *s);
85 130
86 131
87 #define SDL_PULSEAUDIO_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) } 132 #define SDL_PULSEAUDIO_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
88 static struct 133 static struct
89 { 134 {
91 void **func; 136 void **func;
92 } pulse_functions[] = { 137 } pulse_functions[] = {
93 /* *INDENT-OFF* */ 138 /* *INDENT-OFF* */
94 SDL_PULSEAUDIO_SYM(pa_simple_new), 139 SDL_PULSEAUDIO_SYM(pa_simple_new),
95 SDL_PULSEAUDIO_SYM(pa_simple_free), 140 SDL_PULSEAUDIO_SYM(pa_simple_free),
96 SDL_PULSEAUDIO_SYM(pa_simple_drain), 141 SDL_PULSEAUDIO_SYM(pa_mainloop_new),
97 SDL_PULSEAUDIO_SYM(pa_simple_write), 142 SDL_PULSEAUDIO_SYM(pa_mainloop_get_api),
143 SDL_PULSEAUDIO_SYM(pa_mainloop_iterate),
144 SDL_PULSEAUDIO_SYM(pa_mainloop_free),
145 SDL_PULSEAUDIO_SYM(pa_operation_get_state),
146 SDL_PULSEAUDIO_SYM(pa_operation_cancel),
147 SDL_PULSEAUDIO_SYM(pa_operation_unref),
148 SDL_PULSEAUDIO_SYM(pa_context_new),
149 SDL_PULSEAUDIO_SYM(pa_context_connect),
150 SDL_PULSEAUDIO_SYM(pa_context_get_state),
151 SDL_PULSEAUDIO_SYM(pa_context_disconnect),
152 SDL_PULSEAUDIO_SYM(pa_context_unref),
153 SDL_PULSEAUDIO_SYM(pa_stream_new),
154 SDL_PULSEAUDIO_SYM(pa_stream_connect_playback),
155 SDL_PULSEAUDIO_SYM(pa_stream_get_state),
156 SDL_PULSEAUDIO_SYM(pa_stream_writable_size),
157 SDL_PULSEAUDIO_SYM(pa_stream_write),
158 SDL_PULSEAUDIO_SYM(pa_stream_drain),
159 SDL_PULSEAUDIO_SYM(pa_stream_disconnect),
160 SDL_PULSEAUDIO_SYM(pa_stream_unref),
98 SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto), 161 SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto),
99 SDL_PULSEAUDIO_SYM(pa_strerror), 162 SDL_PULSEAUDIO_SYM(pa_strerror),
100 /* *INDENT-ON* */ 163 /* *INDENT-ON* */
101 }; 164 };
102 165
153 216
154 /* This function waits until it is possible to write a full sound buffer */ 217 /* This function waits until it is possible to write a full sound buffer */
155 static void 218 static void
156 PULSEAUDIO_WaitDevice(_THIS) 219 PULSEAUDIO_WaitDevice(_THIS)
157 { 220 {
158 Sint32 ticks; 221 while(1) {
159 222 if (SDL_NAME(pa_context_get_state)(this->hidden->context) != PA_CONTEXT_READY ||
160 /* Check to see if the thread-parent process is still alive */ 223 SDL_NAME(pa_stream_get_state)(this->hidden->stream) != PA_STREAM_READY ||
161 { 224 SDL_NAME(pa_mainloop_iterate)(this->hidden->mainloop, 1, NULL) < 0) {
162 static int cnt = 0; 225 this->enabled = 0;
163 /* Note that this only works with thread implementations 226 return;
164 that use a different process id for each thread. 227 }
165 */ 228 if (SDL_NAME(pa_stream_writable_size)(this->hidden->stream) >= this->hidden->mixlen) {
166 /* Check every 10 loops */ 229 return;
167 if (this->hidden->parent && (((++cnt) % 10) == 0)) { 230 }
168 if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
169 this->enabled = 0;
170 }
171 }
172 }
173
174 /* Use timer for general audio synchronization */
175 ticks =
176 ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
177 if (ticks > 0) {
178 SDL_Delay(ticks);
179 } 231 }
180 } 232 }
181 233
182 static void 234 static void
183 PULSEAUDIO_PlayDevice(_THIS) 235 PULSEAUDIO_PlayDevice(_THIS)
184 { 236 {
185 /* Write the audio data */ 237 /* Write the audio data */
186 if (SDL_NAME(pa_simple_write) (this->hidden->stream, this->hidden->mixbuf, 238 if (SDL_NAME(pa_stream_write) (this->hidden->stream, this->hidden->mixbuf,
187 this->hidden->mixlen, NULL) != 0) { 239 this->hidden->mixlen, NULL, 0LL,
240 PA_SEEK_RELATIVE) < 0) {
188 this->enabled = 0; 241 this->enabled = 0;
189 } 242 }
190 } 243 }
191 244
192 static void 245 static void
246 stream_drain_complete(pa_stream *s, int success, void *userdata)
247 {
248 /* no-op for pa_stream_drain() to use for callback. */
249 }
250
251 static void
193 PULSEAUDIO_WaitDone(_THIS) 252 PULSEAUDIO_WaitDone(_THIS)
194 { 253 {
195 SDL_NAME(pa_simple_drain) (this->hidden->stream, NULL); 254 pa_operation *o;
196 } 255
256 o = SDL_NAME(pa_stream_drain)(this->hidden->stream, stream_drain_complete, NULL);
257 if (!o) {
258 return;
259 }
260
261 while (SDL_NAME(pa_operation_get_state)(o) != PA_OPERATION_DONE) {
262 if (SDL_NAME(pa_context_get_state)(this->hidden->context) != PA_CONTEXT_READY ||
263 SDL_NAME(pa_stream_get_state)(this->hidden->stream) != PA_STREAM_READY ||
264 SDL_NAME(pa_mainloop_iterate)(this->hidden->mainloop, 1, NULL) < 0) {
265 SDL_NAME(pa_operation_cancel)(o);
266 break;
267 }
268 }
269
270 SDL_NAME(pa_operation_unref)(o);
271 }
272
197 273
198 274
199 static Uint8 * 275 static Uint8 *
200 PULSEAUDIO_GetDeviceBuf(_THIS) 276 PULSEAUDIO_GetDeviceBuf(_THIS)
201 { 277 {
210 if (this->hidden->mixbuf != NULL) { 286 if (this->hidden->mixbuf != NULL) {
211 SDL_FreeAudioMem(this->hidden->mixbuf); 287 SDL_FreeAudioMem(this->hidden->mixbuf);
212 this->hidden->mixbuf = NULL; 288 this->hidden->mixbuf = NULL;
213 } 289 }
214 if (this->hidden->stream) { 290 if (this->hidden->stream) {
215 SDL_NAME(pa_simple_drain) (this->hidden->stream, NULL); 291 SDL_NAME(pa_stream_disconnect)(this->hidden->stream);
216 SDL_NAME(pa_simple_free) (this->hidden->stream); 292 SDL_NAME(pa_stream_unref)(this->hidden->stream);
217 this->hidden->stream = NULL; 293 this->hidden->stream = NULL;
294 }
295 if (this->hidden->context != NULL) {
296 SDL_NAME(pa_context_disconnect)(this->hidden->context);
297 SDL_NAME(pa_context_unref)(this->hidden->context);
298 this->hidden->context = NULL;
299 }
300 if (this->hidden->mainloop != NULL) {
301 SDL_NAME(pa_mainloop_free)(this->hidden->mainloop);
302 this->hidden->mainloop = NULL;
218 } 303 }
219 SDL_free(this->hidden); 304 SDL_free(this->hidden);
220 this->hidden = NULL; 305 this->hidden = NULL;
221 } 306 }
222 } 307 }
225 /* !!! FIXME: this could probably be expanded. */ 310 /* !!! FIXME: this could probably be expanded. */
226 /* Try to get the name of the program */ 311 /* Try to get the name of the program */
227 static char * 312 static char *
228 get_progname(void) 313 get_progname(void)
229 { 314 {
315 #ifdef __LINUX__
230 char *progname = NULL; 316 char *progname = NULL;
231 #ifdef __LINUX__
232 FILE *fp; 317 FILE *fp;
233 static char temp[BUFSIZ]; 318 static char temp[BUFSIZ];
234 319
235 SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid()); 320 SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
236 fp = fopen(temp, "r"); 321 fp = fopen(temp, "r");
243 progname = progname + 1; 328 progname = progname + 1;
244 } 329 }
245 } 330 }
246 fclose(fp); 331 fclose(fp);
247 } 332 }
333 return(progname);
334 #elif defined(__NetBSD__)
335 return getprogname();
336 #else
337 return("unknown");
248 #endif 338 #endif
249 return (progname);
250 } 339 }
251 340
252 341
253 static int 342 static int
254 PULSEAUDIO_OpenDevice(_THIS, const char *devname, int iscapture) 343 PULSEAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
255 { 344 {
256 Uint16 test_format = 0; 345 Uint16 test_format = 0;
257 pa_sample_spec paspec; 346 pa_sample_spec paspec;
258 pa_buffer_attr paattr; 347 pa_buffer_attr paattr;
259 pa_channel_map pacmap; 348 pa_channel_map pacmap;
260 int err = 0; 349 pa_stream_flags_t flags = 0;
350 int state = 0;
261 351
262 /* Initialize all variables that we clean on shutdown */ 352 /* Initialize all variables that we clean on shutdown */
263 this->hidden = (struct SDL_PrivateAudioData *) 353 this->hidden = (struct SDL_PrivateAudioData *)
264 SDL_malloc((sizeof *this->hidden)); 354 SDL_malloc((sizeof *this->hidden));
265 if (this->hidden == NULL) { 355 if (this->hidden == NULL) {
300 return 0; 390 return 0;
301 } 391 }
302 this->spec.format = test_format; 392 this->spec.format = test_format;
303 393
304 /* Calculate the final parameters for this audio specification */ 394 /* Calculate the final parameters for this audio specification */
395 #ifdef PA_STREAM_ADJUST_LATENCY
396 this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
397 #endif
305 SDL_CalculateAudioSpec(&this->spec); 398 SDL_CalculateAudioSpec(&this->spec);
306 399
307 /* Allocate mixing buffer */ 400 /* Allocate mixing buffer */
308 this->hidden->mixlen = this->spec.size; 401 this->hidden->mixlen = this->spec.size;
309 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); 402 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
316 409
317 paspec.channels = this->spec.channels; 410 paspec.channels = this->spec.channels;
318 paspec.rate = this->spec.freq; 411 paspec.rate = this->spec.freq;
319 412
320 /* Reduced prebuffering compared to the defaults. */ 413 /* Reduced prebuffering compared to the defaults. */
321 paattr.tlength = this->hidden->mixlen; 414 #ifdef PA_STREAM_ADJUST_LATENCY
415 paattr.tlength = this->hidden->mixlen * 4; /* 2x original requested bufsize */
416 paattr.prebuf = -1;
417 paattr.maxlength = -1;
418 /* -1 can lead to pa_stream_writable_size() >= this->hidden->mixlen never being true */
322 paattr.minreq = this->hidden->mixlen; 419 paattr.minreq = this->hidden->mixlen;
323 paattr.fragsize = this->hidden->mixlen; 420 flags = PA_STREAM_ADJUST_LATENCY;
324 paattr.prebuf = this->hidden->mixlen; 421 #else
325 paattr.maxlength = this->hidden->mixlen * 4; 422 paattr.tlength = this->hidden->mixlen*2;
423 paattr.prebuf = this->hidden->mixlen*2;
424 paattr.maxlength = this->hidden->mixlen*2;
425 paattr.minreq = this->hidden->mixlen;
426 #endif
326 427
327 /* The SDL ALSA output hints us that we use Windows' channel mapping */ 428 /* The SDL ALSA output hints us that we use Windows' channel mapping */
328 /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */ 429 /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
329 SDL_NAME(pa_channel_map_init_auto) (&pacmap, this->spec.channels, 430 SDL_NAME(pa_channel_map_init_auto) (&pacmap, this->spec.channels,
330 PA_CHANNEL_MAP_WAVEEX); 431 PA_CHANNEL_MAP_WAVEEX);
331 432
433 /* Set up a new main loop */
434 if (!(this->hidden->mainloop = SDL_NAME(pa_mainloop_new)())) {
435 PULSEAUDIO_CloseDevice(this);
436 SDL_SetError("pa_mainloop_new() failed");
437 return 0;
438 }
439
440 this->hidden->mainloop_api = SDL_NAME(pa_mainloop_get_api)(this->hidden->mainloop);
441 if (!(this->hidden->context = SDL_NAME(pa_context_new)(this->hidden->mainloop_api, get_progname()))) {
442 PULSEAUDIO_CloseDevice(this);
443 SDL_SetError("pa_context_new() failed");
444 return 0;
445 }
446
332 /* Connect to the PulseAudio server */ 447 /* Connect to the PulseAudio server */
333 this->hidden->stream = SDL_NAME(pa_simple_new) (SDL_getenv("PASERVER"), /* server */ 448 if (SDL_NAME(pa_context_connect)(this->hidden->context, NULL, 0, NULL) < 0) {
334 get_progname(), /* application name */ 449 PULSEAUDIO_CloseDevice(this);
335 PA_STREAM_PLAYBACK, /* playback mode */ 450 SDL_SetError("Could not setup connection to PulseAudio");
336 SDL_getenv("PADEVICE"), /* device on the server */ 451 return 0;
337 "Simple DirectMedia Layer", /* stream description */ 452 }
338 &paspec, /* sample format spec */ 453
339 &pacmap, /* channel map */ 454 do {
340 &paattr, /* buffering attributes */ 455 if (SDL_NAME(pa_mainloop_iterate)(this->hidden->mainloop, 1, NULL) < 0) {
341 &err /* error code */ 456 PULSEAUDIO_CloseDevice(this);
457 SDL_SetError("pa_mainloop_iterate() failed");
458 return 0;
459 }
460 state = SDL_NAME(pa_context_get_state)(this->hidden->context);
461 if (!PA_CONTEXT_IS_GOOD(state)) {
462 PULSEAUDIO_CloseDevice(this);
463 SDL_SetError("Could not connect to PulseAudio");
464 return 0;
465 }
466 } while (state != PA_CONTEXT_READY);
467
468 this->hidden->stream = SDL_NAME(pa_stream_new)(
469 this->hidden->context,
470 "Simple DirectMedia Layer", /* stream description */
471 &paspec, /* sample format spec */
472 &pacmap /* channel map */
342 ); 473 );
343 474
344 if (this->hidden->stream == NULL) { 475 if (this->hidden->stream == NULL) {
345 PULSEAUDIO_CloseDevice(this); 476 PULSEAUDIO_CloseDevice(this);
346 SDL_SetError("Could not connect to PulseAudio: %s", 477 SDL_SetError("Could not set up PulseAudio stream");
347 SDL_NAME(pa_strerror(err))); 478 return 0;
348 return 0; 479 }
349 } 480
350 481 if (SDL_NAME(pa_stream_connect_playback)(this->hidden->stream, NULL, &paattr, flags,
351 /* Get the parent process id (we're the parent of the audio thread) */ 482 NULL, NULL) < 0) {
352 this->hidden->parent = getpid(); 483 PULSEAUDIO_CloseDevice(this);
484 SDL_SetError("Could not connect PulseAudio stream");
485 return 0;
486 }
487
488 do {
489 if (SDL_NAME(pa_mainloop_iterate)(this->hidden->mainloop, 1, NULL) < 0) {
490 PULSEAUDIO_CloseDevice(this);
491 SDL_SetError("pa_mainloop_iterate() failed");
492 return 0;
493 }
494 state = SDL_NAME(pa_stream_get_state)(this->hidden->stream);
495 if (!PA_STREAM_IS_GOOD(state)) {
496 PULSEAUDIO_CloseDevice(this);
497 SDL_SetError("Could not create to PulseAudio stream");
498 return 0;
499 }
500 } while (state != PA_STREAM_READY);
353 501
354 /* We're ready to rock and roll. :-) */ 502 /* We're ready to rock and roll. :-) */
355 return 1; 503 return 1;
356 } 504 }
357 505