]> code.delx.au - pulseaudio/blobdiff - src/modules/alsa/alsa-source.c
alsa: Use card description in default sink/source prefix when available
[pulseaudio] / src / modules / alsa / alsa-source.c
index d27cd9e7f708de7dcdc40073a15a80d6a99ef1f1..425f4f0f85f6a710eaedcd69a83f26ea70c4b57f 100644 (file)
@@ -118,13 +118,14 @@ struct userdata {
 
     pa_usec_t watermark_dec_not_before;
     pa_usec_t min_latency_ref;
+    pa_usec_t tsched_watermark_usec;
 
     char *device_name;  /* name of the PCM device */
     char *control_device; /* name of the control device */
 
-    pa_bool_t use_mmap:1, use_tsched:1, deferred_volume:1, fixed_latency_range:1;
+    bool use_mmap:1, use_tsched:1, deferred_volume:1, fixed_latency_range:1;
 
-    pa_bool_t first;
+    bool first;
 
     pa_rtpoll_item *alsa_rtpoll_item;
 
@@ -137,6 +138,9 @@ struct userdata {
     pa_hook_slot *reserve_slot;
     pa_reserve_monitor_wrapper *monitor;
     pa_hook_slot *monitor_slot;
+
+    /* ucm context */
+    pa_alsa_ucm_mapping_context *ucm_context;
 };
 
 static void userdata_free(struct userdata *u);
@@ -145,7 +149,9 @@ static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct u
     pa_assert(r);
     pa_assert(u);
 
-    if (pa_source_suspend(u->source, TRUE, PA_SUSPEND_APPLICATION) < 0)
+    pa_log_debug("Suspending source %s, because another application requested us to release the device.", u->source->name);
+
+    if (pa_source_suspend(u->source, true, PA_SUSPEND_APPLICATION) < 0)
         return PA_HOOK_CANCEL;
 
     return PA_HOOK_OK;
@@ -207,14 +213,17 @@ static int reserve_init(struct userdata *u, const char *dname) {
 }
 
 static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) {
-    pa_bool_t b;
-
     pa_assert(w);
     pa_assert(u);
 
-    b = PA_PTR_TO_UINT(busy) && !u->reserve;
+    if (PA_PTR_TO_UINT(busy) && !u->reserve) {
+        pa_log_debug("Suspending source %s, because another application is blocking the access to the device.", u->source->name);
+        pa_source_suspend(u->source, true, PA_SUSPEND_APPLICATION);
+    } else {
+        pa_log_debug("Resuming source %s, because other applications aren't blocking access to the device any more.", u->source->name);
+        pa_source_suspend(u->source, false, PA_SUSPEND_APPLICATION);
+    }
 
-    pa_source_suspend(u->source, b, PA_SUSPEND_APPLICATION);
     return PA_HOOK_OK;
 }
 
