X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/0bc538b08ca5c4efea86700cb6c4685da3f34345..e11b699d45fa3dca2cde8a976cbf25490f5501a4:/src/pulse/volume.c diff --git a/src/pulse/volume.c b/src/pulse/volume.c index 42cde5b9..69273924 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -25,10 +25,10 @@ #include #include - -#include +#include #include +#include #include #include @@ -40,6 +40,10 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { pa_assert(b); pa_return_val_if_fail(pa_cvolume_valid(a), 0); + + if (PA_UNLIKELY(a == b)) + return 1; + pa_return_val_if_fail(pa_cvolume_valid(b), 0); if (a->channels != b->channels) @@ -60,7 +64,7 @@ pa_cvolume* pa_cvolume_init(pa_cvolume *a) { a->channels = 0; for (c = 0; c < PA_CHANNELS_MAX; c++) - a->values[c] = (pa_volume_t) -1; + a->values[c] = PA_VOLUME_INVALID; return a; } @@ -69,13 +73,14 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { int i; pa_assert(a); - pa_assert(channels > 0); - pa_assert(channels <= PA_CHANNELS_MAX); + pa_assert(pa_channels_valid(channels)); a->channels = (uint8_t) channels; for (i = 0; i < a->channels; i++) - a->values[i] = v; + /* Clamp in case there is stale data that exceeds the current + * PA_VOLUME_MAX */ + a->values[i] = PA_CLAMP_VOLUME(v); return a; } @@ -122,7 +127,7 @@ pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, p } pa_volume_t pa_cvolume_max(const pa_cvolume *a) { - pa_volume_t m = 0; + pa_volume_t m = PA_VOLUME_MUTED; unsigned c; pa_assert(a); @@ -135,9 +140,23 @@ pa_volume_t pa_cvolume_max(const pa_cvolume *a) { return m; } +pa_volume_t pa_cvolume_min(const pa_cvolume *a) { + pa_volume_t m = PA_VOLUME_MAX; + unsigned c; + + pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) + if (a->values[c] < m) + m = a->values[c]; + + return m; +} + pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { - pa_volume_t m = 0; - unsigned c, n; + pa_volume_t m = PA_VOLUME_MUTED; + unsigned c; pa_assert(a); @@ -146,7 +165,7 @@ pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, p pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED); - for (c = n = 0; c < a->channels; c++) { + for (c = 0; c < a->channels; c++) { if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask)) continue; @@ -158,17 +177,48 @@ pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, p return m; } +pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { + pa_volume_t m = PA_VOLUME_MAX; + unsigned c; + + pa_assert(a); + + if (!cm) + return pa_cvolume_min(a); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) { + + if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask)) + continue; + + if (a->values[c] < m) + m = a->values[c]; + } + + return m; +} + pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) { - return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a) * pa_sw_volume_to_linear(b)); + + pa_return_val_if_fail(PA_VOLUME_IS_VALID(a), PA_VOLUME_INVALID); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), PA_VOLUME_INVALID); + + /* cbrt((a/PA_VOLUME_NORM)^3*(b/PA_VOLUME_NORM)^3)*PA_VOLUME_NORM = a*b/PA_VOLUME_NORM */ + + return (pa_volume_t) PA_CLAMP_VOLUME((((uint64_t) a * (uint64_t) b + (uint64_t) PA_VOLUME_NORM / 2ULL) / (uint64_t) PA_VOLUME_NORM)); } pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) { - double v = pa_sw_volume_to_linear(b); - if (v <= 0) + pa_return_val_if_fail(PA_VOLUME_IS_VALID(a), PA_VOLUME_INVALID); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), PA_VOLUME_INVALID); + + if (b <= PA_VOLUME_MUTED) return 0; - return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a) / v); + return (pa_volume_t) (((uint64_t) a * (uint64_t) PA_VOLUME_NORM + (uint64_t) b / 2ULL) / (uint64_t) b); } /* Amplitude, not power */ @@ -189,6 +239,8 @@ pa_volume_t pa_sw_volume_from_dB(double dB) { double pa_sw_volume_to_dB(pa_volume_t v) { + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), PA_DECIBEL_MININFTY); + if (v <= PA_VOLUME_MUTED) return PA_DECIBEL_MININFTY; @@ -205,14 +257,19 @@ pa_volume_t pa_sw_volume_from_linear(double v) { * * http://www.robotplanet.dk/audio/audio_gui_design/ * http://lists.linuxaudio.org/pipermail/linux-audio-dev/2009-May/thread.html#23151 + * + * We make sure that the conversion to linear and back yields the + * same volume value! That's why we need the lround() below! */ - return (pa_volume_t) (cbrt(v) * PA_VOLUME_NORM); + return (pa_volume_t) PA_CLAMP_VOLUME((uint64_t) lround(cbrt(v) * PA_VOLUME_NORM)); } double pa_sw_volume_to_linear(pa_volume_t v) { double f; + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0.0); + if (v <= PA_VOLUME_MUTED) return 0.0; @@ -226,7 +283,7 @@ double pa_sw_volume_to_linear(pa_volume_t v) { char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { unsigned channel; - pa_bool_t first = TRUE; + bool first = true; char *e; pa_assert(s); @@ -246,10 +303,10 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { l -= pa_snprintf(e, l, "%s%u: %3u%%", first ? "" : " ", channel, - (c->values[channel]*100)/PA_VOLUME_NORM); + (c->values[channel]*100+PA_VOLUME_NORM/2)/PA_VOLUME_NORM); e = strchr(e, 0); - first = FALSE; + first = false; } return s; @@ -261,18 +318,18 @@ char *pa_volume_snprint(char *s, size_t l, pa_volume_t v) { pa_init_i18n(); - if (v == (pa_volume_t) -1) { + if (!PA_VOLUME_IS_VALID(v)) { pa_snprintf(s, l, _("(invalid)")); return s; } - pa_snprintf(s, l, "%3u%%", (v*100)/PA_VOLUME_NORM); + pa_snprintf(s, l, "%3u%%", (v*100+PA_VOLUME_NORM/2)/PA_VOLUME_NORM); return s; } char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c) { unsigned channel; - pa_bool_t first = TRUE; + bool first = true; char *e; pa_assert(s); @@ -297,7 +354,49 @@ char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c) { isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f); e = strchr(e, 0); - first = FALSE; + first = false; + } + + return s; +} + +char *pa_cvolume_snprint_verbose(char *s, size_t l, const pa_cvolume *c, const pa_channel_map *map, int print_dB) { + char *current = s; + bool first = true; + + pa_assert(s); + pa_assert(l > 0); + pa_assert(c); + + pa_init_i18n(); + + if (!pa_cvolume_valid(c)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + pa_assert(!map || (map->channels == c->channels)); + pa_assert(!map || pa_channel_map_valid(map)); + + current[0] = 0; + + for (unsigned channel = 0; channel < c->channels && l > 1; channel++) { + char channel_position[32]; + size_t bytes_printed; + char buf[PA_VOLUME_SNPRINT_VERBOSE_MAX]; + + if (map) + pa_snprintf(channel_position, sizeof(channel_position), "%s", pa_channel_position_to_string(map->map[channel])); + else + pa_snprintf(channel_position, sizeof(channel_position), "%u", channel); + + bytes_printed = pa_snprintf(current, l, "%s%s: %s", + first ? "" : ", ", + channel_position, + pa_volume_snprint_verbose(buf, sizeof(buf), c->values[channel], print_dB)); + l -= bytes_printed; + current += bytes_printed; + first = false; } return s; @@ -311,14 +410,35 @@ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) { pa_init_i18n(); - if (v == (pa_volume_t) -1) { + if (!PA_VOLUME_IS_VALID(v)) { pa_snprintf(s, l, _("(invalid)")); return s; } f = pa_sw_volume_to_dB(v); - pa_snprintf(s, l, "%0.2f dB", - isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f); + pa_snprintf(s, l, "%0.2f dB", isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f); + + return s; +} + +char *pa_volume_snprint_verbose(char *s, size_t l, pa_volume_t v, int print_dB) { + char dB[PA_SW_VOLUME_SNPRINT_DB_MAX]; + + pa_assert(s); + pa_assert(l > 0); + + pa_init_i18n(); + + if (!PA_VOLUME_IS_VALID(v)) { + pa_snprintf(s, l, _("(invalid)")); + return s; + } + + pa_snprintf(s, l, "%" PRIu32 " / %3u%%%s%s", + v, + (v * 100 + PA_VOLUME_NORM / 2) / PA_VOLUME_NORM, + print_dB ? " / " : "", + print_dB ? pa_sw_volume_snprint_dB(dB, sizeof(dB), v) : ""); return s; } @@ -328,6 +448,7 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { pa_assert(a); pa_return_val_if_fail(pa_cvolume_valid(a), 0); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), 0); for (c = 0; c < a->channels; c++) if (a->values[c] != v) @@ -361,6 +482,7 @@ pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_assert(a); pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), NULL); for (i = 0; i < a->channels; i++) dest->values[i] = pa_sw_volume_multiply(a->values[i], b); @@ -395,6 +517,7 @@ pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, p pa_assert(a); pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(b), NULL); for (i = 0; i < a->channels; i++) dest->values[i] = pa_sw_volume_divide(a->values[i], b); @@ -409,37 +532,37 @@ int pa_cvolume_valid(const pa_cvolume *v) { pa_assert(v); - if (v->channels <= 0 || v->channels > PA_CHANNELS_MAX) + if (!pa_channels_valid(v->channels)) return 0; for (c = 0; c < v->channels; c++) - if (v->values[c] == (pa_volume_t) -1) + if (!PA_VOLUME_IS_VALID(v->values[c])) return 0; return 1; } -static pa_bool_t on_left(pa_channel_position_t p) { +static bool on_left(pa_channel_position_t p) { return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LEFT); } -static pa_bool_t on_right(pa_channel_position_t p) { +static bool on_right(pa_channel_position_t p) { return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_RIGHT); } -static pa_bool_t on_center(pa_channel_position_t p) { +static bool on_center(pa_channel_position_t p) { return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_CENTER); } -static pa_bool_t on_lfe(pa_channel_position_t p) { +static bool on_lfe(pa_channel_position_t p) { return p == PA_CHANNEL_POSITION_LFE; } -static pa_bool_t on_front(pa_channel_position_t p) { +static bool on_front(pa_channel_position_t p) { return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_FRONT); } -static pa_bool_t on_rear(pa_channel_position_t p) { +static bool on_rear(pa_channel_position_t p) { return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_REAR); } @@ -451,8 +574,6 @@ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa pa_assert(from); pa_assert(to); - pa_return_val_if_fail(pa_cvolume_valid(v), NULL); - pa_return_val_if_fail(pa_channel_map_valid(from), NULL); pa_return_val_if_fail(pa_channel_map_valid(to), NULL); pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, from), NULL); @@ -554,8 +675,6 @@ float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) { pa_assert(v); pa_assert(map); - pa_return_val_if_fail(pa_cvolume_valid(v), 0.0f); - pa_return_val_if_fail(pa_channel_map_valid(map), 0.0f); pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); if (!pa_channel_map_can_balance(map)) @@ -587,12 +706,10 @@ pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, flo pa_assert(map); pa_assert(v); - pa_assert(new_balance >= -1.0f); - pa_assert(new_balance <= 1.0f); - pa_return_val_if_fail(pa_cvolume_valid(v), NULL); - pa_return_val_if_fail(pa_channel_map_valid(map), NULL); 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_balance(map)) return v; @@ -602,9 +719,9 @@ pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, flo m = PA_MAX(left, right); if (new_balance <= 0) { - nright = (new_balance + 1.0f) * m; + nright = (new_balance + 1.0f) * m; nleft = m; - } else { + } else { nleft = (1.0f - new_balance) * m; nright = m; } @@ -614,12 +731,12 @@ pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, flo if (left == 0) v->values[c] = nleft; else - v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nleft) / (uint64_t) left); + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nleft) / (uint64_t) left); } else if (on_right(map->map[c])) { if (right == 0) v->values[c] = nright; else - v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nright) / (uint64_t) right); + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nright) / (uint64_t) right); } } @@ -633,7 +750,7 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) { pa_assert(v); pa_return_val_if_fail(pa_cvolume_valid(v), NULL); - pa_return_val_if_fail(max != (pa_volume_t) -1, NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(max), NULL); t = pa_cvolume_max(v); @@ -641,7 +758,7 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) { return pa_cvolume_set(v, v->channels, max); for (c = 0; c < v->channels; c++) - v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t); + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t); return v; } @@ -652,8 +769,12 @@ pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map pa_assert(v); - pa_return_val_if_fail(pa_cvolume_valid(v), NULL); - pa_return_val_if_fail(max != (pa_volume_t) -1, NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(max), NULL); + + if (!cm) + return pa_cvolume_scale(v, max); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, cm), NULL); t = pa_cvolume_max_mask(v, cm, mask); @@ -661,7 +782,7 @@ pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map return pa_cvolume_set(v, v->channels, max); for (c = 0; c < v->channels; c++) - v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t); + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) max) / (uint64_t) t); return v; } @@ -704,8 +825,6 @@ float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) { pa_assert(v); pa_assert(map); - pa_return_val_if_fail(pa_cvolume_valid(v), 0.0f); - pa_return_val_if_fail(pa_channel_map_valid(map), 0.0f); pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); if (!pa_channel_map_can_fade(map)) @@ -728,12 +847,10 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float pa_assert(map); pa_assert(v); - pa_assert(new_fade >= -1.0f); - pa_assert(new_fade <= 1.0f); - pa_return_val_if_fail(pa_cvolume_valid(v), NULL); - pa_return_val_if_fail(pa_channel_map_valid(map), NULL); pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL); + pa_return_val_if_fail(new_fade >= -1.0f, NULL); + pa_return_val_if_fail(new_fade <= 1.0f, NULL); if (!pa_channel_map_can_fade(map)) return v; @@ -743,9 +860,9 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float m = PA_MAX(front, rear); if (new_fade <= 0) { - nfront = (new_fade + 1.0f) * m; + nfront = (new_fade + 1.0f) * m; nrear = m; - } else { + } else { nrear = (1.0f - new_fade) * m; nfront = m; } @@ -755,12 +872,12 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float if (front == 0) v->values[c] = nfront; else - v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nfront) / (uint64_t) front); + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nfront) / (uint64_t) front); } else if (on_rear(map->map[c])) { if (rear == 0) v->values[c] = nrear; else - v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nrear) / (uint64_t) rear); + v->values[c] = (pa_volume_t) PA_CLAMP_VOLUME(((uint64_t) v->values[c] * (uint64_t) nrear) / (uint64_t) rear); } } @@ -774,18 +891,19 @@ pa_cvolume* pa_cvolume_set_position( pa_volume_t v) { unsigned c; - pa_bool_t good = FALSE; + bool good = false; pa_assert(cv); pa_assert(map); pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), NULL); pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(v), NULL); for (c = 0; c < map->channels; c++) if (map->map[c] == t) { cv->values[c] = v; - good = TRUE; + good = true; } return good ? cv : NULL; @@ -812,3 +930,61 @@ pa_volume_t pa_cvolume_get_position( return v; } + +pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_valid(b), NULL); + + for (i = 0; i < a->channels && i < b->channels; i++) + dest->values[i] = PA_MAX(a->values[i], b->values[i]); + + dest->channels = (uint8_t) i; + + return dest; +} + +pa_cvolume* pa_cvolume_inc_clamp(pa_cvolume *v, pa_volume_t inc, pa_volume_t limit) { + pa_volume_t m; + + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(inc), NULL); + + m = pa_cvolume_max(v); + + if (m >= limit - inc) + m = limit; + else + m += inc; + + return pa_cvolume_scale(v, m); +} + +pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc) { + return pa_cvolume_inc_clamp(v, inc, PA_VOLUME_MAX); +} + +pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec) { + pa_volume_t m; + + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(PA_VOLUME_IS_VALID(dec), NULL); + + m = pa_cvolume_max(v); + + if (m <= PA_VOLUME_MUTED + dec) + m = PA_VOLUME_MUTED; + else + m -= dec; + + return pa_cvolume_scale(v, m); +}