mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-02 09:01:50 -05:00
audioconvert: add support for filter-graphs
Load multiple graphs with audioconvert.filter-graph.N where N is the order where the graph is inserted/replaced. Run the graphs before the channelmixer. Graphs can be added and removed at runtime.
This commit is contained in:
parent
1f4e8b96c2
commit
3a65472e9e
1 changed files with 352 additions and 12 deletions
|
|
@ -11,6 +11,7 @@
|
|||
#include <spa/support/cpu.h>
|
||||
#include <spa/support/loop.h>
|
||||
#include <spa/support/log.h>
|
||||
#include <spa/support/plugin-loader.h>
|
||||
#include <spa/utils/result.h>
|
||||
#include <spa/utils/list.h>
|
||||
#include <spa/utils/json.h>
|
||||
|
|
@ -30,6 +31,7 @@
|
|||
#include <spa/pod/dynamic.h>
|
||||
#include <spa/debug/types.h>
|
||||
#include <spa/control/ump-utils.h>
|
||||
#include <spa/filter-graph/filter-graph.h>
|
||||
|
||||
#include "volume-ops.h"
|
||||
#include "fmt-ops.h"
|
||||
|
|
@ -49,6 +51,7 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.audioconvert");
|
|||
#define MAX_DATAS SPA_AUDIO_MAX_CHANNELS
|
||||
#define MAX_PORTS (SPA_AUDIO_MAX_CHANNELS+1)
|
||||
#define MAX_STAGES 64
|
||||
#define MAX_GRAPH 9 /* 8 active + 1 replacement slot */
|
||||
|
||||
#define DEFAULT_MUTE false
|
||||
#define DEFAULT_VOLUME VOLUME_NORM
|
||||
|
|
@ -96,6 +99,7 @@ struct props {
|
|||
double rate;
|
||||
char wav_path[512];
|
||||
unsigned int lock_volumes:1;
|
||||
unsigned int filter_graph_disabled:1;
|
||||
};
|
||||
|
||||
static void props_reset(struct props *props)
|
||||
|
|
@ -117,6 +121,7 @@ static void props_reset(struct props *props)
|
|||
props->rate = 1.0;
|
||||
spa_zero(props->wav_path);
|
||||
props->lock_volumes = false;
|
||||
props->filter_graph_disabled = false;
|
||||
}
|
||||
|
||||
struct buffer {
|
||||
|
|
@ -219,6 +224,17 @@ struct stage {
|
|||
void (*run) (struct stage *stage, struct stage_context *c);
|
||||
};
|
||||
|
||||
struct filter_graph {
|
||||
struct impl *impl;
|
||||
int order;
|
||||
struct spa_handle *handle;
|
||||
struct spa_filter_graph *graph;
|
||||
struct spa_hook listener;
|
||||
uint32_t n_inputs;
|
||||
uint32_t n_outputs;
|
||||
bool active;
|
||||
};
|
||||
|
||||
struct impl {
|
||||
struct spa_handle handle;
|
||||
struct spa_node node;
|
||||
|
|
@ -226,6 +242,14 @@ struct impl {
|
|||
struct spa_log *log;
|
||||
struct spa_cpu *cpu;
|
||||
struct spa_loop *data_loop;
|
||||
struct spa_plugin_loader *loader;
|
||||
|
||||
uint32_t n_graph;
|
||||
uint32_t graph_index[MAX_GRAPH];
|
||||
|
||||
struct filter_graph filter_graph[MAX_GRAPH];
|
||||
int in_filter_props;
|
||||
int filter_props_count;
|
||||
|
||||
struct stage stages[MAX_STAGES];
|
||||
uint32_t n_stages;
|
||||
|
|
@ -446,6 +470,7 @@ static int impl_node_enum_params(void *object, int seq,
|
|||
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);
|
||||
|
|
@ -776,8 +801,30 @@ static int impl_node_enum_params(void *object, int seq,
|
|||
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->lock_volumes),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
case 28:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_name, SPA_POD_String("audioconvert.filter-graph.disable"),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("Disable Filter graph updates"),
|
||||
SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->filter_graph_disabled),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
case 29:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_name, SPA_POD_String("audioconvert.filter-graph"),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("A filter graph to load"),
|
||||
SPA_PROP_INFO_type, SPA_POD_String(""),
|
||||
SPA_PROP_INFO_params, SPA_POD_Bool(true));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
if (this->filter_graph[0].graph) {
|
||||
res = spa_filter_graph_enum_prop_info(this->filter_graph[0].graph,
|
||||
result.index - 30, &b, ¶m);
|
||||
if (res <= 0)
|
||||
return res;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -856,11 +903,27 @@ static int impl_node_enum_params(void *object, int seq,
|
|||
spa_pod_builder_string(&b, p->wav_path);
|
||||
spa_pod_builder_string(&b, "channelmix.lock-volumes");
|
||||
spa_pod_builder_bool(&b, p->lock_volumes);
|
||||
spa_pod_builder_string(&b, "audioconvert.filter-graph.disable");
|
||||
spa_pod_builder_bool(&b, p->filter_graph_disabled);
|
||||
spa_pod_builder_string(&b, "audioconvert.filter-graph");
|
||||
spa_pod_builder_string(&b, "");
|
||||
spa_pod_builder_pop(&b, &f[1]);
|
||||
param = spa_pod_builder_pop(&b, &f[0]);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
if (result.index > MAX_GRAPH)
|
||||
return 0;
|
||||
|
||||
if (this->filter_graph[result.index-1].graph == NULL)
|
||||
goto next;
|
||||
|
||||
res = spa_filter_graph_get_props(this->filter_graph[result.index-1].graph,
|
||||
&b, ¶m);
|
||||
if (res < 0)
|
||||
return res;
|
||||
if (res == 0)
|
||||
goto next;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -897,8 +960,195 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void graph_info(void *object, const struct spa_filter_graph_info *info)
|
||||
{
|
||||
struct filter_graph *g = object;
|
||||
if (!g->active)
|
||||
return;
|
||||
g->n_inputs = info->n_inputs;
|
||||
g->n_outputs = info->n_outputs;
|
||||
}
|
||||
|
||||
static int apply_props(struct impl *impl, const struct spa_pod *props);
|
||||
|
||||
static void graph_apply_props(void *object, enum spa_direction direction, const struct spa_pod *props)
|
||||
{
|
||||
struct filter_graph *g = object;
|
||||
struct impl *impl = g->impl;
|
||||
if (!g->active)
|
||||
return;
|
||||
if (apply_props(impl, props) > 0)
|
||||
emit_node_info(impl, false);
|
||||
}
|
||||
|
||||
static void graph_props_changed(void *object, enum spa_direction direction)
|
||||
{
|
||||
struct filter_graph *g = object;
|
||||
struct impl *impl = g->impl;
|
||||
if (!g->active)
|
||||
return;
|
||||
impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
|
||||
impl->params[IDX_Props].user++;
|
||||
}
|
||||
|
||||
struct spa_filter_graph_events graph_events = {
|
||||
SPA_VERSION_FILTER_GRAPH_EVENTS,
|
||||
.info = graph_info,
|
||||
.apply_props = graph_apply_props,
|
||||
.props_changed = graph_props_changed,
|
||||
};
|
||||
|
||||
static int setup_filter_graph(struct impl *this, struct spa_filter_graph *graph)
|
||||
{
|
||||
int res;
|
||||
char rate_str[64];
|
||||
struct dir *in;
|
||||
|
||||
if (graph == NULL)
|
||||
return 0;
|
||||
|
||||
in = &this->dir[SPA_DIRECTION_INPUT];
|
||||
snprintf(rate_str, sizeof(rate_str), "%d", in->format.info.raw.rate);
|
||||
|
||||
spa_filter_graph_deactivate(graph);
|
||||
res = spa_filter_graph_activate(graph,
|
||||
&SPA_DICT_ITEMS(
|
||||
SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, rate_str)));
|
||||
return res;
|
||||
}
|
||||
|
||||
static int do_sync_filter_graph(struct spa_loop *loop, bool async, uint32_t seq,
|
||||
const void *data, size_t size, void *user_data)
|
||||
{
|
||||
struct impl *impl = user_data;
|
||||
uint32_t i, j;
|
||||
impl->n_graph = 0;
|
||||
for (i = 0; i < MAX_GRAPH; i++) {
|
||||
struct filter_graph *g = &impl->filter_graph[i];
|
||||
if (g->graph == NULL || !g->active)
|
||||
continue;
|
||||
impl->graph_index[impl->n_graph++] = i;
|
||||
|
||||
for (j = impl->n_graph-1; j > 0; j--) {
|
||||
if (impl->filter_graph[impl->graph_index[j]].order >=
|
||||
impl->filter_graph[impl->graph_index[j-1]].order)
|
||||
break;
|
||||
SPA_SWAP(impl->graph_index[j], impl->graph_index[j-1]);
|
||||
}
|
||||
}
|
||||
impl->recalc = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clean_filter_handles(struct impl *impl, bool force)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < MAX_GRAPH; i++) {
|
||||
struct filter_graph *g = &impl->filter_graph[i];
|
||||
if (!g->active || force) {
|
||||
if (g->graph)
|
||||
spa_hook_remove(&g->listener);
|
||||
if (g->handle)
|
||||
spa_plugin_loader_unload(impl->loader, g->handle);
|
||||
spa_zero(*g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int load_filter_graph(struct impl *impl, const char *graph, int order)
|
||||
{
|
||||
char qlimit[64];
|
||||
int res;
|
||||
void *iface;
|
||||
struct spa_handle *new_handle = NULL;
|
||||
uint32_t i, idx, n_graph;
|
||||
struct filter_graph *pending;
|
||||
|
||||
if (impl->props.filter_graph_disabled)
|
||||
return -EPERM;
|
||||
|
||||
/* find graph spot */
|
||||
idx = SPA_ID_INVALID;
|
||||
n_graph = 0;
|
||||
for (i = 0; i < MAX_GRAPH; i++) {
|
||||
pending = &impl->filter_graph[i];
|
||||
/* find the first free spot for our new filter */
|
||||
if (!pending->active && idx == SPA_ID_INVALID)
|
||||
idx = i;
|
||||
/* deactivate an existing filter of the same order */
|
||||
if (pending->active) {
|
||||
if (pending->order == order)
|
||||
pending->active = false;
|
||||
else
|
||||
n_graph++;
|
||||
}
|
||||
}
|
||||
/* we can at most have MAX_GRAPH-1 active filters */
|
||||
if (n_graph >= MAX_GRAPH-1)
|
||||
return -ENOSPC;
|
||||
|
||||
pending = &impl->filter_graph[idx];
|
||||
pending->impl = impl;
|
||||
pending->order = order;
|
||||
|
||||
if (graph != NULL && graph[0] != '\0') {
|
||||
snprintf(qlimit, sizeof(qlimit), "%u", impl->quantum_limit);
|
||||
|
||||
new_handle = spa_plugin_loader_load(impl->loader, "filter.graph",
|
||||
&SPA_DICT_ITEMS(
|
||||
SPA_DICT_ITEM(SPA_KEY_LIBRARY_NAME, "filter-graph/libspa-filter-graph"),
|
||||
SPA_DICT_ITEM("clock.quantum-limit", qlimit),
|
||||
SPA_DICT_ITEM("filter.graph", graph)));
|
||||
if (new_handle == NULL)
|
||||
goto error;
|
||||
|
||||
res = spa_handle_get_interface(new_handle, SPA_TYPE_INTERFACE_FilterGraph, &iface);
|
||||
if (res < 0 || iface == NULL)
|
||||
goto error;
|
||||
|
||||
/* prepare new filter and swap it */
|
||||
res = setup_filter_graph(impl, iface);
|
||||
if (res < 0)
|
||||
goto error;
|
||||
pending->graph = iface;
|
||||
pending->active = true;
|
||||
spa_log_info(impl->log, "loading filter-graph order:%d in %d active:%d",
|
||||
order, idx, n_graph + 1);
|
||||
} else {
|
||||
pending->active = false;
|
||||
spa_log_info(impl->log, "removing filter-graph order:%d active:%d",
|
||||
order, n_graph);
|
||||
}
|
||||
|
||||
/* we call this here on the pending_graph so that the n_input/n_output is updated
|
||||
* before we switch */
|
||||
if (pending->active)
|
||||
spa_filter_graph_add_listener(pending->graph,
|
||||
&pending->listener, &graph_events, pending);
|
||||
|
||||
spa_loop_invoke(impl->data_loop, do_sync_filter_graph, 0, NULL, 0, true, impl);
|
||||
|
||||
if (pending->active)
|
||||
pending->handle = new_handle;
|
||||
|
||||
if (impl->in_filter_props == 0)
|
||||
clean_filter_handles(impl, false);
|
||||
|
||||
impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
|
||||
impl->params[IDX_PropInfo].user++;
|
||||
impl->params[IDX_Props].user++;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
if (new_handle != NULL)
|
||||
spa_plugin_loader_unload(impl->loader, new_handle);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int audioconvert_set_param(struct impl *this, const char *k, const char *s)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (spa_streq(k, "monitor.channel-volumes"))
|
||||
this->monitor_channel_volumes = spa_atob(s);
|
||||
else if (spa_streq(k, "channelmix.disable"))
|
||||
|
|
@ -939,6 +1189,13 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char *
|
|||
}
|
||||
else if (spa_streq(k, "channelmix.lock-volumes"))
|
||||
this->props.lock_volumes = spa_atob(s);
|
||||
else if (spa_strstartswith(k, "audioconvert.filter-graph")) {
|
||||
int order = atoi(k+ strlen("audioconvert.filter-graph."));
|
||||
if ((res = load_filter_graph(this, s, order)) < 0) {
|
||||
spa_log_warn(this->log, "Can't load filter-graph %d: %s",
|
||||
order, spa_strerror(res));
|
||||
}
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
return 1;
|
||||
|
|
@ -1262,7 +1519,8 @@ static int apply_props(struct impl *this, const struct spa_pod *param)
|
|||
}
|
||||
break;
|
||||
case SPA_PROP_params:
|
||||
changed += parse_prop_params(this, &prop->value);
|
||||
if (this->filter_props_count == 0)
|
||||
changed += parse_prop_params(this, &prop->value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -1456,9 +1714,29 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
|||
break;
|
||||
}
|
||||
case SPA_PARAM_Props:
|
||||
if (apply_props(this, param) > 0)
|
||||
{
|
||||
uint32_t i;
|
||||
bool have_graph = false;
|
||||
this->filter_props_count = 0;
|
||||
for (i = 0; i < MAX_GRAPH; i++) {
|
||||
struct filter_graph *g = &this->filter_graph[i];
|
||||
if (!g->active)
|
||||
continue;
|
||||
|
||||
have_graph = true;
|
||||
|
||||
this->in_filter_props++;
|
||||
spa_filter_graph_set_props(g->graph,
|
||||
SPA_DIRECTION_INPUT, param);
|
||||
this->filter_props_count++;
|
||||
this->in_filter_props--;
|
||||
}
|
||||
if (!have_graph && apply_props(this, param) > 0)
|
||||
emit_node_info(this, false);
|
||||
|
||||
clean_filter_handles(this, false);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
|
@ -1975,6 +2253,13 @@ static int setup_convert(struct impl *this)
|
|||
|
||||
if ((res = setup_in_convert(this)) < 0)
|
||||
return res;
|
||||
for (i = 0; i < MAX_GRAPH; i++) {
|
||||
struct filter_graph *g = &this->filter_graph[i];
|
||||
if (!g->active)
|
||||
continue;
|
||||
if ((res = setup_filter_graph(this, g->graph)) < 0)
|
||||
return res;
|
||||
}
|
||||
if ((res = setup_channelmix(this)) < 0)
|
||||
return res;
|
||||
if ((res = setup_resample(this)) < 0)
|
||||
|
|
@ -2007,6 +2292,12 @@ static int setup_convert(struct impl *this)
|
|||
|
||||
static void reset_node(struct impl *this)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < MAX_GRAPH; i++) {
|
||||
struct filter_graph *g = &this->filter_graph[i];
|
||||
if (g->graph)
|
||||
spa_filter_graph_deactivate(g->graph);
|
||||
}
|
||||
if (this->resample.reset)
|
||||
resample_reset(&this->resample);
|
||||
this->in_offset = 0;
|
||||
|
|
@ -3059,6 +3350,30 @@ static void run_channelmix_stage(struct stage *s, struct stage_context *c)
|
|||
channelmix_process(&impl->mix, out_datas, in_datas, c->n_samples);
|
||||
}
|
||||
}
|
||||
|
||||
static void run_filter_stage(struct stage *s, struct stage_context *c)
|
||||
{
|
||||
struct filter_graph *fg = s->data;
|
||||
|
||||
spa_log_trace_fp(s->impl->log, "%p: filter-graph %d", s->impl, c->n_samples);
|
||||
spa_filter_graph_process(fg->graph, (const void **)c->datas[s->in_idx],
|
||||
c->datas[s->out_idx], c->n_samples);
|
||||
}
|
||||
static void add_filter_stage(struct impl *impl, uint32_t i, struct filter_graph *fg, struct stage_context *ctx)
|
||||
{
|
||||
struct stage *s = &impl->stages[impl->n_stages];
|
||||
s->impl = impl;
|
||||
s->passthrough = false;
|
||||
s->in_idx = ctx->src_idx;
|
||||
s->out_idx = ctx->dst_idx;
|
||||
s->n_in = ctx->n_datas;
|
||||
s->n_out = ctx->n_datas;
|
||||
s->data = fg;
|
||||
s->run = run_filter_stage;
|
||||
impl->n_stages++;
|
||||
ctx->src_idx = ctx->dst_idx;
|
||||
}
|
||||
|
||||
static void add_channelmix_stage(struct impl *impl, struct stage_context *ctx)
|
||||
{
|
||||
struct stage *s = &impl->stages[impl->n_stages];
|
||||
|
|
@ -3111,10 +3426,11 @@ static void add_dst_convert_stage(struct impl *impl, struct stage_context *ctx)
|
|||
static void recalc_stages(struct impl *this, struct stage_context *ctx)
|
||||
{
|
||||
struct dir *dir;
|
||||
bool in_passthrough, mix_passthrough, resample_passthrough, out_passthrough;
|
||||
bool filter_passthrough, in_passthrough, mix_passthrough, resample_passthrough, out_passthrough;
|
||||
int tmp = 0;
|
||||
struct port *ctrlport = ctx->ctrlport;
|
||||
bool in_need_remap, out_need_remap;
|
||||
uint32_t i;
|
||||
|
||||
this->recalc = false;
|
||||
this->n_stages = 0;
|
||||
|
|
@ -3128,11 +3444,12 @@ static void recalc_stages(struct impl *this, struct stage_context *ctx)
|
|||
out_need_remap = dir->need_remap;
|
||||
|
||||
resample_passthrough = resample_is_passthrough(this);
|
||||
filter_passthrough = this->n_graph == 0;
|
||||
this->resample_passthrough = resample_passthrough;
|
||||
mix_passthrough = SPA_FLAG_IS_SET(this->mix.flags, CHANNELMIX_FLAG_IDENTITY) &&
|
||||
(ctrlport == NULL || ctrlport->ctrl == NULL) && (this->vol_ramp_sequence == NULL);
|
||||
|
||||
if (in_passthrough && mix_passthrough && resample_passthrough)
|
||||
if (in_passthrough && filter_passthrough && mix_passthrough && resample_passthrough)
|
||||
out_passthrough = false;
|
||||
|
||||
if (out_passthrough && out_need_remap)
|
||||
|
|
@ -3142,7 +3459,7 @@ static void recalc_stages(struct impl *this, struct stage_context *ctx)
|
|||
add_wav_stage(this, ctx);
|
||||
|
||||
if (!in_passthrough) {
|
||||
if (mix_passthrough && resample_passthrough && out_passthrough)
|
||||
if (filter_passthrough && mix_passthrough && resample_passthrough && out_passthrough)
|
||||
ctx->dst_idx = ctx->final_idx;
|
||||
else
|
||||
ctx->dst_idx = CTX_DATA_TMP_0 + ((tmp++) & 1);
|
||||
|
|
@ -3155,7 +3472,7 @@ static void recalc_stages(struct impl *this, struct stage_context *ctx)
|
|||
|
||||
if (this->direction == SPA_DIRECTION_INPUT) {
|
||||
if (!resample_passthrough) {
|
||||
if (mix_passthrough && out_passthrough)
|
||||
if (filter_passthrough && mix_passthrough && out_passthrough)
|
||||
ctx->dst_idx = ctx->final_idx;
|
||||
else
|
||||
ctx->dst_idx = CTX_DATA_TMP_0 + ((tmp++) & 1);
|
||||
|
|
@ -3164,6 +3481,19 @@ static void recalc_stages(struct impl *this, struct stage_context *ctx)
|
|||
resample_passthrough = true;
|
||||
}
|
||||
}
|
||||
if (!filter_passthrough) {
|
||||
for (i = 0; i < this->n_graph; i++) {
|
||||
struct filter_graph *fg = &this->filter_graph[this->graph_index[i]];
|
||||
|
||||
if (mix_passthrough && resample_passthrough && out_passthrough &&
|
||||
i + 1 == this->n_graph)
|
||||
ctx->dst_idx = ctx->final_idx;
|
||||
else
|
||||
ctx->dst_idx = CTX_DATA_TMP_0 + ((tmp++) & 1);
|
||||
|
||||
add_filter_stage(this, i, fg, ctx);
|
||||
}
|
||||
}
|
||||
if (!mix_passthrough) {
|
||||
if (resample_passthrough && out_passthrough)
|
||||
ctx->dst_idx = ctx->final_idx;
|
||||
|
|
@ -3630,6 +3960,8 @@ static int impl_clear(struct spa_handle *handle)
|
|||
|
||||
free_tmp(this);
|
||||
|
||||
clean_filter_handles(this, true);
|
||||
|
||||
if (this->resample.free)
|
||||
resample_free(&this->resample);
|
||||
if (this->wav_file != NULL)
|
||||
|
|
@ -3654,6 +3986,8 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
{
|
||||
struct impl *this;
|
||||
uint32_t i;
|
||||
const char *str;
|
||||
bool filter_graph_disabled;
|
||||
|
||||
spa_return_val_if_fail(factory != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(handle != NULL, -EINVAL);
|
||||
|
|
@ -3672,7 +4006,10 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
this->cpu_flags = spa_cpu_get_flags(this->cpu);
|
||||
this->max_align = SPA_MIN(MAX_ALIGN, spa_cpu_get_max_align(this->cpu));
|
||||
}
|
||||
this->loader = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_PluginLoader);
|
||||
|
||||
props_reset(&this->props);
|
||||
filter_graph_disabled = this->props.filter_graph_disabled;
|
||||
|
||||
this->rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
|
||||
this->rate_limit.burst = 1;
|
||||
|
|
@ -3685,12 +4022,13 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
this->mix.rear_delay = 0.0f;
|
||||
this->mix.widen = 0.0f;
|
||||
|
||||
if (info && (str = spa_dict_lookup(info, "clock.quantum-limit")) != NULL)
|
||||
spa_atou32(str, &this->quantum_limit, 0);
|
||||
|
||||
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"))
|
||||
if (spa_streq(k, "resample.peaks"))
|
||||
this->resample_peaks = spa_atob(s);
|
||||
else if (spa_streq(k, "resample.prefill"))
|
||||
SPA_FLAG_UPDATE(this->resample.options,
|
||||
|
|
@ -3706,10 +4044,12 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
spa_scnprintf(this->group_name, sizeof(this->group_name), "%s", s);
|
||||
else if (spa_streq(k, "monitor.passthrough"))
|
||||
this->monitor_passthrough = spa_atob(s);
|
||||
else if (spa_streq(k, "audioconvert.filter-graph.disable"))
|
||||
filter_graph_disabled = spa_atob(s);
|
||||
else
|
||||
audioconvert_set_param(this, k, s);
|
||||
}
|
||||
|
||||
this->props.filter_graph_disabled = filter_graph_disabled;
|
||||
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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue