diff options
Diffstat (limited to 'src/pulse')
-rw-r--r-- | src/pulse/channelmap.c | 25 | ||||
-rw-r--r-- | src/pulse/channelmap.h | 59 | ||||
-rw-r--r-- | src/pulse/context.c | 6 | ||||
-rw-r--r-- | src/pulse/proplist.c | 15 | ||||
-rw-r--r-- | src/pulse/proplist.h | 14 | ||||
-rw-r--r-- | src/pulse/sample.c | 41 | ||||
-rw-r--r-- | src/pulse/sample.h | 20 | ||||
-rw-r--r-- | src/pulse/simple.c | 71 | ||||
-rw-r--r-- | src/pulse/version.h.in | 7 | ||||
-rw-r--r-- | src/pulse/volume.c | 175 | ||||
-rw-r--r-- | src/pulse/volume.h | 39 |
11 files changed, 388 insertions, 84 deletions
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index ce7dadc9..4654a9ad 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -839,3 +839,28 @@ const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) { return NULL; } + +int pa_channel_map_has_position(const pa_channel_map *map, pa_channel_position_t p) { + unsigned c; + + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + pa_return_val_if_fail(p < PA_CHANNEL_POSITION_MAX, 0); + + for (c = 0; c < map->channels; c++) + if (map->map[c] == p) + return 1; + + return 0; +} + +pa_channel_position_mask_t pa_channel_map_mask(const pa_channel_map *map) { + unsigned c; + pa_channel_position_mask_t r = 0; + + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + + for (c = 0; c < map->channels; c++) + r |= PA_CHANNEL_POSITION_MASK(map->map[c]); + + return r; +} diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index d4db45b0..2aaead01 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -74,26 +74,30 @@ typedef enum pa_channel_position { PA_CHANNEL_POSITION_INVALID = -1, PA_CHANNEL_POSITION_MONO = 0, - PA_CHANNEL_POSITION_LEFT, - PA_CHANNEL_POSITION_RIGHT, - PA_CHANNEL_POSITION_CENTER, + PA_CHANNEL_POSITION_FRONT_LEFT, /* Apple calls this 'Left' */ + PA_CHANNEL_POSITION_FRONT_RIGHT, /* Apple calls this 'Right' */ + PA_CHANNEL_POSITION_FRONT_CENTER, /* Apple calls this 'Center' */ - PA_CHANNEL_POSITION_FRONT_LEFT = PA_CHANNEL_POSITION_LEFT, - PA_CHANNEL_POSITION_FRONT_RIGHT = PA_CHANNEL_POSITION_RIGHT, - PA_CHANNEL_POSITION_FRONT_CENTER = PA_CHANNEL_POSITION_CENTER, +/** \cond fulldocs */ + PA_CHANNEL_POSITION_LEFT = PA_CHANNEL_POSITION_FRONT_LEFT, + PA_CHANNEL_POSITION_RIGHT = PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_CENTER = PA_CHANNEL_POSITION_FRONT_CENTER, +/** \endcond */ - PA_CHANNEL_POSITION_REAR_CENTER, - PA_CHANNEL_POSITION_REAR_LEFT, - PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_REAR_CENTER, /* Microsoft calls this 'Back Center', Apple calls this 'Center Surround' */ + PA_CHANNEL_POSITION_REAR_LEFT, /* Microsoft calls this 'Back Left', Apple calls this 'Left Surround' */ + PA_CHANNEL_POSITION_REAR_RIGHT, /* Microsoft calls this 'Back Right', Apple calls this 'Right Surround' */ - PA_CHANNEL_POSITION_LFE, + PA_CHANNEL_POSITION_LFE, /* Microsoft calls this 'Low Frequency', Apple calls this 'LFEScreen' */ +/** \cond fulldocs */ PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE, +/** \endcond */ - PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, - PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, + PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, /* Apple calls this 'Left Center' */ + PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, /* Apple calls this 'Right Center */ - PA_CHANNEL_POSITION_SIDE_LEFT, - PA_CHANNEL_POSITION_SIDE_RIGHT, + PA_CHANNEL_POSITION_SIDE_LEFT, /* Apple calls this 'Left Surround Direct' */ + PA_CHANNEL_POSITION_SIDE_RIGHT, /* Apple calls this 'Right Surround Direct' */ PA_CHANNEL_POSITION_AUX0, PA_CHANNEL_POSITION_AUX1, @@ -128,15 +132,15 @@ typedef enum pa_channel_position { PA_CHANNEL_POSITION_AUX30, PA_CHANNEL_POSITION_AUX31, - PA_CHANNEL_POSITION_TOP_CENTER, + PA_CHANNEL_POSITION_TOP_CENTER, /* Apple calls this 'Top Center Surround' */ - PA_CHANNEL_POSITION_TOP_FRONT_LEFT, - PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, - PA_CHANNEL_POSITION_TOP_FRONT_CENTER, + PA_CHANNEL_POSITION_TOP_FRONT_LEFT, /* Apple calls this 'Vertical Height Left' */ + PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, /* Apple calls this 'Vertical Height Right' */ + PA_CHANNEL_POSITION_TOP_FRONT_CENTER, /* Apple calls this 'Vertical Height Center' */ - PA_CHANNEL_POSITION_TOP_REAR_LEFT, - PA_CHANNEL_POSITION_TOP_REAR_RIGHT, - PA_CHANNEL_POSITION_TOP_REAR_CENTER, + PA_CHANNEL_POSITION_TOP_REAR_LEFT, /* Microsoft and Apple call this 'Top Back Left' */ + PA_CHANNEL_POSITION_TOP_REAR_RIGHT, /* Microsoft and Apple call this 'Top Back Right' */ + PA_CHANNEL_POSITION_TOP_REAR_CENTER, /* Microsoft and Apple call this 'Top Back Center' */ PA_CHANNEL_POSITION_MAX } pa_channel_position_t; @@ -201,6 +205,12 @@ typedef enum pa_channel_position { #define PA_CHANNEL_POSITION_MAX PA_CHANNEL_POSITION_MAX /** \endcond */ +/** A mask of channel positions. \since 0.9.16 */ +typedef uint64_t pa_channel_position_mask_t; + +/** Makes a bit mask from a channel position. \since 0.9.16 */ +#define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1 << (f))) + /** A list of channel mapping definitions for pa_channel_map_init_auto() */ typedef enum pa_channel_map_def { PA_CHANNEL_MAP_AIFF, @@ -325,6 +335,13 @@ mapping. I.e. "Stereo", "Surround 7.1" and so on. If the channel mapping is unknown NULL will be returned. \since 0.9.15 */ const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) PA_GCC_PURE; +/** Returns TRUE if the specified channel position is available at + * least once in the channel map. \since 0.9.16 */ +int pa_channel_map_has_position(const pa_channel_map *map, pa_channel_position_t p) PA_GCC_PURE; + +/** Generates a bit mask from a channel map. \since 0.9.16 */ +pa_channel_position_mask_t pa_channel_map_mask(const pa_channel_map *map) PA_GCC_PURE; + PA_C_DECL_END #endif diff --git a/src/pulse/context.c b/src/pulse/context.c index bfff0e17..3b7bf08d 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -811,10 +811,10 @@ static int try_next_connection(pa_context *c) { #ifdef HAVE_DBUS if (c->no_fail && !c->server_specified) { - if (!c->system_bus) - track_pulseaudio_on_dbus(c, DBUS_BUS_SYSTEM, &c->system_bus); if (!c->session_bus) track_pulseaudio_on_dbus(c, DBUS_BUS_SESSION, &c->session_bus); + if (!c->system_bus) + track_pulseaudio_on_dbus(c, DBUS_BUS_SYSTEM, &c->system_bus); } else #endif pa_context_fail(c, PA_ERR_CONNECTIONREFUSED); @@ -892,7 +892,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo /* FIXME: We probably should check if this is actually the NameOwnerChanged we were looking for */ - is_session = bus == pa_dbus_wrap_connection_get(c->session_bus); + is_session = c->session_bus && bus == pa_dbus_wrap_connection_get(c->session_bus); pa_log_debug("Rock!! PulseAudio is back on %s bus", is_session ? "session" : "system"); if (is_session) diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index db4c9344..c904f533 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -140,6 +140,21 @@ static int proplist_setn(pa_proplist *p, const char *key, size_t key_length, con return 0; } +/** Will accept only valid UTF-8 */ +int pa_proplist_setp(pa_proplist *p, const char *pair) { + const char *t; + + pa_assert(p); + pa_assert(pair); + + if (!(t = strchr(pair, '='))) + return -1; + + return proplist_setn(p, + pair, t - pair, + t + 1, strchr(pair, 0) - t - 1); +} + static int proplist_sethex(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) { struct property *prop; pa_bool_t add = FALSE; diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 2e7e5ad0..4c791dce 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -39,6 +39,12 @@ PA_C_DECL_BEGIN /** For streams: localized media artist if applicable, formatted as UTF-8. e.g. "Guns'N'Roses" */ #define PA_PROP_MEDIA_ARTIST "media.artist" +/** For streams: localized media copyright string if applicable, formatted as UTF-8. e.g. "Evil Record Corp." */ +#define PA_PROP_MEDIA_COPYRIGHT "media.copyright" + +/** For streams: localized media generator software string if applicable, formatted as UTF-8. e.g. "Foocrop AudioFrobnicator" */ +#define PA_PROP_MEDIA_SOFTWARE "media.software" + /** For streams: media language if applicable, in standard POSIX format. e.g. "de_DE" */ #define PA_PROP_MEDIA_LANGUAGE "media.language" @@ -234,6 +240,14 @@ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value); /** Append a new string entry to the property list, possibly * overwriting an already existing entry with the same key. An * internal copy of the data passed is made. Will accept only valid + * UTF-8. The string passed in must contain a '='. Left hand side of + * the '=' is used as key name, the right hand side as string + * data. \since 0.9.16 */ +int pa_proplist_setp(pa_proplist *p, const char *pair); + +/** Append a new string entry to the property list, possibly + * overwriting an already existing entry with the same key. An + * internal copy of the data passed is made. Will accept only valid * UTF-8. The data can be passed as printf()-style format string with * arguments. \since 0.9.11 */ int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) PA_GCC_PRINTF_ATTR(3,4); diff --git a/src/pulse/sample.c b/src/pulse/sample.c index 1e67b037..0f19f8eb 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -231,13 +231,46 @@ pa_sample_format_t pa_parse_sample_format(const char *format) { else if (strcasecmp(format, "s24re") == 0) return PA_SAMPLE_S24RE; else if (strcasecmp(format, "s24-32le") == 0) - return PA_SAMPLE_S24LE; + return PA_SAMPLE_S24_32LE; else if (strcasecmp(format, "s24-32be") == 0) - return PA_SAMPLE_S24BE; + return PA_SAMPLE_S24_32BE; else if (strcasecmp(format, "s24-32ne") == 0 || strcasecmp(format, "s24-32") == 0) - return PA_SAMPLE_S24NE; + return PA_SAMPLE_S24_32NE; else if (strcasecmp(format, "s24-32re") == 0) - return PA_SAMPLE_S24RE; + return PA_SAMPLE_S24_32RE; return -1; } + +int pa_sample_format_is_le(pa_sample_format_t f) { + pa_assert(f >= PA_SAMPLE_U8); + pa_assert(f < PA_SAMPLE_MAX); + + switch (f) { + case PA_SAMPLE_S16LE: + case PA_SAMPLE_S24LE: + case PA_SAMPLE_S32LE: + case PA_SAMPLE_S24_32LE: + case PA_SAMPLE_FLOAT32LE: + return 1; + + case PA_SAMPLE_S16BE: + case PA_SAMPLE_S24BE: + case PA_SAMPLE_S32BE: + case PA_SAMPLE_S24_32BE: + case PA_SAMPLE_FLOAT32BE: + return 0; + + default: + return -1; + } +} + +int pa_sample_format_is_be(pa_sample_format_t f) { + int r; + + if ((r = pa_sample_format_is_le(f)) < 0) + return r; + + return !r; +} diff --git a/src/pulse/sample.h b/src/pulse/sample.h index 138f13cf..53d7dea3 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -305,6 +305,26 @@ char* pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec); /** Pretty print a byte size value. (i.e. "2.5 MiB") */ char* pa_bytes_snprint(char *s, size_t l, unsigned v); +/** Return 1 when the specified format is little endian, return -1 + * when endianess does not apply to this format. \since 0.9.16 */ +int pa_sample_format_is_le(pa_sample_format_t f) PA_GCC_PURE; + +/** Return 1 when the specified format is big endian, return -1 when + * endianess does not apply to this format. \since 0.9.16 */ +int pa_sample_format_is_be(pa_sample_format_t f) PA_GCC_PURE; + +#ifdef WORDS_BIGENDIAN +#define pa_sample_format_is_ne(f) pa_sample_format_is_be(f) +#define pa_sample_format_is_re(f) pa_sample_format_is_le(f) +#else +/** Return 1 when the specified format is native endian, return -1 + * when endianess does not apply to this format. \since 0.9.16 */ +#define pa_sample_format_is_ne(f) pa_sample_format_is_le(f) +/** Return 1 when the specified format is reverse endian, return -1 + * when endianess does not apply to this format. \since 0.9.16 */ +#define pa_sample_format_is_re(f) pa_sample_format_is_be(f) +#endif + PA_C_DECL_END #endif diff --git a/src/pulse/simple.c b/src/pulse/simple.c index e70b7b1f..f4481fc3 100644 --- a/src/pulse/simple.c +++ b/src/pulse/simple.c @@ -50,35 +50,38 @@ struct pa_simple { int operation_success; }; -#define CHECK_VALIDITY_RETURN_ANY(rerror, expression, error, ret) do { \ -if (!(expression)) { \ - if (rerror) \ - *(rerror) = error; \ - return (ret); \ - } \ -} while(0); - -#define CHECK_SUCCESS_GOTO(p, rerror, expression, label) do { \ -if (!(expression)) { \ - if (rerror) \ - *(rerror) = pa_context_errno((p)->context); \ - goto label; \ - } \ -} while(0); - -#define CHECK_DEAD_GOTO(p, rerror, label) do { \ -if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \ - !(p)->stream || pa_stream_get_state((p)->stream) != PA_STREAM_READY) { \ - if (((p)->context && pa_context_get_state((p)->context) == PA_CONTEXT_FAILED) || \ - ((p)->stream && pa_stream_get_state((p)->stream) == PA_STREAM_FAILED)) { \ - if (rerror) \ - *(rerror) = pa_context_errno((p)->context); \ - } else \ - if (rerror) \ - *(rerror) = PA_ERR_BADSTATE; \ - goto label; \ - } \ -} while(0); +#define CHECK_VALIDITY_RETURN_ANY(rerror, expression, error, ret) \ + do { \ + if (!(expression)) { \ + if (rerror) \ + *(rerror) = error; \ + return (ret); \ + } \ + } while(FALSE); + +#define CHECK_SUCCESS_GOTO(p, rerror, expression, label) \ + do { \ + if (!(expression)) { \ + if (rerror) \ + *(rerror) = pa_context_errno((p)->context); \ + goto label; \ + } \ + } while(FALSE); + +#define CHECK_DEAD_GOTO(p, rerror, label) \ + do { \ + if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \ + !(p)->stream || pa_stream_get_state((p)->stream) != PA_STREAM_READY) { \ + if (((p)->context && pa_context_get_state((p)->context) == PA_CONTEXT_FAILED) || \ + ((p)->stream && pa_stream_get_state((p)->stream) == PA_STREAM_FAILED)) { \ + if (rerror) \ + *(rerror) = pa_context_errno((p)->context); \ + } else \ + if (rerror) \ + *(rerror) = PA_ERR_BADSTATE; \ + goto label; \ + } \ + } while(FALSE); static void context_state_cb(pa_context *c, void *userdata) { pa_simple *p = userdata; @@ -198,9 +201,15 @@ pa_simple* pa_simple_new( pa_stream_set_latency_update_callback(p->stream, stream_latency_update_cb, p); if (dir == PA_STREAM_PLAYBACK) - r = pa_stream_connect_playback(p->stream, dev, attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); + r = pa_stream_connect_playback(p->stream, dev, attr, + PA_STREAM_INTERPOLATE_TIMING + |PA_STREAM_ADJUST_LATENCY + |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); else - r = pa_stream_connect_record(p->stream, dev, attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE); + r = pa_stream_connect_record(p->stream, dev, attr, + PA_STREAM_INTERPOLATE_TIMING + |PA_STREAM_ADJUST_LATENCY + |PA_STREAM_AUTO_TIMING_UPDATE); if (r < 0) { error = pa_context_errno(p->context); diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in index 566dd55e..3143e98e 100644 --- a/src/pulse/version.h.in +++ b/src/pulse/version.h.in @@ -60,6 +60,13 @@ const char* pa_get_library_version(void); /** The micro version of PA. \since 0.9.15 */ #define PA_MICRO @PA_MICRO@ +/** Evaluates to TRUE if the PulseAudio library version is equal or + * newer than the specified. \since 0.9.16 */ +#define PA_CHECK_VERSION(major,minor,micro) \ + ((PA_MAJOR > (major)) || \ + (PA_MAJOR == (major) && CA_MINOR > (minor)) || \ + (PA_MAJOR == (major) && CA_MINOR == (minor) && CA_MICRO >= (micro))) + PA_C_DECL_END #endif 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; +} diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 5b7e1213..c07fd99a 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -178,9 +178,23 @@ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v); /** Return the average volume of all channels */ pa_volume_t pa_cvolume_avg(const pa_cvolume *a) PA_GCC_PURE; +/** Return the average volume of all channels that are included in the + * specified channel map with the specified channel position mask. If + * cm is NULL this call is identical to pa_cvolume_avg(). If no + * channel is selected the returned value will be + * PA_VOLUME_MUTED. \since 0.9.16 */ +pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) PA_GCC_PURE; + /** Return the maximum volume of all channels. \since 0.9.12 */ pa_volume_t pa_cvolume_max(const pa_cvolume *a) PA_GCC_PURE; +/** Return the maximum volume of all channels that are included in the + * specified channel map with the specified channel position mask. If + * cm is NULL this call is identical to pa_cvolume_max(). If no + * channel is selected the returned value will be PA_VOLUME_MUTED. + * \since 0.9.16 */ +pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) PA_GCC_PURE; + /** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */ int pa_cvolume_valid(const pa_cvolume *v) PA_GCC_PURE; @@ -212,10 +226,10 @@ pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) PA_GCC_CONST; * *dest. This is only valid for software volumes! \since 0.9.13 */ pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); -/** Convert a decibel value to a volume. This is only valid for software volumes! */ +/** Convert a decibel value to a volume (amplitude, not power). This is only valid for software volumes! */ pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST; -/** Convert a volume to a decibel value. This is only valid for software volumes! */ +/** Convert a volume to a decibel value (amplitude, not power). This is only valid for software volumes! */ double pa_sw_volume_to_dB(pa_volume_t v) PA_GCC_CONST; /** Convert a linear factor to a volume. This is only valid for software volumes! */ @@ -227,7 +241,7 @@ double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST; #ifdef INFINITY #define PA_DECIBEL_MININFTY ((double) -INFINITY) #else -/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). */ +/** This floor value is used as minus infinity when using pa_volume_{to,from}_dB(). */ #define PA_DECIBEL_MININFTY ((double) -200.0) #endif @@ -283,6 +297,25 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float * volumes are kept. \since 0.9.15 */ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max); +/** Scale the passed pa_cvolume structure so that the maximum volume + * of all channels selected via cm/mask equals max. This also modifies + * the volume of those channels that are unmasked. The proportions + * between the channel volumes are kept. \since 0.9.16 */ +pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map *cm, pa_channel_position_mask_t mask); + +/** Set the passed volume to all channels at the specified channel + * position. Will return the updated volume struct, or NULL if there + * is no channel at the position specified. You can check if a channel + * map includes a specific position by calling + * pa_channel_map_has_position(). \since 0.9.16 */ +pa_cvolume* pa_cvolume_set_position(pa_cvolume *cv, const pa_channel_map *map, pa_channel_position_t t, pa_volume_t v); + +/** Get the maximum volume of all channels at the specified channel + * position. Will return 0 if there is no channel at the position + * specified. You can check if a channel map includes a specific + * position by calling pa_channel_map_has_position(). \since 0.9.16 */ +pa_volume_t pa_cvolume_get_position(pa_cvolume *cv, const pa_channel_map *map, pa_channel_position_t t) PA_GCC_PURE; + PA_C_DECL_END #endif |