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) \