diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c deleted file mode 100644 index 02c7bc1a9..000000000 --- a/spa/plugins/audioconvert/audioconvert.c +++ /dev/null @@ -1,1440 +0,0 @@ -/* Spa - * - * Copyright © 2018 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. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#undef SPA_LOG_TOPIC_DEFAULT -#define SPA_LOG_TOPIC_DEFAULT log_topic -static struct spa_log_topic *log_topic = &SPA_LOG_TOPIC(0, "spa.audioconvert"); - -#define DEFAULT_ALIGN 16 - -#define MAX_PORTS SPA_AUDIO_MAX_CHANNELS - -struct buffer { - struct spa_list link; -#define BUFFER_FLAG_OUT (1 << 0) - uint32_t flags; - struct spa_buffer *outbuf; - struct spa_meta_header *h; -}; - -struct link { - struct spa_node *out_node; - uint32_t out_port; - uint32_t out_flags; - struct spa_node *in_node; - uint32_t in_port; - uint32_t in_flags; - struct spa_io_buffers io; - uint32_t min_buffers; - uint32_t n_buffers; - struct spa_buffer **buffers; - unsigned int negotiated:1; -}; - -struct impl { - struct spa_handle handle; - struct spa_node node; - - struct spa_log *log; - struct spa_cpu *cpu; - - uint32_t max_align; - - struct spa_hook_list hooks; - - uint64_t info_all; - struct spa_node_info info; -#define IDX_EnumPortConfig 0 -#define IDX_PortConfig 1 -#define IDX_PropInfo 2 -#define IDX_Props 3 - struct spa_param_info params[4]; - uint32_t param_flags[4]; - - int n_links; - struct link links[8]; - int n_nodes; - struct spa_node *nodes[8]; - - enum spa_param_port_config_mode mode[2]; - bool fmt_removing[2]; - - struct spa_handle *hnd_merger; - struct spa_handle *hnd_convert_in; - struct spa_handle *hnd_channelmix; - struct spa_handle *hnd_resample; - struct spa_handle *hnd_convert_out; - struct spa_handle *hnd_splitter; - - struct spa_node *merger; - struct spa_node *convert_in; - struct spa_node *channelmix; - struct spa_node *resample; - struct spa_node *convert_out; - struct spa_node *splitter; - - struct spa_node *fmt[2]; - struct spa_hook fmt_listener[2]; - bool have_fmt_listener[2]; - - struct spa_hook listener[2]; - - unsigned int started:1; - unsigned int add_listener:1; -}; - -#define IS_MONITOR_PORT(this,dir,port_id) (dir == SPA_DIRECTION_OUTPUT && port_id > 0 && \ - this->mode[SPA_DIRECTION_INPUT] == SPA_PARAM_PORT_CONFIG_MODE_dsp && \ - this->mode[SPA_DIRECTION_OUTPUT] != SPA_PARAM_PORT_CONFIG_MODE_dsp) - -static void emit_node_info(struct impl *this, bool full) -{ - uint32_t i; - uint64_t old = full ? this->info.change_mask : 0; - - if (this->add_listener) - return; - - if (full) - this->info.change_mask = this->info_all; - if (this->info.change_mask) { - if (this->info.change_mask & SPA_NODE_CHANGE_MASK_PARAMS) { - for (i = 0; i < SPA_N_ELEMENTS(this->params); i++) { - if (this->params[i].user > 0) { - this->params[i].flags ^= SPA_PARAM_INFO_SERIAL; - this->params[i].user = 0; - } - } - } - spa_node_emit_info(&this->hooks, &this->info); - this->info.change_mask = old; - } -} - -static int make_link(struct impl *this, - struct spa_node *out_node, uint32_t out_port, - struct spa_node *in_node, uint32_t in_port, uint32_t min_buffers) -{ - struct link *l = &this->links[this->n_links++]; - - l->out_node = out_node; - l->out_port = out_port; - l->out_flags = 0; - l->in_node = in_node; - l->in_port = in_port; - l->in_flags = 0; - l->negotiated = false; - l->io = SPA_IO_BUFFERS_INIT; - l->n_buffers = 0; - l->min_buffers = min_buffers; - - spa_node_port_set_io(out_node, - SPA_DIRECTION_OUTPUT, out_port, - SPA_IO_Buffers, - &l->io, sizeof(l->io)); - spa_node_port_set_io(in_node, - SPA_DIRECTION_INPUT, in_port, - SPA_IO_Buffers, - &l->io, sizeof(l->io)); - return 0; -} - -static void clean_link(struct impl *this, struct link *link) -{ - spa_node_port_set_param(link->in_node, - SPA_DIRECTION_INPUT, link->in_port, - SPA_PARAM_Format, 0, NULL); - spa_node_port_set_param(link->out_node, - SPA_DIRECTION_OUTPUT, link->out_port, - SPA_PARAM_Format, 0, NULL); - if (link->buffers) - free(link->buffers); - link->buffers = NULL; -} - -static int debug_params(struct impl *this, struct spa_node *node, - enum spa_direction direction, uint32_t port_id, uint32_t id, struct spa_pod *filter) -{ - struct spa_pod_builder b = { 0 }; - uint8_t buffer[4096]; - uint32_t state; - struct spa_pod *param; - int res; - - spa_log_error(this->log, "params:"); - - state = 0; - while (true) { - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - res = spa_node_port_enum_params_sync(node, - direction, port_id, - id, &state, - NULL, ¶m, &b); - if (res != 1) - break; - - spa_debug_pod(2, NULL, param); - } - - spa_log_error(this->log, "failed filter:"); - if (filter) - spa_debug_pod(2, NULL, filter); - - return 0; -} - -static int negotiate_link_format(struct impl *this, struct link *link) -{ - struct spa_pod_builder b = { 0 }; - uint8_t buffer[4096]; - uint32_t state; - struct spa_pod *format, *filter; - int res; - - if (link->negotiated) - return 0; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - state = 0; - filter = NULL; - if ((res = spa_node_port_enum_params_sync(link->out_node, - SPA_DIRECTION_OUTPUT, link->out_port, - SPA_PARAM_EnumFormat, &state, - filter, &format, &b)) != 1) { - debug_params(this, link->out_node, SPA_DIRECTION_OUTPUT, link->out_port, - SPA_PARAM_EnumFormat, filter); - return -ENOTSUP; - } - filter = format; - state = 0; - if ((res = spa_node_port_enum_params_sync(link->in_node, - SPA_DIRECTION_INPUT, link->in_port, - SPA_PARAM_EnumFormat, &state, - filter, &format, &b)) != 1) { - debug_params(this, link->in_node, SPA_DIRECTION_INPUT, link->in_port, - SPA_PARAM_EnumFormat, filter); - return -ENOTSUP; - } - filter = format; - - spa_pod_fixate(filter); - - if ((res = spa_node_port_set_param(link->out_node, - SPA_DIRECTION_OUTPUT, link->out_port, - SPA_PARAM_Format, 0, - filter)) < 0) - return res; - - if ((res = spa_node_port_set_param(link->in_node, - SPA_DIRECTION_INPUT, link->in_port, - SPA_PARAM_Format, 0, - filter)) < 0) - return res; - - link->negotiated = true; - - return 0; -} - -static int setup_convert(struct impl *this) -{ - int i, j, res; - - spa_log_debug(this->log, "setup convert n_links:%d", this->n_links); - - if (this->n_links > 0) - return 0; - - this->n_nodes = 0; - /* unpack */ - this->nodes[this->n_nodes++] = this->fmt[SPA_DIRECTION_INPUT]; - /* down mix */ - this->nodes[this->n_nodes++] = this->channelmix; - /* resample */ - this->nodes[this->n_nodes++] = this->resample; - /* pack */ - this->nodes[this->n_nodes++] = this->fmt[SPA_DIRECTION_OUTPUT]; - - make_link(this, this->nodes[0], 0, this->nodes[1], 0, 2); - make_link(this, this->nodes[1], 0, this->nodes[2], 0, 2); - make_link(this, this->nodes[2], 0, this->nodes[3], 0, 1); - - for (i = 0, j = this->n_links - 1; j >= i; i++, j--) { - spa_log_debug(this->log, "negotiate %d", i); - if ((res = negotiate_link_format(this, &this->links[i])) < 0) - return res; - spa_log_debug(this->log, "negotiate %d", j); - if ((res = negotiate_link_format(this, &this->links[j])) < 0) - return res; - } - return 0; -} - -static int negotiate_link_buffers(struct impl *this, struct link *link) -{ - uint8_t buffer[4096]; - struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - uint32_t state; - struct spa_pod *param = NULL, *filter; - int res; - bool in_alloc, out_alloc; - uint32_t i, size, buffers, blocks, align, flags; - uint32_t *aligns; - struct spa_data *datas; - - if (link->n_buffers > 0) - return 0; - - state = 0; - filter = NULL; - if ((res = spa_node_port_enum_params_sync(link->in_node, - SPA_DIRECTION_INPUT, link->in_port, - SPA_PARAM_Buffers, &state, - filter, ¶m, &b)) != 1) { - debug_params(this, link->in_node, SPA_DIRECTION_INPUT, link->in_port, - SPA_PARAM_Buffers, filter); - return -ENOTSUP; - } - state = 0; - filter = param; - if ((res = spa_node_port_enum_params_sync(link->out_node, - SPA_DIRECTION_OUTPUT, link->out_port, - SPA_PARAM_Buffers, &state, - filter, ¶m, &b)) != 1) { - debug_params(this, link->out_node, SPA_DIRECTION_OUTPUT, link->out_port, - SPA_PARAM_Buffers, filter); - return -ENOTSUP; - } - - spa_pod_fixate(param); - - in_alloc = SPA_FLAG_IS_SET(link->in_flags, - SPA_PORT_FLAG_CAN_ALLOC_BUFFERS); - out_alloc = SPA_FLAG_IS_SET(link->out_flags, - SPA_PORT_FLAG_CAN_ALLOC_BUFFERS); - - flags = 0; - if (out_alloc || in_alloc) { - flags |= SPA_BUFFER_ALLOC_FLAG_NO_DATA; - if (out_alloc) - in_alloc = false; - } - - align = DEFAULT_ALIGN; - - if (spa_pod_parse_object(param, - SPA_TYPE_OBJECT_ParamBuffers, NULL, - SPA_PARAM_BUFFERS_buffers, SPA_POD_Int(&buffers), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(&blocks), - SPA_PARAM_BUFFERS_size, SPA_POD_Int(&size), - SPA_PARAM_BUFFERS_align, SPA_POD_OPT_Int(&align)) < 0) - return -EINVAL; - - spa_log_debug(this->log, "%p: buffers %d, blocks %d, size %d, align %d %d:%d", - this, buffers, blocks, size, align, out_alloc, in_alloc); - - align = SPA_MAX(align, this->max_align); - - datas = alloca(sizeof(struct spa_data) * blocks); - memset(datas, 0, sizeof(struct spa_data) * blocks); - aligns = alloca(sizeof(uint32_t) * blocks); - for (i = 0; i < blocks; i++) { - datas[i].type = SPA_DATA_MemPtr; - datas[i].flags = SPA_DATA_FLAG_DYNAMIC; - datas[i].maxsize = size; - aligns[i] = align; - } - - buffers = SPA_MAX(link->min_buffers, buffers); - - if (link->buffers) - free(link->buffers); - link->buffers = spa_buffer_alloc_array(buffers, flags, 0, NULL, blocks, datas, aligns); - if (link->buffers == NULL) - return -errno; - - link->n_buffers = buffers; - - if ((res = spa_node_port_use_buffers(link->out_node, - SPA_DIRECTION_OUTPUT, link->out_port, - out_alloc ? SPA_NODE_BUFFERS_FLAG_ALLOC : 0, - link->buffers, link->n_buffers)) < 0) - return res; - - if ((res = spa_node_port_use_buffers(link->in_node, - SPA_DIRECTION_INPUT, link->in_port, - in_alloc ? SPA_NODE_BUFFERS_FLAG_ALLOC : 0, - link->buffers, link->n_buffers)) < 0) - return res; - - return 0; -} - -static void flush_convert(struct impl *this) -{ - int i; - spa_log_debug(this->log, "%p: %d", this, this->n_links); - for (i = 0; i < this->n_links; i++) - this->links[i].io.status = SPA_STATUS_OK; -} - -static void clean_convert(struct impl *this) -{ - int i; - - spa_log_debug(this->log, "%p: %d", this, this->n_links); - - for (i = 0; i < this->n_links; i++) - clean_link(this, &this->links[i]); - this->n_links = 0; -} - -static int setup_buffers(struct impl *this, enum spa_direction direction) -{ - int i, res; - - spa_log_debug(this->log, "%p: %d %d", this, direction, this->n_links); - - if (direction == SPA_DIRECTION_INPUT) { - for (i = 0; i < this->n_links; i++) { - if ((res = negotiate_link_buffers(this, &this->links[i])) < 0) - spa_log_error(this->log, "%p: buffers %d failed %s", - this, i, spa_strerror(res)); - } - } else { - for (i = this->n_links-1; i >= 0 ; i--) { - if ((res = negotiate_link_buffers(this, &this->links[i])) < 0) - spa_log_error(this->log, "%p: buffers %d failed %s", - this, i, spa_strerror(res)); - } - } - - return 0; -} - -static int enum_params(struct impl *this, - uint32_t id, - struct spa_result_node_params *result, - const struct spa_pod *filter, - struct spa_pod_builder *builder) -{ - int res; - result->param = NULL; - result->next = result->index; - if (result->next < 0x1000) { - if (this->fmt[SPA_DIRECTION_INPUT] == this->merger && - (res = spa_node_enum_params_sync(this->merger, - id, &result->next, filter, &result->param, builder)) == 1) { - return res; - } - result->next = 0x1000; - } - if (result->next < 0x2000) { - result->next &= 0xfff; - if ((res = spa_node_enum_params_sync(this->channelmix, - id, &result->next, filter, &result->param, builder)) == 1) { - result->next |= 0x1000; - return res; - } - result->next = 0x2000; - } - if (result->next >= 0x2000) { - result->next &= 0xfff; - if ((res = spa_node_enum_params_sync(this->resample, - id, &result->next, filter, &result->param, builder)) == 1) { - result->next |= 0x2000; - return res; - } - } - return 0; -} -static int impl_node_enum_params(void *object, int seq, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - struct impl *this = object; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer[4096]; - struct spa_result_node_params result; - uint32_t count = 0; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - - result.id = id; - result.next = start; - next: - res = 0; - result.index = result.next++; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - case SPA_PARAM_EnumPortConfig: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamPortConfig, id, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(SPA_DIRECTION_INPUT), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp)); - break; - case 1: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamPortConfig, id, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(SPA_DIRECTION_OUTPUT), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp)); - break; - case 2: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamPortConfig, id, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(SPA_DIRECTION_INPUT), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_convert)); - break; - case 3: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamPortConfig, id, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(SPA_DIRECTION_OUTPUT), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_convert)); - break; - default: - return 0; - } - break; - - case SPA_PARAM_PortConfig: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamPortConfig, id, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(SPA_DIRECTION_INPUT), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(this->mode[SPA_DIRECTION_INPUT])); - break; - case 1: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamPortConfig, id, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(SPA_DIRECTION_OUTPUT), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(this->mode[SPA_DIRECTION_OUTPUT])); - break; - default: - return 0; - } - break; - - case SPA_PARAM_PropInfo: - if ((res = enum_params(this, id, &result, filter, &b)) != 1) - return res; - break; - - case SPA_PARAM_Props: - if ((res = enum_params(this, id, &result, filter, &b)) != 1) - return res; - break; - - default: - return -ENOENT; - } - - if (res == 0) { - if (spa_pod_filter(&b, &result.param, param, filter) < 0) - goto next; - } - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); - - if (++count != num) - goto next; - - return 0; -} - -static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_debug(this->log, "%p: io %d %p/%zd", this, id, data, size); - - switch (id) { - case SPA_IO_Position: - res = spa_node_set_io(this->resample, id, data, size); - res = spa_node_set_io(this->channelmix, id, data, size); - res = spa_node_set_io(this->fmt[0], id, data, size); - res = spa_node_set_io(this->fmt[1], id, data, size); - break; - default: - res = -ENOENT; - break; - } - return res; -} - -static void fmt_input_port_info(void *data, - enum spa_direction direction, uint32_t port, - const struct spa_port_info *info) -{ - struct impl *this = data; - bool is_monitor = IS_MONITOR_PORT(this, direction, port); - - if (this->fmt_removing[direction]) - info = NULL; - if (is_monitor && this->fmt_removing[SPA_DIRECTION_INPUT]) - info = NULL; - - spa_log_debug(this->log, "%p: %d.%d info", this, direction, port); - - if (direction == SPA_DIRECTION_INPUT || is_monitor) - spa_node_emit_port_info(&this->hooks, direction, port, info); -} - -static const struct spa_node_events fmt_input_events = { - SPA_VERSION_NODE_EVENTS, - .port_info = fmt_input_port_info, -}; - -static void fmt_output_port_info(void *data, - enum spa_direction direction, uint32_t port, - const struct spa_port_info *info) -{ - struct impl *this = data; - - if (this->fmt_removing[direction]) - info = NULL; - - spa_log_debug(this->log, "%p: %d.%d info", this, direction, port); - - if (direction == SPA_DIRECTION_OUTPUT) - spa_node_emit_port_info(&this->hooks, direction, port, info); -} - -static const struct spa_node_events fmt_output_events = { - SPA_VERSION_NODE_EVENTS, - .port_info = fmt_output_port_info, -}; - -static void on_channelmix_info(void *data, const struct spa_node_info *info) -{ - struct impl *this = data; - uint32_t i; - - if ((info->change_mask & SPA_NODE_CHANGE_MASK_PARAMS) == 0) - return; - - for (i = 0; i < info->n_params; i++) { - uint32_t idx; - - switch (info->params[i].id) { - case SPA_PARAM_PropInfo: - idx = IDX_PropInfo; - break; - case SPA_PARAM_Props: - idx = IDX_Props; - break; - default: - continue; - } - if (!this->add_listener && - this->param_flags[idx] == info->params[i].flags) - continue; - - this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; - this->param_flags[idx] = info->params[i].flags; - this->params[idx].flags = - (this->params[idx].flags & SPA_PARAM_INFO_SERIAL) | - (info->params[i].flags & SPA_PARAM_INFO_READWRITE); - - if (!this->add_listener) - this->params[idx].user++; - } - emit_node_info(this, false); -} - -static const struct spa_node_events channelmix_events = { - SPA_VERSION_NODE_EVENTS, - .info = on_channelmix_info, -}; - -static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode mode, - enum spa_direction direction, bool monitor, struct spa_audio_info *info) -{ - int res = 0; - struct spa_node *old, *new; - bool do_signal; - - spa_log_debug(this->log, "%p: mode %d", this, mode); - - /* old node on input/output */ - old = this->fmt[direction]; - - /* decide on new node based on mode and direction */ - switch (mode) { - case SPA_PARAM_PORT_CONFIG_MODE_convert: - new = direction == SPA_DIRECTION_INPUT ? this->convert_in : this->convert_out; - break; - - case SPA_PARAM_PORT_CONFIG_MODE_dsp: - new = direction == SPA_DIRECTION_INPUT ? this->merger : this->splitter; - break; - case SPA_PARAM_PORT_CONFIG_MODE_none: - new = NULL; - break; - default: - return -EIO; - } - - clean_convert(this); - - this->fmt[direction] = new; - - /* signal if we change nodes or when DSP config changes */ - do_signal = this->fmt[direction] != old || - mode == SPA_PARAM_PORT_CONFIG_MODE_dsp; - - if (do_signal && old != NULL) { - /* change, remove old ports. We trigger a new port_info event - * on the old node with info set to NULL to mark delete */ - if (this->have_fmt_listener[direction]) { - spa_hook_remove(&this->fmt_listener[direction]); - - this->fmt_removing[direction] = true; - spa_node_add_listener(old, - &this->fmt_listener[direction], - direction == SPA_DIRECTION_INPUT ? - &fmt_input_events : &fmt_output_events, - this); - this->fmt_removing[direction] = false; - - spa_hook_remove(&this->fmt_listener[direction]); - this->have_fmt_listener[direction] = false; - } - } - - this->mode[direction] = mode; - - if (new != NULL) { - struct spa_pod_builder b = { 0 }; - uint8_t buffer[4096]; - struct spa_pod *param = NULL; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - if (info) { - spa_log_debug(this->log, "%p: port config %d", this, info->info.raw.channels); - param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &info->info.raw); - } - if (mode == SPA_PARAM_PORT_CONFIG_MODE_dsp) { - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(direction), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp), - SPA_PARAM_PORT_CONFIG_monitor, SPA_POD_Bool(monitor), - SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param)); - res = spa_node_set_param(this->fmt[direction], SPA_PARAM_PortConfig, 0, param); - } else { - res = spa_node_port_set_param(this->fmt[direction], direction, 0, - SPA_PARAM_Format, 0, param); - } - if (res < 0) - return res; - - this->info.change_mask |= SPA_NODE_CHANGE_MASK_FLAGS | SPA_NODE_CHANGE_MASK_PARAMS; - this->info.flags &= ~SPA_NODE_FLAG_NEED_CONFIGURE; - this->params[IDX_Props].user++; - } - - /* notify ports of new node */ - if (do_signal && new != NULL) { - if (this->have_fmt_listener[direction]) - spa_hook_remove(&this->fmt_listener[direction]); - - spa_node_add_listener(this->fmt[direction], - &this->fmt_listener[direction], - direction == SPA_DIRECTION_INPUT ? - &fmt_input_events : &fmt_output_events, - this); - this->have_fmt_listener[direction] = true; - } - emit_node_info(this, false); - - return 0; -} - -static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - int res = 0; - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - switch (id) { - case SPA_PARAM_PortConfig: - { - enum spa_direction dir; - enum spa_param_port_config_mode mode; - struct spa_pod *format = NULL; - struct spa_audio_info info = { 0, }, *infop = NULL; - int monitor = false; - - if (spa_pod_parse_object(param, - SPA_TYPE_OBJECT_ParamPortConfig, NULL, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(&dir), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(&mode), - SPA_PARAM_PORT_CONFIG_monitor, SPA_POD_OPT_Bool(&monitor), - SPA_PARAM_PORT_CONFIG_format, SPA_POD_OPT_Pod(&format)) < 0) - return -EINVAL; - - if (format) { - 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 -ENOTSUP; - - if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) - return -EINVAL; - - if (info.info.raw.channels == 0 || info.info.raw.rate == 0) - return -EINVAL; - - infop = &info; - } - - spa_log_debug(this->log, "mode:%d direction:%d %d", mode, dir, monitor); - - switch (mode) { - case SPA_PARAM_PORT_CONFIG_MODE_passthrough: - return -ENOTSUP; - - case SPA_PARAM_PORT_CONFIG_MODE_none: - case SPA_PARAM_PORT_CONFIG_MODE_convert: - break; - - case SPA_PARAM_PORT_CONFIG_MODE_dsp: - info.info.raw.format = SPA_AUDIO_FORMAT_F32P; - break; - default: - return -EINVAL; - } - - res = reconfigure_mode(this, mode, dir, monitor, infop); - - break; - } - case SPA_PARAM_Props: - { - if (this->fmt[SPA_DIRECTION_INPUT] == this->merger) - res = spa_node_set_param(this->merger, id, flags, param); - res = spa_node_set_param(this->channelmix, id, flags, param); - res = spa_node_set_param(this->resample, id, flags, param); - break; - } - default: - res = -ENOTSUP; - break; - } - return res; -} - -static int impl_node_send_command(void *object, const struct spa_command *command) -{ - struct impl *this = object; - int res, i; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(command != NULL, -EINVAL); - - switch (SPA_NODE_COMMAND_ID(command)) { - case SPA_NODE_COMMAND_Start: - if ((res = setup_convert(this)) < 0) - return res; - if ((res = setup_buffers(this, SPA_DIRECTION_INPUT)) < 0) - return res; - break; - - case SPA_NODE_COMMAND_Suspend: - clean_convert(this); - SPA_FALLTHROUGH - case SPA_NODE_COMMAND_Flush: - flush_convert(this); - SPA_FALLTHROUGH - case SPA_NODE_COMMAND_Pause: - this->started = false; - break; - default: - return -ENOTSUP; - } - - for (i = 0; i < this->n_nodes; i++) { - if ((res = spa_node_send_command(this->nodes[i], command)) < 0) { - spa_log_error(this->log, "%p: can't send command to node %d: %s", - this, i, spa_strerror(res)); - } - } - - switch (SPA_NODE_COMMAND_ID(command)) { - case SPA_NODE_COMMAND_Start: - this->started = true; - break; - } - - return 0; -} - -static int -impl_node_add_listener(void *object, - struct spa_hook *listener, - const struct spa_node_events *events, - void *data) -{ - struct impl *this = object; - struct spa_hook_list save; - struct spa_hook l[3]; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_hook_list_isolate(&this->hooks, &save, listener, events, data); - - spa_log_trace(this->log, "%p: add listener %p", this, listener); - - this->add_listener = true; - - spa_zero(l); - if (this->fmt[SPA_DIRECTION_INPUT]) - spa_node_add_listener(this->fmt[SPA_DIRECTION_INPUT], - &l[0], &fmt_input_events, this); - spa_node_add_listener(this->channelmix, - &l[1], &channelmix_events, this); - if (this->fmt[SPA_DIRECTION_OUTPUT]) - spa_node_add_listener(this->fmt[SPA_DIRECTION_OUTPUT], - &l[2], &fmt_output_events, this); - - if (this->fmt[SPA_DIRECTION_INPUT]) - spa_hook_remove(&l[0]); - spa_hook_remove(&l[1]); - if (this->fmt[SPA_DIRECTION_OUTPUT]) - spa_hook_remove(&l[2]); - - this->add_listener = false; - - emit_node_info(this, true); - - spa_hook_list_join(&this->hooks, &save); - - return 0; -} - -static int -impl_node_set_callbacks(void *object, - const struct spa_node_callbacks *callbacks, - void *user_data) -{ - return -ENOTSUP; -} - -static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id, - const struct spa_dict *props) -{ - return -ENOTSUP; -} - -static int -impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) -{ - return -ENOTSUP; -} - -static int -impl_node_port_enum_params(void *object, int seq, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - struct impl *this = object; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer[4096]; - struct spa_result_node_params result; - uint32_t count = 0; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - - spa_log_debug(this->log, "%p: port %d.%d %d %u", this, direction, port_id, seq, id); - - result.id = id; - result.next = start; - next: - result.index = result.next; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - case SPA_PARAM_IO: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamIO, id, - SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers), - SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); - break; - case 1: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamIO, id, - SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_RateMatch), - SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_rate_match))); - break; - default: - return 0; - } - result.next++; - break; - default: - { - struct spa_node *target; - - if (IS_MONITOR_PORT(this, direction, port_id)) - target = this->fmt[SPA_DIRECTION_INPUT]; - else - target = this->fmt[direction]; - - res = spa_node_port_enum_params_sync(target, - direction, port_id, - id, &result.next, - NULL, ¶m, &b); - if (res != 1) - return res; - } - } - - if (spa_pod_filter(&b, &result.param, param, filter) < 0) - goto next; - - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); - - if (++count != num) - goto next; - - return 0; -} - -static int -impl_node_port_set_param(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - int res; - struct spa_node *target; - bool is_monitor; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_debug(this->log, "%p: set param %u on port %d:%d %p", - this, id, direction, port_id, param); - - switch (id) { - default: - is_monitor = IS_MONITOR_PORT(this, direction, port_id); - if (is_monitor) - target = this->fmt[SPA_DIRECTION_INPUT]; - else - target = this->fmt[direction]; - break; - } - - if ((res = spa_node_port_set_param(target, - direction, port_id, id, flags, param)) < 0) - return res; - - switch (id) { - case SPA_PARAM_Latency: - if (port_id == 0) { - target = this->fmt[SPA_DIRECTION_REVERSE(direction)]; - if ((res = spa_node_port_set_param(target, - direction, port_id, id, flags, param)) < 0) - return res; - } - break; - } - - return res; -} - -static int -impl_node_port_use_buffers(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t flags, - struct spa_buffer **buffers, - uint32_t n_buffers) -{ - struct impl *this = object; - int res; - struct spa_node *target; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - if (IS_MONITOR_PORT(this, direction, port_id)) - target = this->fmt[SPA_DIRECTION_INPUT]; - else - target = this->fmt[direction]; - - if ((res = spa_node_port_use_buffers(target, - direction, port_id, flags, buffers, n_buffers)) < 0) - return res; - - return res; -} - -static int -impl_node_port_set_io(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - struct spa_node *target; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_debug(this->log, "set io %d %d %d", id, direction, port_id); - - switch (id) { - case SPA_IO_RateMatch: - res = spa_node_port_set_io(this->resample, direction, 0, id, data, size); - break; - default: - if (IS_MONITOR_PORT(this, direction, port_id)) - target = this->fmt[SPA_DIRECTION_INPUT]; - else - target = this->fmt[direction]; - - res = spa_node_port_set_io(target, direction, port_id, id, data, size); - break; - } - return res; -} - -static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) -{ - struct impl *this = object; - struct spa_node *target; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - if (IS_MONITOR_PORT(this, SPA_DIRECTION_OUTPUT, port_id)) - target = this->fmt[SPA_DIRECTION_INPUT]; - else - target = this->fmt[SPA_DIRECTION_OUTPUT]; - - return spa_node_port_reuse_buffer(target, port_id, buffer_id); -} - -static int impl_node_process(void *object) -{ - struct impl *this = object; - int r, i, res = SPA_STATUS_OK; - int ready; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_trace_fp(this->log, "%p: process %d %d", this, this->n_links, this->n_nodes); - - while (1) { - res = SPA_STATUS_OK; - ready = 0; - for (i = 0; i < this->n_nodes; i++) { - r = spa_node_process(this->nodes[i]); - - spa_log_trace_fp(this->log, "%p: process %d %d: %s", - this, i, r, r < 0 ? spa_strerror(r) : "ok"); - - if (SPA_UNLIKELY(r < 0)) - return r; - - if (r & SPA_STATUS_HAVE_DATA) - ready++; - - if (SPA_UNLIKELY(i == 0)) - res |= r & SPA_STATUS_NEED_DATA; - if (SPA_UNLIKELY(i == this->n_nodes-1)) - res |= r & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED); - } - if (res & SPA_STATUS_HAVE_DATA) - break; - if (ready == 0) - break; - } - - spa_log_trace_fp(this->log, "%p: process result: %d", this, res); - - return res; -} - -static const struct spa_node_methods impl_node = { - SPA_VERSION_NODE_METHODS, - .add_listener = impl_node_add_listener, - .set_callbacks = impl_node_set_callbacks, - .enum_params = impl_node_enum_params, - .set_param = impl_node_set_param, - .set_io = impl_node_set_io, - .send_command = impl_node_send_command, - .add_port = impl_node_add_port, - .remove_port = impl_node_remove_port, - .port_enum_params = impl_node_port_enum_params, - .port_set_param = impl_node_port_set_param, - .port_use_buffers = impl_node_port_use_buffers, - .port_set_io = impl_node_port_set_io, - .port_reuse_buffer = impl_node_port_reuse_buffer, - .process = impl_node_process, -}; - -static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) -{ - struct impl *this; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - spa_return_val_if_fail(interface != NULL, -EINVAL); - - this = (struct impl *) handle; - - if (spa_streq(type, SPA_TYPE_INTERFACE_Node)) - *interface = &this->node; - else - return -ENOENT; - - return 0; -} - -static int impl_clear(struct spa_handle *handle) -{ - struct impl *this; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - - this = (struct impl *) handle; - - clean_convert(this); - - spa_handle_clear(this->hnd_merger); - spa_handle_clear(this->hnd_convert_in); - spa_handle_clear(this->hnd_channelmix); - spa_handle_clear(this->hnd_resample); - spa_handle_clear(this->hnd_convert_out); - spa_handle_clear(this->hnd_splitter); - - return 0; -} - -extern const struct spa_handle_factory spa_fmtconvert_factory; -extern const struct spa_handle_factory spa_channelmix_factory; -extern const struct spa_handle_factory spa_resample_factory; -extern const struct spa_handle_factory spa_splitter_factory; -extern const struct spa_handle_factory spa_merger_factory; - -static size_t -impl_get_size(const struct spa_handle_factory *factory, - const struct spa_dict *params) -{ - size_t size; - - size = sizeof(struct impl); - size += spa_handle_factory_get_size(&spa_merger_factory, params); - size += spa_handle_factory_get_size(&spa_fmtconvert_factory, params); - size += spa_handle_factory_get_size(&spa_channelmix_factory, params); - size += spa_handle_factory_get_size(&spa_resample_factory, params); - size += spa_handle_factory_get_size(&spa_fmtconvert_factory, params); - size += spa_handle_factory_get_size(&spa_splitter_factory, params); - - return size; -} - -static int -impl_init(const struct spa_handle_factory *factory, - struct spa_handle *handle, - const struct spa_dict *info, - const struct spa_support *support, - uint32_t n_support) -{ - struct impl *this; - size_t size; - void *iface; - - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(handle != NULL, -EINVAL); - - handle->get_interface = impl_get_interface; - handle->clear = impl_clear; - - this = (struct impl *) handle; - - this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - spa_log_topic_init(this->log, log_topic); - - this->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); - if (this->cpu) - this->max_align = spa_cpu_get_max_align(this->cpu); - - this->node.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_Node, - SPA_VERSION_NODE, - &impl_node, this); - spa_hook_list_init(&this->hooks); - - this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | - SPA_NODE_CHANGE_MASK_PARAMS; - this->info = SPA_NODE_INFO_INIT(); - this->info.max_input_ports = MAX_PORTS; - this->info.max_output_ports = MAX_PORTS; - this->info.flags = SPA_NODE_FLAG_RT | - SPA_NODE_FLAG_IN_PORT_CONFIG | - SPA_NODE_FLAG_OUT_PORT_CONFIG | - SPA_NODE_FLAG_NEED_CONFIGURE; - 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_PropInfo] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); - this->params[IDX_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); - this->info.params = this->params; - this->info.n_params = 4; - - this->hnd_merger = SPA_PTROFF(this, sizeof(struct impl), struct spa_handle); - spa_handle_factory_init(&spa_merger_factory, - this->hnd_merger, - info, support, n_support); - size = spa_handle_factory_get_size(&spa_merger_factory, info); - - this->hnd_convert_in = SPA_PTROFF(this->hnd_merger, size, struct spa_handle); - spa_handle_factory_init(&spa_fmtconvert_factory, - this->hnd_convert_in, - info, support, n_support); - size = spa_handle_factory_get_size(&spa_fmtconvert_factory, info); - - this->hnd_channelmix = SPA_PTROFF(this->hnd_convert_in, size, struct spa_handle); - spa_handle_factory_init(&spa_channelmix_factory, - this->hnd_channelmix, - info, support, n_support); - size = spa_handle_factory_get_size(&spa_channelmix_factory, info); - - this->hnd_resample = SPA_PTROFF(this->hnd_channelmix, size, struct spa_handle); - spa_handle_factory_init(&spa_resample_factory, - this->hnd_resample, - info, support, n_support); - size = spa_handle_factory_get_size(&spa_resample_factory, info); - - this->hnd_convert_out = SPA_PTROFF(this->hnd_resample, size, struct spa_handle); - spa_handle_factory_init(&spa_fmtconvert_factory, - this->hnd_convert_out, - info, support, n_support); - size = spa_handle_factory_get_size(&spa_fmtconvert_factory, info); - - this->hnd_splitter = SPA_PTROFF(this->hnd_convert_out, size, struct spa_handle); - spa_handle_factory_init(&spa_splitter_factory, - this->hnd_splitter, - info, support, n_support); - - spa_handle_get_interface(this->hnd_merger, SPA_TYPE_INTERFACE_Node, &iface); - this->merger = iface; - spa_handle_get_interface(this->hnd_convert_in, SPA_TYPE_INTERFACE_Node, &iface); - this->convert_in = iface; - spa_handle_get_interface(this->hnd_channelmix, SPA_TYPE_INTERFACE_Node, &iface); - this->channelmix = iface; - spa_handle_get_interface(this->hnd_resample, SPA_TYPE_INTERFACE_Node, &iface); - this->resample = iface; - spa_handle_get_interface(this->hnd_convert_out, SPA_TYPE_INTERFACE_Node, &iface); - this->convert_out = iface; - spa_handle_get_interface(this->hnd_splitter, SPA_TYPE_INTERFACE_Node, &iface); - this->splitter = iface; - - reconfigure_mode(this, SPA_PARAM_PORT_CONFIG_MODE_convert, SPA_DIRECTION_OUTPUT, false, NULL); - reconfigure_mode(this, SPA_PARAM_PORT_CONFIG_MODE_convert, SPA_DIRECTION_INPUT, false, NULL); - - spa_node_add_listener(this->channelmix, - &this->listener[0], &channelmix_events, this); - - return 0; -} - -static const struct spa_interface_info impl_interfaces[] = { - { SPA_TYPE_INTERFACE_Node, }, -}; - -static int -impl_enum_interface_info(const struct spa_handle_factory *factory, - const struct spa_interface_info **info, - uint32_t *index) -{ - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(info != NULL, -EINVAL); - spa_return_val_if_fail(index != NULL, -EINVAL); - - switch (*index) { - case 0: - *info = &impl_interfaces[*index]; - break; - default: - return 0; - } - (*index)++; - return 1; -} - -const struct spa_handle_factory spa_audioconvert_factory = { - SPA_VERSION_HANDLE_FACTORY, - SPA_NAME_AUDIO_CONVERT, - NULL, - impl_get_size, - impl_init, - impl_enum_interface_info, -}; diff --git a/spa/plugins/audioconvert/channelmix.c b/spa/plugins/audioconvert/channelmix.c deleted file mode 100644 index 8cc581bef..000000000 --- a/spa/plugins/audioconvert/channelmix.c +++ /dev/null @@ -1,1785 +0,0 @@ -/* Spa - * - * Copyright © 2018 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. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "channelmix-ops.h" - -#undef SPA_LOG_TOPIC_DEFAULT -#define SPA_LOG_TOPIC_DEFAULT log_topic -struct spa_log_topic *log_topic = &SPA_LOG_TOPIC(0, "spa.channelmix"); - -#define DEFAULT_RATE 48000 -#define DEFAULT_CHANNELS 2 - -#define MAX_BUFFERS 32 -#define MAX_DATAS SPA_AUDIO_MAX_CHANNELS -#define MAX_ALIGN CHANNELMIX_OPS_MAX_ALIGN - -#define DEFAULT_CONTROL_BUFFER_SIZE 32768 - -struct impl; - -#define DEFAULT_MUTE false -#define DEFAULT_VOLUME 1.0f - -struct volumes { - bool mute; - uint32_t n_volumes; - float volumes[SPA_AUDIO_MAX_CHANNELS]; -}; - -static void init_volumes(struct volumes *vol) -{ - uint32_t i; - vol->mute = DEFAULT_MUTE; - vol->n_volumes = 0; - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) - vol->volumes[i] = DEFAULT_VOLUME; -} - -struct props { - float volume; - uint32_t n_channels; - uint32_t channel_map[SPA_AUDIO_MAX_CHANNELS]; - struct volumes channel; - struct volumes soft; - struct volumes monitor; - unsigned int have_soft_volume:1; - unsigned int disabled:1; -}; - -static void props_reset(struct props *props) -{ - uint32_t i; - props->volume = DEFAULT_VOLUME; - props->n_channels = 0; - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) - props->channel_map[i] = SPA_AUDIO_CHANNEL_UNKNOWN; - init_volumes(&props->channel); - init_volumes(&props->soft); - init_volumes(&props->monitor); -} - -struct buffer { - uint32_t id; -#define BUFFER_FLAG_OUT (1 << 0) - uint32_t flags; - struct spa_list link; - struct spa_buffer *outbuf; - struct spa_meta_header *h; - void *datas[MAX_DATAS]; -}; - -struct port { - uint32_t direction; - uint32_t id; - - uint64_t info_all; - struct spa_port_info info; -#define IDX_EnumFormat 0 -#define IDX_Meta 1 -#define IDX_IO 2 -#define IDX_Format 3 -#define IDX_Buffers 4 - struct spa_param_info params[5]; - - struct spa_io_buffers *io; - - bool have_format; - struct spa_audio_info format; - uint32_t stride; - uint32_t blocks; - uint32_t size; - - struct buffer buffers[MAX_BUFFERS]; - uint32_t n_buffers; - - struct spa_list queue; - - struct spa_pod_sequence *ctrl; - uint32_t ctrl_offset; -}; - -struct impl { - struct spa_handle handle; - struct spa_node node; - - struct spa_log *log; - struct spa_cpu *cpu; - uint32_t quantum_limit; - - enum spa_direction direction; - struct spa_io_position *io_position; - - struct spa_hook_list hooks; - - uint64_t info_all; - struct spa_node_info info; - struct props props; -#define IDX_PropInfo 0 -#define IDX_Props 1 - struct spa_param_info params[2]; - - - struct port control_port; - struct port in_port; - struct port out_port; - - struct channelmix mix; - unsigned int started:1; - unsigned int is_passthrough:1; - uint32_t cpu_flags; - uint32_t max_align; -}; - -#define IS_CONTROL_PORT(this,d,id) (id == 1 && d == SPA_DIRECTION_INPUT) -#define IS_DATA_PORT(this,d,id) (id == 0) - -#define CHECK_PORT(this,d,id) (IS_CONTROL_PORT(this,d,id) || IS_DATA_PORT(this,d,id)) -#define GET_CONTROL_PORT(this,id) (&this->control_port) -#define GET_IN_PORT(this,id) (&this->in_port) -#define GET_OUT_PORT(this,id) (&this->out_port) -#define GET_PORT(this,d,id) (IS_CONTROL_PORT(this,d,id) ? GET_CONTROL_PORT(this,id) : (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,id) : GET_OUT_PORT(this,id))) - -#define _MASK(ch) (1ULL << SPA_AUDIO_CHANNEL_ ## ch) -#define STEREO (_MASK(FL)|_MASK(FR)) - -static void emit_info(struct impl *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) { - spa_node_emit_info(&this->hooks, &this->info); - this->info.change_mask = old; - } -} - -static void emit_props_changed(struct impl *this) -{ - this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; - this->params[IDX_Props].flags ^= SPA_PARAM_INFO_SERIAL; - emit_info(this, false); -} - -static uint64_t default_mask(uint32_t channels) -{ - uint64_t mask = 0; - switch (channels) { - case 7: - case 8: - mask |= _MASK(RL); - mask |= _MASK(RR); - SPA_FALLTHROUGH - case 5: - case 6: - mask |= _MASK(SL); - mask |= _MASK(SR); - if ((channels & 1) == 0) - mask |= _MASK(LFE); - SPA_FALLTHROUGH - case 3: - mask |= _MASK(FC); - SPA_FALLTHROUGH - case 2: - mask |= _MASK(FL); - mask |= _MASK(FR); - break; - case 1: - mask |= _MASK(MONO); - break; - case 4: - mask |= _MASK(FL); - mask |= _MASK(FR); - mask |= _MASK(RL); - mask |= _MASK(RR); - break; - } - return mask; -} - -static void fix_volumes(struct volumes *vols, uint32_t channels) -{ - float s; - uint32_t i; - if (vols->n_volumes > 0) { - s = 0.0f; - for (i = 0; i < vols->n_volumes; i++) - s += vols->volumes[i]; - s /= vols->n_volumes; - } else { - s = 1.0f; - } - vols->n_volumes = channels; - for (i = 0; i < vols->n_volumes; i++) - vols->volumes[i] = s; -} - -static int remap_volumes(struct impl *this, const struct spa_audio_info *info) -{ - struct props *p = &this->props; - uint32_t i, j, target = info->info.raw.channels; - - for (i = 0; i < p->n_channels; i++) { - for (j = i; j < target; j++) { - spa_log_debug(this->log, "%d %d: %d <-> %d", i, j, - p->channel_map[i], info->info.raw.position[j]); - if (p->channel_map[i] != info->info.raw.position[j]) - continue; - if (i != j) { - SPA_SWAP(p->channel_map[i], p->channel_map[j]); - SPA_SWAP(p->channel.volumes[i], p->channel.volumes[j]); - SPA_SWAP(p->soft.volumes[i], p->soft.volumes[j]); - SPA_SWAP(p->monitor.volumes[i], p->monitor.volumes[j]); - } - break; - } - } - p->n_channels = target; - for (i = 0; i < p->n_channels; i++) - p->channel_map[i] = info->info.raw.position[i]; - - if (target == 0) - return 0; - if (p->channel.n_volumes != target) - fix_volumes(&p->channel, target); - if (p->soft.n_volumes != target) - fix_volumes(&p->soft, target); - if (p->monitor.n_volumes != target) - fix_volumes(&p->monitor, target); - - return 1; -} - -static void set_volume(struct impl *this) -{ - struct volumes *vol; - - if (this->mix.set_volume == NULL || - this->props.disabled) - return; - - if (this->props.have_soft_volume) - vol = &this->props.soft; - else - vol = &this->props.channel; - - channelmix_set_volume(&this->mix, this->props.volume, vol->mute, - vol->n_volumes, vol->volumes); -} - -static int setup_convert(struct impl *this, - enum spa_direction direction, - const struct spa_audio_info *info) -{ - const struct spa_audio_info *src_info, *dst_info; - uint32_t i, src_chan, dst_chan, p; - uint64_t src_mask, dst_mask; - int res; - - if (direction == SPA_DIRECTION_INPUT) { - src_info = info; - dst_info = &GET_OUT_PORT(this, 0)->format; - } else { - src_info = &GET_IN_PORT(this, 0)->format; - dst_info = info; - } - - src_chan = src_info->info.raw.channels; - dst_chan = dst_info->info.raw.channels; - - for (i = 0, src_mask = 0; i < src_chan; i++) { - p = src_info->info.raw.position[i]; - src_mask |= 1ULL << (p < 64 ? p : 0); - } - for (i = 0, dst_mask = 0; i < dst_chan; i++) { - p = dst_info->info.raw.position[i]; - dst_mask |= 1ULL << (p < 64 ? p : 0); - } - - if (src_mask & 1) - src_mask = default_mask(src_chan); - if (dst_mask & 1) - dst_mask = default_mask(dst_chan); - - spa_log_info(this->log, "%p: %s/%d@%d->%s/%d@%d %08"PRIx64":%08"PRIx64, this, - spa_debug_type_find_name(spa_type_audio_format, src_info->info.raw.format), - src_chan, - src_info->info.raw.rate, - spa_debug_type_find_name(spa_type_audio_format, dst_info->info.raw.format), - dst_chan, - dst_info->info.raw.rate, - src_mask, dst_mask); - - if (src_info->info.raw.rate != dst_info->info.raw.rate) - return -EINVAL; - - this->mix.src_chan = src_chan; - this->mix.src_mask = src_mask; - this->mix.dst_chan = dst_chan; - this->mix.dst_mask = dst_mask; - this->mix.cpu_flags = this->cpu_flags; - this->mix.log = this->log; - this->mix.freq = src_info->info.raw.rate; - - if ((res = channelmix_init(&this->mix)) < 0) - return res; - - remap_volumes(this, this->direction == SPA_DIRECTION_INPUT ? src_info : dst_info); - set_volume(this); - - emit_props_changed(this); - - this->is_passthrough = SPA_FLAG_IS_SET(this->mix.flags, CHANNELMIX_FLAG_IDENTITY); - if (!this->is_passthrough && this->props.disabled) - return -EINVAL; - - spa_log_debug(this->log, "%p: got channelmix features %08x:%08x flags:%08x passthrough:%d", - this, this->cpu_flags, this->mix.cpu_flags, - this->mix.flags, this->is_passthrough); - - - return 0; -} - -static int impl_node_enum_params(void *object, int seq, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - struct impl *this = object; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer[4096]; - struct spa_result_node_params result; - uint32_t count = 0; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - - result.id = id; - result.next = start; - next: - result.index = result.next++; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - case SPA_PARAM_PropInfo: - { - struct props *p = &this->props; - - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_volume), - SPA_PROP_INFO_description, SPA_POD_String("Volume"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0)); - break; - case 1: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_mute), - SPA_PROP_INFO_description, SPA_POD_String("Mute"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->channel.mute)); - break; - case 2: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_channelVolumes), - SPA_PROP_INFO_description, SPA_POD_String("Channel Volumes"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0), - SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array)); - break; - case 3: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_channelMap), - SPA_PROP_INFO_description, SPA_POD_String("Channel Map"), - SPA_PROP_INFO_type, SPA_POD_Id(SPA_AUDIO_CHANNEL_UNKNOWN), - SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array)); - break; - case 4: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_softMute), - SPA_PROP_INFO_description, SPA_POD_String("Soft Mute"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->soft.mute)); - break; - case 5: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_softVolumes), - SPA_PROP_INFO_description, SPA_POD_String("Soft Volumes"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0), - SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array)); - break; - case 6: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_monitorMute), - SPA_PROP_INFO_description, SPA_POD_String("Monitor Mute"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->monitor.mute)); - break; - case 7: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_monitorVolumes), - SPA_PROP_INFO_description, SPA_POD_String("Monitor Volumes"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float(p->volume, 0.0, 10.0), - SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array)); - break; - case 8: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("channelmix.disable"), - SPA_PROP_INFO_description, SPA_POD_String("Disable Channel mixing"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->disabled), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 9: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("channelmix.normalize"), - SPA_PROP_INFO_description, SPA_POD_String("Normalize Volumes"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool( - SPA_FLAG_IS_SET(this->mix.options, CHANNELMIX_OPTION_NORMALIZE)), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 10: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("channelmix.mix-lfe"), - SPA_PROP_INFO_description, SPA_POD_String("Mix LFE into channels"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool( - SPA_FLAG_IS_SET(this->mix.options, CHANNELMIX_OPTION_MIX_LFE)), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 11: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("channelmix.upmix"), - SPA_PROP_INFO_description, SPA_POD_String("Enable upmixing"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool( - SPA_FLAG_IS_SET(this->mix.options, CHANNELMIX_OPTION_UPMIX)), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 12: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("channelmix.lfe-cutoff"), - SPA_PROP_INFO_description, SPA_POD_String("LFE cutoff frequency (Hz)"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float( - this->mix.lfe_cutoff, 0.0, 1000.0), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 13: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("channelmix.fc-cutoff"), - SPA_PROP_INFO_description, SPA_POD_String("FC cutoff frequency (Hz)"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float( - this->mix.fc_cutoff, 0.0, 48000.0), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 14: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("channelmix.rear-delay"), - SPA_PROP_INFO_description, SPA_POD_String("Rear channels delay (ms)"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float( - this->mix.rear_delay, 0.0, 1000.0), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 15: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("channelmix.stereo-widen"), - SPA_PROP_INFO_description, SPA_POD_String("Stereo widen"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float( - this->mix.widen, 0.0, 1.0), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 16: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("channelmix.hilbert-taps"), - SPA_PROP_INFO_description, SPA_POD_String("Taps for phase shift of rear"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int( - this->mix.hilbert_taps, 0, MAX_TAPS), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 17: - { - struct spa_pod_frame f[2]; - uint32_t i; - spa_pod_builder_push_object(&b, &f[0], - SPA_TYPE_OBJECT_PropInfo, id); - spa_pod_builder_add(&b, - SPA_PROP_INFO_name, SPA_POD_String("channelmix.upmix-method"), - SPA_PROP_INFO_description, SPA_POD_String("Upmix Method to use"), - SPA_PROP_INFO_type, SPA_POD_String( - channelmix_upmix_info[this->mix.upmix].label), - 0); - spa_pod_builder_prop(&b, SPA_PROP_INFO_labels, 0); - spa_pod_builder_push_struct(&b, &f[1]); - for (i = 0; i < SPA_N_ELEMENTS(channelmix_upmix_info); i++) { - spa_pod_builder_string(&b, channelmix_upmix_info[i].label); - spa_pod_builder_string(&b, channelmix_upmix_info[i].description); - } - spa_pod_builder_pop(&b, &f[1]); - spa_pod_builder_add(&b, - SPA_PROP_INFO_params, SPA_POD_Bool(true), - 0); - param = spa_pod_builder_pop(&b, &f[0]); - break; - } - default: - return 0; - } - break; - } - case SPA_PARAM_Props: - { - struct props *p = &this->props; - struct spa_pod_frame f[2]; - - switch (result.index) { - case 0: - spa_pod_builder_push_object(&b, &f[0], - SPA_TYPE_OBJECT_Props, id); - spa_pod_builder_add(&b, - SPA_PROP_volume, SPA_POD_Float(p->volume), - SPA_PROP_mute, SPA_POD_Bool(p->channel.mute), - SPA_PROP_channelVolumes, SPA_POD_Array(sizeof(float), - SPA_TYPE_Float, - p->channel.n_volumes, - p->channel.volumes), - SPA_PROP_channelMap, SPA_POD_Array(sizeof(uint32_t), - SPA_TYPE_Id, - p->n_channels, - p->channel_map), - SPA_PROP_softMute, SPA_POD_Bool(p->soft.mute), - SPA_PROP_softVolumes, SPA_POD_Array(sizeof(float), - SPA_TYPE_Float, - p->soft.n_volumes, - p->soft.volumes), - SPA_PROP_monitorMute, SPA_POD_Bool(p->monitor.mute), - SPA_PROP_monitorVolumes, SPA_POD_Array(sizeof(float), - SPA_TYPE_Float, - p->monitor.n_volumes, - p->monitor.volumes), - 0); - spa_pod_builder_prop(&b, SPA_PROP_params, 0); - spa_pod_builder_push_struct(&b, &f[1]); - spa_pod_builder_string(&b, "channelmix.disable"); - spa_pod_builder_bool(&b, this->props.disabled); - spa_pod_builder_string(&b, "channelmix.normalize"); - spa_pod_builder_bool(&b, SPA_FLAG_IS_SET(this->mix.options, - CHANNELMIX_OPTION_NORMALIZE)); - spa_pod_builder_string(&b, "channelmix.mix-lfe"); - spa_pod_builder_bool(&b, SPA_FLAG_IS_SET(this->mix.options, - CHANNELMIX_OPTION_MIX_LFE)); - spa_pod_builder_string(&b, "channelmix.upmix"); - spa_pod_builder_bool(&b, SPA_FLAG_IS_SET(this->mix.options, - CHANNELMIX_OPTION_UPMIX)); - spa_pod_builder_string(&b, "channelmix.lfe-cutoff"); - spa_pod_builder_float(&b, this->mix.lfe_cutoff); - spa_pod_builder_string(&b, "channelmix.fc-cutoff"); - spa_pod_builder_float(&b, this->mix.fc_cutoff); - spa_pod_builder_string(&b, "channelmix.rear-delay"); - spa_pod_builder_float(&b, this->mix.rear_delay); - spa_pod_builder_string(&b, "channelmix.stereo-widen"); - spa_pod_builder_float(&b, this->mix.widen); - spa_pod_builder_string(&b, "channelmix.hilbert-taps"); - spa_pod_builder_int(&b, this->mix.hilbert_taps); - spa_pod_builder_string(&b, "channelmix.upmix-method"); - spa_pod_builder_string(&b, channelmix_upmix_info[this->mix.upmix].label); - spa_pod_builder_pop(&b, &f[1]); - param = spa_pod_builder_pop(&b, &f[0]); - break; - default: - return 0; - } - break; - } - default: - return -ENOENT; - } - - if (spa_pod_filter(&b, &result.param, param, filter) < 0) - goto next; - - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); - - if (++count != num) - goto next; - - return 0; -} - -static int channelmix_set_param(struct impl *this, const char *k, const char *s) -{ - if (spa_streq(k, "channelmix.disable")) - this->props.disabled = spa_atob(s); - else if (spa_streq(k, "channelmix.normalize")) - SPA_FLAG_UPDATE(this->mix.options, CHANNELMIX_OPTION_NORMALIZE, spa_atob(s)); - else if (spa_streq(k, "channelmix.mix-lfe")) - SPA_FLAG_UPDATE(this->mix.options, CHANNELMIX_OPTION_MIX_LFE, spa_atob(s)); - else if (spa_streq(k, "channelmix.upmix")) - SPA_FLAG_UPDATE(this->mix.options, CHANNELMIX_OPTION_UPMIX, spa_atob(s)); - else if (spa_streq(k, "channelmix.lfe-cutoff")) - spa_atof(s, &this->mix.lfe_cutoff); - else if (spa_streq(k, "channelmix.fc-cutoff")) - spa_atof(s, &this->mix.fc_cutoff); - else if (spa_streq(k, "channelmix.rear-delay")) - spa_atof(s, &this->mix.rear_delay); - else if (spa_streq(k, "channelmix.stereo-widen")) - spa_atof(s, &this->mix.widen); - else if (spa_streq(k, "channelmix.hilbert-taps")) - spa_atou32(s, &this->mix.hilbert_taps, 0); - else if (spa_streq(k, "channelmix.upmix-method")) - this->mix.upmix = channelmix_upmix_from_label(s); - else - return 0; - return 1; -} - -static int parse_prop_params(struct impl *this, struct spa_pod *params) -{ - struct spa_pod_parser prs; - struct spa_pod_frame f; - int changed = 0; - - spa_pod_parser_pod(&prs, params); - if (spa_pod_parser_push_struct(&prs, &f) < 0) - return 0; - - while (true) { - const char *name; - struct spa_pod *pod; - char value[512], buf[128]; - - if (spa_pod_parser_get_string(&prs, &name) < 0) - break; - - if (spa_pod_parser_get_pod(&prs, &pod) < 0) - break; - - if (spa_pod_is_string(pod)) { - spa_pod_copy_string(pod, sizeof(value), value); - } else if (spa_pod_is_float(pod)) { - snprintf(value, sizeof(value), "%s", - spa_json_format_float(buf, sizeof(buf), - SPA_POD_VALUE(struct spa_pod_float, pod))); - } else if (spa_pod_is_int(pod)) { - snprintf(value, sizeof(value), "%d", - SPA_POD_VALUE(struct spa_pod_int, pod)); - } else if (spa_pod_is_bool(pod)) { - snprintf(value, sizeof(value), "%s", - SPA_POD_VALUE(struct spa_pod_bool, pod) ? - "true" : "false"); - } else - continue; - - spa_log_info(this->log, "key:'%s' val:'%s'", name, value); - changed += channelmix_set_param(this, name, value); - } - if (changed && !this->props.disabled) - channelmix_init(&this->mix); - return changed; -} - -static int apply_props(struct impl *this, const struct spa_pod *param) -{ - struct spa_pod_prop *prop; - struct spa_pod_object *obj = (struct spa_pod_object *) param; - struct props *p = &this->props; - int changed = 0; - bool have_channel_volume = false; - bool have_soft_volume = false; - uint32_t n; - - if (param == NULL) - return 0; - - SPA_POD_OBJECT_FOREACH(obj, prop) { - switch (prop->key) { - case SPA_PROP_volume: - if (spa_pod_get_float(&prop->value, &p->volume) == 0) - changed++; - break; - case SPA_PROP_mute: - if (spa_pod_get_bool(&prop->value, &p->channel.mute) == 0) { - changed++; - have_channel_volume = true; - } - break; - case SPA_PROP_channelVolumes: - if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - p->channel.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { - p->channel.n_volumes = n; - changed++; - have_channel_volume = true; - } - break; - case SPA_PROP_channelMap: - if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Id, - p->channel_map, SPA_AUDIO_MAX_CHANNELS)) > 0) { - p->n_channels = n; - changed++; - } - break; - case SPA_PROP_softMute: - if (spa_pod_get_bool(&prop->value, &p->soft.mute) == 0) { - changed++; - have_soft_volume = true; - } - break; - case SPA_PROP_softVolumes: - if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - p->soft.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { - p->soft.n_volumes = n; - changed++; - have_soft_volume = true; - } - break; - case SPA_PROP_monitorMute: - if (spa_pod_get_bool(&prop->value, &p->monitor.mute) == 0) - changed++; - break; - case SPA_PROP_monitorVolumes: - if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - p->monitor.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { - p->monitor.n_volumes = n; - changed++; - } - break; - case SPA_PROP_params: - changed += parse_prop_params(this, &prop->value); - break; - default: - break; - } - } - - if (changed) { - struct port *port = GET_PORT(this, this->direction, 0); - if (have_soft_volume) - p->have_soft_volume = true; - else if (have_channel_volume) - p->have_soft_volume = false; - - if (port->have_format) - remap_volumes(this, &port->format); - - set_volume(this); - } - return changed; -} - -static int apply_midi(struct impl *this, const struct spa_pod *value) -{ - const uint8_t *val = SPA_POD_BODY(value); - uint32_t size = SPA_POD_BODY_SIZE(value); - struct props *p = &this->props; - - if (size < 3) - return -EINVAL; - - if ((val[0] & 0xf0) != 0xb0 || val[1] != 7) - return 0; - - p->volume = val[2] / 127.0; - set_volume(this); - return 1; -} - -static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_debug(this->log, "%p: io %d %p/%zd", this, id, data, size); - - switch (id) { - case SPA_IO_Position: - this->io_position = data; - break; - default: - return -ENOENT; - } - return 0; -} - -static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - switch (id) { - case SPA_PARAM_Props: - if (apply_props(this, param) > 0) - emit_props_changed(this); - break; - default: - return -ENOENT; - } - return 0; -} - -static int impl_node_send_command(void *object, const struct spa_command *command) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(command != NULL, -EINVAL); - - switch (SPA_NODE_COMMAND_ID(command)) { - case SPA_NODE_COMMAND_Start: - this->started = true; - break; - case SPA_NODE_COMMAND_Suspend: - case SPA_NODE_COMMAND_Flush: - case SPA_NODE_COMMAND_Pause: - this->started = false; - break; - default: - return -ENOTSUP; - } - return 0; -} - -static void emit_port_info(struct impl *this, struct port *port, bool full) -{ - uint64_t old = full ? port->info.change_mask : 0; - if (full) - port->info.change_mask = port->info_all; - if (port->info.change_mask) { - spa_node_emit_port_info(&this->hooks, - port->direction, port->id, &port->info); - port->info.change_mask = old; - } -} - -static int -impl_node_add_listener(void *object, - struct spa_hook *listener, - const struct spa_node_events *events, - void *data) -{ - struct impl *this = object; - struct spa_hook_list save; - struct spa_dict_item items[2]; - uint32_t n_items = 0; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_hook_list_isolate(&this->hooks, &save, listener, events, data); - - emit_info(this, true); - emit_port_info(this, GET_IN_PORT(this, 0), true); - emit_port_info(this, GET_OUT_PORT(this, 0), true); - - struct port *control_port = GET_CONTROL_PORT(this, 1); - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_NAME, "control"); - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "8 bit raw midi"); - control_port->info.props = &SPA_DICT_INIT(items, n_items); - emit_port_info(this, control_port, true); - - spa_hook_list_join(&this->hooks, &save); - - return 0; -} - -static int -impl_node_set_callbacks(void *object, - const struct spa_node_callbacks *callbacks, - void *user_data) -{ - return 0; -} - -static int impl_node_add_port(void *object, - enum spa_direction direction, uint32_t port_id, - const struct spa_dict *props) -{ - return -ENOTSUP; -} - -static int impl_node_remove_port(void *object, - enum spa_direction direction, uint32_t port_id) -{ - return -ENOTSUP; -} - -static int port_enum_formats(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t index, - struct spa_pod **param, - struct spa_pod_builder *builder) -{ - struct impl *this = object; - - switch (index) { - case 0: - if (IS_CONTROL_PORT(this, direction, port_id)) { - *param = spa_pod_builder_add_object(builder, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control)); - } else { - struct spa_pod_frame f; - struct port *other; - int32_t channels, min = 1, max = INT32_MAX; - - other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), 0); - - spa_pod_builder_push_object(builder, &f, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); - spa_pod_builder_add(builder, - 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), - 0); - - if (other->have_format) { - channels = other->format.info.raw.channels; - if (this->props.disabled) - min = max = channels; - - spa_pod_builder_add(builder, - SPA_FORMAT_AUDIO_rate, SPA_POD_Int(other->format.info.raw.rate), - SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int( - channels, min, max), - 0); - } else { - uint32_t rate = this->io_position ? - this->io_position->clock.rate.denom : DEFAULT_RATE; - channels = DEFAULT_CHANNELS; - - spa_pod_builder_add(builder, - SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_RANGE_Int(rate, 0, INT32_MAX), - SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int( - channels, min, max), - 0); - } - *param = spa_pod_builder_pop(builder, &f); - } - break; - default: - return 0; - } - return 1; -} - -static int -impl_node_port_enum_params(void *object, int seq, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - struct impl *this = object; - struct port *port, *other; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer[1024]; - struct spa_result_node_params result; - uint32_t count = 0; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), port_id); - - spa_log_debug(this->log, "%p: enum params port %d.%d %d %u", - this, direction, port_id, seq, id); - - result.id = id; - result.next = start; - next: - result.index = result.next++; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - case SPA_PARAM_EnumFormat: - if ((res = port_enum_formats(this, direction, port_id, - result.index, ¶m, &b)) <= 0) - return res; - break; - - case SPA_PARAM_Format: - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - if (IS_CONTROL_PORT(this, direction, port_id)) - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control)); - else - param = spa_format_audio_raw_build(&b, id, &port->format.info.raw); - break; - - case SPA_PARAM_Buffers: - { - uint32_t buffers, size; - - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - if (IS_CONTROL_PORT(this, direction, port_id)) { - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), - SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( - DEFAULT_CONTROL_BUFFER_SIZE, - 1024, - INT32_MAX), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(1)); - } else { - if (other->n_buffers > 0) { - buffers = other->n_buffers; - size = other->size / other->stride; - } else { - buffers = 1; - size = this->quantum_limit; - } - - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(buffers, 1, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(port->blocks), - SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( - size * port->stride, - 16 * port->stride, - INT32_MAX), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->stride)); - } - break; - } - case SPA_PARAM_Meta: - switch (result.index) { - case 0: - if (IS_CONTROL_PORT(this, direction, port_id)) - return -EINVAL; - - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamMeta, id, - SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), - SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); - break; - default: - return 0; - } - break; - - case SPA_PARAM_IO: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamIO, id, - SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers), - SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); - break; - default: - return 0; - } - break; - default: - return -ENOENT; - } - - if (spa_pod_filter(&b, &result.param, param, filter) < 0) - goto next; - - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); - - if (++count != num) - goto next; - - return 0; -} - -static int clear_buffers(struct impl *this, struct port *port) -{ - if (port->n_buffers > 0) { - spa_log_debug(this->log, "%p: clear buffers %p", this, port); - port->n_buffers = 0; - spa_list_init(&port->queue); - } - return 0; -} - -static int port_set_format(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t flags, - const struct spa_pod *format) -{ - struct impl *this = object; - struct port *port, *other; - int res = 0; - - port = GET_PORT(this, direction, port_id); - other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), port_id); - - if (format == NULL) { - if (port->have_format) { - port->have_format = false; - clear_buffers(this, port); - if (this->mix.process) - channelmix_free(&this->mix); - } - } else { - struct spa_audio_info info = { 0 }; - - if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) - return res; - - if (IS_CONTROL_PORT(this, direction, port_id)) { - if (info.media_type != SPA_MEDIA_TYPE_application || - info.media_subtype != SPA_MEDIA_SUBTYPE_control) - return -EINVAL; - } else { - 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; - - if (info.info.raw.format != SPA_AUDIO_FORMAT_F32P) - return -EINVAL; - - port->stride = sizeof(float); - port->blocks = info.info.raw.channels; - - if (other->have_format) { - if ((res = setup_convert(this, direction, &info)) < 0) - return res; - } - } - port->format = info; - port->have_format = true; - - spa_log_debug(this->log, "%p: set format on port %d %d", this, port_id, res); - } - - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - if (port->have_format) { - port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); - port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); - } else { - port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - } - emit_port_info(this, port, false); - - return res; -} - -static int -impl_node_port_set_param(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - switch (id) { - case SPA_PARAM_Format: - return port_set_format(object, direction, port_id, flags, param); - default: - break; - } - - return -ENOENT; -} - -static int -impl_node_port_use_buffers(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t flags, - struct spa_buffer **buffers, uint32_t n_buffers) -{ - struct impl *this = object; - struct port *port; - uint32_t i, j, size = SPA_ID_INVALID; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - - if (IS_DATA_PORT(this, direction, port_id)) - spa_return_val_if_fail(port->have_format, -EIO); - - spa_log_debug(this->log, "%p: use buffers %d on port %d", this, n_buffers, port_id); - - clear_buffers(this, port); - - for (i = 0; i < n_buffers; i++) { - struct buffer *b; - uint32_t n_datas = buffers[i]->n_datas; - struct spa_data *d = buffers[i]->datas; - - b = &port->buffers[i]; - b->id = i; - b->flags = 0; - b->outbuf = buffers[i]; - b->h = spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h)); - - for (j = 0; j < n_datas; j++) { - if (size == SPA_ID_INVALID) - size = d[j].maxsize; - else if (size != d[j].maxsize) - return -EINVAL; - - if (d[j].data == NULL) { - spa_log_error(this->log, "%p: invalid memory on buffer %p", this, - buffers[i]); - return -EINVAL; - } - if (!SPA_IS_ALIGNED(d[j].data, this->max_align)) { - spa_log_warn(this->log, "%p: memory %d on buffer %d not aligned", - this, j, i); - } - b->datas[j] = d[j].data; - if (direction == SPA_DIRECTION_OUTPUT && - !SPA_FLAG_IS_SET(d[j].flags, SPA_DATA_FLAG_DYNAMIC)) - this->is_passthrough = false; - } - if (direction == SPA_DIRECTION_OUTPUT) - spa_list_append(&port->queue, &b->link); - else - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - } - port->n_buffers = n_buffers; - port->size = size; - - return 0; -} - -static int -impl_node_port_set_io(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - struct port *port; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - - switch (id) { - case SPA_IO_Buffers: - port->io = data; - break; - default: - return -ENOENT; - } - return 0; -} - -static void recycle_buffer(struct impl *this, uint32_t id) -{ - struct port *port = GET_OUT_PORT(this, 0); - struct buffer *b = &port->buffers[id]; - - if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) { - spa_list_append(&port->queue, &b->link); - SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); - spa_log_trace_fp(this->log, "%p: recycle buffer %d", this, id); - } -} - -static struct buffer *dequeue_buffer(struct impl *this, struct port *port) -{ - struct buffer *b; - - if (spa_list_is_empty(&port->queue)) - return NULL; - - b = spa_list_first(&port->queue, struct buffer, link); - spa_list_remove(&b->link); - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - - return b; -} - - -static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL); - - recycle_buffer(this, buffer_id); - - return 0; -} - -static int channelmix_process_control(struct impl *this, struct port *ctrlport, - void * SPA_RESTRICT dst[], const void * SPA_RESTRICT src[], - uint32_t n_samples) -{ - struct spa_pod_control *c, *prev = NULL; - uint32_t avail_samples = n_samples; - uint32_t i; - const float **s = (const float **)src; - float **d = (float **)dst; - - SPA_POD_SEQUENCE_FOREACH(ctrlport->ctrl, c) { - uint32_t chunk; - - if (avail_samples == 0) - return 0; - - /* ignore old control offsets */ - if (c->offset <= ctrlport->ctrl_offset) { - prev = c; - continue; - } - - if (prev) { - switch (prev->type) { - case SPA_CONTROL_Midi: - apply_midi(this, &prev->value); - break; - case SPA_CONTROL_Properties: - apply_props(this, &prev->value); - break; - default: - continue; - } - } - - chunk = SPA_MIN(avail_samples, c->offset - ctrlport->ctrl_offset); - - spa_log_trace_fp(this->log, "%p: process %d %d", this, - c->offset, chunk); - - channelmix_process(&this->mix, dst, src, chunk); - for (i = 0; i < this->mix.src_chan; i++) - s[i] += chunk; - for (i = 0; i < this->mix.dst_chan; i++) - d[i] += chunk; - - avail_samples -= chunk; - ctrlport->ctrl_offset += chunk; - - prev = c; - } - - /* when we get here we run out of control points but still have some - * remaining samples */ - spa_log_trace_fp(this->log, "%p: remain %d", this, avail_samples); - if (avail_samples > 0) - channelmix_process(&this->mix, dst, src, avail_samples); - - return 1; -} - -static int impl_node_process(void *object) -{ - struct impl *this = object; - struct port *outport, *inport, *ctrlport; - struct spa_io_buffers *outio, *inio, *ctrlio; - struct buffer *sbuf, *dbuf; - struct spa_pod_sequence *ctrl = NULL; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - outport = GET_OUT_PORT(this, 0); - inport = GET_IN_PORT(this, 0); - ctrlport = GET_CONTROL_PORT(this, 1); - - outio = outport->io; - inio = inport->io; - ctrlio = ctrlport->io; - - spa_return_val_if_fail(outio != NULL, -EIO); - spa_return_val_if_fail(inio != NULL, -EIO); - - spa_log_trace_fp(this->log, "%p: status %p %d %d -> %p %d %d", this, - inio, inio->status, inio->buffer_id, - outio, outio->status, outio->buffer_id); - - if (SPA_UNLIKELY(outio->status == SPA_STATUS_HAVE_DATA)) - return SPA_STATUS_HAVE_DATA; - /* recycle */ - if (SPA_LIKELY(outio->buffer_id < outport->n_buffers)) { - recycle_buffer(this, outio->buffer_id); - outio->buffer_id = SPA_ID_INVALID; - } - if (SPA_UNLIKELY(inio->status != SPA_STATUS_HAVE_DATA)) - return outio->status = inio->status; - - if (SPA_UNLIKELY(inio->buffer_id >= inport->n_buffers)) - return inio->status = -EINVAL; - - if (SPA_UNLIKELY((dbuf = dequeue_buffer(this, outport)) == NULL)) - return outio->status = -EPIPE; - - sbuf = &inport->buffers[inio->buffer_id]; - - if (ctrlio != NULL && - ctrlio->status == SPA_STATUS_HAVE_DATA && - ctrlio->buffer_id < ctrlport->n_buffers) { - struct buffer *cbuf = &ctrlport->buffers[ctrlio->buffer_id]; - struct spa_data *d = &cbuf->outbuf->datas[0]; - - ctrl = spa_pod_from_data(d->data, d->maxsize, d->chunk->offset, d->chunk->size); - if (ctrl && !spa_pod_is_sequence(&ctrl->pod)) - ctrl = NULL; - if (ctrl != ctrlport->ctrl) { - ctrlport->ctrl = ctrl; - ctrlport->ctrl_offset = 0; - } - } - - { - uint32_t i, n_samples; - struct spa_buffer *sb = sbuf->outbuf, *db = dbuf->outbuf; - uint32_t n_src_datas = sb->n_datas; - uint32_t n_dst_datas = db->n_datas; - const void *src_datas[n_src_datas]; - void *dst_datas[n_dst_datas]; - bool is_passthrough; - - is_passthrough = this->is_passthrough && - SPA_FLAG_IS_SET(this->mix.flags, CHANNELMIX_FLAG_IDENTITY) && - ctrlport->ctrl == NULL; - - n_samples = sb->datas[0].chunk->size / inport->stride; - - for (i = 0; i < n_src_datas; i++) - src_datas[i] = sb->datas[i].data; - for (i = 0; i < n_dst_datas; i++) { - dst_datas[i] = is_passthrough ? (void*)src_datas[i] : dbuf->datas[i]; - db->datas[i].data = dst_datas[i]; - db->datas[i].chunk->size = n_samples * outport->stride; - } - - spa_log_trace_fp(this->log, "%p: n_src:%d n_dst:%d n_samples:%d p:%d", - this, n_src_datas, n_dst_datas, n_samples, is_passthrough); - - if (!is_passthrough) { - if (ctrlport->ctrl != NULL) { - /* if return value is 1, the sequence has been processed */ - if (channelmix_process_control(this, ctrlport, dst_datas, - src_datas, n_samples) == 1) { - ctrlio->status = SPA_STATUS_OK; - ctrlport->ctrl = NULL; - } - } else { - channelmix_process(&this->mix, dst_datas, - src_datas, n_samples); - } - } - } - - outio->status = SPA_STATUS_HAVE_DATA; - outio->buffer_id = dbuf->id; - - inio->status = SPA_STATUS_NEED_DATA; - - return SPA_STATUS_HAVE_DATA | SPA_STATUS_NEED_DATA; -} - -static const struct spa_node_methods impl_node = { - SPA_VERSION_NODE_METHODS, - .add_listener = impl_node_add_listener, - .set_callbacks = impl_node_set_callbacks, - .enum_params = impl_node_enum_params, - .set_param = impl_node_set_param, - .set_io = impl_node_set_io, - .send_command = impl_node_send_command, - .add_port = impl_node_add_port, - .remove_port = impl_node_remove_port, - .port_enum_params = impl_node_port_enum_params, - .port_set_param = impl_node_port_set_param, - .port_use_buffers = impl_node_port_use_buffers, - .port_set_io = impl_node_port_set_io, - .port_reuse_buffer = impl_node_port_reuse_buffer, - .process = impl_node_process, -}; - -static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) -{ - struct impl *this; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - spa_return_val_if_fail(interface != NULL, -EINVAL); - - this = (struct impl *) handle; - - if (spa_streq(type, SPA_TYPE_INTERFACE_Node)) - *interface = &this->node; - else - return -ENOENT; - - return 0; -} - -static int impl_clear(struct spa_handle *handle) -{ - return 0; -} - -static size_t -impl_get_size(const struct spa_handle_factory *factory, - const struct spa_dict *params) -{ - return sizeof(struct impl); -} - -static uint32_t channel_from_name(const char *name) -{ - int i; - for (i = 0; spa_type_audio_channel[i].name; i++) { - if (spa_streq(name, spa_debug_type_short_name(spa_type_audio_channel[i].name))) - return spa_type_audio_channel[i].type; - } - return SPA_AUDIO_CHANNEL_UNKNOWN; -} - -static inline uint32_t parse_position(uint32_t *pos, const char *val, size_t len) -{ - struct spa_json it[2]; - char v[256]; - uint32_t i = 0; - - spa_json_init(&it[0], val, len); - if (spa_json_enter_array(&it[0], &it[1]) <= 0) - spa_json_init(&it[1], val, len); - - while (spa_json_get_string(&it[1], v, sizeof(v)) > 0 && - i < SPA_AUDIO_MAX_CHANNELS) { - pos[i++] = channel_from_name(v); - } - return i; -} - -static int -impl_init(const struct spa_handle_factory *factory, - struct spa_handle *handle, - const struct spa_dict *info, - const struct spa_support *support, - uint32_t n_support) -{ - struct impl *this; - struct port *port; - uint32_t i; - - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(handle != NULL, -EINVAL); - - handle->get_interface = impl_get_interface; - handle->clear = impl_clear; - - this = (struct impl *) handle; - - this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - spa_log_topic_init(this->log, log_topic); - - this->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); - if (this->cpu) { - this->cpu_flags = spa_cpu_get_flags(this->cpu); - this->max_align = SPA_MIN(MAX_ALIGN, spa_cpu_get_max_align(this->cpu)); - } - - spa_hook_list_init(&this->hooks); - - props_reset(&this->props); - - this->mix.options = CHANNELMIX_OPTION_UPMIX; - this->mix.upmix = CHANNELMIX_UPMIX_PSD; - this->mix.log = this->log; - this->mix.lfe_cutoff = 150.0f; - this->mix.fc_cutoff = 12000.0f; - this->mix.rear_delay = 12.0f; - this->mix.widen = 0.0f; - - for (i = 0; info && i < info->n_items; i++) { - const char *k = info->items[i].key; - const char *s = info->items[i].value; - if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) - this->props.n_channels = parse_position(this->props.channel_map, s, strlen(s)); - else if (spa_streq(k, "clock.quantum-limit")) - spa_atou32(s, &this->quantum_limit, 0); - else if (spa_streq(k, "factory.mode")) { - if (spa_streq(s, "merge")) - this->direction = SPA_DIRECTION_OUTPUT; - else - this->direction = SPA_DIRECTION_INPUT; - } - else - channelmix_set_param(this, k, s); - - } - this->props.channel.n_volumes = this->props.n_channels; - this->props.soft.n_volumes = this->props.n_channels; - this->props.monitor.n_volumes = this->props.n_channels; - - this->node.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_Node, - SPA_VERSION_NODE, - &impl_node, this); - this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | - SPA_NODE_CHANGE_MASK_PARAMS; - this->info = SPA_NODE_INFO_INIT(); - this->info.flags = SPA_NODE_FLAG_RT; - this->info.max_input_ports = 2; - this->info.max_output_ports = 1; - this->params[IDX_PropInfo] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); - this->params[IDX_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); - this->info.params = this->params; - this->info.n_params = 2; - - port = GET_OUT_PORT(this, 0); - port->direction = SPA_DIRECTION_OUTPUT; - port->id = 0; - port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | - SPA_PORT_CHANGE_MASK_PARAMS; - port->info = SPA_PORT_INFO_INIT(); - port->info.flags = SPA_PORT_FLAG_DYNAMIC_DATA; - port->params[IDX_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); - port->params[IDX_Meta] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); - port->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - port->info.params = port->params; - port->info.n_params = 5; - spa_list_init(&port->queue); - - port = GET_IN_PORT(this, 0); - port->direction = SPA_DIRECTION_INPUT; - port->id = 0; - port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | - SPA_PORT_CHANGE_MASK_PARAMS; - port->info = SPA_PORT_INFO_INIT(); - port->info.flags = SPA_PORT_FLAG_NO_REF | - SPA_PORT_FLAG_DYNAMIC_DATA; - port->params[IDX_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); - port->params[IDX_Meta] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); - port->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - port->info.params = port->params; - port->info.n_params = 0; - spa_list_init(&port->queue); - - port = GET_CONTROL_PORT(this, 1); - port->direction = SPA_DIRECTION_INPUT; - port->id = 1; - port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | - SPA_PORT_CHANGE_MASK_PROPS | - SPA_PORT_CHANGE_MASK_PARAMS; - port->info = SPA_PORT_INFO_INIT(); - port->info.flags = SPA_PORT_FLAG_NO_REF | - SPA_PORT_FLAG_DYNAMIC_DATA; - port->params[IDX_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); - port->params[IDX_Meta] = SPA_PARAM_INFO(SPA_PARAM_Meta, 0); - port->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - port->info.params = port->params; - port->info.n_params = 4; - spa_list_init(&port->queue); - - return 0; -} - -static const struct spa_interface_info impl_interfaces[] = { - {SPA_TYPE_INTERFACE_Node,}, -}; - -static int -impl_enum_interface_info(const struct spa_handle_factory *factory, - const struct spa_interface_info **info, - uint32_t *index) -{ - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(info != NULL, -EINVAL); - spa_return_val_if_fail(index != NULL, -EINVAL); - - switch (*index) { - case 0: - *info = &impl_interfaces[*index]; - break; - default: - return 0; - } - (*index)++; - return 1; -} - -const struct spa_handle_factory spa_channelmix_factory = { - SPA_VERSION_HANDLE_FACTORY, - SPA_NAME_AUDIO_PROCESS_CHANNELMIX, - NULL, - impl_get_size, - impl_init, - impl_enum_interface_info, -}; diff --git a/spa/plugins/audioconvert/fmtconvert.c b/spa/plugins/audioconvert/fmtconvert.c deleted file mode 100644 index d1d52ad48..000000000 --- a/spa/plugins/audioconvert/fmtconvert.c +++ /dev/null @@ -1,1161 +0,0 @@ -/* Spa - * - * Copyright © 2018 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. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fmt-ops.h" - -#undef SPA_LOG_TOPIC_DEFAULT -#define SPA_LOG_TOPIC_DEFAULT log_topic -static struct spa_log_topic *log_topic = &SPA_LOG_TOPIC(0, "spa.fmtconvert"); - -#define DEFAULT_RATE 48000 -#define DEFAULT_CHANNELS 2 - -#define MAX_BUFFERS 32 -#define MAX_ALIGN FMT_OPS_MAX_ALIGN -#define MAX_DATAS SPA_AUDIO_MAX_CHANNELS - -#define PROP_DEFAULT_TRUNCATE false -#define PROP_DEFAULT_DITHER 0 - -struct impl; - -struct props { - bool truncate; - uint32_t dither; -}; - -static void props_reset(struct props *props) -{ - props->truncate = PROP_DEFAULT_TRUNCATE; - props->dither = PROP_DEFAULT_DITHER; -} - -struct buffer { - uint32_t id; -#define BUFFER_FLAG_OUT (1 << 0) - uint32_t flags; - struct spa_list link; - struct spa_buffer *outbuf; - struct spa_meta_header *h; - void *datas[MAX_DATAS]; -}; - -struct port { - uint32_t direction; - uint32_t id; - - struct spa_io_buffers *io; - - uint64_t info_all; - struct spa_port_info 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 - struct spa_param_info params[N_PORT_PARAMS]; - - struct spa_audio_info format; - uint32_t stride; - uint32_t blocks; - uint32_t size; - unsigned int have_format:1; - - struct buffer buffers[MAX_BUFFERS]; - uint32_t n_buffers; - - struct spa_list queue; -}; - -struct impl { - struct spa_handle handle; - struct spa_node node; - - struct spa_log *log; - struct spa_cpu *cpu; - uint32_t cpu_flags; - uint32_t max_align; - uint32_t quantum_limit; - - struct spa_io_position *io_position; - - uint64_t info_all; - struct spa_node_info info; - struct props props; -#define N_NODE_PARAMS 0 - struct spa_param_info params[1]; - - struct spa_hook_list hooks; - - struct port ports[2][1]; - - uint32_t src_remap[SPA_AUDIO_MAX_CHANNELS]; - uint32_t dst_remap[SPA_AUDIO_MAX_CHANNELS]; - - struct spa_latency_info latency[2]; - - struct convert conv; - unsigned int started:1; - unsigned int is_passthrough:1; -}; - -#define CHECK_PORT(this,d,id) (id == 0) -#define GET_PORT(this,d,id) (&this->ports[d][id]) -#define GET_IN_PORT(this,id) GET_PORT(this,SPA_DIRECTION_INPUT,id) -#define GET_OUT_PORT(this,id) GET_PORT(this,SPA_DIRECTION_OUTPUT,id) - -static int can_convert(const struct spa_audio_info *info1, const struct spa_audio_info *info2) -{ - if (info1->info.raw.channels != info2->info.raw.channels || - info1->info.raw.rate != info2->info.raw.rate) { - return 0; - } - return 1; -} - -static int setup_convert(struct impl *this) -{ - uint32_t src_fmt, dst_fmt; - struct spa_audio_info informat, outformat; - struct port *inport, *outport; - uint32_t i, j; - int res; - - inport = GET_IN_PORT(this, 0); - outport = GET_OUT_PORT(this, 0); - - if (!inport->have_format || !outport->have_format) - return -EIO; - - informat = inport->format; - outformat = outport->format; - - src_fmt = informat.info.raw.format; - dst_fmt = outformat.info.raw.format; - - spa_log_info(this->log, "%p: %s/%d@%d->%s/%d@%d", this, - spa_debug_type_find_name(spa_type_audio_format, src_fmt), - informat.info.raw.channels, - informat.info.raw.rate, - spa_debug_type_find_name(spa_type_audio_format, dst_fmt), - outformat.info.raw.channels, - outformat.info.raw.rate); - - if (!can_convert(&informat, &outformat)) - return -EINVAL; - - for (i = 0; i < informat.info.raw.channels; i++) { - for (j = 0; j < outformat.info.raw.channels; j++) { - if (informat.info.raw.position[i] != - outformat.info.raw.position[j]) - continue; - if (inport->blocks > 1) { - this->src_remap[j] = i; - if (outport->blocks > 1) - this->dst_remap[j] = j; - else - this->dst_remap[j] = 0; - } else { - this->src_remap[j] = 0; - if (outport->blocks > 1) - this->dst_remap[i] = j; - else - this->dst_remap[j] = 0; - } - spa_log_debug(this->log, "%p: channel %d -> %d (%s -> %s)", this, - i, j, - spa_debug_type_find_short_name(spa_type_audio_channel, - informat.info.raw.position[i]), - spa_debug_type_find_short_name(spa_type_audio_channel, - outformat.info.raw.position[j])); - outformat.info.raw.position[j] = -1; - break; - } - } - this->conv.src_fmt = src_fmt; - this->conv.dst_fmt = dst_fmt; - this->conv.n_channels = outformat.info.raw.channels; - this->conv.cpu_flags = this->cpu_flags; - - if ((res = convert_init(&this->conv)) < 0) - return res; - - this->is_passthrough = this->conv.is_passthrough; - - spa_log_debug(this->log, "%p: got converter features %08x:%08x passthrough:%d", this, - this->cpu_flags, this->conv.cpu_flags, this->is_passthrough); - - return 0; -} - -static int impl_node_enum_params(void *object, int seq, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - return -ENOTSUP; -} - -static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - return -ENOTSUP; -} - -static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_debug(this->log, "%p: io %d %p/%zd", this, id, data, size); - - switch (id) { - case SPA_IO_Position: - this->io_position = data; - break; - default: - return -ENOENT; - } - return 0; -} - -static int impl_node_send_command(void *object, const struct spa_command *command) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(command != NULL, -EINVAL); - - switch (SPA_NODE_COMMAND_ID(command)) { - case SPA_NODE_COMMAND_Start: - this->started = true; - break; - case SPA_NODE_COMMAND_Suspend: - case SPA_NODE_COMMAND_Flush: - case SPA_NODE_COMMAND_Pause: - this->started = false; - break; - default: - return -ENOTSUP; - } - return 0; -} - -static void emit_info(struct impl *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) { - spa_node_emit_info(&this->hooks, &this->info); - this->info.change_mask = old; - } -} - -static void emit_port_info(struct impl *this, struct port *port, bool full) -{ - uint64_t old = full ? port->info.change_mask : 0; - if (full) - port->info.change_mask = port->info_all; - if (port->info.change_mask) { - spa_node_emit_port_info(&this->hooks, - port->direction, port->id, &port->info); - port->info.change_mask = old; - } -} - -static int -impl_node_add_listener(void *object, - struct spa_hook *listener, - const struct spa_node_events *events, - void *data) -{ - struct impl *this = object; - struct spa_hook_list save; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_hook_list_isolate(&this->hooks, &save, listener, events, data); - - emit_info(this, true); - emit_port_info(this, GET_IN_PORT(this, 0), true); - emit_port_info(this, GET_OUT_PORT(this, 0), true); - - spa_hook_list_join(&this->hooks, &save); - - return 0; -} - -static int -impl_node_set_callbacks(void *object, - const struct spa_node_callbacks *callbacks, - void *user_data) -{ - return 0; -} - -static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id, - const struct spa_dict *props) -{ - return -ENOTSUP; -} - -static int -impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) -{ - return -ENOTSUP; -} - -static int int32_cmp(const void *v1, const void *v2) -{ - int32_t a1 = *(int32_t*)v1; - int32_t a2 = *(int32_t*)v2; - if (a1 == 0 && a2 != 0) - return 1; - if (a2 == 0 && a1 != 0) - return -1; - return a1 - a2; -} - -static int port_enum_formats(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t index, - struct spa_pod **param, - struct spa_pod_builder *builder) -{ - struct impl *this = object; - struct port *port, *other; - - port = GET_PORT(this, direction, port_id); - other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), 0); - - spa_log_debug(this->log, "%p: enum %p %d %d", this, other, port->have_format, other->have_format); - switch (index) { - case 0: - if (port->have_format) { - *param = spa_format_audio_raw_build(builder, - SPA_PARAM_EnumFormat, &port->format.info.raw); - } - else { - struct spa_pod_frame f; - struct spa_audio_info info; - - spa_pod_builder_push_object(builder, &f, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); - - spa_pod_builder_add(builder, - SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), - 0); - - if (other->have_format) - info = other->format; - else - info.info.raw.format = SPA_AUDIO_FORMAT_F32P; - - if (!other->have_format || - info.info.raw.format == SPA_AUDIO_FORMAT_F32P || - info.info.raw.format == SPA_AUDIO_FORMAT_F32) { - spa_pod_builder_add(builder, - SPA_FORMAT_AUDIO_format, SPA_POD_CHOICE_ENUM_Id(29, - info.info.raw.format, - SPA_AUDIO_FORMAT_F32P, - SPA_AUDIO_FORMAT_F32, - SPA_AUDIO_FORMAT_F32_OE, - SPA_AUDIO_FORMAT_F64P, - SPA_AUDIO_FORMAT_F64, - SPA_AUDIO_FORMAT_F64_OE, - SPA_AUDIO_FORMAT_S32P, - SPA_AUDIO_FORMAT_S32, - SPA_AUDIO_FORMAT_S32_OE, - SPA_AUDIO_FORMAT_U32, - SPA_AUDIO_FORMAT_S24_32P, - SPA_AUDIO_FORMAT_S24_32, - SPA_AUDIO_FORMAT_S24_32_OE, - SPA_AUDIO_FORMAT_U24_32, - SPA_AUDIO_FORMAT_S24P, - SPA_AUDIO_FORMAT_S24, - SPA_AUDIO_FORMAT_S24_OE, - SPA_AUDIO_FORMAT_U24, - SPA_AUDIO_FORMAT_S16P, - SPA_AUDIO_FORMAT_S16, - SPA_AUDIO_FORMAT_S16_OE, - SPA_AUDIO_FORMAT_U16, - SPA_AUDIO_FORMAT_S8P, - SPA_AUDIO_FORMAT_S8, - SPA_AUDIO_FORMAT_U8P, - SPA_AUDIO_FORMAT_U8, - SPA_AUDIO_FORMAT_ULAW, - SPA_AUDIO_FORMAT_ALAW), - 0); - } else { - spa_pod_builder_add(builder, - SPA_FORMAT_AUDIO_format, SPA_POD_CHOICE_ENUM_Id(5, - info.info.raw.format, - info.info.raw.format, - SPA_AUDIO_FORMAT_F32, - SPA_AUDIO_FORMAT_F32P, - SPA_AUDIO_FORMAT_F32_OE), - 0); - } - if (other->have_format) { - spa_pod_builder_add(builder, - SPA_FORMAT_AUDIO_rate, SPA_POD_Int(info.info.raw.rate), - SPA_FORMAT_AUDIO_channels, SPA_POD_Int(info.info.raw.channels), - 0); - if (!SPA_FLAG_IS_SET(info.info.raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED)) { - qsort(info.info.raw.position, info.info.raw.channels, - sizeof(uint32_t), int32_cmp); - spa_pod_builder_prop(builder, SPA_FORMAT_AUDIO_position, 0); - spa_pod_builder_array(builder, sizeof(uint32_t), SPA_TYPE_Id, - info.info.raw.channels, info.info.raw.position); - } - } else { - uint32_t rate = this->io_position ? - this->io_position->clock.rate.denom : DEFAULT_RATE; - - spa_pod_builder_add(builder, - SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_RANGE_Int( - rate, 1, INT32_MAX), - SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int( - DEFAULT_CHANNELS, 1, INT32_MAX), - 0); - } - *param = spa_pod_builder_pop(builder, &f); - } - break; - default: - return 0; - } - - return 1; -} - -static int -impl_node_port_enum_params(void *object, int seq, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - struct impl *this = object; - struct port *port, *other; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer[4096]; - struct spa_result_node_params result; - uint32_t count = 0; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), port_id); - - spa_log_debug(this->log, "%p: enum params port %d.%d %d %u", - this, direction, port_id, seq, id); - - result.id = id; - result.next = start; - next: - result.index = result.next++; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - case SPA_PARAM_EnumFormat: - if ((res = port_enum_formats(this, direction, port_id, - result.index, ¶m, &b)) <= 0) - return res; - break; - - case SPA_PARAM_Format: - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - param = spa_format_audio_raw_build(&b, id, &port->format.info.raw); - break; - - case SPA_PARAM_Buffers: - { - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - if (other->n_buffers > 0) { - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(port->blocks), - SPA_PARAM_BUFFERS_size, SPA_POD_Int(other->size / other->stride * port->stride), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->stride)); - - - } else { - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(port->blocks), - SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( - this->quantum_limit * 2 * port->stride, - 16 * port->stride, - INT32_MAX), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->stride)); - } - break; - } - case SPA_PARAM_Meta: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamMeta, id, - SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), - SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); - break; - default: - return 0; - } - break; - - case SPA_PARAM_IO: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamIO, id, - SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers), - SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); - break; - default: - return 0; - } - break; - - case SPA_PARAM_Latency: - switch (result.index) { - case 0: case 1: - param = spa_latency_build(&b, id, &this->latency[result.index]); - break; - default: - return 0; - } - break; - - default: - return -ENOENT; - } - - if (spa_pod_filter(&b, &result.param, param, filter) < 0) - goto next; - - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); - - if (++count != num) - goto next; - - return 0; -} - -static int calc_width(struct spa_audio_info *info) -{ - switch (info->info.raw.format) { - case SPA_AUDIO_FORMAT_U8P: - case SPA_AUDIO_FORMAT_U8: - case SPA_AUDIO_FORMAT_S8P: - case SPA_AUDIO_FORMAT_S8: - case SPA_AUDIO_FORMAT_ALAW: - case SPA_AUDIO_FORMAT_ULAW: - return 1; - case SPA_AUDIO_FORMAT_S16P: - case SPA_AUDIO_FORMAT_S16: - case SPA_AUDIO_FORMAT_S16_OE: - case SPA_AUDIO_FORMAT_U16: - return 2; - case SPA_AUDIO_FORMAT_S24P: - case SPA_AUDIO_FORMAT_S24: - case SPA_AUDIO_FORMAT_S24_OE: - case SPA_AUDIO_FORMAT_U24: - return 3; - case SPA_AUDIO_FORMAT_F64P: - case SPA_AUDIO_FORMAT_F64: - case SPA_AUDIO_FORMAT_F64_OE: - return 8; - default: - return 4; - } -} - -static int clear_buffers(struct impl *this, struct port *port) -{ - if (port->n_buffers > 0) { - spa_log_debug(this->log, "%p: clear buffers %p", this, port); - port->n_buffers = 0; - spa_list_init(&port->queue); - } - return 0; -} -static int port_set_format(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t flags, - const struct spa_pod *format) -{ - struct impl *this = object; - struct port *port, *other; - int res; - - port = GET_PORT(this, direction, port_id); - other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), port_id); - - if (format == NULL) { - if (port->have_format) { - port->have_format = false; - clear_buffers(this, port); - if (this->conv.process) - convert_free(&this->conv); - } - } else { - struct spa_audio_info info = { 0 }; - - 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; - - if (other->have_format) { - spa_log_debug(this->log, "%p: channels:%d<>%d rate:%d<>%d format:%d<>%d", this, - info.info.raw.channels, other->format.info.raw.channels, - info.info.raw.rate, other->format.info.raw.rate, - info.info.raw.format, other->format.info.raw.format); - if (!can_convert(&info, &other->format)) - return -ENOTSUP; - } - - port->stride = calc_width(&info); - - if (SPA_AUDIO_FORMAT_IS_PLANAR(info.info.raw.format)) { - port->blocks = info.info.raw.channels; - } else { - port->stride *= info.info.raw.channels; - port->blocks = 1; - } - - port->have_format = true; - port->format = info; - - if (other->have_format && port->have_format) - if ((res = setup_convert(this)) < 0) - return res; - - spa_log_debug(this->log, "%p: set format on port %d:%d res:%d stride:%d", - this, direction, port_id, res, port->stride); - } - if (port->have_format) { - port->params[PORT_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); - port->params[PORT_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); - } else { - port->params[PORT_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[PORT_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - } - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - emit_port_info(this, port, false); - return 0; -} - -static int -impl_node_port_set_param(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - struct port *port; - int res; - - spa_return_val_if_fail(object != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(object, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - - spa_log_debug(this->log, "%p: set param %u on port %d:%d %p", - this, id, direction, port_id, param); - - switch (id) { - case SPA_PARAM_Latency: - { - struct spa_latency_info info; - if (param == NULL) - return 0; - if ((res = spa_latency_parse(param, &info)) < 0) - return res; - if (direction == info.direction) - return -EINVAL; - - this->latency[info.direction] = info; - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - port->params[PORT_Latency].flags ^= SPA_PARAM_INFO_SERIAL; - emit_port_info(this, port, false); - break; - } - case SPA_PARAM_Format: - res = port_set_format(object, direction, port_id, flags, param); - break; - default: - res = -ENOENT; - } - return res; -} - -static int -impl_node_port_use_buffers(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t flags, - struct spa_buffer **buffers, - uint32_t n_buffers) -{ - struct impl *this = object; - struct port *port; - uint32_t i, size = SPA_ID_INVALID, j; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - - spa_return_val_if_fail(port->have_format, -EIO); - - spa_log_debug(this->log, "%p: use buffers %d on port %d", this, n_buffers, port_id); - - clear_buffers(this, port); - - for (i = 0; i < n_buffers; i++) { - struct buffer *b; - uint32_t n_datas = buffers[i]->n_datas; - struct spa_data *d = buffers[i]->datas; - - b = &port->buffers[i]; - b->id = i; - b->flags = 0; - b->outbuf = buffers[i]; - b->h = spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h)); - - if (n_datas != port->blocks) { - spa_log_error(this->log, "%p: expected %d blocks on buffer %d", this, - port->blocks, i); - return -EINVAL; - } - - for (j = 0; j < n_datas; j++) { - if (size == SPA_ID_INVALID) - size = d[j].maxsize; - else if (size != d[j].maxsize) { - spa_log_error(this->log, "%p: expected size %d on buffer %d", - this, size, i); - return -EINVAL; - } - - if (d[j].data == NULL) { - spa_log_error(this->log, "%p: invalid memory %d on buffer %d", - this, j, i); - return -EINVAL; - } - if (!SPA_IS_ALIGNED(d[j].data, this->max_align)) { - spa_log_warn(this->log, "%p: memory %d on buffer %d not aligned", - this, j, i); - } - b->datas[j] = d[j].data; - if (direction == SPA_DIRECTION_OUTPUT && - !SPA_FLAG_IS_SET(d[j].flags, SPA_DATA_FLAG_DYNAMIC)) - this->is_passthrough = false; - } - - if (direction == SPA_DIRECTION_OUTPUT) - spa_list_append(&port->queue, &b->link); - else - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - } - port->n_buffers = n_buffers; - port->size = size; - - spa_log_debug(this->log, "%p: buffer size %d", this, size); - - return 0; -} - -static int -impl_node_port_set_io(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - struct port *port; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - - spa_log_debug(this->log, "%p: port %d:%d update io %d %p", - this, direction, port_id, id, data); - - switch (id) { - case SPA_IO_Buffers: - port->io = data; - break; - default: - return -ENOENT; - } - return 0; -} - -static void recycle_buffer(struct impl *this, struct port *port, uint32_t id) -{ - struct buffer *b = &port->buffers[id]; - - if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) { - spa_list_append(&port->queue, &b->link); - SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); - spa_log_trace_fp(this->log, "%p: recycle buffer %d", this, id); - } -} - -static inline struct buffer *dequeue_buffer(struct impl *this, struct port *port) -{ - struct buffer *b; - - if (spa_list_is_empty(&port->queue)) - return NULL; - b = spa_list_first(&port->queue, struct buffer, link); - spa_list_remove(&b->link); - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - return b; -} - -static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) -{ - struct impl *this = object; - struct port *port; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL); - - port = GET_OUT_PORT(this, port_id); - - recycle_buffer(this, port, buffer_id); - - return 0; -} - -static int impl_node_process(void *object) -{ - struct impl *this = object; - struct port *inport, *outport; - struct spa_io_buffers *inio, *outio; - struct buffer *inbuf, *outbuf; - struct spa_buffer *inb, *outb; - const void **src_datas; - void **dst_datas; - uint32_t i, n_src_datas, n_dst_datas; - uint32_t n_samples, size, maxsize, offs; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - outport = GET_OUT_PORT(this, 0); - inport = GET_IN_PORT(this, 0); - - outio = outport->io; - inio = inport->io; - - spa_log_trace_fp(this->log, "%p: io %p %p", this, inio, outio); - - spa_return_val_if_fail(outio != NULL, -EIO); - spa_return_val_if_fail(inio != NULL, -EIO); - - spa_log_trace_fp(this->log, "%p: status %p %d %d -> %p %d %d", this, - inio, inio->status, inio->buffer_id, - outio, outio->status, outio->buffer_id); - - if (SPA_UNLIKELY(outio->status == SPA_STATUS_HAVE_DATA)) - return inio->status | outio->status; - - if (SPA_LIKELY(outio->buffer_id < outport->n_buffers)) { - recycle_buffer(this, outport, outio->buffer_id); - outio->buffer_id = SPA_ID_INVALID; - } - if (SPA_UNLIKELY(inio->status != SPA_STATUS_HAVE_DATA)) - return outio->status = inio->status; - - if (SPA_UNLIKELY(inio->buffer_id >= inport->n_buffers)) - return inio->status = -EINVAL; - - if (SPA_UNLIKELY((outbuf = dequeue_buffer(this, outport)) == NULL)) - return outio->status = -EPIPE; - - inbuf = &inport->buffers[inio->buffer_id]; - inb = inbuf->outbuf; - - n_src_datas = inb->n_datas; - src_datas = alloca(sizeof(void*) * n_src_datas); - - outb = outbuf->outbuf; - - n_dst_datas = outb->n_datas; - dst_datas = alloca(sizeof(void*) * n_dst_datas); - - size = UINT32_MAX; - for (i = 0; i < n_src_datas; i++) { - uint32_t src_remap = this->src_remap[i]; - struct spa_data *sd = &inb->datas[src_remap]; - offs = SPA_MIN(sd->chunk->offset, sd->maxsize); - size = SPA_MIN(size, SPA_MIN(sd->maxsize - offs, sd->chunk->size)); - src_datas[i] = SPA_PTROFF(sd->data, offs, void); - } - n_samples = size / inport->stride; - - maxsize = outb->datas[0].maxsize; - n_samples = SPA_MIN(n_samples, maxsize / outport->stride); - - spa_log_trace_fp(this->log, "%p: n_src:%d n_dst:%d size:%d maxsize:%d n_samples:%d p:%d", - this, n_src_datas, n_dst_datas, size, maxsize, n_samples, - this->is_passthrough); - - for (i = 0; i < n_dst_datas; i++) { - uint32_t dst_remap = this->dst_remap[i]; - struct spa_data *dd = outb->datas; - - if (this->is_passthrough) - dd[i].data = (void *)src_datas[i]; - else - dst_datas[i] = dd[dst_remap].data = outbuf->datas[dst_remap]; - - dd[i].chunk->offset = 0; - dd[i].chunk->size = n_samples * outport->stride; - } - if (!this->is_passthrough) - convert_process(&this->conv, dst_datas, src_datas, n_samples); - - inio->status = SPA_STATUS_NEED_DATA; - - outio->status = SPA_STATUS_HAVE_DATA; - outio->buffer_id = outbuf->id; - - return SPA_STATUS_NEED_DATA | SPA_STATUS_HAVE_DATA; -} - -static const struct spa_node_methods impl_node = { - SPA_VERSION_NODE_METHODS, - .add_listener = impl_node_add_listener, - .set_callbacks = impl_node_set_callbacks, - .enum_params = impl_node_enum_params, - .set_param = impl_node_set_param, - .set_io = impl_node_set_io, - .send_command = impl_node_send_command, - .add_port = impl_node_add_port, - .remove_port = impl_node_remove_port, - .port_enum_params = impl_node_port_enum_params, - .port_set_param = impl_node_port_set_param, - .port_use_buffers = impl_node_port_use_buffers, - .port_set_io = impl_node_port_set_io, - .port_reuse_buffer = impl_node_port_reuse_buffer, - .process = impl_node_process, -}; - -static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) -{ - struct impl *this; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - spa_return_val_if_fail(interface != NULL, -EINVAL); - - this = (struct impl *) handle; - - if (spa_streq(type, SPA_TYPE_INTERFACE_Node)) - *interface = &this->node; - else - return -ENOENT; - - return 0; -} - -static int impl_clear(struct spa_handle *handle) -{ - return 0; -} - -static int init_port(struct impl *this, enum spa_direction direction, uint32_t port_id) -{ - struct port *port; - - port = GET_PORT(this, direction, port_id); - port->direction = direction; - port->id = port_id; - - spa_list_init(&port->queue); - port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | - SPA_PORT_CHANGE_MASK_PARAMS; - port->info = SPA_PORT_INFO_INIT(); - port->info.flags = SPA_PORT_FLAG_NO_REF | - SPA_PORT_FLAG_DYNAMIC_DATA; - port->params[PORT_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); - port->params[PORT_Meta] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); - port->params[PORT_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - port->params[PORT_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[PORT_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - port->params[PORT_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE); - port->info.params = port->params; - port->info.n_params = N_PORT_PARAMS; - port->have_format = false; - - return 0; -} - -static size_t -impl_get_size(const struct spa_handle_factory *factory, - const struct spa_dict *params) -{ - return sizeof(struct impl); -} - -static int -impl_init(const struct spa_handle_factory *factory, - struct spa_handle *handle, - const struct spa_dict *info, - const struct spa_support *support, - uint32_t n_support) -{ - struct impl *this; - uint32_t i; - - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(handle != NULL, -EINVAL); - - handle->get_interface = impl_get_interface; - handle->clear = impl_clear; - - this = (struct impl *) handle; - - this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - spa_log_topic_init(this->log, log_topic); - - this->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); - if (this->cpu) { - this->cpu_flags = spa_cpu_get_flags(this->cpu); - this->max_align = SPA_MIN(MAX_ALIGN, spa_cpu_get_max_align(this->cpu)); - } - - for (i = 0; info && i < info->n_items; i++) { - const char *k = info->items[i].key; - const char *s = info->items[i].value; - if (spa_streq(k, "clock.quantum-limit")) - spa_atou32(s, &this->quantum_limit, 0); - } - - this->node.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_Node, - SPA_VERSION_NODE, - &impl_node, this); - spa_hook_list_init(&this->hooks); - - this->info_all = SPA_PORT_CHANGE_MASK_FLAGS; - this->info = SPA_NODE_INFO_INIT(); - this->info.flags = SPA_NODE_FLAG_RT; - this->info.params = this->params; - this->info.n_params = N_NODE_PARAMS; - props_reset(&this->props); - - this->latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT); - this->latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT); - - init_port(this, SPA_DIRECTION_OUTPUT, 0); - init_port(this, SPA_DIRECTION_INPUT, 0); - - return 0; -} - -static const struct spa_interface_info impl_interfaces[] = { - {SPA_TYPE_INTERFACE_Node,}, -}; - -static int -impl_enum_interface_info(const struct spa_handle_factory *factory, - const struct spa_interface_info **info, - uint32_t *index) -{ - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(info != NULL, -EINVAL); - spa_return_val_if_fail(index != NULL, -EINVAL); - - switch (*index) { - case 0: - *info = &impl_interfaces[*index]; - break; - default: - return 0; - } - (*index)++; - return 1; -} - -const struct spa_handle_factory spa_fmtconvert_factory = { - SPA_VERSION_HANDLE_FACTORY, - SPA_NAME_AUDIO_PROCESS_FORMAT, - NULL, - impl_get_size, - impl_init, - impl_enum_interface_info, -}; diff --git a/spa/plugins/audioconvert/merger.c b/spa/plugins/audioconvert/merger.c deleted file mode 100644 index 331085586..000000000 --- a/spa/plugins/audioconvert/merger.c +++ /dev/null @@ -1,1550 +0,0 @@ -/* Spa - * - * Copyright © 2018 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. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "volume-ops.h" -#include "fmt-ops.h" - -#undef SPA_LOG_TOPIC_DEFAULT -#define SPA_LOG_TOPIC_DEFAULT log_topic -static struct spa_log_topic *log_topic = &SPA_LOG_TOPIC(0, "spa.merger"); - -#define DEFAULT_RATE 48000 -#define DEFAULT_CHANNELS 2 - -#define MAX_ALIGN FMT_OPS_MAX_ALIGN -#define MAX_BUFFERS 32 -#define MAX_DATAS SPA_AUDIO_MAX_CHANNELS -#define MAX_PORTS SPA_AUDIO_MAX_CHANNELS - -#define DEFAULT_MUTE false -#define DEFAULT_VOLUME VOLUME_NORM - -struct volumes { - bool mute; - uint32_t n_volumes; - float volumes[SPA_AUDIO_MAX_CHANNELS]; -}; - -static void init_volumes(struct volumes *vol) -{ - uint32_t i; - vol->mute = DEFAULT_MUTE; - vol->n_volumes = 0; - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) - vol->volumes[i] = DEFAULT_VOLUME; -} - -struct props { - float volume; - uint32_t n_channels; - uint32_t channel_map[SPA_AUDIO_MAX_CHANNELS]; - struct volumes channel; - struct volumes soft; - struct volumes monitor; -}; - -static void props_reset(struct props *props) -{ - uint32_t i; - props->volume = DEFAULT_VOLUME; - props->n_channels = 0; - for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) - props->channel_map[i] = SPA_AUDIO_CHANNEL_UNKNOWN; - init_volumes(&props->channel); - init_volumes(&props->soft); - init_volumes(&props->monitor); -} - -struct buffer { - uint32_t id; -#define BUFFER_FLAG_QUEUED (1<<0) - uint32_t flags; - struct spa_list link; - struct spa_buffer *buf; - void *datas[MAX_DATAS]; -}; - -struct port { - uint32_t direction; - uint32_t id; - - struct spa_io_buffers *io; - - uint64_t info_all; - struct spa_port_info info; -#define IDX_EnumFormat 0 -#define IDX_Meta 1 -#define IDX_IO 2 -#define IDX_Format 3 -#define IDX_Buffers 4 -#define IDX_Latency 5 -#define N_PORT_PARAMS 6 - struct spa_param_info params[N_PORT_PARAMS]; - char position[16]; - - struct spa_audio_info format; - uint32_t blocks; - uint32_t stride; - - struct buffer buffers[MAX_BUFFERS]; - uint32_t n_buffers; - - struct spa_list queue; - - unsigned int have_format:1; -}; - -struct impl { - struct spa_handle handle; - struct spa_node node; - - struct spa_log *log; - struct spa_cpu *cpu; - - uint32_t cpu_flags; - uint32_t max_align; - uint32_t quantum_limit; - - struct spa_io_position *io_position; - - uint64_t info_all; - struct spa_node_info info; -#define IDX_PortConfig 0 -#define IDX_PropInfo 1 -#define IDX_Props 2 -#define N_NODE_PARAMS 3 - struct spa_param_info params[N_NODE_PARAMS]; - - struct spa_hook_list hooks; - - uint32_t port_count; - uint32_t monitor_count; - struct port *in_ports[MAX_PORTS]; - struct port *out_ports[MAX_PORTS + 1]; - - struct spa_audio_info format; - unsigned int have_profile:1; - - struct convert conv; - unsigned int is_passthrough:1; - unsigned int started:1; - unsigned int monitor:1; - unsigned int monitor_channel_volumes:1; - - struct volume volume; - struct props props; - - uint32_t src_remap[SPA_AUDIO_MAX_CHANNELS]; - uint32_t dst_remap[SPA_AUDIO_MAX_CHANNELS]; - - struct spa_latency_info latency[2]; - - uint32_t empty_size; - float *empty; -}; - -#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) <= this->monitor_count) -#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 PORT_IS_DSP(d,p) (p != 0 || d != SPA_DIRECTION_OUTPUT) - -static void emit_node_info(struct impl *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) { - spa_node_emit_info(&this->hooks, &this->info); - this->info.change_mask = old; - } -} - -static void emit_port_info(struct impl *this, struct port *port, bool full) -{ - uint64_t old = full ? port->info.change_mask : 0; - if (full) - port->info.change_mask = port->info_all; - if (port->info.change_mask) { - struct spa_dict_item items[3]; - uint32_t n_items = 0; - - if (PORT_IS_DSP(port->direction, port->id)) { - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "32 bit float mono audio"); - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_CHANNEL, port->position); - if (port->direction == SPA_DIRECTION_OUTPUT) - items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_PORT_MONITOR, "true"); - } - port->info.props = &SPA_DICT_INIT(items, n_items); - - spa_node_emit_port_info(&this->hooks, port->direction, port->id, &port->info); - port->info.change_mask = old; - } -} - -static int init_port(struct impl *this, enum spa_direction direction, uint32_t port_id, - uint32_t position) -{ - struct port *port = GET_PORT(this, direction, port_id); - const char *name; - - if (port == NULL) { - port = calloc(1, sizeof(struct port)); - if (port == NULL) - return -errno; - if (direction == SPA_DIRECTION_INPUT) - this->in_ports[port_id] = port; - else - this->out_ports[port_id] = port; - } - port->direction = direction; - port->id = port_id; - - name = spa_debug_type_find_short_name(spa_type_audio_channel, position); - snprintf(port->position, sizeof(port->position), "%s", name ? name : "UNK"); - - port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | - SPA_PORT_CHANGE_MASK_PROPS | - SPA_PORT_CHANGE_MASK_PARAMS; - port->info = SPA_PORT_INFO_INIT(); - port->info.flags = SPA_PORT_FLAG_NO_REF | - SPA_PORT_FLAG_DYNAMIC_DATA; - port->params[IDX_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); - port->params[IDX_Meta] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); - port->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - port->params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE); - port->info.params = port->params; - port->info.n_params = N_PORT_PARAMS; - - port->n_buffers = 0; - port->have_format = false; - port->format.media_type = SPA_MEDIA_TYPE_audio; - port->format.media_subtype = SPA_MEDIA_SUBTYPE_dsp; - port->format.info.dsp.format = SPA_AUDIO_FORMAT_DSP_F32; - spa_list_init(&port->queue); - - spa_log_debug(this->log, "%p: add port %d:%d position:%s", - this, direction, port_id, port->position); - emit_port_info(this, port, true); - - return 0; -} - -static int impl_node_enum_params(void *object, int seq, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - struct impl *this = object; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer[4096]; - struct spa_result_node_params result; - uint32_t count = 0; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - - result.id = id; - result.next = start; - next: - result.index = result.next++; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - case SPA_PARAM_PortConfig: - return -ENOTSUP; - - case SPA_PARAM_PropInfo: - { - switch (result.index) { - default: - return 0; - } - break; - } - - case SPA_PARAM_Props: - { - switch (result.index) { - default: - return 0; - } - break; - } - default: - return 0; - } - - if (spa_pod_filter(&b, &result.param, param, filter) < 0) - goto next; - - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); - - if (++count != num) - goto next; - - return 0; -} - -static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_debug(this->log, "%p: io %d %p/%zd", this, id, data, size); - - switch (id) { - case SPA_IO_Position: - this->io_position = data; - break; - default: - return -ENOENT; - } - return 0; -} - -static int merger_set_param(struct impl *this, const char *k, const char *s) -{ - if (spa_streq(k, "monitor.channel-volumes")) - this->monitor_channel_volumes = spa_atob(s); - return 0; -} - -static int parse_prop_params(struct impl *this, struct spa_pod *params) -{ - struct spa_pod_parser prs; - struct spa_pod_frame f; - - spa_pod_parser_pod(&prs, params); - if (spa_pod_parser_push_struct(&prs, &f) < 0) - return 0; - - while (true) { - const char *name; - struct spa_pod *pod; - char value[512]; - - if (spa_pod_parser_get_string(&prs, &name) < 0) - break; - - if (spa_pod_parser_get_pod(&prs, &pod) < 0) - break; - - if (spa_pod_is_bool(pod)) { - snprintf(value, sizeof(value), "%s", - SPA_POD_VALUE(struct spa_pod_bool, pod) ? - "true" : "false"); - } else - continue; - - spa_log_info(this->log, "key:'%s' val:'%s'", name, value); - merger_set_param(this, name, value); - } - return 0; -} - -static int apply_props(struct impl *this, const struct spa_pod *param) -{ - struct spa_pod_prop *prop; - struct spa_pod_object *obj = (struct spa_pod_object *) param; - struct props *p = &this->props; - int changed = 0; - uint32_t n; - - SPA_POD_OBJECT_FOREACH(obj, prop) { - switch (prop->key) { - case SPA_PROP_volume: - if (spa_pod_get_float(&prop->value, &p->volume) == 0) - changed++; - break; - case SPA_PROP_mute: - if (spa_pod_get_bool(&prop->value, &p->channel.mute) == 0) - changed++; - break; - case SPA_PROP_channelVolumes: - if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - p->channel.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { - p->channel.n_volumes = n; - changed++; - } - break; - case SPA_PROP_channelMap: - if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Id, - p->channel_map, SPA_AUDIO_MAX_CHANNELS)) > 0) { - p->n_channels = n; - changed++; - } - break; - case SPA_PROP_softMute: - if (spa_pod_get_bool(&prop->value, &p->soft.mute) == 0) - changed++; - break; - case SPA_PROP_softVolumes: - if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - p->soft.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { - p->soft.n_volumes = n; - changed++; - } - break; - case SPA_PROP_monitorMute: - if (spa_pod_get_bool(&prop->value, &p->monitor.mute) == 0) - changed++; - break; - case SPA_PROP_monitorVolumes: - if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float, - p->monitor.volumes, SPA_AUDIO_MAX_CHANNELS)) > 0) { - p->monitor.n_volumes = n; - changed++; - } - break; - case SPA_PROP_params: - parse_prop_params(this, &prop->value); - break; - default: - break; - } - } - return changed; -} - -static int int32_cmp(const void *v1, const void *v2) -{ - int32_t a1 = *(int32_t*)v1; - int32_t a2 = *(int32_t*)v2; - if (a1 == 0 && a2 != 0) - return 1; - if (a2 == 0 && a1 != 0) - return -1; - return a1 - a2; -} - -static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - if (param == NULL) - return 0; - - switch (id) { - case SPA_PARAM_PortConfig: - { - struct spa_audio_info info = { 0, }; - struct port *port; - struct spa_pod *format; - enum spa_direction direction; - enum spa_param_port_config_mode mode; - bool monitor = false; - uint32_t i; - - if (spa_pod_parse_object(param, - SPA_TYPE_OBJECT_ParamPortConfig, NULL, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(&direction), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(&mode), - SPA_PARAM_PORT_CONFIG_monitor, SPA_POD_OPT_Bool(&monitor), - SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(&format)) < 0) - return -EINVAL; - - if (!spa_pod_is_object_type(format, SPA_TYPE_OBJECT_Format)) - return -EINVAL; - - if (mode != SPA_PARAM_PORT_CONFIG_MODE_dsp) - return -ENOTSUP; - if (direction != SPA_DIRECTION_INPUT) - 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; - - info.info.raw.rate = 0; - - if (this->have_profile && - memcmp(&this->format, &info, sizeof(info)) == 0 && - this->monitor == monitor) - return 0; - - spa_log_debug(this->log, "%p: port config %d/%d %d", this, - info.info.raw.rate, info.info.raw.channels, monitor); - - for (i = 0; i < this->port_count; i++) { - spa_node_emit_port_info(&this->hooks, - SPA_DIRECTION_INPUT, i, NULL); - if (this->monitor) - spa_node_emit_port_info(&this->hooks, - SPA_DIRECTION_OUTPUT, i+1, NULL); - } - - this->monitor = monitor; - this->format = info; - this->have_profile = true; - this->port_count = info.info.raw.channels; - this->monitor_count = this->monitor ? this->port_count : 0; - for (i = 0; i < this->port_count; i++) - this->props.channel_map[i] = info.info.raw.position[i]; - this->props.channel.n_volumes = this->port_count; - this->props.monitor.n_volumes = this->port_count; - this->props.soft.n_volumes = this->port_count; - this->props.n_channels = this->port_count; - - for (i = 0; i < this->port_count; i++) { - init_port(this, SPA_DIRECTION_INPUT, i, info.info.raw.position[i]); - if (this->monitor) - init_port(this, SPA_DIRECTION_OUTPUT, i+1, - info.info.raw.position[i]); - } - - port = GET_OUT_PORT(this, 0); - qsort(info.info.raw.position, info.info.raw.channels, - sizeof(uint32_t), int32_cmp); - port->format = info; - port->have_format = true; - - this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; - this->params[IDX_Props].flags ^= SPA_PARAM_INFO_SERIAL; - emit_node_info(this, false); - return 0; - } - case SPA_PARAM_Props: - if (apply_props(this, param) > 0) { - this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; - this->params[IDX_Props].flags ^= SPA_PARAM_INFO_SERIAL; - emit_node_info(this, false); - } - break; - default: - return -ENOENT; - } - return 0; -} - -static int impl_node_send_command(void *object, const struct spa_command *command) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(command != NULL, -EINVAL); - - switch (SPA_NODE_COMMAND_ID(command)) { - case SPA_NODE_COMMAND_Start: - this->started = true; - break; - case SPA_NODE_COMMAND_Suspend: - case SPA_NODE_COMMAND_Flush: - case SPA_NODE_COMMAND_Pause: - this->started = false; - break; - default: - return -ENOTSUP; - } - return 0; -} - -static int -impl_node_add_listener(void *object, - struct spa_hook *listener, - const struct spa_node_events *events, - void *data) -{ - struct impl *this = object; - uint32_t i; - struct spa_hook_list save; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_trace(this->log, "%p: add listener %p", this, listener); - spa_hook_list_isolate(&this->hooks, &save, listener, events, data); - - emit_node_info(this, true); - emit_port_info(this, GET_OUT_PORT(this, 0), true); - for (i = 0; i < this->port_count; i++) { - emit_port_info(this, GET_IN_PORT(this, i), true); - if (this->monitor) - emit_port_info(this, GET_OUT_PORT(this, i+1), true); - } - - spa_hook_list_join(&this->hooks, &save); - - return 0; -} - -static int -impl_node_set_callbacks(void *object, - const struct spa_node_callbacks *callbacks, - void *user_data) -{ - return 0; -} - -static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id, - const struct spa_dict *props) -{ - return -ENOTSUP; -} - -static int -impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) -{ - return -ENOTSUP; -} - -static int port_enum_formats(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t index, - struct spa_pod **param, - struct spa_pod_builder *builder) -{ - struct impl *this = object; - struct port *port = GET_PORT(this, direction, port_id); - - switch (index) { - case 0: - if (PORT_IS_DSP(direction, port_id)) { - *param = spa_format_audio_dsp_build(builder, - SPA_PARAM_EnumFormat, &port->format.info.dsp); - } else if (port->have_format) { - *param = spa_format_audio_raw_build(builder, - SPA_PARAM_EnumFormat, &port->format.info.raw); - } - else { - uint32_t rate = this->io_position ? - this->io_position->clock.rate.denom : DEFAULT_RATE; - - *param = spa_pod_builder_add_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(25, - SPA_AUDIO_FORMAT_F32P, - SPA_AUDIO_FORMAT_F32P, - SPA_AUDIO_FORMAT_F32, - SPA_AUDIO_FORMAT_F32_OE, - SPA_AUDIO_FORMAT_F64P, - SPA_AUDIO_FORMAT_F64, - SPA_AUDIO_FORMAT_F64_OE, - SPA_AUDIO_FORMAT_S32P, - SPA_AUDIO_FORMAT_S32, - SPA_AUDIO_FORMAT_S32_OE, - SPA_AUDIO_FORMAT_S24_32P, - SPA_AUDIO_FORMAT_S24_32, - SPA_AUDIO_FORMAT_S24_32_OE, - SPA_AUDIO_FORMAT_S24P, - SPA_AUDIO_FORMAT_S24, - SPA_AUDIO_FORMAT_S24_OE, - SPA_AUDIO_FORMAT_S16P, - SPA_AUDIO_FORMAT_S16, - SPA_AUDIO_FORMAT_S16_OE, - SPA_AUDIO_FORMAT_S8P, - SPA_AUDIO_FORMAT_S8, - SPA_AUDIO_FORMAT_U8P, - SPA_AUDIO_FORMAT_U8, - SPA_AUDIO_FORMAT_ULAW, - SPA_AUDIO_FORMAT_ALAW), - SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_RANGE_Int( - rate, 1, INT32_MAX), - SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int( - DEFAULT_CHANNELS, 1, MAX_PORTS)); - } - break; - default: - return 0; - } - return 1; -} - -static int -impl_node_port_enum_params(void *object, int seq, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - struct impl *this = object; - struct port *port; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer[2048]; - struct spa_result_node_params result; - uint32_t count = 0; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - - spa_log_debug(this->log, "%p: enum params port %d.%d %d %u", - this, direction, port_id, seq, id); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - - result.id = id; - result.next = start; - next: - result.index = result.next++; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - case SPA_PARAM_EnumFormat: - if ((res = port_enum_formats(object, direction, port_id, result.index, ¶m, &b)) <= 0) - return res; - break; - case SPA_PARAM_Format: - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - if (PORT_IS_DSP(direction, port_id)) - param = spa_format_audio_dsp_build(&b, id, &port->format.info.dsp); - else - param = spa_format_audio_raw_build(&b, id, &port->format.info.raw); - break; - case SPA_PARAM_Buffers: - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(port->blocks), - SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( - this->quantum_limit * port->stride, - 16 * port->stride, - INT32_MAX), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->stride)); - break; - case SPA_PARAM_Meta: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamMeta, id, - SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), - SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); - break; - default: - return 0; - } - break; - case SPA_PARAM_IO: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamIO, id, - SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers), - SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); - break; - default: - return 0; - } - break; - case SPA_PARAM_Latency: - switch (result.index) { - case 0: case 1: - param = spa_latency_build(&b, id, &this->latency[result.index]); - break; - default: - return 0; - } - break; - default: - return -ENOENT; - } - - if (spa_pod_filter(&b, &result.param, param, filter) < 0) - goto next; - - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); - - if (++count != num) - goto next; - - return 0; -} - -static int clear_buffers(struct impl *this, struct port *port) -{ - if (port->n_buffers > 0) { - spa_log_debug(this->log, "%p: clear buffers %p", this, port); - port->n_buffers = 0; - spa_list_init(&port->queue); - } - return 0; -} - -static int setup_convert(struct impl *this) -{ - struct port *outport; - struct spa_audio_info informat, outformat; - uint32_t i, j, src_fmt, dst_fmt; - int res; - - outport = GET_OUT_PORT(this, 0); - - informat = this->format; - outformat = outport->format; - - src_fmt = SPA_AUDIO_FORMAT_DSP_F32; - dst_fmt = outformat.info.raw.format; - - spa_log_info(this->log, "%p: %s/%d@%dx%d->%s/%d@%d", this, - spa_debug_type_find_name(spa_type_audio_format, src_fmt), - 1, - informat.info.raw.rate, - informat.info.raw.channels, - spa_debug_type_find_name(spa_type_audio_format, dst_fmt), - outformat.info.raw.channels, - outformat.info.raw.rate); - - for (i = 0; i < informat.info.raw.channels; i++) { - for (j = 0; j < outformat.info.raw.channels; j++) { - if (informat.info.raw.position[i] != - outformat.info.raw.position[j]) - continue; - this->src_remap[j] = i; - this->dst_remap[i] = j; - spa_log_debug(this->log, "%p: channel %d -> %d (%s -> %s)", this, - i, j, - spa_debug_type_find_short_name(spa_type_audio_channel, - informat.info.raw.position[i]), - spa_debug_type_find_short_name(spa_type_audio_channel, - outformat.info.raw.position[j])); - outformat.info.raw.position[j] = -1; - break; - } - } - - this->conv.src_fmt = src_fmt; - this->conv.dst_fmt = dst_fmt; - this->conv.n_channels = outformat.info.raw.channels; - this->conv.cpu_flags = this->cpu_flags; - - if ((res = convert_init(&this->conv)) < 0) - return res; - - this->is_passthrough = this->conv.is_passthrough; - - spa_log_debug(this->log, "%p: got converter features %08x:%08x passthrough:%d", this, - this->cpu_flags, this->conv.cpu_flags, this->is_passthrough); - - return 0; -} - -static int calc_width(struct spa_audio_info *info) -{ - switch (info->info.raw.format) { - case SPA_AUDIO_FORMAT_U8: - case SPA_AUDIO_FORMAT_U8P: - case SPA_AUDIO_FORMAT_S8: - case SPA_AUDIO_FORMAT_S8P: - case SPA_AUDIO_FORMAT_ULAW: - case SPA_AUDIO_FORMAT_ALAW: - return 1; - case SPA_AUDIO_FORMAT_S16P: - case SPA_AUDIO_FORMAT_S16: - case SPA_AUDIO_FORMAT_S16_OE: - return 2; - case SPA_AUDIO_FORMAT_S24P: - case SPA_AUDIO_FORMAT_S24: - case SPA_AUDIO_FORMAT_S24_OE: - return 3; - case SPA_AUDIO_FORMAT_F64P: - case SPA_AUDIO_FORMAT_F64: - case SPA_AUDIO_FORMAT_F64_OE: - return 8; - default: - return 4; - } -} - -static int port_set_latency(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t flags, - const struct spa_pod *latency) -{ - struct impl *this = object; - struct port *port; - enum spa_direction other = SPA_DIRECTION_REVERSE(direction); - uint32_t i; - - spa_log_debug(this->log, "%p: set latency direction:%d id:%d", - this, direction, port_id); - - if (direction == SPA_DIRECTION_OUTPUT && port_id != 0) - return 0; - - if (latency == NULL) { - this->latency[other] = SPA_LATENCY_INFO(other); - } else { - struct spa_latency_info info; - if (spa_latency_parse(latency, &info) < 0 || - info.direction != other) - return -EINVAL; - this->latency[other] = info; - } - for (i = 0; i < this->port_count; i++) { - port = GET_IN_PORT(this, i); - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - port->params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL; - emit_port_info(this, port, false); - } - port = GET_OUT_PORT(this, 0); - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - port->params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL; - emit_port_info(this, port, false); - return 0; -} - -static int port_set_format(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t flags, - const struct spa_pod *format) -{ - struct impl *this = object; - struct port *port; - int res; - - port = GET_PORT(this, direction, port_id); - - spa_log_debug(this->log, "%p: set format", this); - - if (format == NULL) { - if (port->have_format) { - if (PORT_IS_DSP(direction, port_id)) - port->have_format = false; - else - port->have_format = this->have_profile; - port->format.info.raw.rate = 0; - clear_buffers(this, port); - } - } else { - struct spa_audio_info info = { 0 }; - - if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) { - spa_log_error(this->log, "can't parse format %s", spa_strerror(res)); - return res; - } - if (PORT_IS_DSP(direction, port_id)) { - if (info.media_type != SPA_MEDIA_TYPE_audio || - info.media_subtype != SPA_MEDIA_SUBTYPE_dsp) { - spa_log_error(this->log, "unexpected types %d/%d", - info.media_type, info.media_subtype); - return -EINVAL; - } - if ((res = spa_format_audio_dsp_parse(format, &info.info.dsp)) < 0) { - spa_log_error(this->log, "can't parse format %s", spa_strerror(res)); - return res; - } - if (info.info.dsp.format != SPA_AUDIO_FORMAT_DSP_F32) { - spa_log_error(this->log, "unexpected format %d<->%d", - info.info.dsp.format, SPA_AUDIO_FORMAT_DSP_F32); - return -EINVAL; - } - port->blocks = 1; - port->stride = 4; - } - else { - if (info.media_type != SPA_MEDIA_TYPE_audio || - info.media_subtype != SPA_MEDIA_SUBTYPE_raw) { - spa_log_error(this->log, "unexpected types %d/%d", - info.media_type, info.media_subtype); - return -EINVAL; - } - if ((res = spa_format_audio_raw_parse(format, &info.info.raw)) < 0) { - spa_log_error(this->log, "can't parse format %s", spa_strerror(res)); - return res; - } - if (info.info.raw.channels != this->port_count) { - spa_log_error(this->log, "unexpected channels %d<->%d", - info.info.raw.channels, this->port_count); - return -EINVAL; - } - port->stride = calc_width(&info); - if (SPA_AUDIO_FORMAT_IS_PLANAR(info.info.raw.format)) { - port->blocks = info.info.raw.channels; - } - else { - port->stride *= info.info.raw.channels; - port->blocks = 1; - } - } - port->format = info; - - spa_log_debug(this->log, "%p: %d %d %d", this, - port_id, port->stride, port->blocks); - - if (!PORT_IS_DSP(direction, port_id)) - if ((res = setup_convert(this)) < 0) - return res; - - port->have_format = true; - } - - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - if (port->have_format) { - port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); - port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); - } else { - port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - } - emit_port_info(this, port, false); - - return 0; -} - - -static int -impl_node_port_set_param(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_debug(this->log, "%p: set param port %d.%d %u", - this, direction, port_id, id); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - switch (id) { - case SPA_PARAM_Latency: - return port_set_latency(this, direction, port_id, flags, param); - case SPA_PARAM_Format: - return port_set_format(this, direction, port_id, flags, param); - default: - return -ENOENT; - } -} - -static void queue_buffer(struct impl *this, struct port *port, uint32_t id) -{ - struct buffer *b = &port->buffers[id]; - - spa_log_trace_fp(this->log, "%p: queue buffer %d on port %d %d", - this, id, port->id, b->flags); - if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_QUEUED)) - return; - - spa_list_append(&port->queue, &b->link); - SPA_FLAG_SET(b->flags, BUFFER_FLAG_QUEUED); -} - -static struct buffer *dequeue_buffer(struct impl *this, struct port *port) -{ - struct buffer *b; - - if (spa_list_is_empty(&port->queue)) - return NULL; - - b = spa_list_first(&port->queue, struct buffer, link); - spa_list_remove(&b->link); - SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_QUEUED); - spa_log_trace_fp(this->log, "%p: dequeue buffer %d on port %d %u", - this, b->id, port->id, b->flags); - - return b; -} - -static int -impl_node_port_use_buffers(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t flags, - struct spa_buffer **buffers, - uint32_t n_buffers) -{ - struct impl *this = object; - struct port *port; - uint32_t i, j, maxsize; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - - spa_return_val_if_fail(port->have_format, -EIO); - - spa_log_debug(this->log, "%p: use buffers %d on port %d:%d", - this, n_buffers, direction, port_id); - - clear_buffers(this, port); - - maxsize = 0; - for (i = 0; i < n_buffers; i++) { - struct buffer *b; - uint32_t n_datas = buffers[i]->n_datas; - struct spa_data *d = buffers[i]->datas; - - b = &port->buffers[i]; - b->id = i; - b->flags = 0; - b->buf = buffers[i]; - - if (n_datas != port->blocks) { - spa_log_error(this->log, "%p: invalid blocks %d on buffer %d", - this, n_datas, i); - return -EINVAL; - } - - for (j = 0; j < n_datas; j++) { - if (d[j].data == NULL) { - spa_log_error(this->log, "%p: invalid memory %d on buffer %d %d %p", - this, j, i, d[j].type, d[j].data); - return -EINVAL; - } - if (!SPA_IS_ALIGNED(d[j].data, this->max_align)) { - spa_log_warn(this->log, "%p: memory %d on buffer %d not aligned", - this, j, i); - } - b->datas[j] = d[j].data; - if (direction == SPA_DIRECTION_OUTPUT && - !SPA_FLAG_IS_SET(d[j].flags, SPA_DATA_FLAG_DYNAMIC)) - this->is_passthrough = false; - - maxsize = SPA_MAX(maxsize, d[j].maxsize); - } - if (direction == SPA_DIRECTION_OUTPUT) - queue_buffer(this, port, i); - } - if (maxsize > this->empty_size) { - this->empty = realloc(this->empty, maxsize + MAX_ALIGN); - if (this->empty == NULL) - return -errno; - memset(this->empty, 0, maxsize + MAX_ALIGN); - this->empty_size = maxsize; - } - port->n_buffers = n_buffers; - - return 0; -} - -static int -impl_node_port_set_io(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - struct port *port; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_debug(this->log, "%p: set io %d on port %d:%d %p", - this, id, direction, port_id, data); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - - switch (id) { - case SPA_IO_Buffers: - port->io = data; - break; - default: - return -ENOENT; - } - return 0; -} - -static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) -{ - struct impl *this = object; - struct port *port; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL); - - port = GET_OUT_PORT(this, port_id); - queue_buffer(this, port, buffer_id); - - return 0; -} - -static inline int get_in_buffer(struct impl *this, struct port *port, struct buffer **buf) -{ - struct spa_io_buffers *io; - - if ((io = port->io) == NULL) { - spa_log_trace_fp(this->log, "%p: no io on port %d", - this, port->id); - return -EIO; - } - if (io->status != SPA_STATUS_HAVE_DATA || - io->buffer_id >= port->n_buffers) { - spa_log_trace_fp(this->log, "%p: empty port %d %p %d %d %d", - this, port->id, io, io->status, io->buffer_id, - port->n_buffers); - return -EPIPE; - } - - *buf = &port->buffers[io->buffer_id]; - io->status = SPA_STATUS_NEED_DATA; - - return 0; -} - -static inline int get_out_buffer(struct impl *this, struct port *port, struct buffer **buf) -{ - struct spa_io_buffers *io; - - if (SPA_UNLIKELY((io = port->io) == NULL || - io->status == SPA_STATUS_HAVE_DATA)) - return SPA_STATUS_HAVE_DATA; - - if (SPA_LIKELY(io->buffer_id < port->n_buffers)) - queue_buffer(this, port, io->buffer_id); - - if (SPA_UNLIKELY((*buf = dequeue_buffer(this, port)) == NULL)) - return -EPIPE; - - io->status = SPA_STATUS_HAVE_DATA; - io->buffer_id = (*buf)->id; - - return 0; -} - -static inline int handle_monitor(struct impl *this, const void *data, float volume, int n_samples, struct port *outport) -{ - struct buffer *dbuf; - struct spa_data *dd; - int res, size; - - if (SPA_UNLIKELY((res = get_out_buffer(this, outport, &dbuf)) != 0)) - return res; - - dd = &dbuf->buf->datas[0]; - size = SPA_MIN(dd->maxsize, n_samples * outport->stride); - dd->chunk->offset = 0; - dd->chunk->size = size; - - spa_log_trace(this->log, "%p: io %p %08x", this, outport->io, dd->flags); - - if (SPA_FLAG_IS_SET(dd->flags, SPA_DATA_FLAG_DYNAMIC) && volume == VOLUME_NORM) - dd->data = (void*)data; - else - volume_process(&this->volume, dd->data, data, volume, size / outport->stride); - - return res; -} - -static int impl_node_process(void *object) -{ - struct impl *this = object; - struct port *outport; - struct spa_io_buffers *outio; - uint32_t i, maxsize, n_samples; - struct spa_data *sd, *dd; - struct buffer *sbuf, *dbuf; - uint32_t n_src_datas, n_dst_datas; - const void **src_datas; - void **dst_datas; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - outport = GET_OUT_PORT(this, 0); - outio = outport->io; - spa_return_val_if_fail(outio != NULL, -EIO); - spa_return_val_if_fail(this->conv.process != NULL, -EIO); - - spa_log_trace_fp(this->log, "%p: status %p %d %d", this, - outio, outio->status, outio->buffer_id); - - if (SPA_UNLIKELY((res = get_out_buffer(this, outport, &dbuf)) != 0)) - return res; - - dd = &dbuf->buf->datas[0]; - - maxsize = dd->maxsize; - - if (SPA_LIKELY(this->io_position)) - n_samples = this->io_position->clock.duration; - else - n_samples = maxsize / outport->stride; - - - n_dst_datas = dbuf->buf->n_datas; - dst_datas = alloca(sizeof(void*) * n_dst_datas); - - n_src_datas = this->port_count; - src_datas = alloca(sizeof(void*) * this->port_count); - - /* produce more output if possible */ - for (i = 0; i < n_src_datas; i++) { - struct port *inport = GET_IN_PORT(this, i); - - if (SPA_UNLIKELY(get_in_buffer(this, inport, &sbuf) < 0)) { - src_datas[i] = SPA_PTR_ALIGN(this->empty, MAX_ALIGN, void); - continue; - } - - sd = &sbuf->buf->datas[0]; - - src_datas[i] = SPA_PTROFF(sd->data, sd->chunk->offset, void); - - n_samples = SPA_MIN(n_samples, sd->chunk->size / inport->stride); - - spa_log_trace_fp(this->log, "%p: %d %d %d %p", this, - sd->chunk->size, maxsize, n_samples, src_datas[i]); - } - - for (i = 0; i < this->monitor_count; i++) { - float volume; - - volume = this->props.monitor.mute ? 0.0f : this->props.monitor.volumes[i]; - if (this->monitor_channel_volumes) - volume *= this->props.channel.mute ? 0.0f : this->props.channel.volumes[i]; - - handle_monitor(this, src_datas[i], volume, n_samples, - GET_OUT_PORT(this, i + 1)); - } - - for (i = 0; i < n_dst_datas; i++) { - uint32_t dst_remap = this->dst_remap[i]; - uint32_t src_remap = this->src_remap[i]; - struct spa_data *dd = dbuf->buf->datas; - - if (this->is_passthrough) - dd[i].data = (void *)src_datas[src_remap]; - else - dst_datas[dst_remap] = dd[i].data = dbuf->datas[i]; - - dd[i].chunk->offset = 0; - dd[i].chunk->size = n_samples * outport->stride; - } - - spa_log_trace_fp(this->log, "%p: n_src:%d n_dst:%d n_samples:%d max:%d p:%d", this, - n_src_datas, n_dst_datas, n_samples, maxsize, this->is_passthrough); - - if (!this->is_passthrough) - convert_process(&this->conv, dst_datas, src_datas, n_samples); - - return SPA_STATUS_NEED_DATA | SPA_STATUS_HAVE_DATA; -} - -static const struct spa_node_methods impl_node = { - SPA_VERSION_NODE_METHODS, - .add_listener = impl_node_add_listener, - .set_callbacks = impl_node_set_callbacks, - .enum_params = impl_node_enum_params, - .set_param = impl_node_set_param, - .set_io = impl_node_set_io, - .send_command = impl_node_send_command, - .add_port = impl_node_add_port, - .remove_port = impl_node_remove_port, - .port_enum_params = impl_node_port_enum_params, - .port_set_param = impl_node_port_set_param, - .port_use_buffers = impl_node_port_use_buffers, - .port_set_io = impl_node_port_set_io, - .port_reuse_buffer = impl_node_port_reuse_buffer, - .process = impl_node_process, -}; - -static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) -{ - struct impl *this; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - spa_return_val_if_fail(interface != NULL, -EINVAL); - - this = (struct impl *) handle; - - if (spa_streq(type, SPA_TYPE_INTERFACE_Node)) - *interface = &this->node; - else - return -ENOENT; - - return 0; -} - -static int impl_clear(struct spa_handle *handle) -{ - struct impl *this; - uint32_t i; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - - this = (struct impl *) handle; - - for (i = 0; i < MAX_PORTS; i++) - free(this->in_ports[i]); - for (i = 0; i < MAX_PORTS+1; i++) - free(this->out_ports[i]); - free(this->empty); - return 0; -} - -static size_t -impl_get_size(const struct spa_handle_factory *factory, - const struct spa_dict *params) -{ - return sizeof(struct impl); -} - -static int -impl_init(const struct spa_handle_factory *factory, - struct spa_handle *handle, - const struct spa_dict *info, - const struct spa_support *support, - uint32_t n_support) -{ - struct impl *this; - uint32_t i; - - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(handle != NULL, -EINVAL); - - handle->get_interface = impl_get_interface; - handle->clear = impl_clear; - - this = (struct impl *) handle; - - this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - spa_log_topic_init(this->log, log_topic); - - this->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); - if (this->cpu) { - this->cpu_flags = spa_cpu_get_flags(this->cpu); - this->max_align = SPA_MIN(MAX_ALIGN, spa_cpu_get_max_align(this->cpu)); - } - - for (i = 0; info && i < info->n_items; i++) { - const char *k = info->items[i].key; - const char *s = info->items[i].value; - if (spa_streq(k, "clock.quantum-limit")) - spa_atou32(s, &this->quantum_limit, 0); - else - merger_set_param(this, k, s); - } - - this->latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT); - this->latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT); - - this->node.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_Node, - SPA_VERSION_NODE, - &impl_node, this); - spa_hook_list_init(&this->hooks); - - this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | - SPA_NODE_CHANGE_MASK_PARAMS; - this->info = SPA_NODE_INFO_INIT(); - this->info.max_input_ports = MAX_PORTS; - this->info.max_output_ports = MAX_PORTS+1; - this->info.flags = SPA_NODE_FLAG_RT | - SPA_NODE_FLAG_IN_PORT_CONFIG; - this->params[IDX_PortConfig] = SPA_PARAM_INFO(SPA_PARAM_PortConfig, SPA_PARAM_INFO_WRITE); - this->params[IDX_PropInfo] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); - this->params[IDX_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); - this->info.params = this->params; - this->info.n_params = N_NODE_PARAMS; - - init_port(this, SPA_DIRECTION_OUTPUT, 0, 0); - - this->volume.cpu_flags = this->cpu_flags; - volume_init(&this->volume); - props_reset(&this->props); - - return 0; -} - -static const struct spa_interface_info impl_interfaces[] = { - {SPA_TYPE_INTERFACE_Node,}, -}; - -static int -impl_enum_interface_info(const struct spa_handle_factory *factory, - const struct spa_interface_info **info, - uint32_t *index) -{ - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(info != NULL, -EINVAL); - spa_return_val_if_fail(index != NULL, -EINVAL); - - switch (*index) { - case 0: - *info = &impl_interfaces[*index]; - break; - default: - return 0; - } - (*index)++; - return 1; -} - -const struct spa_handle_factory spa_merger_factory = { - SPA_VERSION_HANDLE_FACTORY, - SPA_NAME_AUDIO_PROCESS_INTERLEAVE, - NULL, - impl_get_size, - impl_init, - impl_enum_interface_info, -}; diff --git a/spa/plugins/audioconvert/meson.build b/spa/plugins/audioconvert/meson.build index 9cd8f4b1b..3f2102f1d 100644 --- a/spa/plugins/audioconvert/meson.build +++ b/spa/plugins/audioconvert/meson.build @@ -1,12 +1,7 @@ audioconvert_sources = ['audioadapter.c', - 'audioconvert.c', 'audioconvert2.c', - 'fmtconvert.c', - 'channelmix.c', - 'merger.c', - 'plugin.c', - 'resample.c', - 'splitter.c'] + 'plugin.c' +] simd_cargs = [] simd_dependencies = [] diff --git a/spa/plugins/audioconvert/plugin.c b/spa/plugins/audioconvert/plugin.c index 6849ef4a3..7cb3cd5e8 100644 --- a/spa/plugins/audioconvert/plugin.c +++ b/spa/plugins/audioconvert/plugin.c @@ -26,13 +26,7 @@ #include -extern const struct spa_handle_factory spa_audioconvert_factory; extern const struct spa_handle_factory spa_audioconvert2_factory; -extern const struct spa_handle_factory spa_fmtconvert_factory; -extern const struct spa_handle_factory spa_channelmix_factory; -extern const struct spa_handle_factory spa_resample_factory; -extern const struct spa_handle_factory spa_splitter_factory; -extern const struct spa_handle_factory spa_merger_factory; extern const struct spa_handle_factory spa_audioadapter_factory; SPA_EXPORT @@ -46,26 +40,8 @@ int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *factory = &spa_audioconvert2_factory; break; case 1: - *factory = &spa_fmtconvert_factory; - break; - case 2: - *factory = &spa_channelmix_factory; - break; - case 3: - *factory = &spa_resample_factory; - break; - case 4: - *factory = &spa_splitter_factory; - break; - case 5: - *factory = &spa_merger_factory; - break; - case 6: *factory = &spa_audioadapter_factory; break; - case 7: - *factory = &spa_audioconvert_factory; - break; default: return 0; } diff --git a/spa/plugins/audioconvert/resample.c b/spa/plugins/audioconvert/resample.c deleted file mode 100644 index 91dabb6cc..000000000 --- a/spa/plugins/audioconvert/resample.c +++ /dev/null @@ -1,1307 +0,0 @@ -/* Spa - * - * Copyright © 2018 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. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "resample.h" - -#undef SPA_LOG_TOPIC_DEFAULT -#define SPA_LOG_TOPIC_DEFAULT log_topic -static struct spa_log_topic *log_topic = &SPA_LOG_TOPIC(0, "spa.resample"); - -#define DEFAULT_RATE 48000 -#define DEFAULT_CHANNELS 2 - -#define MAX_BUFFERS 32 - -struct impl; - -struct props { - double rate; - int quality; - bool disabled; -}; - -static void props_reset(struct props *props) -{ - props->rate = 1.0; - props->quality = RESAMPLE_DEFAULT_QUALITY; - props->disabled = false; -} - -struct buffer { - uint32_t id; -#define BUFFER_FLAG_OUT (1 << 0) - uint32_t flags; - struct spa_list link; - struct spa_buffer *outbuf; - struct spa_meta_header *h; -}; - -struct port { - uint32_t direction; - uint32_t id; - - uint64_t info_all; - struct spa_port_info info; - struct spa_param_info params[8]; - - struct spa_io_buffers *io; - - struct spa_audio_info format; - uint32_t stride; - uint32_t blocks; - uint32_t size; - unsigned int have_format:1; - - struct buffer buffers[MAX_BUFFERS]; - uint32_t n_buffers; - - uint32_t offset; - struct spa_list queue; -}; - -struct impl { - struct spa_handle handle; - struct spa_node node; - - struct spa_log *log; - struct spa_cpu *cpu; - - uint32_t quantum_limit; - - struct spa_io_position *io_position; - struct spa_io_rate_match *io_rate_match; - - uint64_t info_all; - struct spa_node_info info; - struct props props; - - struct spa_hook_list hooks; - - struct port in_port; - struct port out_port; - -#define MODE_SPLIT 0 -#define MODE_MERGE 1 -#define MODE_CONVERT 2 - int mode; - unsigned int started:1; - unsigned int peaks:1; - unsigned int drained:1; - - struct resample resample; - - double rate_scale; -}; - -#define CHECK_PORT(this,d,id) (id == 0) -#define GET_IN_PORT(this,id) (&this->in_port) -#define GET_OUT_PORT(this,id) (&this->out_port) -#define GET_PORT(this,d,id) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,id) : GET_OUT_PORT(this,id)) - -static int setup_convert(struct impl *this, - enum spa_direction direction, - const struct spa_audio_info *info) -{ - const struct spa_audio_info *src_info, *dst_info; - int err; - - if (direction == SPA_DIRECTION_INPUT) { - src_info = info; - dst_info = &GET_OUT_PORT(this, 0)->format; - } else { - src_info = &GET_IN_PORT(this, 0)->format; - dst_info = info; - } - - spa_log_info(this->log, "%p: %s/%d@%d->%s/%d@%d", this, - spa_debug_type_find_name(spa_type_audio_format, src_info->info.raw.format), - src_info->info.raw.channels, - src_info->info.raw.rate, - spa_debug_type_find_name(spa_type_audio_format, dst_info->info.raw.format), - dst_info->info.raw.channels, - dst_info->info.raw.rate); - - if (src_info->info.raw.channels != dst_info->info.raw.channels) - return -EINVAL; - - if (this->resample.free) - resample_free(&this->resample); - - this->resample.channels = src_info->info.raw.channels; - this->resample.i_rate = src_info->info.raw.rate; - this->resample.o_rate = dst_info->info.raw.rate; - this->resample.log = this->log; - this->resample.quality = this->props.quality; - - if (this->peaks) - err = resample_peaks_init(&this->resample); - else - err = resample_native_init(&this->resample); - - return err; -} - -static int impl_node_enum_params(void *object, int seq, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - struct impl *this = object; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer[4096]; - struct spa_result_node_params result; - uint32_t count = 0; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - - result.id = id; - result.next = start; - next: - result.index = result.next++; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - case SPA_PARAM_PropInfo: - { - struct props *p = &this->props; - - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_rate), - SPA_PROP_INFO_description, SPA_POD_String("Rate scaler"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Double(p->rate, 0.0, 10.0)); - break; - case 1: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_quality), - SPA_PROP_INFO_name, SPA_POD_String("resample.quality"), - SPA_PROP_INFO_description, SPA_POD_String("Resample Quality"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->quality, 0, 14), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - case 2: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_name, SPA_POD_String("resample.disable"), - SPA_PROP_INFO_description, SPA_POD_String("Disable Resampling"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->disabled), - SPA_PROP_INFO_params, SPA_POD_Bool(true)); - break; - default: - return 0; - } - break; - } - case SPA_PARAM_Props: - { - struct props *p = &this->props; - struct spa_pod_frame f[2]; - - switch (result.index) { - case 0: - spa_pod_builder_push_object(&b, &f[0], SPA_TYPE_OBJECT_Props, id); - spa_pod_builder_add(&b, - SPA_PROP_rate, SPA_POD_Double(p->rate), - SPA_PROP_quality, SPA_POD_Int(p->quality), - 0); - spa_pod_builder_prop(&b, SPA_PROP_params, 0); - spa_pod_builder_push_struct(&b, &f[1]); - spa_pod_builder_string(&b, "resample.quality"); - spa_pod_builder_int(&b, p->quality); - spa_pod_builder_string(&b, "resample.disable"); - spa_pod_builder_bool(&b, p->disabled); - spa_pod_builder_pop(&b, &f[1]); - param = spa_pod_builder_pop(&b, &f[0]); - break; - default: - return 0; - } - break; - } - default: - return -ENOENT; - } - - if (spa_pod_filter(&b, &result.param, param, filter) < 0) - goto next; - - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); - - if (++count != num) - goto next; - - return 0; -} - -static int resample_set_param(struct impl *this, const char *k, const char *s) -{ - if (spa_streq(k, "resample.quality")) - this->props.quality = atoi(s); - else if (spa_streq(k, "resample.disable")) - this->props.disabled = spa_atob(s); - return 0; -} - -static int parse_prop_params(struct impl *this, struct spa_pod *params) -{ - struct spa_pod_parser prs; - struct spa_pod_frame f; - - spa_pod_parser_pod(&prs, params); - if (spa_pod_parser_push_struct(&prs, &f) < 0) - return 0; - - while (true) { - const char *name; - struct spa_pod *pod; - char value[512]; - - if (spa_pod_parser_get_string(&prs, &name) < 0) - break; - - if (spa_pod_parser_get_pod(&prs, &pod) < 0) - break; - - if (spa_pod_is_string(pod)) { - spa_pod_copy_string(pod, sizeof(value), value); - } else if (spa_pod_is_int(pod)) { - snprintf(value, sizeof(value), "%d", - SPA_POD_VALUE(struct spa_pod_int, pod)); - } else if (spa_pod_is_bool(pod)) { - snprintf(value, sizeof(value), "%s", - SPA_POD_VALUE(struct spa_pod_bool, pod) ? - "true" : "false"); - } else - continue; - - spa_log_info(this->log, "key:'%s' val:'%s'", name, value); - resample_set_param(this, name, value); - } - return 0; -} - -static int apply_props(struct impl *this, const struct spa_pod *param) -{ - struct spa_pod_prop *prop; - struct spa_pod_object *obj = (struct spa_pod_object *) param; - struct props *p = &this->props; - int changed = 0; - - SPA_POD_OBJECT_FOREACH(obj, prop) { - switch (prop->key) { - case SPA_PROP_rate: - if (spa_pod_get_double(&prop->value, &p->rate) == 0) { - resample_update_rate(&this->resample, p->rate); - changed++; - } - break; - case SPA_PROP_quality: - if (spa_pod_get_int(&prop->value, &p->quality) == 0) - changed++; - break; - case SPA_PROP_params: - changed += parse_prop_params(this, &prop->value); - break; - default: - break; - } - } - return changed; -} - -static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - int res = 0; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - switch (id) { - case SPA_PARAM_Props: - apply_props(this, param); - break; - default: - return -ENOTSUP; - } - - return res; -} - -static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_debug(this->log, "%p: io %d %p/%zd", this, id, data, size); - - switch (id) { - case SPA_IO_Position: - this->io_position = data; - break; - default: - return -ENOENT; - } - return 0; -} - -static void update_rate_match(struct impl *this, bool passthrough, uint32_t out_size, uint32_t in_queued) -{ - double r = this->rate_scale / this->props.rate; - - if (this->io_rate_match) { - uint32_t delay, match_size; - - if (passthrough) { - delay = in_queued; - match_size = out_size; - } else { - if (SPA_FLAG_IS_SET(this->io_rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE)) - resample_update_rate(&this->resample, r * this->io_rate_match->rate); - else - resample_update_rate(&this->resample, r); - - delay = resample_delay(&this->resample) + in_queued; - match_size = resample_in_len(&this->resample, out_size); - } - match_size -= SPA_MIN(match_size, in_queued); - this->io_rate_match->size = match_size; - this->io_rate_match->delay = delay; - spa_log_trace_fp(this->log, "%p: next match:%u queued:%u delay:%u", this, match_size, - in_queued, delay); - } else { - resample_update_rate(&this->resample, r); - } -} -static inline bool is_passthrough(struct impl *this) -{ - return this->resample.i_rate == this->resample.o_rate && - this->rate_scale == 1.0 && this->props.rate == 1.0 && - (this->io_rate_match == NULL || this->props.disabled || - !SPA_FLAG_IS_SET(this->io_rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE)); -} - -static void recalc_rate_match(struct impl *this) -{ - bool passthrough = is_passthrough(this); - uint32_t out_size = this->io_position ? this->io_position->clock.duration : 1024; - update_rate_match(this, passthrough, out_size, 0); -} - -static void reset_node(struct impl *this) -{ - struct port *outport, *inport; - outport = GET_OUT_PORT(this, 0); - inport = GET_IN_PORT(this, 0); - - if (this->resample.reset) - resample_reset(&this->resample); - outport->offset = 0; - inport->offset = 0; -} - -static int impl_node_send_command(void *object, const struct spa_command *command) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(command != NULL, -EINVAL); - - switch (SPA_NODE_COMMAND_ID(command)) { - case SPA_NODE_COMMAND_Start: - recalc_rate_match(this); - this->started = true; - break; - case SPA_NODE_COMMAND_Suspend: - case SPA_NODE_COMMAND_Flush: - reset_node(this); - SPA_FALLTHROUGH; - case SPA_NODE_COMMAND_Pause: - this->started = false; - break; - default: - return -ENOTSUP; - } - return 0; -} - -static void emit_node_info(struct impl *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) { - spa_node_emit_info(&this->hooks, &this->info); - this->info.change_mask = old; - } -} - -static void emit_port_info(struct impl *this, struct port *port, bool full) -{ - uint64_t old = full ? port->info.change_mask : 0; - if (full) - port->info.change_mask = port->info_all; - if (port->info.change_mask) { - spa_node_emit_port_info(&this->hooks, - port->direction, port->id, &port->info); - port->info.change_mask = old; - } -} - -static int -impl_node_add_listener(void *object, - struct spa_hook *listener, - const struct spa_node_events *events, - void *data) -{ - struct impl *this = object; - struct spa_hook_list save; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_hook_list_isolate(&this->hooks, &save, listener, events, data); - - emit_node_info(this, true); - emit_port_info(this, GET_IN_PORT(this, 0), true); - emit_port_info(this, GET_OUT_PORT(this, 0), true); - - spa_hook_list_join(&this->hooks, &save); - - return 0; -} - -static int -impl_node_set_callbacks(void *object, - const struct spa_node_callbacks *callbacks, - void *user_data) -{ - return 0; -} - -static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id, - const struct spa_dict *props) -{ - return -ENOTSUP; -} - -static int -impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) -{ - return -ENOTSUP; -} - -static int port_enum_formats(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t index, - struct spa_pod **param, - struct spa_pod_builder *builder) -{ - struct impl *this = object; - struct port *other; - struct spa_pod_frame f; - uint32_t rate, min = 1, max = INT32_MAX; - - other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), 0); - - switch (index) { - case 0: - if (other->have_format) { - rate = other->format.info.raw.rate; - if (this->props.disabled) - min = max = rate; - - spa_pod_builder_push_object(builder, &f, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); - spa_pod_builder_add(builder, - 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, SPA_POD_CHOICE_RANGE_Int(rate, min, max), - SPA_FORMAT_AUDIO_channels, SPA_POD_Int(other->format.info.raw.channels), - 0); - spa_pod_builder_prop(builder, SPA_FORMAT_AUDIO_position, 0); - spa_pod_builder_array(builder, sizeof(uint32_t), SPA_TYPE_Id, - other->format.info.raw.channels, other->format.info.raw.position); - *param = spa_pod_builder_pop(builder, &f); - } else { - rate = this->io_position ? - this->io_position->clock.rate.denom : DEFAULT_RATE; - if (this->props.disabled) - min = max = rate; - - *param = spa_pod_builder_add_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, SPA_POD_CHOICE_RANGE_Int(rate, min, max), - SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int(DEFAULT_CHANNELS, 1, INT32_MAX)); - } - break; - default: - return 0; - } - return 1; -} - -static int -impl_node_port_enum_params(void *object, int seq, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - struct impl *this = object; - struct port *port, *other; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer[1024]; - struct spa_result_node_params result; - uint32_t count = 0; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), port_id); - - spa_log_debug(this->log, "%p: enum params port %d.%d %d %u", - this, direction, port_id, seq, id); - - result.id = id; - result.next = start; - next: - result.index = result.next++; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - case SPA_PARAM_EnumFormat: - if ((res = port_enum_formats(this, direction, port_id, - result.index, ¶m, &b)) <= 0) - return res; - break; - case SPA_PARAM_Format: - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - param = spa_format_audio_raw_build(&b, id, &port->format.info.raw); - break; - case SPA_PARAM_Buffers: - { - uint32_t buffers, size; - uint32_t rate; - - if (!port->have_format || !other->have_format) - return -EIO; - if (result.index > 0) - return 0; - - if (direction == SPA_DIRECTION_OUTPUT) { - rate = (this->resample.o_rate + this->resample.i_rate - 1) / this->resample.i_rate; - } else { - rate = (this->resample.i_rate + this->resample.o_rate - 1) / this->resample.o_rate; - } - if (other->n_buffers > 0) { - buffers = other->n_buffers; - size = (other->size / other->stride) * rate; - } else { - buffers = 1; - size = this->quantum_limit * rate; - } - size = SPA_MAX(size, this->quantum_limit) * 2; - - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(buffers, 1, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(port->blocks), - SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( - size * port->stride, - 16 * port->stride, - INT32_MAX), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->stride)); - break; - } - case SPA_PARAM_Meta: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamMeta, id, - SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), - SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); - break; - default: - return 0; - } - break; - case SPA_PARAM_IO: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamIO, id, - SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers), - SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); - break; - default: - return 0; - } - break; - default: - return -ENOENT; - } - - if (spa_pod_filter(&b, &result.param, param, filter) < 0) - goto next; - - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); - - if (++count != num) - goto next; - - return 0; -} - -static int clear_buffers(struct impl *this, struct port *port) -{ - if (port->n_buffers > 0) { - spa_log_debug(this->log, "%p: clear buffers %p", this, port); - port->n_buffers = 0; - spa_list_init(&port->queue); - } - return 0; -} - -static int port_set_format(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t flags, - const struct spa_pod *format) -{ - struct impl *this = object; - struct port *port, *other; - int res = 0; - - port = GET_PORT(this, direction, port_id); - other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), port_id); - - if (format == NULL) { - if (port->have_format) { - port->have_format = false; - clear_buffers(this, port); - } - } else { - struct spa_audio_info info = { 0 }; - - 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; - - if (info.info.raw.format != SPA_AUDIO_FORMAT_F32P) - return -EINVAL; - - port->stride = sizeof(float); - port->blocks = info.info.raw.channels; - - if (other->have_format) { - if ((res = setup_convert(this, direction, &info)) < 0) - return res; - } - port->format = info; - port->have_format = true; - - spa_log_debug(this->log, "%p: set format on port %d %d", this, port_id, res); - } - - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - if (port->have_format) { - port->params[3] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[4] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - } else { - port->params[3] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); - port->params[4] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); - } - emit_port_info(this, port, false); - - return res; -} - -static int -impl_node_port_set_param(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - spa_return_val_if_fail(object != NULL, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(object, direction, port_id), -EINVAL); - - if (id == SPA_PARAM_Format) { - return port_set_format(object, direction, port_id, flags, param); - } - else - return -ENOENT; -} - -static int -impl_node_port_use_buffers(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t flags, - struct spa_buffer **buffers, - uint32_t n_buffers) -{ - struct impl *this = object; - struct port *port; - uint32_t i, j, size = SPA_ID_INVALID; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - - spa_return_val_if_fail(port->have_format, -EIO); - - spa_log_debug(this->log, "%p: use buffers %d on port %d:%d", this, - n_buffers, direction, port_id); - - clear_buffers(this, port); - - for (i = 0; i < n_buffers; i++) { - struct buffer *b; - struct spa_data *d = buffers[i]->datas; - - b = &port->buffers[i]; - b->id = i; - b->flags = 0; - b->outbuf = buffers[i]; - b->h = spa_buffer_find_meta_data(buffers[i], SPA_META_Header, sizeof(*b->h)); - - for (j = 0; j < buffers[i]->n_datas; j++) { - if (size == SPA_ID_INVALID) - size = d[j].maxsize; - else - if (size != d[j].maxsize) { - spa_log_error(this->log, "%p: invalid size %d on buffer %p", this, - size, buffers[i]); - return -EINVAL; - } - - if (d[j].data == NULL) { - spa_log_error(this->log, "%p: invalid memory on buffer %p", this, - buffers[i]); - return -EINVAL; - } - } - - if (direction == SPA_DIRECTION_OUTPUT) - spa_list_append(&port->queue, &b->link); - else - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - - port->offset = 0; - } - port->n_buffers = n_buffers; - port->size = size; - - return 0; -} - -static int -impl_node_port_set_io(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - struct port *port; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - spa_log_trace_fp(this->log, "%p: %d:%d io %d", this, direction, port_id, id); - - port = GET_PORT(this, direction, port_id); - - switch (id) { - case SPA_IO_Buffers: - port->io = data; - break; - case SPA_IO_RateMatch: - this->io_rate_match = data; - break; - default: - return -ENOENT; - } - return 0; -} - -static void recycle_buffer(struct impl *this, uint32_t id) -{ - struct port *port = GET_OUT_PORT(this, 0); - struct buffer *b = &port->buffers[id]; - - if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) { - spa_list_append(&port->queue, &b->link); - SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); - spa_log_trace_fp(this->log, "%p: recycle buffer %d", this, id); - } -} - -static struct buffer *peek_buffer(struct impl *this, struct port *port) -{ - struct buffer *b; - - if (spa_list_is_empty(&port->queue)) - return NULL; - - b = spa_list_first(&port->queue, struct buffer, link); - return b; -} - -static void dequeue_buffer(struct impl *this, struct buffer *b) -{ - spa_list_remove(&b->link); - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); -} - -static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL); - - recycle_buffer(this, buffer_id); - - return 0; -} - -static int impl_node_process(void *object) -{ - struct impl *this = object; - struct port *outport, *inport; - struct spa_io_buffers *outio, *inio; - struct buffer *sbuf, *dbuf; - struct spa_buffer *sb, *db; - uint32_t i, size, in_len, out_len, maxsize, max; -#ifndef FASTPATH - uint32_t pin_len, pout_len; -#endif - int res = 0; - const void **src_datas; - void **dst_datas; - bool flush_out = false; - bool flush_in = false; - bool draining = false; - bool passthrough; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - outport = GET_OUT_PORT(this, 0); - inport = GET_IN_PORT(this, 0); - - outio = outport->io; - inio = inport->io; - - spa_return_val_if_fail(outio != NULL, -EIO); - spa_return_val_if_fail(inio != NULL, -EIO); - - spa_log_trace_fp(this->log, "%p: status %p %d %d -> %p %d %d", this, - inio, inio->status, inio->buffer_id, - outio, outio->status, outio->buffer_id); - - if (SPA_UNLIKELY(outio->status == SPA_STATUS_HAVE_DATA)) - return SPA_STATUS_HAVE_DATA; - /* recycle */ - if (SPA_LIKELY(outio->buffer_id < outport->n_buffers)) { - recycle_buffer(this, outio->buffer_id); - outio->buffer_id = SPA_ID_INVALID; - } - if (SPA_UNLIKELY(inio->status != SPA_STATUS_HAVE_DATA)) { - if (inio->status != SPA_STATUS_DRAINED || this->drained) { - recalc_rate_match(this); - return outio->status = inio->status; - } - inio->buffer_id = 0; - inport->buffers[0].outbuf->datas[0].chunk->size = -1; - } - - if (SPA_UNLIKELY(inio->buffer_id >= inport->n_buffers)) - return inio->status = -EINVAL; - - if (SPA_UNLIKELY((dbuf = peek_buffer(this, outport)) == NULL)) - return outio->status = -EPIPE; - - sbuf = &inport->buffers[inio->buffer_id]; - - sb = sbuf->outbuf; - db = dbuf->outbuf; - - maxsize = db->datas[0].maxsize; - - if (SPA_LIKELY(this->io_position)) { - double r = this->rate_scale; - - max = this->io_position->clock.duration * sizeof(float); - if (this->mode == MODE_SPLIT) { - if (this->io_position->clock.rate.denom != this->resample.o_rate) - r = (double) this->io_position->clock.rate.denom / this->resample.o_rate; - else - r = 1.0; - } else { - if (this->io_position->clock.rate.denom != this->resample.i_rate) - r = (double) this->resample.i_rate / this->io_position->clock.rate.denom; - else - r = 1.0; - } - if (this->rate_scale != r) { - spa_log_info(this->log, "scale %f->%f", this->rate_scale, r); - this->rate_scale = r; - } - } - else - max = maxsize; - - switch (this->mode) { - case MODE_SPLIT: - /* in split mode we need to output exactly the size of the - * duration so we don't try to flush early */ - maxsize = SPA_MIN(maxsize, max); - flush_out = false; - break; - case MODE_MERGE: - default: - /* in merge mode we consume one duration of samples and - * always output the resulting data */ - flush_out = true; - break; - } - src_datas = alloca(sizeof(void*) * this->resample.channels); - dst_datas = alloca(sizeof(void*) * this->resample.channels); - - if (outport->offset > maxsize) - outport->offset = maxsize; - - size = sb->datas[0].chunk->size; - if (size == (uint32_t)-1) { - size = sb->datas[0].maxsize; - memset(sb->datas[0].data, 0, size); - for (i = 0; i < sb->n_datas; i++) - src_datas[i] = sb->datas[0].data; - inport->offset = 0; - flush_in = draining = true; - } else { - size = SPA_MIN(size, sb->datas[0].maxsize); - if (inport->offset > size) - inport->offset = size; - for (i = 0; i < sb->n_datas; i++) - src_datas[i] = SPA_PTROFF(sb->datas[i].data, inport->offset, void); - } - for (i = 0; i < db->n_datas; i++) - dst_datas[i] = SPA_PTROFF(db->datas[i].data, outport->offset, void); - - in_len = (size - inport->offset) / sizeof(float); - out_len = (maxsize - outport->offset) / sizeof(float); - -#ifndef FASTPATH - pin_len = in_len; - pout_len = out_len; -#endif - passthrough = is_passthrough(this); - - if (passthrough) { - uint32_t len = SPA_MIN(in_len, out_len); - for (i = 0; i < sb->n_datas; i++) - spa_memcpy(dst_datas[i], src_datas[i], len * sizeof(float)); - out_len = in_len = len; - } else { - resample_process(&this->resample, src_datas, &in_len, dst_datas, &out_len); - } - -#ifndef FASTPATH - spa_log_trace_fp(this->log, "%p: in %d/%d %zd %d out %d/%d %zd %d max:%d", - this, pin_len, in_len, size / sizeof(float), inport->offset, - pout_len, out_len, maxsize / sizeof(float), outport->offset, - max); -#endif - - for (i = 0; i < db->n_datas; i++) { - db->datas[i].chunk->size = outport->offset + (out_len * sizeof(float)); - db->datas[i].chunk->offset = 0; - } - - inport->offset += in_len * sizeof(float); - if (inport->offset >= size || flush_in) { - inio->status = SPA_STATUS_NEED_DATA; - spa_log_trace_fp(this->log, "%p: return input buffer of %zd samples", - this, size / sizeof(float)); - inport->offset = 0; - size = 0; - SPA_FLAG_SET(res, inio->status); - } - - outport->offset += out_len * sizeof(float); - if (outport->offset > 0 && (outport->offset >= maxsize || flush_out)) { - outio->status = SPA_STATUS_HAVE_DATA; - outio->buffer_id = dbuf->id; - spa_log_trace_fp(this->log, "%p: have output buffer of %zd samples", - this, outport->offset / sizeof(float)); - dequeue_buffer(this, dbuf); - outport->offset = 0; - this->drained = draining; - SPA_FLAG_SET(res, SPA_STATUS_HAVE_DATA); - } - if (out_len == 0 && this->peaks) { - outio->status = SPA_STATUS_HAVE_DATA; - outio->buffer_id = SPA_ID_INVALID; - SPA_FLAG_SET(res, SPA_STATUS_HAVE_DATA); - spa_log_trace_fp(this->log, "%p: no output buffer", this); - } - - update_rate_match(this, passthrough, (max - outport->offset) / sizeof(float), - (size - inport->offset) / sizeof(float)); - return res; -} - -static const struct spa_node_methods impl_node = { - SPA_VERSION_NODE_METHODS, - .add_listener = impl_node_add_listener, - .set_callbacks = impl_node_set_callbacks, - .enum_params = impl_node_enum_params, - .set_param = impl_node_set_param, - .set_io = impl_node_set_io, - .send_command = impl_node_send_command, - .add_port = impl_node_add_port, - .remove_port = impl_node_remove_port, - .port_enum_params = impl_node_port_enum_params, - .port_set_param = impl_node_port_set_param, - .port_use_buffers = impl_node_port_use_buffers, - .port_set_io = impl_node_port_set_io, - .port_reuse_buffer = impl_node_port_reuse_buffer, - .process = impl_node_process, -}; - -static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) -{ - struct impl *this; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - spa_return_val_if_fail(interface != NULL, -EINVAL); - - this = (struct impl *) handle; - - if (spa_streq(type, SPA_TYPE_INTERFACE_Node)) - *interface = &this->node; - else - return -ENOENT; - - return 0; -} - -static int impl_clear(struct spa_handle *handle) -{ - struct impl *this; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - - this = (struct impl *) handle; - - if (this->resample.free) - resample_free(&this->resample); - return 0; -} - -static size_t -impl_get_size(const struct spa_handle_factory *factory, - const struct spa_dict *params) -{ - return sizeof(struct impl); -} - -static int -impl_init(const struct spa_handle_factory *factory, - struct spa_handle *handle, - const struct spa_dict *info, - const struct spa_support *support, - uint32_t n_support) -{ - struct impl *this; - struct port *port; - uint32_t i; - - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(handle != NULL, -EINVAL); - - handle->get_interface = impl_get_interface; - handle->clear = impl_clear; - - this = (struct impl *) handle; - - this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - spa_log_topic_init(this->log, log_topic); - - this->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); - if (this->cpu) - this->resample.cpu_flags = spa_cpu_get_flags(this->cpu); - - props_reset(&this->props); - - for (i = 0; info && i < info->n_items; i++) { - const char *k = info->items[i].key; - const char *s = info->items[i].value; - if (spa_streq(k, "clock.quantum-limit")) - spa_atou32(s, &this->quantum_limit, 0); - else if (spa_streq(k, "resample.peaks")) - this->peaks = spa_atob(s); - else if (spa_streq(k, "factory.mode")) { - if (spa_streq(s, "split")) - this->mode = MODE_SPLIT; - else if (spa_streq(s, "merge")) - this->mode = MODE_MERGE; - else - this->mode = MODE_CONVERT; - } else - resample_set_param(this, k, s); - - } - - spa_log_debug(this->log, "mode:%d", this->mode); - - this->node.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_Node, - SPA_VERSION_NODE, - &impl_node, this); - - spa_hook_list_init(&this->hooks); - - this->rate_scale = 1.0; - - this->info = SPA_NODE_INFO_INIT(); - this->info_all = SPA_NODE_CHANGE_MASK_FLAGS; - this->info.max_input_ports = 1; - this->info.max_output_ports = 1; - this->info.flags = SPA_NODE_FLAG_RT; - - port = GET_OUT_PORT(this, 0); - port->direction = SPA_DIRECTION_OUTPUT; - port->id = 0; - port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | - SPA_PORT_CHANGE_MASK_PARAMS; - port->info = SPA_PORT_INFO_INIT(); - port->info.flags = 0; - port->params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); - port->params[1] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); - port->params[2] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - port->params[3] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[4] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - port->info.params = port->params; - port->info.n_params = 5; - spa_list_init(&port->queue); - - port = GET_IN_PORT(this, 0); - port->direction = SPA_DIRECTION_INPUT; - port->id = 0; - port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | - SPA_PORT_CHANGE_MASK_PARAMS; - port->info = SPA_PORT_INFO_INIT(); - port->info.flags = SPA_PORT_FLAG_DYNAMIC_DATA; - port->params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); - port->params[1] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); - port->params[2] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - port->params[3] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[4] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - port->info.params = port->params; - port->info.n_params = 5; - spa_list_init(&port->queue); - - return 0; -} - -static const struct spa_interface_info impl_interfaces[] = { - {SPA_TYPE_INTERFACE_Node,}, -}; - -static int -impl_enum_interface_info(const struct spa_handle_factory *factory, - const struct spa_interface_info **info, - uint32_t *index) -{ - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(info != NULL, -EINVAL); - spa_return_val_if_fail(index != NULL, -EINVAL); - - switch (*index) { - case 0: - *info = &impl_interfaces[*index]; - break; - default: - return 0; - } - (*index)++; - return 1; -} - -const struct spa_handle_factory spa_resample_factory = { - SPA_VERSION_HANDLE_FACTORY, - SPA_NAME_AUDIO_PROCESS_RESAMPLE, - NULL, - impl_get_size, - impl_init, - impl_enum_interface_info, -}; diff --git a/spa/plugins/audioconvert/splitter.c b/spa/plugins/audioconvert/splitter.c deleted file mode 100644 index bcacb8c3c..000000000 --- a/spa/plugins/audioconvert/splitter.c +++ /dev/null @@ -1,1251 +0,0 @@ -/* Spa - * - * Copyright © 2018 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. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fmt-ops.h" - -#undef SPA_LOG_TOPIC_DEFAULT -#define SPA_LOG_TOPIC_DEFAULT log_topic -static struct spa_log_topic *log_topic = &SPA_LOG_TOPIC(0, "spa.splitter"); - -#define DEFAULT_RATE 48000 -#define DEFAULT_CHANNELS 2 -#define DEFAULT_MASK (1LL << SPA_AUDIO_CHANNEL_FL) | (1LL << SPA_AUDIO_CHANNEL_FR) - -#define MAX_ALIGN FMT_OPS_MAX_ALIGN -#define MAX_BUFFERS 32 -#define MAX_DATAS SPA_AUDIO_MAX_CHANNELS -#define MAX_PORTS SPA_AUDIO_MAX_CHANNELS - -struct buffer { - uint32_t id; -#define BUFFER_FLAG_QUEUED (1<<0) - uint32_t flags; - struct spa_list link; - struct spa_buffer *buf; - void *datas[MAX_DATAS]; -}; - -struct port { - uint32_t direction; - uint32_t id; - - struct spa_io_buffers *io; - - uint64_t info_all; - struct spa_port_info info; -#define IDX_EnumFormat 0 -#define IDX_Meta 1 -#define IDX_IO 2 -#define IDX_Format 3 -#define IDX_Buffers 4 -#define IDX_Latency 5 -#define N_PORT_PARAMS 6 - struct spa_param_info params[N_PORT_PARAMS]; - - struct spa_dict info_props; - struct spa_dict_item info_props_items[2]; - char position[16]; - - bool have_format; - struct spa_audio_info format; - uint32_t blocks; - uint32_t stride; - - struct buffer buffers[MAX_BUFFERS]; - uint32_t n_buffers; - - struct spa_list queue; -}; - -struct impl { - struct spa_handle handle; - struct spa_node node; - - struct spa_log *log; - struct spa_cpu *cpu; - - uint32_t cpu_flags; - uint32_t max_align; - uint32_t quantum_limit; - - struct spa_io_position *io_position; - - uint64_t info_all; - struct spa_node_info info; -#define IDX_PortConfig 0 -#define N_NODE_PARAMS 1 - struct spa_param_info params[N_NODE_PARAMS]; - - struct spa_hook_list hooks; - - struct port in_ports[1]; - struct port *out_ports[MAX_PORTS]; - uint32_t port_count; - - struct spa_audio_info format; - unsigned int have_profile:1; - - struct convert conv; - unsigned int is_passthrough:1; - unsigned int started:1; - - struct spa_latency_info latency[2]; - - uint32_t src_remap[SPA_AUDIO_MAX_CHANNELS]; - uint32_t dst_remap[SPA_AUDIO_MAX_CHANNELS]; - - uint32_t empty_size; - float *empty; -}; - -#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 void emit_node_info(struct impl *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) { - spa_node_emit_info(&this->hooks, &this->info); - this->info.change_mask = old; - } -} -static void emit_port_info(struct impl *this, struct port *port, bool full) -{ - uint64_t old = full ? port->info.change_mask : 0; - if (full) - port->info.change_mask = port->info_all; - if (port->info.change_mask) { - spa_node_emit_port_info(&this->hooks, - port->direction, port->id, &port->info); - port->info.change_mask = old; - } -} - -static int init_port(struct impl *this, enum spa_direction direction, - uint32_t port_id, uint32_t position) -{ - struct port *port = GET_OUT_PORT(this, port_id); - const char *name; - - if (port == NULL) { - port = calloc(1, sizeof(struct port)); - if (port == NULL) - return -errno; - this->out_ports[port_id] = port; - } - port->direction = direction; - port->id = port_id; - - name = spa_debug_type_find_short_name(spa_type_audio_channel, position); - snprintf(port->position, sizeof(port->position), "%s", name ? name : "UNK"); - - port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | - SPA_PORT_CHANGE_MASK_PROPS | - SPA_PORT_CHANGE_MASK_PARAMS; - - port->info = SPA_PORT_INFO_INIT(); - port->info.flags = SPA_PORT_FLAG_DYNAMIC_DATA; - port->info_props_items[0] = SPA_DICT_ITEM_INIT(SPA_KEY_FORMAT_DSP, "32 bit float mono audio"); - port->info_props_items[1] = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_CHANNEL, port->position); - port->info_props = SPA_DICT_INIT(port->info_props_items, 2); - port->info.props = &port->info_props; - port->params[IDX_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); - port->params[IDX_Meta] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); - port->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - port->params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE); - port->info.params = port->params; - port->info.n_params = N_PORT_PARAMS; - - 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_dsp; - port->format.info.dsp.format = SPA_AUDIO_FORMAT_DSP_F32; - - spa_log_debug(this->log, "%p: init port %d:%d position:%s", - this, direction, port_id, port->position); - emit_port_info(this, port, true); - - return 0; -} - -static int impl_node_enum_params(void *object, int seq, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - struct impl *this = object; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer[4096]; - struct spa_result_node_params result; - uint32_t count = 0; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - - result.id = id; - result.next = start; - next: - result.index = result.next++; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - default: - return 0; - } - - if (spa_pod_filter(&b, &result.param, param, filter) < 0) - goto next; - - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); - - if (++count != num) - goto next; - - return 0; -} - -static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_debug(this->log, "%p: io %d %p/%zd", this, id, data, size); - - switch (id) { - case SPA_IO_Position: - this->io_position = data; - break; - default: - return -ENOENT; - } - return 0; -} - -static int int32_cmp(const void *v1, const void *v2) -{ - int32_t a1 = *(int32_t*)v1; - int32_t a2 = *(int32_t*)v2; - if (a1 == 0 && a2 != 0) - return 1; - if (a2 == 0 && a1 != 0) - return -1; - return a1 - a2; -} - -static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - if (param == NULL) - return 0; - - switch (id) { - case SPA_PARAM_PortConfig: - { - struct port *port; - struct spa_audio_info info = { 0, }; - struct spa_pod *format; - enum spa_direction direction; - enum spa_param_port_config_mode mode; - uint32_t i; - - if (spa_pod_parse_object(param, - SPA_TYPE_OBJECT_ParamPortConfig, NULL, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(&direction), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(&mode), - SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(&format)) < 0) - return -EINVAL; - - if (!spa_pod_is_object_type(format, SPA_TYPE_OBJECT_Format)) - return -EINVAL; - - if (mode != SPA_PARAM_PORT_CONFIG_MODE_dsp) - return -ENOTSUP; - if (direction != SPA_DIRECTION_OUTPUT) - 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 -ENOTSUP; - - if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) - return -EINVAL; - - info.info.raw.rate = 0; - - if (this->have_profile && memcmp(&this->format, &info, sizeof(info)) == 0) - return 0; - - spa_log_debug(this->log, "%p: port config %d/%d", this, - info.info.raw.rate, info.info.raw.channels); - - for (i = 0; i < this->port_count; i++) - spa_node_emit_port_info(&this->hooks, - SPA_DIRECTION_OUTPUT, i, NULL); - - this->have_profile = true; - this->is_passthrough = true; - this->format = info; - - this->port_count = info.info.raw.channels; - for (i = 0; i < this->port_count; i++) { - init_port(this, SPA_DIRECTION_OUTPUT, i, - info.info.raw.position[i]); - } - port = GET_IN_PORT(this, 0); - qsort(info.info.raw.position, info.info.raw.channels, - sizeof(uint32_t), int32_cmp); - port->format = info; - port->have_format = true; - return 0; - } - default: - return -ENOENT; - } - return 0; -} - -static int impl_node_send_command(void *object, const struct spa_command *command) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(command != NULL, -EINVAL); - - switch (SPA_NODE_COMMAND_ID(command)) { - case SPA_NODE_COMMAND_Start: - this->started = true; - break; - case SPA_NODE_COMMAND_Suspend: - case SPA_NODE_COMMAND_Flush: - case SPA_NODE_COMMAND_Pause: - this->started = false; - break; - default: - return -ENOTSUP; - } - return 0; -} - -static int -impl_node_add_listener(void *object, - struct spa_hook *listener, - const struct spa_node_events *events, - void *data) -{ - struct impl *this = object; - struct spa_hook_list save; - uint32_t i; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_hook_list_isolate(&this->hooks, &save, listener, events, data); - - emit_node_info(this, true); - emit_port_info(this, GET_IN_PORT(this, 0), true); - for (i = 0; i < this->port_count; i++) - emit_port_info(this, GET_OUT_PORT(this, i), true); - - spa_hook_list_join(&this->hooks, &save); - - return 0; -} - -static int -impl_node_set_callbacks(void *object, - const struct spa_node_callbacks *callbacks, - void *user_data) -{ - return 0; -} - -static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id, - const struct spa_dict *props) -{ - return -ENOTSUP; -} - -static int -impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) -{ - return -ENOTSUP; -} - -static int port_enum_formats(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t index, - struct spa_pod **param, - struct spa_pod_builder *builder) -{ - struct impl *this = object; - struct port *port = GET_PORT(this, direction, port_id); - - switch (index) { - case 0: - if (direction == SPA_DIRECTION_OUTPUT) { - *param = spa_format_audio_dsp_build(builder, - SPA_PARAM_EnumFormat, &port->format.info.dsp); - } else if (port->have_format) { - *param = spa_format_audio_raw_build(builder, - SPA_PARAM_EnumFormat, &port->format.info.raw); - } - else { - uint32_t rate = this->io_position ? - this->io_position->clock.rate.denom : DEFAULT_RATE; - - *param = spa_pod_builder_add_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(25, - SPA_AUDIO_FORMAT_F32P, - SPA_AUDIO_FORMAT_F32P, - SPA_AUDIO_FORMAT_F32, - SPA_AUDIO_FORMAT_F32_OE, - SPA_AUDIO_FORMAT_F64P, - SPA_AUDIO_FORMAT_F64, - SPA_AUDIO_FORMAT_F64_OE, - SPA_AUDIO_FORMAT_S32P, - SPA_AUDIO_FORMAT_S32, - SPA_AUDIO_FORMAT_S32_OE, - SPA_AUDIO_FORMAT_S24_32P, - SPA_AUDIO_FORMAT_S24_32, - SPA_AUDIO_FORMAT_S24_32_OE, - SPA_AUDIO_FORMAT_S24P, - SPA_AUDIO_FORMAT_S24, - SPA_AUDIO_FORMAT_S24_OE, - SPA_AUDIO_FORMAT_S16P, - SPA_AUDIO_FORMAT_S16, - SPA_AUDIO_FORMAT_S16_OE, - SPA_AUDIO_FORMAT_S8P, - SPA_AUDIO_FORMAT_S8, - SPA_AUDIO_FORMAT_U8P, - SPA_AUDIO_FORMAT_U8, - SPA_AUDIO_FORMAT_ULAW, - SPA_AUDIO_FORMAT_ALAW), - SPA_FORMAT_AUDIO_rate, SPA_POD_CHOICE_RANGE_Int( - rate, 1, INT32_MAX), - SPA_FORMAT_AUDIO_channels, SPA_POD_CHOICE_RANGE_Int( - DEFAULT_CHANNELS, 1, MAX_PORTS)); - } - break; - default: - return 0; - } - return 1; -} - -static int -impl_node_port_enum_params(void *object, int seq, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - struct impl *this = object; - struct port *port; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer[2048]; - struct spa_result_node_params result; - uint32_t count = 0; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - - spa_log_debug(this->log, "%p: enum params port %d.%d %d %u", - this, direction, port_id, seq, id); - - result.id = id; - result.next = start; - next: - result.index = result.next++; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - case SPA_PARAM_EnumFormat: - if ((res = port_enum_formats(this, direction, port_id, - result.index, ¶m, &b)) <= 0) - return res; - break; - case SPA_PARAM_Format: - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - if (direction == SPA_DIRECTION_OUTPUT) - param = spa_format_audio_dsp_build(&b, id, &port->format.info.dsp); - else - param = spa_format_audio_raw_build(&b, id, &port->format.info.raw); - break; - case SPA_PARAM_Buffers: - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(1, 1, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(port->blocks), - SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( - this->quantum_limit * port->stride, - 16 * port->stride, - INT32_MAX), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->stride)); - break; - - case SPA_PARAM_Meta: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamMeta, id, - SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), - SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); - break; - default: - return 0; - } - break; - case SPA_PARAM_IO: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamIO, id, - SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers), - SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); - break; - default: - return 0; - } - break; - case SPA_PARAM_Latency: - switch (result.index) { - case 0: case 1: - param = spa_latency_build(&b, id, &this->latency[result.index]); - break; - default: - return 0; - } - break; - default: - return -ENOENT; - } - - if (spa_pod_filter(&b, &result.param, param, filter) < 0) - goto next; - - spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); - - if (++count != num) - goto next; - - return 0; -} - -static int clear_buffers(struct impl *this, struct port *port) -{ - if (port->n_buffers > 0) { - spa_log_debug(this->log, "%p: clear buffers %p", this, port); - port->n_buffers = 0; - spa_list_init(&port->queue); - } - return 0; -} - -static int setup_convert(struct impl *this) -{ - struct port *inport; - struct spa_audio_info informat, outformat; - uint32_t i, j, src_fmt, dst_fmt; - int res; - - inport = GET_IN_PORT(this, 0); - - informat = inport->format; - outformat = this->format; - - src_fmt = informat.info.raw.format; - dst_fmt = SPA_AUDIO_FORMAT_DSP_F32; - - spa_log_info(this->log, "%p: %s/%d@%d->%s/%d@%dx%d", this, - spa_debug_type_find_name(spa_type_audio_format, src_fmt), - informat.info.raw.channels, - informat.info.raw.rate, - spa_debug_type_find_name(spa_type_audio_format, dst_fmt), - 1, - outformat.info.raw.rate, - outformat.info.raw.channels); - - for (i = 0; i < informat.info.raw.channels; i++) { - for (j = 0; j < outformat.info.raw.channels; j++) { - if (informat.info.raw.position[i] != - outformat.info.raw.position[j]) - continue; - this->src_remap[i] = j; - this->dst_remap[j] = i; - spa_log_debug(this->log, "%p: channel %d -> %d (%s -> %s)", this, - i, j, - spa_debug_type_find_short_name(spa_type_audio_channel, - informat.info.raw.position[i]), - spa_debug_type_find_short_name(spa_type_audio_channel, - outformat.info.raw.position[j])); - outformat.info.raw.position[j] = -1; - break; - } - } - - this->conv.src_fmt = src_fmt; - this->conv.dst_fmt = dst_fmt; - this->conv.n_channels = informat.info.raw.channels; - this->conv.cpu_flags = this->cpu_flags; - - if ((res = convert_init(&this->conv)) < 0) - return res; - - this->is_passthrough &= this->conv.is_passthrough; - - spa_log_debug(this->log, "%p: got converter features %08x:%08x passthrough:%d", this, - this->cpu_flags, this->conv.cpu_flags, this->is_passthrough); - - return 0; -} - -static int calc_width(struct spa_audio_info *info) -{ - switch (info->info.raw.format) { - case SPA_AUDIO_FORMAT_U8: - case SPA_AUDIO_FORMAT_U8P: - case SPA_AUDIO_FORMAT_S8: - case SPA_AUDIO_FORMAT_S8P: - case SPA_AUDIO_FORMAT_ULAW: - case SPA_AUDIO_FORMAT_ALAW: - return 1; - case SPA_AUDIO_FORMAT_S16P: - case SPA_AUDIO_FORMAT_S16: - case SPA_AUDIO_FORMAT_S16_OE: - return 2; - case SPA_AUDIO_FORMAT_S24P: - case SPA_AUDIO_FORMAT_S24: - case SPA_AUDIO_FORMAT_S24_OE: - return 3; - case SPA_AUDIO_FORMAT_F64P: - case SPA_AUDIO_FORMAT_F64: - case SPA_AUDIO_FORMAT_F64_OE: - return 8; - default: - return 4; - } -} - -static int port_set_latency(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t flags, - const struct spa_pod *latency) -{ - struct impl *this = object; - struct port *port; - enum spa_direction other = SPA_DIRECTION_REVERSE(direction); - uint32_t i; - - spa_log_debug(this->log, "%p: set latency direction:%d", this, direction); - - if (latency == NULL) { - this->latency[other] = SPA_LATENCY_INFO(other); - } else { - struct spa_latency_info info; - if (spa_latency_parse(latency, &info) < 0 || - info.direction != other) - return -EINVAL; - this->latency[other] = info; - } - for (i = 0; i < this->port_count; i++) { - port = GET_OUT_PORT(this, i); - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - port->params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL; - emit_port_info(this, port, false); - } - port = GET_IN_PORT(this, 0); - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - port->params[IDX_Latency].flags ^= SPA_PARAM_INFO_SERIAL; - emit_port_info(this, port, false); - return 0; -} - -static int port_set_format(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t flags, - const struct spa_pod *format) -{ - struct impl *this = object; - struct port *port; - int res; - - port = GET_PORT(this, direction, port_id); - - spa_log_debug(this->log, "%p: set format", this); - - if (format == NULL) { - if (port->have_format) { - if (direction == SPA_DIRECTION_INPUT) - port->have_format = this->have_profile; - else - port->have_format = false; - port->format.info.raw.rate = 0; - clear_buffers(this, port); - } - } else { - struct spa_audio_info info = { 0 }; - - if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) - return res; - - if (direction == SPA_DIRECTION_OUTPUT) { - if (info.media_type != SPA_MEDIA_TYPE_audio || - info.media_subtype != SPA_MEDIA_SUBTYPE_dsp) - return -EINVAL; - if (spa_format_audio_dsp_parse(format, &info.info.dsp) < 0) - return -EINVAL; - if (info.info.dsp.format != SPA_AUDIO_FORMAT_DSP_F32) - return -EINVAL; - - port->stride = 4; - port->blocks = 1; - } - else { - 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; - if (info.info.raw.channels != this->port_count) - return -EINVAL; - - port->stride = calc_width(&info); - if (SPA_AUDIO_FORMAT_IS_PLANAR(info.info.raw.format)) { - port->blocks = info.info.raw.channels; - } else { - port->stride *= info.info.raw.channels; - port->blocks = 1; - } - } - - port->format = info; - - spa_log_debug(this->log, "%p: %d %d %d", this, port_id, port->stride, port->blocks); - - if (direction == SPA_DIRECTION_INPUT) - if ((res = setup_convert(this)) < 0) - return res; - - port->have_format = true; - } - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - if (port->have_format) { - port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); - port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); - } else { - port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - } - emit_port_info(this, port, false); - - return 0; -} - - -static int -impl_node_port_set_param(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_debug(this->log, "%p: set param port %d.%d %u", - this, direction, port_id, id); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - switch (id) { - case SPA_PARAM_Latency: - return port_set_latency(this, direction, port_id, flags, param); - case SPA_PARAM_Format: - return port_set_format(this, direction, port_id, flags, param); - default: - return -ENOENT; - } -} - -static void queue_buffer(struct impl *this, struct port *port, uint32_t id) -{ - struct buffer *b = &port->buffers[id]; - - spa_log_trace_fp(this->log, "%p: queue buffer %d on port %d %d", - this, id, port->id, b->flags); - if (SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_QUEUED)) - return; - - spa_list_append(&port->queue, &b->link); - SPA_FLAG_SET(b->flags, BUFFER_FLAG_QUEUED); -} - -static struct buffer *dequeue_buffer(struct impl *this, struct port *port) -{ - struct buffer *b; - - if (spa_list_is_empty(&port->queue)) - return NULL; - - b = spa_list_first(&port->queue, struct buffer, link); - spa_list_remove(&b->link); - SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_QUEUED); - spa_log_trace_fp(this->log, "%p: dequeue buffer %d on port %d %u", - this, b->id, port->id, b->flags); - - return b; -} - -static int -impl_node_port_use_buffers(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t flags, - struct spa_buffer **buffers, - uint32_t n_buffers) -{ - struct impl *this = object; - struct port *port; - uint32_t i, j, maxsize; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - - spa_return_val_if_fail(port->have_format, -EIO); - - spa_log_debug(this->log, "%p: use buffers %d on port %d", this, n_buffers, port_id); - - clear_buffers(this, port); - - maxsize = 0; - for (i = 0; i < n_buffers; i++) { - struct buffer *b; - uint32_t n_datas = buffers[i]->n_datas; - struct spa_data *d = buffers[i]->datas; - - b = &port->buffers[i]; - b->id = i; - b->buf = buffers[i]; - b->flags = 0; - - for (j = 0; j < n_datas; j++) { - if (d[j].data == NULL) { - spa_log_error(this->log, "%p: invalid memory %d on buffer %d %d %p", - this, j, i, d[j].type, d[j].data); - return -EINVAL; - } - if (!SPA_IS_ALIGNED(d[j].data, this->max_align)) { - spa_log_warn(this->log, "%p: memory %d on buffer %d not aligned", - this, j, i); - } - b->datas[j] = d[j].data; - if (direction == SPA_DIRECTION_OUTPUT && - !SPA_FLAG_IS_SET(d[j].flags, SPA_DATA_FLAG_DYNAMIC)) - this->is_passthrough = false; - - spa_log_debug(this->log, "%p: buffer %d data %d flags:%08x %p", - this, i, j, d[j].flags, b->datas[j]); - - maxsize = SPA_MAX(maxsize, d[j].maxsize); - } - if (direction == SPA_DIRECTION_OUTPUT) - queue_buffer(this, port, i); - } - if (maxsize > this->empty_size) { - this->empty = realloc(this->empty, maxsize + MAX_ALIGN); - if (this->empty == NULL) - return -errno; - memset(this->empty, 0, maxsize + MAX_ALIGN); - this->empty_size = maxsize; - } - port->n_buffers = n_buffers; - - return 0; -} - -static int -impl_node_port_set_io(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - struct port *port; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); - - switch (id) { - case SPA_IO_Buffers: - port->io = data; - break; - default: - return -ENOENT; - } - return 0; -} - -static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) -{ - struct impl *this = object; - struct port *port; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL); - - port = GET_OUT_PORT(this, port_id); - queue_buffer(this, port, buffer_id); - - return 0; -} - -static int impl_node_process(void *object) -{ - struct impl *this = object; - struct port *inport; - struct spa_io_buffers *inio; - uint32_t i, maxsize, n_samples; - struct spa_data *sd, *dd; - struct buffer *sbuf, *dbuf; - uint32_t n_src_datas, n_dst_datas; - const void **src_datas; - void **dst_datas; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - inport = GET_IN_PORT(this, 0); - inio = inport->io; - spa_return_val_if_fail(inio != NULL, -EIO); - spa_return_val_if_fail(this->conv.process != NULL, -EIO); - - spa_log_trace_fp(this->log, "%p: status %p %d %d", this, - inio, inio->status, inio->buffer_id); - - if (SPA_UNLIKELY(inio->status != SPA_STATUS_HAVE_DATA)) - return inio->status; - - if (SPA_UNLIKELY(inio->buffer_id >= inport->n_buffers)) - return inio->status = -EINVAL; - - sbuf = &inport->buffers[inio->buffer_id]; - sd = sbuf->buf->datas; - - n_src_datas = sbuf->buf->n_datas; - src_datas = alloca(sizeof(void*) * n_src_datas); - - maxsize = INT_MAX; - for (i = 0; i < n_src_datas; i++) { - src_datas[i] = SPA_PTROFF(sd[i].data, - sd[i].chunk->offset, void); - maxsize = SPA_MIN(sd[i].chunk->size, maxsize); - } - n_samples = maxsize / inport->stride; - - n_dst_datas = this->port_count; - dst_datas = alloca(sizeof(void*) * n_dst_datas); - - for (i = 0; i < n_dst_datas; i++) { - struct port *outport = GET_OUT_PORT(this, i); - struct spa_io_buffers *outio; - uint32_t src_remap = this->src_remap[i]; - uint32_t dst_remap = this->dst_remap[i]; - - if (SPA_UNLIKELY((outio = outport->io) == NULL)) - goto empty; - - spa_log_trace_fp(this->log, "%p: %d %p %d %d %d", this, i, - outio, outio->status, outio->buffer_id, outport->stride); - - if (SPA_UNLIKELY(outio->status == SPA_STATUS_HAVE_DATA)) - goto empty; - - if (SPA_LIKELY(outio->buffer_id < outport->n_buffers)) { - queue_buffer(this, outport, outio->buffer_id); - outio->buffer_id = SPA_ID_INVALID; - } - - if (SPA_UNLIKELY((dbuf = dequeue_buffer(this, outport)) == NULL)) { - outio->status = -EPIPE; - empty: - spa_log_trace_fp(this->log, "%p: %d skip output", this, i); - dst_datas[dst_remap] = SPA_PTR_ALIGN(this->empty, MAX_ALIGN, void); - continue; - } - - dd = dbuf->buf->datas; - - maxsize = dd->maxsize; - n_samples = SPA_MIN(n_samples, maxsize / outport->stride); - - if (this->is_passthrough) - dd[0].data = (void *)src_datas[src_remap]; - else - dst_datas[dst_remap] = dd[0].data = dbuf->datas[0]; - - dd[0].chunk->offset = 0; - dd[0].chunk->size = n_samples * outport->stride; - - outio->status = SPA_STATUS_HAVE_DATA; - outio->buffer_id = dbuf->id; - } - - spa_log_trace_fp(this->log, "%p: n_src:%d n_dst:%d n_samples:%d max:%d stride:%d p:%d", this, - n_src_datas, n_dst_datas, n_samples, maxsize, inport->stride, - this->is_passthrough); - - if (!this->is_passthrough) - convert_process(&this->conv, dst_datas, src_datas, n_samples); - - inio->status = SPA_STATUS_NEED_DATA; - - return SPA_STATUS_NEED_DATA | SPA_STATUS_HAVE_DATA; -} - -static const struct spa_node_methods impl_node = { - SPA_VERSION_NODE_METHODS, - .add_listener = impl_node_add_listener, - .set_callbacks = impl_node_set_callbacks, - .enum_params = impl_node_enum_params, - .set_param = impl_node_set_param, - .set_io = impl_node_set_io, - .send_command = impl_node_send_command, - .add_port = impl_node_add_port, - .remove_port = impl_node_remove_port, - .port_enum_params = impl_node_port_enum_params, - .port_set_param = impl_node_port_set_param, - .port_use_buffers = impl_node_port_use_buffers, - .port_set_io = impl_node_port_set_io, - .port_reuse_buffer = impl_node_port_reuse_buffer, - .process = impl_node_process, -}; - -static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) -{ - struct impl *this; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - spa_return_val_if_fail(interface != NULL, -EINVAL); - - this = (struct impl *) handle; - - if (spa_streq(type, SPA_TYPE_INTERFACE_Node)) - *interface = &this->node; - else - return -ENOENT; - - return 0; -} - -static int impl_clear(struct spa_handle *handle) -{ - struct impl *this; - uint32_t i; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - - this = (struct impl *) handle; - - for (i = 0; i < MAX_PORTS; i++) - free(this->out_ports[i]); - free(this->empty); - return 0; -} - -static size_t -impl_get_size(const struct spa_handle_factory *factory, - const struct spa_dict *params) -{ - return sizeof(struct impl); -} - -static int -impl_init(const struct spa_handle_factory *factory, - struct spa_handle *handle, - const struct spa_dict *info, - const struct spa_support *support, - uint32_t n_support) -{ - struct impl *this; - struct port *port; - uint32_t i; - - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(handle != NULL, -EINVAL); - - handle->get_interface = impl_get_interface; - handle->clear = impl_clear; - - this = (struct impl *) handle; - - this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - spa_log_topic_init(this->log, log_topic); - - this->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); - if (this->cpu) { - this->cpu_flags = spa_cpu_get_flags(this->cpu); - this->max_align = SPA_MIN(MAX_ALIGN, spa_cpu_get_max_align(this->cpu)); - } - - for (i = 0; info && i < info->n_items; i++) { - const char *k = info->items[i].key; - const char *s = info->items[i].value; - if (spa_streq(k, "clock.quantum-limit")) - spa_atou32(s, &this->quantum_limit, 0); - } - - spa_hook_list_init(&this->hooks); - - this->latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT); - this->latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT); - - this->node.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_Node, - SPA_VERSION_NODE, - &impl_node, this); - this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | - SPA_NODE_CHANGE_MASK_PARAMS; - this->info = SPA_NODE_INFO_INIT(); - this->info.max_input_ports = 1; - this->info.max_output_ports = MAX_PORTS; - this->info.flags = SPA_NODE_FLAG_RT | - SPA_NODE_FLAG_OUT_PORT_CONFIG; - this->params[IDX_PortConfig] = SPA_PARAM_INFO(SPA_PARAM_PortConfig, SPA_PARAM_INFO_WRITE); - this->info.params = this->params; - this->info.n_params = N_NODE_PARAMS; - - port = GET_IN_PORT(this, 0); - port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | - SPA_PORT_CHANGE_MASK_PARAMS; - port->direction = SPA_DIRECTION_INPUT; - port->id = 0; - port->info = SPA_PORT_INFO_INIT(); - port->info.flags = SPA_PORT_FLAG_NO_REF | - SPA_PORT_FLAG_DYNAMIC_DATA; - port->params[IDX_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); - port->params[IDX_Meta] = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); - port->params[IDX_IO] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - port->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params[IDX_Buffers] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - port->params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE); - port->info.params = port->params; - port->info.n_params = N_PORT_PARAMS; - - return 0; -} - -static const struct spa_interface_info impl_interfaces[] = { - {SPA_TYPE_INTERFACE_Node,}, -}; - -static int -impl_enum_interface_info(const struct spa_handle_factory *factory, - const struct spa_interface_info **info, - uint32_t *index) -{ - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(info != NULL, -EINVAL); - spa_return_val_if_fail(index != NULL, -EINVAL); - - switch (*index) { - case 0: - *info = &impl_interfaces[*index]; - break; - default: - return 0; - } - (*index)++; - return 1; -} - -const struct spa_handle_factory spa_splitter_factory = { - SPA_VERSION_HANDLE_FACTORY, - SPA_NAME_AUDIO_PROCESS_DEINTERLEAVE, - NULL, - impl_get_size, - impl_init, - impl_enum_interface_info, -};