@@ -285,6 +294,8 @@ static void fix_tsched_watermark(struct userdata *u) {
 
     if (u->tsched_watermark < u->min_wakeup)
         u->tsched_watermark = u->min_wakeup;
+
+   u->tsched_watermark_usec = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec);
 }
 
 static void increase_watermark(struct userdata *u) {
@@ -301,7 +312,7 @@ static void increase_watermark(struct userdata *u) {
 
     if (old_watermark != u->tsched_watermark) {
         pa_log_info("Increasing wakeup watermark to %0.2f ms",
-                    (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
+                    (double) u->tsched_watermark_usec / PA_USEC_PER_MSEC);
         return;
     }
 
@@ -351,7 +362,7 @@ static void decrease_watermark(struct userdata *u) {
 
     if (old_watermark != u->tsched_watermark)
         pa_log_info("Decreasing wakeup watermark to %0.2f ms",
-                    (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
+                    (double) u->tsched_watermark_usec / PA_USEC_PER_MSEC);
 
     /* We don't change the latency range*/
 
@@ -373,7 +384,7 @@ static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*p
     if (usec == (pa_usec_t) -1)
         usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec);
 
-    wm = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec);
+    wm = u->tsched_watermark_usec;
 
     if (wm > usec)
         wm = usec/2;
@@ -409,14 +420,14 @@ static int try_recover(struct userdata *u, const char *call, int err) {
         return -1;
     }
 
-    u->first = TRUE;
+    u->first = true;
     return 0;
 }
 
-static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) {
+static size_t check_left_to_record(struct userdata *u, size_t n_bytes, bool on_timeout) {
     size_t left_to_record;
     size_t rec_space = u->hwbuf_size - u->hwbuf_unused;
-    pa_bool_t overrun = FALSE;
+    bool overrun = false;
 
     /* We use <= instead of < for this check here because an overrun
      * only happens after the last sample was processed, not already when
@@ -429,7 +440,7 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t
 
         /* We got a dropout. What a mess! */
         left_to_record = 0;
-        overrun = TRUE;
+        overrun = true;
 
 #ifdef DEBUG_TIMING
         PA_DEBUG_TRAP;
@@ -444,12 +455,12 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t
 #endif
 
     if (u->use_tsched) {
-        pa_bool_t reset_not_before = TRUE;
+        bool reset_not_before = true;
 
         if (overrun || left_to_record < u->watermark_inc_threshold)
             increase_watermark(u);
         else if (left_to_record > u->watermark_dec_threshold) {
-            reset_not_before = FALSE;
+            reset_not_before = false;
 
             /* We decrease the watermark only if have actually
              * been woken up by a timeout. If something else woke
@@ -466,8 +477,8 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t
     return left_to_record;
 }
 
-static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
-    pa_bool_t work_done = FALSE;
+static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, bool polled, bool on_timeout) {
+    bool work_done = false;
     pa_usec_t max_sleep_usec = 0, process_usec = 0;
     size_t left_to_record;
     unsigned j = 0;
@@ -482,7 +493,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
         snd_pcm_sframes_t n;
         size_t n_bytes;
         int r;
-        pa_bool_t after_avail = TRUE;
+        bool after_avail = true;
 
         if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
 
@@ -499,7 +510,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
 #endif
 
         left_to_record = check_left_to_record(u, n_bytes, on_timeout);
-        on_timeout = FALSE;
+        on_timeout = false;
 
         if (u->use_tsched)
             if (!polled &&
@@ -528,7 +539,6 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
             break;
         }
 
-
         if (++j > 10) {
 #ifdef DEBUG_TIMING
             pa_log_debug("Not filling up, because already too many iterations.");
@@ -537,7 +547,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
             break;
         }
 
-        polled = FALSE;
+        polled = false;
 
 #ifdef DEBUG_TIMING
         pa_log_debug("Reading");
@@ -573,7 +583,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
                 break;
 
             pa_assert(frames > 0);
-            after_avail = FALSE;
+            after_avail = false;
 
             /* Check these are multiples of 8 bit */
             pa_assert((areas[0].first & 7) == 0);
@@ -585,7 +595,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
 
             p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
 
-            chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
+            chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, true);
             chunk.length = pa_memblock_get_length(chunk.memblock);
             chunk.index = 0;
 
@@ -600,7 +610,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
                 return r;
             }
 
-            work_done = TRUE;
+            work_done = true;
 
             u->read_count += frames * u->frame_size;
 
@@ -617,7 +627,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
 
     if (u->use_tsched) {
         *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec);
-        process_usec = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec);
+        process_usec = u->tsched_watermark_usec;
 
         if (*sleep_usec > process_usec)
             *sleep_usec -= process_usec;
@@ -628,8 +638,8 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
     return work_done ? 1 : 0;
 }
 
-static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {
-    int work_done = FALSE;
+static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, bool polled, bool on_timeout) {
+    int work_done = false;
     pa_usec_t max_sleep_usec = 0, process_usec = 0;
     size_t left_to_record;
     unsigned j = 0;
@@ -644,7 +654,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
         snd_pcm_sframes_t n;
         size_t n_bytes;
         int r;
-        pa_bool_t after_avail = TRUE;
+        bool after_avail = true;
 
         if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
 
@@ -656,7 +666,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
 
         n_bytes = (size_t) n * u->frame_size;
         left_to_record = check_left_to_record(u, n_bytes, on_timeout);
-        on_timeout = FALSE;
+        on_timeout = false;
 
         if (u->use_tsched)
             if (!polled &&
@@ -686,7 +696,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
             break;
         }
 
-        polled = FALSE;
+        polled = false;
 
         for (;;) {
             void *p;
@@ -724,7 +734,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
             }
 
             pa_assert(frames > 0);
-            after_avail = FALSE;
+            after_avail = false;
 
             chunk.index = 0;
             chunk.length = (size_t) frames * u->frame_size;
@@ -732,7 +742,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
             pa_source_post(u->source, &chunk);
             pa_memblock_unref(chunk.memblock);
 
-            work_done = TRUE;
+            work_done = true;
 
             u->read_count += frames * u->frame_size;
 
@@ -747,7 +757,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
 
     if (u->use_tsched) {
         *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec);
-        process_usec = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec);
+        process_usec = u->tsched_watermark_usec;
 
         if (*sleep_usec > process_usec)
             *sleep_usec -= process_usec;
@@ -764,6 +774,7 @@ static void update_smoother(struct userdata *u) {
     int err;
     pa_usec_t now1 = 0, now2;
     snd_pcm_status_t *status;
+    snd_htimestamp_t htstamp = { 0, 0 };
 
     snd_pcm_status_alloca(&status);
 
@@ -772,18 +783,13 @@ static void update_smoother(struct userdata *u) {
 
     /* Let's update the time smoother */
 
-    if (PA_UNLIKELY((err = pa_alsa_safe_delay(u->pcm_handle, &delay, u->hwbuf_size, &u->source->sample_spec, TRUE)) < 0)) {
+    if (PA_UNLIKELY((err = pa_alsa_safe_delay(u->pcm_handle, status, &delay, u->hwbuf_size, &u->source->sample_spec, true)) < 0)) {
         pa_log_warn("Failed to get delay: %s", pa_alsa_strerror(err));
         return;
     }
 
-    if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0))
-        pa_log_warn("Failed to get timestamp: %s", pa_alsa_strerror(err));
-    else {
-        snd_htimestamp_t htstamp = { 0, 0 };
-        snd_pcm_status_get_htstamp(status, &htstamp);
-        now1 = pa_timespec_load(&htstamp);
-    }
+    snd_pcm_status_get_htstamp(status, &htstamp);
+    now1 = pa_timespec_load(&htstamp);
 
     /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */
     if (now1 <= 0)
@@ -907,8 +913,7 @@ static int update_sw_params(struct userdata *u) {
 
 /* Called from IO Context on unsuspend or from main thread when creating source */
 static void reset_watermark(struct userdata *u, size_t tsched_watermark, pa_sample_spec *ss,
-                            pa_bool_t in_thread)
-{
+                            bool in_thread) {
     u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, ss),
                                                     &u->source->sample_spec);
 
@@ -937,14 +942,14 @@ static void reset_watermark(struct userdata *u, size_t tsched_watermark, pa_samp
     }
 
     pa_log_info("Time scheduling watermark is %0.2fms",
-                (double) pa_bytes_to_usec(u->tsched_watermark, ss) / PA_USEC_PER_MSEC);
+                (double) u->tsched_watermark_usec / PA_USEC_PER_MSEC);
 }
 
 /* Called from IO context */
 static int unsuspend(struct userdata *u) {
     pa_sample_spec ss;
     int err;
-    pa_bool_t b, d;
+    bool b, d;
     snd_pcm_uframes_t period_size, buffer_size;
 
     pa_assert(u);
@@ -967,7 +972,7 @@ static int unsuspend(struct userdata *u) {
     b = u->use_mmap;
     d = u->use_tsched;
 
-    if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, TRUE)) < 0) {
+    if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, true)) < 0) {
         pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err));
         goto fail;
     }
