mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-31 22:25:33 -04:00
alsa: Take syncronized HW volume infra into use for alsa-sink
Signed-off-by: Jyri Sarha <jyri.sarha@nokia.com> Reviewed-by: Tanu Kaskinen <tanu.kaskinen@digia.com> Reviewd-by: Colin Guthrie <cguthrie@mandriva.org>
This commit is contained in:
parent
5391daf8df
commit
1bea955829
6 changed files with 246 additions and 25 deletions
|
|
@ -243,6 +243,95 @@ int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_hand
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct pa_alsa_mixer_pdata {
|
||||
pa_rtpoll *rtpoll;
|
||||
pa_rtpoll_item *poll_item;
|
||||
snd_mixer_t *mixer;
|
||||
};
|
||||
|
||||
|
||||
struct pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void) {
|
||||
struct pa_alsa_mixer_pdata *pd;
|
||||
|
||||
pd = pa_xnew0(struct pa_alsa_mixer_pdata, 1);
|
||||
|
||||
return pd;
|
||||
}
|
||||
|
||||
void pa_alsa_mixer_pdata_free(struct pa_alsa_mixer_pdata *pd) {
|
||||
pa_assert(pd);
|
||||
|
||||
if (pd->poll_item) {
|
||||
pa_rtpoll_item_free(pd->poll_item);
|
||||
}
|
||||
|
||||
pa_xfree(pd);
|
||||
}
|
||||
|
||||
static int rtpoll_work_cb(pa_rtpoll_item *i) {
|
||||
struct pa_alsa_mixer_pdata *pd;
|
||||
struct pollfd *p;
|
||||
unsigned n_fds;
|
||||
unsigned short revents = 0;
|
||||
int err;
|
||||
|
||||
pd = pa_rtpoll_item_get_userdata(i);
|
||||
pa_assert_fp(pd);
|
||||
pa_assert_fp(i == pd->poll_item);
|
||||
|
||||
p = pa_rtpoll_item_get_pollfd(i, &n_fds);
|
||||
|
||||
if ((err = snd_mixer_poll_descriptors_revents(pd->mixer, p, n_fds, &revents)) < 0) {
|
||||
pa_log_error("Unable to get poll revent: %s", pa_alsa_strerror(err));
|
||||
pa_rtpoll_item_free(i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (revents) {
|
||||
snd_mixer_handle_events(pd->mixer);
|
||||
pa_rtpoll_item_free(i);
|
||||
pa_alsa_set_mixer_rtpoll(pd, pd->mixer, pd->rtpoll);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp) {
|
||||
pa_rtpoll_item *i;
|
||||
struct pollfd *p;
|
||||
int err, n;
|
||||
|
||||
pa_assert(pd);
|
||||
pa_assert(mixer);
|
||||
pa_assert(rtp);
|
||||
|
||||
if ((n = snd_mixer_poll_descriptors_count(mixer)) < 0) {
|
||||
pa_log("snd_mixer_poll_descriptors_count() failed: %s", pa_alsa_strerror(n));
|
||||
return -1;
|
||||
}
|
||||
|
||||
i = pa_rtpoll_item_new(rtp, PA_RTPOLL_LATE, (unsigned) n);
|
||||
|
||||
p = pa_rtpoll_item_get_pollfd(i, NULL);
|
||||
|
||||
memset(p, 0, sizeof(struct pollfd) * n);
|
||||
|
||||
if ((err = snd_mixer_poll_descriptors(mixer, p, (unsigned) n)) < 0) {
|
||||
pa_log_error("Unable to get poll descriptors: %s", pa_alsa_strerror(err));
|
||||
pa_rtpoll_item_free(i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pd->rtpoll = rtp;
|
||||
pd->poll_item = i;
|
||||
pd->mixer = mixer;
|
||||
|
||||
pa_rtpoll_item_set_userdata(i, pd);
|
||||
pa_rtpoll_item_set_work_callback(i, rtpoll_work_cb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepare_mixer(snd_mixer_t *mixer, const char *dev) {
|
||||
int err;
|
||||
|
||||
|
|
@ -671,7 +760,8 @@ int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
|
||||
static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw) {
|
||||
|
||||
snd_mixer_selem_id_t *sid;
|
||||
pa_cvolume rv;
|
||||
snd_mixer_elem_t *me;
|
||||
|
|
@ -720,14 +810,26 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
|
|||
* if the channel is available, ALSA behaves ver
|
||||
* strangely and doesn't fail the call */
|
||||
if (snd_mixer_selem_has_playback_channel(me, c)) {
|
||||
if ((r = snd_mixer_selem_set_playback_dB(me, c, value, +1)) >= 0)
|
||||
r = snd_mixer_selem_get_playback_dB(me, c, &value);
|
||||
if (write_to_hw) {
|
||||
if ((r = snd_mixer_selem_set_playback_dB(me, c, value, +1)) >= 0)
|
||||
r = snd_mixer_selem_get_playback_dB(me, c, &value);
|
||||
} else {
|
||||
long alsa_val;
|
||||
if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, +1, &alsa_val)) >= 0)
|
||||
r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value);
|
||||
}
|
||||
} else
|
||||
r = -1;
|
||||
} else {
|
||||
if (snd_mixer_selem_has_capture_channel(me, c)) {
|
||||
if ((r = snd_mixer_selem_set_capture_dB(me, c, value, +1)) >= 0)
|
||||
r = snd_mixer_selem_get_capture_dB(me, c, &value);
|
||||
if (write_to_hw) {
|
||||
if ((r = snd_mixer_selem_set_capture_dB(me, c, value, +1)) >= 0)
|
||||
r = snd_mixer_selem_get_capture_dB(me, c, &value);
|
||||
} else {
|
||||
long alsa_val;
|
||||
if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, +1, &alsa_val)) >= 0)
|
||||
r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value);
|
||||
}
|
||||
} else
|
||||
r = -1;
|
||||
}
|
||||
|
|
@ -782,7 +884,8 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
|
|||
return 0;
|
||||
}
|
||||
|
||||
int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v) {
|
||||
int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw) {
|
||||
|
||||
pa_alsa_element *e;
|
||||
pa_cvolume rv;
|
||||
|
||||
|
|
@ -807,7 +910,7 @@ int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_ma
|
|||
pa_assert(!p->has_dB || e->has_dB);
|
||||
|
||||
ev = rv;
|
||||
if (element_set_volume(e, m, cm, &ev) < 0)
|
||||
if (element_set_volume(e, m, cm, &ev, write_to_hw) < 0)
|
||||
return -1;
|
||||
|
||||
if (!p->has_dB) {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include <pulsecore/log.h>
|
||||
|
||||
typedef struct pa_alsa_fdlist pa_alsa_fdlist;
|
||||
typedef struct pa_alsa_mixer_pdata pa_alsa_mixer_pdata;
|
||||
typedef struct pa_alsa_setting pa_alsa_setting;
|
||||
typedef struct pa_alsa_option pa_alsa_option;
|
||||
typedef struct pa_alsa_element pa_alsa_element;
|
||||
|
|
@ -202,7 +203,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB);
|
|||
void pa_alsa_path_dump(pa_alsa_path *p);
|
||||
int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v);
|
||||
int pa_alsa_path_get_mute(pa_alsa_path *path, snd_mixer_t *m, pa_bool_t *muted);
|
||||
int pa_alsa_path_set_volume(pa_alsa_path *path, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v);
|
||||
int pa_alsa_path_set_volume(pa_alsa_path *path, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw);
|
||||
int pa_alsa_path_set_mute(pa_alsa_path *path, snd_mixer_t *m, pa_bool_t muted);
|
||||
int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m);
|
||||
void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata);
|
||||
|
|
@ -279,6 +280,12 @@ pa_alsa_fdlist *pa_alsa_fdlist_new(void);
|
|||
void pa_alsa_fdlist_free(pa_alsa_fdlist *fdl);
|
||||
int pa_alsa_fdlist_set_mixer(pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m);
|
||||
|
||||
/* Alternative for handling alsa mixer events in io-thread. */
|
||||
|
||||
pa_alsa_mixer_pdata *pa_alsa_mixer_pdata_new(void);
|
||||
void pa_alsa_mixer_pdata_free(pa_alsa_mixer_pdata *pd);
|
||||
int pa_alsa_set_mixer_rtpoll(struct pa_alsa_mixer_pdata *pd, snd_mixer_t *mixer, pa_rtpoll *rtp);
|
||||
|
||||
/* Data structure for inclusion in pa_device_port for alsa
|
||||
* sinks/sources. This contains nothing that needs to be freed
|
||||
* individually */
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ struct userdata {
|
|||
snd_pcm_t *pcm_handle;
|
||||
|
||||
pa_alsa_fdlist *mixer_fdl;
|
||||
pa_alsa_mixer_pdata *mixer_pd;
|
||||
snd_mixer_t *mixer_handle;
|
||||
pa_alsa_path_set *mixer_path_set;
|
||||
pa_alsa_path *mixer_path;
|
||||
|
|
@ -1124,7 +1125,7 @@ static int sink_set_state_cb(pa_sink *s, pa_sink_state_t new_state) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
|
||||
static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
|
||||
struct userdata *u = snd_mixer_elem_get_callback_private(elem);
|
||||
|
||||
pa_assert(u);
|
||||
|
|
@ -1144,6 +1145,24 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
|
||||
struct userdata *u = snd_mixer_elem_get_callback_private(elem);
|
||||
|
||||
pa_assert(u);
|
||||
pa_assert(u->mixer_handle);
|
||||
|
||||
if (mask == SND_CTL_EVENT_MASK_REMOVE)
|
||||
return 0;
|
||||
|
||||
if (u->sink->suspend_cause & PA_SUSPEND_SESSION)
|
||||
return 0;
|
||||
|
||||
if (mask & SND_CTL_EVENT_MASK_VALUE)
|
||||
pa_sink_update_volume_and_mute(u->sink);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sink_get_volume_cb(pa_sink *s) {
|
||||
struct userdata *u = s->userdata;
|
||||
pa_cvolume r;
|
||||
|
|
@ -1175,6 +1194,7 @@ static void sink_set_volume_cb(pa_sink *s) {
|
|||
struct userdata *u = s->userdata;
|
||||
pa_cvolume r;
|
||||
char t[PA_CVOLUME_SNPRINT_MAX];
|
||||
pa_bool_t write_to_hw = (s->flags & PA_SINK_SYNC_VOLUME) ? FALSE : TRUE;
|
||||
|
||||
pa_assert(u);
|
||||
pa_assert(u->mixer_path);
|
||||
|
|
@ -1183,7 +1203,7 @@ static void sink_set_volume_cb(pa_sink *s) {
|
|||
/* Shift up by the base volume */
|
||||
pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume);
|
||||
|
||||
if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0)
|
||||
if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, write_to_hw) < 0)
|
||||
return;
|
||||
|
||||
/* Shift down by the base volume, so that 0dB becomes maximum volume */
|
||||
|
|
@ -1223,6 +1243,33 @@ static void sink_set_volume_cb(pa_sink *s) {
|
|||
}
|
||||
}
|
||||
|
||||
static void sink_write_volume_cb(pa_sink *s) {
|
||||
struct userdata *u = s->userdata;
|
||||
pa_cvolume hw_vol = s->thread_info.current_hw_volume;
|
||||
|
||||
pa_assert(u);
|
||||
pa_assert(u->mixer_path);
|
||||
pa_assert(u->mixer_handle);
|
||||
pa_assert(s->flags & PA_SINK_SYNC_VOLUME);
|
||||
|
||||
if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE) < 0)
|
||||
pa_log_error("Writing HW volume failed");
|
||||
else {
|
||||
pa_cvolume tmp_vol;
|
||||
pa_bool_t accurate_enough;
|
||||
pa_sw_cvolume_divide(&tmp_vol, &hw_vol, &s->thread_info.current_hw_volume);
|
||||
accurate_enough =
|
||||
(pa_cvolume_min(&tmp_vol) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) &&
|
||||
(pa_cvolume_max(&tmp_vol) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
|
||||
if (!accurate_enough) {
|
||||
char t[PA_CVOLUME_SNPRINT_MAX];
|
||||
pa_log_debug("Written HW volume did not match with the request %s != %s",
|
||||
pa_cvolume_snprint(t, sizeof(t), &s->thread_info.current_hw_volume),
|
||||
pa_cvolume_snprint(t, sizeof(t), &hw_vol));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sink_get_mute_cb(pa_sink *s) {
|
||||
struct userdata *u = s->userdata;
|
||||
pa_bool_t b;
|
||||
|
|
@ -1385,6 +1432,7 @@ static void thread_func(void *userdata) {
|
|||
|
||||
for (;;) {
|
||||
int ret;
|
||||
pa_usec_t rtpoll_sleep = 0;
|
||||
|
||||
#ifdef DEBUG_TIMING
|
||||
pa_log_debug("Loop");
|
||||
|
|
@ -1453,20 +1501,32 @@ static void thread_func(void *userdata) {
|
|||
/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
|
||||
|
||||
/* We don't trust the conversion, so we wake up whatever comes first */
|
||||
pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec));
|
||||
rtpoll_sleep = PA_MIN(sleep_usec, cusec);
|
||||
}
|
||||
|
||||
u->after_rewind = FALSE;
|
||||
|
||||
} else if (u->use_tsched)
|
||||
}
|
||||
|
||||
/* OK, we're in an invalid state, let's disable our timers */
|
||||
if (u->sink->flags & PA_SINK_SYNC_VOLUME) {
|
||||
pa_usec_t volume_sleep;
|
||||
pa_sink_volume_change_apply(u->sink, &volume_sleep);
|
||||
if (volume_sleep > 0)
|
||||
rtpoll_sleep = MIN(volume_sleep, rtpoll_sleep);
|
||||
}
|
||||
|
||||
if (rtpoll_sleep > 0)
|
||||
pa_rtpoll_set_timer_relative(u->rtpoll, rtpoll_sleep);
|
||||
else
|
||||
pa_rtpoll_set_timer_disabled(u->rtpoll);
|
||||
|
||||
/* Hmm, nothing to do. Let's sleep */
|
||||
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
|
||||
goto fail;
|
||||
|
||||
if (u->sink->flags & PA_SINK_SYNC_VOLUME)
|
||||
pa_sink_volume_change_apply(u->sink, NULL);
|
||||
|
||||
if (ret == 0)
|
||||
goto finish;
|
||||
|
||||
|
|
@ -1585,7 +1645,9 @@ fail:
|
|||
}
|
||||
}
|
||||
|
||||
static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
|
||||
static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_volume) {
|
||||
int (*mixer_callback)(snd_mixer_elem_t *, unsigned int);
|
||||
|
||||
pa_assert(u);
|
||||
|
||||
if (!u->mixer_handle)
|
||||
|
|
@ -1651,8 +1713,17 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
|
|||
|
||||
u->sink->get_volume = sink_get_volume_cb;
|
||||
u->sink->set_volume = sink_set_volume_cb;
|
||||
u->sink->write_volume = sink_write_volume_cb;
|
||||
|
||||
u->sink->flags |= PA_SINK_HW_VOLUME_CTRL;
|
||||
if (u->mixer_path->has_dB) {
|
||||
u->sink->flags |= PA_SINK_DECIBEL_VOLUME;
|
||||
if (sync_volume) {
|
||||
u->sink->flags |= PA_SINK_SYNC_VOLUME;
|
||||
pa_log_info("Successfully enabled synchronous volume.");
|
||||
}
|
||||
}
|
||||
|
||||
u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->mixer_path->has_dB ? PA_SINK_DECIBEL_VOLUME : 0);
|
||||
pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported");
|
||||
}
|
||||
|
||||
|
|
@ -1665,11 +1736,23 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
|
|||
pa_log_info("Using hardware mute control.");
|
||||
}
|
||||
|
||||
u->mixer_fdl = pa_alsa_fdlist_new();
|
||||
if (sync_volume) {
|
||||
u->mixer_pd = pa_alsa_mixer_pdata_new();
|
||||
mixer_callback = io_mixer_callback;
|
||||
|
||||
if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, u->core->mainloop) < 0) {
|
||||
pa_log("Failed to initialize file descriptor monitoring");
|
||||
return -1;
|
||||
if (pa_alsa_set_mixer_rtpoll(u->mixer_pd, u->mixer_handle, u->rtpoll) < 0) {
|
||||
pa_log("Failed to initialize file descriptor monitoring");
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
u->mixer_fdl = pa_alsa_fdlist_new();
|
||||
mixer_callback = ctl_mixer_callback;
|
||||
|
||||
if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, u->core->mainloop) < 0) {
|
||||
pa_log("Failed to initialize file descriptor monitoring");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (u->mixer_path_set)
|
||||
|
|
@ -1689,7 +1772,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
|
|||
uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark, rewind_safeguard;
|
||||
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;
|
||||
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE, namereg_fail = FALSE, sync_volume = FALSE;
|
||||
pa_sink_new_data data;
|
||||
pa_alsa_profile_set *profile_set = NULL;
|
||||
|
||||
|
|
@ -1748,6 +1831,11 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (pa_modargs_get_value_boolean(ma, "sync_volume", &sync_volume) < 0) {
|
||||
pa_log("Failed to parse sync_volume argument.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
use_tsched = pa_alsa_may_tsched(use_tsched);
|
||||
|
||||
u = pa_xnew0(struct userdata, 1);
|
||||
|
|
@ -1913,6 +2001,18 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (pa_modargs_get_value_u32(ma, "sync_volume_safety_margin",
|
||||
&u->sink->thread_info.volume_change_safety_margin) < 0) {
|
||||
pa_log("Failed to parse sync_volume_safety_margin parameter");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pa_modargs_get_value_s32(ma, "sync_volume_extra_delay",
|
||||
&u->sink->thread_info.volume_change_extra_delay) < 0) {
|
||||
pa_log("Failed to parse sync_volume_extra_delay parameter");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
u->sink->parent.process_msg = sink_process_msg;
|
||||
if (u->use_tsched)
|
||||
u->sink->update_requested_latency = sink_update_requested_latency_cb;
|
||||
|
|
@ -1969,7 +2069,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
|
|||
if (update_sw_params(u) < 0)
|
||||
goto fail;
|
||||
|
||||
if (setup_mixer(u, ignore_dB) < 0)
|
||||
if (setup_mixer(u, ignore_dB, sync_volume) < 0)
|
||||
goto fail;
|
||||
|
||||
pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
|
||||
|
|
@ -2033,6 +2133,9 @@ static void userdata_free(struct userdata *u) {
|
|||
if (u->memchunk.memblock)
|
||||
pa_memblock_unref(u->memchunk.memblock);
|
||||
|
||||
if (u->mixer_pd)
|
||||
pa_alsa_mixer_pdata_free(u->mixer_pd);
|
||||
|
||||
if (u->alsa_rtpoll_item)
|
||||
pa_rtpoll_item_free(u->alsa_rtpoll_item);
|
||||
|
||||
|
|
|
|||
|
|
@ -1108,7 +1108,7 @@ static void source_set_volume_cb(pa_source *s) {
|
|||
/* Shift up by the base volume */
|
||||
pa_sw_cvolume_divide_scalar(&r, &s->volume, s->base_volume);
|
||||
|
||||
if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0)
|
||||
if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, TRUE) < 0)
|
||||
return;
|
||||
|
||||
/* Shift down by the base volume, so that 0dB becomes maximum volume */
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ PA_MODULE_USAGE(
|
|||
"tsched_buffer_size=<buffer size when using timer based scheduling> "
|
||||
"tsched_buffer_watermark=<lower fill watermark> "
|
||||
"profile=<profile name> "
|
||||
"ignore_dB=<ignore dB information from the device?>");
|
||||
"ignore_dB=<ignore dB information from the device?> "
|
||||
"sync_volume=<syncronize sw and hw voluchanges in IO-thread?>");
|
||||
|
||||
static const char* const valid_modargs[] = {
|
||||
"name",
|
||||
|
|
@ -86,6 +87,7 @@ static const char* const valid_modargs[] = {
|
|||
"tsched_buffer_watermark",
|
||||
"profile",
|
||||
"ignore_dB",
|
||||
"sync_volume",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -54,8 +54,11 @@ PA_MODULE_USAGE(
|
|||
"tsched_buffer_size=<buffer size when using timer based scheduling> "
|
||||
"tsched_buffer_watermark=<lower fill watermark> "
|
||||
"ignore_dB=<ignore dB information from the device?> "
|
||||
"control=<name of mixer control>"
|
||||
"rewind_safeguard=<number of bytes that cannot be rewound");
|
||||
"control=<name of mixer control> "
|
||||
"rewind_safeguard=<number of bytes that cannot be rewound> "
|
||||
"sync_volume=<syncronize sw and hw voluchanges in IO-thread?> "
|
||||
"sync_volume_safety_margin=<usec adjustment depending on volume direction> "
|
||||
"sync_volume_extra_delay=<usec adjustment to HW volume changes>");
|
||||
|
||||
static const char* const valid_modargs[] = {
|
||||
"name",
|
||||
|
|
@ -76,6 +79,9 @@ static const char* const valid_modargs[] = {
|
|||
"ignore_dB",
|
||||
"control",
|
||||
"rewind_safeguard",
|
||||
"sync_volume",
|
||||
"sync_volume_safety_margin",
|
||||
"sync_volume_extra_delay",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue