duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

cubeb_pulse.c (52402B)


      1 /*
      2  * Copyright © 2011 Mozilla Foundation
      3  *
      4  * This program is made available under an ISC-style license.  See the
      5  * accompanying file LICENSE for details.
      6  */
      7 #undef NDEBUG
      8 #include "cubeb-internal.h"
      9 #include "cubeb/cubeb.h"
     10 #include "cubeb_mixer.h"
     11 #include "cubeb_strings.h"
     12 #include <assert.h>
     13 #include <dlfcn.h>
     14 #include <pulse/pulseaudio.h>
     15 #include <stdio.h>
     16 #include <stdlib.h>
     17 #include <string.h>
     18 
     19 #ifdef DISABLE_LIBPULSE_DLOPEN
     20 #define WRAP(x) x
     21 #else
     22 #define WRAP(x) (*cubeb_##x)
     23 #define LIBPULSE_API_VISIT(X)                                                  \
     24   X(pa_channel_map_can_balance)                                                \
     25   X(pa_channel_map_init)                                                       \
     26   X(pa_context_connect)                                                        \
     27   X(pa_context_disconnect)                                                     \
     28   X(pa_context_drain)                                                          \
     29   X(pa_context_get_server_info)                                                \
     30   X(pa_context_get_sink_info_by_name)                                          \
     31   X(pa_context_get_sink_info_list)                                             \
     32   X(pa_context_get_sink_input_info)                                            \
     33   X(pa_context_get_source_info_list)                                           \
     34   X(pa_context_get_state)                                                      \
     35   X(pa_context_new)                                                            \
     36   X(pa_context_rttime_new)                                                     \
     37   X(pa_context_set_sink_input_volume)                                          \
     38   X(pa_context_set_state_callback)                                             \
     39   X(pa_context_unref)                                                          \
     40   X(pa_cvolume_set)                                                            \
     41   X(pa_cvolume_set_balance)                                                    \
     42   X(pa_frame_size)                                                             \
     43   X(pa_operation_get_state)                                                    \
     44   X(pa_operation_unref)                                                        \
     45   X(pa_proplist_gets)                                                          \
     46   X(pa_rtclock_now)                                                            \
     47   X(pa_stream_begin_write)                                                     \
     48   X(pa_stream_cancel_write)                                                    \
     49   X(pa_stream_connect_playback)                                                \
     50   X(pa_stream_cork)                                                            \
     51   X(pa_stream_disconnect)                                                      \
     52   X(pa_stream_get_channel_map)                                                 \
     53   X(pa_stream_get_index)                                                       \
     54   X(pa_stream_get_latency)                                                     \
     55   X(pa_stream_get_sample_spec)                                                 \
     56   X(pa_stream_get_state)                                                       \
     57   X(pa_stream_get_time)                                                        \
     58   X(pa_stream_new)                                                             \
     59   X(pa_stream_set_state_callback)                                              \
     60   X(pa_stream_set_write_callback)                                              \
     61   X(pa_stream_unref)                                                           \
     62   X(pa_stream_update_timing_info)                                              \
     63   X(pa_stream_write)                                                           \
     64   X(pa_sw_volume_from_linear)                                                  \
     65   X(pa_threaded_mainloop_free)                                                 \
     66   X(pa_threaded_mainloop_get_api)                                              \
     67   X(pa_threaded_mainloop_in_thread)                                            \
     68   X(pa_threaded_mainloop_lock)                                                 \
     69   X(pa_threaded_mainloop_new)                                                  \
     70   X(pa_threaded_mainloop_signal)                                               \
     71   X(pa_threaded_mainloop_start)                                                \
     72   X(pa_threaded_mainloop_stop)                                                 \
     73   X(pa_threaded_mainloop_unlock)                                               \
     74   X(pa_threaded_mainloop_wait)                                                 \
     75   X(pa_usec_to_bytes)                                                          \
     76   X(pa_stream_set_read_callback)                                               \
     77   X(pa_stream_connect_record)                                                  \
     78   X(pa_stream_readable_size)                                                   \
     79   X(pa_stream_writable_size)                                                   \
     80   X(pa_stream_peek)                                                            \
     81   X(pa_stream_drop)                                                            \
     82   X(pa_stream_get_buffer_attr)                                                 \
     83   X(pa_stream_get_device_name)                                                 \
     84   X(pa_context_set_subscribe_callback)                                         \
     85   X(pa_context_subscribe)                                                      \
     86   X(pa_mainloop_api_once)                                                      \
     87   X(pa_get_library_version)                                                    \
     88   X(pa_channel_map_init_auto)                                                  \
     89   X(pa_stream_set_name)
     90 
     91 #define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
     92 LIBPULSE_API_VISIT(MAKE_TYPEDEF);
     93 #undef MAKE_TYPEDEF
     94 #endif
     95 
     96 #if PA_CHECK_VERSION(2, 0, 0)
     97 static int has_pulse_v2 = 0;
     98 #endif
     99 
    100 static struct cubeb_ops const pulse_ops;
    101 
    102 struct cubeb_default_sink_info {
    103   pa_channel_map channel_map;
    104   uint32_t sample_spec_rate;
    105   pa_sink_flags_t flags;
    106 };
    107 
    108 struct cubeb {
    109   struct cubeb_ops const * ops;
    110   void * libpulse;
    111   pa_threaded_mainloop * mainloop;
    112   pa_context * context;
    113   struct cubeb_default_sink_info * default_sink_info;
    114   char * context_name;
    115   int error;
    116   cubeb_device_collection_changed_callback output_collection_changed_callback;
    117   void * output_collection_changed_user_ptr;
    118   cubeb_device_collection_changed_callback input_collection_changed_callback;
    119   void * input_collection_changed_user_ptr;
    120   cubeb_strings * device_ids;
    121 };
    122 
    123 struct cubeb_stream {
    124   /* Note: Must match cubeb_stream layout in cubeb.c. */
    125   cubeb * context;
    126   void * user_ptr;
    127   /**/
    128   pa_stream * output_stream;
    129   pa_stream * input_stream;
    130   cubeb_data_callback data_callback;
    131   cubeb_state_callback state_callback;
    132   pa_time_event * drain_timer;
    133   pa_sample_spec output_sample_spec;
    134   pa_sample_spec input_sample_spec;
    135   int shutdown;
    136   float volume;
    137   cubeb_state state;
    138 };
    139 
    140 static const float PULSE_NO_GAIN = -1.0;
    141 
    142 enum cork_state { UNCORK = 0, CORK = 1 << 0, NOTIFY = 1 << 1 };
    143 
    144 static int
    145 intern_device_id(cubeb * ctx, char const ** id)
    146 {
    147   char const * interned;
    148 
    149   assert(ctx);
    150   assert(id);
    151 
    152   interned = cubeb_strings_intern(ctx->device_ids, *id);
    153   if (!interned) {
    154     return CUBEB_ERROR;
    155   }
    156 
    157   *id = interned;
    158 
    159   return CUBEB_OK;
    160 }
    161 
    162 static void
    163 sink_info_callback(pa_context * context, const pa_sink_info * info, int eol,
    164                    void * u)
    165 {
    166   (void)context;
    167   cubeb * ctx = u;
    168   if (!eol) {
    169     free(ctx->default_sink_info);
    170     ctx->default_sink_info = malloc(sizeof(struct cubeb_default_sink_info));
    171     memcpy(&ctx->default_sink_info->channel_map, &info->channel_map,
    172            sizeof(pa_channel_map));
    173     ctx->default_sink_info->sample_spec_rate = info->sample_spec.rate;
    174     ctx->default_sink_info->flags = info->flags;
    175   }
    176   WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
    177 }
    178 
    179 static void
    180 server_info_callback(pa_context * context, const pa_server_info * info,
    181                      void * u)
    182 {
    183   pa_operation * o;
    184   o = WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name,
    185                                              sink_info_callback, u);
    186   if (o) {
    187     WRAP(pa_operation_unref)(o);
    188   }
    189 }
    190 
    191 static void
    192 context_state_callback(pa_context * c, void * u)
    193 {
    194   cubeb * ctx = u;
    195   if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(c))) {
    196     ctx->error = 1;
    197   }
    198   WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
    199 }
    200 
    201 static void
    202 context_notify_callback(pa_context * c, void * u)
    203 {
    204   (void)c;
    205   cubeb * ctx = u;
    206   WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
    207 }
    208 
    209 static void
    210 stream_success_callback(pa_stream * s, int success, void * u)
    211 {
    212   (void)s;
    213   (void)success;
    214   cubeb_stream * stm = u;
    215   WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0);
    216 }
    217 
    218 static void
    219 stream_state_change_callback(cubeb_stream * stm, cubeb_state s)
    220 {
    221   stm->state = s;
    222   stm->state_callback(stm, stm->user_ptr, s);
    223 }
    224 
    225 static void
    226 stream_drain_callback(pa_mainloop_api * a, pa_time_event * e,
    227                       struct timeval const * tv, void * u)
    228 {
    229   (void)a;
    230   (void)tv;
    231   cubeb_stream * stm = u;
    232   assert(stm->drain_timer == e);
    233   stream_state_change_callback(stm, CUBEB_STATE_DRAINED);
    234   /* there's no pa_rttime_free, so use this instead. */
    235   a->time_free(stm->drain_timer);
    236   stm->drain_timer = NULL;
    237   WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0);
    238 }
    239 
    240 static void
    241 stream_state_callback(pa_stream * s, void * u)
    242 {
    243   cubeb_stream * stm = u;
    244   if (!PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(s))) {
    245     stream_state_change_callback(stm, CUBEB_STATE_ERROR);
    246   }
    247   WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0);
    248 }
    249 
    250 static void
    251 trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes,
    252                       cubeb_stream * stm)
    253 {
    254   void * buffer;
    255   size_t size;
    256   int r;
    257   long got;
    258   size_t towrite, read_offset;
    259   size_t frame_size;
    260 
    261   frame_size = WRAP(pa_frame_size)(&stm->output_sample_spec);
    262   assert(nbytes % frame_size == 0);
    263 
    264   towrite = nbytes;
    265   read_offset = 0;
    266   while (towrite) {
    267     size = towrite;
    268     r = WRAP(pa_stream_begin_write)(s, &buffer, &size);
    269     // Note: this has failed running under rr on occassion - needs
    270     // investigation.
    271     assert(r == 0);
    272     assert(size > 0);
    273     assert(size % frame_size == 0);
    274 
    275     LOGV("Trigger user callback with output buffer size=%zd, read_offset=%zd",
    276          size, read_offset);
    277     got = stm->data_callback(stm, stm->user_ptr,
    278                              (uint8_t const *)input_data + read_offset, buffer,
    279                              size / frame_size);
    280     if (got < 0) {
    281       WRAP(pa_stream_cancel_write)(s);
    282       stm->shutdown = 1;
    283       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
    284       return;
    285     }
    286     // If more iterations move offset of read buffer
    287     if (input_data) {
    288       size_t in_frame_size = WRAP(pa_frame_size)(&stm->input_sample_spec);
    289       read_offset += (size / frame_size) * in_frame_size;
    290     }
    291 
    292     if (stm->volume != PULSE_NO_GAIN) {
    293       uint32_t samples = size * stm->output_sample_spec.channels / frame_size;
    294 
    295       if (stm->output_sample_spec.format == PA_SAMPLE_S16BE ||
    296           stm->output_sample_spec.format == PA_SAMPLE_S16LE) {
    297         short * b = buffer;
    298         for (uint32_t i = 0; i < samples; i++) {
    299           b[i] *= stm->volume;
    300         }
    301       } else {
    302         float * b = buffer;
    303         for (uint32_t i = 0; i < samples; i++) {
    304           b[i] *= stm->volume;
    305         }
    306       }
    307     }
    308 
    309     r = WRAP(pa_stream_write)(s, buffer, got * frame_size, NULL, 0,
    310                               PA_SEEK_RELATIVE);
    311     assert(r == 0);
    312 
    313     if ((size_t)got < size / frame_size) {
    314       pa_usec_t latency = 0;
    315       r = WRAP(pa_stream_get_latency)(s, &latency, NULL);
    316       if (r == -PA_ERR_NODATA) {
    317         /* this needs a better guess. */
    318         latency = 100 * PA_USEC_PER_MSEC;
    319       }
    320       assert(r == 0 || r == -PA_ERR_NODATA);
    321       /* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */
    322       /* arbitrary safety margin: double the current latency. */
    323       assert(!stm->drain_timer);
    324       stm->drain_timer = WRAP(pa_context_rttime_new)(
    325           stm->context->context, WRAP(pa_rtclock_now)() + 2 * latency,
    326           stream_drain_callback, stm);
    327       stm->shutdown = 1;
    328       return;
    329     }
    330 
    331     towrite -= size;
    332   }
    333 
    334   assert(towrite == 0);
    335 }
    336 
    337 static int
    338 read_from_input(pa_stream * s, void const ** buffer, size_t * size)
    339 {
    340   size_t readable_size = WRAP(pa_stream_readable_size)(s);
    341   if (readable_size > 0) {
    342     if (WRAP(pa_stream_peek)(s, buffer, size) < 0) {
    343       return -1;
    344     }
    345   }
    346   return readable_size;
    347 }
    348 
    349 static void
    350 stream_write_callback(pa_stream * s, size_t nbytes, void * u)
    351 {
    352   LOGV("Output callback to be written buffer size %zd", nbytes);
    353   cubeb_stream * stm = u;
    354   if (stm->shutdown || stm->state != CUBEB_STATE_STARTED) {
    355     return;
    356   }
    357 
    358   if (!stm->input_stream) {
    359     // Output/playback only operation.
    360     // Write directly to output
    361     assert(!stm->input_stream && stm->output_stream);
    362     trigger_user_callback(s, NULL, nbytes, stm);
    363   }
    364 }
    365 
    366 static void
    367 stream_read_callback(pa_stream * s, size_t nbytes, void * u)
    368 {
    369   LOGV("Input callback buffer size %zd", nbytes);
    370   cubeb_stream * stm = u;
    371   if (stm->shutdown) {
    372     return;
    373   }
    374 
    375   void const * read_data = NULL;
    376   size_t read_size;
    377   while (read_from_input(s, &read_data, &read_size) > 0) {
    378     /* read_data can be NULL in case of a hole. */
    379     if (read_data) {
    380       size_t in_frame_size = WRAP(pa_frame_size)(&stm->input_sample_spec);
    381       size_t read_frames = read_size / in_frame_size;
    382 
    383       if (stm->output_stream) {
    384         // input/capture + output/playback operation
    385         size_t out_frame_size = WRAP(pa_frame_size)(&stm->output_sample_spec);
    386         size_t write_size = read_frames * out_frame_size;
    387         // Offer full duplex data for writing
    388         trigger_user_callback(stm->output_stream, read_data, write_size, stm);
    389       } else {
    390         // input/capture only operation. Call callback directly
    391         long got = stm->data_callback(stm, stm->user_ptr, read_data, NULL,
    392                                       read_frames);
    393         if (got < 0 || (size_t)got != read_frames) {
    394           WRAP(pa_stream_cancel_write)(s);
    395           stm->shutdown = 1;
    396           if (got < 0) {
    397             stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
    398           }
    399           break;
    400         }
    401       }
    402     }
    403     if (read_size > 0) {
    404       WRAP(pa_stream_drop)(s);
    405     }
    406 
    407     if (stm->shutdown) {
    408       return;
    409     }
    410   }
    411 }
    412 
    413 static int
    414 wait_until_context_ready(cubeb * ctx)
    415 {
    416   for (;;) {
    417     pa_context_state_t state = WRAP(pa_context_get_state)(ctx->context);
    418     if (!PA_CONTEXT_IS_GOOD(state))
    419       return -1;
    420     if (state == PA_CONTEXT_READY)
    421       break;
    422     WRAP(pa_threaded_mainloop_wait)(ctx->mainloop);
    423   }
    424   return 0;
    425 }
    426 
    427 static int
    428 wait_until_io_stream_ready(pa_stream * stream, pa_threaded_mainloop * mainloop)
    429 {
    430   if (!stream || !mainloop) {
    431     return -1;
    432   }
    433   for (;;) {
    434     pa_stream_state_t state = WRAP(pa_stream_get_state)(stream);
    435     if (!PA_STREAM_IS_GOOD(state))
    436       return -1;
    437     if (state == PA_STREAM_READY)
    438       break;
    439     WRAP(pa_threaded_mainloop_wait)(mainloop);
    440   }
    441   return 0;
    442 }
    443 
    444 static int
    445 wait_until_stream_ready(cubeb_stream * stm)
    446 {
    447   if (stm->output_stream &&
    448       wait_until_io_stream_ready(stm->output_stream, stm->context->mainloop) ==
    449           -1) {
    450     return -1;
    451   }
    452   if (stm->input_stream &&
    453       wait_until_io_stream_ready(stm->input_stream, stm->context->mainloop) ==
    454           -1) {
    455     return -1;
    456   }
    457   return 0;
    458 }
    459 
    460 static int
    461 operation_wait(cubeb * ctx, pa_stream * stream, pa_operation * o)
    462 {
    463   while (WRAP(pa_operation_get_state)(o) == PA_OPERATION_RUNNING) {
    464     WRAP(pa_threaded_mainloop_wait)(ctx->mainloop);
    465     if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(ctx->context))) {
    466       return -1;
    467     }
    468     if (stream && !PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(stream))) {
    469       return -1;
    470     }
    471   }
    472   return 0;
    473 }
    474 
    475 static void
    476 cork_io_stream(cubeb_stream * stm, pa_stream * io_stream, enum cork_state state)
    477 {
    478   pa_operation * o;
    479   if (!io_stream) {
    480     return;
    481   }
    482   o = WRAP(pa_stream_cork)(io_stream, state & CORK, stream_success_callback,
    483                            stm);
    484   if (o) {
    485     operation_wait(stm->context, io_stream, o);
    486     WRAP(pa_operation_unref)(o);
    487   }
    488 }
    489 
    490 static void
    491 stream_cork(cubeb_stream * stm, enum cork_state state)
    492 {
    493   WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
    494   cork_io_stream(stm, stm->output_stream, state);
    495   cork_io_stream(stm, stm->input_stream, state);
    496   WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
    497 
    498   if (state & NOTIFY) {
    499     stream_state_change_callback(stm, state & CORK ? CUBEB_STATE_STOPPED
    500                                                    : CUBEB_STATE_STARTED);
    501   }
    502 }
    503 
    504 static int
    505 stream_update_timing_info(cubeb_stream * stm)
    506 {
    507   int r = -1;
    508   pa_operation * o = NULL;
    509   if (stm->output_stream) {
    510     o = WRAP(pa_stream_update_timing_info)(stm->output_stream,
    511                                            stream_success_callback, stm);
    512     if (o) {
    513       r = operation_wait(stm->context, stm->output_stream, o);
    514       WRAP(pa_operation_unref)(o);
    515     }
    516     if (r != 0) {
    517       return r;
    518     }
    519   }
    520 
    521   if (stm->input_stream) {
    522     o = WRAP(pa_stream_update_timing_info)(stm->input_stream,
    523                                            stream_success_callback, stm);
    524     if (o) {
    525       r = operation_wait(stm->context, stm->input_stream, o);
    526       WRAP(pa_operation_unref)(o);
    527     }
    528   }
    529 
    530   return r;
    531 }
    532 
    533 static pa_channel_position_t
    534 cubeb_channel_to_pa_channel(cubeb_channel channel)
    535 {
    536   switch (channel) {
    537   case CHANNEL_FRONT_LEFT:
    538     return PA_CHANNEL_POSITION_FRONT_LEFT;
    539   case CHANNEL_FRONT_RIGHT:
    540     return PA_CHANNEL_POSITION_FRONT_RIGHT;
    541   case CHANNEL_FRONT_CENTER:
    542     return PA_CHANNEL_POSITION_FRONT_CENTER;
    543   case CHANNEL_LOW_FREQUENCY:
    544     return PA_CHANNEL_POSITION_LFE;
    545   case CHANNEL_BACK_LEFT:
    546     return PA_CHANNEL_POSITION_REAR_LEFT;
    547   case CHANNEL_BACK_RIGHT:
    548     return PA_CHANNEL_POSITION_REAR_RIGHT;
    549   case CHANNEL_FRONT_LEFT_OF_CENTER:
    550     return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
    551   case CHANNEL_FRONT_RIGHT_OF_CENTER:
    552     return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
    553   case CHANNEL_BACK_CENTER:
    554     return PA_CHANNEL_POSITION_REAR_CENTER;
    555   case CHANNEL_SIDE_LEFT:
    556     return PA_CHANNEL_POSITION_SIDE_LEFT;
    557   case CHANNEL_SIDE_RIGHT:
    558     return PA_CHANNEL_POSITION_SIDE_RIGHT;
    559   case CHANNEL_TOP_CENTER:
    560     return PA_CHANNEL_POSITION_TOP_CENTER;
    561   case CHANNEL_TOP_FRONT_LEFT:
    562     return PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
    563   case CHANNEL_TOP_FRONT_CENTER:
    564     return PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
    565   case CHANNEL_TOP_FRONT_RIGHT:
    566     return PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
    567   case CHANNEL_TOP_BACK_LEFT:
    568     return PA_CHANNEL_POSITION_TOP_REAR_LEFT;
    569   case CHANNEL_TOP_BACK_CENTER:
    570     return PA_CHANNEL_POSITION_TOP_REAR_CENTER;
    571   case CHANNEL_TOP_BACK_RIGHT:
    572     return PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
    573   default:
    574     return PA_CHANNEL_POSITION_INVALID;
    575   }
    576 }
    577 
    578 static void
    579 layout_to_channel_map(cubeb_channel_layout layout, pa_channel_map * cm)
    580 {
    581   assert(cm && layout != CUBEB_LAYOUT_UNDEFINED);
    582 
    583   WRAP(pa_channel_map_init)(cm);
    584 
    585   uint32_t channels = 0;
    586   cubeb_channel_layout channelMap = layout;
    587   for (uint32_t i = 0; channelMap != 0; ++i) {
    588     uint32_t channel = (channelMap & 1) << i;
    589     if (channel != 0) {
    590       cm->map[channels] = cubeb_channel_to_pa_channel(channel);
    591       channels++;
    592     }
    593     channelMap = channelMap >> 1;
    594   }
    595   unsigned int channels_from_layout = cubeb_channel_layout_nb_channels(layout);
    596   assert(channels_from_layout <= UINT8_MAX);
    597   cm->channels = (uint8_t)channels_from_layout;
    598 
    599   // Special case single channel center mapping as mono.
    600   if (cm->channels == 1 && cm->map[0] == PA_CHANNEL_POSITION_FRONT_CENTER) {
    601     cm->map[0] = PA_CHANNEL_POSITION_MONO;
    602   }
    603 }
    604 
    605 static void
    606 pulse_context_destroy(cubeb * ctx);
    607 static void
    608 pulse_destroy(cubeb * ctx);
    609 
    610 static int
    611 pulse_context_init(cubeb * ctx)
    612 {
    613   int r;
    614 
    615   if (ctx->context) {
    616     assert(ctx->error == 1);
    617     pulse_context_destroy(ctx);
    618   }
    619 
    620   ctx->context = WRAP(pa_context_new)(
    621       WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop), ctx->context_name);
    622   if (!ctx->context) {
    623     return -1;
    624   }
    625   WRAP(pa_context_set_state_callback)
    626   (ctx->context, context_state_callback, ctx);
    627 
    628   WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
    629   r = WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL);
    630 
    631   if (r < 0 || wait_until_context_ready(ctx) != 0) {
    632     WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
    633     pulse_context_destroy(ctx);
    634     ctx->context = NULL;
    635     return -1;
    636   }
    637 
    638   WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
    639 
    640   ctx->error = 0;
    641 
    642   return 0;
    643 }
    644 
    645 static int
    646 pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask);
    647 
    648 /*static*/ int
    649 pulse_init(cubeb ** context, char const * context_name)
    650 {
    651   void * libpulse = NULL;
    652   cubeb * ctx;
    653   pa_operation * o;
    654 
    655   *context = NULL;
    656 
    657 #ifndef DISABLE_LIBPULSE_DLOPEN
    658   libpulse = dlopen("libpulse.so.0", RTLD_LAZY);
    659   if (!libpulse) {
    660     libpulse = dlopen("libpulse.so", RTLD_LAZY);
    661     if (!libpulse) {
    662       return CUBEB_ERROR;
    663     }
    664   }
    665 
    666 #define LOAD(x)                                                                \
    667   {                                                                            \
    668     cubeb_##x = dlsym(libpulse, #x);                                           \
    669     if (!cubeb_##x) {                                                          \
    670       dlclose(libpulse);                                                       \
    671       return CUBEB_ERROR;                                                      \
    672     }                                                                          \
    673   }
    674 
    675   LIBPULSE_API_VISIT(LOAD);
    676 #undef LOAD
    677 #endif
    678 
    679 #if PA_CHECK_VERSION(2, 0, 0)
    680   const char * version = WRAP(pa_get_library_version)();
    681   has_pulse_v2 = strtol(version, NULL, 10) >= 2;
    682 #endif
    683 
    684   ctx = calloc(1, sizeof(*ctx));
    685   assert(ctx);
    686 
    687   ctx->ops = &pulse_ops;
    688   ctx->libpulse = libpulse;
    689   if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) {
    690     pulse_destroy(ctx);
    691     return CUBEB_ERROR;
    692   }
    693 
    694   ctx->mainloop = WRAP(pa_threaded_mainloop_new)();
    695   ctx->default_sink_info = NULL;
    696 
    697   WRAP(pa_threaded_mainloop_start)(ctx->mainloop);
    698 
    699   ctx->context_name = context_name ? strdup(context_name) : NULL;
    700   if (pulse_context_init(ctx) != 0) {
    701     pulse_destroy(ctx);
    702     return CUBEB_ERROR;
    703   }
    704 
    705   /* server_info_callback performs a second async query, which is
    706      responsible for initializing default_sink_info and signalling the
    707      mainloop to end the wait. */
    708   WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
    709   o = WRAP(pa_context_get_server_info)(ctx->context, server_info_callback, ctx);
    710   if (o) {
    711     operation_wait(ctx, NULL, o);
    712     WRAP(pa_operation_unref)(o);
    713   }
    714   WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
    715 
    716   /* Update `default_sink_info` when the default device changes. */
    717   pulse_subscribe_notifications(ctx, PA_SUBSCRIPTION_MASK_SERVER);
    718 
    719   *context = ctx;
    720 
    721   return CUBEB_OK;
    722 }
    723 
    724 static char const *
    725 pulse_get_backend_id(cubeb * ctx)
    726 {
    727   (void)ctx;
    728   return "pulse";
    729 }
    730 
    731 static int
    732 pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
    733 {
    734   (void)ctx;
    735   assert(ctx && max_channels);
    736 
    737   if (!ctx->default_sink_info)
    738     return CUBEB_ERROR;
    739 
    740   *max_channels = ctx->default_sink_info->channel_map.channels;
    741 
    742   return CUBEB_OK;
    743 }
    744 
    745 static int
    746 pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
    747 {
    748   assert(ctx && rate);
    749   (void)ctx;
    750 
    751   if (!ctx->default_sink_info)
    752     return CUBEB_ERROR;
    753 
    754   *rate = ctx->default_sink_info->sample_spec_rate;
    755 
    756   return CUBEB_OK;
    757 }
    758 
    759 static int
    760 pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params,
    761                       uint32_t * latency_frames)
    762 {
    763   (void)ctx;
    764   // According to PulseAudio developers, this is a safe minimum.
    765   *latency_frames = 25 * params.rate / 1000;
    766 
    767   return CUBEB_OK;
    768 }
    769 
    770 static void
    771 pulse_context_destroy(cubeb * ctx)
    772 {
    773   pa_operation * o;
    774 
    775   WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
    776   o = WRAP(pa_context_drain)(ctx->context, context_notify_callback, ctx);
    777   if (o) {
    778     operation_wait(ctx, NULL, o);
    779     WRAP(pa_operation_unref)(o);
    780   }
    781   WRAP(pa_context_set_state_callback)(ctx->context, NULL, NULL);
    782   WRAP(pa_context_disconnect)(ctx->context);
    783   WRAP(pa_context_unref)(ctx->context);
    784   WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
    785 }
    786 
    787 static void
    788 pulse_destroy(cubeb * ctx)
    789 {
    790   assert(!ctx->input_collection_changed_callback &&
    791          !ctx->input_collection_changed_user_ptr &&
    792          !ctx->output_collection_changed_callback &&
    793          !ctx->output_collection_changed_user_ptr);
    794   free(ctx->context_name);
    795   if (ctx->context) {
    796     pulse_context_destroy(ctx);
    797   }
    798 
    799   if (ctx->mainloop) {
    800     WRAP(pa_threaded_mainloop_stop)(ctx->mainloop);
    801     WRAP(pa_threaded_mainloop_free)(ctx->mainloop);
    802   }
    803 
    804   if (ctx->device_ids) {
    805     cubeb_strings_destroy(ctx->device_ids);
    806   }
    807 #ifndef DISABLE_LIBPULSE_DLOPEN
    808   if (ctx->libpulse) {
    809     dlclose(ctx->libpulse);
    810   }
    811 #endif
    812   free(ctx->default_sink_info);
    813   free(ctx);
    814 }
    815 
    816 static void
    817 pulse_stream_destroy(cubeb_stream * stm);
    818 
    819 static pa_sample_format_t
    820 to_pulse_format(cubeb_sample_format format)
    821 {
    822   switch (format) {
    823   case CUBEB_SAMPLE_S16LE:
    824     return PA_SAMPLE_S16LE;
    825   case CUBEB_SAMPLE_S16BE:
    826     return PA_SAMPLE_S16BE;
    827   case CUBEB_SAMPLE_FLOAT32LE:
    828     return PA_SAMPLE_FLOAT32LE;
    829   case CUBEB_SAMPLE_FLOAT32BE:
    830     return PA_SAMPLE_FLOAT32BE;
    831   default:
    832     return PA_SAMPLE_INVALID;
    833   }
    834 }
    835 
    836 static cubeb_channel_layout
    837 pulse_default_layout_for_channels(uint32_t ch)
    838 {
    839   assert(ch > 0 && ch <= 8);
    840   switch (ch) {
    841   case 1:
    842     return CUBEB_LAYOUT_MONO;
    843   case 2:
    844     return CUBEB_LAYOUT_STEREO;
    845   case 3:
    846     return CUBEB_LAYOUT_3F;
    847   case 4:
    848     return CUBEB_LAYOUT_QUAD;
    849   case 5:
    850     return CUBEB_LAYOUT_3F2;
    851   case 6:
    852     return CUBEB_LAYOUT_3F_LFE | CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT;
    853   case 7:
    854     return CUBEB_LAYOUT_3F3R_LFE;
    855   case 8:
    856     return CUBEB_LAYOUT_3F4_LFE;
    857   }
    858   // Never get here!
    859   return CUBEB_LAYOUT_UNDEFINED;
    860 }
    861 
    862 static int
    863 create_pa_stream(cubeb_stream * stm, pa_stream ** pa_stm,
    864                  cubeb_stream_params * stream_params, char const * stream_name)
    865 {
    866   assert(stm && stream_params);
    867   assert(&stm->input_stream == pa_stm ||
    868          (&stm->output_stream == pa_stm &&
    869           (stream_params->layout == CUBEB_LAYOUT_UNDEFINED ||
    870            (stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
    871             cubeb_channel_layout_nb_channels(stream_params->layout) ==
    872                 stream_params->channels))));
    873   if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
    874     return CUBEB_ERROR_NOT_SUPPORTED;
    875   }
    876   *pa_stm = NULL;
    877   pa_sample_spec ss;
    878   ss.format = to_pulse_format(stream_params->format);
    879   if (ss.format == PA_SAMPLE_INVALID)
    880     return CUBEB_ERROR_INVALID_FORMAT;
    881   ss.rate = stream_params->rate;
    882   if (stream_params->channels > UINT8_MAX)
    883     return CUBEB_ERROR_INVALID_FORMAT;
    884   ss.channels = (uint8_t)stream_params->channels;
    885 
    886   if (stream_params->layout == CUBEB_LAYOUT_UNDEFINED) {
    887     pa_channel_map cm;
    888     if (stream_params->channels <= 8 &&
    889         !WRAP(pa_channel_map_init_auto)(&cm, stream_params->channels,
    890                                         PA_CHANNEL_MAP_DEFAULT)) {
    891       LOG("Layout undefined and PulseAudio's default layout has not been "
    892           "configured, guess one.");
    893       layout_to_channel_map(
    894           pulse_default_layout_for_channels(stream_params->channels), &cm);
    895       *pa_stm =
    896           WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm);
    897     } else {
    898       LOG("Layout undefined, PulseAudio will use its default.");
    899       *pa_stm =
    900           WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL);
    901     }
    902   } else {
    903     pa_channel_map cm;
    904     layout_to_channel_map(stream_params->layout, &cm);
    905     *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm);
    906   }
    907   return (*pa_stm == NULL) ? CUBEB_ERROR : CUBEB_OK;
    908 }
    909 
    910 static pa_buffer_attr
    911 set_buffering_attribute(unsigned int latency_frames,
    912                         pa_sample_spec * sample_spec)
    913 {
    914   pa_buffer_attr battr;
    915   battr.maxlength = -1;
    916   battr.prebuf = -1;
    917   battr.tlength = latency_frames * WRAP(pa_frame_size)(sample_spec);
    918   battr.minreq = battr.tlength / 4;
    919   battr.fragsize = battr.minreq;
    920 
    921   LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq "
    922       "%u, fragsize %u",
    923       battr.maxlength, battr.tlength, battr.prebuf, battr.minreq,
    924       battr.fragsize);
    925 
    926   return battr;
    927 }
    928 
    929 static int
    930 pulse_stream_init(cubeb * context, cubeb_stream ** stream,
    931                   char const * stream_name, cubeb_devid input_device,
    932                   cubeb_stream_params * input_stream_params,
    933                   cubeb_devid output_device,
    934                   cubeb_stream_params * output_stream_params,
    935                   unsigned int latency_frames,
    936                   cubeb_data_callback data_callback,
    937                   cubeb_state_callback state_callback, void * user_ptr)
    938 {
    939   cubeb_stream * stm;
    940   pa_buffer_attr battr;
    941   int r;
    942 
    943   assert(context);
    944 
    945   // If the connection failed for some reason, try to reconnect
    946   if (context->error == 1 && pulse_context_init(context) != 0) {
    947     return CUBEB_ERROR;
    948   }
    949 
    950   *stream = NULL;
    951 
    952   stm = calloc(1, sizeof(*stm));
    953   assert(stm);
    954 
    955   stm->context = context;
    956   stm->data_callback = data_callback;
    957   stm->state_callback = state_callback;
    958   stm->user_ptr = user_ptr;
    959   stm->volume = PULSE_NO_GAIN;
    960   stm->state = -1;
    961   assert(stm->shutdown == 0);
    962 
    963   WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
    964   if (output_stream_params) {
    965     r = create_pa_stream(stm, &stm->output_stream, output_stream_params,
    966                          stream_name);
    967     if (r != CUBEB_OK) {
    968       WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
    969       pulse_stream_destroy(stm);
    970       return r;
    971     }
    972 
    973     stm->output_sample_spec =
    974         *(WRAP(pa_stream_get_sample_spec)(stm->output_stream));
    975 
    976     WRAP(pa_stream_set_state_callback)
    977     (stm->output_stream, stream_state_callback, stm);
    978     WRAP(pa_stream_set_write_callback)
    979     (stm->output_stream, stream_write_callback, stm);
    980 
    981     battr = set_buffering_attribute(latency_frames, &stm->output_sample_spec);
    982     WRAP(pa_stream_connect_playback)
    983     (stm->output_stream, (char const *)output_device, &battr,
    984      PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
    985          PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY,
    986      NULL, NULL);
    987   }
    988 
    989   // Set up input stream
    990   if (input_stream_params) {
    991     r = create_pa_stream(stm, &stm->input_stream, input_stream_params,
    992                          stream_name);
    993     if (r != CUBEB_OK) {
    994       WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
    995       pulse_stream_destroy(stm);
    996       return r;
    997     }
    998 
    999     stm->input_sample_spec =
   1000         *(WRAP(pa_stream_get_sample_spec)(stm->input_stream));
   1001 
   1002     WRAP(pa_stream_set_state_callback)
   1003     (stm->input_stream, stream_state_callback, stm);
   1004     WRAP(pa_stream_set_read_callback)
   1005     (stm->input_stream, stream_read_callback, stm);
   1006 
   1007     battr = set_buffering_attribute(latency_frames, &stm->input_sample_spec);
   1008     WRAP(pa_stream_connect_record)
   1009     (stm->input_stream, (char const *)input_device, &battr,
   1010      PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
   1011          PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY);
   1012   }
   1013 
   1014   r = wait_until_stream_ready(stm);
   1015   if (r == 0) {
   1016     /* force a timing update now, otherwise timing info does not become valid
   1017        until some point after initialization has completed. */
   1018     r = stream_update_timing_info(stm);
   1019   }
   1020 
   1021   WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
   1022 
   1023   if (r != 0) {
   1024     pulse_stream_destroy(stm);
   1025     return CUBEB_ERROR;
   1026   }
   1027 
   1028   if (cubeb_log_get_level()) {
   1029     if (output_stream_params) {
   1030       const pa_buffer_attr * output_att;
   1031       output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream);
   1032       LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, "
   1033           "minreq %u, fragsize %u",
   1034           output_att->maxlength, output_att->tlength, output_att->prebuf,
   1035           output_att->minreq, output_att->fragsize);
   1036     }
   1037 
   1038     if (input_stream_params) {
   1039       const pa_buffer_attr * input_att;
   1040       input_att = WRAP(pa_stream_get_buffer_attr)(stm->input_stream);
   1041       LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq "
   1042           "%u, fragsize %u",
   1043           input_att->maxlength, input_att->tlength, input_att->prebuf,
   1044           input_att->minreq, input_att->fragsize);
   1045     }
   1046   }
   1047 
   1048   *stream = stm;
   1049   LOG("Cubeb stream (%p) init successful.", *stream);
   1050 
   1051   return CUBEB_OK;
   1052 }
   1053 
   1054 static void
   1055 pulse_stream_destroy(cubeb_stream * stm)
   1056 {
   1057   stream_cork(stm, CORK);
   1058 
   1059   WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
   1060   if (stm->output_stream) {
   1061 
   1062     if (stm->drain_timer) {
   1063       /* there's no pa_rttime_free, so use this instead. */
   1064       WRAP(pa_threaded_mainloop_get_api)
   1065       (stm->context->mainloop)->time_free(stm->drain_timer);
   1066     }
   1067 
   1068     WRAP(pa_stream_set_state_callback)(stm->output_stream, NULL, NULL);
   1069     WRAP(pa_stream_set_write_callback)(stm->output_stream, NULL, NULL);
   1070     WRAP(pa_stream_disconnect)(stm->output_stream);
   1071     WRAP(pa_stream_unref)(stm->output_stream);
   1072   }
   1073 
   1074   if (stm->input_stream) {
   1075     WRAP(pa_stream_set_state_callback)(stm->input_stream, NULL, NULL);
   1076     WRAP(pa_stream_set_read_callback)(stm->input_stream, NULL, NULL);
   1077     WRAP(pa_stream_disconnect)(stm->input_stream);
   1078     WRAP(pa_stream_unref)(stm->input_stream);
   1079   }
   1080   WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
   1081 
   1082   LOG("Cubeb stream (%p) destroyed successfully.", stm);
   1083   free(stm);
   1084 }
   1085 
   1086 static void
   1087 pulse_defer_event_cb(pa_mainloop_api * a, void * userdata)
   1088 {
   1089   (void)a;
   1090   cubeb_stream * stm = userdata;
   1091   if (stm->shutdown) {
   1092     return;
   1093   }
   1094   size_t writable_size = WRAP(pa_stream_writable_size)(stm->output_stream);
   1095   trigger_user_callback(stm->output_stream, NULL, writable_size, stm);
   1096 }
   1097 
   1098 static int
   1099 pulse_stream_start(cubeb_stream * stm)
   1100 {
   1101   stm->shutdown = 0;
   1102   stream_cork(stm, UNCORK | NOTIFY);
   1103 
   1104   if (stm->output_stream && !stm->input_stream) {
   1105     /* On output only case need to manually call user cb once in order to make
   1106      * things roll. This is done via a defer event in order to execute it
   1107      * from PA server thread. */
   1108     WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
   1109     WRAP(pa_mainloop_api_once)
   1110     (WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop),
   1111      pulse_defer_event_cb, stm);
   1112     WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
   1113   }
   1114 
   1115   LOG("Cubeb stream (%p) started successfully.", stm);
   1116   return CUBEB_OK;
   1117 }
   1118 
   1119 static int
   1120 pulse_stream_stop(cubeb_stream * stm)
   1121 {
   1122   WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
   1123   stm->shutdown = 1;
   1124   // If draining is taking place wait to finish
   1125   while (stm->drain_timer) {
   1126     WRAP(pa_threaded_mainloop_wait)(stm->context->mainloop);
   1127   }
   1128   WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
   1129 
   1130   stream_cork(stm, CORK | NOTIFY);
   1131   LOG("Cubeb stream (%p) stopped successfully.", stm);
   1132   return CUBEB_OK;
   1133 }
   1134 
   1135 static int
   1136 pulse_stream_get_position(cubeb_stream * stm, uint64_t * position)
   1137 {
   1138   int r, in_thread;
   1139   pa_usec_t r_usec;
   1140   uint64_t bytes;
   1141 
   1142   if (!stm || !stm->output_stream) {
   1143     return CUBEB_ERROR;
   1144   }
   1145 
   1146   in_thread = WRAP(pa_threaded_mainloop_in_thread)(stm->context->mainloop);
   1147 
   1148   if (!in_thread) {
   1149     WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
   1150   }
   1151   r = WRAP(pa_stream_get_time)(stm->output_stream, &r_usec);
   1152   if (!in_thread) {
   1153     WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
   1154   }
   1155 
   1156   if (r != 0) {
   1157     return CUBEB_ERROR;
   1158   }
   1159 
   1160   bytes = WRAP(pa_usec_to_bytes)(r_usec, &stm->output_sample_spec);
   1161   *position = bytes / WRAP(pa_frame_size)(&stm->output_sample_spec);
   1162 
   1163   return CUBEB_OK;
   1164 }
   1165 
   1166 static int
   1167 pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
   1168 {
   1169   pa_usec_t r_usec;
   1170   int negative, r;
   1171 
   1172   if (!stm || !stm->output_stream) {
   1173     return CUBEB_ERROR;
   1174   }
   1175 
   1176   r = WRAP(pa_stream_get_latency)(stm->output_stream, &r_usec, &negative);
   1177   assert(!negative);
   1178   if (r) {
   1179     return CUBEB_ERROR;
   1180   }
   1181 
   1182   *latency = r_usec * stm->output_sample_spec.rate / PA_USEC_PER_SEC;
   1183   return CUBEB_OK;
   1184 }
   1185 
   1186 static void
   1187 volume_success(pa_context * c, int success, void * userdata)
   1188 {
   1189   (void)success;
   1190   (void)c;
   1191   cubeb_stream * stream = userdata;
   1192   assert(success);
   1193   WRAP(pa_threaded_mainloop_signal)(stream->context->mainloop, 0);
   1194 }
   1195 
   1196 static void
   1197 rename_success(pa_stream * s, int success, void * userdata)
   1198 {
   1199   cubeb_stream * stream = userdata;
   1200   assert(success);
   1201   WRAP(pa_threaded_mainloop_signal)(stream->context->mainloop, 0);
   1202 }
   1203 
   1204 static int
   1205 pulse_stream_set_volume(cubeb_stream * stm, float volume)
   1206 {
   1207   uint32_t index;
   1208   pa_operation * op;
   1209   pa_volume_t vol;
   1210   pa_cvolume cvol;
   1211   const pa_sample_spec * ss;
   1212   cubeb * ctx;
   1213 
   1214   if (!stm->output_stream) {
   1215     return CUBEB_ERROR;
   1216   }
   1217 
   1218   WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
   1219 
   1220   /* if the pulse daemon is configured to use flat volumes,
   1221    * apply our own gain instead of changing the input volume on the sink. */
   1222   ctx = stm->context;
   1223   if (ctx->default_sink_info &&
   1224       (ctx->default_sink_info->flags & PA_SINK_FLAT_VOLUME)) {
   1225     stm->volume = volume;
   1226   } else {
   1227     ss = WRAP(pa_stream_get_sample_spec)(stm->output_stream);
   1228 
   1229     vol = WRAP(pa_sw_volume_from_linear)(volume);
   1230     WRAP(pa_cvolume_set)(&cvol, ss->channels, vol);
   1231 
   1232     index = WRAP(pa_stream_get_index)(stm->output_stream);
   1233 
   1234     op = WRAP(pa_context_set_sink_input_volume)(ctx->context, index, &cvol,
   1235                                                 volume_success, stm);
   1236     if (op) {
   1237       operation_wait(ctx, stm->output_stream, op);
   1238       WRAP(pa_operation_unref)(op);
   1239     }
   1240   }
   1241 
   1242   WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
   1243 
   1244   return CUBEB_OK;
   1245 }
   1246 
   1247 static int
   1248 pulse_stream_set_name(cubeb_stream * stm, char const * stream_name)
   1249 {
   1250   if (!stm || !stm->output_stream) {
   1251     return CUBEB_ERROR;
   1252   }
   1253 
   1254   WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
   1255 
   1256   pa_operation * op = WRAP(pa_stream_set_name)(stm->output_stream, stream_name,
   1257                                                rename_success, stm);
   1258 
   1259   if (op) {
   1260     operation_wait(stm->context, stm->output_stream, op);
   1261     WRAP(pa_operation_unref)(op);
   1262   }
   1263 
   1264   WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
   1265 
   1266   return CUBEB_OK;
   1267 }
   1268 
   1269 typedef struct {
   1270   char * default_sink_name;
   1271   char * default_source_name;
   1272 
   1273   cubeb_device_info * devinfo;
   1274   uint32_t max;
   1275   uint32_t count;
   1276   cubeb * context;
   1277 } pulse_dev_list_data;
   1278 
   1279 static cubeb_device_fmt
   1280 pulse_format_to_cubeb_format(pa_sample_format_t format)
   1281 {
   1282   switch (format) {
   1283   case PA_SAMPLE_S16LE:
   1284     return CUBEB_DEVICE_FMT_S16LE;
   1285   case PA_SAMPLE_S16BE:
   1286     return CUBEB_DEVICE_FMT_S16BE;
   1287   case PA_SAMPLE_FLOAT32LE:
   1288     return CUBEB_DEVICE_FMT_F32LE;
   1289   case PA_SAMPLE_FLOAT32BE:
   1290     return CUBEB_DEVICE_FMT_F32BE;
   1291   default:
   1292     return CUBEB_DEVICE_FMT_F32NE;
   1293   }
   1294 }
   1295 
   1296 static void
   1297 pulse_ensure_dev_list_data_list_size(pulse_dev_list_data * list_data)
   1298 {
   1299   if (list_data->count == list_data->max) {
   1300     list_data->max += 8;
   1301     list_data->devinfo =
   1302         realloc(list_data->devinfo, sizeof(cubeb_device_info) * list_data->max);
   1303   }
   1304 }
   1305 
   1306 static cubeb_device_state
   1307 pulse_get_state_from_sink_port(pa_sink_port_info * info)
   1308 {
   1309   if (info != NULL) {
   1310 #if PA_CHECK_VERSION(2, 0, 0)
   1311     if (has_pulse_v2 && info->available == PA_PORT_AVAILABLE_NO)
   1312       return CUBEB_DEVICE_STATE_UNPLUGGED;
   1313     else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */
   1314 #endif
   1315       return CUBEB_DEVICE_STATE_ENABLED;
   1316   }
   1317 
   1318   return CUBEB_DEVICE_STATE_ENABLED;
   1319 }
   1320 
   1321 static void
   1322 pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, int eol,
   1323                    void * user_data)
   1324 {
   1325   pulse_dev_list_data * list_data = user_data;
   1326   cubeb_device_info * devinfo;
   1327   char const * prop = NULL;
   1328   char const * device_id = NULL;
   1329 
   1330   (void)context;
   1331 
   1332   if (eol) {
   1333     WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
   1334     return;
   1335   }
   1336 
   1337   if (info == NULL)
   1338     return;
   1339 
   1340   device_id = info->name;
   1341   if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
   1342     assert(NULL);
   1343     return;
   1344   }
   1345 
   1346   pulse_ensure_dev_list_data_list_size(list_data);
   1347   devinfo = &list_data->devinfo[list_data->count];
   1348   memset(devinfo, 0, sizeof(cubeb_device_info));
   1349 
   1350   devinfo->device_id = device_id;
   1351   devinfo->devid = (cubeb_devid)devinfo->device_id;
   1352   devinfo->friendly_name = strdup(info->description);
   1353   prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
   1354   if (prop)
   1355     devinfo->group_id = strdup(prop);
   1356   prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name");
   1357   if (prop)
   1358     devinfo->vendor_name = strdup(prop);
   1359 
   1360   devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT;
   1361   devinfo->state = pulse_get_state_from_sink_port(info->active_port);
   1362   devinfo->preferred = (strcmp(info->name, list_data->default_sink_name) == 0)
   1363                            ? CUBEB_DEVICE_PREF_ALL
   1364                            : CUBEB_DEVICE_PREF_NONE;
   1365 
   1366   devinfo->format = CUBEB_DEVICE_FMT_ALL;
   1367   devinfo->default_format =
   1368       pulse_format_to_cubeb_format(info->sample_spec.format);
   1369   devinfo->max_channels = info->channel_map.channels;
   1370   devinfo->min_rate = 1;
   1371   devinfo->max_rate = PA_RATE_MAX;
   1372   devinfo->default_rate = info->sample_spec.rate;
   1373 
   1374   devinfo->latency_lo = 0;
   1375   devinfo->latency_hi = 0;
   1376 
   1377   list_data->count += 1;
   1378 }
   1379 
   1380 static cubeb_device_state
   1381 pulse_get_state_from_source_port(pa_source_port_info * info)
   1382 {
   1383   if (info != NULL) {
   1384 #if PA_CHECK_VERSION(2, 0, 0)
   1385     if (has_pulse_v2 && info->available == PA_PORT_AVAILABLE_NO)
   1386       return CUBEB_DEVICE_STATE_UNPLUGGED;
   1387     else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */
   1388 #endif
   1389       return CUBEB_DEVICE_STATE_ENABLED;
   1390   }
   1391 
   1392   return CUBEB_DEVICE_STATE_ENABLED;
   1393 }
   1394 
   1395 static void
   1396 pulse_source_info_cb(pa_context * context, const pa_source_info * info, int eol,
   1397                      void * user_data)
   1398 {
   1399   pulse_dev_list_data * list_data = user_data;
   1400   cubeb_device_info * devinfo;
   1401   char const * prop = NULL;
   1402   char const * device_id = NULL;
   1403 
   1404   (void)context;
   1405 
   1406   if (eol) {
   1407     WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
   1408     return;
   1409   }
   1410 
   1411   device_id = info->name;
   1412   if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
   1413     assert(NULL);
   1414     return;
   1415   }
   1416 
   1417   pulse_ensure_dev_list_data_list_size(list_data);
   1418   devinfo = &list_data->devinfo[list_data->count];
   1419   memset(devinfo, 0, sizeof(cubeb_device_info));
   1420 
   1421   devinfo->device_id = device_id;
   1422   devinfo->devid = (cubeb_devid)devinfo->device_id;
   1423   devinfo->friendly_name = strdup(info->description);
   1424   prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
   1425   if (prop)
   1426     devinfo->group_id = strdup(prop);
   1427   prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name");
   1428   if (prop)
   1429     devinfo->vendor_name = strdup(prop);
   1430 
   1431   devinfo->type = CUBEB_DEVICE_TYPE_INPUT;
   1432   devinfo->state = pulse_get_state_from_source_port(info->active_port);
   1433   devinfo->preferred = (strcmp(info->name, list_data->default_source_name) == 0)
   1434                            ? CUBEB_DEVICE_PREF_ALL
   1435                            : CUBEB_DEVICE_PREF_NONE;
   1436 
   1437   devinfo->format = CUBEB_DEVICE_FMT_ALL;
   1438   devinfo->default_format =
   1439       pulse_format_to_cubeb_format(info->sample_spec.format);
   1440   devinfo->max_channels = info->channel_map.channels;
   1441   devinfo->min_rate = 1;
   1442   devinfo->max_rate = PA_RATE_MAX;
   1443   devinfo->default_rate = info->sample_spec.rate;
   1444 
   1445   devinfo->latency_lo = 0;
   1446   devinfo->latency_hi = 0;
   1447 
   1448   list_data->count += 1;
   1449 }
   1450 
   1451 static void
   1452 pulse_server_info_cb(pa_context * c, const pa_server_info * i, void * userdata)
   1453 {
   1454   pulse_dev_list_data * list_data = userdata;
   1455 
   1456   (void)c;
   1457 
   1458   free(list_data->default_sink_name);
   1459   free(list_data->default_source_name);
   1460   list_data->default_sink_name =
   1461       i->default_sink_name ? strdup(i->default_sink_name) : NULL;
   1462   list_data->default_source_name =
   1463       i->default_source_name ? strdup(i->default_source_name) : NULL;
   1464 
   1465   WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
   1466 }
   1467 
   1468 static int
   1469 pulse_enumerate_devices(cubeb * context, cubeb_device_type type,
   1470                         cubeb_device_collection * collection)
   1471 {
   1472   pulse_dev_list_data user_data = {NULL, NULL, NULL, 0, 0, context};
   1473   pa_operation * o;
   1474 
   1475   WRAP(pa_threaded_mainloop_lock)(context->mainloop);
   1476 
   1477   o = WRAP(pa_context_get_server_info)(context->context, pulse_server_info_cb,
   1478                                        &user_data);
   1479   if (o) {
   1480     operation_wait(context, NULL, o);
   1481     WRAP(pa_operation_unref)(o);
   1482   }
   1483 
   1484   if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
   1485     o = WRAP(pa_context_get_sink_info_list)(context->context,
   1486                                             pulse_sink_info_cb, &user_data);
   1487     if (o) {
   1488       operation_wait(context, NULL, o);
   1489       WRAP(pa_operation_unref)(o);
   1490     }
   1491   }
   1492 
   1493   if (type & CUBEB_DEVICE_TYPE_INPUT) {
   1494     o = WRAP(pa_context_get_source_info_list)(context->context,
   1495                                               pulse_source_info_cb, &user_data);
   1496     if (o) {
   1497       operation_wait(context, NULL, o);
   1498       WRAP(pa_operation_unref)(o);
   1499     }
   1500   }
   1501 
   1502   WRAP(pa_threaded_mainloop_unlock)(context->mainloop);
   1503 
   1504   collection->device = user_data.devinfo;
   1505   collection->count = user_data.count;
   1506 
   1507   free(user_data.default_sink_name);
   1508   free(user_data.default_source_name);
   1509   return CUBEB_OK;
   1510 }
   1511 
   1512 static int
   1513 pulse_device_collection_destroy(cubeb * ctx,
   1514                                 cubeb_device_collection * collection)
   1515 {
   1516   size_t n;
   1517 
   1518   for (n = 0; n < collection->count; n++) {
   1519     free((void *)collection->device[n].friendly_name);
   1520     free((void *)collection->device[n].vendor_name);
   1521     free((void *)collection->device[n].group_id);
   1522   }
   1523 
   1524   free(collection->device);
   1525   return CUBEB_OK;
   1526 }
   1527 
   1528 static int
   1529 pulse_stream_get_current_device(cubeb_stream * stm,
   1530                                 cubeb_device ** const device)
   1531 {
   1532 #if PA_CHECK_VERSION(0, 9, 8)
   1533   *device = calloc(1, sizeof(cubeb_device));
   1534   if (*device == NULL)
   1535     return CUBEB_ERROR;
   1536 
   1537   if (stm->input_stream) {
   1538     const char * name = WRAP(pa_stream_get_device_name)(stm->input_stream);
   1539     (*device)->input_name = (name == NULL) ? NULL : strdup(name);
   1540   }
   1541 
   1542   if (stm->output_stream) {
   1543     const char * name = WRAP(pa_stream_get_device_name)(stm->output_stream);
   1544     (*device)->output_name = (name == NULL) ? NULL : strdup(name);
   1545   }
   1546 
   1547   return CUBEB_OK;
   1548 #else
   1549   return CUBEB_ERROR_NOT_SUPPORTED;
   1550 #endif
   1551 }
   1552 
   1553 static int
   1554 pulse_stream_device_destroy(cubeb_stream * stream, cubeb_device * device)
   1555 {
   1556   (void)stream;
   1557   free(device->input_name);
   1558   free(device->output_name);
   1559   free(device);
   1560   return CUBEB_OK;
   1561 }
   1562 
   1563 static void
   1564 pulse_subscribe_callback(pa_context * ctx, pa_subscription_event_type_t t,
   1565                          uint32_t index, void * userdata)
   1566 {
   1567   (void)ctx;
   1568   cubeb * context = userdata;
   1569 
   1570   switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
   1571   case PA_SUBSCRIPTION_EVENT_SERVER:
   1572     if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
   1573       LOG("Server changed %d", index);
   1574       WRAP(pa_context_get_server_info)
   1575       (context->context, server_info_callback, context);
   1576     }
   1577     break;
   1578   case PA_SUBSCRIPTION_EVENT_SOURCE:
   1579   case PA_SUBSCRIPTION_EVENT_SINK:
   1580 
   1581     if (cubeb_log_get_level()) {
   1582       if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
   1583               PA_SUBSCRIPTION_EVENT_SOURCE &&
   1584           (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
   1585               PA_SUBSCRIPTION_EVENT_REMOVE) {
   1586         LOG("Removing source index %d", index);
   1587       } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
   1588                      PA_SUBSCRIPTION_EVENT_SOURCE &&
   1589                  (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
   1590                      PA_SUBSCRIPTION_EVENT_NEW) {
   1591         LOG("Adding source index %d", index);
   1592       }
   1593       if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
   1594               PA_SUBSCRIPTION_EVENT_SINK &&
   1595           (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
   1596               PA_SUBSCRIPTION_EVENT_REMOVE) {
   1597         LOG("Removing sink index %d", index);
   1598       } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
   1599                      PA_SUBSCRIPTION_EVENT_SINK &&
   1600                  (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
   1601                      PA_SUBSCRIPTION_EVENT_NEW) {
   1602         LOG("Adding sink index %d", index);
   1603       }
   1604     }
   1605 
   1606     if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE ||
   1607         (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
   1608       if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
   1609           PA_SUBSCRIPTION_EVENT_SOURCE) {
   1610         context->input_collection_changed_callback(
   1611             context, context->input_collection_changed_user_ptr);
   1612       }
   1613       if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
   1614           PA_SUBSCRIPTION_EVENT_SINK) {
   1615         context->output_collection_changed_callback(
   1616             context, context->output_collection_changed_user_ptr);
   1617       }
   1618     }
   1619     break;
   1620   }
   1621 }
   1622 
   1623 static void
   1624 subscribe_success(pa_context * c, int success, void * userdata)
   1625 {
   1626   (void)c;
   1627   cubeb * context = userdata;
   1628   assert(success);
   1629   WRAP(pa_threaded_mainloop_signal)(context->mainloop, 0);
   1630 }
   1631 
   1632 static int
   1633 pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask)
   1634 {
   1635   WRAP(pa_threaded_mainloop_lock)(context->mainloop);
   1636 
   1637   WRAP(pa_context_set_subscribe_callback)
   1638   (context->context, pulse_subscribe_callback, context);
   1639 
   1640   pa_operation * o;
   1641   o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success,
   1642                                  context);
   1643   if (o == NULL) {
   1644     WRAP(pa_threaded_mainloop_unlock)(context->mainloop);
   1645     LOG("Context subscribe failed");
   1646     return CUBEB_ERROR;
   1647   }
   1648   operation_wait(context, NULL, o);
   1649   WRAP(pa_operation_unref)(o);
   1650 
   1651   WRAP(pa_threaded_mainloop_unlock)(context->mainloop);
   1652 
   1653   return CUBEB_OK;
   1654 }
   1655 
   1656 static int
   1657 pulse_register_device_collection_changed(
   1658     cubeb * context, cubeb_device_type devtype,
   1659     cubeb_device_collection_changed_callback collection_changed_callback,
   1660     void * user_ptr)
   1661 {
   1662   if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
   1663     context->input_collection_changed_callback = collection_changed_callback;
   1664     context->input_collection_changed_user_ptr = user_ptr;
   1665   }
   1666   if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
   1667     context->output_collection_changed_callback = collection_changed_callback;
   1668     context->output_collection_changed_user_ptr = user_ptr;
   1669   }
   1670 
   1671   pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_NULL;
   1672   if (context->input_collection_changed_callback) {
   1673     /* Input added or removed */
   1674     mask |= PA_SUBSCRIPTION_MASK_SOURCE;
   1675   }
   1676   if (context->output_collection_changed_callback) {
   1677     /* Output added or removed */
   1678     mask |= PA_SUBSCRIPTION_MASK_SINK;
   1679   }
   1680   /* Default device changed, this is always registered in order to update the
   1681    * `default_sink_info` when the default device changes. */
   1682   mask |= PA_SUBSCRIPTION_MASK_SERVER;
   1683 
   1684   return pulse_subscribe_notifications(context, mask);
   1685 }
   1686 
   1687 static struct cubeb_ops const pulse_ops = {
   1688     .init = pulse_init,
   1689     .get_backend_id = pulse_get_backend_id,
   1690     .get_max_channel_count = pulse_get_max_channel_count,
   1691     .get_min_latency = pulse_get_min_latency,
   1692     .get_preferred_sample_rate = pulse_get_preferred_sample_rate,
   1693     .get_supported_input_processing_params = NULL,
   1694     .enumerate_devices = pulse_enumerate_devices,
   1695     .device_collection_destroy = pulse_device_collection_destroy,
   1696     .destroy = pulse_destroy,
   1697     .stream_init = pulse_stream_init,
   1698     .stream_destroy = pulse_stream_destroy,
   1699     .stream_start = pulse_stream_start,
   1700     .stream_stop = pulse_stream_stop,
   1701     .stream_get_position = pulse_stream_get_position,
   1702     .stream_get_latency = pulse_stream_get_latency,
   1703     .stream_get_input_latency = NULL,
   1704     .stream_set_volume = pulse_stream_set_volume,
   1705     .stream_set_name = pulse_stream_set_name,
   1706     .stream_get_current_device = pulse_stream_get_current_device,
   1707     .stream_set_input_mute = NULL,
   1708     .stream_set_input_processing_params = NULL,
   1709     .stream_device_destroy = pulse_stream_device_destroy,
   1710     .stream_register_device_changed_callback = NULL,
   1711     .register_device_collection_changed =
   1712         pulse_register_device_collection_changed};