@@ -999,15 +1004,15 @@ static int unsuspend(struct userdata *u) {
     /* FIXME: We need to reload the volume somehow */
 
     u->read_count = 0;
-    pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE);
+    pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
     u->smoother_interval = SMOOTHER_MIN_INTERVAL;
     u->last_smoother_update = 0;
 
-    u->first = TRUE;
+    u->first = true;
 
     /* reset the watermark to the value defined when source was created */
     if (u->use_tsched)
-        reset_watermark(u, u->tsched_watermark_ref, &u->source->sample_spec, TRUE);
+        reset_watermark(u, u->tsched_watermark_ref, &u->source->sample_spec, true);
 
     pa_log_info("Resumed successfully...");
 
@@ -1115,13 +1120,13 @@ static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
         return 0;
 
     if (u->source->suspend_cause & PA_SUSPEND_SESSION) {
-        pa_source_set_mixer_dirty(u->source, TRUE);
+        pa_source_set_mixer_dirty(u->source, true);
         return 0;
     }
 
     if (mask & SND_CTL_EVENT_MASK_VALUE) {
-        pa_source_get_volume(u->source, TRUE);
-        pa_source_get_mute(u->source, TRUE);
+        pa_source_get_volume(u->source, true);
+        pa_source_get_mute(u->source, true);
     }
 
     return 0;
@@ -1137,7 +1142,7 @@ static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
         return 0;
 
     if (u->source->suspend_cause & PA_SUSPEND_SESSION) {
-        pa_source_set_mixer_dirty(u->source, TRUE);
+        pa_source_set_mixer_dirty(u->source, true);
         return 0;
     }
 
@@ -1150,7 +1155,7 @@ static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
 static void source_get_volume_cb(pa_source *s) {
     struct userdata *u = s->userdata;
     pa_cvolume r;
-    char vol_str_pcnt[PA_CVOLUME_SNPRINT_MAX];
+    char volume_buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
 
     pa_assert(u);
     pa_assert(u->mixer_path);
@@ -1162,13 +1167,8 @@ static void source_get_volume_cb(pa_source *s) {
     /* Shift down by the base volume, so that 0dB becomes maximum volume */
     pa_sw_cvolume_multiply_scalar(&r, &r, s->base_volume);
 
-    pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(vol_str_pcnt, sizeof(vol_str_pcnt), &r));
-
-    if (u->mixer_path->has_dB) {
-        char vol_str_db[PA_SW_CVOLUME_SNPRINT_DB_MAX];
-
-        pa_log_debug("               in dB: %s", pa_sw_cvolume_snprint_dB(vol_str_db, sizeof(vol_str_db), &r));
-    }
+    pa_log_debug("Read hardware volume: %s",
+                 pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &r, &s->channel_map, u->mixer_path->has_dB));
 
     if (pa_cvolume_equal(&u->hardware_volume, &r))
         return;
