1 /* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2012 Sam Lantinga 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 19 Stphan Kochen 20 stephan (at) kochen.nl 21 22 Based on parts of the ALSA and ESounD output drivers. 23 */ 24 #include "SDL_config.h" 25 26 /* Allow access to an PulseAudio network stream mixing buffer */ 27 28 #include <sys/types.h> 29 #include <unistd.h> 30 #include <signal.h> 31 #include <errno.h> 32 #include <pulse/pulseaudio.h> 33 #include <pulse/simple.h> 34 35 #include "SDL_timer.h" 36 #include "SDL_audio.h" 37 #include "../SDL_audiomem.h" 38 #include "../SDL_audio_c.h" 39 #include "../SDL_audiodev_c.h" 40 #include "../../../include/SDL_video.h" /* for SDL_WM_GetCaption(). */ 41 #include "SDL_pulseaudio.h" 42 43 #ifdef SDL_AUDIO_DRIVER_PULSE_DYNAMIC 44 #include "SDL_name.h" 45 #include "SDL_loadso.h" 46 #else 47 #define SDL_NAME(X) X 48 #endif 49 50 /* The tag name used by the driver */ 51 #define PULSE_DRIVER_NAME "pulse" 52 53 /* Audio driver functions */ 54 static int PULSE_OpenAudio(_THIS, SDL_AudioSpec *spec); 55 static void PULSE_WaitAudio(_THIS); 56 static void PULSE_PlayAudio(_THIS); 57 static Uint8 *PULSE_GetAudioBuf(_THIS); 58 static void PULSE_CloseAudio(_THIS); 59 static void PULSE_WaitDone(_THIS); 60 static void PULSE_SetCaption(_THIS, const char *str); 61 62 #ifdef SDL_AUDIO_DRIVER_PULSE_DYNAMIC 63 64 static const char *pulse_library = SDL_AUDIO_DRIVER_PULSE_DYNAMIC; 65 static void *pulse_handle = NULL; 66 static int pulse_loaded = 0; 67 68 static pa_simple* (*SDL_NAME(pa_simple_new))( 69 const char *server, 70 const char *name, 71 pa_stream_direction_t dir, 72 const char *dev, 73 const char *stream_name, 74 const pa_sample_spec *ss, 75 const pa_channel_map *map, 76 const pa_buffer_attr *attr, 77 int *error 78 ); 79 static void (*SDL_NAME(pa_simple_free))(pa_simple *s); 80 81 static pa_channel_map* (*SDL_NAME(pa_channel_map_init_auto))( 82 pa_channel_map *m, 83 unsigned channels, 84 pa_channel_map_def_t def 85 ); 86 87 static pa_mainloop * (*SDL_NAME(pa_mainloop_new))(void); 88 static pa_mainloop_api * (*SDL_NAME(pa_mainloop_get_api))(pa_mainloop *m); 89 static int (*SDL_NAME(pa_mainloop_iterate))(pa_mainloop *m, int block, int *retval); 90 static void (*SDL_NAME(pa_mainloop_free))(pa_mainloop *m); 91 92 static pa_operation_state_t (*SDL_NAME(pa_operation_get_state))(pa_operation *o); 93 static void (*SDL_NAME(pa_operation_cancel))(pa_operation *o); 94 static void (*SDL_NAME(pa_operation_unref))(pa_operation *o); 95 96 static pa_context * (*SDL_NAME(pa_context_new))( 97 pa_mainloop_api *m, const char *name); 98 static int (*SDL_NAME(pa_context_connect))( 99 pa_context *c, const char *server, 100 pa_context_flags_t flags, const pa_spawn_api *api); 101 static pa_context_state_t (*SDL_NAME(pa_context_get_state))(pa_context *c); 102 static void (*SDL_NAME(pa_context_disconnect))(pa_context *c); 103 static void (*SDL_NAME(pa_context_unref))(pa_context *c); 104 105 static pa_stream * (*SDL_NAME(pa_stream_new))(pa_context *c, 106 const char *name, const pa_sample_spec *ss, const pa_channel_map *map); 107 static int (*SDL_NAME(pa_stream_connect_playback))(pa_stream *s, const char *dev, 108 const pa_buffer_attr *attr, pa_stream_flags_t flags, 109 pa_cvolume *volume, pa_stream *sync_stream); 110 static pa_stream_state_t (*SDL_NAME(pa_stream_get_state))(pa_stream *s); 111 static size_t (*SDL_NAME(pa_stream_writable_size))(pa_stream *s); 112 static int (*SDL_NAME(pa_stream_write))(pa_stream *s, const void *data, size_t nbytes, 113 pa_free_cb_t free_cb, int64_t offset, pa_seek_mode_t seek); 114 static pa_operation * (*SDL_NAME(pa_stream_drain))(pa_stream *s, 115 pa_stream_success_cb_t cb, void *userdata); 116 static int (*SDL_NAME(pa_stream_disconnect))(pa_stream *s); 117 static void (*SDL_NAME(pa_stream_unref))(pa_stream *s); 118 static pa_operation* (*SDL_NAME(pa_context_set_name))(pa_context *c, 119 const char *name, pa_context_success_cb_t cb, void *userdata); 120 121 static struct { 122 const char *name; 123 void **func; 124 } pulse_functions[] = { 125 { "pa_simple_new", 126 (void **)&SDL_NAME(pa_simple_new) }, 127 { "pa_simple_free", 128 (void **)&SDL_NAME(pa_simple_free) }, 129 { "pa_channel_map_init_auto", 130 (void **)&SDL_NAME(pa_channel_map_init_auto) }, 131 { "pa_mainloop_new", 132 (void **)&SDL_NAME(pa_mainloop_new) }, 133 { "pa_mainloop_get_api", 134 (void **)&SDL_NAME(pa_mainloop_get_api) }, 135 { "pa_mainloop_iterate", 136 (void **)&SDL_NAME(pa_mainloop_iterate) }, 137 { "pa_mainloop_free", 138 (void **)&SDL_NAME(pa_mainloop_free) }, 139 { "pa_operation_get_state", 140 (void **)&SDL_NAME(pa_operation_get_state) }, 141 { "pa_operation_cancel", 142 (void **)&SDL_NAME(pa_operation_cancel) }, 143 { "pa_operation_unref", 144 (void **)&SDL_NAME(pa_operation_unref) }, 145 { "pa_context_new", 146 (void **)&SDL_NAME(pa_context_new) }, 147 { "pa_context_connect", 148 (void **)&SDL_NAME(pa_context_connect) }, 149 { "pa_context_get_state", 150 (void **)&SDL_NAME(pa_context_get_state) }, 151 { "pa_context_disconnect", 152 (void **)&SDL_NAME(pa_context_disconnect) }, 153 { "pa_context_unref", 154 (void **)&SDL_NAME(pa_context_unref) }, 155 { "pa_stream_new", 156 (void **)&SDL_NAME(pa_stream_new) }, 157 { "pa_stream_connect_playback", 158 (void **)&SDL_NAME(pa_stream_connect_playback) }, 159 { "pa_stream_get_state", 160 (void **)&SDL_NAME(pa_stream_get_state) }, 161 { "pa_stream_writable_size", 162 (void **)&SDL_NAME(pa_stream_writable_size) }, 163 { "pa_stream_write", 164 (void **)&SDL_NAME(pa_stream_write) }, 165 { "pa_stream_drain", 166 (void **)&SDL_NAME(pa_stream_drain) }, 167 { "pa_stream_disconnect", 168 (void **)&SDL_NAME(pa_stream_disconnect) }, 169 { "pa_stream_unref", 170 (void **)&SDL_NAME(pa_stream_unref) }, 171 { "pa_context_set_name", 172 (void **)&SDL_NAME(pa_context_set_name) }, 173 }; 174 175 static void UnloadPulseLibrary() 176 { 177 if ( pulse_loaded ) { 178 SDL_UnloadObject(pulse_handle); 179 pulse_handle = NULL; 180 pulse_loaded = 0; 181 } 182 } 183 184 static int LoadPulseLibrary(void) 185 { 186 int i, retval = -1; 187 188 pulse_handle = SDL_LoadObject(pulse_library); 189 if ( pulse_handle ) { 190 pulse_loaded = 1; 191 retval = 0; 192 for ( i=0; i<SDL_arraysize(pulse_functions); ++i ) { 193 *pulse_functions[i].func = SDL_LoadFunction(pulse_handle, pulse_functions[i].name); 194 if ( !*pulse_functions[i].func ) { 195 retval = -1; 196 UnloadPulseLibrary(); 197 break; 198 } 199 } 200 } 201 return retval; 202 } 203 204 #else 205 206 static void UnloadPulseLibrary() 207 { 208 return; 209 } 210 211 static int LoadPulseLibrary(void) 212 { 213 return 0; 214 } 215 216 #endif /* SDL_AUDIO_DRIVER_PULSE_DYNAMIC */ 217 218 /* Audio driver bootstrap functions */ 219 220 static int Audio_Available(void) 221 { 222 pa_sample_spec paspec; 223 pa_simple *connection; 224 int available; 225 226 available = 0; 227 if ( LoadPulseLibrary() < 0 ) { 228 return available; 229 } 230 231 /* Connect with a dummy format. */ 232 paspec.format = PA_SAMPLE_U8; 233 paspec.rate = 11025; 234 paspec.channels = 1; 235 connection = SDL_NAME(pa_simple_new)( 236 NULL, /* server */ 237 "Test stream", /* application name */ 238 PA_STREAM_PLAYBACK, /* playback mode */ 239 NULL, /* device on the server */ 240 "Simple DirectMedia Layer", /* stream description */ 241 &paspec, /* sample format spec */ 242 NULL, /* channel map */ 243 NULL, /* buffering attributes */ 244 NULL /* error code */ 245 ); 246 if ( connection != NULL ) { 247 available = 1; 248 SDL_NAME(pa_simple_free)(connection); 249 } 250 251 UnloadPulseLibrary(); 252 return(available); 253 } 254 255 static void Audio_DeleteDevice(SDL_AudioDevice *device) 256 { 257 SDL_free(device->hidden->caption); 258 SDL_free(device->hidden); 259 SDL_free(device); 260 UnloadPulseLibrary(); 261 } 262 263 static SDL_AudioDevice *Audio_CreateDevice(int devindex) 264 { 265 SDL_AudioDevice *this; 266 267 /* Initialize all variables that we clean on shutdown */ 268 LoadPulseLibrary(); 269 this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); 270 if ( this ) { 271 SDL_memset(this, 0, (sizeof *this)); 272 this->hidden = (struct SDL_PrivateAudioData *) 273 SDL_malloc((sizeof *this->hidden)); 274 } 275 if ( (this == NULL) || (this->hidden == NULL) ) { 276 SDL_OutOfMemory(); 277 if ( this ) { 278 SDL_free(this); 279 } 280 return(0); 281 } 282 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 283 284 /* Set the function pointers */ 285 this->OpenAudio = PULSE_OpenAudio; 286 this->WaitAudio = PULSE_WaitAudio; 287 this->PlayAudio = PULSE_PlayAudio; 288 this->GetAudioBuf = PULSE_GetAudioBuf; 289 this->CloseAudio = PULSE_CloseAudio; 290 this->WaitDone = PULSE_WaitDone; 291 this->SetCaption = PULSE_SetCaption; 292 293 this->free = Audio_DeleteDevice; 294 295 return this; 296 } 297 298 AudioBootStrap PULSE_bootstrap = { 299 PULSE_DRIVER_NAME, "PulseAudio", 300 Audio_Available, Audio_CreateDevice 301 }; 302 303 /* This function waits until it is possible to write a full sound buffer */ 304 static void PULSE_WaitAudio(_THIS) 305 { 306 int size; 307 while(1) { 308 if (SDL_NAME(pa_context_get_state)(context) != PA_CONTEXT_READY || 309 SDL_NAME(pa_stream_get_state)(stream) != PA_STREAM_READY || 310 SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) { 311 this->enabled = 0; 312 return; 313 } 314 size = SDL_NAME(pa_stream_writable_size)(stream); 315 if (size >= mixlen) 316 return; 317 } 318 } 319 320 static void PULSE_PlayAudio(_THIS) 321 { 322 /* Write the audio data */ 323 if (SDL_NAME(pa_stream_write)(stream, mixbuf, mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0) 324 this->enabled = 0; 325 } 326 327 static Uint8 *PULSE_GetAudioBuf(_THIS) 328 { 329 return(mixbuf); 330 } 331 332 static void PULSE_CloseAudio(_THIS) 333 { 334 if ( mixbuf != NULL ) { 335 SDL_FreeAudioMem(mixbuf); 336 mixbuf = NULL; 337 } 338 if ( stream != NULL ) { 339 SDL_NAME(pa_stream_disconnect)(stream); 340 SDL_NAME(pa_stream_unref)(stream); 341 stream = NULL; 342 } 343 if (context != NULL) { 344 SDL_NAME(pa_context_disconnect)(context); 345 SDL_NAME(pa_context_unref)(context); 346 context = NULL; 347 } 348 if (mainloop != NULL) { 349 SDL_NAME(pa_mainloop_free)(mainloop); 350 mainloop = NULL; 351 } 352 } 353 354 /* Try to get the name of the program */ 355 static char *get_progname(void) 356 { 357 #ifdef __LINUX__ 358 char *progname = NULL; 359 FILE *fp; 360 static char temp[BUFSIZ]; 361 362 SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid()); 363 fp = fopen(temp, "r"); 364 if ( fp != NULL ) { 365 if ( fgets(temp, sizeof(temp)-1, fp) ) { 366 progname = SDL_strrchr(temp, '/'); 367 if ( progname == NULL ) { 368 progname = temp; 369 } else { 370 progname = progname+1; 371 } 372 } 373 fclose(fp); 374 } 375 return(progname); 376 #elif defined(__NetBSD__) 377 return getprogname(); 378 #else 379 return("unknown"); 380 #endif 381 } 382 383 static void caption_set_complete(pa_context *c, int success, void *userdata) 384 { 385 /* no-op. */ 386 } 387 388 static void PULSE_SetCaption(_THIS, const char *str) 389 { 390 SDL_free(this->hidden->caption); 391 if ((str == NULL) || (*str == '\0')) { 392 str = get_progname(); /* set a default so SOMETHING shows up. */ 393 } 394 this->hidden->caption = SDL_strdup(str); 395 if (context != NULL) { 396 SDL_NAME(pa_context_set_name)(context, this->hidden->caption, 397 caption_set_complete, 0); 398 } 399 } 400 401 static void stream_drain_complete(pa_stream *s, int success, void *userdata) 402 { 403 /* no-op. */ 404 } 405 406 static void PULSE_WaitDone(_THIS) 407 { 408 pa_operation *o; 409 410 o = SDL_NAME(pa_stream_drain)(stream, stream_drain_complete, NULL); 411 if (!o) 412 return; 413 414 while (SDL_NAME(pa_operation_get_state)(o) != PA_OPERATION_DONE) { 415 if (SDL_NAME(pa_context_get_state)(context) != PA_CONTEXT_READY || 416 SDL_NAME(pa_stream_get_state)(stream) != PA_STREAM_READY || 417 SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) { 418 SDL_NAME(pa_operation_cancel)(o); 419 break; 420 } 421 } 422 SDL_NAME(pa_operation_unref)(o); 423 } 424 425 static int PULSE_OpenAudio(_THIS, SDL_AudioSpec *spec) 426 { 427 int state; 428 Uint16 test_format; 429 pa_sample_spec paspec; 430 pa_buffer_attr paattr; 431 pa_channel_map pacmap; 432 pa_stream_flags_t flags = 0; 433 434 paspec.format = PA_SAMPLE_INVALID; 435 for ( test_format = SDL_FirstAudioFormat(spec->format); test_format; ) { 436 switch ( test_format ) { 437 case AUDIO_U8: 438 paspec.format = PA_SAMPLE_U8; 439 break; 440 case AUDIO_S16LSB: 441 paspec.format = PA_SAMPLE_S16LE; 442 break; 443 case AUDIO_S16MSB: 444 paspec.format = PA_SAMPLE_S16BE; 445 break; 446 } 447 if ( paspec.format != PA_SAMPLE_INVALID ) 448 break; 449 test_format = SDL_NextAudioFormat(); 450 } 451 if (paspec.format == PA_SAMPLE_INVALID ) { 452 SDL_SetError("Couldn't find any suitable audio formats"); 453 return(-1); 454 } 455 spec->format = test_format; 456 457 paspec.channels = spec->channels; 458 paspec.rate = spec->freq; 459 460 /* Calculate the final parameters for this audio specification */ 461 #ifdef PA_STREAM_ADJUST_LATENCY 462 spec->samples /= 2; /* Mix in smaller chunck to avoid underruns */ 463 #endif 464 SDL_CalculateAudioSpec(spec); 465 466 /* Allocate mixing buffer */ 467 mixlen = spec->size; 468 mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); 469 if ( mixbuf == NULL ) { 470 return(-1); 471 } 472 SDL_memset(mixbuf, spec->silence, spec->size); 473 474 /* Reduced prebuffering compared to the defaults. */ 475 #ifdef PA_STREAM_ADJUST_LATENCY 476 paattr.tlength = mixlen * 4; /* 2x original requested bufsize */ 477 paattr.prebuf = -1; 478 paattr.maxlength = -1; 479 paattr.minreq = mixlen; /* -1 can lead to pa_stream_writable_size() 480 >= mixlen never becoming true */ 481 flags = PA_STREAM_ADJUST_LATENCY; 482 #else 483 paattr.tlength = mixlen*2; 484 paattr.prebuf = mixlen*2; 485 paattr.maxlength = mixlen*2; 486 paattr.minreq = mixlen; 487 #endif 488 489 /* The SDL ALSA output hints us that we use Windows' channel mapping */ 490 /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */ 491 SDL_NAME(pa_channel_map_init_auto)( 492 &pacmap, spec->channels, PA_CHANNEL_MAP_WAVEEX); 493 494 /* Set up a new main loop */ 495 if (!(mainloop = SDL_NAME(pa_mainloop_new)())) { 496 PULSE_CloseAudio(this); 497 SDL_SetError("pa_mainloop_new() failed"); 498 return(-1); 499 } 500 501 if (this->hidden->caption == NULL) { 502 char *title = NULL; 503 SDL_WM_GetCaption(&title, NULL); 504 PULSE_SetCaption(this, title); 505 } 506 507 mainloop_api = SDL_NAME(pa_mainloop_get_api)(mainloop); 508 if (!(context = SDL_NAME(pa_context_new)(mainloop_api, 509 this->hidden->caption))) { 510 PULSE_CloseAudio(this); 511 SDL_SetError("pa_context_new() failed"); 512 return(-1); 513 } 514 515 /* Connect to the PulseAudio server */ 516 if (SDL_NAME(pa_context_connect)(context, NULL, 0, NULL) < 0) { 517 PULSE_CloseAudio(this); 518 SDL_SetError("Could not setup connection to PulseAudio"); 519 return(-1); 520 } 521 522 do { 523 if (SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) { 524 PULSE_CloseAudio(this); 525 SDL_SetError("pa_mainloop_iterate() failed"); 526 return(-1); 527 } 528 state = SDL_NAME(pa_context_get_state)(context); 529 if (!PA_CONTEXT_IS_GOOD(state)) { 530 PULSE_CloseAudio(this); 531 SDL_SetError("Could not connect to PulseAudio"); 532 return(-1); 533 } 534 } while (state != PA_CONTEXT_READY); 535 536 stream = SDL_NAME(pa_stream_new)( 537 context, 538 "Simple DirectMedia Layer", /* stream description */ 539 &paspec, /* sample format spec */ 540 &pacmap /* channel map */ 541 ); 542 if ( stream == NULL ) { 543 PULSE_CloseAudio(this); 544 SDL_SetError("Could not setup PulseAudio stream"); 545 return(-1); 546 } 547 548 if (SDL_NAME(pa_stream_connect_playback)(stream, NULL, &paattr, flags, 549 NULL, NULL) < 0) { 550 PULSE_CloseAudio(this); 551 SDL_SetError("Could not connect PulseAudio stream"); 552 return(-1); 553 } 554 555 do { 556 if (SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) { 557 PULSE_CloseAudio(this); 558 SDL_SetError("pa_mainloop_iterate() failed"); 559 return(-1); 560 } 561 state = SDL_NAME(pa_stream_get_state)(stream); 562 if (!PA_STREAM_IS_GOOD(state)) { 563 PULSE_CloseAudio(this); 564 SDL_SetError("Could not create to PulseAudio stream"); 565 return(-1); 566 } 567 } while (state != PA_STREAM_READY); 568 569 return(0); 570 } 571