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