@@ -1183,8 +1183,8 @@ static void source_get_volume_cb(pa_source *s) {
 static void source_set_volume_cb(pa_source *s) {
     struct userdata *u = s->userdata;
     pa_cvolume r;
-    char vol_str_pcnt[PA_CVOLUME_SNPRINT_MAX];
-    pa_bool_t deferred_volume = !!(s->flags & PA_SOURCE_DEFERRED_VOLUME);
+    char volume_buf[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+    bool deferred_volume = !!(s->flags & PA_SOURCE_DEFERRED_VOLUME);
 
     pa_assert(u);
     pa_assert(u->mixer_path);
@@ -1203,8 +1203,7 @@ static void source_set_volume_cb(pa_source *s) {
 
     if (u->mixer_path->has_dB) {
         pa_cvolume new_soft_volume;
-        pa_bool_t accurate_enough;
-        char vol_str_db[PA_SW_CVOLUME_SNPRINT_DB_MAX];
+        bool accurate_enough;
 
         /* Match exactly what the user requested by software */
         pa_sw_cvolume_divide(&new_soft_volume, &s->real_volume, &u->hardware_volume);
@@ -1216,20 +1215,20 @@ static void source_set_volume_cb(pa_source *s) {
             (pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) &&
             (pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
 
-        pa_log_debug("Requested volume: %s", pa_cvolume_snprint(vol_str_pcnt, sizeof(vol_str_pcnt), &s->real_volume));
-        pa_log_debug("           in dB: %s", pa_sw_cvolume_snprint_dB(vol_str_db, sizeof(vol_str_db), &s->real_volume));
-        pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(vol_str_pcnt, sizeof(vol_str_pcnt), &u->hardware_volume));
-        pa_log_debug("              in dB: %s", pa_sw_cvolume_snprint_dB(vol_str_db, sizeof(vol_str_db), &u->hardware_volume));
+        pa_log_debug("Requested volume: %s",
+                     pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &s->real_volume, &s->channel_map, true));
+        pa_log_debug("Got hardware volume: %s",
+                     pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &u->hardware_volume, &s->channel_map, true));
         pa_log_debug("Calculated software volume: %s (accurate-enough=%s)",
-                     pa_cvolume_snprint(vol_str_pcnt, sizeof(vol_str_pcnt), &new_soft_volume),
+                     pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &new_soft_volume, &s->channel_map, true),
                      pa_yes_no(accurate_enough));
-        pa_log_debug("                     in dB: %s", pa_sw_cvolume_snprint_dB(vol_str_db, sizeof(vol_str_db), &new_soft_volume));
 
         if (!accurate_enough)
             s->soft_volume = new_soft_volume;
 
     } else {
-        pa_log_debug("Wrote hardware volume: %s", pa_cvolume_snprint(vol_str_pcnt, sizeof(vol_str_pcnt), &r));
+        pa_log_debug("Wrote hardware volume: %s",
+                     pa_cvolume_snprint_verbose(volume_buf, sizeof(volume_buf), &r, &s->channel_map, false));
 
         /* We can't match exactly what the user requested, hence let's
          * at least tell the user about it */
@@ -1250,11 +1249,11 @@ static void source_write_volume_cb(pa_source *s) {
     /* Shift up by the base volume */
     pa_sw_cvolume_divide_scalar(&hw_vol, &hw_vol, s->base_volume);
 
-    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE, TRUE) < 0)
+    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, true, true) < 0)
         pa_log_error("Writing HW volume failed");
     else {
         pa_cvolume tmp_vol;
-        pa_bool_t accurate_enough;
+        bool accurate_enough;
 
         /* Shift down by the base volume, so that 0dB becomes maximum volume */
         pa_sw_cvolume_multiply_scalar(&hw_vol, &hw_vol, s->base_volume);
@@ -1265,24 +1264,22 @@ static void source_write_volume_cb(pa_source *s) {
             (pa_cvolume_max(&tmp_vol) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
 
         if (!accurate_enough) {
-            union {
-                char db[2][PA_SW_CVOLUME_SNPRINT_DB_MAX];
-                char pcnt[2][PA_CVOLUME_SNPRINT_MAX];
-            } vol;
+            char volume_buf[2][PA_CVOLUME_SNPRINT_VERBOSE_MAX];
 
             pa_log_debug("Written HW volume did not match with the request: %s (request) != %s",
-                         pa_cvolume_snprint(vol.pcnt[0], sizeof(vol.pcnt[0]), &s->thread_info.current_hw_volume),
-                         pa_cvolume_snprint(vol.pcnt[1], sizeof(vol.pcnt[1]), &hw_vol));
-            pa_log_debug("                                           in dB: %s (request) != %s",
-                         pa_sw_cvolume_snprint_dB(vol.db[0], sizeof(vol.db[0]), &s->thread_info.current_hw_volume),
-                         pa_sw_cvolume_snprint_dB(vol.db[1], sizeof(vol.db[1]), &hw_vol));
+                         pa_cvolume_snprint_verbose(volume_buf[0],
+                                                    sizeof(volume_buf[0]),
+                                                    &s->thread_info.current_hw_volume,
+                                                    &s->channel_map,
+                                                    true),
+                         pa_cvolume_snprint_verbose(volume_buf[1], sizeof(volume_buf[1]), &hw_vol, &s->channel_map, true));
         }
     }
 }
 
 static void source_get_mute_cb(pa_source *s) {
     struct userdata *u = s->userdata;
-    pa_bool_t b;
+    bool b;
 
     pa_assert(u);
     pa_assert(u->mixer_path);
@@ -1324,7 +1321,7 @@ static void mixer_volume_init(struct userdata *u) {
             pa_source_set_write_volume_callback(u->source, NULL);
 
         if (u->mixer_path->has_dB) {
-            pa_source_enable_decibel_volume(u->source, TRUE);
+            pa_source_enable_decibel_volume(u->source, true);
             pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB);
 
             u->source->base_volume = pa_sw_volume_from_dB(-u->mixer_path->max_dB);
@@ -1332,7 +1329,7 @@ static void mixer_volume_init(struct userdata *u) {
 
             pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->source->base_volume));
         } else {
-            pa_source_enable_decibel_volume(u->source, FALSE);
+            pa_source_enable_decibel_volume(u->source, false);
             pa_log_info("Hardware volume ranges from %li to %li.", u->mixer_path->min_volume, u->mixer_path->max_volume);
 
             u->source->base_volume = PA_VOLUME_NORM;
@@ -1353,6 +1350,16 @@ static void mixer_volume_init(struct userdata *u) {
     }
 }
 
