From 9fecb6eb32d19b5f2dfc194426b7b3cd678a7520 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 27 Oct 2015 09:54:39 +0100 Subject: [PATCH] volume: Add LFE balance API The gnome/unity-control-center UIs have a master volume slider, and three sub-sliders: balance, fade, and subwoofer. Balance and fade use PA's set_balance and set_fade APIs accordingly, but the subwoofer slider sometimes does unintuitive things. In order to make that slider behave better, let's add a LFE balance API that these volume control UIs can use instead. With this API, the UI can balance between "no subwoofer" and "only subwoofer" with "equal balance" in the middle, which would make it more consistent with the behaviour of the other sliders. BugLink: https://bugzilla.gnome.org/show_bug.cgi?id=753847 Signed-off-by: David Henningsson --- src/map-file | 3 +++ src/pulse/channelmap.c | 13 ++++++++++++ src/pulse/channelmap.h | 5 +++++ src/pulse/volume.c | 42 ++++++++++++++++++++++++++++++++++++- src/pulse/volume.h | 18 ++++++++++++++++ src/pulsecore/sample-util.h | 8 +++++++ 6 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/map-file b/src/map-file index 5159829a5..93a62b860 100644 --- a/src/map-file +++ b/src/map-file @@ -7,6 +7,7 @@ pa_bytes_snprint; pa_bytes_to_usec; pa_channel_map_can_balance; pa_channel_map_can_fade; +pa_channel_map_can_lfe_balance; pa_channel_map_compatible; pa_channel_map_equal; pa_channel_map_has_position; @@ -127,6 +128,7 @@ pa_cvolume_dec; pa_cvolume_equal; pa_cvolume_get_balance; pa_cvolume_get_fade; +pa_cvolume_get_lfe_balance; pa_cvolume_get_position; pa_cvolume_inc; pa_cvolume_inc_clamp; @@ -142,6 +144,7 @@ pa_cvolume_scale_mask; pa_cvolume_set; pa_cvolume_set_balance; pa_cvolume_set_fade; +pa_cvolume_set_lfe_balance; pa_cvolume_set_position; pa_cvolume_snprint; pa_cvolume_snprint_verbose; diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index c342ef63a..c44dca411 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -684,6 +684,19 @@ int pa_channel_map_can_fade(const pa_channel_map *map) { (PA_CHANNEL_POSITION_MASK_REAR & m); } +int pa_channel_map_can_lfe_balance(const pa_channel_map *map) { + pa_channel_position_mask_t m; + + pa_assert(map); + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + + m = pa_channel_map_mask(map); + + return + (PA_CHANNEL_POSITION_MASK_LFE & m) && + (PA_CHANNEL_POSITION_MASK_HFE & m); +} + const char* pa_channel_map_to_name(const pa_channel_map *map) { pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)]; unsigned c; diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index 30904ef84..6eabe20ba 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -338,6 +338,11 @@ int pa_channel_map_can_balance(const pa_channel_map *map) PA_GCC_PURE; * there are front/rear channels available. \since 0.9.15 */ int pa_channel_map_can_fade(const pa_channel_map *map) PA_GCC_PURE; +/** Returns non-zero if it makes sense to apply a volume 'lfe balance' + * (i.e.\ 'balance' between LFE and non-LFE channels) with this mapping, + * i.e.\ if there are LFE and non-LFE channels available. \since 8.0 */ +int pa_channel_map_can_lfe_balance(const pa_channel_map *map) PA_GCC_PURE; + /** Tries to find a well-known channel mapping name for this channel * mapping, i.e.\ "stereo", "surround-71" and so on. If the channel * mapping is unknown NULL will be returned. This name can be parsed diff --git a/src/pulse/volume.c b/src/pulse/volume.c index b3addcefc..1667b940c 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -552,8 +552,12 @@ static bool on_center(pa_channel_position_t p) { return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_CENTER); } +static bool on_hfe(pa_channel_position_t p) { + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_HFE); +} + static bool on_lfe(pa_channel_position_t p) { - return p == PA_CHANNEL_POSITION_LFE; + return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LFE); } static bool on_front(pa_channel_position_t p) { @@ -833,6 +837,42 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float return set_balance(v, map, new_fade, on_rear, on_front); } +float pa_cvolume_get_lfe_balance(const pa_cvolume *v, const pa_channel_map *map) { + pa_volume_t hfe, lfe; + + pa_assert(v); + pa_assert(map); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); + + if (!pa_channel_map_can_lfe_balance(map)) + return 0.0f; + + get_avg(map, v, &hfe, &lfe, on_hfe, on_lfe); + + if (hfe == lfe) + return 0.0f; + + if (hfe > lfe) + return -1.0f + ((float) lfe / (float) hfe); + else + return 1.0f - ((float) hfe / (float) lfe); +} + +pa_cvolume* pa_cvolume_set_lfe_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) { + pa_assert(map); + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL); + pa_return_val_if_fail(new_balance >= -1.0f, NULL); + pa_return_val_if_fail(new_balance <= 1.0f, NULL); + + if (!pa_channel_map_can_lfe_balance(map)) + return v; + + return set_balance(v, map, new_balance, on_hfe, on_lfe); +} + pa_cvolume* pa_cvolume_set_position( pa_cvolume *cv, const pa_channel_map *map, diff --git a/src/pulse/volume.h b/src/pulse/volume.h index ec777b269..8cf4fa45b 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -372,6 +372,24 @@ float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) PA_GCC * pa_channel_map_can_fade(). \since 0.9.15 */ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float new_fade); +/** Calculate a 'lfe balance' value for the specified volume with + * the specified channel map. The return value will range from + * -1.0f (no lfe) to +1.0f (only lfe), where 0.0f is balanced. + * If no value is applicable to this channel map the return value + * will always be 0.0f. See pa_channel_map_can_lfe_balance(). \since 8.0 */ +float pa_cvolume_get_lfe_balance(const pa_cvolume *v, const pa_channel_map *map) PA_GCC_PURE; + +/** Adjust the 'lfe balance' value for the specified volume with + * the specified channel map. v will be modified in place and returned. + * The balance is a value between -1.0f (no lfe) and +1.0f (only lfe). + * This operation might not be reversible! Also, after this call + * pa_cvolume_get_lfe_balance() is not guaranteed to actually + * return the requested value (e.g. when the input volume was + * zero anyway for all channels). If no lfe balance value is applicable to + * this channel map the volume will not be modified. See + * pa_channel_map_can_lfe_balance(). \since 8.0 */ +pa_cvolume* pa_cvolume_set_lfe_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance); + /** Scale the passed pa_cvolume structure so that the maximum volume * of all channels equals max. The proportions between the channel * volumes are kept. \since 0.9.15 */ diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h index 95671f161..c817bc958 100644 --- a/src/pulsecore/sample-util.h +++ b/src/pulsecore/sample-util.h @@ -126,6 +126,14 @@ size_t pa_convert_size(size_t size, const pa_sample_spec *from, const pa_sample_ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER)) +#define PA_CHANNEL_POSITION_MASK_LFE \ + PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE) + +#define PA_CHANNEL_POSITION_MASK_HFE \ + (PA_CHANNEL_POSITION_MASK_REAR | PA_CHANNEL_POSITION_MASK_FRONT \ + | PA_CHANNEL_POSITION_MASK_LEFT | PA_CHANNEL_POSITION_MASK_RIGHT \ + | PA_CHANNEL_POSITION_MASK_CENTER) + #define PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER \ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \