summaryrefslogtreecommitdiffstats
path: root/src/pulse
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulse')
-rw-r--r--src/pulse/channelmap.c25
-rw-r--r--src/pulse/channelmap.h59
-rw-r--r--src/pulse/context.c6
-rw-r--r--src/pulse/proplist.c15
-rw-r--r--src/pulse/proplist.h14
-rw-r--r--src/pulse/sample.c41
-rw-r--r--src/pulse/sample.h20
-rw-r--r--src/pulse/simple.c71
-rw-r--r--src/pulse/version.h.in7
-rw-r--r--src/pulse/volume.c175
-rw-r--r--src/pulse/volume.h39
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