+static int source_set_port_ucm_cb(pa_source *s, pa_device_port *p) {
+    struct userdata *u = s->userdata;
+
+    pa_assert(u);
+    pa_assert(p);
+    pa_assert(u->ucm_context);
+
+    return pa_alsa_ucm_set_port(u->ucm_context, p, false);
+}
+
 static int source_set_port_cb(pa_source *s, pa_device_port *p) {
     struct userdata *u = s->userdata;
     pa_alsa_port_data *data;
@@ -1394,33 +1401,32 @@ static void source_update_requested_latency_cb(pa_source *s) {
     update_sw_params(u);
 }
 
-static pa_bool_t source_update_rate_cb(pa_source *s, uint32_t rate)
-{
+static int source_update_rate_cb(pa_source *s, uint32_t rate) {
     struct userdata *u = s->userdata;
     int i;
-    pa_bool_t supported = FALSE;
+    bool supported = false;
 
     pa_assert(u);
 
     for (i = 0; u->rates[i]; i++) {
         if (u->rates[i] == rate) {
-            supported = TRUE;
+            supported = true;
             break;
         }
     }
 
     if (!supported) {
-        pa_log_info("Sink does not support sample rate of %d Hz", rate);
-        return FALSE;
+        pa_log_info("Source does not support sample rate of %d Hz", rate);
+        return -1;
     }
 
     if (!PA_SOURCE_IS_OPENED(s->state)) {
         pa_log_info("Updating rate for device %s, new rate is %d", u->device_name, rate);
         u->source->sample_spec.rate = rate;
-        return TRUE;
+        return 0;
     }
 
-    return FALSE;
+    return -1;
 }
 
 static void thread_func(void *userdata) {
@@ -1438,7 +1444,7 @@ static void thread_func(void *userdata) {
 
     for (;;) {
         int ret;
-        pa_usec_t rtpoll_sleep = 0;
+        pa_usec_t rtpoll_sleep = 0, real_sleep;
 
 #ifdef DEBUG_TIMING
         pa_log_debug("Loop");
@@ -1448,15 +1454,15 @@ static void thread_func(void *userdata) {
         if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
             int work_done;
             pa_usec_t sleep_usec = 0;
-            pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll);
+            bool on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll);
 
             if (u->first) {
                 pa_log_info("Starting capture.");
                 snd_pcm_start(u->pcm_handle);
 
-                pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE);
+                pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
 
-                u->first = FALSE;
+                u->first = false;
             }
 
             if (u->use_mmap)
@@ -1502,15 +1508,30 @@ static void thread_func(void *userdata) {
             }
         }
 
-        if (rtpoll_sleep > 0)
+        if (rtpoll_sleep > 0) {
             pa_rtpoll_set_timer_relative(u->rtpoll, rtpoll_sleep);
+            real_sleep = pa_rtclock_now();
+        }
         else
             pa_rtpoll_set_timer_disabled(u->rtpoll);
 
         /* Hmm, nothing to do. Let's sleep */
-        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+        if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0)
             goto fail;
 
+        if (rtpoll_sleep > 0) {
+            real_sleep = pa_rtclock_now() - real_sleep;
+#ifdef DEBUG_TIMING
+            pa_log_debug("Expected sleep: %0.2fms, real sleep: %0.2fms (diff %0.2f ms)",
+                (double) rtpoll_sleep / PA_USEC_PER_MSEC, (double) real_sleep / PA_USEC_PER_MSEC,
+                (double) ((int64_t) real_sleep - (int64_t) rtpoll_sleep) / PA_USEC_PER_MSEC);
+#endif
+            if (u->use_tsched && real_sleep > rtpoll_sleep + u->tsched_watermark_usec)
+                pa_log_info("Scheduling delay of %0.2f ms > %0.2f ms, you might want to investigate this to improve latency...",
+                    (double) (real_sleep - rtpoll_sleep) / PA_USEC_PER_MSEC,
+                    (double) (u->tsched_watermark_usec) / PA_USEC_PER_MSEC);
+        }
+
         if (u->source->flags & PA_SOURCE_DEFERRED_VOLUME)
             pa_source_volume_change_apply(u->source, NULL);
 
@@ -1534,7 +1555,7 @@ static void thread_func(void *userdata) {
                 if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
                     goto fail;
 
-                u->first = TRUE;
+                u->first = true;
                 revents = 0;
             } else if (revents && u->use_tsched && pa_log_ratelimit(PA_LOG_DEBUG))
                 pa_log_debug("Wakeup from ALSA!");
@@ -1563,15 +1584,15 @@ static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char
 
     if ((n = pa_modargs_get_value(ma, "source_name", NULL))) {
         pa_source_new_data_set_name(data, n);
-        data->namereg_fail = TRUE;
+        data->namereg_fail = true;
         return;
     }
 
     if ((n = pa_modargs_get_value(ma, "name", NULL)))
-        data->namereg_fail = TRUE;
+        data->namereg_fail = true;
     else {
         n = device_id ? device_id : device_name;
-        data->namereg_fail = FALSE;
+        data->namereg_fail = false;
     }
 
     if (mapping)
@@ -1583,7 +1604,7 @@ static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char
     pa_xfree(t);
 }
 
