mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	pulse-server: fill volumes and state
This commit is contained in:
		
							parent
							
								
									3c2b58d192
								
							
						
					
					
						commit
						2bf5cfa2f7
					
				
					 4 changed files with 198 additions and 95 deletions
				
			
		| 
						 | 
					@ -274,6 +274,32 @@ enum {
 | 
				
			||||||
	SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U
 | 
						SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum {
 | 
				
			||||||
 | 
						STATE_INVALID = -1,
 | 
				
			||||||
 | 
						STATE_RUNNING = 0,
 | 
				
			||||||
 | 
						STATE_IDLE = 1,
 | 
				
			||||||
 | 
						STATE_SUSPENDED = 2,
 | 
				
			||||||
 | 
						STATE_INIT = -2,
 | 
				
			||||||
 | 
						STATE_UNLINKED = -3
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int node_state(enum pw_node_state state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (state) {
 | 
				
			||||||
 | 
						case PW_NODE_STATE_ERROR:
 | 
				
			||||||
 | 
							return STATE_UNLINKED;
 | 
				
			||||||
 | 
						case PW_NODE_STATE_CREATING:
 | 
				
			||||||
 | 
							return STATE_INIT;
 | 
				
			||||||
 | 
						case PW_NODE_STATE_SUSPENDED:
 | 
				
			||||||
 | 
							return STATE_SUSPENDED;
 | 
				
			||||||
 | 
						case PW_NODE_STATE_IDLE:
 | 
				
			||||||
 | 
							return STATE_IDLE;
 | 
				
			||||||
 | 
						case PW_NODE_STATE_RUNNING:
 | 
				
			||||||
 | 
							return STATE_RUNNING;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return STATE_INVALID;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline bool pw_endswith(const char *s, const char *sfx)
 | 
					static inline bool pw_endswith(const char *s, const char *sfx)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
        size_t l1, l2;
 | 
					        size_t l1, l2;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,6 +97,11 @@ struct sample_spec {
 | 
				
			||||||
	uint32_t rate;
 | 
						uint32_t rate;
 | 
				
			||||||
	uint8_t channels;
 | 
						uint8_t channels;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					#define SAMPLE_SPEC_INIT	(struct sample_spec) {			\
 | 
				
			||||||
 | 
										.format = SAMPLE_FLOAT32LE,	\
 | 
				
			||||||
 | 
										.rate = 44100,			\
 | 
				
			||||||
 | 
										.channels = 2,			\
 | 
				
			||||||
 | 
									};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline uint32_t sample_spec_frame_size(const struct sample_spec *ss)
 | 
					static inline uint32_t sample_spec_frame_size(const struct sample_spec *ss)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -247,6 +252,12 @@ struct channel_map {
 | 
				
			||||||
	enum channel_position map[CHANNELS_MAX];
 | 
						enum channel_position map[CHANNELS_MAX];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CHANNEL_MAP_INIT	(struct channel_map) {				\
 | 
				
			||||||
 | 
										.channels = 2,				\
 | 
				
			||||||
 | 
										.map[0] = CHANNEL_POSITION_FRONT_LEFT,	\
 | 
				
			||||||
 | 
										.map[1] = CHANNEL_POSITION_FRONT_RIGHT,	\
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline uint32_t channel_pa2id(enum channel_position channel)
 | 
					static inline uint32_t channel_pa2id(enum channel_position channel)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
        if (channel < 0 || (size_t)channel >= SPA_N_ELEMENTS(audio_channels))
 | 
					        if (channel < 0 || (size_t)channel >= SPA_N_ELEMENTS(audio_channels))
 | 
				
			||||||
| 
						 | 
					@ -283,11 +294,6 @@ static void channel_map_to_positions(const struct channel_map *map, uint32_t *po
 | 
				
			||||||
		pos[i] = channel_pa2id(map->map[i]);
 | 
							pos[i] = channel_pa2id(map->map[i]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct volume {
 | 
					 | 
				
			||||||
	uint8_t channels;
 | 
					 | 
				
			||||||
	float values[CHANNELS_MAX];
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum encoding {
 | 
					enum encoding {
 | 
				
			||||||
	ENCODING_ANY,
 | 
						ENCODING_ANY,
 | 
				
			||||||
	ENCODING_PCM,
 | 
						ENCODING_PCM,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,6 +57,7 @@
 | 
				
			||||||
#include "defs.h"
 | 
					#include "defs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "format.c"
 | 
					#include "format.c"
 | 
				
			||||||
 | 
					#include "volume.c"
 | 
				
			||||||
#include "message.c"
 | 
					#include "message.c"
 | 
				
			||||||
#include "manager.h"
 | 
					#include "manager.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2800,9 +2801,9 @@ static int fill_sink_info(struct client *client, struct message *m,
 | 
				
			||||||
		struct pw_manager_object *o)
 | 
							struct pw_manager_object *o)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct pw_node_info *info = o->info;
 | 
						struct pw_node_info *info = o->info;
 | 
				
			||||||
	struct sample_spec ss;
 | 
						struct volume_info volume_info = VOLUME_INFO_INIT;
 | 
				
			||||||
	struct volume volume;
 | 
						struct sample_spec ss = SAMPLE_SPEC_INIT;
 | 
				
			||||||
	struct channel_map map;
 | 
						struct channel_map map = CHANNEL_MAP_INIT;
 | 
				
			||||||
	const char *name, *str;
 | 
						const char *name, *str;
 | 
				
			||||||
	char *monitor_name = NULL;
 | 
						char *monitor_name = NULL;
 | 
				
			||||||
	uint32_t module_id = SPA_ID_INVALID;
 | 
						uint32_t module_id = SPA_ID_INVALID;
 | 
				
			||||||
| 
						 | 
					@ -2812,27 +2813,6 @@ static int fill_sink_info(struct client *client, struct message *m,
 | 
				
			||||||
	if (o == NULL || info == NULL || info->props == NULL || !is_sink(o))
 | 
						if (o == NULL || info == NULL || info->props == NULL || !is_sink(o))
 | 
				
			||||||
		return ERR_NOENTITY;
 | 
							return ERR_NOENTITY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ss = (struct sample_spec) {
 | 
					 | 
				
			||||||
			.format = SAMPLE_FLOAT32LE,
 | 
					 | 
				
			||||||
			.rate = 44100,
 | 
					 | 
				
			||||||
			.channels = 2, };
 | 
					 | 
				
			||||||
	map = (struct channel_map) {
 | 
					 | 
				
			||||||
			.channels = 2,
 | 
					 | 
				
			||||||
			.map[0] = 1,
 | 
					 | 
				
			||||||
			.map[1] = 2, };
 | 
					 | 
				
			||||||
	volume = (struct volume) {
 | 
					 | 
				
			||||||
			.channels = 2,
 | 
					 | 
				
			||||||
			.values[0] = 1.0f,
 | 
					 | 
				
			||||||
			.values[1] = 1.0f, };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spa_list_for_each(p, &o->param_list, link) {
 | 
					 | 
				
			||||||
		switch (p->id) {
 | 
					 | 
				
			||||||
		case SPA_PARAM_Format:
 | 
					 | 
				
			||||||
			format_parse_param(p->param, &ss, &map);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) != NULL) {
 | 
						if ((name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) != NULL) {
 | 
				
			||||||
		size_t size = strlen(name) + 10;
 | 
							size_t size = strlen(name) + 10;
 | 
				
			||||||
		monitor_name = alloca(size);
 | 
							monitor_name = alloca(size);
 | 
				
			||||||
| 
						 | 
					@ -2843,6 +2823,16 @@ static int fill_sink_info(struct client *client, struct message *m,
 | 
				
			||||||
	if ((str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)) != NULL)
 | 
						if ((str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)) != NULL)
 | 
				
			||||||
		card_id = (uint32_t)atoi(str);
 | 
							card_id = (uint32_t)atoi(str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_list_for_each(p, &o->param_list, link) {
 | 
				
			||||||
 | 
							switch (p->id) {
 | 
				
			||||||
 | 
							case SPA_PARAM_Format:
 | 
				
			||||||
 | 
								format_parse_param(p->param, &ss, &map);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (volume_info.volume.channels != map.channels)
 | 
				
			||||||
 | 
							volume_info.volume.channels = map.channels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	message_put(m,
 | 
						message_put(m,
 | 
				
			||||||
		TAG_U32, o->id,				/* sink index */
 | 
							TAG_U32, o->id,				/* sink index */
 | 
				
			||||||
		TAG_STRING, spa_dict_lookup(info->props, PW_KEY_NODE_NAME),
 | 
							TAG_STRING, spa_dict_lookup(info->props, PW_KEY_NODE_NAME),
 | 
				
			||||||
| 
						 | 
					@ -2850,8 +2840,8 @@ static int fill_sink_info(struct client *client, struct message *m,
 | 
				
			||||||
		TAG_SAMPLE_SPEC, &ss,
 | 
							TAG_SAMPLE_SPEC, &ss,
 | 
				
			||||||
		TAG_CHANNEL_MAP, &map,
 | 
							TAG_CHANNEL_MAP, &map,
 | 
				
			||||||
		TAG_U32, module_id,			/* module index */
 | 
							TAG_U32, module_id,			/* module index */
 | 
				
			||||||
		TAG_CVOLUME, &volume,
 | 
							TAG_CVOLUME, &volume_info.volume,
 | 
				
			||||||
		TAG_BOOLEAN, false,
 | 
							TAG_BOOLEAN, volume_info.mute,
 | 
				
			||||||
		TAG_U32, o->id | 0x10000U,		/* monitor source */
 | 
							TAG_U32, o->id | 0x10000U,		/* monitor source */
 | 
				
			||||||
		TAG_STRING, monitor_name,		/* monitor source name */
 | 
							TAG_STRING, monitor_name,		/* monitor source name */
 | 
				
			||||||
		TAG_USEC, 0LL,				/* latency */
 | 
							TAG_USEC, 0LL,				/* latency */
 | 
				
			||||||
| 
						 | 
					@ -2867,9 +2857,9 @@ static int fill_sink_info(struct client *client, struct message *m,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (client->version >= 15) {
 | 
						if (client->version >= 15) {
 | 
				
			||||||
		message_put(m,
 | 
							message_put(m,
 | 
				
			||||||
			TAG_VOLUME, 1.0f,		/* base volume */
 | 
								TAG_VOLUME, volume_info.base,	/* base volume */
 | 
				
			||||||
			TAG_U32, 0,			/* state */
 | 
								TAG_U32, node_state(info->state),	/* state */
 | 
				
			||||||
			TAG_U32, 256,			/* n_volume_steps */
 | 
								TAG_U32, volume_info.steps,	/* n_volume_steps */
 | 
				
			||||||
			TAG_U32, card_id,		/* card index */
 | 
								TAG_U32, card_id,		/* card index */
 | 
				
			||||||
			TAG_INVALID);
 | 
								TAG_INVALID);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -2897,34 +2887,22 @@ static int fill_source_info(struct client *client, struct message *m,
 | 
				
			||||||
		struct pw_manager_object *o)
 | 
							struct pw_manager_object *o)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct pw_node_info *info = o->info;
 | 
						struct pw_node_info *info = o->info;
 | 
				
			||||||
	struct sample_spec ss;
 | 
						struct volume_info volume_info = VOLUME_INFO_INIT;
 | 
				
			||||||
	struct volume volume;
 | 
						struct sample_spec ss = SAMPLE_SPEC_INIT;
 | 
				
			||||||
	struct channel_map map;
 | 
						struct channel_map map = CHANNEL_MAP_INIT;
 | 
				
			||||||
	bool is_monitor;
 | 
						bool is_monitor;
 | 
				
			||||||
	const char *name, *desc, *str;
 | 
						const char *name, *desc, *str;
 | 
				
			||||||
	char *monitor_name = NULL;
 | 
						char *monitor_name = NULL;
 | 
				
			||||||
	char *monitor_desc = NULL;
 | 
						char *monitor_desc = NULL;
 | 
				
			||||||
	uint32_t module_id = SPA_ID_INVALID;
 | 
						uint32_t module_id = SPA_ID_INVALID;
 | 
				
			||||||
	uint32_t card_id = SPA_ID_INVALID;
 | 
						uint32_t card_id = SPA_ID_INVALID;
 | 
				
			||||||
 | 
						struct pw_manager_param *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	is_monitor = is_sink(o);
 | 
						is_monitor = is_sink(o);
 | 
				
			||||||
	if (o == NULL || info == NULL || info->props == NULL ||
 | 
						if (o == NULL || info == NULL || info->props == NULL ||
 | 
				
			||||||
	    (!is_source(o) && !is_monitor))
 | 
						    (!is_source(o) && !is_monitor))
 | 
				
			||||||
		return ERR_NOENTITY;
 | 
							return ERR_NOENTITY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ss = (struct sample_spec) {
 | 
					 | 
				
			||||||
			.format = SAMPLE_FLOAT32LE,
 | 
					 | 
				
			||||||
			.rate = 44100,
 | 
					 | 
				
			||||||
			.channels = 2, };
 | 
					 | 
				
			||||||
	volume = (struct volume) {
 | 
					 | 
				
			||||||
			.channels = 2,
 | 
					 | 
				
			||||||
			.values[0] = 1.0f,
 | 
					 | 
				
			||||||
			.values[1] = 1.0f, };
 | 
					 | 
				
			||||||
	map = (struct channel_map) {
 | 
					 | 
				
			||||||
			.channels = 2,
 | 
					 | 
				
			||||||
			.map[0] = 1,
 | 
					 | 
				
			||||||
			.map[1] = 2, };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) != NULL) {
 | 
						if ((name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) != NULL) {
 | 
				
			||||||
		size_t size = strlen(name) + 10;
 | 
							size_t size = strlen(name) + 10;
 | 
				
			||||||
		monitor_name = alloca(size);
 | 
							monitor_name = alloca(size);
 | 
				
			||||||
| 
						 | 
					@ -2940,15 +2918,25 @@ static int fill_source_info(struct client *client, struct message *m,
 | 
				
			||||||
	if ((str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)) != NULL)
 | 
						if ((str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)) != NULL)
 | 
				
			||||||
		card_id = (uint32_t)atoi(str);
 | 
							card_id = (uint32_t)atoi(str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_list_for_each(p, &o->param_list, link) {
 | 
				
			||||||
 | 
							switch (p->id) {
 | 
				
			||||||
 | 
							case SPA_PARAM_Format:
 | 
				
			||||||
 | 
								format_parse_param(p->param, &ss, &map);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (volume_info.volume.channels != map.channels)
 | 
				
			||||||
 | 
							volume_info.volume.channels = map.channels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	message_put(m,
 | 
						message_put(m,
 | 
				
			||||||
		TAG_U32, is_monitor ? o->id | 0x10000 : o->id,	/* source index */
 | 
							TAG_U32, is_monitor ? o->id | 0x10000 : o->id,	/* source index */
 | 
				
			||||||
		TAG_STRING, is_monitor ? monitor_name :  name,
 | 
							TAG_STRING, is_monitor ? monitor_name : name,
 | 
				
			||||||
		TAG_STRING, is_monitor ? monitor_desc :  desc,
 | 
							TAG_STRING, is_monitor ? monitor_desc : desc,
 | 
				
			||||||
		TAG_SAMPLE_SPEC, &ss,
 | 
							TAG_SAMPLE_SPEC, &ss,
 | 
				
			||||||
		TAG_CHANNEL_MAP, &map,
 | 
							TAG_CHANNEL_MAP, &map,
 | 
				
			||||||
		TAG_U32, module_id,				/* module index */
 | 
							TAG_U32, module_id,				/* module index */
 | 
				
			||||||
		TAG_CVOLUME, &volume,
 | 
							TAG_CVOLUME, &volume_info.volume,
 | 
				
			||||||
		TAG_BOOLEAN, false,
 | 
							TAG_BOOLEAN, volume_info.mute,
 | 
				
			||||||
		TAG_U32, is_monitor ? o->id : SPA_ID_INVALID,	/* monitor of sink */
 | 
							TAG_U32, is_monitor ? o->id : SPA_ID_INVALID,	/* monitor of sink */
 | 
				
			||||||
		TAG_STRING, is_monitor ? name : NULL,		/* monitor of sink name */
 | 
							TAG_STRING, is_monitor ? name : NULL,		/* monitor of sink name */
 | 
				
			||||||
		TAG_USEC, 0LL,					/* latency */
 | 
							TAG_USEC, 0LL,					/* latency */
 | 
				
			||||||
| 
						 | 
					@ -2964,9 +2952,9 @@ static int fill_source_info(struct client *client, struct message *m,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (client->version >= 15) {
 | 
						if (client->version >= 15) {
 | 
				
			||||||
		message_put(m,
 | 
							message_put(m,
 | 
				
			||||||
			TAG_VOLUME, 1.0f,		/* base volume */
 | 
								TAG_VOLUME, volume_info.base,	/* base volume */
 | 
				
			||||||
			TAG_U32, 0,			/* state */
 | 
								TAG_U32, node_state(info->state),	/* state */
 | 
				
			||||||
			TAG_U32, 256,			/* n_volume_steps */
 | 
								TAG_U32, volume_info.steps,	/* n_volume_steps */
 | 
				
			||||||
			TAG_U32, card_id,		/* card index */
 | 
								TAG_U32, card_id,		/* card index */
 | 
				
			||||||
			TAG_INVALID);
 | 
								TAG_INVALID);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -2994,34 +2982,33 @@ static int fill_sink_input_info(struct client *client, struct message *m,
 | 
				
			||||||
		struct pw_manager_object *o)
 | 
							struct pw_manager_object *o)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct pw_node_info *info = o->info;
 | 
						struct pw_node_info *info = o->info;
 | 
				
			||||||
	struct sample_spec ss;
 | 
						struct volume_info volume_info = VOLUME_INFO_INIT;
 | 
				
			||||||
	struct volume volume;
 | 
						struct sample_spec ss = SAMPLE_SPEC_INIT;
 | 
				
			||||||
	struct channel_map map;
 | 
						struct channel_map map = CHANNEL_MAP_INIT;
 | 
				
			||||||
	struct pw_manager_object *peer;
 | 
						struct pw_manager_object *peer;
 | 
				
			||||||
	const char *str;
 | 
						const char *str;
 | 
				
			||||||
	uint32_t module_id = SPA_ID_INVALID, client_id = SPA_ID_INVALID;
 | 
						uint32_t module_id = SPA_ID_INVALID, client_id = SPA_ID_INVALID;
 | 
				
			||||||
 | 
						struct pw_manager_param *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (o == NULL || info == NULL || info->props == NULL || !is_sink_input(o))
 | 
						if (o == NULL || info == NULL || info->props == NULL || !is_sink_input(o))
 | 
				
			||||||
		return ERR_NOENTITY;
 | 
							return ERR_NOENTITY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ss = (struct sample_spec) {
 | 
					 | 
				
			||||||
			.format = SAMPLE_FLOAT32LE,
 | 
					 | 
				
			||||||
			.rate = 44100,
 | 
					 | 
				
			||||||
			.channels = 2, };
 | 
					 | 
				
			||||||
	volume = (struct volume) {
 | 
					 | 
				
			||||||
			.channels = 2,
 | 
					 | 
				
			||||||
			.values[0] = 1.0f,
 | 
					 | 
				
			||||||
			.values[1] = 1.0f, };
 | 
					 | 
				
			||||||
	map = (struct channel_map) {
 | 
					 | 
				
			||||||
			.channels = 2,
 | 
					 | 
				
			||||||
			.map[0] = 1,
 | 
					 | 
				
			||||||
			.map[1] = 2, };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID)) != NULL)
 | 
						if ((str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID)) != NULL)
 | 
				
			||||||
		module_id = (uint32_t)atoi(str);
 | 
							module_id = (uint32_t)atoi(str);
 | 
				
			||||||
	if ((str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID)) != NULL)
 | 
						if ((str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID)) != NULL)
 | 
				
			||||||
		client_id = (uint32_t)atoi(str);
 | 
							client_id = (uint32_t)atoi(str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_list_for_each(p, &o->param_list, link) {
 | 
				
			||||||
 | 
							switch (p->id) {
 | 
				
			||||||
 | 
							case SPA_PARAM_Format:
 | 
				
			||||||
 | 
								format_parse_param(p->param, &ss, &map);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case SPA_PARAM_Props:
 | 
				
			||||||
 | 
								volume_parse_param(p->param, &volume_info);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	peer = find_linked(client, o->id, PW_DIRECTION_OUTPUT);
 | 
						peer = find_linked(client, o->id, PW_DIRECTION_OUTPUT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	message_put(m,
 | 
						message_put(m,
 | 
				
			||||||
| 
						 | 
					@ -3032,7 +3019,7 @@ static int fill_sink_input_info(struct client *client, struct message *m,
 | 
				
			||||||
		TAG_U32, peer ? peer->id : SPA_ID_INVALID,	/* sink index */
 | 
							TAG_U32, peer ? peer->id : SPA_ID_INVALID,	/* sink index */
 | 
				
			||||||
		TAG_SAMPLE_SPEC, &ss,
 | 
							TAG_SAMPLE_SPEC, &ss,
 | 
				
			||||||
		TAG_CHANNEL_MAP, &map,
 | 
							TAG_CHANNEL_MAP, &map,
 | 
				
			||||||
		TAG_CVOLUME, &volume,
 | 
							TAG_CVOLUME, &volume_info.volume,
 | 
				
			||||||
		TAG_USEC, 0LL,				/* latency */
 | 
							TAG_USEC, 0LL,				/* latency */
 | 
				
			||||||
		TAG_USEC, 0LL,				/* sink latency */
 | 
							TAG_USEC, 0LL,				/* sink latency */
 | 
				
			||||||
		TAG_STRING, "PipeWire",			/* resample method */
 | 
							TAG_STRING, "PipeWire",			/* resample method */
 | 
				
			||||||
| 
						 | 
					@ -3040,7 +3027,7 @@ static int fill_sink_input_info(struct client *client, struct message *m,
 | 
				
			||||||
		TAG_INVALID);
 | 
							TAG_INVALID);
 | 
				
			||||||
	if (client->version >= 11)
 | 
						if (client->version >= 11)
 | 
				
			||||||
		message_put(m,
 | 
							message_put(m,
 | 
				
			||||||
			TAG_BOOLEAN, false,		/* muted */
 | 
								TAG_BOOLEAN, volume_info.mute,	/* muted */
 | 
				
			||||||
			TAG_INVALID);
 | 
								TAG_INVALID);
 | 
				
			||||||
	if (client->version >= 13)
 | 
						if (client->version >= 13)
 | 
				
			||||||
		message_put(m,
 | 
							message_put(m,
 | 
				
			||||||
| 
						 | 
					@ -3048,7 +3035,7 @@ static int fill_sink_input_info(struct client *client, struct message *m,
 | 
				
			||||||
			TAG_INVALID);
 | 
								TAG_INVALID);
 | 
				
			||||||
	if (client->version >= 19)
 | 
						if (client->version >= 19)
 | 
				
			||||||
		message_put(m,
 | 
							message_put(m,
 | 
				
			||||||
			TAG_BOOLEAN, false,		/* corked */
 | 
								TAG_BOOLEAN, info->state != PW_NODE_STATE_RUNNING,		/* corked */
 | 
				
			||||||
			TAG_INVALID);
 | 
								TAG_INVALID);
 | 
				
			||||||
	if (client->version >= 20)
 | 
						if (client->version >= 20)
 | 
				
			||||||
		message_put(m,
 | 
							message_put(m,
 | 
				
			||||||
| 
						 | 
					@ -3070,35 +3057,34 @@ static int fill_source_output_info(struct client *client, struct message *m,
 | 
				
			||||||
		struct pw_manager_object *o)
 | 
							struct pw_manager_object *o)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct pw_node_info *info = o->info;
 | 
						struct pw_node_info *info = o->info;
 | 
				
			||||||
	struct sample_spec ss;
 | 
						struct volume_info volume_info = VOLUME_INFO_INIT;
 | 
				
			||||||
	struct volume volume;
 | 
						struct sample_spec ss = SAMPLE_SPEC_INIT;
 | 
				
			||||||
	struct channel_map map;
 | 
						struct channel_map map = CHANNEL_MAP_INIT;
 | 
				
			||||||
	struct pw_manager_object *peer;
 | 
						struct pw_manager_object *peer;
 | 
				
			||||||
	const char *str;
 | 
						const char *str;
 | 
				
			||||||
	uint32_t module_id = SPA_ID_INVALID, client_id = SPA_ID_INVALID;
 | 
						uint32_t module_id = SPA_ID_INVALID, client_id = SPA_ID_INVALID;
 | 
				
			||||||
	uint32_t peer_id;
 | 
						uint32_t peer_id;
 | 
				
			||||||
 | 
						struct pw_manager_param *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (o == NULL || info == NULL || info->props == NULL || !is_source_output(o))
 | 
						if (o == NULL || info == NULL || info->props == NULL || !is_source_output(o))
 | 
				
			||||||
		return ERR_NOENTITY;
 | 
							return ERR_NOENTITY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ss = (struct sample_spec) {
 | 
					 | 
				
			||||||
			.format = SAMPLE_FLOAT32LE,
 | 
					 | 
				
			||||||
			.rate = 44100,
 | 
					 | 
				
			||||||
			.channels = 2, };
 | 
					 | 
				
			||||||
	volume = (struct volume) {
 | 
					 | 
				
			||||||
			.channels = 2,
 | 
					 | 
				
			||||||
			.values[0] = 1.0f,
 | 
					 | 
				
			||||||
			.values[1] = 1.0f, };
 | 
					 | 
				
			||||||
	map = (struct channel_map) {
 | 
					 | 
				
			||||||
			.channels = 2,
 | 
					 | 
				
			||||||
			.map[0] = 1,
 | 
					 | 
				
			||||||
			.map[1] = 2, };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID)) != NULL)
 | 
						if ((str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID)) != NULL)
 | 
				
			||||||
		module_id = (uint32_t)atoi(str);
 | 
							module_id = (uint32_t)atoi(str);
 | 
				
			||||||
	if ((str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID)) != NULL)
 | 
						if ((str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID)) != NULL)
 | 
				
			||||||
		client_id = (uint32_t)atoi(str);
 | 
							client_id = (uint32_t)atoi(str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_list_for_each(p, &o->param_list, link) {
 | 
				
			||||||
 | 
							switch (p->id) {
 | 
				
			||||||
 | 
							case SPA_PARAM_Format:
 | 
				
			||||||
 | 
								format_parse_param(p->param, &ss, &map);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case SPA_PARAM_Props:
 | 
				
			||||||
 | 
								volume_parse_param(p->param, &volume_info);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	peer = find_linked(client, o->id, PW_DIRECTION_INPUT);
 | 
						peer = find_linked(client, o->id, PW_DIRECTION_INPUT);
 | 
				
			||||||
	if (peer) {
 | 
						if (peer) {
 | 
				
			||||||
		peer_id = peer->id;
 | 
							peer_id = peer->id;
 | 
				
			||||||
| 
						 | 
					@ -3127,15 +3113,15 @@ static int fill_source_output_info(struct client *client, struct message *m,
 | 
				
			||||||
			TAG_INVALID);
 | 
								TAG_INVALID);
 | 
				
			||||||
	if (client->version >= 19)
 | 
						if (client->version >= 19)
 | 
				
			||||||
		message_put(m,
 | 
							message_put(m,
 | 
				
			||||||
			TAG_BOOLEAN, false,		/* corked */
 | 
								TAG_BOOLEAN, info->state != PW_NODE_STATE_RUNNING,		/* corked */
 | 
				
			||||||
			TAG_INVALID);
 | 
								TAG_INVALID);
 | 
				
			||||||
	if (client->version >= 22) {
 | 
						if (client->version >= 22) {
 | 
				
			||||||
		struct format_info fi;
 | 
							struct format_info fi;
 | 
				
			||||||
		spa_zero(fi);
 | 
							spa_zero(fi);
 | 
				
			||||||
		fi.encoding = ENCODING_PCM;
 | 
							fi.encoding = ENCODING_PCM;
 | 
				
			||||||
		message_put(m,
 | 
							message_put(m,
 | 
				
			||||||
			TAG_CVOLUME, &volume,
 | 
								TAG_CVOLUME, &volume_info.volume,
 | 
				
			||||||
			TAG_BOOLEAN, false,		/* muted */
 | 
								TAG_BOOLEAN, volume_info.mute,	/* muted */
 | 
				
			||||||
			TAG_BOOLEAN, true,		/* has_volume */
 | 
								TAG_BOOLEAN, true,		/* has_volume */
 | 
				
			||||||
			TAG_BOOLEAN, true,		/* volume writable */
 | 
								TAG_BOOLEAN, true,		/* volume writable */
 | 
				
			||||||
			TAG_FORMAT_INFO, &fi,
 | 
								TAG_FORMAT_INFO, &fi,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										85
									
								
								src/modules/module-protocol-pulse/volume.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/modules/module-protocol-pulse/volume.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,85 @@
 | 
				
			||||||
 | 
					/* PipeWire
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2020 Wim Taymans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct volume {
 | 
				
			||||||
 | 
						uint8_t channels;
 | 
				
			||||||
 | 
						float values[CHANNELS_MAX];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define VOLUME_INIT	(struct volume) {		\
 | 
				
			||||||
 | 
									.channels = 2,		\
 | 
				
			||||||
 | 
									.values[0] = 1.0f,	\
 | 
				
			||||||
 | 
									.values[1] = 1.0f,	\
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct volume_info {
 | 
				
			||||||
 | 
						struct volume volume;
 | 
				
			||||||
 | 
						bool mute;
 | 
				
			||||||
 | 
						float level;
 | 
				
			||||||
 | 
						float base;
 | 
				
			||||||
 | 
						uint32_t steps;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define VOLUME_INFO_INIT	(struct volume_info) {		\
 | 
				
			||||||
 | 
										.volume = VOLUME_INIT,	\
 | 
				
			||||||
 | 
										.mute = false,		\
 | 
				
			||||||
 | 
										.level = 1.0,		\
 | 
				
			||||||
 | 
										.base = 1.0,		\
 | 
				
			||||||
 | 
										.steps = 256,		\
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int volume_parse_param(const struct spa_pod *param, struct volume_info *info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct spa_pod_object *obj = (struct spa_pod_object *) param;
 | 
				
			||||||
 | 
						struct spa_pod_prop *prop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SPA_POD_OBJECT_FOREACH(obj, prop) {
 | 
				
			||||||
 | 
							switch (prop->key) {
 | 
				
			||||||
 | 
							case SPA_PROP_volume:
 | 
				
			||||||
 | 
								spa_pod_get_float(&prop->value, &info->level);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case SPA_PROP_mute:
 | 
				
			||||||
 | 
								spa_pod_get_bool(&prop->value, &info->mute);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case SPA_PROP_channelVolumes:
 | 
				
			||||||
 | 
								info->volume.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
 | 
				
			||||||
 | 
										info->volume.values, SPA_AUDIO_MAX_CHANNELS);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case SPA_PROP_volumeBase:
 | 
				
			||||||
 | 
								spa_pod_get_float(&prop->value, &info->base);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case SPA_PROP_volumeStep:
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								float step;
 | 
				
			||||||
 | 
								if (spa_pod_get_float(&prop->value, &step) >= 0)
 | 
				
			||||||
 | 
									info->steps = 0x10000u * step;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue