src/audio/pulseaudio/SDL_pulseaudio.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 23 Oct 2018 01:34:03 -0400
branchSDL-ryan-batching-renderer
changeset 12352 2b1707cc57fc
parent 11878 625cdd85edfe
child 12201 8bdc4d340419
permissions -rw-r--r--
render: Add floating point versions of various draw APIs.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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 #include "SDL_assert.h"
    30 
    31 #if SDL_AUDIO_DRIVER_PULSEAUDIO
    32 
    33 /* Allow access to a raw mixing buffer */
    34 
    35 #ifdef HAVE_SIGNAL_H
    36 #include <signal.h>
    37 #endif
    38 #include <unistd.h>
    39 #include <sys/types.h>
    40 #include <pulse/pulseaudio.h>
    41 
    42 #include "SDL_timer.h"
    43 #include "SDL_audio.h"
    44 #include "../SDL_audio_c.h"
    45 #include "SDL_pulseaudio.h"
    46 #include "SDL_loadso.h"
    47 #include "../../thread/SDL_systhread.h"
    48 
    49 #if (PA_API_VERSION < 12)
    50 /** Return non-zero if the passed state is one of the connected states */
    51 static SDL_INLINE int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
    52     return
    53         x == PA_CONTEXT_CONNECTING ||
    54         x == PA_CONTEXT_AUTHORIZING ||
    55         x == PA_CONTEXT_SETTING_NAME ||
    56         x == PA_CONTEXT_READY;
    57 }
    58 /** Return non-zero if the passed state is one of the connected states */
    59 static SDL_INLINE int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
    60     return
    61         x == PA_STREAM_CREATING ||
    62         x == PA_STREAM_READY;
    63 }
    64 #endif /* pulseaudio <= 0.9.10 */
    65 
    66 
    67 static const char *(*PULSEAUDIO_pa_get_library_version) (void);
    68 static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto) (
    69     pa_channel_map *, unsigned, pa_channel_map_def_t);
    70 static const char * (*PULSEAUDIO_pa_strerror) (int);
    71 static pa_mainloop * (*PULSEAUDIO_pa_mainloop_new) (void);
    72 static pa_mainloop_api * (*PULSEAUDIO_pa_mainloop_get_api) (pa_mainloop *);
    73 static int (*PULSEAUDIO_pa_mainloop_iterate) (pa_mainloop *, int, int *);
    74 static int (*PULSEAUDIO_pa_mainloop_run) (pa_mainloop *, int *);
    75 static void (*PULSEAUDIO_pa_mainloop_quit) (pa_mainloop *, int);
    76 static void (*PULSEAUDIO_pa_mainloop_free) (pa_mainloop *);
    77 
    78 static pa_operation_state_t (*PULSEAUDIO_pa_operation_get_state) (
    79     pa_operation *);
    80 static void (*PULSEAUDIO_pa_operation_cancel) (pa_operation *);
    81 static void (*PULSEAUDIO_pa_operation_unref) (pa_operation *);
    82 
    83 static pa_context * (*PULSEAUDIO_pa_context_new) (pa_mainloop_api *,
    84     const char *);
    85 static int (*PULSEAUDIO_pa_context_connect) (pa_context *, const char *,
    86     pa_context_flags_t, const pa_spawn_api *);
    87 static pa_operation * (*PULSEAUDIO_pa_context_get_sink_info_list) (pa_context *, pa_sink_info_cb_t, void *);
    88 static pa_operation * (*PULSEAUDIO_pa_context_get_source_info_list) (pa_context *, pa_source_info_cb_t, void *);
    89 static pa_operation * (*PULSEAUDIO_pa_context_get_sink_info_by_index) (pa_context *, uint32_t, pa_sink_info_cb_t, void *);
    90 static pa_operation * (*PULSEAUDIO_pa_context_get_source_info_by_index) (pa_context *, uint32_t, pa_source_info_cb_t, void *);
    91 static pa_context_state_t (*PULSEAUDIO_pa_context_get_state) (pa_context *);
    92 static pa_operation * (*PULSEAUDIO_pa_context_subscribe) (pa_context *, pa_subscription_mask_t, pa_context_success_cb_t, void *);
    93 static void (*PULSEAUDIO_pa_context_set_subscribe_callback) (pa_context *, pa_context_subscribe_cb_t, void *);
    94 static void (*PULSEAUDIO_pa_context_disconnect) (pa_context *);
    95 static void (*PULSEAUDIO_pa_context_unref) (pa_context *);
    96 
    97 static pa_stream * (*PULSEAUDIO_pa_stream_new) (pa_context *, const char *,
    98     const pa_sample_spec *, const pa_channel_map *);
    99 static int (*PULSEAUDIO_pa_stream_connect_playback) (pa_stream *, const char *,
   100     const pa_buffer_attr *, pa_stream_flags_t, pa_cvolume *, pa_stream *);
   101 static int (*PULSEAUDIO_pa_stream_connect_record) (pa_stream *, const char *,
   102     const pa_buffer_attr *, pa_stream_flags_t);
   103 static pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state) (pa_stream *);
   104 static size_t (*PULSEAUDIO_pa_stream_writable_size) (pa_stream *);
   105 static size_t (*PULSEAUDIO_pa_stream_readable_size) (pa_stream *);
   106 static int (*PULSEAUDIO_pa_stream_write) (pa_stream *, const void *, size_t,
   107     pa_free_cb_t, int64_t, pa_seek_mode_t);
   108 static pa_operation * (*PULSEAUDIO_pa_stream_drain) (pa_stream *,
   109     pa_stream_success_cb_t, void *);
   110 static int (*PULSEAUDIO_pa_stream_peek) (pa_stream *, const void **, size_t *);
   111 static int (*PULSEAUDIO_pa_stream_drop) (pa_stream *);
   112 static pa_operation * (*PULSEAUDIO_pa_stream_flush)	(pa_stream *,
   113     pa_stream_success_cb_t, void *);
   114 static int (*PULSEAUDIO_pa_stream_disconnect) (pa_stream *);
   115 static void (*PULSEAUDIO_pa_stream_unref) (pa_stream *);
   116 
   117 static int load_pulseaudio_syms(void);
   118 
   119 
   120 #ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC
   121 
   122 static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC;
   123 static void *pulseaudio_handle = NULL;
   124 
   125 static int
   126 load_pulseaudio_sym(const char *fn, void **addr)
   127 {
   128     *addr = SDL_LoadFunction(pulseaudio_handle, fn);
   129     if (*addr == NULL) {
   130         /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
   131         return 0;
   132     }
   133 
   134     return 1;
   135 }
   136 
   137 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
   138 #define SDL_PULSEAUDIO_SYM(x) \
   139     if (!load_pulseaudio_sym(#x, (void **) (char *) &PULSEAUDIO_##x)) return -1
   140 
   141 static void
   142 UnloadPulseAudioLibrary(void)
   143 {
   144     if (pulseaudio_handle != NULL) {
   145         SDL_UnloadObject(pulseaudio_handle);
   146         pulseaudio_handle = NULL;
   147     }
   148 }
   149 
   150 static int
   151 LoadPulseAudioLibrary(void)
   152 {
   153     int retval = 0;
   154     if (pulseaudio_handle == NULL) {
   155         pulseaudio_handle = SDL_LoadObject(pulseaudio_library);
   156         if (pulseaudio_handle == NULL) {
   157             retval = -1;
   158             /* Don't call SDL_SetError(): SDL_LoadObject already did. */
   159         } else {
   160             retval = load_pulseaudio_syms();
   161             if (retval < 0) {
   162                 UnloadPulseAudioLibrary();
   163             }
   164         }
   165     }
   166     return retval;
   167 }
   168 
   169 #else
   170 
   171 #define SDL_PULSEAUDIO_SYM(x) PULSEAUDIO_##x = x
   172 
   173 static void
   174 UnloadPulseAudioLibrary(void)
   175 {
   176 }
   177 
   178 static int
   179 LoadPulseAudioLibrary(void)
   180 {
   181     load_pulseaudio_syms();
   182     return 0;
   183 }
   184 
   185 #endif /* SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC */
   186 
   187 
   188 static int
   189 load_pulseaudio_syms(void)
   190 {
   191     SDL_PULSEAUDIO_SYM(pa_get_library_version);
   192     SDL_PULSEAUDIO_SYM(pa_mainloop_new);
   193     SDL_PULSEAUDIO_SYM(pa_mainloop_get_api);
   194     SDL_PULSEAUDIO_SYM(pa_mainloop_iterate);
   195     SDL_PULSEAUDIO_SYM(pa_mainloop_run);
   196     SDL_PULSEAUDIO_SYM(pa_mainloop_quit);
   197     SDL_PULSEAUDIO_SYM(pa_mainloop_free);
   198     SDL_PULSEAUDIO_SYM(pa_operation_get_state);
   199     SDL_PULSEAUDIO_SYM(pa_operation_cancel);
   200     SDL_PULSEAUDIO_SYM(pa_operation_unref);
   201     SDL_PULSEAUDIO_SYM(pa_context_new);
   202     SDL_PULSEAUDIO_SYM(pa_context_connect);
   203     SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_list);
   204     SDL_PULSEAUDIO_SYM(pa_context_get_source_info_list);
   205     SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_by_index);
   206     SDL_PULSEAUDIO_SYM(pa_context_get_source_info_by_index);
   207     SDL_PULSEAUDIO_SYM(pa_context_get_state);
   208     SDL_PULSEAUDIO_SYM(pa_context_subscribe);
   209     SDL_PULSEAUDIO_SYM(pa_context_set_subscribe_callback);
   210     SDL_PULSEAUDIO_SYM(pa_context_disconnect);
   211     SDL_PULSEAUDIO_SYM(pa_context_unref);
   212     SDL_PULSEAUDIO_SYM(pa_stream_new);
   213     SDL_PULSEAUDIO_SYM(pa_stream_connect_playback);
   214     SDL_PULSEAUDIO_SYM(pa_stream_connect_record);
   215     SDL_PULSEAUDIO_SYM(pa_stream_get_state);
   216     SDL_PULSEAUDIO_SYM(pa_stream_writable_size);
   217     SDL_PULSEAUDIO_SYM(pa_stream_readable_size);
   218     SDL_PULSEAUDIO_SYM(pa_stream_write);
   219     SDL_PULSEAUDIO_SYM(pa_stream_drain);
   220     SDL_PULSEAUDIO_SYM(pa_stream_disconnect);
   221     SDL_PULSEAUDIO_SYM(pa_stream_peek);
   222     SDL_PULSEAUDIO_SYM(pa_stream_drop);
   223     SDL_PULSEAUDIO_SYM(pa_stream_flush);
   224     SDL_PULSEAUDIO_SYM(pa_stream_unref);
   225     SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto);
   226     SDL_PULSEAUDIO_SYM(pa_strerror);
   227     return 0;
   228 }
   229 
   230 static SDL_INLINE int
   231 squashVersion(const int major, const int minor, const int patch)
   232 {
   233     return ((major & 0xFF) << 16) | ((minor & 0xFF) << 8) | (patch & 0xFF);
   234 }
   235 
   236 /* Workaround for older pulse: pa_context_new() must have non-NULL appname */
   237 static const char *
   238 getAppName(void)
   239 {
   240     const char *verstr = PULSEAUDIO_pa_get_library_version();
   241     if (verstr != NULL) {
   242         int maj, min, patch;
   243         if (SDL_sscanf(verstr, "%d.%d.%d", &maj, &min, &patch) == 3) {
   244             if (squashVersion(maj, min, patch) >= squashVersion(0, 9, 15)) {
   245                 return NULL;  /* 0.9.15+ handles NULL correctly. */
   246             }
   247         }
   248     }
   249     return "SDL Application";  /* oh well. */
   250 }
   251 
   252 static void
   253 WaitForPulseOperation(pa_mainloop *mainloop, pa_operation *o)
   254 {
   255     /* This checks for NO errors currently. Either fix that, check results elsewhere, or do things you don't care about. */
   256     if (mainloop && o) {
   257         SDL_bool okay = SDL_TRUE;
   258         while (okay && (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING)) {
   259             okay = (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) >= 0);
   260         }
   261         PULSEAUDIO_pa_operation_unref(o);
   262     }
   263 }
   264 
   265 static void
   266 DisconnectFromPulseServer(pa_mainloop *mainloop, pa_context *context)
   267 {
   268     if (context) {
   269         PULSEAUDIO_pa_context_disconnect(context);
   270         PULSEAUDIO_pa_context_unref(context);
   271     }
   272     if (mainloop != NULL) {
   273         PULSEAUDIO_pa_mainloop_free(mainloop);
   274     }
   275 }
   276 
   277 static int
   278 ConnectToPulseServer_Internal(pa_mainloop **_mainloop, pa_context **_context)
   279 {
   280     pa_mainloop *mainloop = NULL;
   281     pa_context *context = NULL;
   282     pa_mainloop_api *mainloop_api = NULL;
   283     int state = 0;
   284 
   285     *_mainloop = NULL;
   286     *_context = NULL;
   287 
   288     /* Set up a new main loop */
   289     if (!(mainloop = PULSEAUDIO_pa_mainloop_new())) {
   290         return SDL_SetError("pa_mainloop_new() failed");
   291     }
   292 
   293     *_mainloop = mainloop;
   294 
   295     mainloop_api = PULSEAUDIO_pa_mainloop_get_api(mainloop);
   296     SDL_assert(mainloop_api);  /* this never fails, right? */
   297 
   298     context = PULSEAUDIO_pa_context_new(mainloop_api, getAppName());
   299     if (!context) {
   300         return SDL_SetError("pa_context_new() failed");
   301     }
   302     *_context = context;
   303 
   304     /* Connect to the PulseAudio server */
   305     if (PULSEAUDIO_pa_context_connect(context, NULL, 0, NULL) < 0) {
   306         return SDL_SetError("Could not setup connection to PulseAudio");
   307     }
   308 
   309     do {
   310         if (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) < 0) {
   311             return SDL_SetError("pa_mainloop_iterate() failed");
   312         }
   313         state = PULSEAUDIO_pa_context_get_state(context);
   314         if (!PA_CONTEXT_IS_GOOD(state)) {
   315             return SDL_SetError("Could not connect to PulseAudio");
   316         }
   317     } while (state != PA_CONTEXT_READY);
   318 
   319     return 0;  /* connected and ready! */
   320 }
   321 
   322 static int
   323 ConnectToPulseServer(pa_mainloop **_mainloop, pa_context **_context)
   324 {
   325     const int retval = ConnectToPulseServer_Internal(_mainloop, _context);
   326     if (retval < 0) {
   327         DisconnectFromPulseServer(*_mainloop, *_context);
   328     }
   329     return retval;
   330 }
   331 
   332 
   333 /* This function waits until it is possible to write a full sound buffer */
   334 static void
   335 PULSEAUDIO_WaitDevice(_THIS)
   336 {
   337     struct SDL_PrivateAudioData *h = this->hidden;
   338 
   339     while (SDL_AtomicGet(&this->enabled)) {
   340         if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
   341             PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
   342             PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
   343             SDL_OpenedAudioDeviceDisconnected(this);
   344             return;
   345         }
   346         if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) {
   347             return;
   348         }
   349     }
   350 }
   351 
   352 static void
   353 PULSEAUDIO_PlayDevice(_THIS)
   354 {
   355     /* Write the audio data */
   356     struct SDL_PrivateAudioData *h = this->hidden;
   357     if (SDL_AtomicGet(&this->enabled)) {
   358         if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
   359             SDL_OpenedAudioDeviceDisconnected(this);
   360         }
   361     }
   362 }
   363 
   364 static Uint8 *
   365 PULSEAUDIO_GetDeviceBuf(_THIS)
   366 {
   367     return (this->hidden->mixbuf);
   368 }
   369 
   370 
   371 static int
   372 PULSEAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
   373 {
   374     struct SDL_PrivateAudioData *h = this->hidden;
   375     const void *data = NULL;
   376     size_t nbytes = 0;
   377 
   378     while (SDL_AtomicGet(&this->enabled)) {
   379         if (h->capturebuf != NULL) {
   380             const int cpy = SDL_min(buflen, h->capturelen);
   381             SDL_memcpy(buffer, h->capturebuf, cpy);
   382             /*printf("PULSEAUDIO: fed %d captured bytes\n", cpy);*/
   383             h->capturebuf += cpy;
   384             h->capturelen -= cpy;
   385             if (h->capturelen == 0) {
   386                 h->capturebuf = NULL;
   387                 PULSEAUDIO_pa_stream_drop(h->stream);  /* done with this fragment. */
   388             }
   389             return cpy;  /* new data, return it. */
   390         }
   391 
   392         if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
   393             PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
   394             PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
   395             SDL_OpenedAudioDeviceDisconnected(this);
   396             return -1;  /* uhoh, pulse failed! */
   397         }
   398 
   399         if (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0) {
   400             continue;  /* no data available yet. */
   401         }
   402 
   403         /* a new fragment is available! */
   404         PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
   405         SDL_assert(nbytes > 0);
   406         if (data == NULL) {  /* NULL==buffer had a hole. Ignore that. */
   407             PULSEAUDIO_pa_stream_drop(h->stream);  /* drop this fragment. */
   408         } else {
   409             /* store this fragment's data, start feeding it to SDL. */
   410             /*printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);*/
   411             h->capturebuf = (const Uint8 *) data;
   412             h->capturelen = nbytes;
   413         }
   414     }
   415 
   416     return -1;  /* not enabled? */
   417 }
   418 
   419 static void
   420 PULSEAUDIO_FlushCapture(_THIS)
   421 {
   422     struct SDL_PrivateAudioData *h = this->hidden;
   423     const void *data = NULL;
   424     size_t nbytes = 0;
   425 
   426     if (h->capturebuf != NULL) {
   427         PULSEAUDIO_pa_stream_drop(h->stream);
   428         h->capturebuf = NULL;
   429         h->capturelen = 0;
   430     }
   431 
   432     while (SDL_TRUE) {
   433         if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
   434             PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
   435             PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
   436             SDL_OpenedAudioDeviceDisconnected(this);
   437             return;  /* uhoh, pulse failed! */
   438         }
   439 
   440         if (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0) {
   441             break;  /* no data available, so we're done. */
   442         }
   443 
   444         /* a new fragment is available! Just dump it. */
   445         PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
   446         PULSEAUDIO_pa_stream_drop(h->stream);  /* drop this fragment. */
   447     }
   448 }
   449 
   450 static void
   451 PULSEAUDIO_CloseDevice(_THIS)
   452 {
   453     if (this->hidden->stream) {
   454         if (this->hidden->capturebuf != NULL) {
   455             PULSEAUDIO_pa_stream_drop(this->hidden->stream);
   456         }
   457         PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
   458         PULSEAUDIO_pa_stream_unref(this->hidden->stream);
   459     }
   460 
   461     DisconnectFromPulseServer(this->hidden->mainloop, this->hidden->context);
   462     SDL_free(this->hidden->mixbuf);
   463     SDL_free(this->hidden->device_name);
   464     SDL_free(this->hidden);
   465 }
   466 
   467 static void
   468 SinkDeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
   469 {
   470     if (i) {
   471         char **devname = (char **) data;
   472         *devname = SDL_strdup(i->name);
   473     }
   474 }
   475 
   476 static void
   477 SourceDeviceNameCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
   478 {
   479     if (i) {
   480         char **devname = (char **) data;
   481         *devname = SDL_strdup(i->name);
   482     }
   483 }
   484 
   485 static SDL_bool
   486 FindDeviceName(struct SDL_PrivateAudioData *h, const int iscapture, void *handle)
   487 {
   488     const uint32_t idx = ((uint32_t) ((size_t) handle)) - 1;
   489 
   490     if (handle == NULL) {  /* NULL == default device. */
   491         return SDL_TRUE;
   492     }
   493 
   494     if (iscapture) {
   495         WaitForPulseOperation(h->mainloop,
   496             PULSEAUDIO_pa_context_get_source_info_by_index(h->context, idx,
   497                 SourceDeviceNameCallback, &h->device_name));
   498     } else {
   499         WaitForPulseOperation(h->mainloop,
   500             PULSEAUDIO_pa_context_get_sink_info_by_index(h->context, idx,
   501                 SinkDeviceNameCallback, &h->device_name));
   502     }
   503 
   504     return (h->device_name != NULL);
   505 }
   506 
   507 static int
   508 PULSEAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   509 {
   510     struct SDL_PrivateAudioData *h = NULL;
   511     Uint16 test_format = 0;
   512     pa_sample_spec paspec;
   513     pa_buffer_attr paattr;
   514     pa_channel_map pacmap;
   515     pa_stream_flags_t flags = 0;
   516     int state = 0;
   517     int rc = 0;
   518 
   519     /* Initialize all variables that we clean on shutdown */
   520     h = this->hidden = (struct SDL_PrivateAudioData *)
   521         SDL_malloc((sizeof *this->hidden));
   522     if (this->hidden == NULL) {
   523         return SDL_OutOfMemory();
   524     }
   525     SDL_zerop(this->hidden);
   526 
   527     paspec.format = PA_SAMPLE_INVALID;
   528 
   529     /* Try for a closest match on audio format */
   530     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   531          (paspec.format == PA_SAMPLE_INVALID) && test_format;) {
   532 #ifdef DEBUG_AUDIO
   533         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   534 #endif
   535         switch (test_format) {
   536         case AUDIO_U8:
   537             paspec.format = PA_SAMPLE_U8;
   538             break;
   539         case AUDIO_S16LSB:
   540             paspec.format = PA_SAMPLE_S16LE;
   541             break;
   542         case AUDIO_S16MSB:
   543             paspec.format = PA_SAMPLE_S16BE;
   544             break;
   545         case AUDIO_S32LSB:
   546             paspec.format = PA_SAMPLE_S32LE;
   547             break;
   548         case AUDIO_S32MSB:
   549             paspec.format = PA_SAMPLE_S32BE;
   550             break;
   551         case AUDIO_F32LSB:
   552             paspec.format = PA_SAMPLE_FLOAT32LE;
   553             break;
   554         case AUDIO_F32MSB:
   555             paspec.format = PA_SAMPLE_FLOAT32BE;
   556             break;
   557         default:
   558             paspec.format = PA_SAMPLE_INVALID;
   559             break;
   560         }
   561         if (paspec.format == PA_SAMPLE_INVALID) {
   562             test_format = SDL_NextAudioFormat();
   563         }
   564     }
   565     if (paspec.format == PA_SAMPLE_INVALID) {
   566         return SDL_SetError("Couldn't find any hardware audio formats");
   567     }
   568     this->spec.format = test_format;
   569 
   570     /* Calculate the final parameters for this audio specification */
   571 #ifdef PA_STREAM_ADJUST_LATENCY
   572     this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
   573 #endif
   574     SDL_CalculateAudioSpec(&this->spec);
   575 
   576     /* Allocate mixing buffer */
   577     if (!iscapture) {
   578         h->mixlen = this->spec.size;
   579         h->mixbuf = (Uint8 *) SDL_malloc(h->mixlen);
   580         if (h->mixbuf == NULL) {
   581             return SDL_OutOfMemory();
   582         }
   583         SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
   584     }
   585 
   586     paspec.channels = this->spec.channels;
   587     paspec.rate = this->spec.freq;
   588 
   589     /* Reduced prebuffering compared to the defaults. */
   590 #ifdef PA_STREAM_ADJUST_LATENCY
   591     /* 2x original requested bufsize */
   592     paattr.tlength = h->mixlen * 4;
   593     paattr.prebuf = -1;
   594     paattr.maxlength = -1;
   595     /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
   596     paattr.minreq = h->mixlen;
   597     flags = PA_STREAM_ADJUST_LATENCY;
   598 #else
   599     paattr.tlength = h->mixlen*2;
   600     paattr.prebuf = h->mixlen*2;
   601     paattr.maxlength = h->mixlen*2;
   602     paattr.minreq = h->mixlen;
   603 #endif
   604 
   605     if (ConnectToPulseServer(&h->mainloop, &h->context) < 0) {
   606         return SDL_SetError("Could not connect to PulseAudio server");
   607     }
   608 
   609     if (!FindDeviceName(h, iscapture, handle)) {
   610         return SDL_SetError("Requested PulseAudio sink/source missing?");
   611     }
   612 
   613     /* The SDL ALSA output hints us that we use Windows' channel mapping */
   614     /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
   615     PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels,
   616                                         PA_CHANNEL_MAP_WAVEEX);
   617 
   618     h->stream = PULSEAUDIO_pa_stream_new(
   619         h->context,
   620         "Simple DirectMedia Layer", /* stream description */
   621         &paspec,    /* sample format spec */
   622         &pacmap     /* channel map */
   623         );
   624 
   625     if (h->stream == NULL) {
   626         return SDL_SetError("Could not set up PulseAudio stream");
   627     }
   628 
   629     /* now that we have multi-device support, don't move a stream from
   630         a device that was unplugged to something else, unless we're default. */
   631     if (h->device_name != NULL) {
   632         flags |= PA_STREAM_DONT_MOVE;
   633     }
   634 
   635     if (iscapture) {
   636         rc = PULSEAUDIO_pa_stream_connect_record(h->stream, h->device_name, &paattr, flags);
   637     } else {
   638         rc = PULSEAUDIO_pa_stream_connect_playback(h->stream, h->device_name, &paattr, flags, NULL, NULL);
   639     }
   640 
   641     if (rc < 0) {
   642         return SDL_SetError("Could not connect PulseAudio stream");
   643     }
   644 
   645     do {
   646         if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
   647             return SDL_SetError("pa_mainloop_iterate() failed");
   648         }
   649         state = PULSEAUDIO_pa_stream_get_state(h->stream);
   650         if (!PA_STREAM_IS_GOOD(state)) {
   651             return SDL_SetError("Could not connect PulseAudio stream");
   652         }
   653     } while (state != PA_STREAM_READY);
   654 
   655     /* We're ready to rock and roll. :-) */
   656     return 0;
   657 }
   658 
   659 static pa_mainloop *hotplug_mainloop = NULL;
   660 static pa_context *hotplug_context = NULL;
   661 static SDL_Thread *hotplug_thread = NULL;
   662 
   663 /* device handles are device index + 1, cast to void*, so we never pass a NULL. */
   664 
   665 /* This is called when PulseAudio adds an output ("sink") device. */
   666 static void
   667 SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
   668 {
   669     if (i) {
   670         SDL_AddAudioDevice(SDL_FALSE, i->description, (void *) ((size_t) i->index+1));
   671     }
   672 }
   673 
   674 /* This is called when PulseAudio adds a capture ("source") device. */
   675 static void
   676 SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
   677 {
   678     if (i) {
   679         /* Skip "monitor" sources. These are just output from other sinks. */
   680         if (i->monitor_of_sink == PA_INVALID_INDEX) {
   681             SDL_AddAudioDevice(SDL_TRUE, i->description, (void *) ((size_t) i->index+1));
   682         }
   683     }
   684 }
   685 
   686 /* This is called when PulseAudio has a device connected/removed/changed. */
   687 static void
   688 HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *data)
   689 {
   690     const SDL_bool added = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW);
   691     const SDL_bool removed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE);
   692 
   693     if (added || removed) {  /* we only care about add/remove events. */
   694         const SDL_bool sink = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK);
   695         const SDL_bool source = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
   696 
   697         /* adds need sink details from the PulseAudio server. Another callback... */
   698         if (added && sink) {
   699             PULSEAUDIO_pa_context_get_sink_info_by_index(hotplug_context, idx, SinkInfoCallback, NULL);
   700         } else if (added && source) {
   701             PULSEAUDIO_pa_context_get_source_info_by_index(hotplug_context, idx, SourceInfoCallback, NULL);
   702         } else if (removed && (sink || source)) {
   703             /* removes we can handle just with the device index. */
   704             SDL_RemoveAudioDevice(source != 0, (void *) ((size_t) idx+1));
   705         }
   706     }
   707 }
   708 
   709 /* this runs as a thread while the Pulse target is initialized to catch hotplug events. */
   710 static int SDLCALL
   711 HotplugThread(void *data)
   712 {
   713     pa_operation *o;
   714     SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
   715     PULSEAUDIO_pa_context_set_subscribe_callback(hotplug_context, HotplugCallback, NULL);
   716     o = PULSEAUDIO_pa_context_subscribe(hotplug_context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, NULL, NULL);
   717     PULSEAUDIO_pa_operation_unref(o);  /* don't wait for it, just do our thing. */
   718     PULSEAUDIO_pa_mainloop_run(hotplug_mainloop, NULL);
   719     return 0;
   720 }
   721 
   722 static void
   723 PULSEAUDIO_DetectDevices()
   724 {
   725     WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_sink_info_list(hotplug_context, SinkInfoCallback, NULL));
   726     WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_source_info_list(hotplug_context, SourceInfoCallback, NULL));
   727 
   728     /* ok, we have a sane list, let's set up hotplug notifications now... */
   729     hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, NULL);
   730 }
   731 
   732 static void
   733 PULSEAUDIO_Deinitialize(void)
   734 {
   735     if (hotplug_thread) {
   736         PULSEAUDIO_pa_mainloop_quit(hotplug_mainloop, 0);
   737         SDL_WaitThread(hotplug_thread, NULL);
   738         hotplug_thread = NULL;
   739     }
   740 
   741     DisconnectFromPulseServer(hotplug_mainloop, hotplug_context);
   742     hotplug_mainloop = NULL;
   743     hotplug_context = NULL;
   744 
   745     UnloadPulseAudioLibrary();
   746 }
   747 
   748 static int
   749 PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
   750 {
   751     if (LoadPulseAudioLibrary() < 0) {
   752         return 0;
   753     }
   754 
   755     if (ConnectToPulseServer(&hotplug_mainloop, &hotplug_context) < 0) {
   756         UnloadPulseAudioLibrary();
   757         return 0;
   758     }
   759 
   760     /* Set the function pointers */
   761     impl->DetectDevices = PULSEAUDIO_DetectDevices;
   762     impl->OpenDevice = PULSEAUDIO_OpenDevice;
   763     impl->PlayDevice = PULSEAUDIO_PlayDevice;
   764     impl->WaitDevice = PULSEAUDIO_WaitDevice;
   765     impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
   766     impl->CloseDevice = PULSEAUDIO_CloseDevice;
   767     impl->Deinitialize = PULSEAUDIO_Deinitialize;
   768     impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
   769     impl->FlushCapture = PULSEAUDIO_FlushCapture;
   770 
   771     impl->HasCaptureSupport = SDL_TRUE;
   772 
   773     return 1;   /* this audio target is available. */
   774 }
   775 
   776 AudioBootStrap PULSEAUDIO_bootstrap = {
   777     "pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0
   778 };
   779 
   780 #endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
   781 
   782 /* vi: set ts=4 sw=4 expandtab: */