diff options
Diffstat (limited to 'src/pulse')
35 files changed, 1099 insertions, 238 deletions
diff --git a/src/pulse/browser.h b/src/pulse/browser.h index c4e0a17e..499fae2e 100644 --- a/src/pulse/browser.h +++ b/src/pulse/browser.h @@ -26,6 +26,7 @@ #include <pulse/sample.h> #include <pulse/channelmap.h> #include <pulse/cdecl.h> +#include <pulse/version.h> /** \file * An abstract interface for Zeroconf browsing of PulseAudio servers */ diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index fd313bd3..455bda1b 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -32,6 +32,7 @@ #include <pulse/i18n.h> #include <pulsecore/core-util.h> #include <pulsecore/macro.h> +#include <pulsecore/bitset.h> #include "channelmap.h" @@ -497,11 +498,58 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { pa_channel_map_init(&map); + /* We don't need to match against the well known channel mapping + * "mono" here explicitly, because that can be understood as + * listing with one channel called "mono". */ + if (strcmp(s, "stereo") == 0) { map.channels = 2; map.map[0] = PA_CHANNEL_POSITION_LEFT; map.map[1] = PA_CHANNEL_POSITION_RIGHT; goto finish; + } else if (strcmp(s, "surround-40") == 0) { + map.channels = 4; + map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + goto finish; + } else if (strcmp(s, "surround-41") == 0) { + map.channels = 5; + map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + map.map[4] = PA_CHANNEL_POSITION_LFE; + goto finish; + } else if (strcmp(s, "surround-50") == 0) { + map.channels = 5; + map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; + goto finish; + } else if (strcmp(s, "surround-51") == 0) { + map.channels = 6; + map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; + map.map[5] = PA_CHANNEL_POSITION_LFE; + goto finish; + } else if (strcmp(s, "surround-71") == 0) { + map.channels = 8; + map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; + map.map[5] = PA_CHANNEL_POSITION_LFE; + map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT; + map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; + goto finish; } state = NULL; @@ -577,3 +625,155 @@ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *s return map->channels == ss->channels; } + +int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) { + pa_bitset_t in_a[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)]; + unsigned i; + + pa_assert(a); + pa_assert(b); + + memset(in_a, 0, sizeof(in_a)); + + for (i = 0; i < a->channels; i++) + pa_bitset_set(in_a, a->map[i], TRUE); + + for (i = 0; i < b->channels; i++) + if (!pa_bitset_get(in_a, b->map[i])) + return 0; + + return 1; +} + +int pa_channel_map_can_balance(const pa_channel_map *map) { + unsigned c; + + pa_assert(map); + + for (c = 0; c < map->channels; c++) + + switch (map->map[c]) { + case PA_CHANNEL_POSITION_LEFT: + case PA_CHANNEL_POSITION_RIGHT: + case PA_CHANNEL_POSITION_REAR_LEFT: + case PA_CHANNEL_POSITION_REAR_RIGHT: + case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: + case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: + case PA_CHANNEL_POSITION_SIDE_LEFT: + case PA_CHANNEL_POSITION_SIDE_RIGHT: + case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: + case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: + case PA_CHANNEL_POSITION_TOP_REAR_LEFT: + case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: + return 1; + + default: + ; + } + + return 0; +} + +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; + + pa_assert(map); + + memset(in_map, 0, sizeof(in_map)); + + for (c = 0; c < map->channels; c++) + pa_bitset_set(in_map, map->map[c], TRUE); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_MONO, -1)) + return "mono"; + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1)) + return "stereo"; + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1)) + return "surround-40"; + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_LFE, -1)) + return "surround-41"; + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, -1)) + return "surround-50"; + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1)) + return "surround-51"; + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, + PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1)) + return "surround-71"; + + return NULL; +} + +const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) { + pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)]; + unsigned c; + + pa_assert(map); + + memset(in_map, 0, sizeof(in_map)); + + for (c = 0; c < map->channels; c++) + pa_bitset_set(in_map, map->map[c], TRUE); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_MONO, -1)) + return _("Mono"); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1)) + return _("Stereo"); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1)) + return _("Surround 4.0"); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_LFE, -1)) + return _("Surround 4.1"); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, -1)) + return _("Surround 5.0"); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1)) + return _("Surround 5.1"); + + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, + PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, + PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1)) + return _("Surround 7.1"); + + return NULL; +} diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index d7d19d79..de2d712a 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -26,6 +26,7 @@ #include <pulse/sample.h> #include <pulse/cdecl.h> #include <pulse/gccmacro.h> +#include <pulse/version.h> /** \page channelmap Channel Maps * @@ -214,7 +215,10 @@ const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos); /** Make a humand readable string from the specified channel map */ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map); -/** Parse a channel position list into a channel map structure. */ +/** Parse a channel position list or well-known mapping name into a + * channel map structure. This turns the output of + * pa_channel_map_snprint() and pa_channel_map_to_name() back into a + * pa_channel_map */ pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s); /** Compare two channel maps. Return 1 if both match. */ @@ -227,6 +231,25 @@ int pa_channel_map_valid(const pa_channel_map *map) PA_GCC_PURE; * the specified sample spec. \since 0.9.12 */ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) PA_GCC_PURE; +/** Returns non-zero if every channel defined in b is also defined in a. \since 0.9.15 */ +int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) PA_GCC_PURE; + +/** Returns non-zero if it makes sense to apply a volume 'balance' + * with this mapping, i.e. if there are left/right channels + * available. \since 0.9.15 */ +int pa_channel_map_can_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 + * with pa_channel_map_parse() \since 0.9.15 */ +const char* pa_channel_map_to_name(const pa_channel_map *map) PA_GCC_PURE; + +/** Tries to find a human readable text label for this channel +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; + PA_C_DECL_END #endif diff --git a/src/pulse/context.c b/src/pulse/context.c index 3145d9c8..d41e62e2 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -366,7 +366,8 @@ int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa if (command == PA_COMMAND_ERROR) { pa_assert(t); - if (pa_tagstruct_getu32(t, &err) < 0) { + if (pa_tagstruct_getu32(t, &err) < 0 || + !pa_tagstruct_eof(t)) { pa_context_fail(c, PA_ERR_PROTOCOL); return -1; } diff --git a/src/pulse/context.h b/src/pulse/context.h index 3b513976..dfb7e4a1 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -29,6 +29,7 @@ #include <pulse/cdecl.h> #include <pulse/operation.h> #include <pulse/proplist.h> +#include <pulse/version.h> /** \page async Asynchronous API * @@ -232,14 +233,14 @@ uint32_t pa_context_get_protocol_version(pa_context *c); /** Return the protocol version of the connected server. */ uint32_t pa_context_get_server_protocol_version(pa_context *c); -/* Update the property list of the client, adding new entries. Please +/** Update the property list of the client, adding new entries. Please * note that it is highly recommended to set as much properties * initially via pa_context_new_with_proplist() as possible instead a * posteriori with this function, since that information may then be * used to route streams of the client to the right device. \since 0.9.11 */ pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata); -/* Update the property list of the client, remove entries. \since 0.9.11 */ +/** Update the property list of the client, remove entries. \since 0.9.11 */ pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata); /** Return the client index this context is diff --git a/src/pulse/def.h b/src/pulse/def.h index ace56574..7517a7aa 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -29,6 +29,7 @@ #include <pulse/cdecl.h> #include <pulse/sample.h> +#include <pulse/version.h> /** \file * Global definitions */ @@ -46,7 +47,7 @@ typedef enum pa_context_state { PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */ } pa_context_state_t; -/** Return non-zero if the passed state is one of the connected states */ +/** Return non-zero if the passed state is one of the connected states. \since 0.9.11 */ static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) { return x == PA_CONTEXT_CONNECTING || @@ -55,6 +56,10 @@ static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) { x == PA_CONTEXT_READY; } +/** \cond fulldocs */ +#define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD +/** \endcond */ + /** The state of a stream */ typedef enum pa_stream_state { PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */ @@ -64,13 +69,17 @@ typedef enum pa_stream_state { PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */ } pa_stream_state_t; -/** Return non-zero if the passed state is one of the connected states */ +/** Return non-zero if the passed state is one of the connected states. \since 0.9.11 */ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) { return x == PA_STREAM_CREATING || x == PA_STREAM_READY; } +/** \cond fulldocs */ +#define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD +/** \endcond */ + /** The state of an operation */ typedef enum pa_operation_state { PA_OPERATION_RUNNING, /**< The operation is still running */ @@ -226,13 +235,13 @@ typedef enum pa_stream_flags { PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND = 0x8000U, /**< If set this stream won't be taken into account when we it is * checked whether the device this stream is connected to should - * auto-suspend. \ since 0.9.14 */ + * auto-suspend. \since 0.9.15 */ PA_STREAM_START_UNMUTED = 0x10000U /**< Create in unmuted state. If neither PA_STREAM_START_UNMUTED * nor PA_STREAM_START_MUTED it is left to the server to decide * whether to create the stream in muted or in unmuted - * state. \since 0.9.14 */ + * state. \since 0.9.15 */ } pa_stream_flags_t; @@ -348,6 +357,7 @@ enum { PA_ERR_NOTSUPPORTED, /**< Operation not supported \since 0.9.5 */ PA_ERR_UNKNOWN, /**< The error code was unknown to the client */ PA_ERR_NOEXTENSION, /**< Extension does not exist. \since 0.9.12 */ + PA_ERR_OBSOLETE, /**< Obsolete functionality. \since 0.9.15 */ PA_ERR_MAX /**< Not really an error but the first invalid error code */ }; @@ -380,10 +390,15 @@ typedef enum pa_subscription_mask { PA_SUBSCRIPTION_MASK_SERVER = 0x0080U, /**< Other global server changes. */ +/** \cond fulldocs */ PA_SUBSCRIPTION_MASK_AUTOLOAD = 0x0100U, - /**< Autoload table events. */ + /**< \deprecated Autoload table events. */ +/** \endcond */ + + PA_SUBSCRIPTION_MASK_CARD = 0x0200U, + /**< Card events. \since 0.9.15 */ - PA_SUBSCRIPTION_MASK_ALL = 0x01ffU + PA_SUBSCRIPTION_MASK_ALL = 0x02ffU /**< Catch all events */ } pa_subscription_mask_t; @@ -413,8 +428,13 @@ typedef enum pa_subscription_event_type { PA_SUBSCRIPTION_EVENT_SERVER = 0x0007U, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. */ +/** \cond fulldocs */ PA_SUBSCRIPTION_EVENT_AUTOLOAD = 0x0008U, - /**< Event type: Autoload table changes. */ + /**< \deprecated Event type: Autoload table changes. */ +/** \endcond */ + + PA_SUBSCRIPTION_EVENT_CARD = 0x0009U, + /**< Event type: Card \since 0.9.15 */ PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 0x000FU, /**< A mask to extract the event type from an event value */ @@ -428,7 +448,7 @@ typedef enum pa_subscription_event_type { PA_SUBSCRIPTION_EVENT_REMOVE = 0x0020U, /**< An object was removed */ - PA_SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U, + PA_SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U /**< A mask to extract the event operation from an event value */ } pa_subscription_event_type_t; @@ -580,9 +600,13 @@ typedef enum pa_sink_flags { PA_SINK_HW_MUTE_CTRL = 0x0010U, /**< Supports hardware mute control \since 0.9.11 */ - PA_SINK_DECIBEL_VOLUME = 0x0020U + PA_SINK_DECIBEL_VOLUME = 0x0020U, /**< Volume can be translated to dB with pa_sw_volume_to_dB() * \since 0.9.11 */ + + PA_SINK_FLAT_VOLUME = 0x0040U + /**< This sink is in flat volume mode, i.e. always the maximum of + * the volume of all connected inputs. \since 0.9.15 */ } pa_sink_flags_t; /** \cond fulldocs */ @@ -590,8 +614,52 @@ typedef enum pa_sink_flags { #define PA_SINK_LATENCY PA_SINK_LATENCY #define PA_SINK_HARDWARE PA_SINK_HARDWARE #define PA_SINK_NETWORK PA_SINK_NETWORK -#define PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL +#define PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL #define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME +#define PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME +/** \endcond */ + +/** Sink state. \since 0.9.15 */ +typedef enum pa_sink_state { /* enum serialized in u8 */ + PA_SINK_INVALID_STATE = -1, + /**< This state is used when the server does not support sink state introspection \since 0.9.15 */ + + PA_SINK_RUNNING = 0, + /**< Running, sink is playing and used by at least one non-corked sink-input \since 0.9.15 */ + + PA_SINK_IDLE = 1, + /**< When idle, the sink is playing but there is no non-corked sink-input attached to it \since 0.9.15 */ + + PA_SINK_SUSPENDED = 2, + /**< When suspended, actual sink access can be closed, for instance \since 0.9.15 */ + +/** \cond fulldocs */ + /* PRIVATE: Server-side values -- DO NOT USE THIS ON THE CLIENT + * SIDE! These values are *not* considered part of the official PA + * API/ABI. If you use them your application might break when PA + * is upgraded. Also, please note that these values are not useful + * on the client side anyway. */ + + PA_SINK_INIT = -2, + /**< Initialization state */ + + PA_SINK_UNLINKED = -3 + /**< The state when the sink is getting unregistered and removed from client access */ +/** \endcond */ + +} pa_sink_state_t; + +/** Returns non-zero if sink is playing: running or idle. \since 0.9.15 */ +static inline int PA_SINK_IS_OPENED(pa_sink_state_t x) { + return x == PA_SINK_RUNNING || x == PA_SINK_IDLE; +} + +/** \cond fulldocs */ +#define PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE +#define PA_SINK_RUNNING PA_SINK_RUNNING +#define PA_SINK_IDLE PA_SINK_IDLE +#define PA_SINK_SUSPENDED PA_SINK_SUSPENDED +#define PA_SINK_IS_OPENED PA_SINK_IS_OPENED /** \endcond */ /** Special source flags. */ @@ -607,7 +675,7 @@ typedef enum pa_source_flags { * "virtual"/software source \since 0.9.3 */ PA_SOURCE_NETWORK = 0x0008U, - /**< Is a networked sink of some kind. \since 0.9.7 */ + /**< Is a networked source of some kind. \since 0.9.7 */ PA_SOURCE_HW_MUTE_CTRL = 0x0010U, /**< Supports hardware mute control \since 0.9.11 */ @@ -622,10 +690,53 @@ typedef enum pa_source_flags { #define PA_SOURCE_LATENCY PA_SOURCE_LATENCY #define PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE #define PA_SOURCE_NETWORK PA_SOURCE_NETWORK -#define PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL +#define PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL #define PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME /** \endcond */ +/** Source state. \since 0.9.15 */ +typedef enum pa_source_state { + PA_SOURCE_INVALID_STATE = -1, + /**< This state is used when the server does not support source state introspection \since 0.9.15 */ + + PA_SOURCE_RUNNING = 0, + /**< Running, source is recording and used by at least one non-corked source-output \since 0.9.15 */ + + PA_SOURCE_IDLE = 1, + /**< When idle, the source is still recording but there is no non-corked source-output \since 0.9.15 */ + + PA_SOURCE_SUSPENDED = 2, + /**< When suspended, actual source access can be closed, for instance \since 0.9.15 */ + +/** \cond fulldocs */ + /* PRIVATE: Server-side values -- DO NOT USE THIS ON THE CLIENT + * SIDE! These values are *not* considered part of the official PA + * API/ABI. If you use them your application might break when PA + * is upgraded. Also, please note that these values are not useful + * on the client side anyway. */ + + PA_SOURCE_INIT = -2, + /**< Initialization state */ + + PA_SOURCE_UNLINKED = -3 + /**< The state when the source is getting unregistered and removed from client access */ +/** \endcond */ + +} pa_source_state_t; + +/** Returns non-zero if source is recording: running or idle. \since 0.9.15 */ +static inline int PA_SOURCE_IS_OPENED(pa_source_state_t x) { + return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE; +} + +/** \cond fulldocs */ +#define PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE +#define PA_SOURCE_RUNNING PA_SOURCE_RUNNING +#define PA_SOURCE_IDLE PA_SOURCE_IDLE +#define PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED +#define PA_SOURCE_IS_OPENED PA_SOURCE_IS_OPENED +/** \endcond */ + /** A generic free() like callback prototype */ typedef void (*pa_free_cb_t)(void *p); diff --git a/src/pulse/error.h b/src/pulse/error.h index 9f9e3d33..c30b80bd 100644 --- a/src/pulse/error.h +++ b/src/pulse/error.h @@ -25,6 +25,7 @@ #include <inttypes.h> #include <pulse/cdecl.h> +#include <pulse/version.h> /** \file * Error management */ diff --git a/src/pulse/ext-stream-restore.h b/src/pulse/ext-stream-restore.h index 2038eb4a..ac01d235 100644 --- a/src/pulse/ext-stream-restore.h +++ b/src/pulse/ext-stream-restore.h @@ -23,6 +23,7 @@ ***/ #include <pulse/context.h> +#include <pulse/version.h> /** \file * diff --git a/src/pulse/gccmacro.h b/src/pulse/gccmacro.h index 0533b109..0b1a1a66 100644 --- a/src/pulse/gccmacro.h +++ b/src/pulse/gccmacro.h @@ -22,6 +22,9 @@ USA. ***/ +/** \file + * GCC attribute macros */ + #ifdef __GNUC__ #define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) #else @@ -100,6 +103,7 @@ #else /** Macro for usage of GCC's alloc_size attribute */ #define PA_GCC_ALLOC_SIZE(x) +/** Macro for usage of GCC's alloc_size attribute */ #define PA_GCC_ALLOC_SIZE2(x,y) #endif #endif diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h index 60fd61a3..fd68f8ac 100644 --- a/src/pulse/glib-mainloop.h +++ b/src/pulse/glib-mainloop.h @@ -27,6 +27,7 @@ #include <pulse/mainloop-api.h> #include <pulse/cdecl.h> +#include <pulse/version.h> /** \page glib-mainloop GLIB Main Loop Bindings * diff --git a/src/pulse/i18n.h b/src/pulse/i18n.h index 4c0ef9d3..f91c0bf9 100644 --- a/src/pulse/i18n.h +++ b/src/pulse/i18n.h @@ -24,6 +24,7 @@ #include <pulse/cdecl.h> #include <pulse/gccmacro.h> +#include <pulse/version.h> PA_C_DECL_BEGIN diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 5fe4210e..9a2d6457 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -225,20 +225,37 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag); -#define PA_CHECK_VALIDITY(context, expression, error) do { \ - if (!(expression)) \ +#define PA_CHECK_VALIDITY(context, expression, error) \ + do { \ + if (!(expression)) \ return -pa_context_set_error((context), (error)); \ -} while(0) + } while(FALSE) -#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) do { \ - if (!(expression)) { \ - pa_context_set_error((context), (error)); \ - return value; \ - } \ -} while(0) +#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) \ + do { \ + if (!(expression)) { \ + pa_context_set_error((context), (error)); \ + return value; \ + } \ + } while(FALSE) -#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL) +#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) \ + PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL) + +#define PA_FAIL(context, error) \ + do { \ + return -pa_context_set_error((context), (error)); \ + } while(FALSE) + +#define PA_FAIL_RETURN_ANY(context, error, value) \ + do { \ + pa_context_set_error((context), (error)); \ + return value; \ + } while(FALSE) + +#define PA_FAIL_RETURN_NULL(context, error) \ + PA_FAIL_RETURN_ANY(context, error, NULL) void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t); diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 8056a5a1..1d50939c 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -28,8 +28,10 @@ #include <pulse/context.h> #include <pulse/gccmacro.h> +#include <pulse/xmalloc.h> #include <pulsecore/macro.h> +#include <pulsecore/core-util.h> #include <pulsecore/pstream-util.h> #include "internal.h" @@ -60,7 +62,8 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, uint32_t t pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 || pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 || pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 || - pa_tagstruct_getu32(t, &i.scache_size) < 0) { + pa_tagstruct_getu32(t, &i.scache_size) < 0 || + !pa_tagstruct_eof(t)) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } @@ -146,15 +149,19 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u eol = -1; } else { - uint32_t flags; while (!pa_tagstruct_eof(t)) { pa_sink_info i; - pa_bool_t mute = FALSE; + pa_bool_t mute; + uint32_t flags; + uint32_t state; memset(&i, 0, sizeof(i)); i.proplist = pa_proplist_new(); i.base_volume = PA_VOLUME_NORM; + i.n_volume_steps = PA_VOLUME_NORM+1; + mute = FALSE; + state = PA_SINK_INVALID_STATE; if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || @@ -173,7 +180,9 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u (pa_tagstruct_get_proplist(t, i.proplist) < 0 || pa_tagstruct_get_usec(t, &i.configured_latency) < 0)) || (o->context->version >= 15 && - pa_tagstruct_get_volume(t, &i.base_volume) < 0)) { + (pa_tagstruct_get_volume(t, &i.base_volume) < 0 || + pa_tagstruct_getu32(t, &state) < 0 || + pa_tagstruct_getu32(t, &i.n_volume_steps) < 0))) { pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_proplist_free(i.proplist); @@ -182,6 +191,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u i.mute = (int) mute; i.flags = (pa_sink_flags_t) flags; + i.state = (pa_sink_state_t) state; if (o->callback) { pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback; @@ -273,12 +283,16 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, while (!pa_tagstruct_eof(t)) { pa_source_info i; + pa_bool_t mute; uint32_t flags; - pa_bool_t mute = FALSE; + uint32_t state; memset(&i, 0, sizeof(i)); i.proplist = pa_proplist_new(); i.base_volume = PA_VOLUME_NORM; + i.n_volume_steps = PA_VOLUME_NORM+1; + mute = FALSE; + state = PA_SOURCE_INVALID_STATE; if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || @@ -297,7 +311,9 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, (pa_tagstruct_get_proplist(t, i.proplist) < 0 || pa_tagstruct_get_usec(t, &i.configured_latency) < 0)) || (o->context->version >= 15 && - pa_tagstruct_get_volume(t, &i.base_volume) < 0)) { + (pa_tagstruct_get_volume(t, &i.base_volume) < 0 || + pa_tagstruct_getu32(t, &state) < 0 || + pa_tagstruct_getu32(t, &i.n_volume_steps) < 0))) { pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_proplist_free(i.proplist); @@ -306,6 +322,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, i.mute = (int) mute; i.flags = (pa_source_flags_t) flags; + i.state = (pa_source_state_t) state; if (o->callback) { pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback; @@ -457,6 +474,201 @@ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, (pa_operation_cb_t) cb, userdata); } +/*** Card info ***/ + +static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eol = 1; + + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) + goto finish; + + eol = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_card_info i; + uint32_t j; + const char*ap; + + memset(&i, 0, sizeof(i)); + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_getu32(t, &i.owner_module) < 0 || + pa_tagstruct_gets(t, &i.driver) < 0 || + pa_tagstruct_getu32(t, &i.n_profiles) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (i.n_profiles > 0) { + i.profiles = pa_xnew(pa_card_profile_info, i.n_profiles+1); + + for (j = 0; j < i.n_profiles; j++) { + + if (pa_tagstruct_gets(t, &i.profiles[j].name) < 0 || + pa_tagstruct_gets(t, &i.profiles[j].description) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_xfree(i.profiles); + goto finish; + } + } + + /* Terminate with an extra NULL entry, just to make sure */ + i.profiles[j].name = NULL; + i.profiles[j].description = NULL; + } + + i.proplist = pa_proplist_new(); + + if (pa_tagstruct_gets(t, &ap) < 0 || + pa_tagstruct_get_proplist(t, i.proplist) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_xfree(i.profiles); + pa_proplist_free(i.proplist); + goto finish; + } + + if (ap) { + for (j = 0; j < i.n_profiles; j++) + if (pa_streq(i.profiles[j].name, ap)) { + i.active_profile = &i.profiles[j]; + break; + } + } + + if (o->callback) { + pa_card_info_cb_t cb = (pa_card_info_cb_t) o->callback; + cb(o->context, &i, 0, o->userdata); + } + + pa_proplist_free(i.proplist); + pa_xfree(i.profiles); + } + } + + if (o->callback) { + pa_card_info_cb_t cb = (pa_card_info_cb_t) o->callback; + cb(o->context, NULL, eol, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_CARD_INFO, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_card_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char*name, pa_card_info_cb_t cb, void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(cb); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_GET_CARD_INFO, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_card_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_CARD_INFO_LIST, context_get_card_info_callback, (pa_operation_cb_t) cb, userdata); +} + +pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_CARD_PROFILE, &tag); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_tagstruct_puts(t, profile); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char *name, const char*profile, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_CARD_PROFILE, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_tagstruct_puts(t, profile); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + /*** Module info ***/ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { @@ -480,13 +692,16 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, while (!pa_tagstruct_eof(t)) { pa_module_info i; pa_bool_t auto_unload = FALSE; + memset(&i, 0, sizeof(i)); + i.proplist = pa_proplist_new(); if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_gets(t, &i.argument) < 0 || pa_tagstruct_getu32(t, &i.n_used) < 0 || - pa_tagstruct_get_boolean(t, &auto_unload) < 0) { + (o->context->version < 15 && pa_tagstruct_get_boolean(t, &auto_unload) < 0) || + (o->context->version >= 15 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } @@ -497,6 +712,8 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback; cb(o->context, &i, 0, o->userdata); } + + pa_proplist_free(i.proplist); } } @@ -1167,186 +1384,59 @@ pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_s /*** Autoload stuff ***/ -static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - int eol = 1; - - pa_assert(pd); - pa_assert(o); - pa_assert(PA_REFCNT_VALUE(o) >= 1); - - if (!o->context) - goto finish; - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t, FALSE) < 0) - goto finish; - - eol = -1; - } else { - - while (!pa_tagstruct_eof(t)) { - pa_autoload_info i; - - memset(&i, 0, sizeof(i)); - - if (pa_tagstruct_getu32(t, &i.index) < 0 || - pa_tagstruct_gets(t, &i.name) < 0 || - pa_tagstruct_getu32(t, &i.type) < 0 || - pa_tagstruct_gets(t, &i.module) < 0 || - pa_tagstruct_gets(t, &i.argument) < 0) { - pa_context_fail(o->context, PA_ERR_PROTOCOL); - goto finish; - } - - if (o->callback) { - pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback; - cb(o->context, NULL, eol, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server."); +PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Module auto-loading no longer supported."); pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); - pa_assert(cb); - - PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); - PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID); - o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); - - t = pa_tagstruct_command(c, PA_COMMAND_GET_AUTOLOAD_INFO, &tag); - pa_tagstruct_puts(t, name); - pa_tagstruct_putu32(t, type); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); - - return o; + PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE); } -PA_WARN_REFERENCE(pa_context_get_autoload_info_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server."); +PA_WARN_REFERENCE(pa_context_get_autoload_info_by_index, "Module auto-loading no longer supported."); pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); - pa_assert(cb); - - PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); - - o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); - - t = pa_tagstruct_command(c, PA_COMMAND_GET_AUTOLOAD_INFO, &tag); - pa_tagstruct_putu32(t, idx); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); - return o; + PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE); } - -PA_WARN_REFERENCE(pa_context_get_autoload_info_list, "Autoload will no longer be implemented by future versions of the PulseAudio server."); +PA_WARN_REFERENCE(pa_context_get_autoload_info_list, "Module auto-loading no longer supported."); pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_cb_t) cb, userdata); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE); } -PA_WARN_REFERENCE(pa_context_add_autoload, "Autoload will no longer be implemented by future versions of the PulseAudio server."); +PA_WARN_REFERENCE(pa_context_add_autoload, "Module auto-loading no longer supported."); pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); - PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); - PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID); - PA_CHECK_VALIDITY_RETURN_NULL(c, module && *module, PA_ERR_INVALID); - - o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); - - t = pa_tagstruct_command(c, PA_COMMAND_ADD_AUTOLOAD, &tag); - pa_tagstruct_puts(t, name); - pa_tagstruct_putu32(t, type); - pa_tagstruct_puts(t, module); - pa_tagstruct_puts(t, argument); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_index_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); - - return o; + PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE); } -PA_WARN_REFERENCE(pa_context_remove_autoload_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server."); +PA_WARN_REFERENCE(pa_context_remove_autoload_by_name, "Module auto-loading no longer supported."); pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); - PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID); - PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID); - - o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); - - t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_AUTOLOAD, &tag); - pa_tagstruct_puts(t, name); - pa_tagstruct_putu32(t, type); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); - - return o; + PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE); } -PA_WARN_REFERENCE(pa_context_remove_autoload_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server."); +PA_WARN_REFERENCE(pa_context_remove_autoload_by_index, "Module auto-loading no longer supported."); pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); - PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID); - - o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); - - t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_AUTOLOAD, &tag); - pa_tagstruct_putu32(t, idx); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); - - return o; + PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE); } pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) { diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index b409cadb..badc787e 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -32,6 +32,7 @@ #include <pulse/channelmap.h> #include <pulse/volume.h> #include <pulse/proplist.h> +#include <pulse/version.h> /** \page introspect Server Query and Control * @@ -128,18 +129,6 @@ * pa_context_get_module_info() or pa_context_get_module_info_list(). The * information structure is called pa_module_info. * - * \subsection autoload_subsec Autoload Entries - * - * Modules can be autoloaded as a result of a client requesting a - * certain sink or source. Please note that autoloading is deprecated - * in 0.9.11. and is likely to be removed from the API in a later - * version. This mapping between sink/source names and modules can be - * queried from the server: - * - * \li By index - pa_context_get_autoload_info_by_index() - * \li By sink/source name - pa_context_get_autoload_info_by_name() - * \li All - pa_context_get_autoload_info_list() - * * \subsection client_subsec Clients * * PulseAudio clients are also identified by index and are retrieved using @@ -189,14 +178,6 @@ * Server modules can be remotely loaded and unloaded using * pa_context_load_module() and pa_context_unload_module(). * - * \subsection autoload_subsec Autoload Entries - * - * New module autoloading rules can be added, and existing can be removed - * using pa_context_add_autoload() and pa_context_remove_autoload_by_index() - * / pa_context_remove_autoload_by_name(). Please note that autoloading is deprecated - * in 0.9.11. and is likely to be removed from the API in a later - * version. - * * \subsection client_subsec Clients * * The only operation supported on clients, is the possibility of kicking @@ -231,7 +212,9 @@ typedef struct pa_sink_info { pa_sink_flags_t flags; /**< Flags */ pa_proplist *proplist; /**< Property list \since 0.9.11 */ pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */ - pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.14 */ + pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.15 */ + pa_sink_state_t state; /**< State \since 0.9.15 */ + uint32_t n_volume_steps; /**< Number of volume steps for sinks which do not support arbitrary volumes. \since 0.9.15 */ } pa_sink_info; /** Callback prototype for pa_context_get_sink_info_by_name() and friends */ @@ -241,7 +224,7 @@ typedef void (*pa_sink_info_cb_t)(pa_context *c, const pa_sink_info *i, int eol, pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata); /** Get information about a sink by its index */ -pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, pa_sink_info_cb_t cb, void *userdata); +pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata); /** Get the complete sink list */ pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata); @@ -287,7 +270,9 @@ typedef struct pa_source_info { pa_source_flags_t flags; /**< Flags */ pa_proplist *proplist; /**< Property list \since 0.9.11 */ pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */ - pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.14 */ + pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.15 */ + pa_source_state_t state; /**< State \since 0.9.15 */ + uint32_t n_volume_steps; /**< Number of volume steps for sources which do not support arbitrary volumes. \since 0.9.15 */ } pa_source_info; /** Callback prototype for pa_context_get_source_info_by_name() and friends */ @@ -297,7 +282,7 @@ typedef void (*pa_source_info_cb_t)(pa_context *c, const pa_source_info *i, int pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata); /** Get information about a source by its index */ -pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, pa_source_info_cb_t cb, void *userdata); +pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata); /** Get the complete source list */ pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata); @@ -350,7 +335,10 @@ typedef struct pa_module_info { const char*name, /**< Name of the module */ *argument; /**< Argument string of the module */ uint32_t n_used; /**< Usage counter or PA_INVALID_INDEX */ - int auto_unload; /**< Non-zero if this is an autoloaded module */ +/** \cond fulldocs */ + int auto_unload; /**< \deprecated Non-zero if this is an autoloaded module */ +/** \endcond */ + pa_proplist *proplist; /**< Property list \since 0.9.15 */ } pa_module_info; /** Callback prototype for pa_context_get_module_info() and firends*/ @@ -400,6 +388,50 @@ pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_suc /** @} */ +/** @{ \name Cards */ + +/** Stores information about a specific profile of a card. Please + * note that this structure can be extended as part of evolutionary + * API updates at any time in any new release. \since 0.9.15 */ +typedef struct pa_card_profile_info { + const char *name; /**< Name of this profile */ + const char *description; /**< Description of this profile */ +} pa_card_profile_info; + +/** Stores information about cards. Please note that this structure + * can be extended as part of evolutionary API updates at any time in + * any new release. \since 0.9.15 */ +typedef struct pa_card_info { + uint32_t index; /**< Index of this card */ + const char *name; /**< Name of this card */ + uint32_t owner_module; /**< Index of the owning module, or PA_INVALID_INDEX */ + const char *driver; /**< Driver name */ + uint32_t n_profiles; /**< Number of entries in profile array */ + pa_card_profile_info* profiles; /**< Array of available profile, or NULL. Array is terminated by an entry with name set to NULL. Number of entries is stored in n_profiles */ + pa_card_profile_info* active_profile; /**< Pointer to active profile in the array, or NULL */ + pa_proplist *proplist; /**< Property list */ +} pa_card_info; + +/** Callback prototype for pa_context_get_card_info() and firends \since 0.9.15 */ +typedef void (*pa_card_info_cb_t) (pa_context *c, const pa_card_info*i, int eol, void *userdata); + +/** Get information about a card by its index \since 0.9.15 */ +pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata); + +/** Get information about a card by its name \since 0.9.15 */ +pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, pa_card_info_cb_t cb, void *userdata); + +/** Get the complete card list \since 0.9.15 */ +pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata); + +/** Change the profile of a card. \since 0.9.15 */ +pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata); + +/** Change the profile of a card. \since 0.9.15 */ +pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name, const char*profile, pa_context_success_cb_t cb, void *userdata); + +/** @} */ + /** @{ \name Sink Inputs */ /** Stores information about sink inputs. Please note that this structure @@ -551,13 +583,13 @@ pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t /** @{ \name Autoload Entries */ -/** Type of an autoload entry. */ +/** \deprecated Type of an autoload entry. */ typedef enum pa_autoload_type { PA_AUTOLOAD_SINK = 0, PA_AUTOLOAD_SOURCE = 1 } pa_autoload_type_t; -/** Stores information about autoload entries. Please note that this structure +/** \deprecated Stores information about autoload entries. Please note that this structure * can be extended as part of evolutionary API updates at any time in * any new release. */ typedef struct pa_autoload_info { @@ -568,25 +600,25 @@ typedef struct pa_autoload_info { const char *argument; /**< Argument string for module */ } pa_autoload_info; -/** Callback prototype for pa_context_get_autoload_info_by_name() and firends */ +/** \deprecated Callback prototype for pa_context_get_autoload_info_by_name() and firends */ typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata); -/** Get info about a specific autoload entry. */ +/** \deprecated Get info about a specific autoload entry. */ pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED; -/** Get info about a specific autoload entry. */ +/** \deprecated Get info about a specific autoload entry. */ pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED; -/** Get the complete list of autoload entries. */ +/** \deprecated Get the complete list of autoload entries. */ pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED; -/** Add a new autoload entry. */ +/** \deprecated Add a new autoload entry. */ pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata) PA_GCC_DEPRECATED; -/** Remove an autoload entry. */ +/** \deprecated Remove an autoload entry. */ pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED; -/** Remove an autoload entry. */ +/** \deprecated Remove an autoload entry. */ pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED; /** @} */ diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h index 53c7411e..e353ed96 100644 --- a/src/pulse/mainloop-api.h +++ b/src/pulse/mainloop-api.h @@ -27,6 +27,7 @@ #include <time.h> #include <pulse/cdecl.h> +#include <pulse/version.h> /** \file * diff --git a/src/pulse/mainloop-signal.h b/src/pulse/mainloop-signal.h index a6c16f2f..a9e250bc 100644 --- a/src/pulse/mainloop-signal.h +++ b/src/pulse/mainloop-signal.h @@ -40,8 +40,10 @@ PA_C_DECL_BEGIN /** An opaque UNIX signal event source object */ typedef struct pa_signal_event pa_signal_event; +/** Callback prototype for signal events */ typedef void (*pa_signal_cb_t) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata); +/** Destroy callback prototype for signal events */ typedef void (*pa_signal_destroy_cb_t) (pa_mainloop_api *api, pa_signal_event*e, void *userdata); /** Initialize the UNIX signal subsystem and bind it to the specified main loop */ diff --git a/src/pulse/operation.h b/src/pulse/operation.h index 188e2cb9..b68e7816 100644 --- a/src/pulse/operation.h +++ b/src/pulse/operation.h @@ -24,6 +24,7 @@ #include <pulse/cdecl.h> #include <pulse/def.h> +#include <pulse/version.h> /** \file * Asynchronous operations */ diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index 93bc0034..282fe5cc 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -259,21 +259,24 @@ const char *pa_proplist_iterate(pa_proplist *p, void **state) { return prop->key; } -char *pa_proplist_to_string(pa_proplist *p) { +char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) { const char *key; void *state = NULL; pa_strbuf *buf; pa_assert(p); + pa_assert(sep); buf = pa_strbuf_new(); while ((key = pa_proplist_iterate(p, &state))) { - const char *v; + if (!pa_strbuf_isempty(buf)) + pa_strbuf_puts(buf, sep); + if ((v = pa_proplist_gets(p, key))) - pa_strbuf_printf(buf, "%s = \"%s\"\n", key, v); + pa_strbuf_printf(buf, "%s = \"%s\"", key, v); else { const void *value; size_t nbytes; @@ -283,7 +286,7 @@ char *pa_proplist_to_string(pa_proplist *p) { c = pa_xmalloc(nbytes*2+1); pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1); - pa_strbuf_printf(buf, "%s = hex:%s\n", key, c); + pa_strbuf_printf(buf, "%s = hex:%s", key, c); pa_xfree(c); } } @@ -291,6 +294,100 @@ char *pa_proplist_to_string(pa_proplist *p) { return pa_strbuf_tostring_free(buf); } +char *pa_proplist_to_string(pa_proplist *p) { + char *s, *t; + + s = pa_proplist_to_string_sep(p, "\n"); + t = pa_sprintf_malloc("%s\n", s); + pa_xfree(s); + + return t; +} + +/* Remove all whitepsapce from the beginning and the end of *s. *s may + * be modified. (from conf-parser.c) */ +#define WHITESPACE " \t\n" +#define in_string(c,s) (strchr(s,c) != NULL) + +static char *strip(char *s) { + char *b = s+strspn(s, WHITESPACE); + char *e, *l = NULL; + + for (e = b; *e; e++) + if (!in_string(*e, WHITESPACE)) + l = e; + + if (l) + *(l+1) = 0; + + return b; +} + +pa_proplist *pa_proplist_from_string(const char *str) { + pa_proplist *p; + char *s, *v, *k, *e; + + pa_assert(str); + pa_assert_se(p = pa_proplist_new()); + pa_assert_se(s = strdup(str)); + + for (k = s; *k; k = e) { + k = k+strspn(k, WHITESPACE); + + if (!*k) + break; + + if (!(v = strchr(k, '='))) { + pa_log("Missing '='."); + break; + } + + *v++ = '\0'; + k = strip(k); + + v = v+strspn(v, WHITESPACE); + if (*v == '"') { + v++; + if (!(e = strchr(v, '"'))) { /* FIXME: handle escape */ + pa_log("Missing '\"' at end of string value."); + break; + } + *e++ = '\0'; + pa_proplist_sets(p, k, v); + } else { + uint8_t *blob; + + if (*v++ != 'h' || *v++ != 'e' || *v++ != 'x' || *v++ != ':') { + pa_log("Value must be a string or \"hex:\""); + break; + } + + e = v; + while (in_string(*e, "0123456789abcdefABCDEF")) + ++e; + + if ((e - v) % 2) { + pa_log("Invalid \"hex:\" value data"); + break; + } + + blob = pa_xmalloc((size_t)(e-v)/2); + if (pa_parsehex(v, blob, (e-v)/2) != (size_t)((e-v)/2)) { + pa_log("Invalid \"hex:\" value data"); + pa_xfree(blob); + break; + } + + pa_proplist_set(p, k, blob, (e-v)/2); + pa_xfree(blob); + } + } + + pa_xfree(s); + + return p; +} + int pa_proplist_contains(pa_proplist *p, const char *key) { pa_assert(p); pa_assert(key); @@ -322,3 +419,15 @@ pa_proplist* pa_proplist_copy(pa_proplist *template) { return p; } + +unsigned pa_proplist_size(pa_proplist *p) { + pa_assert(p); + + return pa_hashmap_size(MAKE_HASHMAP(p)); +} + +int pa_proplist_isempty(pa_proplist *p) { + pa_assert(p); + + return pa_hashmap_isempty(MAKE_HASHMAP(p)); +} diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index c23ef238..203a28c5 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -26,6 +26,7 @@ #include <pulse/cdecl.h> #include <pulse/gccmacro.h> +#include <pulse/version.h> PA_C_DECL_BEGIN @@ -75,8 +76,10 @@ PA_C_DECL_BEGIN * device.connector isa, pci, usb, firewire, bluetooth * device.access_mode mmap, mmap_rewrite, serial * device.master_device - * device.bufferin.buffer_size - * device.bufferin.fragment_size + * device.buffering.buffer_size + * device.buffering.fragment_size + * device.profile.name analog-stereo, analog-surround-40, iec958-stereo, ... + * device.profile.description "Analog Stereo", ... */ #define PA_PROP_MEDIA_NAME "media.name" #define PA_PROP_MEDIA_TITLE "media.title" @@ -124,6 +127,12 @@ PA_C_DECL_BEGIN #define PA_PROP_DEVICE_MASTER_DEVICE "device.master_device" #define PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE "device.buffering.buffer_size" #define PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE "device.buffering.fragment_size" +#define PA_PROP_DEVICE_PROFILE_NAME "device.profile.name" +#define PA_PROP_DEVICE_PROFILE_DESCRIPTION "device.profile.description" +#define PA_PROP_MODULE_AUTHOR "module.author" +#define PA_PROP_MODULE_DESCRIPTION "module.description" +#define PA_PROP_MODULE_USAGE "module.usage" +#define PA_PROP_MODULE_VERSION "module.version" /** A property list object. Basically a dictionary with UTF-8 strings * as keys and arbitrary data as values. \since 0.9.11 */ @@ -153,7 +162,7 @@ int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) P * internal copy of the data passed is made. \since 0.9.11 */ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes); -/* Return a string entry for the specified key. Will return NULL if +/** Return a string entry for the specified key. Will return NULL if * the data is not valid UTF-8. Will return a NUL-terminated string in * an internally allocated buffer. The caller should make a copy of * the data before accessing the property list again. \since 0.9.11 */ @@ -209,11 +218,22 @@ int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]); * have any particular order. \since 0.9.11 */ const char *pa_proplist_iterate(pa_proplist *p, void **state); -/** Format the property list nicely as a human readable string. Call pa_xfree() on the result. \since - * 0.9.11 */ +/** Format the property list nicely as a human readable string. This + * works very much like pa_proplist_to_string_sep() and uses a newline + * as seperator and appends one final one. Call pa_xfree() on the + * result. \since 0.9.11 */ char *pa_proplist_to_string(pa_proplist *p); -/** Returns 1 if an entry for the specified key is existant in the +/** Format the property list nicely as a human readable string and + * choose the seperator. Call pa_xfree() on the result. \since + * 0.9.15 */ +char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep); + +/** Allocate a new property list and assign key/value from a human + * readable string. \since 0.9.15 */ +pa_proplist *pa_proplist_from_string(const char *str); + + /** Returns 1 if an entry for the specified key is existant in the * property list. \since 0.9.11 */ int pa_proplist_contains(pa_proplist *p, const char *key); @@ -224,6 +244,12 @@ void pa_proplist_clear(pa_proplist *p); * the specific list. \since 0.9.11 */ pa_proplist* pa_proplist_copy(pa_proplist *t); +/** Return the number of entries on the property list. \since 0.9.15 */ +unsigned pa_proplist_size(pa_proplist *t); + +/** Returns 0 when the proplist is empty, positive otherwise \since 0.9.15 */ +int pa_proplist_isempty(pa_proplist *t); + PA_C_DECL_END #endif diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h index e09cacaf..5086783d 100644 --- a/src/pulse/pulseaudio.h +++ b/src/pulse/pulseaudio.h @@ -108,7 +108,6 @@ * modules: * * \li libpulse - The asynchronous API and the internal main loop implementation. - * \li libpulse-mainloop-glib12 - GLIB 1.2 main loop bindings. * \li libpulse-mainloop-glib - GLIB 2.x main loop bindings. * \li libpulse-simple - The simple PulseAudio API. */ diff --git a/src/pulse/sample.c b/src/pulse/sample.c index 29501595..a6c77345 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -48,6 +48,10 @@ size_t pa_sample_size(const pa_sample_spec *spec) { [PA_SAMPLE_FLOAT32BE] = 4, [PA_SAMPLE_S32LE] = 4, [PA_SAMPLE_S32BE] = 4, + [PA_SAMPLE_S24LE] = 3, + [PA_SAMPLE_S24BE] = 3, + [PA_SAMPLE_S24_32LE] = 4, + [PA_SAMPLE_S24_32BE] = 4 }; pa_assert(spec); @@ -125,6 +129,10 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) { [PA_SAMPLE_FLOAT32BE] = "float32be", [PA_SAMPLE_S32LE] = "s32le", [PA_SAMPLE_S32BE] = "s32be", + [PA_SAMPLE_S24LE] = "s24le", + [PA_SAMPLE_S24BE] = "s24be", + [PA_SAMPLE_S24_32LE] = "s24-32le", + [PA_SAMPLE_S24_32BE] = "s24-32be", }; if (f < 0 || f >= PA_SAMPLE_MAX) @@ -195,7 +203,23 @@ pa_sample_format_t pa_parse_sample_format(const char *format) { else if (strcasecmp(format, "s32ne") == 0 || strcasecmp(format, "s32") == 0 || strcasecmp(format, "32") == 0) return PA_SAMPLE_S32NE; else if (strcasecmp(format, "s32re") == 0) - return PA_SAMPLE_S32RE; + return PA_SAMPLE_S24RE; + else if (strcasecmp(format, "s24le") == 0) + return PA_SAMPLE_S24LE; + else if (strcasecmp(format, "s24be") == 0) + return PA_SAMPLE_S24BE; + else if (strcasecmp(format, "s24ne") == 0 || strcasecmp(format, "s24") == 0 || strcasecmp(format, "24") == 0) + return PA_SAMPLE_S24NE; + else if (strcasecmp(format, "s24re") == 0) + return PA_SAMPLE_S24RE; + else if (strcasecmp(format, "s24-32le") == 0) + return PA_SAMPLE_S24LE; + else if (strcasecmp(format, "s24-32be") == 0) + return PA_SAMPLE_S24BE; + else if (strcasecmp(format, "s24-32ne") == 0 || strcasecmp(format, "s24-32") == 0) + return PA_SAMPLE_S24NE; + else if (strcasecmp(format, "s24-32re") == 0) + return PA_SAMPLE_S24RE; return -1; } diff --git a/src/pulse/sample.h b/src/pulse/sample.h index 3c7dd0e7..45a481fe 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -30,6 +30,7 @@ #include <pulse/gccmacro.h> #include <pulse/cdecl.h> +#include <pulse/version.h> /** \page sample Sample Format Specifications * @@ -51,6 +52,10 @@ * \li PA_SAMPLE_ULAW - 8 bit mu-Law. * \li PA_SAMPLE_S32LE - Signed 32 bit integer PCM, little endian. * \li PA_SAMPLE_S32BE - Signed 32 bit integer PCM, big endian. + * \li PA_SAMPLE_S24LE - Signed 24 bit integer PCM packed, little endian. + * \li PA_SAMPLE_S24BE - Signed 24 bit integer PCM packed, big endian. + * \li PA_SAMPLE_S24_32LE - Signed 24 bit integer PCM in LSB of 32 bit words, little endian. + * \li PA_SAMPLE_S24_32BE - Signed 24 bit integer PCM in LSB of 32 bit words, big endian. * * The floating point sample formats have the range from -1.0 to 1.0. * @@ -59,14 +64,14 @@ * * \section rate_sec Sample Rates * - * PulseAudio supports any sample rate between 1 Hz and 4 GHz. There is no + * PulseAudio supports any sample rate between 1 Hz and 192000 Hz. There is no * point trying to exceed the sample rate of the output device though as the * signal will only get downsampled, consuming CPU on the machine running the * server. * * \section chan_sec Channels * - * PulseAudio supports up to 16 individiual channels. The order of the + * PulseAudio supports up to 32 individiual channels. The order of the * channels is up to the application, but they must be continous. To map * channels to speakers, see \ref channelmap. * @@ -136,16 +141,28 @@ typedef enum pa_sample_format { /**< Signed 16 Bit PCM, big endian */ PA_SAMPLE_FLOAT32LE, - /**< 32 Bit IEEE floating point, little endian, range -1 to 1 */ + /**< 32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0 */ PA_SAMPLE_FLOAT32BE, - /**< 32 Bit IEEE floating point, big endian, range -1 to 1 */ + /**< 32 Bit IEEE floating point, big endian, range -1.0 to 1.0 */ PA_SAMPLE_S32LE, /**< Signed 32 Bit PCM, little endian (PC) */ PA_SAMPLE_S32BE, - /**< Signed 32 Bit PCM, big endian (PC) */ + /**< Signed 32 Bit PCM, big endian */ + + PA_SAMPLE_S24LE, + /**< Signed 24 Bit PCM packed, little endian (PC). \since 0.9.15 */ + + PA_SAMPLE_S24BE, + /**< Signed 24 Bit PCM packed, big endian. \since 0.9.15 */ + + PA_SAMPLE_S24_32LE, + /**< Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC). \since 0.9.15 */ + + PA_SAMPLE_S24_32BE, + /**< Signed 24 Bit PCM in LSB of 32 Bit words, big endian. \since 0.9.15 */ PA_SAMPLE_MAX, /**< Upper limit of valid sample types */ @@ -161,12 +178,21 @@ typedef enum pa_sample_format { #define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE /** Signed 32 Bit PCM, native endian */ #define PA_SAMPLE_S32NE PA_SAMPLE_S32BE +/** Signed 24 Bit PCM packed, native endian. \since 0.9.15 */ +#define PA_SAMPLE_S24NE PA_SAMPLE_S24BE +/** Signed 24 Bit PCM in LSB of 32 Bit words, native endian. \since 0.9.15 */ +#define PA_SAMPLE_S24_32NE PA_SAMPLE_S24_32BE + /** Signed 16 Bit PCM reverse endian */ #define PA_SAMPLE_S16RE PA_SAMPLE_S16LE /** 32 Bit IEEE floating point, reverse endian */ #define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE -/** Signed 32 Bit PCM reverse endian */ +/** Signed 32 Bit PCM, reverse endian */ #define PA_SAMPLE_S32RE PA_SAMPLE_S32LE +/** Signed 24 Bit PCM, packed reverse endian. \since 0.9.15 */ +#define PA_SAMPLE_S24RE PA_SAMPLE_S24LE +/** Signed 24 Bit PCM, in LSB of 32 Bit words, reverse endian. \since 0.9.15 */ +#define PA_SAMPLE_S24_32RE PA_SAMPLE_S24_32LE #else /** Signed 16 Bit PCM, native endian */ #define PA_SAMPLE_S16NE PA_SAMPLE_S16LE @@ -174,12 +200,21 @@ typedef enum pa_sample_format { #define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE /** Signed 32 Bit PCM, native endian */ #define PA_SAMPLE_S32NE PA_SAMPLE_S32LE -/** Signed 16 Bit PCM reverse endian */ +/** Signed 24 Bit PCM packed, native endian. \since 0.9.15 */ +#define PA_SAMPLE_S24NE PA_SAMPLE_S24LE +/** Signed 24 Bit PCM in LSB of 32 Bit words, native endian. \since 0.9.15 */ +#define PA_SAMPLE_S24_32NE PA_SAMPLE_S24_32LE + +/** Signed 16 Bit PCM, reverse endian */ #define PA_SAMPLE_S16RE PA_SAMPLE_S16BE /** 32 Bit IEEE floating point, reverse endian */ #define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE -/** Signed 32 Bit PCM reverse endian */ +/** Signed 32 Bit PCM, reverse endian */ #define PA_SAMPLE_S32RE PA_SAMPLE_S32BE +/** Signed 24 Bit PCM, packed reverse endian. \since 0.9.15 */ +#define PA_SAMPLE_S24RE PA_SAMPLE_S24BE +/** Signed 24 Bit PCM, in LSB of 32 Bit words, reverse endian. \since 0.9.15 */ +#define PA_SAMPLE_S24_32RE PA_SAMPLE_S24_32BE #endif /** A Shortcut for PA_SAMPLE_FLOAT32NE */ @@ -196,6 +231,10 @@ typedef enum pa_sample_format { #define PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE #define PA_SAMPLE_S32LE PA_SAMPLE_S32LE #define PA_SAMPLE_S32BE PA_SAMPLE_S32BE +#define PA_SAMPLE_S24LE PA_SAMPLE_S24LE +#define PA_SAMPLE_S24BE PA_SAMPLE_S24BE +#define PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE +#define PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE /** \endcond */ /** A sample format and attribute specification */ diff --git a/src/pulse/scache.c b/src/pulse/scache.c index fd3b9876..c96c42ad 100644 --- a/src/pulse/scache.c +++ b/src/pulse/scache.c @@ -188,6 +188,10 @@ pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag); pa_tagstruct_putu32(t, PA_INVALID_INDEX); pa_tagstruct_puts(t, dev); + + if (volume == (pa_volume_t) -1 && c->version < 15) + volume = PA_VOLUME_NORM; + pa_tagstruct_putu32(t, volume); pa_tagstruct_puts(t, name); @@ -225,6 +229,10 @@ pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *na t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag); pa_tagstruct_putu32(t, PA_INVALID_INDEX); pa_tagstruct_puts(t, dev); + + if (volume == (pa_volume_t) -1 && c->version < 15) + volume = PA_VOLUME_NORM; + pa_tagstruct_putu32(t, volume); pa_tagstruct_puts(t, name); pa_tagstruct_put_proplist(t, p); diff --git a/src/pulse/scache.h b/src/pulse/scache.h index f380b4e8..79fcfbc5 100644 --- a/src/pulse/scache.h +++ b/src/pulse/scache.h @@ -28,6 +28,7 @@ #include <pulse/context.h> #include <pulse/stream.h> #include <pulse/cdecl.h> +#include <pulse/version.h> /** \page scache Sample Cache * @@ -100,7 +101,7 @@ pa_operation* pa_context_play_sample( pa_context *c /**< Context */, const char *name /**< Name of the sample to play */, const char *dev /**< Sink to play this sample on */, - pa_volume_t volume /**< Volume to play this sample with */ , + pa_volume_t volume /**< Volume to play this sample with. Starting with 0.9.15 you may pass here (pa_volume_t) -1 which will leave the decision about the volume to the server side which is a good idea. */ , pa_context_success_cb_t cb /**< Call this function after successfully starting playback, or NULL */, void *userdata /**< Userdata to pass to the callback */); @@ -112,7 +113,7 @@ pa_operation* pa_context_play_sample_with_proplist( pa_context *c /**< Context */, const char *name /**< Name of the sample to play */, const char *dev /**< Sink to play this sample on */, - pa_volume_t volume /**< Volume to play this sample with */ , + pa_volume_t volume /**< Volume to play this sample with. Starting with 0.9.15 you may pass here (pa_volume_t) -1 which will leave the decision about the volume to the server side which is a good idea. */ , pa_proplist *proplist /**< Property list for this sound. The property list of the cached entry will be merged into this property list */, pa_context_play_sample_cb_t cb /**< Call this function after successfully starting playback, or NULL */, void *userdata /**< Userdata to pass to the callback */); diff --git a/src/pulse/simple.h b/src/pulse/simple.h index a1380a0a..3f57a654 100644 --- a/src/pulse/simple.h +++ b/src/pulse/simple.h @@ -29,6 +29,7 @@ #include <pulse/channelmap.h> #include <pulse/def.h> #include <pulse/cdecl.h> +#include <pulse/version.h> /** \page simple Simple API * diff --git a/src/pulse/stream.c b/src/pulse/stream.c index c0ae4ac2..5a29bd63 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -87,6 +87,8 @@ pa_stream *pa_stream_new_with_proplist( PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID); PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED); PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID); PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID); diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h index 0e4be8c3..2707cec5 100644 --- a/src/pulse/subscribe.h +++ b/src/pulse/subscribe.h @@ -28,6 +28,7 @@ #include <pulse/def.h> #include <pulse/context.h> #include <pulse/cdecl.h> +#include <pulse/version.h> /** \page subscribe Event Subscription * diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h index 521e29b0..4de338a1 100644 --- a/src/pulse/thread-mainloop.h +++ b/src/pulse/thread-mainloop.h @@ -25,6 +25,7 @@ #include <pulse/mainloop-api.h> #include <pulse/cdecl.h> +#include <pulse/version.h> PA_C_DECL_BEGIN diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h index ee398296..2b3faf16 100644 --- a/src/pulse/timeval.h +++ b/src/pulse/timeval.h @@ -26,17 +26,29 @@ #include <pulse/cdecl.h> #include <pulse/gccmacro.h> #include <pulse/sample.h> +#include <pulse/version.h> /** \file * Utility functions for handling timeval calculations */ PA_C_DECL_BEGIN +/** The number of milliseconds in a second */ #define PA_MSEC_PER_SEC ((pa_usec_t) 1000ULL) + +/** The number of microseconds in a second */ #define PA_USEC_PER_SEC ((pa_usec_t) 1000000ULL) + +/** The number of nanoseconds in a second */ #define PA_NSEC_PER_SEC ((pa_usec_t) 1000000000ULL) + +/** The number of microseconds in a millisecond */ #define PA_USEC_PER_MSEC ((pa_usec_t) 1000ULL) + +/** The number of nanoseconds in a millisecond */ #define PA_NSEC_PER_MSEC ((pa_usec_t) 1000000ULL) + +/** The number of nanoseconds in a microsecond */ #define PA_NSEC_PER_USEC ((pa_usec_t) 1000ULL) struct timeval; diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h index 6c7e7a5b..4d751953 100644 --- a/src/pulse/utf8.h +++ b/src/pulse/utf8.h @@ -25,6 +25,7 @@ #include <pulse/cdecl.h> #include <pulse/gccmacro.h> +#include <pulse/version.h> /** \file * UTF8 Validation functions diff --git a/src/pulse/util.h b/src/pulse/util.h index cf06d4fd..f6dd40cb 100644 --- a/src/pulse/util.h +++ b/src/pulse/util.h @@ -27,6 +27,7 @@ #include <pulse/cdecl.h> #include <pulse/gccmacro.h> +#include <pulse/version.h> /** \file * Assorted utility functions */ diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in index e6226c44..566dd55e 100644 --- a/src/pulse/version.h.in +++ b/src/pulse/version.h.in @@ -43,13 +43,23 @@ const char* pa_get_library_version(void); /** The current API version. Version 6 relates to Polypaudio * 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have - * PA_API_VERSION undefined. */ + * PA_API_VERSION undefined. Please note that this is only ever + * increased on incompatible API changes! */ #define PA_API_VERSION @PA_API_VERSION@ /** The current protocol version. Version 8 relates to Polypaudio * 0.8/PulseAudio 0.9. */ #define PA_PROTOCOL_VERSION @PA_PROTOCOL_VERSION@ +/** The major version of PA. \since 0.9.15 */ +#define PA_MAJOR @PA_MAJOR@ + +/** The minor version of PA. \since 0.9.15 */ +#define PA_MINOR @PA_MINOR@ + +/** The micro version of PA. \since 0.9.15 */ +#define PA_MICRO @PA_MICRO@ + PA_C_DECL_END #endif diff --git a/src/pulse/volume.c b/src/pulse/volume.c index ace5c4d6..3434cb18 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -341,7 +341,7 @@ static pa_bool_t on_lfe(pa_channel_position_t p) { p == PA_CHANNEL_POSITION_LFE; } -pa_cvolume *pa_cvolume_remap(pa_cvolume *v, pa_channel_map *from, pa_channel_map *to) { +pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) { int a, b; pa_cvolume result; @@ -402,3 +402,120 @@ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) { return v->channels == ss->channels; } + +static void get_avg_lr(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *l, pa_volume_t *r) { + int c; + pa_volume_t left = 0, right = 0; + unsigned n_left = 0, n_right = 0; + + pa_assert(v); + pa_assert(map); + pa_assert(map->channels == v->channels); + pa_assert(l); + pa_assert(r); + + for (c = 0; c < map->channels; c++) { + if (on_left(map->map[c])) { + left += v->values[c]; + n_left++; + } else if (on_right(map->map[c])) { + right += v->values[c]; + n_right++; + } + } + + if (n_left <= 0) + *l = PA_VOLUME_NORM; + else + *l = left / n_left; + + if (n_right <= 0) + *r = PA_VOLUME_NORM; + else + *r = right / n_right; +} + +float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) { + pa_volume_t left, right; + + pa_assert(v); + pa_assert(map); + pa_assert(map->channels == v->channels); + + get_avg_lr(map, v, &left, &right); + + if (left == right) + return 0.0f; + + /* 1.0, 0.0 => -1.0 + 0.0, 1.0 => 1.0 + 0.0, 0.0 => 0.0 + 0.5, 0.5 => 0.0 + 1.0, 0.5 => -0.5 + 1.0, 0.25 => -0.75 + 0.75, 0.25 => -0.66 + 0.5, 0.25 => -0.5 */ + + if (left > right) + return -1.0f + ((float) right / (float) left); + else + return 1.0f - ((float) left / (float) right); +} + +pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) { + pa_volume_t left, nleft, right, nright, m; + unsigned c; + + pa_assert(map->channels == v->channels); + pa_assert(map); + pa_assert(v); + pa_assert(new_balance >= -1.0f); + pa_assert(new_balance <= 1.0f); + + get_avg_lr(map, v, &left, &right); + + m = PA_MAX(left, right); + + if (new_balance <= 0) { + nright = (new_balance + 1.0f) * m; + nleft = m; + } else { + nleft = (1.0f - new_balance) * m; + nright = m; + } + + for (c = 0; c < map->channels; c++) { + if (on_left(map->map[c])) { + if (left == 0) + v->values[c] = 0; + else + v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nleft) / (uint64_t) left); + } else if (on_right(map->map[c])) { + if (right == 0) + v->values[c] = 0; + else + v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nright) / (uint64_t) right); + } + } + + return v; +} + +pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) { + unsigned c; + pa_volume_t t = 0; + + pa_assert(c); + + for (c = 0; c < v->channels; c++) + if (v->values[c] > t) + t = v->values[c]; + + if (t <= 0) + 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); + + return v; +} diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 5815c906..9a883ca7 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -29,6 +29,7 @@ #include <pulse/gccmacro.h> #include <pulse/sample.h> #include <pulse/channelmap.h> +#include <pulse/version.h> /** \page volume Volume Control * @@ -154,20 +155,20 @@ char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c); * pa_volume_snprint(). Please note that this value can change with * any release without warning and without being considered API or ABI * breakage. You should not use this definition anywhere where it - * might become part of an ABI. \since 0.9.14 */ + * might become part of an ABI. \since 0.9.15 */ #define PA_VOLUME_SNPRINT_MAX 10 -/** Pretty print a volume \since 0.9.14 */ +/** Pretty print a volume \since 0.9.15 */ char *pa_volume_snprint(char *s, size_t l, pa_volume_t v); /** Maximum length of the strings returned by * pa_volume_snprint_dB(). Please note that this value can change with * any release without warning and without being considered API or ABI * breakage. You should not use this definition anywhere where it - * might become part of an ABI. \since 0.9.14 */ + * might become part of an ABI. \since 0.9.15 */ #define PA_SW_VOLUME_SNPRINT_DB_MAX 10 -/** Pretty print a volume but show dB values. \since 0.9.14 */ +/** Pretty print a volume but show dB values. \since 0.9.15 */ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v); /** Return the average volume of all channels */ @@ -227,12 +228,31 @@ double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST; #endif /** Remap a volume from one channel mapping to a different channel mapping. \since 0.9.12 */ -pa_cvolume *pa_cvolume_remap(pa_cvolume *v, pa_channel_map *from, pa_channel_map *to); +pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to); /** Return non-zero if the specified volume is compatible with * the specified sample spec. \since 0.9.13 */ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) PA_GCC_PURE; +/** Calculate a 'balance' value for the specified volume with the + * specified channel map. The return value will range from -1.0f + * (left) to +1.0f (right) \since 0.9.15 */ +float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) PA_GCC_PURE; + +/** Adjust the '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 and +1.0f. This + * operation might not be reversable! Also, after this call + * pa_cvolume_get_balance() is not guaranteed to actually return the + * requested balance (e.g. when the input volume was zero anyway for + * all channels) \since 0.9.15 */ +pa_cvolume* pa_cvolume_set_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 */ +pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max); + PA_C_DECL_END #endif diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h index b2643588..c30d4df1 100644 --- a/src/pulse/xmalloc.h +++ b/src/pulse/xmalloc.h @@ -29,6 +29,7 @@ #include <pulse/cdecl.h> #include <pulse/gccmacro.h> +#include <pulse/version.h> /** \file * Memory allocation functions. |