src/audio/pulseaudio/SDL_pulseaudio.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Sat, 27 May 2017 23:30:07 +0200
changeset 11038 b2883845e32c
parent 10737 3406a0f8b041
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Removed unused errno includes.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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 stream_operation_complete_no_op(pa_stream *s, int success, void *userdata)
   254 {
   255     /* no-op for pa_stream_drain(), etc, to use for callback. */
   256 }
   257 
   258 static void
   259 WaitForPulseOperation(pa_mainloop *mainloop, pa_operation *o)
   260 {
   261     /* This checks for NO errors currently. Either fix that, check results elsewhere, or do things you don't care about. */
   262     if (mainloop && o) {
   263         SDL_bool okay = SDL_TRUE;
   264         while (okay && (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING)) {
   265             okay = (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) >= 0);
   266         }
   267         PULSEAUDIO_pa_operation_unref(o);
   268     }
   269 }
   270 
   271 static void
   272 DisconnectFromPulseServer(pa_mainloop *mainloop, pa_context *context)
   273 {
   274     if (context) {
   275         PULSEAUDIO_pa_context_disconnect(context);
   276         PULSEAUDIO_pa_context_unref(context);
   277     }
   278     if (mainloop != NULL) {
   279         PULSEAUDIO_pa_mainloop_free(mainloop);
   280     }
   281 }
   282 
   283 static int
   284 ConnectToPulseServer_Internal(pa_mainloop **_mainloop, pa_context **_context)
   285 {
   286     pa_mainloop *mainloop = NULL;
   287     pa_context *context = NULL;
   288     pa_mainloop_api *mainloop_api = NULL;
   289     int state = 0;
   290 
   291     *_mainloop = NULL;
   292     *_context = NULL;
   293 
   294     /* Set up a new main loop */
   295     if (!(mainloop = PULSEAUDIO_pa_mainloop_new())) {
   296         return SDL_SetError("pa_mainloop_new() failed");
   297     }
   298 
   299     *_mainloop = mainloop;
   300 
   301     mainloop_api = PULSEAUDIO_pa_mainloop_get_api(mainloop);
   302     SDL_assert(mainloop_api);  /* this never fails, right? */
   303 
   304     context = PULSEAUDIO_pa_context_new(mainloop_api, getAppName());
   305     if (!context) {
   306         return SDL_SetError("pa_context_new() failed");
   307     }
   308     *_context = context;
   309 
   310     /* Connect to the PulseAudio server */
   311     if (PULSEAUDIO_pa_context_connect(context, NULL, 0, NULL) < 0) {
   312         return SDL_SetError("Could not setup connection to PulseAudio");
   313     }
   314 
   315     do {
   316         if (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) < 0) {
   317             return SDL_SetError("pa_mainloop_iterate() failed");
   318         }
   319         state = PULSEAUDIO_pa_context_get_state(context);
   320         if (!PA_CONTEXT_IS_GOOD(state)) {
   321             return SDL_SetError("Could not connect to PulseAudio");
   322         }
   323     } while (state != PA_CONTEXT_READY);
   324 
   325     return 0;  /* connected and ready! */
   326 }
   327 
   328 static int
   329 ConnectToPulseServer(pa_mainloop **_mainloop, pa_context **_context)
   330 {
   331     const int retval = ConnectToPulseServer_Internal(_mainloop, _context);
   332     if (retval < 0) {
   333         DisconnectFromPulseServer(*_mainloop, *_context);
   334     }
   335     return retval;
   336 }
   337 
   338 
   339 /* This function waits until it is possible to write a full sound buffer */
   340 static void
   341 PULSEAUDIO_WaitDevice(_THIS)
   342 {
   343     struct SDL_PrivateAudioData *h = this->hidden;
   344 
   345     while (SDL_AtomicGet(&this->enabled)) {
   346         if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
   347             PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
   348             PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
   349             SDL_OpenedAudioDeviceDisconnected(this);
   350             return;
   351         }
   352         if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) {
   353             return;
   354         }
   355     }
   356 }
   357 
   358 static void
   359 PULSEAUDIO_PlayDevice(_THIS)
   360 {
   361     /* Write the audio data */
   362     struct SDL_PrivateAudioData *h = this->hidden;
   363     if (SDL_AtomicGet(&this->enabled)) {
   364         if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
   365             SDL_OpenedAudioDeviceDisconnected(this);
   366         }
   367     }
   368 }
   369 
   370 static Uint8 *
   371 PULSEAUDIO_GetDeviceBuf(_THIS)
   372 {
   373     return (this->hidden->mixbuf);
   374 }
   375 
   376 
   377 static int
   378 PULSEAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
   379 {
   380     struct SDL_PrivateAudioData *h = this->hidden;
   381     const void *data = NULL;
   382     size_t nbytes = 0;
   383 
   384     while (SDL_AtomicGet(&this->enabled)) {
   385         if (h->capturebuf != NULL) {
   386             const int cpy = SDL_min(buflen, h->capturelen);
   387             SDL_memcpy(buffer, h->capturebuf, cpy);
   388             /*printf("PULSEAUDIO: fed %d captured bytes\n", cpy);*/
   389             h->capturebuf += cpy;
   390             h->capturelen -= cpy;
   391             if (h->capturelen == 0) {
   392                 h->capturebuf = NULL;
   393                 PULSEAUDIO_pa_stream_drop(h->stream);  /* done with this fragment. */
   394             }
   395             return cpy;  /* new data, return it. */
   396         }
   397 
   398         if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
   399             PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
   400             PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
   401             SDL_OpenedAudioDeviceDisconnected(this);
   402             return -1;  /* uhoh, pulse failed! */
   403         }
   404 
   405         if (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0) {
   406             continue;  /* no data available yet. */
   407         }
   408 
   409         /* a new fragment is available! */
   410         PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
   411         SDL_assert(nbytes > 0);
   412         if (data == NULL) {  /* NULL==buffer had a hole. Ignore that. */
   413             PULSEAUDIO_pa_stream_drop(h->stream);  /* drop this fragment. */
   414         } else {
   415             /* store this fragment's data, start feeding it to SDL. */
   416             /*printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);*/
   417             h->capturebuf = (const Uint8 *) data;
   418             h->capturelen = nbytes;
   419         }
   420     }
   421 
   422     return -1;  /* not enabled? */
   423 }
   424 
   425 static void
   426 PULSEAUDIO_FlushCapture(_THIS)
   427 {
   428     struct SDL_PrivateAudioData *h = this->hidden;
   429 
   430     if (h->capturebuf != NULL) {
   431         PULSEAUDIO_pa_stream_drop(h->stream);
   432         h->capturebuf = NULL;
   433         h->capturelen = 0;
   434     }
   435 
   436     WaitForPulseOperation(h->mainloop, PULSEAUDIO_pa_stream_flush(h->stream, stream_operation_complete_no_op, NULL));
   437 }
   438 
   439 static void
   440 PULSEAUDIO_CloseDevice(_THIS)
   441 {
   442     if (this->hidden->stream) {
   443         if (this->hidden->capturebuf != NULL) {
   444             PULSEAUDIO_pa_stream_drop(this->hidden->stream);
   445         }
   446         PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
   447         PULSEAUDIO_pa_stream_unref(this->hidden->stream);
   448     }
   449 
   450     DisconnectFromPulseServer(this->hidden->mainloop, this->hidden->context);
   451     SDL_free(this->hidden->mixbuf);
   452     SDL_free(this->hidden->device_name);
   453     SDL_free(this->hidden);
   454 }
   455 
   456 static void
   457 SinkDeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
   458 {
   459     if (i) {
   460         char **devname = (char **) data;
   461         *devname = SDL_strdup(i->name);
   462     }
   463 }
   464 
   465 static void
   466 SourceDeviceNameCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
   467 {
   468     if (i) {
   469         char **devname = (char **) data;
   470         *devname = SDL_strdup(i->name);
   471     }
   472 }
   473 
   474 static SDL_bool
   475 FindDeviceName(struct SDL_PrivateAudioData *h, const int iscapture, void *handle)
   476 {
   477     const uint32_t idx = ((uint32_t) ((size_t) handle)) - 1;
   478 
   479     if (handle == NULL) {  /* NULL == default device. */
   480         return SDL_TRUE;
   481     }
   482 
   483     if (iscapture) {
   484         WaitForPulseOperation(h->mainloop,
   485             PULSEAUDIO_pa_context_get_source_info_by_index(h->context, idx,
   486                 SourceDeviceNameCallback, &h->device_name));
   487     } else {
   488         WaitForPulseOperation(h->mainloop,
   489             PULSEAUDIO_pa_context_get_sink_info_by_index(h->context, idx,
   490                 SinkDeviceNameCallback, &h->device_name));
   491     }
   492 
   493     return (h->device_name != NULL);
   494 }
   495 
   496 static int
   497 PULSEAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   498 {
   499     struct SDL_PrivateAudioData *h = NULL;
   500     Uint16 test_format = 0;
   501     pa_sample_spec paspec;
   502     pa_buffer_attr paattr;
   503     pa_channel_map pacmap;
   504     pa_stream_flags_t flags = 0;
   505     int state = 0;
   506     int rc = 0;
   507 
   508     /* Initialize all variables that we clean on shutdown */
   509     h = this->hidden = (struct SDL_PrivateAudioData *)
   510         SDL_malloc((sizeof *this->hidden));
   511     if (this->hidden == NULL) {
   512         return SDL_OutOfMemory();
   513     }
   514     SDL_zerop(this->hidden);
   515 
   516     paspec.format = PA_SAMPLE_INVALID;
   517 
   518     /* Try for a closest match on audio format */
   519     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   520          (paspec.format == PA_SAMPLE_INVALID) && test_format;) {
   521 #ifdef DEBUG_AUDIO
   522         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   523 #endif
   524         switch (test_format) {
   525         case AUDIO_U8:
   526             paspec.format = PA_SAMPLE_U8;
   527             break;
   528         case AUDIO_S16LSB:
   529             paspec.format = PA_SAMPLE_S16LE;
   530             break;
   531         case AUDIO_S16MSB:
   532             paspec.format = PA_SAMPLE_S16BE;
   533             break;
   534         case AUDIO_S32LSB:
   535             paspec.format = PA_SAMPLE_S32LE;
   536             break;
   537         case AUDIO_S32MSB:
   538             paspec.format = PA_SAMPLE_S32BE;
   539             break;
   540         case AUDIO_F32LSB:
   541             paspec.format = PA_SAMPLE_FLOAT32LE;
   542             break;
   543         case AUDIO_F32MSB:
   544             paspec.format = PA_SAMPLE_FLOAT32BE;
   545             break;
   546         default:
   547             paspec.format = PA_SAMPLE_INVALID;
   548             break;
   549         }
   550         if (paspec.format == PA_SAMPLE_INVALID) {
   551             test_format = SDL_NextAudioFormat();
   552         }
   553     }
   554     if (paspec.format == PA_SAMPLE_INVALID) {
   555         return SDL_SetError("Couldn't find any hardware audio formats");
   556     }
   557     this->spec.format = test_format;
   558 
   559     /* Calculate the final parameters for this audio specification */
   560 #ifdef PA_STREAM_ADJUST_LATENCY
   561     this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
   562 #endif
   563     SDL_CalculateAudioSpec(&this->spec);
   564 
   565     /* Allocate mixing buffer */
   566     if (!iscapture) {
   567         h->mixlen = this->spec.size;
   568         h->mixbuf = (Uint8 *) SDL_malloc(h->mixlen);
   569         if (h->mixbuf == NULL) {
   570             return SDL_OutOfMemory();
   571         }
   572         SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
   573     }
   574 
   575     paspec.channels = this->spec.channels;
   576     paspec.rate = this->spec.freq;
   577 
   578     /* Reduced prebuffering compared to the defaults. */
   579 #ifdef PA_STREAM_ADJUST_LATENCY
   580     /* 2x original requested bufsize */
   581     paattr.tlength = h->mixlen * 4;
   582     paattr.prebuf = -1;
   583     paattr.maxlength = -1;
   584     /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
   585     paattr.minreq = h->mixlen;
   586     flags = PA_STREAM_ADJUST_LATENCY;
   587 #else
   588     paattr.tlength = h->mixlen*2;
   589     paattr.prebuf = h->mixlen*2;
   590     paattr.maxlength = h->mixlen*2;
   591     paattr.minreq = h->mixlen;
   592 #endif
   593 
   594     if (ConnectToPulseServer(&h->mainloop, &h->context) < 0) {
   595         return SDL_SetError("Could not connect to PulseAudio server");
   596     }
   597 
   598     if (!FindDeviceName(h, iscapture, handle)) {
   599         return SDL_SetError("Requested PulseAudio sink/source missing?");
   600     }
   601 
   602     /* The SDL ALSA output hints us that we use Windows' channel mapping */
   603     /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
   604     PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels,
   605                                         PA_CHANNEL_MAP_WAVEEX);
   606 
   607     h->stream = PULSEAUDIO_pa_stream_new(
   608         h->context,
   609         "Simple DirectMedia Layer", /* stream description */
   610         &paspec,    /* sample format spec */
   611         &pacmap     /* channel map */
   612         );
   613 
   614     if (h->stream == NULL) {
   615         return SDL_SetError("Could not set up PulseAudio stream");
   616     }
   617 
   618     /* now that we have multi-device support, don't move a stream from
   619         a device that was unplugged to something else, unless we're default. */
   620     if (h->device_name != NULL) {
   621         flags |= PA_STREAM_DONT_MOVE;
   622     }
   623 
   624     if (iscapture) {
   625         rc = PULSEAUDIO_pa_stream_connect_record(h->stream, h->device_name, &paattr, flags);
   626     } else {
   627         rc = PULSEAUDIO_pa_stream_connect_playback(h->stream, h->device_name, &paattr, flags, NULL, NULL);
   628     }
   629 
   630     if (rc < 0) {
   631         return SDL_SetError("Could not connect PulseAudio stream");
   632     }
   633 
   634     do {
   635         if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
   636             return SDL_SetError("pa_mainloop_iterate() failed");
   637         }
   638         state = PULSEAUDIO_pa_stream_get_state(h->stream);
   639         if (!PA_STREAM_IS_GOOD(state)) {
   640             return SDL_SetError("Could not connect PulseAudio stream");
   641         }
   642     } while (state != PA_STREAM_READY);
   643 
   644     /* We're ready to rock and roll. :-) */
   645     return 0;
   646 }
   647 
   648 static pa_mainloop *hotplug_mainloop = NULL;
   649 static pa_context *hotplug_context = NULL;
   650 static SDL_Thread *hotplug_thread = NULL;
   651 
   652 /* device handles are device index + 1, cast to void*, so we never pass a NULL. */
   653 
   654 /* This is called when PulseAudio adds an output ("sink") device. */
   655 static void
   656 SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
   657 {
   658     if (i) {
   659         SDL_AddAudioDevice(SDL_FALSE, i->description, (void *) ((size_t) i->index+1));
   660     }
   661 }
   662 
   663 /* This is called when PulseAudio adds a capture ("source") device. */
   664 static void
   665 SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
   666 {
   667     if (i) {
   668         /* Skip "monitor" sources. These are just output from other sinks. */
   669         if (i->monitor_of_sink == PA_INVALID_INDEX) {
   670             SDL_AddAudioDevice(SDL_TRUE, i->description, (void *) ((size_t) i->index+1));
   671         }
   672     }
   673 }
   674 
   675 /* This is called when PulseAudio has a device connected/removed/changed. */
   676 static void
   677 HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *data)
   678 {
   679     const SDL_bool added = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW);
   680     const SDL_bool removed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE);
   681 
   682     if (added || removed) {  /* we only care about add/remove events. */
   683         const SDL_bool sink = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK);
   684         const SDL_bool source = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
   685 
   686         /* adds need sink details from the PulseAudio server. Another callback... */
   687         if (added && sink) {
   688             PULSEAUDIO_pa_context_get_sink_info_by_index(hotplug_context, idx, SinkInfoCallback, NULL);
   689         } else if (added && source) {
   690             PULSEAUDIO_pa_context_get_source_info_by_index(hotplug_context, idx, SourceInfoCallback, NULL);
   691         } else if (removed && (sink || source)) {
   692             /* removes we can handle just with the device index. */
   693             SDL_RemoveAudioDevice(source != 0, (void *) ((size_t) idx+1));
   694         }
   695     }
   696 }
   697 
   698 /* this runs as a thread while the Pulse target is initialized to catch hotplug events. */
   699 static int SDLCALL
   700 HotplugThread(void *data)
   701 {
   702     pa_operation *o;
   703     SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
   704     PULSEAUDIO_pa_context_set_subscribe_callback(hotplug_context, HotplugCallback, NULL);
   705     o = PULSEAUDIO_pa_context_subscribe(hotplug_context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, NULL, NULL);
   706     PULSEAUDIO_pa_operation_unref(o);  /* don't wait for it, just do our thing. */
   707     PULSEAUDIO_pa_mainloop_run(hotplug_mainloop, NULL);
   708     return 0;
   709 }
   710 
   711 static void
   712 PULSEAUDIO_DetectDevices()
   713 {
   714     WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_sink_info_list(hotplug_context, SinkInfoCallback, NULL));
   715     WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_source_info_list(hotplug_context, SourceInfoCallback, NULL));
   716 
   717     /* ok, we have a sane list, let's set up hotplug notifications now... */
   718     hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, NULL);
   719 }
   720 
   721 static void
   722 PULSEAUDIO_Deinitialize(void)
   723 {
   724     if (hotplug_thread) {
   725         PULSEAUDIO_pa_mainloop_quit(hotplug_mainloop, 0);
   726         SDL_WaitThread(hotplug_thread, NULL);
   727         hotplug_thread = NULL;
   728     }
   729 
   730     DisconnectFromPulseServer(hotplug_mainloop, hotplug_context);
   731     hotplug_mainloop = NULL;
   732     hotplug_context = NULL;
   733 
   734     UnloadPulseAudioLibrary();
   735 }
   736 
   737 static int
   738 PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
   739 {
   740     if (LoadPulseAudioLibrary() < 0) {
   741         return 0;
   742     }
   743 
   744     if (ConnectToPulseServer(&hotplug_mainloop, &hotplug_context) < 0) {
   745         UnloadPulseAudioLibrary();
   746         return 0;
   747     }
   748 
   749     /* Set the function pointers */
   750     impl->DetectDevices = PULSEAUDIO_DetectDevices;
   751     impl->OpenDevice = PULSEAUDIO_OpenDevice;
   752     impl->PlayDevice = PULSEAUDIO_PlayDevice;
   753     impl->WaitDevice = PULSEAUDIO_WaitDevice;
   754     impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
   755     impl->CloseDevice = PULSEAUDIO_CloseDevice;
   756     impl->Deinitialize = PULSEAUDIO_Deinitialize;
   757     impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
   758     impl->FlushCapture = PULSEAUDIO_FlushCapture;
   759 
   760     impl->HasCaptureSupport = SDL_TRUE;
   761 
   762     return 1;   /* this audio target is available. */
   763 }
   764 
   765 AudioBootStrap PULSEAUDIO_bootstrap = {
   766     "pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0
   767 };
   768 
   769 #endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
   770 
   771 /* vi: set ts=4 sw=4 expandtab: */