mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-01 22:58:50 -04: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
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
size_t l1, l2;
|
||||
|
|
|
|||
|
|
@ -97,6 +97,11 @@ struct sample_spec {
|
|||
uint32_t rate;
|
||||
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)
|
||||
{
|
||||
|
|
@ -247,6 +252,12 @@ struct channel_map {
|
|||
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)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
|
||||
struct volume {
|
||||
uint8_t channels;
|
||||
float values[CHANNELS_MAX];
|
||||
};
|
||||
|
||||
enum encoding {
|
||||
ENCODING_ANY,
|
||||
ENCODING_PCM,
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@
|
|||
#include "defs.h"
|
||||
|
||||
#include "format.c"
|
||||
#include "volume.c"
|
||||
#include "message.c"
|
||||
#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_node_info *info = o->info;
|
||||
struct sample_spec ss;
|
||||
struct volume volume;
|
||||
struct channel_map map;
|
||||
struct volume_info volume_info = VOLUME_INFO_INIT;
|
||||
struct sample_spec ss = SAMPLE_SPEC_INIT;
|
||||
struct channel_map map = CHANNEL_MAP_INIT;
|
||||
const char *name, *str;
|
||||
char *monitor_name = NULL;
|
||||
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))
|
||||
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) {
|
||||
size_t size = strlen(name) + 10;
|
||||
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)
|
||||
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,
|
||||
TAG_U32, o->id, /* sink index */
|
||||
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_CHANNEL_MAP, &map,
|
||||
TAG_U32, module_id, /* module index */
|
||||
TAG_CVOLUME, &volume,
|
||||
TAG_BOOLEAN, false,
|
||||
TAG_CVOLUME, &volume_info.volume,
|
||||
TAG_BOOLEAN, volume_info.mute,
|
||||
TAG_U32, o->id | 0x10000U, /* monitor source */
|
||||
TAG_STRING, monitor_name, /* monitor source name */
|
||||
TAG_USEC, 0LL, /* latency */
|
||||
|
|
@ -2867,9 +2857,9 @@ static int fill_sink_info(struct client *client, struct message *m,
|
|||
}
|
||||
if (client->version >= 15) {
|
||||
message_put(m,
|
||||
TAG_VOLUME, 1.0f, /* base volume */
|
||||
TAG_U32, 0, /* state */
|
||||
TAG_U32, 256, /* n_volume_steps */
|
||||
TAG_VOLUME, volume_info.base, /* base volume */
|
||||
TAG_U32, node_state(info->state), /* state */
|
||||
TAG_U32, volume_info.steps, /* n_volume_steps */
|
||||
TAG_U32, card_id, /* card index */
|
||||
TAG_INVALID);
|
||||
}
|
||||
|
|
@ -2897,34 +2887,22 @@ static int fill_source_info(struct client *client, struct message *m,
|
|||
struct pw_manager_object *o)
|
||||
{
|
||||
struct pw_node_info *info = o->info;
|
||||
struct sample_spec ss;
|
||||
struct volume volume;
|
||||
struct channel_map map;
|
||||
struct volume_info volume_info = VOLUME_INFO_INIT;
|
||||
struct sample_spec ss = SAMPLE_SPEC_INIT;
|
||||
struct channel_map map = CHANNEL_MAP_INIT;
|
||||
bool is_monitor;
|
||||
const char *name, *desc, *str;
|
||||
char *monitor_name = NULL;
|
||||
char *monitor_desc = NULL;
|
||||
uint32_t module_id = SPA_ID_INVALID;
|
||||
uint32_t card_id = SPA_ID_INVALID;
|
||||
struct pw_manager_param *p;
|
||||
|
||||
is_monitor = is_sink(o);
|
||||
if (o == NULL || info == NULL || info->props == NULL ||
|
||||
(!is_source(o) && !is_monitor))
|
||||
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) {
|
||||
size_t size = strlen(name) + 10;
|
||||
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)
|
||||
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,
|
||||
TAG_U32, is_monitor ? o->id | 0x10000 : o->id, /* source index */
|
||||
TAG_STRING, is_monitor ? monitor_name : name,
|
||||
TAG_STRING, is_monitor ? monitor_desc : desc,
|
||||
TAG_STRING, is_monitor ? monitor_name : name,
|
||||
TAG_STRING, is_monitor ? monitor_desc : desc,
|
||||
TAG_SAMPLE_SPEC, &ss,
|
||||
TAG_CHANNEL_MAP, &map,
|
||||
TAG_U32, module_id, /* module index */
|
||||
TAG_CVOLUME, &volume,
|
||||
TAG_BOOLEAN, false,
|
||||
TAG_CVOLUME, &volume_info.volume,
|
||||
TAG_BOOLEAN, volume_info.mute,
|
||||
TAG_U32, is_monitor ? o->id : SPA_ID_INVALID, /* monitor of sink */
|
||||
TAG_STRING, is_monitor ? name : NULL, /* monitor of sink name */
|
||||
TAG_USEC, 0LL, /* latency */
|
||||
|
|
@ -2964,9 +2952,9 @@ static int fill_source_info(struct client *client, struct message *m,
|
|||
}
|
||||
if (client->version >= 15) {
|
||||
message_put(m,
|
||||
TAG_VOLUME, 1.0f, /* base volume */
|
||||
TAG_U32, 0, /* state */
|
||||
TAG_U32, 256, /* n_volume_steps */
|
||||
TAG_VOLUME, volume_info.base, /* base volume */
|
||||
TAG_U32, node_state(info->state), /* state */
|
||||
TAG_U32, volume_info.steps, /* n_volume_steps */
|
||||
TAG_U32, card_id, /* card index */
|
||||
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_node_info *info = o->info;
|
||||
struct sample_spec ss;
|
||||
struct volume volume;
|
||||
struct channel_map map;
|
||||
struct volume_info volume_info = VOLUME_INFO_INIT;
|
||||
struct sample_spec ss = SAMPLE_SPEC_INIT;
|
||||
struct channel_map map = CHANNEL_MAP_INIT;
|
||||
struct pw_manager_object *peer;
|
||||
const char *str;
|
||||
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))
|
||||
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)
|
||||
module_id = (uint32_t)atoi(str);
|
||||
if ((str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID)) != NULL)
|
||||
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);
|
||||
|
||||
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_SAMPLE_SPEC, &ss,
|
||||
TAG_CHANNEL_MAP, &map,
|
||||
TAG_CVOLUME, &volume,
|
||||
TAG_CVOLUME, &volume_info.volume,
|
||||
TAG_USEC, 0LL, /* latency */
|
||||
TAG_USEC, 0LL, /* sink latency */
|
||||
TAG_STRING, "PipeWire", /* resample method */
|
||||
|
|
@ -3040,7 +3027,7 @@ static int fill_sink_input_info(struct client *client, struct message *m,
|
|||
TAG_INVALID);
|
||||
if (client->version >= 11)
|
||||
message_put(m,
|
||||
TAG_BOOLEAN, false, /* muted */
|
||||
TAG_BOOLEAN, volume_info.mute, /* muted */
|
||||
TAG_INVALID);
|
||||
if (client->version >= 13)
|
||||
message_put(m,
|
||||
|
|
@ -3048,7 +3035,7 @@ static int fill_sink_input_info(struct client *client, struct message *m,
|
|||
TAG_INVALID);
|
||||
if (client->version >= 19)
|
||||
message_put(m,
|
||||
TAG_BOOLEAN, false, /* corked */
|
||||
TAG_BOOLEAN, info->state != PW_NODE_STATE_RUNNING, /* corked */
|
||||
TAG_INVALID);
|
||||
if (client->version >= 20)
|
||||
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_node_info *info = o->info;
|
||||
struct sample_spec ss;
|
||||
struct volume volume;
|
||||
struct channel_map map;
|
||||
struct volume_info volume_info = VOLUME_INFO_INIT;
|
||||
struct sample_spec ss = SAMPLE_SPEC_INIT;
|
||||
struct channel_map map = CHANNEL_MAP_INIT;
|
||||
struct pw_manager_object *peer;
|
||||
const char *str;
|
||||
uint32_t module_id = SPA_ID_INVALID, client_id = SPA_ID_INVALID;
|
||||
uint32_t peer_id;
|
||||
struct pw_manager_param *p;
|
||||
|
||||
if (o == NULL || info == NULL || info->props == NULL || !is_source_output(o))
|
||||
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)
|
||||
module_id = (uint32_t)atoi(str);
|
||||
if ((str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID)) != NULL)
|
||||
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);
|
||||
if (peer) {
|
||||
peer_id = peer->id;
|
||||
|
|
@ -3127,15 +3113,15 @@ static int fill_source_output_info(struct client *client, struct message *m,
|
|||
TAG_INVALID);
|
||||
if (client->version >= 19)
|
||||
message_put(m,
|
||||
TAG_BOOLEAN, false, /* corked */
|
||||
TAG_BOOLEAN, info->state != PW_NODE_STATE_RUNNING, /* corked */
|
||||
TAG_INVALID);
|
||||
if (client->version >= 22) {
|
||||
struct format_info fi;
|
||||
spa_zero(fi);
|
||||
fi.encoding = ENCODING_PCM;
|
||||
message_put(m,
|
||||
TAG_CVOLUME, &volume,
|
||||
TAG_BOOLEAN, false, /* muted */
|
||||
TAG_CVOLUME, &volume_info.volume,
|
||||
TAG_BOOLEAN, volume_info.mute, /* muted */
|
||||
TAG_BOOLEAN, true, /* has_volume */
|
||||
TAG_BOOLEAN, true, /* volume writable */
|
||||
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