diff --git a/spa/include/spa/param/param.h b/spa/include/spa/param/param.h index e33382aa7..7bd2291e3 100644 --- a/spa/include/spa/param/param.h +++ b/spa/include/spa/param/param.h @@ -37,6 +37,7 @@ enum spa_param_type { SPA_PARAM_Buffers, /**< buffer configurations */ SPA_PARAM_Meta, /**< allowed metadata for buffers */ SPA_PARAM_IO, /**< configurable IO areas */ + SPA_PARAM_Profile, /**< port profile configuration */ }; /** Properties for SPA_TYPE_OBJECT_ParamList */ @@ -69,6 +70,13 @@ enum spa_param_io { SPA_PARAM_IO_size, /**< size of the io area */ }; +/** properties for SPA_TYPE_OBJECT_ParamProfile */ +enum spa_param_profile { + SPA_PARAM_PROFILE_START, /**< object id, one of enum spa_param_type */ + SPA_PARAM_PROFILE_direction, /**< direction, input/output */ + SPA_PARAM_PROFILE_format, /**< profile format specification */ +}; + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/spa/include/spa/param/type-info.h b/spa/include/spa/param/type-info.h index a9e4f62d1..512fa578b 100644 --- a/spa/include/spa/param/type-info.h +++ b/spa/include/spa/param/type-info.h @@ -43,6 +43,7 @@ static const struct spa_type_info spa_type_param[] = { { SPA_PARAM_Buffers, SPA_TYPE_PARAM_ID_BASE "Buffers", SPA_TYPE_Int, }, { SPA_PARAM_Meta, SPA_TYPE_PARAM_ID_BASE "Meta", SPA_TYPE_Int, }, { SPA_PARAM_IO, SPA_TYPE_PARAM_ID_BASE "IO", SPA_TYPE_Int, }, + { SPA_PARAM_Profile, SPA_TYPE_PARAM_ID_BASE "Profile", SPA_TYPE_Int, }, { 0, NULL, }, }; @@ -250,6 +251,16 @@ static const struct spa_type_info spa_type_param_buffers[] = { { 0, NULL, }, }; +#define SPA_TYPE_PARAM__Profile SPA_TYPE_PARAM_BASE "Profile" +#define SPA_TYPE_PARAM_PROFILE_BASE SPA_TYPE_PARAM__Profile ":" + +static const struct spa_type_info spa_type_param_profile[] = { + { SPA_PARAM_PROFILE_START, SPA_TYPE_PARAM_PROFILE_BASE, SPA_TYPE_Id, spa_type_param, }, + { SPA_PARAM_PROFILE_direction, SPA_TYPE_PARAM_PROFILE_BASE "direction", SPA_TYPE_Id, spa_type_direction }, + { SPA_PARAM_PROFILE_format, SPA_TYPE_PARAM_PROFILE_BASE "format", SPA_TYPE_Object, NULL, }, + { 0, NULL, }, +}; + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/spa/include/spa/utils/type-info.h b/spa/include/spa/utils/type-info.h index 44f231934..76bbe80cf 100644 --- a/spa/include/spa/utils/type-info.h +++ b/spa/include/spa/utils/type-info.h @@ -71,6 +71,18 @@ struct spa_type_info { #define SPA_TYPE__Command SPA_TYPE_OBJECT_BASE "Command" #define SPA_TYPE_COMMAND_BASE SPA_TYPE__Command ":" +#include + +/* base for parameter object enumerations */ +#define SPA_TYPE__Direction SPA_TYPE_ENUM_BASE "Direction" +#define SPA_TYPE_DIRECTION_BASE SPA_TYPE__Direction ":" + +static const struct spa_type_info spa_type_direction[] = { + { SPA_DIRECTION_INPUT, SPA_TYPE_DIRECTION_BASE "Input", SPA_TYPE_Int, }, + { SPA_DIRECTION_OUTPUT, SPA_TYPE_DIRECTION_BASE "Output", SPA_TYPE_Int, }, + { 0, NULL, } +}; + #include #include #include @@ -147,6 +159,7 @@ static const struct spa_type_info spa_types[] = { { SPA_TYPE_OBJECT_ParamBuffers, SPA_TYPE_PARAM__Buffers, SPA_TYPE_Object, spa_type_param_buffers, }, { SPA_TYPE_OBJECT_ParamMeta, SPA_TYPE_PARAM__Meta, SPA_TYPE_Object, spa_type_param_meta }, { SPA_TYPE_OBJECT_ParamIO, SPA_TYPE_PARAM__IO, SPA_TYPE_Object, spa_type_param_io }, + { SPA_TYPE_OBJECT_ParamProfile, SPA_TYPE_PARAM__Profile, SPA_TYPE_Object, spa_type_param_profile }, { 0, NULL, } }; diff --git a/spa/include/spa/utils/type.h b/spa/include/spa/utils/type.h index c2e5b09cf..6c3e67f68 100644 --- a/spa/include/spa/utils/type.h +++ b/spa/include/spa/utils/type.h @@ -89,6 +89,7 @@ enum { SPA_TYPE_OBJECT_ParamBuffers, SPA_TYPE_OBJECT_ParamMeta, SPA_TYPE_OBJECT_ParamIO, + SPA_TYPE_OBJECT_ParamProfile, /* vendor extensions */ SPA_TYPE_VENDOR_PipeWire = 0x02000000, diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index 7c616e44b..777e111f8 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -411,7 +411,40 @@ static int impl_node_enum_params(struct spa_node *node, static int impl_node_set_param(struct spa_node *node, uint32_t id, uint32_t flags, const struct spa_pod *param) { - return -ENOTSUP; + int res = 0; + struct impl *this; + + spa_return_val_if_fail(node != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + + switch (id) { + case SPA_PARAM_Profile: + { + enum spa_direction direction; + + if (spa_pod_object_parse(param, + ":", SPA_PARAM_PROFILE_direction, "I", &direction, + NULL) < 0) + return -EINVAL; + + switch (direction) { + case SPA_DIRECTION_INPUT: + case SPA_DIRECTION_OUTPUT: + res = spa_node_set_param(this->fmt[direction], + id, flags, param); + break; + default: + res = -EINVAL; + break; + } + break; + } + default: + res = -ENOTSUP; + break; + } + return res; } static int impl_node_send_command(struct spa_node *node, const struct spa_command *command) diff --git a/spa/plugins/audioconvert/merger.c b/spa/plugins/audioconvert/merger.c index 3e3e8f876..4cf975632 100644 --- a/spa/plugins/audioconvert/merger.c +++ b/spa/plugins/audioconvert/merger.c @@ -30,10 +30,12 @@ #include #include #include +#include #define NAME "merger" -#define DEFAULT_RATE 48000 +#define DEFAULT_RATE 48000 +#define DEFAULT_CHANNELS 2 #define MAX_SAMPLES 1024 #define MAX_BUFFERS 64 @@ -47,7 +49,6 @@ struct buffer { }; struct port { - bool valid; uint32_t id; struct spa_io_buffers *io; @@ -56,6 +57,7 @@ struct port { struct spa_port_info info; struct spa_dict info_props; struct spa_dict_item info_props_items[2]; + char position[8]; bool have_format; struct spa_audio_info format; @@ -80,28 +82,48 @@ struct impl { void *user_data; int port_count; - int last_port; struct port in_ports[MAX_PORTS]; struct port out_ports[1]; - int n_formats; - bool have_format; - struct spa_audio_info format; - bool force_rate; - bool started; convert_func_t convert; float empty[MAX_SAMPLES]; }; -#define CHECK_FREE_IN_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS && !this->in_ports[(p)].valid) -#define CHECK_IN_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS && this->in_ports[(p)].valid) -#define CHECK_OUT_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) == 0) -#define CHECK_PORT(this,d,p) (CHECK_OUT_PORT(this,d,p) || CHECK_IN_PORT (this,d,p)) -#define GET_IN_PORT(this,p) (&this->in_ports[p]) -#define GET_OUT_PORT(this,p) (&this->out_ports[p]) -#define GET_PORT(this,d,p) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,p) : GET_OUT_PORT(this,p)) +#define CHECK_IN_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < this->port_count) +#define CHECK_OUT_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) == 0) +#define CHECK_PORT(this,d,p) (CHECK_OUT_PORT(this,d,p) || CHECK_IN_PORT (this,d,p)) +#define GET_IN_PORT(this,p) (&this->in_ports[p]) +#define GET_OUT_PORT(this,p) (&this->out_ports[p]) +#define GET_PORT(this,d,p) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,p) : GET_OUT_PORT(this,p)) + +static int init_port(struct impl *this, uint32_t port_id, uint32_t rate, uint32_t position) +{ + struct port *port = GET_IN_PORT(this, port_id); + port->id = port_id; + + snprintf(port->position, 7, "%s", rindex(spa_type_audio_channel[position].name, ':')+1); + + port->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; + port->info_props_items[0] = SPA_DICT_ITEM_INIT("port.dsp", "32 bit float mono audio"); + port->info_props_items[1] = SPA_DICT_ITEM_INIT("port.channel", port->position); + port->info_props = SPA_DICT_INIT(port->info_props_items, 2); + port->info.props = &port->info_props; + + port->n_buffers = 0; + port->have_format = false; + port->format.media_type = SPA_MEDIA_TYPE_audio; + port->format.media_subtype = SPA_MEDIA_SUBTYPE_raw; + port->format.info.raw.format = SPA_AUDIO_FORMAT_F32P; + port->format.info.raw.rate = rate; + port->format.info.raw.channels = 1; + port->format.info.raw.position[0] = position; + + spa_log_debug(this->log, NAME " %p: add port %d", this, port_id); + + return 0; +} static int impl_node_enum_params(struct spa_node *node, uint32_t id, uint32_t *index, @@ -109,13 +131,89 @@ static int impl_node_enum_params(struct spa_node *node, struct spa_pod **param, struct spa_pod_builder *builder) { - return -ENOTSUP; + spa_return_val_if_fail(node != NULL, -EINVAL); + + switch (id) { + case SPA_PARAM_List: + { + uint32_t list[] = { SPA_PARAM_Profile }; + + if (*index < SPA_N_ELEMENTS(list)) + param = spa_pod_builder_object(builder, + SPA_TYPE_OBJECT_ParamList, id, + SPA_PARAM_LIST_id, &SPA_POD_Id(list[*index]), + 0); + else + return 0; + break; + } + default: + return 0; + } + (*index)++; + + return 1; } static int impl_node_set_param(struct spa_node *node, uint32_t id, uint32_t flags, const struct spa_pod *param) { - return -ENOTSUP; + struct impl *this; + int res; + + spa_return_val_if_fail(node != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + + switch (id) { + case SPA_PARAM_Profile: + { + struct spa_audio_info info = { 0, }; + struct port *port; + struct spa_pod *format; + int i; + + if (spa_pod_object_parse(param, + ":", SPA_PARAM_PROFILE_format, "P", &format, + NULL) < 0) + return -EINVAL; + + if (!SPA_POD_IS_OBJECT_TYPE(format, SPA_TYPE_OBJECT_Format)) + return -EINVAL; + + if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) + return res; + + if (info.media_type != SPA_MEDIA_TYPE_audio || + info.media_subtype != SPA_MEDIA_SUBTYPE_raw) + return -EINVAL; + + if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) + return -EINVAL; + + port = GET_OUT_PORT(this, 0); + if (port->have_format && memcmp(&port->format, &info, sizeof(info)) == 0) + return 0; + + port->have_format = true; + port->format = info; + + this->port_count = info.info.raw.channels; + for (i = 0; i < this->port_count; i++) { + init_port(this, i, info.info.raw.rate, + info.info.raw.position[i]); + } + + if (this->callbacks && this->callbacks->event) + this->callbacks->event(this->user_data, + &SPA_NODE_EVENT_INIT(SPA_NODE_EVENT_PortsChanged)); + + return 0; + } + default: + return -ENOENT; + } + return 0; } static int impl_node_send_command(struct spa_node *node, const struct spa_command *command) @@ -173,7 +271,7 @@ impl_node_get_n_ports(struct spa_node *node, if (n_input_ports) *n_input_ports = this->port_count; if (max_input_ports) - *max_input_ports = MAX_PORTS; + *max_input_ports = this->port_count; if (n_output_ports) *n_output_ports = 1; if (max_output_ports) @@ -190,17 +288,16 @@ impl_node_get_port_ids(struct spa_node *node, uint32_t n_output_ids) { struct impl *this; - int i, idx; + int i; spa_return_val_if_fail(node != NULL, -EINVAL); this = SPA_CONTAINER_OF(node, struct impl, node); if (input_ids) { - for (i = 0, idx = 0; i < this->last_port && idx < n_input_ids; i++) { - if (this->in_ports[i].valid) - input_ids[idx++] = i; - } + n_input_ids = SPA_MIN(n_input_ids, this->port_count); + for (i = 0; i < n_input_ids; i++) + input_ids[i] = i; } if (n_output_ids > 0 && output_ids) output_ids[0] = 0; @@ -210,73 +307,15 @@ impl_node_get_port_ids(struct spa_node *node, static int impl_node_add_port(struct spa_node *node, enum spa_direction direction, uint32_t port_id) { - struct impl *this; - struct port *port; - spa_return_val_if_fail(node != NULL, -EINVAL); - - this = SPA_CONTAINER_OF(node, struct impl, node); - - spa_return_val_if_fail(CHECK_FREE_IN_PORT(this, direction, port_id), -EINVAL); - - port = GET_IN_PORT (this, port_id); - port->valid = true; - port->id = port_id; - - port->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | - SPA_PORT_INFO_FLAG_REMOVABLE; - port->info_props_items[0] = SPA_DICT_ITEM_INIT("port.dsp", "32 bit float mono audio"); - port->info_props = SPA_DICT_INIT(port->info_props_items, 1); - port->info.props = &port->info_props; - - this->port_count++; - if (this->last_port <= port_id) - this->last_port = port_id + 1; - - port->have_format = false; - this->have_format = false; - - spa_log_debug(this->log, NAME " %p: add port %d", this, port_id); - - return 0; + return -ENOTSUP; } static int impl_node_remove_port(struct spa_node *node, enum spa_direction direction, uint32_t port_id) { - struct impl *this; - struct port *port; - spa_return_val_if_fail(node != NULL, -EINVAL); - - this = SPA_CONTAINER_OF(node, struct impl, node); - - spa_return_val_if_fail(CHECK_IN_PORT(this, direction, port_id), -EINVAL); - - port = GET_IN_PORT (this, port_id); - - this->port_count--; - if (port->have_format) { - if (--this->n_formats == 0) { - this->have_format = false; - this->convert = NULL; - } - } - - spa_memzero(port, sizeof(struct port)); - - if (port_id == this->last_port + 1) { - int i; - - for (i = this->last_port; i >= 0; i--) - if (GET_IN_PORT (this, i)->valid) - break; - - this->last_port = i + 1; - } - spa_log_debug(this->log, NAME " %p: remove port %d", this, port_id); - - return 0; + return -ENOTSUP; } static int @@ -308,23 +347,21 @@ static int port_enum_formats(struct spa_node *node, struct spa_pod_builder *builder) { struct impl *this = SPA_CONTAINER_OF(node, struct impl, node); - void *rate; + struct port *port = GET_PORT(this, direction, port_id); switch (*index) { case 0: - if (this->have_format || this->force_rate) { - rate = &SPA_POD_Int(this->format.info.raw.rate); - } - else { - rate = &SPA_POD_CHOICE_RANGE_Int(DEFAULT_RATE, 1, INT32_MAX); - } - if (direction == SPA_DIRECTION_OUTPUT) { - *param = spa_pod_builder_object(builder, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_audio), - SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - SPA_FORMAT_AUDIO_format, &SPA_POD_CHOICE_ENUM_Id(13, + if (port->have_format) { + *param = spa_format_audio_raw_build(builder, + SPA_PARAM_EnumFormat, &port->format.info.raw); + } + else { + *param = spa_pod_builder_object(builder, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, + SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_AUDIO_format, &SPA_POD_CHOICE_ENUM_Id(13, SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F32P, @@ -338,19 +375,17 @@ static int port_enum_formats(struct spa_node *node, SPA_AUDIO_FORMAT_S16P, SPA_AUDIO_FORMAT_U8, SPA_AUDIO_FORMAT_U8P), - SPA_FORMAT_AUDIO_rate, rate, - SPA_FORMAT_AUDIO_channels, &SPA_POD_Int(this->port_count), - 0); + SPA_FORMAT_AUDIO_rate, &SPA_POD_CHOICE_RANGE_Int( + DEFAULT_RATE, 1, INT32_MAX), + SPA_FORMAT_AUDIO_channels, &SPA_POD_CHOICE_RANGE_Int( + DEFAULT_CHANNELS, 1, MAX_PORTS), + 0); + } } else { - *param = spa_pod_builder_object(builder, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_audio), - SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - SPA_FORMAT_AUDIO_format, &SPA_POD_Id(SPA_AUDIO_FORMAT_F32P), - SPA_FORMAT_AUDIO_rate, rate, - SPA_FORMAT_AUDIO_channels, &SPA_POD_Int(1), - 0); + struct port *other = GET_IN_PORT(this, port_id); + *param = spa_format_audio_raw_build(builder, + SPA_PARAM_EnumFormat, &other->format.info.raw); } break; default: @@ -387,7 +422,7 @@ impl_node_port_enum_params(struct spa_node *node, next: spa_pod_builder_init(&b, buffer, sizeof(buffer)); - spa_log_debug(this->log, NAME " %p: enum param %d %d", this, id, this->have_format); + spa_log_debug(this->log, NAME " %p: enum param %d %d", this, id, port->have_format); switch (id) { case SPA_PARAM_List: @@ -486,37 +521,21 @@ static int clear_buffers(struct impl *this, struct port *port) return 0; } -static struct port *find_in_port(struct impl *this) -{ - int i; - struct port *port; - - for (i = 0; i < this->last_port; i++) { - port = &this->in_ports[i]; - if (port->have_format) - return port; - } - return NULL; -} - static int setup_convert(struct impl *this) { const struct conv_info *conv; - struct port *inport, *outport; + struct port *outport; uint32_t src_fmt, dst_fmt; outport = GET_OUT_PORT(this, 0); - inport = find_in_port(this); - if (inport == NULL || !outport->have_format) - return -EINVAL; - src_fmt = inport->format.info.raw.format; + src_fmt = SPA_AUDIO_FORMAT_F32P; dst_fmt = outport->format.info.raw.format; spa_log_info(this->log, NAME " %p: %s/%d@%dx%d->%s/%d@%d", this, spa_debug_type_find_name(spa_type_audio_format, src_fmt), - inport->format.info.raw.channels, - inport->format.info.raw.rate, + 1, + outport->format.info.raw.rate, this->port_count, spa_debug_type_find_name(spa_type_audio_format, dst_fmt), outport->format.info.raw.channels, @@ -561,13 +580,11 @@ static int port_set_format(struct spa_node *node, port = GET_PORT(this, direction, port_id); - spa_log_debug(this->log, NAME " %p: set format %d", this, this->have_format); + spa_log_debug(this->log, NAME " %p: set format", this); if (format == NULL) { if (port->have_format) { port->have_format = false; - if (--this->n_formats == 0) - this->have_format = false; clear_buffers(this, port); } } else { @@ -583,19 +600,18 @@ static int port_set_format(struct spa_node *node, if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) return -EINVAL; - if ((this->have_format || this->force_rate) && - info.info.raw.rate != this->format.info.raw.rate) - return -EINVAL; - - if (direction == SPA_DIRECTION_OUTPUT) { - if (info.info.raw.channels != this->port_count) + if (direction == SPA_DIRECTION_INPUT) { + if (info.info.raw.rate != port->format.info.raw.rate) return -EINVAL; - } else { if (info.info.raw.format != SPA_AUDIO_FORMAT_F32P) return -EINVAL; if (info.info.raw.channels != 1) return -EINVAL; } + else { + if (info.info.raw.channels != this->port_count) + return -EINVAL; + } port->format = info; port->stride = calc_width(&info); @@ -608,16 +624,10 @@ static int port_set_format(struct spa_node *node, } spa_log_debug(this->log, NAME " %p: %d %d %d", this, port_id, port->stride, port->blocks); - this->have_format = true; - this->format = info; + if (direction == SPA_DIRECTION_OUTPUT) + setup_convert(this); - if (!port->have_format) { - this->n_formats++; - port->have_format = true; - spa_log_debug(this->log, NAME " %p: set format on port %d", this, port_id); - } - - setup_convert(this); + port->have_format = true; } return 0; @@ -837,7 +847,7 @@ static int impl_node_process(struct spa_node *node) /* produce more output if possible */ n_src_datas = 0; - for (i = 0; i < this->last_port; i++) { + for (i = 0; i < this->port_count; i++) { struct port *inport = GET_IN_PORT(this, i); struct spa_io_buffers *inio; @@ -942,8 +952,6 @@ impl_init(const struct spa_handle_factory *factory, struct impl *this; struct port *port; uint32_t i; - const char *str; - int rate; spa_return_val_if_fail(factory != NULL, -EINVAL); spa_return_val_if_fail(handle != NULL, -EINVAL); @@ -958,17 +966,9 @@ impl_init(const struct spa_handle_factory *factory, this->log = support[i].data; } - if ((str = spa_dict_lookup(info, "node.format.rate"))) - if ((rate = atoi(str)) != 0) { - this->format.info.raw.rate = rate; - this->force_rate = true; - } - this->node = impl_node; - this->have_format = false; port = GET_OUT_PORT(this, 0); - port->valid = true; port->id = 0; port->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; spa_list_init(&port->queue); diff --git a/spa/plugins/audioconvert/splitter.c b/spa/plugins/audioconvert/splitter.c index e290aa225..2d868e73e 100644 --- a/spa/plugins/audioconvert/splitter.c +++ b/spa/plugins/audioconvert/splitter.c @@ -30,10 +30,13 @@ #include #include #include +#include #define NAME "splitter" -#define DEFAULT_RATE 48000 +#define DEFAULT_RATE 48000 +#define DEFAULT_CHANNELS 2 +#define DEFAULT_MASK (1LL << SPA_AUDIO_CHANNEL_FL) | (1LL << SPA_AUDIO_CHANNEL_FR) #define MAX_SAMPLES 1024 #define MAX_BUFFERS 64 @@ -47,7 +50,6 @@ struct buffer { }; struct port { - bool valid; uint32_t id; struct spa_io_buffers *io; @@ -56,6 +58,7 @@ struct port { struct spa_port_info info; struct spa_dict info_props; struct spa_dict_item info_props_items[2]; + char position[8]; bool have_format; struct spa_audio_info format; @@ -80,14 +83,8 @@ struct impl { void *user_data; struct port in_ports[1]; - int port_count; - int last_port; struct port out_ports[MAX_PORTS]; - - int n_formats; - bool have_format; - struct spa_audio_info format; - bool force_rate; + int port_count; bool started; convert_func_t convert; @@ -95,13 +92,40 @@ struct impl { float empty[MAX_SAMPLES]; }; -#define CHECK_FREE_OUT_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) < MAX_PORTS && !this->out_ports[(p)].valid) -#define CHECK_OUT_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) < MAX_PORTS && this->out_ports[(p)].valid) -#define CHECK_IN_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) == 0) -#define CHECK_PORT(this,d,p) (CHECK_OUT_PORT(this,d,p) || CHECK_IN_PORT (this,d,p)) -#define GET_IN_PORT(this,p) (&this->in_ports[p]) -#define GET_OUT_PORT(this,p) (&this->out_ports[p]) -#define GET_PORT(this,d,p) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,p) : GET_OUT_PORT(this,p)) +#define CHECK_OUT_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) < this->port_count) +#define CHECK_IN_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) == 0) +#define CHECK_PORT(this,d,p) (CHECK_OUT_PORT(this,d,p) || CHECK_IN_PORT (this,d,p)) +#define GET_IN_PORT(this,p) (&this->in_ports[p]) +#define GET_OUT_PORT(this,p) (&this->out_ports[p]) +#define GET_PORT(this,d,p) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,p) : GET_OUT_PORT(this,p)) + +static int init_port(struct impl *this, uint32_t port_id, uint32_t rate, uint32_t position) +{ + struct port *port = GET_OUT_PORT(this, port_id); + + port->id = port_id; + port->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; + snprintf(port->position, 7, "%s", rindex(spa_type_audio_channel[position].name, ':')+1); + port->info_props_items[0] = SPA_DICT_ITEM_INIT("port.dsp", "32 bit float mono audio"); + port->info_props_items[1] = SPA_DICT_ITEM_INIT("port.channel", port->position); + port->info_props = SPA_DICT_INIT(port->info_props_items, 2); + port->info.props = &port->info_props; + + spa_list_init(&port->queue); + + port->n_buffers = 0; + port->have_format = false; + port->format.media_type = SPA_MEDIA_TYPE_audio; + port->format.media_subtype = SPA_MEDIA_SUBTYPE_raw; + port->format.info.raw.format = SPA_AUDIO_FORMAT_F32P; + port->format.info.raw.rate = rate; + port->format.info.raw.channels = 1; + port->format.info.raw.position[0] = position; + + spa_log_debug(this->log, NAME " %p: init port %d %s", this, port_id, port->position); + + return 0; +} static int impl_node_enum_params(struct spa_node *node, uint32_t id, uint32_t *index, @@ -109,13 +133,87 @@ static int impl_node_enum_params(struct spa_node *node, struct spa_pod **param, struct spa_pod_builder *builder) { - return -ENOTSUP; + spa_return_val_if_fail(node != NULL, -EINVAL); + + switch (id) { + case SPA_PARAM_List: + { + uint32_t list[] = { SPA_PARAM_Profile }; + + if (*index < SPA_N_ELEMENTS(list)) + param = spa_pod_builder_object(builder, + SPA_TYPE_OBJECT_ParamList, id, + SPA_PARAM_LIST_id, &SPA_POD_Id(list[*index]), + 0); + else + return 0; + break; + } + default: + return 0; + } + (*index)++; + + return 1; } static int impl_node_set_param(struct spa_node *node, uint32_t id, uint32_t flags, const struct spa_pod *param) { - return -ENOTSUP; + struct impl *this; + int res; + + spa_return_val_if_fail(node != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + + switch (id) { + case SPA_PARAM_Profile: + { + struct port *port; + struct spa_audio_info info = { 0, }; + struct spa_pod *format; + int i; + + if (spa_pod_object_parse(param, + ":", SPA_PARAM_PROFILE_format, "P", &format, + NULL) < 0) + return -EINVAL; + + if (!SPA_POD_IS_OBJECT_TYPE(format, SPA_TYPE_OBJECT_Format)) + return -EINVAL; + + if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) + return res; + + if (info.media_type != SPA_MEDIA_TYPE_audio || + info.media_subtype != SPA_MEDIA_SUBTYPE_raw) + return -EINVAL; + + if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) + return -EINVAL; + + port = GET_IN_PORT(this, 0); + if (port->have_format && memcmp(&port->format, &info, sizeof(info)) == 0) + return 0; + + this->port_count = info.info.raw.channels; + for (i = 0; i < this->port_count; i++) { + init_port(this, i, info.info.raw.rate, + info.info.raw.position[i]); + } + port->have_format = true; + port->format = info; + + if (this->callbacks && this->callbacks->event) + this->callbacks->event(this->user_data, + &SPA_NODE_EVENT_INIT(SPA_NODE_EVENT_PortsChanged)); + return 0; + } + default: + return -ENOENT; + } + return 0; } static int impl_node_send_command(struct spa_node *node, const struct spa_command *command) @@ -177,7 +275,7 @@ impl_node_get_n_ports(struct spa_node *node, if (n_output_ports) *n_output_ports = this->port_count; if (max_output_ports) - *max_output_ports = MAX_PORTS; + *max_output_ports = this->port_count; return 0; } @@ -190,7 +288,7 @@ impl_node_get_port_ids(struct spa_node *node, uint32_t n_output_ids) { struct impl *this; - int i, idx; + int i; spa_return_val_if_fail(node != NULL, -EINVAL); @@ -199,86 +297,24 @@ impl_node_get_port_ids(struct spa_node *node, if (n_input_ids > 0 && input_ids) input_ids[0] = 0; if (output_ids) { - for (i = 0, idx = 0; i < this->last_port && idx < n_output_ids; i++) { - if (this->out_ports[i].valid) - output_ids[idx++] = i; - } + n_output_ids = SPA_MIN(n_output_ids, this->port_count); + for (i = 0; i < n_output_ids; i++) + output_ids[i] = i; } return 0; } static int impl_node_add_port(struct spa_node *node, enum spa_direction direction, uint32_t port_id) { - struct impl *this; - struct port *port; - spa_return_val_if_fail(node != NULL, -EINVAL); - - this = SPA_CONTAINER_OF(node, struct impl, node); - - spa_return_val_if_fail(CHECK_FREE_OUT_PORT(this, direction, port_id), -EINVAL); - - port = GET_OUT_PORT (this, port_id); - port->valid = true; - port->id = port_id; - - port->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | - SPA_PORT_INFO_FLAG_REMOVABLE; - - port->info_props_items[0] = SPA_DICT_ITEM_INIT("port.dsp", "32 bit float mono audio"); - port->info_props = SPA_DICT_INIT(port->info_props_items, 1); - port->info.props = &port->info_props; - - spa_list_init(&port->queue); - - this->port_count++; - if (this->last_port <= port_id) - this->last_port = port_id + 1; - - port->have_format = false; - this->have_format = false; - - spa_log_debug(this->log, NAME " %p: add port %d", this, port_id); - - return 0; + return -ENOTSUP; } static int impl_node_remove_port(struct spa_node *node, enum spa_direction direction, uint32_t port_id) { - struct impl *this; - struct port *port; - spa_return_val_if_fail(node != NULL, -EINVAL); - - this = SPA_CONTAINER_OF(node, struct impl, node); - - spa_return_val_if_fail(CHECK_OUT_PORT(this, direction, port_id), -EINVAL); - - port = GET_OUT_PORT (this, port_id); - - this->port_count--; - if (port->have_format) { - if (--this->n_formats == 0) { - this->have_format = false; - this->convert = NULL; - } - } - - spa_memzero(port, sizeof(struct port)); - - if (port_id == this->last_port + 1) { - int i; - - for (i = this->last_port; i >= 0; i--) - if (GET_OUT_PORT (this, i)->valid) - break; - - this->last_port = i + 1; - } - spa_log_debug(this->log, NAME " %p: remove port %d", this, port_id); - - return 0; + return -ENOTSUP; } static int @@ -311,21 +347,21 @@ static int port_enum_formats(struct spa_node *node, struct spa_pod_builder *builder) { struct impl *this = SPA_CONTAINER_OF(node, struct impl, node); - struct spa_pod *prate; + struct port *port = GET_PORT(this, direction, port_id); switch (*index) { case 0: - if (this->have_format || this->force_rate) - prate = (struct spa_pod*)&SPA_POD_Int(this->format.info.raw.rate); - else - prate = (struct spa_pod*)&SPA_POD_CHOICE_RANGE_Int(DEFAULT_RATE, 1, INT32_MAX); - if (direction == SPA_DIRECTION_INPUT) { - *param = spa_pod_builder_object(builder, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_audio), - SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - SPA_FORMAT_AUDIO_format, &SPA_POD_CHOICE_ENUM_Id(18, + if (port->have_format) { + *param = spa_format_audio_raw_build(builder, + SPA_PARAM_EnumFormat, &port->format.info.raw); + } + else { + *param = spa_pod_builder_object(builder, + SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, + SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_AUDIO_format, &SPA_POD_CHOICE_ENUM_Id(18, SPA_AUDIO_FORMAT_F32, SPA_AUDIO_FORMAT_F32P, SPA_AUDIO_FORMAT_F32, @@ -344,19 +380,17 @@ static int port_enum_formats(struct spa_node *node, SPA_AUDIO_FORMAT_S16_OE, SPA_AUDIO_FORMAT_U8P, SPA_AUDIO_FORMAT_U8), - SPA_FORMAT_AUDIO_rate, prate, - SPA_FORMAT_AUDIO_channels, &SPA_POD_Int(this->port_count), - 0); + SPA_FORMAT_AUDIO_rate, &SPA_POD_CHOICE_RANGE_Int( + DEFAULT_RATE, 1, INT32_MAX), + SPA_FORMAT_AUDIO_channels, &SPA_POD_CHOICE_RANGE_Int( + DEFAULT_CHANNELS, 1, MAX_PORTS), + 0); + } } else { - *param = spa_pod_builder_object(builder, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, &SPA_POD_Id(SPA_MEDIA_TYPE_audio), - SPA_FORMAT_mediaSubtype, &SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - SPA_FORMAT_AUDIO_format, &SPA_POD_Id(SPA_AUDIO_FORMAT_F32P), - SPA_FORMAT_AUDIO_rate, prate, - SPA_FORMAT_AUDIO_channels, &SPA_POD_Int(1), - 0); + struct port *other = GET_OUT_PORT(this, port_id); + *param = spa_format_audio_raw_build(builder, + SPA_PARAM_EnumFormat, &other->format.info.raw); } break; default: @@ -393,7 +427,7 @@ impl_node_port_enum_params(struct spa_node *node, next: spa_pod_builder_init(&b, buffer, sizeof(buffer)); - spa_log_debug(this->log, NAME " %p: enum param %d %d", this, id, this->have_format); + spa_log_debug(this->log, NAME " %p: enum param %d", this, id); switch (id) { case SPA_PARAM_List: @@ -495,40 +529,24 @@ static int clear_buffers(struct impl *this, struct port *port) return 0; } -static struct port *find_out_port(struct impl *this) -{ - int i; - struct port *port; - - for (i = 0; i < this->last_port; i++) { - port = GET_OUT_PORT(this, i); - if (port->have_format) - return port; - } - return NULL; -} - static int setup_convert(struct impl *this) { const struct conv_info *conv; - struct port *inport, *outport; + struct port *inport; uint32_t src_fmt, dst_fmt; inport = GET_IN_PORT(this, 0); - outport = find_out_port(this); - if (outport == NULL || !inport->have_format) - return -EINVAL; src_fmt = inport->format.info.raw.format; - dst_fmt = outport->format.info.raw.format; + dst_fmt = SPA_AUDIO_FORMAT_F32P; spa_log_info(this->log, NAME " %p: %s/%d@%d->%s/%d@%dx%d", this, spa_debug_type_find_name(spa_type_audio_format, src_fmt), inport->format.info.raw.channels, inport->format.info.raw.rate, spa_debug_type_find_name(spa_type_audio_format, dst_fmt), - outport->format.info.raw.channels, - outport->format.info.raw.rate, + 1, + inport->format.info.raw.rate, this->port_count); conv = find_conv_info(src_fmt, dst_fmt, FEATURE_SSE); @@ -573,13 +591,11 @@ static int port_set_format(struct spa_node *node, port = GET_PORT(this, direction, port_id); - spa_log_debug(this->log, NAME " %p: set format %d", this, this->have_format); + spa_log_debug(this->log, NAME " %p: set format", this); if (format == NULL) { if (port->have_format) { port->have_format = false; - if (--this->n_formats == 0) - this->have_format = false; clear_buffers(this, port); } } else { @@ -595,22 +611,22 @@ static int port_set_format(struct spa_node *node, if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) return -EINVAL; - if ((this->have_format || this->force_rate) && - info.info.raw.rate != this->format.info.raw.rate) - return -EINVAL; - - if (direction == SPA_DIRECTION_INPUT) { - if (info.info.raw.channels != this->port_count) + if (direction == SPA_DIRECTION_OUTPUT) { + if (info.info.raw.rate != port->format.info.raw.rate) return -EINVAL; - } else { if (info.info.raw.format != SPA_AUDIO_FORMAT_F32P) return -EINVAL; if (info.info.raw.channels != 1) return -EINVAL; } + else { + if (info.info.raw.channels != this->port_count) + return -EINVAL; + } port->format = info; port->stride = calc_width(&info); + if (SPA_AUDIO_FORMAT_IS_PLANAR(info.info.raw.format)) { port->blocks = info.info.raw.channels; } else { @@ -619,16 +635,10 @@ static int port_set_format(struct spa_node *node, } spa_log_debug(this->log, NAME " %p: %d %d %d", this, port_id, port->stride, port->blocks); - this->have_format = true; - this->format = info; + if (direction == SPA_DIRECTION_INPUT) + setup_convert(this); - if (!port->have_format) { - this->n_formats++; - port->have_format = true; - spa_log_debug(this->log, NAME " %p: set format on port %d", this, port_id); - } - - setup_convert(this); + port->have_format = true; } return 0; @@ -848,7 +858,7 @@ static int impl_node_process(struct spa_node *node) dst_datas = alloca(sizeof(void*) * MAX_PORTS); n_dst_datas = 0; - for (i = 0; i < this->last_port; i++) { + for (i = 0; i < this->port_count; i++) { struct port *outport = GET_OUT_PORT(this, i); struct spa_io_buffers *outio; @@ -962,10 +972,7 @@ impl_init(const struct spa_handle_factory *factory, { struct impl *this; struct port *port; - uint32_t i; - const char *str; - int rate; - + int i; spa_return_val_if_fail(factory != NULL, -EINVAL); spa_return_val_if_fail(handle != NULL, -EINVAL); @@ -980,17 +987,9 @@ impl_init(const struct spa_handle_factory *factory, this->log = support[i].data; } - if ((str = spa_dict_lookup(info, "node.format.rate"))) - if ((rate = atoi(str)) != 0) { - this->format.info.raw.rate = rate; - this->force_rate = true; - } - this->node = impl_node; - this->have_format = false; port = GET_IN_PORT(this, 0); - port->valid = true; port->id = 0; port->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; diff --git a/src/examples/media-session.c b/src/examples/media-session.c index e93f207f7..8ad86c5c0 100644 --- a/src/examples/media-session.c +++ b/src/examples/media-session.c @@ -739,7 +739,7 @@ static int find_session(void *data, struct session *sess) return 0; } -static int link_nodes(struct node *peer, enum pw_direction direction, struct node *node) +static int link_nodes(struct node *peer, enum pw_direction direction, struct node *node, int max) { struct impl *impl = peer->obj.impl; struct port *p; @@ -752,6 +752,9 @@ static int link_nodes(struct node *peer, enum pw_direction direction, struct nod if (p->direction == direction) continue; + if (max-- == 0) + return 0; + props = pw_properties_new(NULL, NULL); if (p->direction == PW_DIRECTION_OUTPUT) { pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", node->obj.id); @@ -793,7 +796,10 @@ static int rescan_node(struct impl *impl, struct node *node) struct pw_node_info *info; struct node *peer; enum pw_direction direction; + struct spa_pod_builder b = { 0, }; struct spa_audio_info_raw audio_info; + struct spa_pod *param; + char buf[1024]; if (node->type == NODE_TYPE_DSP || node->type == NODE_TYPE_DEVICE) return 0; @@ -906,12 +912,26 @@ static int rescan_node(struct impl *impl, struct node *node) node->session = session; spa_list_append(&session->node_list, &node->session_link); - audio_info = node->format; - audio_info.format = SPA_AUDIO_FORMAT_F32P; - audio_info.rate = session->node->format.rate; - audio_info.channels = SPA_MIN(session->node->format.channels, audio_info.channels); + if (!exclusive) { + audio_info = node->format; + audio_info.format = SPA_AUDIO_FORMAT_F32P; + audio_info.rate = session->node->format.rate; + audio_info.channels = SPA_MIN(session->node->format.channels, audio_info.channels); - link_nodes(peer, direction, node); + spa_pod_builder_init(&b, buf, sizeof(buf)); + param = spa_pod_builder_object(&b, + SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, + SPA_PARAM_PROFILE_direction, &SPA_POD_Id(pw_direction_reverse(direction)), + SPA_PARAM_PROFILE_format, spa_format_audio_raw_build(&b, + SPA_PARAM_Format, &audio_info), + 0); + + pw_node_proxy_set_param((struct pw_node_proxy*)node->obj.proxy, + SPA_PARAM_Profile, 0, param); + } else { + audio_info.channels = 1; + } + link_nodes(peer, direction, node, audio_info.channels); return 1; } @@ -944,20 +964,18 @@ static void rescan_session(struct impl *impl, struct session *sess) if (sess->need_dsp && sess->dsp == NULL && sess->dsp_proxy == NULL) { struct pw_properties *props; struct node *node = sess->node; - int i; - uint64_t mask = 0; + struct spa_audio_info_raw info; + uint8_t buf[1024]; + struct spa_pod_builder b = { 0, }; + struct spa_pod *param; if (node->info->props == NULL) return; - for (i = 0; i < node->format.channels; i++) - mask |= 1UL << node->format.position[i]; + info = node->format; props = pw_properties_new_dict(node->info->props); pw_properties_setf(props, "audio-dsp.direction", "%d", sess->direction); - pw_properties_setf(props, "audio-dsp.channels", "%d", node->format.channels); - pw_properties_setf(props, "audio-dsp.channelmask", "%"PRIu64, mask); - pw_properties_setf(props, "audio-dsp.rate", "%d", node->format.rate); pw_properties_setf(props, "audio-dsp.maxbuffer", "%ld", MAX_QUANTUM_SIZE * sizeof(float)); pw_log_debug(NAME" %p: making audio dsp for session %d", impl, sess->id); @@ -971,6 +989,18 @@ static void rescan_session(struct impl *impl, struct session *sess) pw_properties_free(props); pw_proxy_add_proxy_listener(sess->dsp_proxy, &sess->listener, &dsp_node_events, sess); + + spa_pod_builder_init(&b, buf, sizeof(buf)); + param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &info); + param = spa_pod_builder_object(&b, + SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, + SPA_PARAM_PROFILE_direction, &SPA_POD_Id(pw_direction_reverse(sess->direction)), + SPA_PARAM_PROFILE_format, param, + 0); + + pw_node_proxy_set_param((struct pw_node_proxy*)sess->dsp_proxy, + SPA_PARAM_Profile, 0, param); + schedule_rescan(impl); } else { sess->starting = false; diff --git a/src/modules/module-audio-dsp.c b/src/modules/module-audio-dsp.c index 043f4350c..d04a0a242 100644 --- a/src/modules/module-audio-dsp.c +++ b/src/modules/module-audio-dsp.c @@ -96,8 +96,7 @@ static void *create_object(void *_data, struct factory_data *d = _data; struct pw_client *client; struct pw_node *dsp; - int res, channels, rate, maxbuffer; - uint64_t channelmask; + int res, maxbuffer; const char *str; enum pw_direction direction; struct node_data *nd; @@ -113,21 +112,6 @@ static void *create_object(void *_data, direction = pw_properties_parse_int(str); - if ((str = pw_properties_get(properties, "audio-dsp.channels")) == NULL) - goto no_props; - - channels = pw_properties_parse_int(str); - - if ((str = pw_properties_get(properties, "audio-dsp.channelmask")) == NULL) - goto no_props; - - channelmask = pw_properties_parse_uint64(str); - - if ((str = pw_properties_get(properties, "audio-dsp.rate")) == NULL) - goto no_props; - - rate = pw_properties_parse_int(str); - if ((str = pw_properties_get(properties, "audio-dsp.maxbuffer")) == NULL) goto no_props; @@ -136,7 +120,7 @@ static void *create_object(void *_data, dsp = pw_audio_dsp_new(pw_module_get_core(d->module), properties, direction, - channels, channelmask, rate, maxbuffer, + maxbuffer, sizeof(struct node_data)); if (dsp == NULL) diff --git a/src/modules/module-audio-dsp/audio-dsp.c b/src/modules/module-audio-dsp/audio-dsp.c index b1ab43e24..b539695c5 100644 --- a/src/modules/module-audio-dsp/audio-dsp.c +++ b/src/modules/module-audio-dsp/audio-dsp.c @@ -43,6 +43,7 @@ #define NAME "audio-dsp" #define PORT_BUFFERS 1 +#define MAX_BUFFER_SIZE 2048 extern const struct spa_handle_factory spa_floatmix_factory; @@ -55,6 +56,8 @@ struct buffer { struct node; struct port { + struct spa_list link; + struct pw_port *port; struct node *node; @@ -65,19 +68,23 @@ struct port { struct spa_handle *spa_handle; struct spa_node *spa_node; - float empty[4096]; + float empty[MAX_BUFFER_SIZE]; }; struct node { struct pw_core *core; + struct pw_node *node; + struct spa_hook node_listener; void *user_data; + enum pw_direction direction; + struct pw_properties *props; - int channels; - uint64_t channelmask; - int sample_rate; + struct spa_audio_info format; int max_buffer_size; + + struct spa_list ports; }; /** \endcond */ @@ -101,6 +108,7 @@ static void init_buffer(struct port *port, uint32_t id) b->datas[0].chunk->size = 0; b->datas[0].chunk->stride = 0; port->bufs[id] = &b->buf; + memset(port->empty, 0, sizeof(port->empty)); } static void init_port(struct port *p, enum spa_direction direction) @@ -146,46 +154,95 @@ static const struct pw_port_implementation port_implementation = { .use_buffers = port_use_buffers, }; -static int make_channel_name(struct node *n, char *channel_name, int i, uint64_t channelmask) +static void node_destroy(void *data) { - int j; - - sprintf(channel_name, "%d", i + 1); - for (j = 0; j < 64; j++) { - if (channelmask & (1LL << j)) { - if (i-- == 0) { - sprintf(channel_name, "%s", - rindex(spa_type_audio_channel[j].name, ':')+1); - return 1; - } - } - } - return 0; + struct node *n = data; + pw_properties_free(n->props); } +static void node_port_init(void *data, struct pw_port *port) +{ + struct node *n = data; + struct port *p; + const struct pw_properties *old; + enum pw_direction direction; + struct pw_properties *new; + const char *str; + void *iface; + const struct spa_support *support; + uint32_t n_support; + + direction = pw_port_get_direction(port); + if (direction == n->direction) + return; + + old = pw_port_get_properties(port); + + new = pw_properties_new( + "port.dsp", "32 bit float mono audio", + "port.physical", "1", + "port.terminal", "1", + NULL); + + if ((str = pw_properties_get(old, "port.channel")) != NULL) + pw_properties_setf(new, "port.name", "%s_%s", + direction == PW_DIRECTION_INPUT ? "playback" : "capture", + str); + + pw_properties_setf(new, "port.alias1", "%s_pcm:%s:%s%s", + pw_properties_get(n->props, "device.api"), + pw_properties_get(n->props, "device.name"), + direction == PW_DIRECTION_INPUT ? "in" : "out", + str ? str : "UNK"); + + pw_port_update_properties(port, &new->dict); + pw_properties_free(new); + + p = calloc(1, sizeof(struct port) + + spa_handle_factory_get_size(&spa_floatmix_factory, NULL)); + p->node = n; + p->port = port; + init_port(p, direction); + p->spa_handle = SPA_MEMBER(p, sizeof(struct port), struct spa_handle); + + support = pw_core_get_support(n->core, &n_support); + spa_handle_factory_init(&spa_floatmix_factory, + p->spa_handle, NULL, + support, n_support); + + spa_handle_get_interface(p->spa_handle, SPA_TYPE_INTERFACE_Node, &iface); + p->spa_node = iface; + + if (direction == PW_DIRECTION_INPUT) { + pw_log_debug("mix node %p", p->spa_node); + pw_port_set_mix(port, p->spa_node, PW_PORT_MIX_FLAG_MULTI); + port->implementation = &port_implementation; + port->implementation_data = p; + } + spa_list_append(&n->ports, &p->link); +} + +static const struct pw_node_events node_events = { + PW_VERSION_NODE_EVENTS, + .destroy = node_destroy, + .port_init = node_port_init, +}; + struct pw_node *pw_audio_dsp_new(struct pw_core *core, const struct pw_properties *props, enum pw_direction direction, - uint32_t channels, - uint64_t channelmask, - uint32_t sample_rate, uint32_t max_buffer_size, size_t user_data_size) { struct pw_node *node; - struct pw_port *port; struct node *n; const char *api, *alias, *plugged, *str; char node_name[128]; struct pw_properties *pr; - const struct spa_support *support; - uint32_t n_support; - void *iface; int i; if ((api = pw_properties_get(props, "device.api")) == NULL) goto error; - if ((alias = pw_properties_get(props, "device.name")) == NULL) goto error; @@ -201,6 +258,7 @@ struct pw_node *pw_audio_dsp_new(struct pw_core *core, "Audio/DSP/Playback" : "Audio/DSP/Capture", "device.name", alias, + "device.api", api, NULL); if ((plugged = pw_properties_get(props, "node.plugged")) != NULL) @@ -208,10 +266,6 @@ struct pw_node *pw_audio_dsp_new(struct pw_core *core, if ((str = pw_properties_get(props, "node.id")) != NULL) pw_properties_set(pr, "node.session", str); - pw_properties_setf(pr, "node.format.rate", "%d", sample_rate); - pw_properties_setf(pr, "node.format.channels", "%d", channels); - pw_properties_setf(pr, "node.format.channelmask", "%"PRIu64, channelmask); - node = pw_spa_node_load(core, NULL, NULL, "audioconvert/libspa-audioconvert", direction == PW_DIRECTION_OUTPUT ? @@ -227,83 +281,19 @@ struct pw_node *pw_audio_dsp_new(struct pw_core *core, n = pw_spa_node_get_user_data(node); n->core = core; n->node = node; + n->direction = direction; + n->props = pw_properties_copy(props); + spa_list_init(&n->ports); - n->channels = channels; - n->channelmask = channelmask; - n->sample_rate = sample_rate; n->max_buffer_size = max_buffer_size; if (user_data_size > 0) n->user_data = SPA_MEMBER(n, sizeof(struct node), void); - pw_node_update_ports(node); + pw_node_add_listener(node, &n->node_listener, &node_events, n); - direction = pw_direction_reverse(direction); - - support = pw_core_get_support(core, &n_support); - - for (i = 0; i < n->channels; i++) { - struct port *p; - struct pw_properties *props; - char channel_name[16]; - - make_channel_name(n, channel_name, i, channelmask); - - props = pw_properties_new( - "port.dsp", "32 bit float mono audio", - "port.physical", "1", - "port.terminal", "1", - NULL); - pw_properties_setf(props, "port.name", "%s_%s", - direction == PW_DIRECTION_INPUT ? "playback" : "capture", - channel_name); - pw_properties_setf(props, "port.alias1", "%s_pcm:%s:%s%s", - api, - alias, - direction == PW_DIRECTION_INPUT ? "in" : "out", - channel_name); - pw_properties_setf(props, "port.channel", "%s", channel_name); - - port = pw_port_new(direction, - i, - props, - sizeof(struct port) + - spa_handle_factory_get_size(&spa_floatmix_factory, NULL)); - if (port == NULL) - goto error_free_node; - - - p = pw_port_get_user_data(port); - port->owner_data = p; - p->node = n; - p->port = port; - init_port(p, direction); - p->spa_handle = SPA_MEMBER(p, sizeof(struct port), struct spa_handle); - - spa_handle_factory_init(&spa_floatmix_factory, - p->spa_handle, NULL, - support, n_support); - - spa_handle_get_interface(p->spa_handle, SPA_TYPE_INTERFACE_Node, &iface); - - p->spa_node = iface; - - if (direction == PW_DIRECTION_INPUT) { - pw_log_debug("mix node %p", p->spa_node); - - pw_port_set_mix(port, p->spa_node, PW_PORT_MIX_FLAG_MULTI); - port->implementation = &port_implementation; - port->implementation_data = p; - } - if (pw_port_add(port, node) < 0) - goto error_free_port; - } return node; - error_free_port: - pw_port_destroy(port); - error_free_node: - pw_node_destroy(node); error: return NULL; } diff --git a/src/modules/module-audio-dsp/audio-dsp.h b/src/modules/module-audio-dsp/audio-dsp.h index 75034260c..4253bf2d4 100644 --- a/src/modules/module-audio-dsp/audio-dsp.h +++ b/src/modules/module-audio-dsp/audio-dsp.h @@ -31,9 +31,6 @@ struct pw_node * pw_audio_dsp_new(struct pw_core *core, const struct pw_properties *properties, enum pw_direction direction, - uint32_t channels, - uint64_t channelmask, - uint32_t sample_rate, uint32_t max_buffer_size, size_t user_data_size); diff --git a/src/modules/module-client-node/client-stream.c b/src/modules/module-client-node/client-stream.c index 3533230ce..3aa47d674 100644 --- a/src/modules/module-client-node/client-stream.c +++ b/src/modules/module-client-node/client-stream.c @@ -126,7 +126,30 @@ static int impl_node_enum_params(struct spa_node *node, static int impl_node_set_param(struct spa_node *node, uint32_t id, uint32_t flags, const struct spa_pod *param) { - return 0; + int res = 0; + struct node *this; + struct impl *impl; + + this = SPA_CONTAINER_OF(node, struct node, node); + impl = this->impl; + + switch (id) { + case SPA_PARAM_Profile: + if (impl->started) + return -EIO; + if (impl->adapter != impl->cnode) { + spa_node_set_param(impl->adapter, id, flags, param); + + if (this->callbacks && this->callbacks->event) + this->callbacks->event(this->callbacks_data, + &SPA_NODE_EVENT_INIT(SPA_NODE_EVENT_PortsChanged)); + } + break; + default: + res = -ENOTSUP; + break; + } + return res; } static int impl_node_send_command(struct spa_node *node, const struct spa_command *command) @@ -958,6 +981,12 @@ static void client_node_initialized(void *data) impl->client_port_mix.io, sizeof(impl->client_port_mix.io))) < 0) return; + if ((res = spa_node_port_set_io(impl->adapter_mix, + impl->direction, 0, + SPA_IO_Range, + &impl->range, + sizeof(&impl->range))) < 0) + return; } switch (media_type) {