diff options
Diffstat (limited to 'src/modules/alsa')
-rw-r--r-- | src/modules/alsa/alsa-sink.c | 148 | ||||
-rw-r--r-- | src/modules/alsa/alsa-source.c | 143 | ||||
-rw-r--r-- | src/modules/alsa/alsa-util.c | 123 | ||||
-rw-r--r-- | src/modules/alsa/alsa-util.h | 5 | ||||
-rw-r--r-- | src/modules/alsa/module-alsa-card.c | 52 |
5 files changed, 435 insertions, 36 deletions
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 239a9d78..dbd95b63 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -53,6 +53,8 @@ #include <pulsecore/rtclock.h> #include <pulsecore/time-smoother.h> +#include <modules/reserve-wrap.h> + #include "alsa-util.h" #include "alsa-sink.h" @@ -101,10 +103,75 @@ struct userdata { pa_smoother *smoother; uint64_t write_count; uint64_t since_start; + + pa_reserve_wrapper *reserve; + pa_hook_slot *reserve_slot; }; static void userdata_free(struct userdata *u); +static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct userdata *u) { + pa_assert(r); + pa_assert(u); + + if (pa_sink_suspend(u->sink, TRUE) < 0) + return PA_HOOK_CANCEL; + + return PA_HOOK_OK; +} + +static void reserve_done(struct userdata *u) { + pa_assert(u); + + if (u->reserve_slot) { + pa_hook_slot_free(u->reserve_slot); + u->reserve_slot = NULL; + } + + if (u->reserve) { + pa_reserve_wrapper_unref(u->reserve); + u->reserve = NULL; + } +} + +static void reserve_update(struct userdata *u) { + const char *description; + pa_assert(u); + + if (!u->sink) + return; + + if ((description = pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION))) + pa_reserve_wrapper_set_application_device_name(u->reserve, description); +} + +static int reserve_init(struct userdata *u, const char *dname) { + char *rname; + + pa_assert(u); + pa_assert(dname); + + if (u->reserve) + return 0; + + /* We are resuming, try to lock the device */ + if (!(rname = pa_alsa_get_reserve_name(dname))) + return 0; + + u->reserve = pa_reserve_wrapper_get(u->core, rname); + pa_xfree(rname); + + if (!(u->reserve)) + return -1; + + reserve_update(u); + + pa_assert(!u->reserve_slot); + u->reserve_slot = pa_hook_connect(pa_reserve_wrapper_hook(u->reserve), PA_HOOK_NORMAL, (pa_hook_cb_t) reserve_cb, u); + + return 0; +} + static void fix_min_sleep_wakeup(struct userdata *u) { size_t max_use, max_use_2; @@ -255,6 +322,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle pa_bool_t work_done = TRUE; pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_play; + unsigned j = 0; pa_assert(u); pa_sink_assert_ref(u->sink); @@ -304,10 +372,15 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle if (PA_UNLIKELY(n_bytes <= u->hwbuf_unused)) { - if (polled && pa_log_ratelimit()) - pa_log(_("ALSA woke us up to write new data to the device, but there was actually nothing to write! " - "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. " - "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.")); + if (polled) + PA_ONCE_BEGIN { + char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle); + pa_log(_("ALSA woke us up to write new data to the device, but there was actually nothing to write!\n" + "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n" + "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."), + pa_strnull(dn)); + pa_xfree(dn); + } PA_ONCE_END; #ifdef DEBUG_TIMING pa_log_debug("Not filling up, because not necessary."); @@ -315,6 +388,15 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle break; } + + if (++j > 10) { +#ifdef DEBUG_TIMING + pa_log_debug("Not filling up, because already too many iterations."); +#endif + + break; + } + n_bytes -= u->hwbuf_unused; polled = FALSE; @@ -394,6 +476,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle pa_bool_t work_done = FALSE; pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_play; + unsigned j = 0; pa_assert(u); pa_sink_assert_ref(u->sink); @@ -431,10 +514,23 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle if (PA_UNLIKELY(n_bytes <= u->hwbuf_unused)) { - if (polled && pa_log_ratelimit()) - pa_log(_("ALSA woke us up to write new data to the device, but there was actually nothing to write! " - "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. " - "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.")); + if (polled) + PA_ONCE_BEGIN { + char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle); + pa_log(_("ALSA woke us up to write new data to the device, but there was actually nothing to write!\n" + "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n" + "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."), + pa_strnull(dn)); + pa_xfree(dn); + } PA_ONCE_END; + + break; + } + + if (++j > 10) { +#ifdef DEBUG_TIMING + pa_log_debug("Not filling up, because already too many iterations."); +#endif break; } @@ -512,7 +608,7 @@ static void update_smoother(struct userdata *u) { /* Let's update the time smoother */ - if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) { + if (PA_UNLIKELY((err = pa_alsa_safe_delay(u->pcm_handle, &delay, u->hwbuf_size, &u->sink->sample_spec)) < 0)) { pa_log_warn("Failed to query DSP status data: %s", snd_strerror(err)); return; } @@ -572,6 +668,7 @@ static int build_pollfd(struct userdata *u) { return 0; } +/* Called from IO context */ static int suspend(struct userdata *u) { pa_assert(u); pa_assert(u->pcm_handle); @@ -593,6 +690,7 @@ static int suspend(struct userdata *u) { return 0; } +/* Called from IO context */ static int update_sw_params(struct userdata *u) { snd_pcm_uframes_t avail_min; int err; @@ -648,6 +746,7 @@ static int update_sw_params(struct userdata *u) { return 0; } +/* Called from IO context */ static int unsuspend(struct userdata *u) { pa_sample_spec ss; int err; @@ -720,6 +819,7 @@ fail: return -1; } +/* Called from IO context */ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK(o)->userdata; @@ -775,6 +875,25 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse return pa_sink_process_msg(o, code, data, offset, chunk); } +/* Called from main context */ +static int sink_set_state_cb(pa_sink *s, pa_sink_state_t new_state) { + pa_sink_state_t old_state; + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + old_state = pa_sink_get_state(u->sink); + + if (PA_SINK_IS_OPENED(old_state) && new_state == PA_SINK_SUSPENDED) + reserve_done(u); + else if (old_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(new_state)) + if (reserve_init(u, u->device_name) < 0) + return -1; + + return 0; +} + static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { struct userdata *u = snd_mixer_elem_get_callback_private(elem); @@ -1378,6 +1497,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_assert(ma); ss = m->core->default_sample_spec; + map = m->core->default_channel_map; 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; @@ -1438,6 +1558,11 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_smoother_set_time_offset(u->smoother, usec); pa_smoother_pause(u->smoother, usec); + if (reserve_init(u, pa_modargs_get_value( + ma, "device_id", + pa_modargs_get_value(ma, "device", DEFAULT_DEVICE))) < 0) + goto fail; + b = use_mmap; d = use_tsched; @@ -1539,6 +1664,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca u->sink->parent.process_msg = sink_process_msg; u->sink->update_requested_latency = sink_update_requested_latency_cb; + u->sink->set_state = sink_set_state_cb; u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); @@ -1571,6 +1697,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_log_info("Time scheduling watermark is %0.2fms", (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC); + reserve_update(u); + if (update_sw_params(u) < 0) goto fail; @@ -1651,6 +1779,8 @@ static void userdata_free(struct userdata *u) { if (u->smoother) pa_smoother_free(u->smoother); + reserve_done(u); + pa_xfree(u->device_name); pa_xfree(u); } diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 50cdb310..39df4a91 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -54,6 +54,8 @@ #include <pulsecore/time-smoother.h> #include <pulsecore/rtclock.h> +#include <modules/reserve-wrap.h> + #include "alsa-util.h" #include "alsa-source.h" @@ -99,10 +101,75 @@ struct userdata { pa_smoother *smoother; uint64_t read_count; + + pa_reserve_wrapper *reserve; + pa_hook_slot *reserve_slot; }; static void userdata_free(struct userdata *u); +static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct userdata *u) { + pa_assert(r); + pa_assert(u); + + if (pa_source_suspend(u->source, TRUE) < 0) + return PA_HOOK_CANCEL; + + return PA_HOOK_OK; +} + +static void reserve_done(struct userdata *u) { + pa_assert(u); + + if (u->reserve_slot) { + pa_hook_slot_free(u->reserve_slot); + u->reserve_slot = NULL; + } + + if (u->reserve) { + pa_reserve_wrapper_unref(u->reserve); + u->reserve = NULL; + } +} + +static void reserve_update(struct userdata *u) { + const char *description; + pa_assert(u); + + if (!u->source) + return; + + if ((description = pa_proplist_gets(u->source->proplist, PA_PROP_DEVICE_DESCRIPTION))) + pa_reserve_wrapper_set_application_device_name(u->reserve, description); +} + +static int reserve_init(struct userdata *u, const char *dname) { + char *rname; + + pa_assert(u); + pa_assert(dname); + + if (u->reserve) + return 0; + + /* We are resuming, try to lock the device */ + if (!(rname = pa_alsa_get_reserve_name(dname))) + return 0; + + u->reserve = pa_reserve_wrapper_get(u->core, rname); + pa_xfree(rname); + + if (!(u->reserve)) + return -1; + + reserve_update(u); + + pa_assert(!u->reserve_slot); + u->reserve_slot = pa_hook_connect(pa_reserve_wrapper_hook(u->reserve), PA_HOOK_NORMAL, (pa_hook_cb_t) reserve_cb, u); + + return 0; +} + static void fix_min_sleep_wakeup(struct userdata *u) { size_t max_use, max_use_2; pa_assert(u); @@ -248,6 +315,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled pa_bool_t work_done = FALSE; pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_record; + unsigned j = 0; pa_assert(u); pa_source_assert_ref(u->source); @@ -287,10 +355,15 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled if (PA_UNLIKELY(n_bytes <= 0)) { - if (polled && pa_log_ratelimit()) - pa_log(_("ALSA woke us up to read new data from the device, but there was actually nothing to read! " - "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. " - "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.")); + if (polled) + PA_ONCE_BEGIN { + char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle); + pa_log(_("ALSA woke us up to read new data from the device, but there was actually nothing to read!\n" + "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n" + "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."), + pa_strnull(dn)); + pa_xfree(dn); + } PA_ONCE_END; #ifdef DEBUG_TIMING pa_log_debug("Not reading, because not necessary."); @@ -298,6 +371,14 @@ 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."); +#endif + + break; + } + polled = FALSE; #ifdef DEBUG_TIMING @@ -376,6 +457,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled int work_done = FALSE; pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_record; + unsigned j = 0; pa_assert(u); pa_source_assert_ref(u->source); @@ -406,10 +488,23 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled if (PA_UNLIKELY(n_bytes <= 0)) { - if (polled && pa_log_ratelimit()) - pa_log(_("ALSA woke us up to read new data from the device, but there was actually nothing to read! " - "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. " - "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.")); + if (polled) + PA_ONCE_BEGIN { + char *dn = pa_alsa_get_driver_name_by_pcm(u->pcm_handle); + pa_log(_("ALSA woke us up to read new data from the device, but there was actually nothing to read!\n" + "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers.\n" + "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail() returned 0 or another value < min_avail."), + pa_strnull(dn)); + pa_xfree(dn); + } PA_ONCE_END; + + break; + } + + if (++j > 10) { +#ifdef DEBUG_TIMING + pa_log_debug("Not filling up, because already too many iterations."); +#endif break; } @@ -482,7 +577,7 @@ static void update_smoother(struct userdata *u) { /* Let's update the time smoother */ - if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) { + if (PA_UNLIKELY((err = pa_alsa_safe_delay(u->pcm_handle, &delay, u->hwbuf_size, &u->source->sample_spec)) < 0)) { pa_log_warn("Failed to get delay: %s", snd_strerror(err)); return; } @@ -737,6 +832,25 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off return pa_source_process_msg(o, code, data, offset, chunk); } +/* Called from main context */ +static int source_set_state_cb(pa_source *s, pa_source_state_t new_state) { + pa_source_state_t old_state; + struct userdata *u; + + pa_source_assert_ref(s); + pa_assert_se(u = s->userdata); + + old_state = pa_source_get_state(u->source); + + if (PA_SINK_IS_OPENED(old_state) && new_state == PA_SINK_SUSPENDED) + reserve_done(u); + else if (old_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(new_state)) + if (reserve_init(u, u->device_name) < 0) + return -1; + + return 0; +} + static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { struct userdata *u = snd_mixer_elem_get_callback_private(elem); @@ -1229,6 +1343,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_assert(ma); ss = m->core->default_sample_spec; + map = m->core->default_channel_map; if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) { pa_log("Failed to parse sample specification"); goto fail; @@ -1287,6 +1402,11 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC*2, DEFAULT_TSCHED_WATERMARK_USEC*2, TRUE, 5); pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); + if (reserve_init(u, pa_modargs_get_value( + ma, "device_id", + pa_modargs_get_value(ma, "device", DEFAULT_DEVICE))) < 0) + goto fail; + b = use_mmap; d = use_tsched; @@ -1385,6 +1505,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p u->source->parent.process_msg = source_process_msg; u->source->update_requested_latency = source_update_requested_latency_cb; + u->source->set_state = source_set_state_cb; u->source->userdata = u; pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); @@ -1414,6 +1535,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_log_info("Time scheduling watermark is %0.2fms", (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC); + reserve_update(u); + if (update_sw_params(u) < 0) goto fail; @@ -1490,6 +1613,8 @@ static void userdata_free(struct userdata *u) { if (u->smoother) pa_smoother_free(u->smoother); + reserve_done(u); + pa_xfree(u->device_name); pa_xfree(u); } diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index d00a80f1..6740c069 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -39,6 +39,7 @@ #include <pulsecore/core-util.h> #include <pulsecore/atomic.h> #include <pulsecore/core-error.h> +#include <pulsecore/once.h> #include "alsa-util.h" @@ -1026,6 +1027,7 @@ snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const pa_assert(name); snd_mixer_selem_id_set_name(sid, name); + snd_mixer_selem_id_set_index(sid, 0); if ((elem = snd_mixer_find_selem(mixer, sid))) { @@ -1042,6 +1044,7 @@ snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const if (fallback) { snd_mixer_selem_id_set_name(sid, fallback); + snd_mixer_selem_id_set_index(sid, 0); if ((fallback_elem = snd_mixer_find_selem(mixer, sid))) { @@ -1084,7 +1087,6 @@ success: return elem; } - int pa_alsa_find_mixer_and_elem( snd_pcm_t *pcm, snd_mixer_t **_m, @@ -1446,9 +1448,9 @@ void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t * cn = pa_proplist_gets(p, "alsa.card_name"); } - if (cn && n) - pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, "%s - %s", cn, n); - else if (cn) + if (cn && n && !strstr(cn, n) && !strstr(n, cn)) + pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, "%s, %s", cn, n); + else if (cn && (!n || strstr(cn, n))) pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, cn); else if (n) pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n); @@ -1572,15 +1574,70 @@ snd_pcm_sframes_t pa_alsa_safe_avail(snd_pcm_t *pcm, size_t hwbuf_size, const pa k = (size_t) n * pa_frame_size(ss); - if (k >= hwbuf_size * 3 || - k >= pa_bytes_per_second(ss)*10) - pa_log(_("snd_pcm_avail_update() returned a value that is exceptionally large: %lu bytes (%lu ms). " - "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers."), - (unsigned long) k, (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC)); + if (k >= hwbuf_size * 5 || + k >= pa_bytes_per_second(ss)*10) { + + PA_ONCE_BEGIN { + char *dn = pa_alsa_get_driver_name_by_pcm(pcm); + pa_log(_("snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu ms).\n" + "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."), + (unsigned long) k, + (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC), + pa_strnull(dn)); + pa_xfree(dn); + } PA_ONCE_END; + + /* Mhmm, let's try not to fail completely */ + n = (snd_pcm_sframes_t) (hwbuf_size / pa_frame_size(ss)); + } return n; } +int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay, size_t hwbuf_size, const pa_sample_spec *ss) { + ssize_t k; + size_t abs_k; + int r; + + pa_assert(pcm); + pa_assert(delay); + pa_assert(hwbuf_size > 0); + pa_assert(ss); + + /* Some ALSA driver expose weird bugs, let's inform the user about + * what is going on */ + + if ((r = snd_pcm_delay(pcm, delay)) < 0) + return r; + + k = (ssize_t) *delay * (ssize_t) pa_frame_size(ss); + + abs_k = k >= 0 ? (size_t) k : (size_t) -k; + + if (abs_k >= hwbuf_size * 5 || + abs_k >= pa_bytes_per_second(ss)*10) { + + PA_ONCE_BEGIN { + char *dn = pa_alsa_get_driver_name_by_pcm(pcm); + pa_log(_("snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s%lu ms).\n" + "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."), + (signed long) k, + k < 0 ? "-" : "", + (unsigned long) (pa_bytes_to_usec(abs_k, ss) / PA_USEC_PER_MSEC), + pa_strnull(dn)); + pa_xfree(dn); + } PA_ONCE_END; + + /* Mhmm, let's try not to fail completely */ + if (k < 0) + *delay = -(snd_pcm_sframes_t) (hwbuf_size / pa_frame_size(ss)); + else + *delay = (snd_pcm_sframes_t) (hwbuf_size / pa_frame_size(ss)); + } + + return 0; +} + int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss) { int r; snd_pcm_uframes_t before; @@ -1606,9 +1663,15 @@ int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas k >= hwbuf_size * 3 || k >= pa_bytes_per_second(ss)*10) - pa_log(_("snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms). " - "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers."), - (unsigned long) k, (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC)); + PA_ONCE_BEGIN { + char *dn = pa_alsa_get_driver_name_by_pcm(pcm); + pa_log(_("snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms).\n" + "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."), + (unsigned long) k, + (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC), + pa_strnull(dn)); + pa_xfree(dn); + } PA_ONCE_END; return r; } @@ -1630,3 +1693,39 @@ char *pa_alsa_get_driver_name(int card) { return n; } + +char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm) { + int card; + + snd_pcm_info_t* info; + snd_pcm_info_alloca(&info); + + if (snd_pcm_info(pcm, info) < 0) + return NULL; + + if ((card = snd_pcm_info_get_card(info)) < 0) + return NULL; + + return pa_alsa_get_driver_name(card); +} + +char *pa_alsa_get_reserve_name(const char *device) { + const char *t; + int i; + + pa_assert(device); + + if ((t = strchr(device, ':'))) + device = t+1; + + if ((i = snd_card_get_index(device)) < 0) { + int32_t k; + + if (pa_atoi(device, &k) < 0) + return NULL; + + i = (int) k; + } + + return pa_sprintf_malloc("Audio%i", i); +} diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index 2d0f407e..899532e2 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -129,8 +129,13 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents); pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll); snd_pcm_sframes_t pa_alsa_safe_avail(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss); +int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay, size_t hwbuf_size, const pa_sample_spec *ss); int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss); char *pa_alsa_get_driver_name(int card); +char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm); + +char *pa_alsa_get_reserve_name(const char *device); + #endif diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index e517ddcc..fc6b886b 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -30,6 +30,8 @@ #include <pulsecore/modargs.h> #include <pulsecore/queue.h> +#include <modules/reserve-wrap.h> + #include "alsa-util.h" #include "alsa-sink.h" #include "alsa-source.h" @@ -88,6 +90,8 @@ struct userdata { pa_source *source; pa_modargs *modargs; + + pa_hashmap *profiles; }; struct profile_data { @@ -99,10 +103,11 @@ static void enumerate_cb( const pa_alsa_profile_info *source, void *userdata) { - pa_hashmap *profiles = (pa_hashmap *) userdata; + struct userdata *u = userdata; char *t, *n; pa_card_profile *p; struct profile_data *d; + unsigned bonus = 0; if (sink && source) { n = pa_sprintf_malloc("output-%s+input-%s", sink->name, source->name); @@ -116,6 +121,20 @@ static void enumerate_cb( t = pa_sprintf_malloc(_("Input %s"), _(source->description)); } + if (sink) { + if (pa_channel_map_equal(&sink->map, &u->core->default_channel_map)) + bonus += 50000; + else if (sink->map.channels == u->core->default_channel_map.channels) + bonus += 40000; + } + + if (source) { + if (pa_channel_map_equal(&source->map, &u->core->default_channel_map)) + bonus += 30000; + else if (source->map.channels == u->core->default_channel_map.channels) + bonus += 20000; + } + pa_log_info("Found output profile '%s'", t); p = pa_card_profile_new(n, t, sizeof(struct profile_data)); @@ -123,7 +142,11 @@ static void enumerate_cb( pa_xfree(t); pa_xfree(n); - p->priority = (sink ? sink->priority : 0)*100 + (source ? source->priority : 0); + p->priority = + (sink ? sink->priority : 0) * 100 + + (source ? source->priority : 0) + + bonus; + p->n_sinks = !!sink; p->n_sources = !!source; @@ -137,7 +160,7 @@ static void enumerate_cb( d->sink_profile = sink; d->source_profile = source; - pa_hashmap_put(profiles, p->name, p); + pa_hashmap_put(u->profiles, p->name, p); } static void add_disabled_profile(pa_hashmap *profiles) { @@ -252,11 +275,14 @@ static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *de pa_xfree(t); } -int pa__init(pa_module*m) { +int pa__init(pa_module *m) { pa_card_new_data data; pa_modargs *ma; int alsa_card_index; struct userdata *u; + char rname[32]; + pa_reserve_wrapper *reserve = NULL; + const char *description; pa_alsa_redirect_errors_inc(); snd_config_update_free_global(); @@ -282,6 +308,11 @@ int pa__init(pa_module*m) { goto fail; } + pa_snprintf(rname, sizeof(rname), "Audio%i", alsa_card_index); + + if (!(reserve = pa_reserve_wrapper_get(m->core, rname))) + goto fail; + pa_card_new_data_init(&data); data.driver = __FILE__; data.module = m; @@ -289,8 +320,12 @@ int pa__init(pa_module*m) { pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_id); set_card_name(&data, ma, u->device_id); - data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - if (pa_alsa_probe_profiles(u->device_id, &m->core->default_sample_spec, enumerate_cb, data.profiles) < 0) { + if (reserve) + if ((description = pa_proplist_gets(data.proplist, PA_PROP_DEVICE_DESCRIPTION))) + pa_reserve_wrapper_set_application_device_name(reserve, description); + + u->profiles = data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + if (pa_alsa_probe_profiles(u->device_id, &m->core->default_sample_spec, enumerate_cb, u) < 0) { pa_card_new_data_done(&data); goto fail; } @@ -314,11 +349,16 @@ int pa__init(pa_module*m) { init_profile(u); + pa_reserve_wrapper_unref(reserve); + return 0; fail: + if (reserve) + pa_reserve_wrapper_unref(reserve); pa__done(m); + return -1; } |