Atlas - SDL_pulseaudio.c
Home / ext / SDL / src / audio / pulseaudio Lines: 1 | Size: 46077 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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#include "SDL_internal.h" 23 24#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO 25 26// Allow access to a raw mixing buffer 27 28#ifdef HAVE_SIGNAL_H 29#include <signal.h> 30#endif 31#include <unistd.h> 32#include <sys/types.h> 33 34#include "../SDL_sysaudio.h" 35#include "SDL_pulseaudio.h" 36#include "../../thread/SDL_systhread.h" 37 38#if (PA_PROTOCOL_VERSION < 28) 39typedef void (*pa_operation_notify_cb_t) (pa_operation *o, void *userdata); 40#endif 41 42typedef struct PulseDeviceHandle 43{ 44 char *device_path; 45 uint32_t device_index; 46} PulseDeviceHandle; 47 48// should we include monitors in the device list? Set at SDL_Init time 49static bool include_monitors = false; 50 51static pa_threaded_mainloop *pulseaudio_threaded_mainloop = NULL; 52static pa_context *pulseaudio_context = NULL; 53static SDL_Thread *pulseaudio_hotplug_thread = NULL; 54static SDL_AtomicInt pulseaudio_hotplug_thread_active; 55 56// These are the OS identifiers (i.e. ALSA strings)...these are allocated in a callback 57// when the default changes, and noticed by the hotplug thread when it alerts SDL 58// to the change. 59static char *default_sink_path = NULL; 60static char *default_source_path = NULL; 61static bool default_sink_changed = false; 62static bool default_source_changed = false; 63 64 65static const char *(*PULSEAUDIO_pa_get_library_version)(void); 66static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto)( 67 pa_channel_map *, unsigned, pa_channel_map_def_t); 68static const char *(*PULSEAUDIO_pa_strerror)(int); 69static pa_proplist *(*PULSEAUDIO_pa_proplist_new)(void); 70static void (*PULSEAUDIO_pa_proplist_free)(pa_proplist *); 71static int (*PULSEAUDIO_pa_proplist_sets)(pa_proplist *, const char *, const char *); 72 73static pa_threaded_mainloop *(*PULSEAUDIO_pa_threaded_mainloop_new)(void); 74static void (*PULSEAUDIO_pa_threaded_mainloop_set_name)(pa_threaded_mainloop *, const char *); 75static pa_mainloop_api *(*PULSEAUDIO_pa_threaded_mainloop_get_api)(pa_threaded_mainloop *); 76static int (*PULSEAUDIO_pa_threaded_mainloop_start)(pa_threaded_mainloop *); 77static void (*PULSEAUDIO_pa_threaded_mainloop_stop)(pa_threaded_mainloop *); 78static void (*PULSEAUDIO_pa_threaded_mainloop_lock)(pa_threaded_mainloop *); 79static void (*PULSEAUDIO_pa_threaded_mainloop_unlock)(pa_threaded_mainloop *); 80static void (*PULSEAUDIO_pa_threaded_mainloop_wait)(pa_threaded_mainloop *); 81static void (*PULSEAUDIO_pa_threaded_mainloop_signal)(pa_threaded_mainloop *, int); 82static void (*PULSEAUDIO_pa_threaded_mainloop_free)(pa_threaded_mainloop *); 83 84static pa_operation_state_t (*PULSEAUDIO_pa_operation_get_state)( 85 const pa_operation *); 86static void (*PULSEAUDIO_pa_operation_set_state_callback)(pa_operation *, pa_operation_notify_cb_t, void *); 87static void (*PULSEAUDIO_pa_operation_cancel)(pa_operation *); 88static void (*PULSEAUDIO_pa_operation_unref)(pa_operation *); 89 90static pa_context *(*PULSEAUDIO_pa_context_new_with_proplist)(pa_mainloop_api *, 91 const char *, 92 const pa_proplist *); 93static void (*PULSEAUDIO_pa_context_set_state_callback)(pa_context *, pa_context_notify_cb_t, void *); 94static int (*PULSEAUDIO_pa_context_connect)(pa_context *, const char *, 95 pa_context_flags_t, const pa_spawn_api *); 96static pa_operation *(*PULSEAUDIO_pa_context_get_sink_info_list)(pa_context *, pa_sink_info_cb_t, void *); 97static pa_operation *(*PULSEAUDIO_pa_context_get_source_info_list)(pa_context *, pa_source_info_cb_t, void *); 98static pa_operation *(*PULSEAUDIO_pa_context_get_sink_info_by_index)(pa_context *, uint32_t, pa_sink_info_cb_t, void *); 99static pa_operation *(*PULSEAUDIO_pa_context_get_source_info_by_index)(pa_context *, uint32_t, pa_source_info_cb_t, void *); 100static pa_context_state_t (*PULSEAUDIO_pa_context_get_state)(const pa_context *); 101static pa_operation *(*PULSEAUDIO_pa_context_subscribe)(pa_context *, pa_subscription_mask_t, pa_context_success_cb_t, void *); 102static void (*PULSEAUDIO_pa_context_set_subscribe_callback)(pa_context *, pa_context_subscribe_cb_t, void *); 103static void (*PULSEAUDIO_pa_context_disconnect)(pa_context *); 104static void (*PULSEAUDIO_pa_context_unref)(pa_context *); 105 106static pa_stream *(*PULSEAUDIO_pa_stream_new)(pa_context *, const char *, 107 const pa_sample_spec *, const pa_channel_map *); 108static void (*PULSEAUDIO_pa_stream_set_state_callback)(pa_stream *, pa_stream_notify_cb_t, void *); 109static int (*PULSEAUDIO_pa_stream_connect_playback)(pa_stream *, const char *, 110 const pa_buffer_attr *, pa_stream_flags_t, const pa_cvolume *, pa_stream *); 111static int (*PULSEAUDIO_pa_stream_connect_record)(pa_stream *, const char *, 112 const pa_buffer_attr *, pa_stream_flags_t); 113static const pa_buffer_attr *(*PULSEAUDIO_pa_stream_get_buffer_attr)(pa_stream *); 114static pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state)(const pa_stream *); 115static size_t (*PULSEAUDIO_pa_stream_writable_size)(const pa_stream *); 116static size_t (*PULSEAUDIO_pa_stream_readable_size)(const pa_stream *); 117static int (*PULSEAUDIO_pa_stream_write)(pa_stream *, const void *, size_t, 118 pa_free_cb_t, int64_t, pa_seek_mode_t); 119static int (*PULSEAUDIO_pa_stream_begin_write)(pa_stream *, void **, size_t *); 120static pa_operation *(*PULSEAUDIO_pa_stream_drain)(pa_stream *, 121 pa_stream_success_cb_t, void *); 122static int (*PULSEAUDIO_pa_stream_peek)(pa_stream *, const void **, size_t *); 123static int (*PULSEAUDIO_pa_stream_drop)(pa_stream *); 124static pa_operation *(*PULSEAUDIO_pa_stream_flush)(pa_stream *, 125 pa_stream_success_cb_t, void *); 126static int (*PULSEAUDIO_pa_stream_disconnect)(pa_stream *); 127static void (*PULSEAUDIO_pa_stream_unref)(pa_stream *); 128static void (*PULSEAUDIO_pa_stream_set_write_callback)(pa_stream *, pa_stream_request_cb_t, void *); 129static void (*PULSEAUDIO_pa_stream_set_read_callback)(pa_stream *, pa_stream_request_cb_t, void *); 130static pa_operation *(*PULSEAUDIO_pa_context_get_server_info)(pa_context *, pa_server_info_cb_t, void *); 131 132static bool load_pulseaudio_syms(void); 133 134#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC 135 136SDL_ELF_NOTE_DLOPEN( 137 "audio-libpulseaudio", 138 "Support for audio through libpulseaudio", 139 SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, 140 SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC 141) 142 143static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC; 144static SDL_SharedObject *pulseaudio_handle = NULL; 145 146static bool load_pulseaudio_sym(const char *fn, void **addr) 147{ 148 *addr = SDL_LoadFunction(pulseaudio_handle, fn); 149 if (!*addr) { 150 // Don't call SDL_SetError(): SDL_LoadFunction already did. 151 return false; 152 } 153 154 return true; 155} 156 157// cast funcs to char* first, to please GCC's strict aliasing rules. 158#define SDL_PULSEAUDIO_SYM(x) \ 159 if (!load_pulseaudio_sym(#x, (void **)(char *)&PULSEAUDIO_##x)) \ 160 return false 161 162static void UnloadPulseAudioLibrary(void) 163{ 164 if (pulseaudio_handle) { 165 SDL_UnloadObject(pulseaudio_handle); 166 pulseaudio_handle = NULL; 167 } 168} 169 170static bool LoadPulseAudioLibrary(void) 171{ 172 bool result = true; 173 if (!pulseaudio_handle) { 174 pulseaudio_handle = SDL_LoadObject(pulseaudio_library); 175 if (!pulseaudio_handle) { 176 result = false; 177 // Don't call SDL_SetError(): SDL_LoadObject already did. 178 } else { 179 result = load_pulseaudio_syms(); 180 if (!result) { 181 UnloadPulseAudioLibrary(); 182 } 183 } 184 } 185 return result; 186} 187 188#else 189 190#define SDL_PULSEAUDIO_SYM(x) PULSEAUDIO_##x = x 191 192static void UnloadPulseAudioLibrary(void) 193{ 194} 195 196static bool LoadPulseAudioLibrary(void) 197{ 198 load_pulseaudio_syms(); 199 return true; 200} 201 202#endif // SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC 203 204static bool load_pulseaudio_syms(void) 205{ 206 SDL_PULSEAUDIO_SYM(pa_get_library_version); 207 SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_new); 208 SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_get_api); 209 SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_start); 210 SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_stop); 211 SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_lock); 212 SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_unlock); 213 SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_wait); 214 SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_signal); 215 SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_free); 216 SDL_PULSEAUDIO_SYM(pa_operation_get_state); 217 SDL_PULSEAUDIO_SYM(pa_operation_cancel); 218 SDL_PULSEAUDIO_SYM(pa_operation_unref); 219 SDL_PULSEAUDIO_SYM(pa_context_new_with_proplist); 220 SDL_PULSEAUDIO_SYM(pa_context_set_state_callback); 221 SDL_PULSEAUDIO_SYM(pa_context_connect); 222 SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_list); 223 SDL_PULSEAUDIO_SYM(pa_context_get_source_info_list); 224 SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_by_index); 225 SDL_PULSEAUDIO_SYM(pa_context_get_source_info_by_index); 226 SDL_PULSEAUDIO_SYM(pa_context_get_state); 227 SDL_PULSEAUDIO_SYM(pa_context_subscribe); 228 SDL_PULSEAUDIO_SYM(pa_context_set_subscribe_callback); 229 SDL_PULSEAUDIO_SYM(pa_context_disconnect); 230 SDL_PULSEAUDIO_SYM(pa_context_unref); 231 SDL_PULSEAUDIO_SYM(pa_stream_new); 232 SDL_PULSEAUDIO_SYM(pa_stream_set_state_callback); 233 SDL_PULSEAUDIO_SYM(pa_stream_connect_playback); 234 SDL_PULSEAUDIO_SYM(pa_stream_connect_record); 235 SDL_PULSEAUDIO_SYM(pa_stream_get_buffer_attr); 236 SDL_PULSEAUDIO_SYM(pa_stream_get_state); 237 SDL_PULSEAUDIO_SYM(pa_stream_writable_size); 238 SDL_PULSEAUDIO_SYM(pa_stream_readable_size); 239 SDL_PULSEAUDIO_SYM(pa_stream_begin_write); 240 SDL_PULSEAUDIO_SYM(pa_stream_write); 241 SDL_PULSEAUDIO_SYM(pa_stream_drain); 242 SDL_PULSEAUDIO_SYM(pa_stream_disconnect); 243 SDL_PULSEAUDIO_SYM(pa_stream_peek); 244 SDL_PULSEAUDIO_SYM(pa_stream_drop); 245 SDL_PULSEAUDIO_SYM(pa_stream_flush); 246 SDL_PULSEAUDIO_SYM(pa_stream_unref); 247 SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto); 248 SDL_PULSEAUDIO_SYM(pa_strerror); 249 SDL_PULSEAUDIO_SYM(pa_stream_set_write_callback); 250 SDL_PULSEAUDIO_SYM(pa_stream_set_read_callback); 251 SDL_PULSEAUDIO_SYM(pa_context_get_server_info); 252 SDL_PULSEAUDIO_SYM(pa_proplist_new); 253 SDL_PULSEAUDIO_SYM(pa_proplist_free); 254 SDL_PULSEAUDIO_SYM(pa_proplist_sets); 255 256 // optional 257#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC 258 load_pulseaudio_sym("pa_operation_set_state_callback", (void **)(char *)&PULSEAUDIO_pa_operation_set_state_callback); // needs pulseaudio 4.0 259 load_pulseaudio_sym("pa_threaded_mainloop_set_name", (void **)(char *)&PULSEAUDIO_pa_threaded_mainloop_set_name); // needs pulseaudio 5.0 260#elif (PA_PROTOCOL_VERSION >= 29) 261 PULSEAUDIO_pa_operation_set_state_callback = pa_operation_set_state_callback; 262 PULSEAUDIO_pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; 263#elif (PA_PROTOCOL_VERSION >= 28) 264 PULSEAUDIO_pa_operation_set_state_callback = pa_operation_set_state_callback; 265 PULSEAUDIO_pa_threaded_mainloop_set_name = NULL; 266#else 267 PULSEAUDIO_pa_operation_set_state_callback = NULL; 268 PULSEAUDIO_pa_threaded_mainloop_set_name = NULL; 269#endif 270 271 return true; 272} 273 274static const char *getAppName(void) 275{ 276 return SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING); 277} 278 279static void OperationStateChangeCallback(pa_operation *o, void *userdata) 280{ 281 PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // just signal any waiting code, it can look up the details. 282} 283 284/* This function assume you are holding `mainloop`'s lock. The operation is unref'd in here, assuming 285 you did the work in the callback and just want to know it's done, though. */ 286static void WaitForPulseOperation(pa_operation *o) 287{ 288 // This checks for NO errors currently. Either fix that, check results elsewhere, or do things you don't care about. 289 SDL_assert(pulseaudio_threaded_mainloop != NULL); 290 if (o) { 291 // note that if PULSEAUDIO_pa_operation_set_state_callback == NULL, then `o` must have a callback that will signal pulseaudio_threaded_mainloop. 292 // If not, on really old (earlier PulseAudio 4.0, from the year 2013!) installs, this call will block forever. 293 // On more modern installs, we won't ever block forever, and maybe be more efficient, thanks to pa_operation_set_state_callback. 294 // WARNING: at the time of this writing: the Steam Runtime is still on PulseAudio 1.1! 295 if (PULSEAUDIO_pa_operation_set_state_callback) { 296 PULSEAUDIO_pa_operation_set_state_callback(o, OperationStateChangeCallback, NULL); 297 } 298 while (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING) { 299 PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); // this releases the lock and blocks on an internal condition variable. 300 } 301 PULSEAUDIO_pa_operation_unref(o); 302 } 303} 304 305static void DisconnectFromPulseServer(void) 306{ 307 if (pulseaudio_threaded_mainloop) { 308 PULSEAUDIO_pa_threaded_mainloop_stop(pulseaudio_threaded_mainloop); 309 } 310 if (pulseaudio_context) { 311 PULSEAUDIO_pa_context_disconnect(pulseaudio_context); 312 PULSEAUDIO_pa_context_unref(pulseaudio_context); 313 pulseaudio_context = NULL; 314 } 315 if (pulseaudio_threaded_mainloop) { 316 PULSEAUDIO_pa_threaded_mainloop_free(pulseaudio_threaded_mainloop); 317 pulseaudio_threaded_mainloop = NULL; 318 } 319} 320 321static void PulseContextStateChangeCallback(pa_context *context, void *userdata) 322{ 323 PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // just signal any waiting code, it can look up the details. 324} 325 326static bool ConnectToPulseServer(void) 327{ 328 pa_mainloop_api *mainloop_api = NULL; 329 pa_proplist *proplist = NULL; 330 const char *icon_name; 331 int state = 0; 332 333 SDL_assert(pulseaudio_threaded_mainloop == NULL); 334 SDL_assert(pulseaudio_context == NULL); 335 336 // Set up a new main loop 337 pulseaudio_threaded_mainloop = PULSEAUDIO_pa_threaded_mainloop_new(); 338 if (!pulseaudio_threaded_mainloop) { 339 return SDL_SetError("pa_threaded_mainloop_new() failed"); 340 } 341 342 if (PULSEAUDIO_pa_threaded_mainloop_set_name) { 343 PULSEAUDIO_pa_threaded_mainloop_set_name(pulseaudio_threaded_mainloop, "PulseMainloop"); 344 } 345 346 if (PULSEAUDIO_pa_threaded_mainloop_start(pulseaudio_threaded_mainloop) < 0) { 347 PULSEAUDIO_pa_threaded_mainloop_free(pulseaudio_threaded_mainloop); 348 pulseaudio_threaded_mainloop = NULL; 349 return SDL_SetError("pa_threaded_mainloop_start() failed"); 350 } 351 352 PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); 353 354 mainloop_api = PULSEAUDIO_pa_threaded_mainloop_get_api(pulseaudio_threaded_mainloop); 355 SDL_assert(mainloop_api != NULL); // this never fails, right? 356 357 proplist = PULSEAUDIO_pa_proplist_new(); 358 if (!proplist) { 359 SDL_SetError("pa_proplist_new() failed"); 360 goto failed; 361 } 362 363 icon_name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_APP_ICON_NAME); 364 if (!icon_name || *icon_name == '\0') { 365 icon_name = "applications-games"; 366 } 367 PULSEAUDIO_pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, icon_name); 368 369 pulseaudio_context = PULSEAUDIO_pa_context_new_with_proplist(mainloop_api, getAppName(), proplist); 370 if (!pulseaudio_context) { 371 SDL_SetError("pa_context_new_with_proplist() failed"); 372 goto failed; 373 } 374 PULSEAUDIO_pa_proplist_free(proplist); 375 376 PULSEAUDIO_pa_context_set_state_callback(pulseaudio_context, PulseContextStateChangeCallback, NULL); 377 378 // Connect to the PulseAudio server 379 if (PULSEAUDIO_pa_context_connect(pulseaudio_context, NULL, 0, NULL) < 0) { 380 SDL_SetError("Could not setup connection to PulseAudio"); 381 goto failed; 382 } 383 384 state = PULSEAUDIO_pa_context_get_state(pulseaudio_context); 385 while (PA_CONTEXT_IS_GOOD(state) && (state != PA_CONTEXT_READY)) { 386 PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); 387 state = PULSEAUDIO_pa_context_get_state(pulseaudio_context); 388 } 389 390 if (state != PA_CONTEXT_READY) { 391 SDL_SetError("Could not connect to PulseAudio"); 392 goto failed; 393 } 394 395 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 396 397 return true; // connected and ready! 398 399failed: 400 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 401 DisconnectFromPulseServer(); 402 return false; 403} 404 405static void WriteCallback(pa_stream *p, size_t nbytes, void *userdata) 406{ 407 struct SDL_PrivateAudioData *h = (struct SDL_PrivateAudioData *)userdata; 408 //SDL_Log("PULSEAUDIO WRITE CALLBACK! nbytes=%u", (unsigned int) nbytes); 409 h->bytes_requested += nbytes; 410 PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); 411} 412 413// This function waits until it is possible to write a full sound buffer 414static bool PULSEAUDIO_WaitDevice(SDL_AudioDevice *device) 415{ 416 struct SDL_PrivateAudioData *h = device->hidden; 417 bool result = true; 418 419 //SDL_Log("PULSEAUDIO WAITDEVICE START! mixlen=%d", available); 420 421 PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); 422 423 while (!SDL_GetAtomicInt(&device->shutdown) && (h->bytes_requested == 0)) { 424 //SDL_Log("PULSEAUDIO WAIT IN WAITDEVICE!"); 425 PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); 426 427 if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { 428 //SDL_Log("PULSEAUDIO DEVICE FAILURE IN WAITDEVICE!"); 429 result = false; 430 break; 431 } 432 } 433 434 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 435 436 return result; 437} 438 439static bool PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) 440{ 441 struct SDL_PrivateAudioData *h = device->hidden; 442 443 //SDL_Log("PULSEAUDIO PLAYDEVICE START! mixlen=%d", available); 444 445 SDL_assert(h->bytes_requested >= buffer_size); 446 447 PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); 448 const int rc = PULSEAUDIO_pa_stream_write(h->stream, buffer, buffer_size, NULL, 0LL, PA_SEEK_RELATIVE); 449 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 450 451 if (rc < 0) { 452 return false; 453 } 454 455 //SDL_Log("PULSEAUDIO FEED! nbytes=%d", buffer_size); 456 h->bytes_requested -= buffer_size; 457 458 //SDL_Log("PULSEAUDIO PLAYDEVICE END! written=%d", written); 459 return true; 460} 461 462static Uint8 *PULSEAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) 463{ 464 struct SDL_PrivateAudioData *h = device->hidden; 465 const size_t reqsize = (size_t) SDL_min(*buffer_size, h->bytes_requested); 466 size_t nbytes = reqsize; 467 void *data = NULL; 468 if (PULSEAUDIO_pa_stream_begin_write(h->stream, &data, &nbytes) == 0) { 469 *buffer_size = (int) nbytes; 470 return (Uint8 *) data; 471 } 472 473 // don't know why this would fail, but we'll fall back just in case. 474 *buffer_size = (int) reqsize; 475 return device->hidden->mixbuf; 476} 477 478static void ReadCallback(pa_stream *p, size_t nbytes, void *userdata) 479{ 480 //SDL_Log("PULSEAUDIO READ CALLBACK! nbytes=%u", (unsigned int) nbytes); 481 PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // the recording code queries what it needs, we just need to signal to end any wait 482} 483 484static bool PULSEAUDIO_WaitRecordingDevice(SDL_AudioDevice *device) 485{ 486 struct SDL_PrivateAudioData *h = device->hidden; 487 488 if (h->recordingbuf) { 489 return true; // there's still data available to read. 490 } 491 492 bool result = true; 493 494 PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); 495 496 while (!SDL_GetAtomicInt(&device->shutdown)) { 497 PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); 498 if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { 499 //SDL_Log("PULSEAUDIO DEVICE FAILURE IN WAITRECORDINGDEVICE!"); 500 result = false; 501 break; 502 } else if (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0) { 503 // a new fragment is available! 504 const void *data = NULL; 505 size_t nbytes = 0; 506 PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes); 507 SDL_assert(nbytes > 0); 508 if (!data) { // If NULL, then the buffer had a hole, ignore that 509 PULSEAUDIO_pa_stream_drop(h->stream); // drop this fragment. 510 } else { 511 // store this fragment's data for use with RecordDevice 512 //SDL_Log("PULSEAUDIO: recorded %d new bytes", (int) nbytes); 513 h->recordingbuf = (const Uint8 *)data; 514 h->recordinglen = nbytes; 515 break; 516 } 517 } 518 } 519 520 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 521 522 return result; 523} 524 525static int PULSEAUDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) 526{ 527 struct SDL_PrivateAudioData *h = device->hidden; 528 529 if (h->recordingbuf) { 530 const int cpy = SDL_min(buflen, h->recordinglen); 531 if (cpy > 0) { 532 //SDL_Log("PULSEAUDIO: fed %d recorded bytes", cpy); 533 SDL_memcpy(buffer, h->recordingbuf, cpy); 534 h->recordingbuf += cpy; 535 h->recordinglen -= cpy; 536 } 537 if (h->recordinglen == 0) { 538 h->recordingbuf = NULL; 539 PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); // don't know if you _have_ to lock for this, but just in case. 540 PULSEAUDIO_pa_stream_drop(h->stream); // done with this fragment. 541 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 542 } 543 return cpy; // new data, return it. 544 } 545 546 return 0; 547} 548 549static void PULSEAUDIO_FlushRecording(SDL_AudioDevice *device) 550{ 551 struct SDL_PrivateAudioData *h = device->hidden; 552 const void *data = NULL; 553 size_t nbytes = 0, buflen = 0; 554 555 PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); 556 557 if (h->recordingbuf) { 558 PULSEAUDIO_pa_stream_drop(h->stream); 559 h->recordingbuf = NULL; 560 h->recordinglen = 0; 561 } 562 563 buflen = PULSEAUDIO_pa_stream_readable_size(h->stream); 564 while (!SDL_GetAtomicInt(&device->shutdown) && (buflen > 0)) { 565 PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); 566 if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { 567 //SDL_Log("PULSEAUDIO DEVICE FAILURE IN FLUSHRECORDING!"); 568 SDL_AudioDeviceDisconnected(device); 569 break; 570 } 571 572 // a fragment of audio present before FlushCapture was call is 573 // still available! Just drop it. 574 PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes); 575 PULSEAUDIO_pa_stream_drop(h->stream); 576 buflen -= nbytes; 577 } 578 579 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 580} 581 582static void PULSEAUDIO_CloseDevice(SDL_AudioDevice *device) 583{ 584 PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); 585 586 if (device->hidden->stream) { 587 if (device->hidden->recordingbuf) { 588 PULSEAUDIO_pa_stream_drop(device->hidden->stream); 589 } 590 PULSEAUDIO_pa_stream_disconnect(device->hidden->stream); 591 PULSEAUDIO_pa_stream_unref(device->hidden->stream); 592 } 593 PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // in case the device thread is waiting somewhere, this will unblock it. 594 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 595 596 SDL_free(device->hidden->mixbuf); 597 SDL_free(device->hidden); 598} 599 600static void PulseStreamStateChangeCallback(pa_stream *stream, void *userdata) 601{ 602 PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // just signal any waiting code, it can look up the details. 603} 604 605// Channel maps that match the order in SDL_Audio.h 606static const pa_channel_position_t Pulse_map_1[] = { PA_CHANNEL_POSITION_MONO }; 607static const pa_channel_position_t Pulse_map_2[] = { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT }; 608 609static const pa_channel_position_t Pulse_map_3[] = { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, 610 PA_CHANNEL_POSITION_LFE }; 611 612static const pa_channel_position_t Pulse_map_4[] = { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, 613 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }; 614 615static const pa_channel_position_t Pulse_map_5[] = { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, 616 PA_CHANNEL_POSITION_LFE, 617 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }; 618 619static const pa_channel_position_t Pulse_map_6[] = { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, 620 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, 621 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }; 622 623static const pa_channel_position_t Pulse_map_7[] = { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, 624 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, 625 PA_CHANNEL_POSITION_REAR_CENTER, 626 PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }; 627 628static const pa_channel_position_t Pulse_map_8[] = { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, 629 PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, 630 PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, 631 PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }; 632 633#define COPY_CHANNEL_MAP(c) SDL_memcpy(pacmap->map, Pulse_map_##c, sizeof(Pulse_map_##c)) 634 635static void PulseCreateChannelMap(pa_channel_map *pacmap, uint8_t channels) 636{ 637 SDL_assert(channels <= PA_CHANNELS_MAX); 638 639 pacmap->channels = channels; 640 641 switch (channels) { 642 case 1: 643 COPY_CHANNEL_MAP(1); 644 break; 645 case 2: 646 COPY_CHANNEL_MAP(2); 647 break; 648 case 3: 649 COPY_CHANNEL_MAP(3); 650 break; 651 case 4: 652 COPY_CHANNEL_MAP(4); 653 break; 654 case 5: 655 COPY_CHANNEL_MAP(5); 656 break; 657 case 6: 658 COPY_CHANNEL_MAP(6); 659 break; 660 case 7: 661 COPY_CHANNEL_MAP(7); 662 break; 663 case 8: 664 COPY_CHANNEL_MAP(8); 665 break; 666 } 667 668} 669 670static bool PULSEAUDIO_OpenDevice(SDL_AudioDevice *device) 671{ 672 const bool recording = device->recording; 673 struct SDL_PrivateAudioData *h = NULL; 674 SDL_AudioFormat test_format; 675 const SDL_AudioFormat *closefmts; 676 pa_sample_spec paspec; 677 pa_buffer_attr paattr; 678 pa_channel_map pacmap; 679 pa_stream_flags_t flags = 0; 680 int format = PA_SAMPLE_INVALID; 681 bool result = true; 682 683 SDL_assert(pulseaudio_threaded_mainloop != NULL); 684 SDL_assert(pulseaudio_context != NULL); 685 686 // Initialize all variables that we clean on shutdown 687 h = device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); 688 if (!device->hidden) { 689 return false; 690 } 691 692 // Try for a closest match on audio format 693 closefmts = SDL_ClosestAudioFormats(device->spec.format); 694 while ((test_format = *(closefmts++)) != 0) { 695#ifdef DEBUG_AUDIO 696 SDL_Log("pulseaudio: Trying format 0x%4.4x", test_format); 697#endif 698 switch (test_format) { 699 case SDL_AUDIO_U8: 700 format = PA_SAMPLE_U8; 701 break; 702 case SDL_AUDIO_S16LE: 703 format = PA_SAMPLE_S16LE; 704 break; 705 case SDL_AUDIO_S16BE: 706 format = PA_SAMPLE_S16BE; 707 break; 708 case SDL_AUDIO_S32LE: 709 format = PA_SAMPLE_S32LE; 710 break; 711 case SDL_AUDIO_S32BE: 712 format = PA_SAMPLE_S32BE; 713 break; 714 case SDL_AUDIO_F32LE: 715 format = PA_SAMPLE_FLOAT32LE; 716 break; 717 case SDL_AUDIO_F32BE: 718 format = PA_SAMPLE_FLOAT32BE; 719 break; 720 default: 721 continue; 722 } 723 break; 724 } 725 if (!test_format) { 726 return SDL_SetError("pulseaudio: Unsupported audio format"); 727 } 728 device->spec.format = test_format; 729 paspec.format = format; 730 731 // Calculate the final parameters for this audio specification 732 SDL_UpdatedAudioDeviceFormat(device); 733 734 // Allocate mixing buffer 735 if (!recording) { 736 h->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size); 737 if (!h->mixbuf) { 738 return false; 739 } 740 SDL_memset(h->mixbuf, device->silence_value, device->buffer_size); 741 } 742 743 paspec.channels = device->spec.channels; 744 paspec.rate = device->spec.freq; 745 746 // Reduced prebuffering compared to the defaults. 747 748 paattr.fragsize = device->buffer_size * 2; // despite the name, this is only used for recording devices, according to PulseAudio docs! (times 2 because we want _more_ than our buffer size sent from the server at a time, which helps some drivers). 749 paattr.tlength = device->buffer_size; 750 paattr.prebuf = -1; 751 paattr.maxlength = -1; 752 paattr.minreq = -1; 753 flags |= PA_STREAM_ADJUST_LATENCY; 754 755 PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); 756 757 const char *name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME); 758 759 PulseCreateChannelMap(&pacmap, device->spec.channels); 760 761 h->stream = PULSEAUDIO_pa_stream_new( 762 pulseaudio_context, 763 (name && *name) ? name : "Audio Stream", // stream description 764 &paspec, // sample format spec 765 &pacmap // channel map 766 ); 767 768 if (!h->stream) { 769 result = SDL_SetError("Could not set up PulseAudio stream"); 770 } else { 771 int rc; 772 773 PULSEAUDIO_pa_stream_set_state_callback(h->stream, PulseStreamStateChangeCallback, NULL); 774 775 // SDL manages device moves if the default changes, so don't ever let Pulse automatically migrate this stream. 776 // UPDATE: This prevents users from moving the audio to a new sink (device) using standard tools. This is slightly in conflict 777 // with how SDL wants to manage audio devices, but if people want to do it, we should let them, so this is commented out 778 // for now. We might revisit later. 779 //flags |= PA_STREAM_DONT_MOVE; 780 781 const char *device_path = ((PulseDeviceHandle *) device->handle)->device_path; 782 if (recording) { 783 PULSEAUDIO_pa_stream_set_read_callback(h->stream, ReadCallback, h); 784 rc = PULSEAUDIO_pa_stream_connect_record(h->stream, device_path, &paattr, flags); 785 } else { 786 PULSEAUDIO_pa_stream_set_write_callback(h->stream, WriteCallback, h); 787 rc = PULSEAUDIO_pa_stream_connect_playback(h->stream, device_path, &paattr, flags, NULL, NULL); 788 } 789 790 if (rc < 0) { 791 result = SDL_SetError("Could not connect PulseAudio stream"); 792 } else { 793 int state = PULSEAUDIO_pa_stream_get_state(h->stream); 794 while (PA_STREAM_IS_GOOD(state) && (state != PA_STREAM_READY)) { 795 PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); 796 state = PULSEAUDIO_pa_stream_get_state(h->stream); 797 } 798 799 if (!PA_STREAM_IS_GOOD(state)) { 800 result = SDL_SetError("Could not connect PulseAudio stream"); 801 } else { 802 const pa_buffer_attr *actual_bufattr = PULSEAUDIO_pa_stream_get_buffer_attr(h->stream); 803 if (!actual_bufattr) { 804 result = SDL_SetError("Could not determine connected PulseAudio stream's buffer attributes"); 805 } else { 806 device->buffer_size = (int) recording ? actual_bufattr->fragsize : actual_bufattr->tlength; 807 device->sample_frames = device->buffer_size / SDL_AUDIO_FRAMESIZE(device->spec); 808 } 809 } 810 } 811 } 812 813 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 814 815 // We're (hopefully) ready to rock and roll. :-) 816 return result; 817} 818 819// device handles are device index + 1, cast to void*, so we never pass a NULL. 820 821static SDL_AudioFormat PulseFormatToSDLFormat(pa_sample_format_t format) 822{ 823 switch (format) { 824 case PA_SAMPLE_U8: 825 return SDL_AUDIO_U8; 826 case PA_SAMPLE_S16LE: 827 return SDL_AUDIO_S16LE; 828 case PA_SAMPLE_S16BE: 829 return SDL_AUDIO_S16BE; 830 case PA_SAMPLE_S32LE: 831 return SDL_AUDIO_S32LE; 832 case PA_SAMPLE_S32BE: 833 return SDL_AUDIO_S32BE; 834 case PA_SAMPLE_FLOAT32LE: 835 return SDL_AUDIO_F32LE; 836 case PA_SAMPLE_FLOAT32BE: 837 return SDL_AUDIO_F32BE; 838 default: 839 return 0; 840 } 841} 842 843static void AddPulseAudioDevice(const bool recording, const char *description, const char *name, const uint32_t index, const pa_sample_spec *sample_spec) 844{ 845 SDL_AudioSpec spec; 846 SDL_zero(spec); 847 spec.format = PulseFormatToSDLFormat(sample_spec->format); 848 spec.channels = sample_spec->channels; 849 spec.freq = sample_spec->rate; 850 PulseDeviceHandle *handle = (PulseDeviceHandle *) SDL_malloc(sizeof (PulseDeviceHandle)); 851 if (handle) { 852 handle->device_path = SDL_strdup(name); 853 if (!handle->device_path) { 854 SDL_free(handle); 855 } else { 856 handle->device_index = index; 857 SDL_AddAudioDevice(recording, description, &spec, handle); 858 } 859 } 860} 861 862// This is called when PulseAudio adds an playback ("sink") device. 863static void SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data) 864{ 865 if (i) { 866 AddPulseAudioDevice(false, i->description, i->name, i->index, &i->sample_spec); 867 } 868 PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); 869} 870 871// This is called when PulseAudio adds a recording ("source") device. 872static void SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data) 873{ 874 // Maybe skip "monitor" sources. These are just output from other sinks. 875 if (i && (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX))) { 876 AddPulseAudioDevice(true, i->description, i->name, i->index, &i->sample_spec); 877 } 878 PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); 879} 880 881static void ServerInfoCallback(pa_context *c, const pa_server_info *i, void *data) 882{ 883 //SDL_Log("PULSEAUDIO ServerInfoCallback!"); 884 885 if (!default_sink_path || (SDL_strcmp(default_sink_path, i->default_sink_name) != 0)) { 886 char *str = SDL_strdup(i->default_sink_name); 887 if (str) { 888 SDL_free(default_sink_path); 889 default_sink_path = str; 890 default_sink_changed = true; 891 } 892 } 893 894 if (!default_source_path || (SDL_strcmp(default_source_path, i->default_source_name) != 0)) { 895 char *str = SDL_strdup(i->default_source_name); 896 if (str) { 897 SDL_free(default_source_path); 898 default_source_path = str; 899 default_source_changed = true; 900 } 901 } 902 903 PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); 904} 905 906static bool FindAudioDeviceByIndex(SDL_AudioDevice *device, void *userdata) 907{ 908 const uint32_t idx = (uint32_t) (uintptr_t) userdata; 909 const PulseDeviceHandle *handle = (const PulseDeviceHandle *) device->handle; 910 return (handle->device_index == idx); 911} 912 913static bool FindAudioDeviceByPath(SDL_AudioDevice *device, void *userdata) 914{ 915 const char *path = (const char *) userdata; 916 const PulseDeviceHandle *handle = (const PulseDeviceHandle *) device->handle; 917 return (SDL_strcmp(handle->device_path, path) == 0); 918} 919 920// This is called when PulseAudio has a device connected/removed/changed. 921static void HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *data) 922{ 923 const bool added = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW); 924 const bool removed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE); 925 const bool changed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE); 926 927 if (added || removed || changed) { // we only care about add/remove events. 928 const bool sink = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK); 929 const bool source = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE); 930 931 if (changed) { 932 PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_server_info(pulseaudio_context, ServerInfoCallback, NULL)); 933 } 934 935 /* adds need sink details from the PulseAudio server. Another callback... 936 (just unref all these operations right away, because we aren't going to wait on them 937 and their callbacks will handle any work, so they can free as soon as that happens.) */ 938 if (added && sink) { 939 PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_sink_info_by_index(pulseaudio_context, idx, SinkInfoCallback, NULL)); 940 } else if (added && source) { 941 PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_source_info_by_index(pulseaudio_context, idx, SourceInfoCallback, NULL)); 942 } else if (removed && (sink || source)) { 943 // removes we can handle just with the device index. 944 SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByIndex, (void *)(uintptr_t)idx)); 945 } 946 } 947 PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); 948} 949 950static bool CheckDefaultDevice(const bool changed, char *device_path) 951{ 952 if (!changed) { 953 return false; // nothing's happening, leave the flag marked as unchanged. 954 } else if (!device_path) { 955 return true; // check again later, we don't have a device name... 956 } 957 958 SDL_AudioDevice *device = SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByPath, device_path); 959 if (device) { // if NULL, we might still be waiting for a SinkInfoCallback or something, we'll try later. 960 SDL_DefaultAudioDeviceChanged(device); 961 return false; // changing complete, set flag to unchanged for future tests. 962 } 963 return true; // couldn't find the changed device, leave it marked as changed to try again later. 964} 965 966// this runs as a thread while the Pulse target is initialized to catch hotplug events. 967static int SDLCALL HotplugThread(void *data) 968{ 969 pa_operation *op; 970 971 SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_LOW); 972 PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); 973 PULSEAUDIO_pa_context_set_subscribe_callback(pulseaudio_context, HotplugCallback, NULL); 974 975 // don't WaitForPulseOperation on the subscription; when it's done we'll be able to get hotplug events, but waiting doesn't changing anything. 976 op = PULSEAUDIO_pa_context_subscribe(pulseaudio_context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SERVER, NULL, NULL); 977 978 SDL_SignalSemaphore((SDL_Semaphore *) data); 979 980 while (SDL_GetAtomicInt(&pulseaudio_hotplug_thread_active)) { 981 PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); 982 if (op && PULSEAUDIO_pa_operation_get_state(op) != PA_OPERATION_RUNNING) { 983 PULSEAUDIO_pa_operation_unref(op); 984 op = NULL; 985 } 986 987 // Update default devices; don't hold the pulse lock during this, since it could deadlock vs a playing device that we're about to lock here. 988 bool check_default_sink = default_sink_changed; 989 bool check_default_source = default_source_changed; 990 char *current_default_sink = check_default_sink ? SDL_strdup(default_sink_path) : NULL; 991 char *current_default_source = check_default_source ? SDL_strdup(default_source_path) : NULL; 992 default_sink_changed = default_source_changed = false; 993 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 994 check_default_sink = CheckDefaultDevice(check_default_sink, current_default_sink); 995 check_default_source = CheckDefaultDevice(check_default_source, current_default_source); 996 PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); 997 998 // free our copies (which will be NULL if nothing changed) 999 SDL_free(current_default_sink); 1000 SDL_free(current_default_source); 1001 1002 // set these to true if we didn't handle the change OR there was _another_ change while we were working unlocked. 1003 default_sink_changed = (default_sink_changed || check_default_sink); 1004 default_source_changed = (default_source_changed || check_default_source); 1005 } 1006 1007 if (op) { 1008 PULSEAUDIO_pa_operation_unref(op); 1009 } 1010 1011 PULSEAUDIO_pa_context_set_subscribe_callback(pulseaudio_context, NULL, NULL); 1012 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 1013 return 0; 1014} 1015 1016static void PULSEAUDIO_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) 1017{ 1018 SDL_Semaphore *ready_sem = SDL_CreateSemaphore(0); 1019 1020 PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); 1021 WaitForPulseOperation(PULSEAUDIO_pa_context_get_server_info(pulseaudio_context, ServerInfoCallback, NULL)); 1022 WaitForPulseOperation(PULSEAUDIO_pa_context_get_sink_info_list(pulseaudio_context, SinkInfoCallback, NULL)); 1023 WaitForPulseOperation(PULSEAUDIO_pa_context_get_source_info_list(pulseaudio_context, SourceInfoCallback, NULL)); 1024 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 1025 1026 if (default_sink_path) { 1027 *default_playback = SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByPath, default_sink_path); 1028 } 1029 1030 if (default_source_path) { 1031 *default_recording = SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByPath, default_source_path); 1032 } 1033 1034 // ok, we have a sane list, let's set up hotplug notifications now... 1035 SDL_SetAtomicInt(&pulseaudio_hotplug_thread_active, 1); 1036 pulseaudio_hotplug_thread = SDL_CreateThread(HotplugThread, "PulseHotplug", ready_sem); 1037 if (pulseaudio_hotplug_thread) { 1038 SDL_WaitSemaphore(ready_sem); // wait until the thread hits it's main loop. 1039 } else { 1040 SDL_SetAtomicInt(&pulseaudio_hotplug_thread_active, 0); // thread failed to start, we'll go on without hotplug. 1041 } 1042 1043 SDL_DestroySemaphore(ready_sem); 1044} 1045 1046static void PULSEAUDIO_FreeDeviceHandle(SDL_AudioDevice *device) 1047{ 1048 PulseDeviceHandle *handle = (PulseDeviceHandle *) device->handle; 1049 SDL_free(handle->device_path); 1050 SDL_free(handle); 1051} 1052 1053static void PULSEAUDIO_DeinitializeStart(void) 1054{ 1055 if (pulseaudio_hotplug_thread) { 1056 PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); 1057 SDL_SetAtomicInt(&pulseaudio_hotplug_thread_active, 0); 1058 PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); 1059 PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); 1060 SDL_WaitThread(pulseaudio_hotplug_thread, NULL); 1061 pulseaudio_hotplug_thread = NULL; 1062 } 1063} 1064 1065static void PULSEAUDIO_Deinitialize(void) 1066{ 1067 DisconnectFromPulseServer(); 1068 1069 SDL_free(default_sink_path); 1070 default_sink_path = NULL; 1071 default_sink_changed = false; 1072 SDL_free(default_source_path); 1073 default_source_path = NULL; 1074 default_source_changed = false; 1075 1076 UnloadPulseAudioLibrary(); 1077} 1078 1079static bool PULSEAUDIO_Init(SDL_AudioDriverImpl *impl) 1080{ 1081 if (!LoadPulseAudioLibrary()) { 1082 return false; 1083 } else if (!ConnectToPulseServer()) { 1084 UnloadPulseAudioLibrary(); 1085 return false; 1086 } 1087 1088 include_monitors = SDL_GetHintBoolean(SDL_HINT_AUDIO_INCLUDE_MONITORS, false); 1089 1090 impl->DetectDevices = PULSEAUDIO_DetectDevices; 1091 impl->OpenDevice = PULSEAUDIO_OpenDevice; 1092 impl->PlayDevice = PULSEAUDIO_PlayDevice; 1093 impl->WaitDevice = PULSEAUDIO_WaitDevice; 1094 impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf; 1095 impl->CloseDevice = PULSEAUDIO_CloseDevice; 1096 impl->DeinitializeStart = PULSEAUDIO_DeinitializeStart; 1097 impl->Deinitialize = PULSEAUDIO_Deinitialize; 1098 impl->WaitRecordingDevice = PULSEAUDIO_WaitRecordingDevice; 1099 impl->RecordDevice = PULSEAUDIO_RecordDevice; 1100 impl->FlushRecording = PULSEAUDIO_FlushRecording; 1101 impl->FreeDeviceHandle = PULSEAUDIO_FreeDeviceHandle; 1102 1103 impl->HasRecordingSupport = true; 1104 1105 return true; 1106} 1107 1108AudioBootStrap PULSEAUDIO_bootstrap = { 1109 "pulseaudio", "PulseAudio", PULSEAUDIO_Init, false, false 1110}; 1111 1112#endif // SDL_AUDIO_DRIVER_PULSEAUDIO 1113[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.