From 6d196b1d1192ee49fb5784f55f920797bd26412e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 17 Aug 2021 09:14:40 +0200 Subject: [PATCH] alsa: use ProcessLatency param to get and set internal latency --- spa/plugins/alsa/alsa-pcm-sink.c | 120 +++++++++++++++--------- spa/plugins/alsa/alsa-pcm-source.c | 108 +++++++++++++-------- spa/plugins/alsa/alsa-pcm.h | 24 ++--- spa/plugins/audioconvert/audioadapter.c | 14 ++- 4 files changed, 174 insertions(+), 92 deletions(-) diff --git a/spa/plugins/alsa/alsa-pcm-sink.c b/spa/plugins/alsa/alsa-pcm-sink.c index 48534e502..c8abe4009 100644 --- a/spa/plugins/alsa/alsa-pcm-sink.c +++ b/spa/plugins/alsa/alsa-pcm-sink.c @@ -55,6 +55,44 @@ static void reset_props(struct props *props) props->use_chmap = DEFAULT_USE_CHMAP; } +static void emit_node_info(struct state *this, bool full) +{ + uint64_t old = full ? this->info.change_mask : 0; + + if (full) + this->info.change_mask = this->info_all; + if (this->info.change_mask) { + struct spa_dict_item items[4]; + uint32_t n_items = 0; + char latency[64]; + + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "alsa"); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Sink"); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true"); + if (this->have_format) { + snprintf(latency, sizeof(latency), "%lu/%d", this->buffer_frames / 4, this->rate); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_MAX_LATENCY, latency); + } + this->info.props = &SPA_DICT_INIT(items, n_items); + + spa_node_emit_info(&this->hooks, &this->info); + + this->info.change_mask = old; + } +} + +static void emit_port_info(struct state *this, bool full) +{ + uint64_t old = full ? this->port_info.change_mask : 0; + if (full) + this->port_info.change_mask = this->port_info_all; + if (this->port_info.change_mask) { + spa_node_emit_port_info(&this->hooks, + SPA_DIRECTION_INPUT, 0, &this->port_info); + this->port_info.change_mask = old; + } +} + static int impl_node_enum_params(void *object, int seq, uint32_t id, uint32_t start, uint32_t num, const struct spa_pod *filter) @@ -168,6 +206,16 @@ static int impl_node_enum_params(void *object, int seq, } break; + case SPA_PARAM_ProcessLatency: + switch (result.index) { + case 0: + param = spa_process_latency_build(&b, id, &this->process_latency); + break; + default: + return 0; + } + break; + default: return -ENOENT; } @@ -208,10 +256,13 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param) { struct state *this = object; + int res; spa_return_val_if_fail(this != NULL, -EINVAL); - if (id == SPA_PARAM_Props) { + switch (id) { + case SPA_PARAM_Props: + { struct props *p = &this->props; if (param == NULL) { @@ -224,10 +275,27 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, SPA_PROP_minLatency, SPA_POD_OPT_Int(&p->min_latency), SPA_PROP_maxLatency, SPA_POD_OPT_Int(&p->max_latency), SPA_PROP_START_CUSTOM, SPA_POD_OPT_Bool(&p->use_chmap)); + break; } - else - return -ENOENT; + case SPA_PARAM_ProcessLatency: + { + struct spa_process_latency_info info; + if ((res = spa_process_latency_parse(param, &info)) < 0) + return res; + this->process_latency = info; + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; + this->params[NODE_ProcessLatency].flags ^= SPA_PARAM_INFO_SERIAL; + emit_node_info(this, false); + + this->port_info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; + this->port_params[PORT_Latency].flags ^= SPA_PARAM_INFO_SERIAL; + emit_port_info(this, false); + break; + } + default: + return -ENOENT; + } return 0; } @@ -271,44 +339,6 @@ static int impl_node_send_command(void *object, const struct spa_command *comman } -static void emit_node_info(struct state *this, bool full) -{ - uint64_t old = full ? this->info.change_mask : 0; - - if (full) - this->info.change_mask = this->info_all; - if (this->info.change_mask) { - struct spa_dict_item items[4]; - uint32_t n_items = 0; - char latency[64]; - - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "alsa"); - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Sink"); - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true"); - if (this->have_format) { - snprintf(latency, sizeof(latency), "%lu/%d", this->buffer_frames / 4, this->rate); - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_MAX_LATENCY, latency); - } - this->info.props = &SPA_DICT_INIT(items, n_items); - - spa_node_emit_info(&this->hooks, &this->info); - - this->info.change_mask = old; - } -} - -static void emit_port_info(struct state *this, bool full) -{ - uint64_t old = full ? this->port_info.change_mask : 0; - if (full) - this->port_info.change_mask = this->port_info_all; - if (this->port_info.change_mask) { - spa_node_emit_port_info(&this->hooks, - SPA_DIRECTION_INPUT, 0, &this->port_info); - this->port_info.change_mask = old; - } -} - static int impl_node_add_listener(void *object, struct spa_hook *listener, @@ -459,8 +489,13 @@ impl_node_port_enum_params(void *object, int seq, case SPA_PARAM_Latency: switch (result.index) { case 0: case 1: - param = spa_latency_build(&b, id, &this->latency[result.index]); + { + struct spa_latency_info latency = this->latency[result.index]; + if (latency.direction == SPA_DIRECTION_INPUT) + spa_process_latency_info_add(&this->process_latency, &latency); + param = spa_latency_build(&b, id, &latency); break; + } default: return 0; } @@ -806,6 +841,7 @@ impl_init(const struct spa_handle_factory *factory, this->params[NODE_PropInfo] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); this->params[NODE_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); this->params[NODE_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); + this->params[NODE_ProcessLatency] = SPA_PARAM_INFO(SPA_PARAM_ProcessLatency, SPA_PARAM_INFO_READWRITE); this->info.params = this->params; this->info.n_params = N_NODE_PARAMS; diff --git a/spa/plugins/alsa/alsa-pcm-source.c b/spa/plugins/alsa/alsa-pcm-source.c index 5116b8b18..3d248b03d 100644 --- a/spa/plugins/alsa/alsa-pcm-source.c +++ b/spa/plugins/alsa/alsa-pcm-source.c @@ -55,6 +55,43 @@ static void reset_props(struct props *props) props->use_chmap = DEFAULT_USE_CHMAP; } +static void emit_node_info(struct state *this, bool full) +{ + uint64_t old = full ? this->info.change_mask : 0; + if (full) + this->info.change_mask = this->info_all; + if (this->info.change_mask) { + struct spa_dict_item items[4]; + uint32_t n_items = 0; + char latency[64]; + + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "alsa"); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Source"); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true"); + if (this->have_format) { + snprintf(latency, sizeof(latency), "%lu/%d", this->buffer_frames / 4, this->rate); + items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_MAX_LATENCY, latency); + } + this->info.props = &SPA_DICT_INIT(items, n_items); + + spa_node_emit_info(&this->hooks, &this->info); + this->info.change_mask = old; + } +} + +static void emit_port_info(struct state *this, bool full) +{ + uint64_t old = full ? this->port_info.change_mask : 0; + if (full) + this->port_info.change_mask = this->port_info_all; + if (this->port_info.change_mask) { + spa_node_emit_port_info(&this->hooks, + SPA_DIRECTION_OUTPUT, 0, &this->port_info); + this->port_info.change_mask = old; + } +} + + static int impl_node_enum_params(void *object, int seq, uint32_t id, uint32_t start, uint32_t num, const struct spa_pod *filter) @@ -165,6 +202,16 @@ static int impl_node_enum_params(void *object, int seq, } break; + case SPA_PARAM_ProcessLatency: + switch (result.index) { + case 0: + param = spa_process_latency_build(&b, id, &this->process_latency); + break; + default: + return 0; + } + break; + default: return -ENOENT; } @@ -206,6 +253,7 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param) { struct state *this = object; + int res; spa_return_val_if_fail(this != NULL, -EINVAL); @@ -226,6 +274,22 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, SPA_PROP_START_CUSTOM, SPA_POD_OPT_Bool(&p->use_chmap)); break; } + case SPA_PARAM_ProcessLatency: + { + struct spa_process_latency_info info; + if ((res = spa_process_latency_parse(param, &info)) < 0) + return res; + this->process_latency = info; + + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; + this->params[NODE_ProcessLatency].flags ^= SPA_PARAM_INFO_SERIAL; + emit_node_info(this, false); + + this->port_info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; + this->port_params[PORT_Latency].flags ^= SPA_PARAM_INFO_SERIAL; + emit_port_info(this, false); + break; + } default: return -ENOENT; } @@ -272,42 +336,6 @@ static int impl_node_send_command(void *object, const struct spa_command *comman return 0; } -static void emit_node_info(struct state *this, bool full) -{ - uint64_t old = full ? this->info.change_mask : 0; - if (full) - this->info.change_mask = this->info_all; - if (this->info.change_mask) { - struct spa_dict_item items[4]; - uint32_t n_items = 0; - char latency[64]; - - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "alsa"); - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Source"); - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true"); - if (this->have_format) { - snprintf(latency, sizeof(latency), "%lu/%d", this->buffer_frames / 4, this->rate); - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_MAX_LATENCY, latency); - } - this->info.props = &SPA_DICT_INIT(items, n_items); - - spa_node_emit_info(&this->hooks, &this->info); - this->info.change_mask = old; - } -} - -static void emit_port_info(struct state *this, bool full) -{ - uint64_t old = full ? this->port_info.change_mask : 0; - if (full) - this->port_info.change_mask = this->port_info_all; - if (this->port_info.change_mask) { - spa_node_emit_port_info(&this->hooks, - SPA_DIRECTION_OUTPUT, 0, &this->port_info); - this->port_info.change_mask = old; - } -} - static int impl_node_add_listener(void *object, struct spa_hook *listener, @@ -457,8 +485,13 @@ impl_node_port_enum_params(void *object, int seq, case SPA_PARAM_Latency: switch (result.index) { case 0: case 1: - param = spa_latency_build(&b, id, &this->latency[result.index]); + { + struct spa_latency_info latency = this->latency[result.index]; + if (latency.direction == SPA_DIRECTION_OUTPUT) + spa_process_latency_info_add(&this->process_latency, &latency); + param = spa_latency_build(&b, id, &latency); break; + } default: return 0; } @@ -818,6 +851,7 @@ impl_init(const struct spa_handle_factory *factory, this->params[NODE_PropInfo] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); this->params[NODE_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); this->params[NODE_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); + this->params[NODE_ProcessLatency] = SPA_PARAM_INFO(SPA_PARAM_ProcessLatency, SPA_PARAM_INFO_READWRITE); this->info.params = this->params; this->info.n_params = N_NODE_PARAMS; reset_props(&this->props); diff --git a/spa/plugins/alsa/alsa-pcm.h b/spa/plugins/alsa/alsa-pcm.h index 1f10544a5..01aadc863 100644 --- a/spa/plugins/alsa/alsa-pcm.h +++ b/spa/plugins/alsa/alsa-pcm.h @@ -104,10 +104,11 @@ struct state { uint64_t info_all; struct spa_node_info info; -#define NODE_PropInfo 0 -#define NODE_Props 1 -#define NODE_IO 2 -#define N_NODE_PARAMS 3 +#define NODE_PropInfo 0 +#define NODE_Props 1 +#define NODE_IO 2 +#define NODE_ProcessLatency 3 +#define N_NODE_PARAMS 4 struct spa_param_info params[N_NODE_PARAMS]; struct props props; @@ -141,13 +142,13 @@ struct state { uint64_t port_info_all; struct spa_port_info port_info; -#define PORT_EnumFormat 0 -#define PORT_Meta 1 -#define PORT_IO 2 -#define PORT_Format 3 -#define PORT_Buffers 4 -#define PORT_Latency 5 -#define N_PORT_PARAMS 6 +#define PORT_EnumFormat 0 +#define PORT_Meta 1 +#define PORT_IO 2 +#define PORT_Format 3 +#define PORT_Buffers 4 +#define PORT_Latency 5 +#define N_PORT_PARAMS 6 struct spa_param_info port_params[N_PORT_PARAMS]; enum spa_direction port_direction; struct spa_io_buffers *io; @@ -198,6 +199,7 @@ struct state { double max_error; struct spa_latency_info latency[2]; + struct spa_process_latency_info process_latency; snd_use_case_mgr_t *ucm; }; diff --git a/spa/plugins/audioconvert/audioadapter.c b/spa/plugins/audioconvert/audioadapter.c index f65dc39b4..ac7407f6d 100644 --- a/spa/plugins/audioconvert/audioadapter.c +++ b/spa/plugins/audioconvert/audioadapter.c @@ -85,7 +85,8 @@ struct impl { #define IDX_EnumPortConfig 4 #define IDX_PortConfig 5 #define IDX_Latency 6 -#define N_NODE_PARAMS 7 +#define IDX_ProcessLatency 7 +#define N_NODE_PARAMS 8 struct spa_param_info params[N_NODE_PARAMS]; uint32_t convert_params_flags[N_NODE_PARAMS]; uint32_t follower_params_flags[N_NODE_PARAMS]; @@ -132,6 +133,7 @@ next: return res; case SPA_PARAM_PropInfo: case SPA_PARAM_Props: + case SPA_PARAM_ProcessLatency: { if (result.next < 0x10000) { if ((res = spa_node_enum_params_sync(this->convert, @@ -458,6 +460,9 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, return res; res = 0; break; + case SPA_PARAM_ProcessLatency: + res = spa_node_set_param(this->follower, id, flags, param); + break; default: res = -ENOTSUP; break; @@ -698,6 +703,9 @@ static void follower_info(void *data, const struct spa_node_info *info) case SPA_PARAM_Props: idx = IDX_Props; break; + case SPA_PARAM_ProcessLatency: + idx = IDX_ProcessLatency; + break; default: continue; } @@ -971,7 +979,8 @@ impl_node_port_set_param(void *object, flags, param)) < 0) return res; - if (id == SPA_PARAM_Latency && direction == this->direction) { + if ((id == SPA_PARAM_Latency) && + direction == this->direction) { if ((res = spa_node_port_set_param(this->follower, direction, 0, id, flags, param)) < 0) return res; @@ -1276,6 +1285,7 @@ impl_init(const struct spa_handle_factory *factory, this->params[IDX_EnumPortConfig] = SPA_PARAM_INFO(SPA_PARAM_EnumPortConfig, SPA_PARAM_INFO_READ); this->params[IDX_PortConfig] = SPA_PARAM_INFO(SPA_PARAM_PortConfig, SPA_PARAM_INFO_READWRITE); this->params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE); + this->params[IDX_ProcessLatency] = SPA_PARAM_INFO(SPA_PARAM_ProcessLatency, SPA_PARAM_INFO_READWRITE); this->info.params = this->params; this->info.n_params = N_NODE_PARAMS;