diff options
Diffstat (limited to 'src/pulse/volume.c')
| -rw-r--r-- | src/pulse/volume.c | 175 | 
1 files changed, 153 insertions, 22 deletions
diff --git a/src/pulse/volume.c b/src/pulse/volume.c index 6848771e..64688e0b 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -80,29 +80,78 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {  pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {      uint64_t sum = 0; -    int i; +    unsigned c;      pa_assert(a);      pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); -    for (i = 0; i < a->channels; i++) -        sum += a->values[i]; +    for (c = 0; c < a->channels; c++) +        sum += a->values[c];      sum /= a->channels;      return (pa_volume_t) sum;  } +pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { +    uint64_t sum = 0; +    unsigned c, n; + +    pa_assert(a); + +    if (!cm) +        return pa_cvolume_avg(a); + +    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED); + +    for (c = n = 0; c < a->channels; c++) { + +        if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask)) +            continue; + +        sum += a->values[c]; +        n ++; +    } + +    if (n > 0) +        sum /= n; + +    return (pa_volume_t) sum; +} +  pa_volume_t pa_cvolume_max(const pa_cvolume *a) {      pa_volume_t m = 0; -    int i; +    unsigned c;      pa_assert(a);      pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); -    for (i = 0; i < a->channels; i++) -        if (a->values[i] > m) -            m = a->values[i]; +    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_assert(a); + +    if (!cm) +        return pa_cvolume_max(a); + +    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED); + +    for (c = n = 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;  } @@ -120,20 +169,28 @@ pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) {      return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a) / v);  } -#define USER_DECIBEL_RANGE 90 +/* Amplitude, not power */ +static double linear_to_dB(double v) { +    return 20.0 * log10(v); +} + +static double dB_to_linear(double v) { +    return pow(10.0, v / 20.0); +}  pa_volume_t pa_sw_volume_from_dB(double dB) { -    if (isinf(dB) < 0 || dB <= -USER_DECIBEL_RANGE) +    if (isinf(dB) < 0 || dB <= PA_DECIBEL_MININFTY)          return PA_VOLUME_MUTED; -    return (pa_volume_t) lrint(ceil((dB/USER_DECIBEL_RANGE+1.0)*PA_VOLUME_NORM)); +    return pa_sw_volume_from_linear(dB_to_linear(dB));  }  double pa_sw_volume_to_dB(pa_volume_t v) { -    if (v == PA_VOLUME_MUTED) + +    if (v <= PA_VOLUME_MUTED)          return PA_DECIBEL_MININFTY; -    return ((double) v/PA_VOLUME_NORM-1)*USER_DECIBEL_RANGE; +    return linear_to_dB(pa_sw_volume_to_linear(v));  }  pa_volume_t pa_sw_volume_from_linear(double v) { @@ -141,18 +198,28 @@ pa_volume_t pa_sw_volume_from_linear(double v) {      if (v <= 0.0)          return PA_VOLUME_MUTED; -    if (v > .999 && v < 1.001) -        return PA_VOLUME_NORM; +    /* +     * We use a cubic mapping here, as suggested and discussed here: +     * +     * http://www.robotplanet.dk/audio/audio_gui_design/ +     * http://lists.linuxaudio.org/pipermail/linux-audio-dev/2009-May/thread.html#23151 +     */ -    return pa_sw_volume_from_dB(20.0*log10(v)); +    return (pa_volume_t) (cbrt(v) * PA_VOLUME_NORM);  }  double pa_sw_volume_to_linear(pa_volume_t v) { +    double f; -    if (v == PA_VOLUME_MUTED) +    if (v <= PA_VOLUME_MUTED)          return 0.0; -    return pow(10.0, pa_sw_volume_to_dB(v)/20.0); +    if (v == PA_VOLUME_NORM) +        return 1.0; + +    f = ((double) v / PA_VOLUME_NORM); + +    return f*f*f;  }  char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { @@ -225,7 +292,7 @@ char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c) {          l -= pa_snprintf(e, l, "%s%u: %0.2f dB",                           first ? "" : " ",                           channel, -                         isinf(f) < 0 || f <= -USER_DECIBEL_RANGE ? -INFINITY : f); +                         isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ? -INFINITY : f);          e = strchr(e, 0);          first = FALSE; @@ -249,7 +316,7 @@ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) {      f = pa_sw_volume_to_dB(v);      pa_snprintf(s, l, "%0.2f dB", -                isinf(f) < 0 || f <= -USER_DECIBEL_RANGE ?  -INFINITY : f); +                isinf(f) < 0 || f <= PA_DECIBEL_MININFTY ?  -INFINITY : f);      return s;  } @@ -572,11 +639,29 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) {      pa_return_val_if_fail(pa_cvolume_valid(v), NULL);      pa_return_val_if_fail(max != (pa_volume_t) -1, NULL); +    t = pa_cvolume_max(v); + +    if (t <= PA_VOLUME_MUTED) +        return pa_cvolume_set(v, v->channels, max); +      for (c = 0; c < v->channels; c++) -        if (v->values[c] > t) -            t = v->values[c]; +        v->values[c] = (pa_volume_t) (((uint64_t)  v->values[c] * (uint64_t) max) / (uint64_t) t); + +    return v; +} -    if (t <= 0) +pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map *cm, pa_channel_position_mask_t mask) { +    unsigned c; +    pa_volume_t t = 0; + +    pa_assert(v); + +    pa_return_val_if_fail(pa_cvolume_valid(v), NULL); +    pa_return_val_if_fail(max != (pa_volume_t) -1, NULL); + +    t = pa_cvolume_max_mask(v, cm, mask); + +    if (t <= PA_VOLUME_MUTED)          return pa_cvolume_set(v, v->channels, max);      for (c = 0; c < v->channels; c++) @@ -685,3 +770,49 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float      return v;  } + +pa_cvolume* pa_cvolume_set_position( +        pa_cvolume *cv, +        const pa_channel_map *map, +        pa_channel_position_t t, +        pa_volume_t v) { + +    unsigned c; +    pa_bool_t 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); + +    for (c = 0; c < map->channels; c++) +        if (map->map[c] == t) { +            cv->values[c] = v; +            good = TRUE; +        } + +    return good ? cv : NULL; +} + +pa_volume_t pa_cvolume_get_position( +        pa_cvolume *cv, +        const pa_channel_map *map, +        pa_channel_position_t t) { + +    unsigned c; +    pa_volume_t v = PA_VOLUME_MUTED; + +    pa_assert(cv); +    pa_assert(map); + +    pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), PA_VOLUME_MUTED); +    pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, PA_VOLUME_MUTED); + +    for (c = 0; c < map->channels; c++) +        if (map->map[c] == t) +            if (cv->values[c] > v) +                v = cv->values[c]; + +    return v; +}  | 