-static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char *element, pa_bool_t ignore_dB) {
+static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char *element, bool ignore_dB) {
     snd_hctl_t *hctl;
 
     if (!mapping && !element)
@@ -1622,8 +1643,8 @@ fail:
     }
 }
 
-static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
-    pa_bool_t need_mixer_callback = FALSE;
+static int setup_mixer(struct userdata *u, bool ignore_dB) {
+    bool need_mixer_callback = false;
 
     pa_assert(u);
 
@@ -1663,7 +1684,7 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
 
         PA_HASHMAP_FOREACH(p, u->mixer_path_set->paths, state) {
             if (p->has_volume || p->has_mute)
-                need_mixer_callback = TRUE;
+                need_mixer_callback = true;
         }
     }
     else if (u->mixer_path)
@@ -1701,22 +1722,39 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
 pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) {
 
     struct userdata *u = NULL;
-    const char *dev_id = NULL;
+    const char *dev_id = NULL, *key, *mod_name;
     pa_sample_spec ss;
+    char *thread_name = NULL;
     uint32_t alternate_sample_rate;
     pa_channel_map map;
     uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark;
     snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames;
     size_t frame_size;
-    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE, namereg_fail = FALSE, deferred_volume = FALSE, fixed_latency_range = FALSE;
+    bool use_mmap = true, b, use_tsched = true, d, ignore_dB = false, namereg_fail = false, deferred_volume = false, fixed_latency_range = false;
     pa_source_new_data data;
     pa_alsa_profile_set *profile_set = NULL;
+    void *state = NULL;
 
     pa_assert(m);
     pa_assert(ma);
 
     ss = m->core->default_sample_spec;
     map = m->core->default_channel_map;
+
+    /* Pick sample spec overrides from the mapping, if any */
+    if (mapping) {
+        if (mapping->sample_spec.format != PA_SAMPLE_INVALID)
+            ss.format = mapping->sample_spec.format;
+        if (mapping->sample_spec.rate != 0)
+            ss.rate = mapping->sample_spec.rate;
+        if (mapping->sample_spec.channels != 0) {
+            ss.channels = mapping->sample_spec.channels;
+            if (pa_channel_map_valid(&mapping->channel_map))
+                pa_assert(pa_channel_map_compatible(&mapping->channel_map, &ss));
+        }
+    }
+
+    /* Override with modargs if provided */
     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
         pa_log("Failed to parse sample specification and channel map");
         goto fail;
@@ -1786,20 +1824,24 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     u->use_tsched = use_tsched;
     u->deferred_volume = deferred_volume;
     u->fixed_latency_range = fixed_latency_range;
-    u->first = TRUE;
+    u->first = true;
     u->rtpoll = pa_rtpoll_new();
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
 
     u->smoother = pa_smoother_new(
             SMOOTHER_ADJUST_USEC,
             SMOOTHER_WINDOW_USEC,
-            TRUE,
-            TRUE,
+            true,
+            true,
             5,
             pa_rtclock_now(),
-            TRUE);
+            true);
     u->smoother_interval = SMOOTHER_MIN_INTERVAL;
 
+    /* use ucm */
+    if (mapping && mapping->ucm_context.ucm)
+        u->ucm_context = &mapping->ucm_context;
+
     dev_id = pa_modargs_get_value(
             ma, "device_id",
             pa_modargs_get_value(ma, "device", DEFAULT_DEVICE));
@@ -1822,6 +1864,13 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
             goto fail;
         }
 
+        if ((mod_name = pa_proplist_gets(mapping->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+            if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
+                pa_log("Failed to enable ucm modifier %s", mod_name);
+            else
+                pa_log_debug("Enabled ucm modifier %s", mod_name);
+        }
+
         if (!(u->pcm_handle = pa_alsa_open_by_device_id_mapping(
                       dev_id,
                       &u->device_name,
@@ -1853,7 +1902,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
                       &ss, &map,
                       SND_PCM_STREAM_CAPTURE,
                       &period_frames, &buffer_frames, tsched_frames,
-                      &b, &d, FALSE)))
+                      &b, &d, false)))
             goto fail;
     }
 
