diff --git a/spa/plugins/alsa/acp/acp.h b/spa/plugins/alsa/acp/acp.h index 66d4bb661..22a4c5d98 100644 --- a/spa/plugins/alsa/acp/acp.h +++ b/spa/plugins/alsa/acp/acp.h @@ -225,6 +225,8 @@ struct acp_device { uint32_t n_ports; struct acp_port **ports; + + int64_t latency_ns; }; struct acp_card_profile { diff --git a/spa/plugins/alsa/alsa-acp-device.c b/spa/plugins/alsa/alsa-acp-device.c index bba035fbf..bcc1885a1 100644 --- a/spa/plugins/alsa/alsa-acp-device.c +++ b/spa/plugins/alsa/alsa-acp-device.c @@ -460,6 +460,9 @@ static struct spa_pod *build_route(struct spa_pod_builder *b, uint32_t id, spa_pod_builder_array(b, sizeof(float), SPA_TYPE_Float, channels, soft_volumes); + spa_pod_builder_prop(b, SPA_PROP_latencyOffsetNsec, 0); + spa_pod_builder_long(b, dev->latency_ns); + spa_pod_builder_pop(b, &f[1]); } spa_pod_builder_prop(b, SPA_PARAM_ROUTE_devices, 0); @@ -574,6 +577,32 @@ static int impl_enum_params(void *object, int seq, return 0; } +static void on_latency_changed(void *data, struct acp_device *dev) +{ + struct impl *this = data; + struct spa_event *event; + uint8_t buffer[4096]; + struct spa_pod_builder b = { 0 }; + struct spa_pod_frame f[1]; + + spa_log_info(this->log, "device %s latency changed", dev->name); + this->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS; + this->params[IDX_Route].user++; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + spa_pod_builder_push_object(&b, &f[0], + SPA_TYPE_EVENT_Device, SPA_DEVICE_EVENT_ObjectConfig); + spa_pod_builder_prop(&b, SPA_EVENT_DEVICE_Object, 0); + spa_pod_builder_int(&b, dev->index); + spa_pod_builder_prop(&b, SPA_EVENT_DEVICE_Props, 0); + spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, SPA_EVENT_DEVICE_Props, + SPA_PROP_latencyOffsetNsec, SPA_POD_Long(dev->latency_ns)); + event = spa_pod_builder_pop(&b, &f[0]); + + spa_device_emit_event(&this->hooks, event); +} + static int apply_device_props(struct impl *this, struct acp_device *dev, struct spa_pod *props) { float volume = 0; @@ -614,6 +643,20 @@ static int apply_device_props(struct impl *this, struct acp_device *dev, struct changed++; } break; + case SPA_PROP_latencyOffsetNsec: + { + int64_t latency_ns; + if (spa_pod_get_long(&prop->value, &latency_ns) == 0) { + if (dev->latency_ns != latency_ns) { + dev->latency_ns = latency_ns; + on_latency_changed(this, dev); + changed++; + } + } + break; + } + default: + break; } } if (n_volumes > 0) diff --git a/spa/plugins/alsa/alsa-pcm-sink.c b/spa/plugins/alsa/alsa-pcm-sink.c index 9d106d8ce..1bb52099b 100644 --- a/spa/plugins/alsa/alsa-pcm-sink.c +++ b/spa/plugins/alsa/alsa-pcm-sink.c @@ -162,6 +162,13 @@ static int impl_node_enum_params(void *object, int seq, SPA_PROP_INFO_name, SPA_POD_String("Use the driver channelmap"), SPA_PROP_INFO_type, SPA_POD_Bool(p->use_chmap)); break; + case 6: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_PropInfo, id, + SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_latencyOffsetNsec), + SPA_PROP_INFO_name, SPA_POD_String("Latency offset (ns)"), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0, 0, INT64_MAX)); + break; default: return 0; } @@ -180,7 +187,8 @@ static int impl_node_enum_params(void *object, int seq, SPA_PROP_cardName, SPA_POD_Stringn(p->card_name, sizeof(p->card_name)), SPA_PROP_minLatency, SPA_POD_Int(p->min_latency), SPA_PROP_maxLatency, SPA_POD_Int(p->max_latency), - SPA_PROP_START_CUSTOM, SPA_POD_Bool(p->use_chmap)); + SPA_PROP_START_CUSTOM, SPA_POD_Bool(p->use_chmap), + SPA_PROP_latencyOffsetNsec, SPA_POD_Long(this->process_latency.ns)); break; default: return 0; @@ -252,6 +260,29 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) return 0; } +static void handle_process_latency(struct state *this, + const struct spa_process_latency_info *info) +{ + bool ns_changed = this->process_latency.ns != info->ns; + + if (this->process_latency.quantum == info->quantum && + this->process_latency.rate == info->rate && + !ns_changed) + return; + + this->process_latency = *info; + + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; + if (ns_changed) + this->params[NODE_Props].flags ^= SPA_PARAM_INFO_SERIAL; + 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); +} + static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param) { @@ -264,17 +295,24 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, case SPA_PARAM_Props: { struct props *p = &this->props; + struct spa_process_latency_info info; if (param == NULL) { reset_props(p); return 0; } + + info = this->process_latency; + spa_pod_parse_object(param, SPA_TYPE_OBJECT_Props, NULL, SPA_PROP_device, SPA_POD_OPT_Stringn(p->device, sizeof(p->device)), SPA_PROP_minLatency, SPA_POD_OPT_Int(&p->min_latency), SPA_PROP_maxLatency, SPA_POD_OPT_Int(&p->max_latency), + SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&info.ns), SPA_PROP_START_CUSTOM, SPA_POD_OPT_Bool(&p->use_chmap)); + + handle_process_latency(this, &info); break; } case SPA_PARAM_ProcessLatency: @@ -282,15 +320,8 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, 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); + handle_process_latency(this, &info); break; } default: diff --git a/spa/plugins/alsa/alsa-pcm-source.c b/spa/plugins/alsa/alsa-pcm-source.c index b0d1d408b..1c2328285 100644 --- a/spa/plugins/alsa/alsa-pcm-source.c +++ b/spa/plugins/alsa/alsa-pcm-source.c @@ -161,6 +161,13 @@ static int impl_node_enum_params(void *object, int seq, SPA_PROP_INFO_name, SPA_POD_String("Use the driver channelmap"), SPA_PROP_INFO_type, SPA_POD_Bool(p->use_chmap)); break; + case 6: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_PropInfo, id, + SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_latencyOffsetNsec), + SPA_PROP_INFO_name, SPA_POD_String("Latency offset (ns)"), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0, 0, INT64_MAX)); + break; default: return 0; } @@ -176,7 +183,8 @@ static int impl_node_enum_params(void *object, int seq, SPA_PROP_cardName, SPA_POD_Stringn(p->card_name, sizeof(p->card_name)), SPA_PROP_minLatency, SPA_POD_Int(p->min_latency), SPA_PROP_maxLatency, SPA_POD_Int(p->max_latency), - SPA_PROP_START_CUSTOM, SPA_POD_Bool(p->use_chmap)); + SPA_PROP_START_CUSTOM, SPA_POD_Bool(p->use_chmap), + SPA_PROP_latencyOffsetNsec, SPA_POD_Long(this->process_latency.ns)); break; default: return 0; @@ -249,6 +257,29 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) return 0; } +static void handle_process_latency(struct state *this, + const struct spa_process_latency_info *info) +{ + bool ns_changed = this->process_latency.ns != info->ns; + + if (this->process_latency.quantum == info->quantum && + this->process_latency.rate == info->rate && + !ns_changed) + return; + + this->process_latency = *info; + + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; + if (ns_changed) + this->params[NODE_Props].flags ^= SPA_PARAM_INFO_SERIAL; + 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); +} + static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, const struct spa_pod *param) { @@ -261,17 +292,24 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, case SPA_PARAM_Props: { struct props *p = &this->props; + struct spa_process_latency_info info; if (param == NULL) { reset_props(p); return 0; } + + info = this->process_latency; + spa_pod_parse_object(param, SPA_TYPE_OBJECT_Props, NULL, SPA_PROP_device, SPA_POD_OPT_Stringn(p->device, sizeof(p->device)), SPA_PROP_minLatency, SPA_POD_OPT_Int(&p->min_latency), SPA_PROP_maxLatency, SPA_POD_OPT_Int(&p->max_latency), + SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&info.ns), SPA_PROP_START_CUSTOM, SPA_POD_OPT_Bool(&p->use_chmap)); + + handle_process_latency(this, &info); break; } case SPA_PARAM_ProcessLatency: @@ -279,15 +317,8 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, 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); + handle_process_latency(this, &info); break; } default: