SDL_pulseaudio.c (17421B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22/* 23 The PulseAudio target for SDL 1.3 is based on the 1.3 arts target, with 24 the appropriate parts replaced with the 1.2 PulseAudio target code. This 25 was the cleanest way to move it to 1.3. The 1.2 target was written by 26 Stéphan Kochen: stephan .a.t. kochen.nl 27*/ 28#include "../../SDL_internal.h" 29 30#if SDL_AUDIO_DRIVER_PULSEAUDIO 31 32/* Allow access to a raw mixing buffer */ 33 34#ifdef HAVE_SIGNAL_H 35#include <signal.h> 36#endif 37#include <unistd.h> 38#include <sys/types.h> 39#include <errno.h> 40#include <pulse/pulseaudio.h> 41#include <pulse/simple.h> 42 43#include "SDL_timer.h" 44#include "SDL_audio.h" 45#include "../SDL_audiomem.h" 46#include "../SDL_audio_c.h" 47#include "SDL_pulseaudio.h" 48#include "SDL_loadso.h" 49 50#if (PA_API_VERSION < 12) 51/** Return non-zero if the passed state is one of the connected states */ 52static SDL_INLINE int PA_CONTEXT_IS_GOOD(pa_context_state_t x) { 53 return 54 x == PA_CONTEXT_CONNECTING || 55 x == PA_CONTEXT_AUTHORIZING || 56 x == PA_CONTEXT_SETTING_NAME || 57 x == PA_CONTEXT_READY; 58} 59/** Return non-zero if the passed state is one of the connected states */ 60static SDL_INLINE int PA_STREAM_IS_GOOD(pa_stream_state_t x) { 61 return 62 x == PA_STREAM_CREATING || 63 x == PA_STREAM_READY; 64} 65#endif /* pulseaudio <= 0.9.10 */ 66 67 68static const char *(*PULSEAUDIO_pa_get_library_version) (void); 69static pa_simple *(*PULSEAUDIO_pa_simple_new) (const char *, const char *, 70 pa_stream_direction_t, const char *, const char *, const pa_sample_spec *, 71 const pa_channel_map *, const pa_buffer_attr *, int *); 72static void (*PULSEAUDIO_pa_simple_free) (pa_simple *); 73static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto) ( 74 pa_channel_map *, unsigned, pa_channel_map_def_t); 75static const char * (*PULSEAUDIO_pa_strerror) (int); 76static pa_mainloop * (*PULSEAUDIO_pa_mainloop_new) (void); 77static pa_mainloop_api * (*PULSEAUDIO_pa_mainloop_get_api) (pa_mainloop *); 78static int (*PULSEAUDIO_pa_mainloop_iterate) (pa_mainloop *, int, int *); 79static void (*PULSEAUDIO_pa_mainloop_free) (pa_mainloop *); 80 81static pa_operation_state_t (*PULSEAUDIO_pa_operation_get_state) ( 82 pa_operation *); 83static void (*PULSEAUDIO_pa_operation_cancel) (pa_operation *); 84static void (*PULSEAUDIO_pa_operation_unref) (pa_operation *); 85 86static pa_context * (*PULSEAUDIO_pa_context_new) (pa_mainloop_api *, 87 const char *); 88static int (*PULSEAUDIO_pa_context_connect) (pa_context *, const char *, 89 pa_context_flags_t, const pa_spawn_api *); 90static pa_context_state_t (*PULSEAUDIO_pa_context_get_state) (pa_context *); 91static void (*PULSEAUDIO_pa_context_disconnect) (pa_context *); 92static void (*PULSEAUDIO_pa_context_unref) (pa_context *); 93 94static pa_stream * (*PULSEAUDIO_pa_stream_new) (pa_context *, const char *, 95 const pa_sample_spec *, const pa_channel_map *); 96static int (*PULSEAUDIO_pa_stream_connect_playback) (pa_stream *, const char *, 97 const pa_buffer_attr *, pa_stream_flags_t, pa_cvolume *, pa_stream *); 98static pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state) (pa_stream *); 99static size_t (*PULSEAUDIO_pa_stream_writable_size) (pa_stream *); 100static int (*PULSEAUDIO_pa_stream_write) (pa_stream *, const void *, size_t, 101 pa_free_cb_t, int64_t, pa_seek_mode_t); 102static pa_operation * (*PULSEAUDIO_pa_stream_drain) (pa_stream *, 103 pa_stream_success_cb_t, void *); 104static int (*PULSEAUDIO_pa_stream_disconnect) (pa_stream *); 105static void (*PULSEAUDIO_pa_stream_unref) (pa_stream *); 106 107static int load_pulseaudio_syms(void); 108 109 110#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC 111 112static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC; 113static void *pulseaudio_handle = NULL; 114 115static int 116load_pulseaudio_sym(const char *fn, void **addr) 117{ 118 *addr = SDL_LoadFunction(pulseaudio_handle, fn); 119 if (*addr == NULL) { 120 /* Don't call SDL_SetError(): SDL_LoadFunction already did. */ 121 return 0; 122 } 123 124 return 1; 125} 126 127/* cast funcs to char* first, to please GCC's strict aliasing rules. */ 128#define SDL_PULSEAUDIO_SYM(x) \ 129 if (!load_pulseaudio_sym(#x, (void **) (char *) &PULSEAUDIO_##x)) return -1 130 131static void 132UnloadPulseAudioLibrary(void) 133{ 134 if (pulseaudio_handle != NULL) { 135 SDL_UnloadObject(pulseaudio_handle); 136 pulseaudio_handle = NULL; 137 } 138} 139 140static int 141LoadPulseAudioLibrary(void) 142{ 143 int retval = 0; 144 if (pulseaudio_handle == NULL) { 145 pulseaudio_handle = SDL_LoadObject(pulseaudio_library); 146 if (pulseaudio_handle == NULL) { 147 retval = -1; 148 /* Don't call SDL_SetError(): SDL_LoadObject already did. */ 149 } else { 150 retval = load_pulseaudio_syms(); 151 if (retval < 0) { 152 UnloadPulseAudioLibrary(); 153 } 154 } 155 } 156 return retval; 157} 158 159#else 160 161#define SDL_PULSEAUDIO_SYM(x) PULSEAUDIO_##x = x 162 163static void 164UnloadPulseAudioLibrary(void) 165{ 166} 167 168static int 169LoadPulseAudioLibrary(void) 170{ 171 load_pulseaudio_syms(); 172 return 0; 173} 174 175#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC */ 176 177 178static int 179load_pulseaudio_syms(void) 180{ 181 SDL_PULSEAUDIO_SYM(pa_get_library_version); 182 SDL_PULSEAUDIO_SYM(pa_simple_new); 183 SDL_PULSEAUDIO_SYM(pa_simple_free); 184 SDL_PULSEAUDIO_SYM(pa_mainloop_new); 185 SDL_PULSEAUDIO_SYM(pa_mainloop_get_api); 186 SDL_PULSEAUDIO_SYM(pa_mainloop_iterate); 187 SDL_PULSEAUDIO_SYM(pa_mainloop_free); 188 SDL_PULSEAUDIO_SYM(pa_operation_get_state); 189 SDL_PULSEAUDIO_SYM(pa_operation_cancel); 190 SDL_PULSEAUDIO_SYM(pa_operation_unref); 191 SDL_PULSEAUDIO_SYM(pa_context_new); 192 SDL_PULSEAUDIO_SYM(pa_context_connect); 193 SDL_PULSEAUDIO_SYM(pa_context_get_state); 194 SDL_PULSEAUDIO_SYM(pa_context_disconnect); 195 SDL_PULSEAUDIO_SYM(pa_context_unref); 196 SDL_PULSEAUDIO_SYM(pa_stream_new); 197 SDL_PULSEAUDIO_SYM(pa_stream_connect_playback); 198 SDL_PULSEAUDIO_SYM(pa_stream_get_state); 199 SDL_PULSEAUDIO_SYM(pa_stream_writable_size); 200 SDL_PULSEAUDIO_SYM(pa_stream_write); 201 SDL_PULSEAUDIO_SYM(pa_stream_drain); 202 SDL_PULSEAUDIO_SYM(pa_stream_disconnect); 203 SDL_PULSEAUDIO_SYM(pa_stream_unref); 204 SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto); 205 SDL_PULSEAUDIO_SYM(pa_strerror); 206 return 0; 207} 208 209 210/* Check to see if we can connect to PulseAudio */ 211static SDL_bool 212CheckPulseAudioAvailable() 213{ 214 pa_simple *s; 215 pa_sample_spec ss; 216 217 ss.format = PA_SAMPLE_S16NE; 218 ss.channels = 1; 219 ss.rate = 22050; 220 221 s = PULSEAUDIO_pa_simple_new(NULL, "SDL", PA_STREAM_PLAYBACK, NULL, 222 "Test", &ss, NULL, NULL, NULL); 223 if (s) { 224 PULSEAUDIO_pa_simple_free(s); 225 return SDL_TRUE; 226 } else { 227 return SDL_FALSE; 228 } 229} 230 231/* This function waits until it is possible to write a full sound buffer */ 232static void 233PULSEAUDIO_WaitDevice(_THIS) 234{ 235 struct SDL_PrivateAudioData *h = this->hidden; 236 237 while(1) { 238 if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY || 239 PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY || 240 PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) { 241 this->enabled = 0; 242 return; 243 } 244 if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) { 245 return; 246 } 247 } 248} 249 250static void 251PULSEAUDIO_PlayDevice(_THIS) 252{ 253 /* Write the audio data */ 254 struct SDL_PrivateAudioData *h = this->hidden; 255 if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL, 256 PA_SEEK_RELATIVE) < 0) { 257 this->enabled = 0; 258 } 259} 260 261static void 262stream_drain_complete(pa_stream *s, int success, void *userdata) 263{ 264 /* no-op for pa_stream_drain() to use for callback. */ 265} 266 267static void 268PULSEAUDIO_WaitDone(_THIS) 269{ 270 struct SDL_PrivateAudioData *h = this->hidden; 271 pa_operation *o; 272 273 o = PULSEAUDIO_pa_stream_drain(h->stream, stream_drain_complete, NULL); 274 if (!o) { 275 return; 276 } 277 278 while (PULSEAUDIO_pa_operation_get_state(o) != PA_OPERATION_DONE) { 279 if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY || 280 PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY || 281 PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) { 282 PULSEAUDIO_pa_operation_cancel(o); 283 break; 284 } 285 } 286 287 PULSEAUDIO_pa_operation_unref(o); 288} 289 290 291 292static Uint8 * 293PULSEAUDIO_GetDeviceBuf(_THIS) 294{ 295 return (this->hidden->mixbuf); 296} 297 298 299static void 300PULSEAUDIO_CloseDevice(_THIS) 301{ 302 if (this->hidden != NULL) { 303 SDL_FreeAudioMem(this->hidden->mixbuf); 304 this->hidden->mixbuf = NULL; 305 if (this->hidden->stream) { 306 PULSEAUDIO_pa_stream_disconnect(this->hidden->stream); 307 PULSEAUDIO_pa_stream_unref(this->hidden->stream); 308 this->hidden->stream = NULL; 309 } 310 if (this->hidden->context != NULL) { 311 PULSEAUDIO_pa_context_disconnect(this->hidden->context); 312 PULSEAUDIO_pa_context_unref(this->hidden->context); 313 this->hidden->context = NULL; 314 } 315 if (this->hidden->mainloop != NULL) { 316 PULSEAUDIO_pa_mainloop_free(this->hidden->mainloop); 317 this->hidden->mainloop = NULL; 318 } 319 SDL_free(this->hidden); 320 this->hidden = NULL; 321 } 322} 323 324 325static SDL_INLINE int 326squashVersion(const int major, const int minor, const int patch) 327{ 328 return ((major & 0xFF) << 16) | ((minor & 0xFF) << 8) | (patch & 0xFF); 329} 330 331/* Workaround for older pulse: pa_context_new() must have non-NULL appname */ 332static const char * 333getAppName(void) 334{ 335 const char *verstr = PULSEAUDIO_pa_get_library_version(); 336 if (verstr != NULL) { 337 int maj, min, patch; 338 if (SDL_sscanf(verstr, "%d.%d.%d", &maj, &min, &patch) == 3) { 339 if (squashVersion(maj, min, patch) >= squashVersion(0, 9, 15)) { 340 return NULL; /* 0.9.15+ handles NULL correctly. */ 341 } 342 } 343 } 344 return "SDL Application"; /* oh well. */ 345} 346 347static int 348PULSEAUDIO_OpenDevice(_THIS, const char *devname, int iscapture) 349{ 350 struct SDL_PrivateAudioData *h = NULL; 351 Uint16 test_format = 0; 352 pa_sample_spec paspec; 353 pa_buffer_attr paattr; 354 pa_channel_map pacmap; 355 pa_stream_flags_t flags = 0; 356 int state = 0; 357 358 /* Initialize all variables that we clean on shutdown */ 359 this->hidden = (struct SDL_PrivateAudioData *) 360 SDL_malloc((sizeof *this->hidden)); 361 if (this->hidden == NULL) { 362 return SDL_OutOfMemory(); 363 } 364 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 365 h = this->hidden; 366 367 paspec.format = PA_SAMPLE_INVALID; 368 369 /* Try for a closest match on audio format */ 370 for (test_format = SDL_FirstAudioFormat(this->spec.format); 371 (paspec.format == PA_SAMPLE_INVALID) && test_format;) { 372#ifdef DEBUG_AUDIO 373 fprintf(stderr, "Trying format 0x%4.4x\n", test_format); 374#endif 375 switch (test_format) { 376 case AUDIO_U8: 377 paspec.format = PA_SAMPLE_U8; 378 break; 379 case AUDIO_S16LSB: 380 paspec.format = PA_SAMPLE_S16LE; 381 break; 382 case AUDIO_S16MSB: 383 paspec.format = PA_SAMPLE_S16BE; 384 break; 385 case AUDIO_S32LSB: 386 paspec.format = PA_SAMPLE_S32LE; 387 break; 388 case AUDIO_S32MSB: 389 paspec.format = PA_SAMPLE_S32BE; 390 break; 391 case AUDIO_F32LSB: 392 paspec.format = PA_SAMPLE_FLOAT32LE; 393 break; 394 case AUDIO_F32MSB: 395 paspec.format = PA_SAMPLE_FLOAT32BE; 396 break; 397 default: 398 paspec.format = PA_SAMPLE_INVALID; 399 break; 400 } 401 if (paspec.format == PA_SAMPLE_INVALID) { 402 test_format = SDL_NextAudioFormat(); 403 } 404 } 405 if (paspec.format == PA_SAMPLE_INVALID) { 406 PULSEAUDIO_CloseDevice(this); 407 return SDL_SetError("Couldn't find any hardware audio formats"); 408 } 409 this->spec.format = test_format; 410 411 /* Calculate the final parameters for this audio specification */ 412#ifdef PA_STREAM_ADJUST_LATENCY 413 this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */ 414#endif 415 SDL_CalculateAudioSpec(&this->spec); 416 417 /* Allocate mixing buffer */ 418 h->mixlen = this->spec.size; 419 h->mixbuf = (Uint8 *) SDL_AllocAudioMem(h->mixlen); 420 if (h->mixbuf == NULL) { 421 PULSEAUDIO_CloseDevice(this); 422 return SDL_OutOfMemory(); 423 } 424 SDL_memset(h->mixbuf, this->spec.silence, this->spec.size); 425 426 paspec.channels = this->spec.channels; 427 paspec.rate = this->spec.freq; 428 429 /* Reduced prebuffering compared to the defaults. */ 430#ifdef PA_STREAM_ADJUST_LATENCY 431 /* 2x original requested bufsize */ 432 paattr.tlength = h->mixlen * 4; 433 paattr.prebuf = -1; 434 paattr.maxlength = -1; 435 /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */ 436 paattr.minreq = h->mixlen; 437 flags = PA_STREAM_ADJUST_LATENCY; 438#else 439 paattr.tlength = h->mixlen*2; 440 paattr.prebuf = h->mixlen*2; 441 paattr.maxlength = h->mixlen*2; 442 paattr.minreq = h->mixlen; 443#endif 444 445 /* The SDL ALSA output hints us that we use Windows' channel mapping */ 446 /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */ 447 PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels, 448 PA_CHANNEL_MAP_WAVEEX); 449 450 /* Set up a new main loop */ 451 if (!(h->mainloop = PULSEAUDIO_pa_mainloop_new())) { 452 PULSEAUDIO_CloseDevice(this); 453 return SDL_SetError("pa_mainloop_new() failed"); 454 } 455 456 h->mainloop_api = PULSEAUDIO_pa_mainloop_get_api(h->mainloop); 457 h->context = PULSEAUDIO_pa_context_new(h->mainloop_api, getAppName()); 458 if (!h->context) { 459 PULSEAUDIO_CloseDevice(this); 460 return SDL_SetError("pa_context_new() failed"); 461 } 462 463 /* Connect to the PulseAudio server */ 464 if (PULSEAUDIO_pa_context_connect(h->context, NULL, 0, NULL) < 0) { 465 PULSEAUDIO_CloseDevice(this); 466 return SDL_SetError("Could not setup connection to PulseAudio"); 467 } 468 469 do { 470 if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) { 471 PULSEAUDIO_CloseDevice(this); 472 return SDL_SetError("pa_mainloop_iterate() failed"); 473 } 474 state = PULSEAUDIO_pa_context_get_state(h->context); 475 if (!PA_CONTEXT_IS_GOOD(state)) { 476 PULSEAUDIO_CloseDevice(this); 477 return SDL_SetError("Could not connect to PulseAudio"); 478 } 479 } while (state != PA_CONTEXT_READY); 480 481 h->stream = PULSEAUDIO_pa_stream_new( 482 h->context, 483 "Simple DirectMedia Layer", /* stream description */ 484 &paspec, /* sample format spec */ 485 &pacmap /* channel map */ 486 ); 487 488 if (h->stream == NULL) { 489 PULSEAUDIO_CloseDevice(this); 490 return SDL_SetError("Could not set up PulseAudio stream"); 491 } 492 493 if (PULSEAUDIO_pa_stream_connect_playback(h->stream, NULL, &paattr, flags, 494 NULL, NULL) < 0) { 495 PULSEAUDIO_CloseDevice(this); 496 return SDL_SetError("Could not connect PulseAudio stream"); 497 } 498 499 do { 500 if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) { 501 PULSEAUDIO_CloseDevice(this); 502 return SDL_SetError("pa_mainloop_iterate() failed"); 503 } 504 state = PULSEAUDIO_pa_stream_get_state(h->stream); 505 if (!PA_STREAM_IS_GOOD(state)) { 506 PULSEAUDIO_CloseDevice(this); 507 return SDL_SetError("Could not create to PulseAudio stream"); 508 } 509 } while (state != PA_STREAM_READY); 510 511 /* We're ready to rock and roll. :-) */ 512 return 0; 513} 514 515 516static void 517PULSEAUDIO_Deinitialize(void) 518{ 519 UnloadPulseAudioLibrary(); 520} 521 522static int 523PULSEAUDIO_Init(SDL_AudioDriverImpl * impl) 524{ 525 if (LoadPulseAudioLibrary() < 0) { 526 return 0; 527 } 528 529 if (!CheckPulseAudioAvailable()) { 530 UnloadPulseAudioLibrary(); 531 return 0; 532 } 533 534 /* Set the function pointers */ 535 impl->OpenDevice = PULSEAUDIO_OpenDevice; 536 impl->PlayDevice = PULSEAUDIO_PlayDevice; 537 impl->WaitDevice = PULSEAUDIO_WaitDevice; 538 impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf; 539 impl->CloseDevice = PULSEAUDIO_CloseDevice; 540 impl->WaitDone = PULSEAUDIO_WaitDone; 541 impl->Deinitialize = PULSEAUDIO_Deinitialize; 542 impl->OnlyHasDefaultOutputDevice = 1; 543 544 return 1; /* this audio target is available. */ 545} 546 547 548AudioBootStrap PULSEAUDIO_bootstrap = { 549 "pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0 550}; 551 552#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */ 553 554/* vi: set ts=4 sw=4 expandtab: */