@@ -1870,12 +1919,12 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
 
     if (use_mmap && !b) {
         pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
-        u->use_mmap = use_mmap = FALSE;
+        u->use_mmap = use_mmap = false;
     }
 
     if (use_tsched && (!b || !d)) {
         pa_log_info("Cannot enable timer-based scheduling, falling back to sound IRQ scheduling.");
-        u->use_tsched = use_tsched = FALSE;
+        u->use_tsched = use_tsched = false;
     }
 
     if (u->use_mmap)
@@ -1887,7 +1936,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
             pa_log_info("Disabling latency range changes on overrun");
     }
 
-    u->rates = pa_alsa_get_supported_rates(u->pcm_handle);
+    u->rates = pa_alsa_get_supported_rates(u->pcm_handle, ss.rate);
     if (!u->rates) {
         pa_log_error("Failed to find any supported sample rates.");
         goto fail;
@@ -1896,7 +1945,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     /* ALSA might tweak the sample spec, so recalculate the frame size */
     frame_size = pa_frame_size(&ss);
 
-    find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
+    if (!u->ucm_context)
+        find_mixer(u, mapping, pa_modargs_get_value(ma, "control", NULL), ignore_dB);
 
     pa_source_new_data_init(&data);
     data.driver = driver;
@@ -1929,9 +1979,12 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     if (mapping) {
         pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, mapping->name);
         pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, mapping->description);
+
+        while ((key = pa_proplist_iterate(mapping->proplist, &state)))
+            pa_proplist_sets(data.proplist, key, pa_proplist_gets(mapping->proplist, key));
     }
 
-    pa_alsa_init_description(data.proplist);
+    pa_alsa_init_description(data.proplist, card);
 
     if (u->control_device)
         pa_alsa_init_proplist_ctl(data.proplist, u->control_device);
@@ -1942,7 +1995,9 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
         goto fail;
     }
 
-    if (u->mixer_path_set)
+    if (u->ucm_context)
+        pa_alsa_ucm_add_ports(&data.ports, data.proplist, u->ucm_context, false, card);
+    else if (u->mixer_path_set)
         pa_alsa_add_ports(&data, u->mixer_path_set, card);
 
     u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|(u->use_tsched ? PA_SOURCE_DYNAMIC_LATENCY : 0));
@@ -1969,7 +2024,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     if (u->use_tsched)
         u->source->update_requested_latency = source_update_requested_latency_cb;
     u->source->set_state = source_set_state_cb;
-    u->source->set_port = source_set_port_cb;
+    if (u->ucm_context)
+        u->source->set_port = source_set_port_ucm_cb;
+    else
+        u->source->set_port = source_set_port_cb;
     if (u->source->alternate_sample_rate)
         u->source->update_rate = source_update_rate_cb;
     u->source->userdata = u;
@@ -1991,7 +2049,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
 
     if (u->use_tsched) {
         u->tsched_watermark_ref = tsched_watermark;
-        reset_watermark(u, u->tsched_watermark_ref, &ss, FALSE);
+        reset_watermark(u, u->tsched_watermark_ref, &ss, false);
     }
     else
         pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->hwbuf_size, &ss));
@@ -2001,15 +2059,21 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     if (update_sw_params(u) < 0)
         goto fail;
 
-    if (setup_mixer(u, ignore_dB) < 0)
+    if (u->ucm_context) {
+        if (u->source->active_port && pa_alsa_ucm_set_port(u->ucm_context, u->source->active_port, false) < 0)
+            goto fail;
+    } else if (setup_mixer(u, ignore_dB) < 0)
         goto fail;
 
     pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
 
-    if (!(u->thread = pa_thread_new("alsa-source", thread_func, u))) {
+    thread_name = pa_sprintf_malloc("alsa-source-%s", pa_strnull(pa_proplist_gets(u->source->proplist, "alsa.id")));
+    if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) {
         pa_log("Failed to create thread.");
         goto fail;
     }
+    pa_xfree(thread_name);
+    thread_name = NULL;
 
     /* Get initial mixer settings */
     if (data.volume_is_set) {
@@ -2039,6 +2103,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     return u->source;
 
 fail:
+    pa_xfree(thread_name);
 
     if (u)
         userdata_free(u);