diff options
Diffstat (limited to 'src/modules')
-rw-r--r-- | src/modules/alsa-util.c | 84 | ||||
-rw-r--r-- | src/modules/alsa-util.h | 6 | ||||
-rw-r--r-- | src/modules/dbus-util.c | 2 | ||||
-rw-r--r-- | src/modules/module-alsa-sink.c | 268 | ||||
-rw-r--r-- | src/modules/module-alsa-source.c | 260 | ||||
-rw-r--r-- | src/modules/module-device-restore.c | 8 | ||||
-rw-r--r-- | src/modules/module-esound-sink.c | 4 | ||||
-rw-r--r-- | src/modules/module-hal-detect.c | 2 | ||||
-rw-r--r-- | src/modules/module-lirc.c | 6 | ||||
-rw-r--r-- | src/modules/module-mmkbd-evdev.c | 6 | ||||
-rw-r--r-- | src/modules/module-native-protocol-fd.c | 13 | ||||
-rw-r--r-- | src/modules/module-protocol-stub.c | 7 | ||||
-rw-r--r-- | src/modules/module-stream-restore.c | 42 | ||||
-rw-r--r-- | src/modules/rtp/module-rtp-send.c | 23 |
14 files changed, 438 insertions, 293 deletions
diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index 8abf834d..e3e8c85c 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -56,7 +56,7 @@ struct pa_alsa_fdlist { void *userdata; }; -static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void *userdata) { +static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { struct pa_alsa_fdlist *fdl = userdata; int err, i; @@ -102,7 +102,7 @@ static void io_cb(pa_mainloop_api*a, pa_io_event* e, PA_GCC_UNUSED int fd, pa_io snd_mixer_handle_events(fdl->mixer); } -static void defer_cb(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event* e, void *userdata) { +static void defer_cb(pa_mainloop_api*a, pa_defer_event* e, void *userdata) { struct pa_alsa_fdlist *fdl = userdata; int num_fds, i, err; struct pollfd *temp; @@ -808,7 +808,7 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel if (channel_map->channels > 1 && ((playback && snd_mixer_selem_has_playback_volume_joined(elem)) || (!playback && snd_mixer_selem_has_capture_volume_joined(elem)))) { - pa_log_info("ALSA device lacks independant volume controls for each channel, falling back to software volume control."); + pa_log_info("ALSA device lacks independant volume controls for each channel."); return -1; } @@ -820,7 +820,7 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel id = alsa_channel_ids[channel_map->map[i]]; if (!is_mono && id == SND_MIXER_SCHN_UNKNOWN) { - pa_log_info("Configured channel map contains channel '%s' that is unknown to the ALSA mixer. Falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i])); + pa_log_info("Configured channel map contains channel '%s' that is unknown to the ALSA mixer.", pa_channel_position_to_string(channel_map->map[i])); return -1; } @@ -832,7 +832,7 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel if ((playback && (!snd_mixer_selem_has_playback_channel(elem, id) || (is_mono && !snd_mixer_selem_is_playback_mono(elem)))) || (!playback && (!snd_mixer_selem_has_capture_channel(elem, id) || (is_mono && !snd_mixer_selem_is_capture_mono(elem))))) { - pa_log_info("ALSA device lacks separate volumes control for channel '%s', falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i])); + pa_log_info("ALSA device lacks separate volumes control for channel '%s'", pa_channel_position_to_string(channel_map->map[i])); return -1; } @@ -850,56 +850,6 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel return 0; } -void pa_alsa_0dB_playback(snd_mixer_elem_t *elem) { - long min, max, v; - - pa_assert(elem); - - /* Try to enable 0 dB if possible. If ALSA cannot do dB, then use - * raw volume levels and fix them to 75% */ - - if (snd_mixer_selem_set_playback_dB_all(elem, 0, -1) >= 0) - return; - - if (snd_mixer_selem_set_playback_dB_all(elem, 0, 1) >= 0) - return; - - if (snd_mixer_selem_get_playback_volume_range(elem, &min, &max) < 0) - return; - - v = min + ((max - min) * 3) / 4; /* 75% */ - - if (v <= min) - v = max; - - snd_mixer_selem_set_playback_volume_all(elem, v); -} - -void pa_alsa_0dB_capture(snd_mixer_elem_t *elem) { - long min, max, v; - - pa_assert(elem); - - /* Try to enable 0 dB if possible. If ALSA cannot do dB, then use - * raw volume levels and fix them to 75% */ - - if (snd_mixer_selem_set_capture_dB_all(elem, 0, -1) >= 0) - return; - - if (snd_mixer_selem_set_capture_dB_all(elem, 0, 1) >= 0) - return; - - if (snd_mixer_selem_get_capture_volume_range(elem, &min, &max) < 0) - return; - - v = min + ((max - min) * 3) / 4; /* 75% */ - - if (v <= min) - v = max; - - snd_mixer_selem_set_capture_volume_all(elem, v); -} - void pa_alsa_dump(snd_pcm_t *pcm) { int err; snd_output_t *out; @@ -1117,3 +1067,27 @@ pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) { return item; } + +pa_cvolume *pa_alsa_volume_divide(pa_cvolume *r, const pa_cvolume *t) { + unsigned i; + + pa_assert(r); + pa_assert(t); + pa_assert(r->channels == t->channels); + + for (i = 0; i < r->channels; i++) { + double a, b, c; + + a = pa_sw_volume_to_linear(r->values[i]); /* the hw volume */ + b = pa_sw_volume_to_linear(t->values[i]); /* the intended volume */ + + if (a <= 0) + c = 0; + else + c = b / a; + + r->values[i] = pa_sw_volume_from_linear(c); + } + + return r; +} diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h index 4de8bcd2..7991a107 100644 --- a/src/modules/alsa-util.h +++ b/src/modules/alsa-util.h @@ -26,6 +26,7 @@ #include <asoundlib.h> #include <pulse/sample.h> +#include <pulse/volume.h> #include <pulse/mainloop-api.h> #include <pulse/channelmap.h> #include <pulse/proplist.h> @@ -79,9 +80,6 @@ snd_pcm_t *pa_alsa_open_by_device_string( int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback); -void pa_alsa_0dB_playback(snd_mixer_elem_t *elem); -void pa_alsa_0dB_capture(snd_mixer_elem_t *elem); - void pa_alsa_dump(snd_pcm_t *pcm); void pa_alsa_dump_status(snd_pcm_t *pcm); @@ -94,4 +92,6 @@ 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); +pa_cvolume *pa_alsa_volume_divide(pa_cvolume *r, const pa_cvolume *t); + #endif diff --git a/src/modules/dbus-util.c b/src/modules/dbus-util.c index 8e0066bc..c9c32a15 100644 --- a/src/modules/dbus-util.c +++ b/src/modules/dbus-util.c @@ -90,7 +90,7 @@ static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) { } /* pa_io_event_cb_t IO event handler */ -static void handle_io_event(PA_GCC_UNUSED pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { +static void handle_io_event(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { unsigned int flags = 0; DBusWatch *watch = userdata; diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index aad6801e..8980ba24 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -28,6 +28,10 @@ #include <asoundlib.h> +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include <valgrind/memcheck.h> +#endif + #include <pulse/xmalloc.h> #include <pulse/util.h> #include <pulse/timeval.h> @@ -68,8 +72,7 @@ PA_MODULE_USAGE( "mmap=<enable memory mapping?> " "tsched=<enable system timer based scheduling mode?> " "tsched_buffer_size=<buffer size when using timer based scheduling> " - "tsched_buffer_watermark=<lower fill watermark> " - "mixer_reset=<reset hw volume and mute settings to sane defaults when falling back to software?>"); + "tsched_buffer_watermark=<lower fill watermark>"); static const char* const valid_modargs[] = { "sink_name", @@ -85,7 +88,6 @@ static const char* const valid_modargs[] = { "tsched", "tsched_buffer_size", "tsched_buffer_watermark", - "mixer_reset", NULL }; @@ -112,6 +114,8 @@ struct userdata { long hw_volume_max, hw_volume_min; long hw_dB_max, hw_dB_min; pa_bool_t hw_dB_supported; + pa_bool_t mixer_seperate_channels; + pa_cvolume hardware_volume; size_t frame_size, fragment_size, hwbuf_size, tsched_watermark; unsigned nfragments; @@ -239,7 +243,7 @@ static size_t check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) { static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) { int work_done = 0; - pa_usec_t max_sleep_usec, process_usec; + pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_play; pa_assert(u); @@ -354,7 +358,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) { static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) { int work_done = 0; - pa_usec_t max_sleep_usec, process_usec; + pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_play; pa_assert(u); @@ -737,8 +741,8 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { return 0; if (mask & SND_CTL_EVENT_MASK_VALUE) { - pa_sink_get_volume(u->sink); - pa_sink_get_mute(u->sink); + pa_sink_get_volume(u->sink, TRUE); + pa_sink_get_mute(u->sink, TRUE); } return 0; @@ -747,30 +751,68 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { static int sink_get_volume_cb(pa_sink *s) { struct userdata *u = s->userdata; int err; - int i; + unsigned i; + pa_cvolume r; + char t[PA_CVOLUME_SNPRINT_MAX]; pa_assert(u); pa_assert(u->mixer_elem); - for (i = 0; i < s->sample_spec.channels; i++) { - long alsa_vol; + if (u->mixer_seperate_channels) { - pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, u->mixer_map[i])); + r.channels = s->sample_spec.channels; - if (u->hw_dB_supported) { + for (i = 0; i < s->sample_spec.channels; i++) { + long alsa_vol; - if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) >= 0) { - s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0); - continue; - } + if (u->hw_dB_supported) { + + if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) + goto fail; + +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); +#endif + + r.values[i] = pa_sw_volume_from_dB((double) alsa_vol / 100.0); + } else { - u->hw_dB_supported = FALSE; + if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) + goto fail; + + r.values[i] = (pa_volume_t) round(((double) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); + } } - if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) + } else { + long alsa_vol; + + pa_assert(u->hw_dB_supported); + + if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) goto fail; - s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); +#endif + + pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) alsa_vol / 100.0)); + } + + pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); + + if (!pa_cvolume_equal(&u->hardware_volume, &r)) { + + u->hardware_volume = s->volume = r; + + if (u->hw_dB_supported) { + pa_cvolume reset; + + /* Hmm, so the hardware volume changed, let's reset our software volume */ + + pa_cvolume_reset(&reset, s->sample_spec.channels); + pa_sink_set_soft_volume(s, &reset); + } } return 0; @@ -784,45 +826,90 @@ fail: static int sink_set_volume_cb(pa_sink *s) { struct userdata *u = s->userdata; int err; - int i; + unsigned i; + pa_cvolume r; pa_assert(u); pa_assert(u->mixer_elem); - for (i = 0; i < s->sample_spec.channels; i++) { - long alsa_vol; - pa_volume_t vol; + if (u->mixer_seperate_channels) { - pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, u->mixer_map[i])); + r.channels = s->sample_spec.channels; - vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM); + for (i = 0; i < s->sample_spec.channels; i++) { + long alsa_vol; + pa_volume_t vol; - if (u->hw_dB_supported) { - alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); - alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); + vol = s->volume.values[i]; - if ((err = snd_mixer_selem_set_playback_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, -1)) >= 0) { + if (u->hw_dB_supported) { - if (snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0) - s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0); + alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); + alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); - continue; - } + if ((err = snd_mixer_selem_set_playback_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, 1)) < 0) + goto fail; + + if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) + goto fail; - u->hw_dB_supported = FALSE; + r.values[i] = pa_sw_volume_from_dB((double) alsa_vol / 100.0); + } else { + + alsa_vol = (long) round(((double) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; + alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); + + if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0) + goto fail; + + if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) + goto fail; + r.values[i] = (pa_volume_t) round(((double) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); + } } - alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; - alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); + } else { + pa_volume_t vol; + long alsa_vol; + + pa_assert(u->hw_dB_supported); + + vol = pa_cvolume_max(&s->volume); - if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0) + alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); + alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); + + if ((err = snd_mixer_selem_set_playback_dB_all(u->mixer_elem, alsa_vol, 1)) < 0) + goto fail; + + if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) goto fail; - if (snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0) - s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); + pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) alsa_vol / 100.0)); } + u->hardware_volume = r; + + if (u->hw_dB_supported) { + char t[PA_CVOLUME_SNPRINT_MAX]; + + /* Match exactly what the user requested by software */ + + pa_alsa_volume_divide(&r, &s->volume); + pa_sink_set_soft_volume(s, &r); + + pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume)); + pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); + pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); + + } else + + /* We can't match exactly what the user requested, hence let's + * at least tell the user about it */ + + s->volume = r; + return 0; fail: @@ -974,7 +1061,7 @@ static void thread_func(void *userdata) { /* Render some data and write it to the dsp */ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { int work_done; - pa_usec_t sleep_usec; + pa_usec_t sleep_usec = 0; if (u->sink->thread_info.rewind_requested) if (process_rewind(u) < 0) @@ -1100,7 +1187,7 @@ int pa__init(pa_module*m) { const char *name; char *name_buf = NULL; pa_bool_t namereg_fail; - pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE; + pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d; pa_usec_t usec; pa_sink_new_data data; @@ -1157,11 +1244,6 @@ int pa__init(pa_module*m) { use_tsched = FALSE; } - if (pa_modargs_get_value_boolean(ma, "mixer_reset", &mixer_reset) < 0) { - pa_log("Failed to parse mixer_reset argument."); - goto fail; - } - u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; @@ -1322,6 +1404,8 @@ int pa__init(pa_module*m) { u->hw_dB_supported = FALSE; u->hw_dB_min = u->hw_dB_max = 0; u->hw_volume_min = u->hw_volume_max = 0; + u->mixer_seperate_channels = FALSE; + pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels); if (use_tsched) fix_tsched_watermark(u); @@ -1349,76 +1433,56 @@ int pa__init(pa_module*m) { if (u->mixer_handle) { pa_assert(u->mixer_elem); - if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) - - if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, TRUE) >= 0 && - snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) >= 0) { - - pa_bool_t suitable = TRUE; + if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) { + pa_bool_t suitable = TRUE; + if (snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) < 0) { + pa_log_info("Failed to get volume range. Falling back to software volume control."); + suitable = FALSE; + } else { pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max); + pa_assert(u->hw_volume_min < u->hw_volume_max); + } - if (u->hw_volume_min > u->hw_volume_max) { - - pa_log_info("Minimal volume %li larger than maximum volume %li. Strange stuff Falling back to software volume control.", u->hw_volume_min, u->hw_volume_max); - suitable = FALSE; - - } else if (u->hw_volume_max - u->hw_volume_min < 3) { - - pa_log_info("Device has less than 4 volume levels. Falling back to software volume control."); - suitable = FALSE; - - } else if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) { - - /* u->hw_dB_max = 0; u->hw_dB_min = -3000; Use this to make valgrind shut up */ - - pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0); - - /* Let's see if this thing actually is useful for muting */ - if (u->hw_dB_min > -6000) { - pa_log_info("Device cannot attenuate for more than -60 dB (only %0.2f dB supported), falling back to software volume control.", ((double) u->hw_dB_min) / 100); - - suitable = FALSE; - } else if (u->hw_dB_max < 0) { - - pa_log_info("Device is still attenuated at maximum volume setting (%0.2f dB is maximum). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_max) / 100); - suitable = FALSE; + if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0) + pa_log_info("Mixer doesn't support dB information."); + else { +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min)); + VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_max, sizeof(u->hw_dB_max)); +#endif - } else if (u->hw_dB_min >= u->hw_dB_max) { + pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0); + pa_assert(u->hw_dB_min < u->hw_dB_max); + u->hw_dB_supported = TRUE; + } - pa_log_info("Minimal dB (%0.2f) larger or equal to maximum dB (%0.2f). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_min) / 100, ((double) u->hw_dB_max) / 100); - suitable = FALSE; + if (suitable && + !u->hw_dB_supported && + u->hw_volume_max - u->hw_volume_min < 3) { - } else { + pa_log_info("Device doesn't do dB volume and has less than 4 volume levels. Falling back to software volume control."); + suitable = FALSE; + } - if (u->hw_dB_max > 0) { - /* dB > 0 means overamplification, and clipping, we don't want that here */ - pa_log_info("Device can do overamplification for %0.2f dB. Limiting to 0 db", ((double) u->hw_dB_max) / 100); - u->hw_dB_max = 0; - } + if (suitable) { + u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, TRUE) >= 0; - u->hw_dB_supported = TRUE; - } - } + u->sink->get_volume = sink_get_volume_cb; + u->sink->set_volume = sink_set_volume_cb; + u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0); + pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported"); - if (suitable) { - u->sink->get_volume = sink_get_volume_cb; - u->sink->set_volume = sink_set_volume_cb; - u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0); - pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported"); - - } else if (mixer_reset) { - pa_log_info("Using software volume control. Trying to reset sound card to 0 dB."); - pa_alsa_0dB_playback(u->mixer_elem); - } else - pa_log_info("Using software volume control. Leaving hw mixer controls untouched."); - } + } else + pa_log_info("Using software volume control."); + } if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) { u->sink->get_mute = sink_get_mute_cb; u->sink->set_mute = sink_set_mute_cb; u->sink->flags |= PA_SINK_HW_MUTE_CTRL; - } + } else + pa_log_info("Using software mute control."); u->mixer_fdl = pa_alsa_fdlist_new(); diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 1cc467d9..9cf5aaf6 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -28,6 +28,10 @@ #include <asoundlib.h> +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include <valgrind/memcheck.h> +#endif + #include <pulse/xmalloc.h> #include <pulse/util.h> #include <pulse/timeval.h> @@ -69,8 +73,7 @@ PA_MODULE_USAGE( "mmap=<enable memory mapping?> " "tsched=<enable system timer based scheduling mode?> " "tsched_buffer_size=<buffer size when using timer based scheduling> " - "tsched_buffer_watermark=<upper fill watermark> " - "mixer_reset=<reset hw volume and mute settings to sane defaults when falling back to software?>"); + "tsched_buffer_watermark=<upper fill watermark>"); static const char* const valid_modargs[] = { "source_name", @@ -86,7 +89,6 @@ static const char* const valid_modargs[] = { "tsched", "tsched_buffer_size", "tsched_buffer_watermark", - "mixer_reset", NULL }; @@ -113,6 +115,9 @@ struct userdata { long hw_volume_max, hw_volume_min; long hw_dB_max, hw_dB_min; pa_bool_t hw_dB_supported; + pa_bool_t mixer_seperate_channels; + + pa_cvolume hardware_volume; size_t frame_size, fragment_size, hwbuf_size, tsched_watermark; unsigned nfragments; @@ -234,7 +239,7 @@ static size_t check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) { static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) { int work_done = 0; - pa_usec_t max_sleep_usec, process_usec; + pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_record; pa_assert(u); @@ -331,7 +336,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) { static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) { int work_done = 0; - pa_usec_t max_sleep_usec, process_usec; + pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_record; pa_assert(u); @@ -680,8 +685,8 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { return 0; if (mask & SND_CTL_EVENT_MASK_VALUE) { - pa_source_get_volume(u->source); - pa_source_get_mute(u->source); + pa_source_get_volume(u->source, TRUE); + pa_source_get_mute(u->source, TRUE); } return 0; @@ -690,30 +695,68 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) { static int source_get_volume_cb(pa_source *s) { struct userdata *u = s->userdata; int err; - int i; + unsigned i; + pa_cvolume r; + char t[PA_CVOLUME_SNPRINT_MAX]; pa_assert(u); pa_assert(u->mixer_elem); - for (i = 0; i < s->sample_spec.channels; i++) { - long alsa_vol; + if (u->mixer_seperate_channels) { - pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, u->mixer_map[i])); + r.channels = s->sample_spec.channels; - if (u->hw_dB_supported) { + for (i = 0; i < s->sample_spec.channels; i++) { + long alsa_vol; - if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) >= 0) { - s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0); - continue; - } + if (u->hw_dB_supported) { + + if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) + goto fail; + +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); +#endif - u->hw_dB_supported = FALSE; + r.values[i] = pa_sw_volume_from_dB((double) alsa_vol / 100.0); + } else { + + if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) + goto fail; + + r.values[i] = (pa_volume_t) round(((double) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); + } } - if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) + } else { + long alsa_vol; + + pa_assert(u->hw_dB_supported); + + if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) goto fail; - s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol)); +#endif + + pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) alsa_vol / 100.0)); + } + + pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); + + if (!pa_cvolume_equal(&u->hardware_volume, &r)) { + + u->hardware_volume = s->volume = r; + + if (u->hw_dB_supported) { + pa_cvolume reset; + + /* Hmm, so the hardware volume changed, let's reset our software volume */ + + pa_cvolume_reset(&reset, s->sample_spec.channels); + pa_source_set_soft_volume(s, &reset); + } } return 0; @@ -727,45 +770,90 @@ fail: static int source_set_volume_cb(pa_source *s) { struct userdata *u = s->userdata; int err; - int i; + unsigned i; + pa_cvolume r; pa_assert(u); pa_assert(u->mixer_elem); - for (i = 0; i < s->sample_spec.channels; i++) { - long alsa_vol; - pa_volume_t vol; + if (u->mixer_seperate_channels) { - pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, u->mixer_map[i])); + r.channels = s->sample_spec.channels; - vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM); + for (i = 0; i < s->sample_spec.channels; i++) { + long alsa_vol; + pa_volume_t vol; - if (u->hw_dB_supported) { - alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); - alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); + vol = s->volume.values[i]; + if (u->hw_dB_supported) { - if ((err = snd_mixer_selem_set_capture_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, -1)) >= 0) { + alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); + alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); - if (snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0) - s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0); + if ((err = snd_mixer_selem_set_capture_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, 1)) < 0) + goto fail; - continue; - } + if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) + goto fail; + + r.values[i] = pa_sw_volume_from_dB((double) alsa_vol / 100.0); + } else { + + alsa_vol = (long) round(((double) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; + alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); + + if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0) + goto fail; - u->hw_dB_supported = FALSE; + if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) + goto fail; + + r.values[i] = (pa_volume_t) round(((double) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); + } } - alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; - alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); + } else { + pa_volume_t vol; + long alsa_vol; + + pa_assert(u->hw_dB_supported); + + vol = pa_cvolume_max(&s->volume); + + alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); + alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); + + if ((err = snd_mixer_selem_set_capture_dB_all(u->mixer_elem, alsa_vol, 1)) < 0) + goto fail; - if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0) + if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0) goto fail; - if (snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0) - s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); + pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) alsa_vol / 100.0)); } + u->hardware_volume = r; + + if (u->hw_dB_supported) { + char t[PA_CVOLUME_SNPRINT_MAX]; + + /* Match exactly what the user requested by software */ + + pa_alsa_volume_divide(&r, &s->volume); + pa_source_set_soft_volume(s, &r); + + pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume)); + pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); + pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); + + } else + + /* We can't match exactly what the user requested, hence let's + * at least tell the user about it */ + + s->volume = r; + return 0; fail: @@ -837,7 +925,7 @@ static void thread_func(void *userdata) { /* Read some data and pass it to the sources */ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { int work_done = 0; - pa_usec_t sleep_usec; + pa_usec_t sleep_usec = 0; if (u->use_mmap) work_done = mmap_read(u, &sleep_usec); @@ -932,7 +1020,7 @@ int pa__init(pa_module*m) { const char *name; char *name_buf = NULL; pa_bool_t namereg_fail; - pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE; + pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d; pa_source_new_data data; snd_pcm_info_alloca(&pcm_info); @@ -988,11 +1076,6 @@ int pa__init(pa_module*m) { use_tsched = FALSE; } - if (pa_modargs_get_value_boolean(ma, "mixer_reset", &mixer_reset) < 0) { - pa_log("Failed to parse mixer_reset argument."); - goto fail; - } - u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; @@ -1146,6 +1229,8 @@ int pa__init(pa_module*m) { u->hw_dB_supported = FALSE; u->hw_dB_min = u->hw_dB_max = 0; u->hw_volume_min = u->hw_volume_max = 0; + u->mixer_seperate_channels = FALSE; + pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels); if (use_tsched) fix_tsched_watermark(u); @@ -1168,67 +1253,56 @@ int pa__init(pa_module*m) { if (u->mixer_handle) { pa_assert(u->mixer_elem); - if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) - if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, FALSE) >= 0 && - snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) >= 0) { - - pa_bool_t suitable = TRUE; + if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) { + pa_bool_t suitable = TRUE; + if (snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) < 0) { + pa_log_info("Failed to get volume range. Falling back to software volume control."); + suitable = FALSE; + } else { pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max); + pa_assert(u->hw_volume_min < u->hw_volume_max); + } - if (u->hw_volume_min > u->hw_volume_max) { - - pa_log_info("Minimal volume %li larger than maximum volume %li. Strange stuff Falling back to software volume control.", u->hw_volume_min, u->hw_volume_max); - suitable = FALSE; - - } else if (u->hw_volume_max - u->hw_volume_min < 3) { - - pa_log_info("Device has less than 4 volume levels. Falling back to software volume control."); - suitable = FALSE; - - } else if (snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) { - - pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0); - - /* Let's see if this thing actually is useful for muting */ - if (u->hw_dB_min > -6000) { - pa_log_info("Device cannot attenuate for more than -60 dB (only %0.2f dB supported), falling back to software volume control.", ((double) u->hw_dB_min) / 100); - - suitable = FALSE; - } else if (u->hw_dB_max < 0) { - - pa_log_info("Device is still attenuated at maximum volume setting (%0.2f dB is maximum). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_max) / 100); - suitable = FALSE; - - } else if (u->hw_dB_min >= u->hw_dB_max) { + if (snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0) + pa_log_info("Mixer doesn't support dB information."); + else { +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min)); + VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_max, sizeof(u->hw_dB_max)); +#endif - pa_log_info("Minimal dB (%0.2f) larger or equal to maximum dB (%0.2f). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_min) / 100, ((double) u->hw_dB_max) / 100); - suitable = FALSE; + pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0); + pa_assert(u->hw_dB_min < u->hw_dB_max); + u->hw_dB_supported = TRUE; + } - } else - u->hw_dB_supported = TRUE; - } + if (suitable && + !u->hw_dB_supported && + u->hw_volume_max - u->hw_volume_min < 3) { - if (suitable) { - u->source->get_volume = source_get_volume_cb; - u->source->set_volume = source_set_volume_cb; - u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0); - pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported"); + pa_log_info("Device has less than 4 volume levels. Falling back to software volume control."); + suitable = FALSE; + } - } else if (mixer_reset) { - pa_log_info("Using software volume control. Trying to reset sound card to 0 dB."); - pa_alsa_0dB_capture(u->mixer_elem); - } else - pa_log_info("Using software volume control. Leaving hw mixer controls untouched."); - } + if (suitable) { + u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, FALSE) >= 0; + u->source->get_volume = source_get_volume_cb; + u->source->set_volume = source_set_volume_cb; + u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0); + pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported"); + } else + pa_log_info("Using software volume control."); + } if (snd_mixer_selem_has_capture_switch(u->mixer_elem)) { u->source->get_mute = source_get_mute_cb; u->source->set_mute = source_set_mute_cb; u->source->flags |= PA_SOURCE_HW_MUTE_CTRL; - } + } else + pa_log_info("Using software mute control."); u->mixer_fdl = pa_alsa_fdlist_new(); diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index 3d731f12..f7d82e4d 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -179,8 +179,8 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 name = pa_sprintf_malloc("sink:%s", sink->name); entry.channel_map = sink->channel_map; - entry.volume = *pa_sink_get_volume(sink); - entry.muted = pa_sink_get_mute(sink); + entry.volume = *pa_sink_get_volume(sink, FALSE); + entry.muted = pa_sink_get_mute(sink, FALSE); } else { pa_source *source; @@ -192,8 +192,8 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 name = pa_sprintf_malloc("source:%s", source->name); entry.channel_map = source->channel_map; - entry.volume = *pa_source_get_volume(source); - entry.muted = pa_source_get_mute(source); + entry.volume = *pa_source_get_volume(source, FALSE); + entry.muted = pa_source_get_mute(source, FALSE); } if ((old = read_entry(u, name))) { diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index f748808e..e0c07d56 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -468,7 +468,7 @@ static int do_read(struct userdata *u) { return 0; } -static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { +static void io_callback(pa_iochannel *io, void*userdata) { struct userdata *u = userdata; pa_assert(u); @@ -483,7 +483,7 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { } } -static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, void *userdata) { +static void on_connection(pa_socket_client *c, pa_iochannel*io, void *userdata) { struct userdata *u = userdata; pa_socket_client_unref(u->client); diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index caa7a1fa..ce766258 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -108,7 +108,7 @@ static void hal_device_free(struct device* d) { pa_xfree(d); } -static void hal_device_free_cb(void *d, PA_GCC_UNUSED void *data) { +static void hal_device_free_cb(void *d, void *data) { hal_device_free(d); } diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c index f34f7be3..97e97dc7 100644 --- a/src/modules/module-lirc.c +++ b/src/modules/module-lirc.c @@ -65,7 +65,7 @@ struct userdata { static int lirc_in_use = 0; -static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) { +static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void*userdata) { struct userdata *u = userdata; char *name = NULL, *code = NULL; @@ -122,7 +122,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC pa_log("Failed to get sink '%s'", u->sink_name); else { int i; - pa_cvolume cv = *pa_sink_get_volume(s); + pa_cvolume cv = *pa_sink_get_volume(s, FALSE); #define DELTA (PA_VOLUME_NORM/20) @@ -159,7 +159,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC case MUTE_TOGGLE: - pa_sink_set_mute(s, !pa_sink_get_mute(s)); + pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE)); break; case INVALID: diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c index 7da77c0d..21f176a4 100644 --- a/src/modules/module-mmkbd-evdev.c +++ b/src/modules/module-mmkbd-evdev.c @@ -76,7 +76,7 @@ struct userdata { pa_module *module; }; -static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) { +static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void*userdata) { struct userdata *u = userdata; pa_assert(io); @@ -113,7 +113,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC pa_log("Failed to get sink '%s'", u->sink_name); else { int i; - pa_cvolume cv = *pa_sink_get_volume(s); + pa_cvolume cv = *pa_sink_get_volume(s, FALSE); #define DELTA (PA_VOLUME_NORM/20) @@ -142,7 +142,7 @@ static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GC case MUTE_TOGGLE: - pa_sink_set_mute(s, !pa_sink_get_mute(s)); + pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE)); break; case INVALID: diff --git a/src/modules/module-native-protocol-fd.c b/src/modules/module-native-protocol-fd.c index fa9c0e4f..f17f435a 100644 --- a/src/modules/module-native-protocol-fd.c +++ b/src/modules/module-native-protocol-fd.c @@ -48,7 +48,8 @@ static const char* const valid_modargs[] = { int pa__init(pa_module*m) { pa_iochannel *io; pa_modargs *ma; - int fd, r = -1; + int32_t fd; + int r = -1; pa_native_options *options = NULL; pa_assert(m); @@ -63,18 +64,16 @@ int pa__init(pa_module*m) { goto finish; } - options = pa_native_options_new(); - options->module = m; - options->auth_anonymous = TRUE; + m->userdata = pa_native_protocol_get(m->core); io = pa_iochannel_new(m->core->mainloop, fd, fd); - m->userdata = pa_native_protocol_get(m->core); + options = pa_native_options_new(); + options->module = m; + options->auth_anonymous = TRUE; pa_native_protocol_connect(m->userdata, io, options); - pa_native_options_unref(options); - r = 0; finish: diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c index 8136c6fc..4fe439f9 100644 --- a/src/modules/module-protocol-stub.c +++ b/src/modules/module-protocol-stub.c @@ -260,7 +260,7 @@ int pa__init(pa_module*m) { goto fail; } - u = pa_xnew0(struct userdata, 1); + m->userdata = u = pa_xnew0(struct userdata, 1); u->module = m; #if defined(USE_PROTOCOL_SIMPLE) @@ -368,8 +368,6 @@ int pa__init(pa_module*m) { # endif #endif - m->userdata = u; - if (ma) pa_modargs_free(ma); @@ -390,7 +388,8 @@ void pa__done(pa_module*m) { pa_assert(m); - u = m->userdata; + if (!(u = m->userdata)) + return; #if defined(USE_PROTOCOL_SIMPLE) if (u->simple_protocol) { diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index ec4e7c79..7bbb47d5 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -306,8 +306,11 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n if (u->restore_device && (s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK, TRUE))) { - pa_log_info("Restoring device for stream %s.", name); - new_data->sink = s; + if (!new_data->sink) { + pa_log_info("Restoring device for stream %s.", name); + new_data->sink = s; + } else + pa_log_info("Not restore device for stream %s, because already set.", name); } pa_xfree(e); @@ -330,13 +333,20 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu if ((e = read_entry(u, name))) { if (u->restore_volume) { - pa_log_info("Restoring volume for sink input %s.", name); - pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map)); + + if (!new_data->volume_is_set) { + pa_log_info("Restoring volume for sink input %s.", name); + pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map)); + } else + pa_log_debug("Not restoring volume for sink input %s, because already set.", name); } if (u->restore_muted) { - pa_log_info("Restoring mute state for sink input %s.", name); - pa_sink_input_new_data_set_muted(new_data, e->muted); + if (!new_data->muted_is_set) { + pa_log_info("Restoring mute state for sink input %s.", name); + pa_sink_input_new_data_set_muted(new_data, e->muted); + } else + pa_log_debug("Not restoring mute state for sink input %s, because already set.", name); } pa_xfree(e); @@ -360,10 +370,14 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou pa_source *s; if (u->restore_device && + !new_data->direct_on_input && (s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE, TRUE))) { - pa_log_info("Restoring device for stream %s.", name); - new_data->source = s; + if (!new_data->source) { + pa_log_info("Restoring device for stream %s.", name); + new_data->source = s; + } else + pa_log_info("Not restroing device for stream %s, because already set", name); } pa_xfree(e); @@ -408,7 +422,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { char *n; pa_sink *s; - if (!(n = get_name(si->proplist, "sink_input"))) + if (!(n = get_name(si->proplist, "sink-input"))) continue; if (strcmp(name, n)) { @@ -417,8 +431,9 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { } if (u->restore_volume) { + pa_cvolume v = e->volume; pa_log_info("Restoring volume for sink input %s.", name); - pa_sink_input_set_volume(si, pa_cvolume_remap(&e->volume, &e->channel_map, &si->channel_map)); + pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map)); } if (u->restore_muted) { @@ -490,7 +505,7 @@ static void dump_database(struct userdata *u) { static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) { struct userdata *u; uint32_t command; - pa_tagstruct *reply; + pa_tagstruct *reply = NULL; pa_assert(p); pa_assert(m); @@ -552,7 +567,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio case SUBCOMMAND_WRITE: { uint32_t mode; - pa_bool_t apply_immediately; + pa_bool_t apply_immediately = FALSE; if (pa_tagstruct_getu32(t, &mode) < 0 || pa_tagstruct_get_boolean(t, &apply_immediately) < 0) @@ -571,6 +586,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio pa_bool_t muted; struct entry entry; datum key, data; + int k; memset(&entry, 0, sizeof(entry)); @@ -593,7 +609,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio data.dptr = (void*) &entry; data.dsize = sizeof(entry); - if (gdbm_store(u->gdbm_file, key, data, mode == PA_UPDATE_REPLACE ? GDBM_REPLACE : GDBM_INSERT) == 1) + if ((k = gdbm_store(u->gdbm_file, key, data, mode == PA_UPDATE_REPLACE ? GDBM_REPLACE : GDBM_INSERT)) == 0) if (apply_immediately) apply_entry(u, name, &entry); } diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c index 5e542253..1423cbc1 100644 --- a/src/modules/rtp/module-rtp-send.c +++ b/src/modules/rtp/module-rtp-send.c @@ -67,10 +67,12 @@ PA_MODULE_USAGE( "destination=<destination IP address> " "port=<port number> " "mtu=<maximum transfer unit> " - "loop=<loopback to local host?>" + "loop=<loopback to local host?> " + "ttl=<ttl value>" ); #define DEFAULT_PORT 46000 +#define DEFAULT_TTL 1 #define SAP_PORT 9875 #define DEFAULT_DESTINATION "224.0.0.56" #define MEMBLOCKQ_MAXLENGTH (1024*170) @@ -86,6 +88,7 @@ static const char* const valid_modargs[] = { "port", "mtu" , "loop", + "ttl", NULL }; @@ -167,6 +170,7 @@ int pa__init(pa_module*m) { pa_modargs *ma = NULL; const char *dest; uint32_t port = DEFAULT_PORT, mtu; + uint32_t ttl = DEFAULT_TTL; int af, fd = -1, sap_fd = -1; pa_source *s; pa_sample_spec ss; @@ -235,6 +239,11 @@ int pa__init(pa_module*m) { if (port & 1) pa_log_warn("Port number not even as suggested in RFC3550!"); + if (pa_modargs_get_value_u32(ma, "ttl", &ttl) < 0 || ttl < 1 || ttl > 0xFF) { + pa_log("ttl= expects a numerical argument between 1 and 255."); + goto fail; + } + dest = pa_modargs_get_value(ma, "destination", DEFAULT_DESTINATION); if (inet_pton(AF_INET6, dest, &sa6.sin6_addr) > 0) { @@ -279,6 +288,15 @@ int pa__init(pa_module*m) { goto fail; } + if (ttl != DEFAULT_TTL) { + int _ttl = (int) ttl; + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &_ttl, sizeof(_ttl)) < 0) { + pa_log("IP_MULTICAST_TTL failed: %s", pa_cstrerror(errno)); + goto fail; + } + } + /* If the socket queue is full, let's drop packets */ pa_make_fd_nonblock(fd); pa_make_udp_socket_low_delay(fd); @@ -290,6 +308,7 @@ int pa__init(pa_module*m) { pa_proplist_sets(data.proplist, "rtp.destination", dest); pa_proplist_setf(data.proplist, "rtp.mtu", "%lu", (unsigned long) mtu); pa_proplist_setf(data.proplist, "rtp.port", "%lu", (unsigned long) port); + pa_proplist_setf(data.proplist, "rtp.ttl", "%lu", (unsigned long) ttl); data.driver = __FILE__; data.module = m; data.source = s; @@ -342,7 +361,7 @@ int pa__init(pa_module*m) { pa_rtp_context_init_send(&u->rtp_context, fd, m->core->cookie, payload, pa_frame_size(&ss)); pa_sap_context_init_send(&u->sap_context, sap_fd, p); - pa_log_info("RTP stream initialized with mtu %u on %s:%u, SSRC=0x%08x, payload=%u, initial sequence #%u", mtu, dest, port, u->rtp_context.ssrc, payload, u->rtp_context.sequence); + pa_log_info("RTP stream initialized with mtu %u on %s:%u ttl=%u, SSRC=0x%08x, payload=%u, initial sequence #%u", mtu, dest, port, ttl, u->rtp_context.ssrc, payload, u->rtp_context.sequence); pa_log_info("SDP-Data:\n%s\nEOF", p); pa_sap_send(&u->sap_context, 0); |