mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-12-18 08:56:45 -05:00
echo-cancel: add wav debug file support
This commit is contained in:
parent
0563e1da52
commit
670bf8fe8f
6 changed files with 112 additions and 26 deletions
|
|
@ -2159,7 +2159,7 @@ static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_wav(struct impl *this, void **src, uint32_t n_samples)
|
static void handle_wav(struct impl *this, const void **src, uint32_t n_samples)
|
||||||
{
|
{
|
||||||
if (SPA_UNLIKELY(this->props.wav_path[0])) {
|
if (SPA_UNLIKELY(this->props.wav_path[0])) {
|
||||||
if (this->wav_file == NULL) {
|
if (this->wav_file == NULL) {
|
||||||
|
|
@ -2576,7 +2576,7 @@ static int impl_node_process(void *object)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->direction == SPA_DIRECTION_INPUT)
|
if (this->direction == SPA_DIRECTION_INPUT)
|
||||||
handle_wav(this, (void**)src_datas, n_samples);
|
handle_wav(this, src_datas, n_samples);
|
||||||
|
|
||||||
dir = &this->dir[SPA_DIRECTION_INPUT];
|
dir = &this->dir[SPA_DIRECTION_INPUT];
|
||||||
if (!in_passthrough) {
|
if (!in_passthrough) {
|
||||||
|
|
@ -2666,7 +2666,7 @@ static int impl_node_process(void *object)
|
||||||
convert_process(&dir->conv, dst_datas, in_datas, n_samples);
|
convert_process(&dir->conv, dst_datas, in_datas, n_samples);
|
||||||
}
|
}
|
||||||
if (this->direction == SPA_DIRECTION_OUTPUT)
|
if (this->direction == SPA_DIRECTION_OUTPUT)
|
||||||
handle_wav(this, dst_datas, n_samples);
|
handle_wav(this, (const void**)dst_datas, n_samples);
|
||||||
|
|
||||||
spa_log_trace_fp(this->log, "%d/%d %d/%d %d->%d", this->in_offset, max_in,
|
spa_log_trace_fp(this->log, "%d/%d %d/%d %d->%d", this->in_offset, max_in,
|
||||||
this->out_offset, max_out, n_samples, n_out);
|
this->out_offset, max_out, n_samples, n_out);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
audioconvert_sources = [
|
audioconvert_sources = [
|
||||||
'audioadapter.c',
|
'audioadapter.c',
|
||||||
'audioconvert.c',
|
'audioconvert.c',
|
||||||
'wavfile.c',
|
|
||||||
'plugin.c'
|
'plugin.c'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -105,6 +104,7 @@ audioconvert_lib = static_library('audioconvert',
|
||||||
'peaks-ops.c',
|
'peaks-ops.c',
|
||||||
'resample-native.c',
|
'resample-native.c',
|
||||||
'resample-peaks.c',
|
'resample-peaks.c',
|
||||||
|
'wavfile.c',
|
||||||
'volume-ops.c' ],
|
'volume-ops.c' ],
|
||||||
c_args : [ simd_cargs, '-O3'],
|
c_args : [ simd_cargs, '-O3'],
|
||||||
link_with : simd_dependencies,
|
link_with : simd_dependencies,
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ static inline ssize_t write_data(struct wav_file *wf, const void *data, size_t s
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t writei(struct wav_file *wf, void **data, size_t samples)
|
static ssize_t writei(struct wav_file *wf, const void **data, size_t samples)
|
||||||
{
|
{
|
||||||
return write_data(wf, data[0], samples * wf->stride);
|
return write_data(wf, data[0], samples * wf->stride);
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +61,7 @@ typedef struct {
|
||||||
} __attribute__ ((packed)) uint24_t;
|
} __attribute__ ((packed)) uint24_t;
|
||||||
|
|
||||||
#define MAKE_WRITEN_FUNC(name, type) \
|
#define MAKE_WRITEN_FUNC(name, type) \
|
||||||
static ssize_t name (struct wav_file *wf, void **data, size_t samples) \
|
static ssize_t name (struct wav_file *wf, const void **data, size_t samples) \
|
||||||
{ \
|
{ \
|
||||||
uint32_t b, n, k, blocks = wf->blocks, chunk; \
|
uint32_t b, n, k, blocks = wf->blocks, chunk; \
|
||||||
uint8_t buf[BLOCK_SIZE]; \
|
uint8_t buf[BLOCK_SIZE]; \
|
||||||
|
|
@ -114,7 +114,7 @@ static struct format_info {
|
||||||
uint32_t bits;
|
uint32_t bits;
|
||||||
bool planar;
|
bool planar;
|
||||||
uint32_t fmt;
|
uint32_t fmt;
|
||||||
ssize_t (*write) (struct wav_file *wf, void **data, size_t samples);
|
ssize_t (*write) (struct wav_file *wf, const void **data, size_t samples);
|
||||||
} format_info[] = {
|
} format_info[] = {
|
||||||
MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_U8P, 8, true, 1, writen_8),
|
MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_U8P, 8, true, 1, writen_8),
|
||||||
MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_U8, 8, false, 1, writei),
|
MAKE_AUDIO_RAW(SPA_AUDIO_FORMAT_U8, 8, false, 1, writei),
|
||||||
|
|
@ -243,7 +243,7 @@ int wav_file_close(struct wav_file *wf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t wav_file_write(struct wav_file *wf, void **data, size_t samples)
|
ssize_t wav_file_write(struct wav_file *wf, const void **data, size_t samples)
|
||||||
{
|
{
|
||||||
return wf->fi->write(wf, data, samples);
|
return wf->fi->write(wf, data, samples);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,4 +38,4 @@ wav_file_open(const char *filename, const char *mode, struct wav_file_info *info
|
||||||
|
|
||||||
int wav_file_close(struct wav_file *wf);
|
int wav_file_close(struct wav_file *wf);
|
||||||
|
|
||||||
ssize_t wav_file_write(struct wav_file *wf, void **data, size_t size);
|
ssize_t wav_file_write(struct wav_file *wf, const void **data, size_t size);
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ pipewire_module_echo_cancel = shared_library('pipewire-module-echo-cancel',
|
||||||
install : true,
|
install : true,
|
||||||
install_dir : modules_install_dir,
|
install_dir : modules_install_dir,
|
||||||
install_rpath: modules_install_dir,
|
install_rpath: modules_install_dir,
|
||||||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
dependencies : [mathlib, dl_lib, pipewire_dep, audioconvert_dep],
|
||||||
)
|
)
|
||||||
|
|
||||||
pipewire_module_profiler = shared_library('pipewire-module-profiler',
|
pipewire_module_profiler = shared_library('pipewire-module-profiler',
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@
|
||||||
#include <spa/support/plugin-loader.h>
|
#include <spa/support/plugin-loader.h>
|
||||||
#include <spa/interfaces/audio/aec.h>
|
#include <spa/interfaces/audio/aec.h>
|
||||||
|
|
||||||
|
#include <spa/plugins/audioconvert/wavfile.h>
|
||||||
|
|
||||||
#include <pipewire/impl.h>
|
#include <pipewire/impl.h>
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
|
|
@ -231,8 +233,50 @@ struct impl {
|
||||||
struct spa_plugin_loader *loader;
|
struct spa_plugin_loader *loader;
|
||||||
|
|
||||||
bool monitor_mode;
|
bool monitor_mode;
|
||||||
|
|
||||||
|
char wav_path[512];
|
||||||
|
struct wav_file *wav_file;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline void aec_run(struct impl *impl, const float *rec[], const float *play[],
|
||||||
|
float *out[], uint32_t n_samples)
|
||||||
|
{
|
||||||
|
spa_audio_aec_run(impl->aec, rec, play, out, n_samples);
|
||||||
|
|
||||||
|
if (SPA_UNLIKELY(impl->wav_path[0])) {
|
||||||
|
if (impl->wav_file == NULL) {
|
||||||
|
struct wav_file_info info;
|
||||||
|
|
||||||
|
info.info.info.raw = impl->info;
|
||||||
|
info.info.info.raw.channels *= 3;
|
||||||
|
info.info.media_type = SPA_MEDIA_TYPE_audio;
|
||||||
|
info.info.media_subtype = SPA_MEDIA_SUBTYPE_raw;
|
||||||
|
|
||||||
|
impl->wav_file = wav_file_open(impl->wav_path,
|
||||||
|
"w", &info);
|
||||||
|
if (impl->wav_file == NULL)
|
||||||
|
pw_log_warn("can't open wav path '%s': %m",
|
||||||
|
impl->wav_path);
|
||||||
|
}
|
||||||
|
if (impl->wav_file) {
|
||||||
|
uint32_t i, c = impl->info.channels;
|
||||||
|
const float *data[c * 3];
|
||||||
|
|
||||||
|
for (i = 0; i < c; i++) {
|
||||||
|
data[i ] = play[i];
|
||||||
|
data[i + c] = rec[i];
|
||||||
|
data[i + 2*c] = out[i];
|
||||||
|
}
|
||||||
|
wav_file_write(impl->wav_file, (void*)data, n_samples);
|
||||||
|
} else {
|
||||||
|
spa_zero(impl->wav_path);
|
||||||
|
}
|
||||||
|
} else if (impl->wav_file != NULL) {
|
||||||
|
wav_file_close(impl->wav_file);
|
||||||
|
impl->wav_file = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void process(struct impl *impl)
|
static void process(struct impl *impl)
|
||||||
{
|
{
|
||||||
struct pw_buffer *cout;
|
struct pw_buffer *cout;
|
||||||
|
|
@ -326,11 +370,11 @@ static void process(struct impl *impl)
|
||||||
pd[i] = play_delayed[i] + delay_left;
|
pd[i] = play_delayed[i] + delay_left;
|
||||||
o[i] = out[i] + delay_left;
|
o[i] = out[i] + delay_left;
|
||||||
}
|
}
|
||||||
spa_audio_aec_run(impl->aec, rec, pd, o, size / sizeof(float) - delay_left);
|
aec_run(impl, rec, pd, o, size / sizeof(float) - delay_left);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* run the canceller */
|
/* run the canceller */
|
||||||
spa_audio_aec_run(impl->aec, rec, play_delayed, out, size / sizeof(float));
|
aec_run(impl, rec, play_delayed, out, size / sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Next, copy over the output to the output ringbuffer */
|
/* Next, copy over the output to the output ringbuffer */
|
||||||
|
|
@ -538,21 +582,64 @@ static void input_param_latency_changed(struct impl *impl, const struct spa_pod
|
||||||
else
|
else
|
||||||
pw_stream_update_params(impl->capture, params, 1);
|
pw_stream_update_params(impl->capture, params, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct spa_pod* get_props_param(struct impl* impl, struct spa_pod_builder* b)
|
static struct spa_pod* get_props_param(struct impl* impl, struct spa_pod_builder* b)
|
||||||
{
|
{
|
||||||
if (spa_audio_aec_get_params(impl->aec, NULL) > 0) {
|
struct spa_pod_frame f[2];
|
||||||
struct spa_pod_frame f[2];
|
|
||||||
spa_pod_builder_push_object(
|
|
||||||
b, &f[0], SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
|
|
||||||
spa_pod_builder_prop(b, SPA_PROP_params, 0);
|
|
||||||
spa_pod_builder_push_struct(b, &f[1]);
|
|
||||||
|
|
||||||
|
spa_pod_builder_push_object(
|
||||||
|
b, &f[0], SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
|
||||||
|
spa_pod_builder_prop(b, SPA_PROP_params, 0);
|
||||||
|
spa_pod_builder_push_struct(b, &f[1]);
|
||||||
|
|
||||||
|
spa_pod_builder_string(b, "debug.aec.wav-path");
|
||||||
|
spa_pod_builder_string(b, impl->wav_path);
|
||||||
|
|
||||||
|
if (spa_audio_aec_get_params(impl->aec, NULL) > 0)
|
||||||
spa_audio_aec_get_params(impl->aec, b);
|
spa_audio_aec_get_params(impl->aec, b);
|
||||||
|
|
||||||
spa_pod_builder_pop(b, &f[1]);
|
spa_pod_builder_pop(b, &f[1]);
|
||||||
return spa_pod_builder_pop(b, &f[0]);
|
return spa_pod_builder_pop(b, &f[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_params(struct impl* impl, const 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];
|
||||||
|
|
||||||
|
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_none(pod)) {
|
||||||
|
spa_zero(value);
|
||||||
|
} else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pw_log_info("key:'%s' val:'%s'", name, value);
|
||||||
|
|
||||||
|
if (spa_streq(name, "debug.aec.wav-path")) {
|
||||||
|
spa_scnprintf(impl->wav_path,
|
||||||
|
sizeof(impl->wav_path), "%s", value);
|
||||||
|
changed++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
spa_audio_aec_set_params(impl->aec, params);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void input_param_changed(void *data, uint32_t id, const struct spa_pod* param)
|
static void input_param_changed(void *data, uint32_t id, const struct spa_pod* param)
|
||||||
|
|
@ -569,11 +656,10 @@ static void input_param_changed(void *data, uint32_t id, const struct spa_pod* p
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
struct spa_pod_dynamic_builder b;
|
struct spa_pod_dynamic_builder b;
|
||||||
const struct spa_pod* params[1];
|
const struct spa_pod* params[1];
|
||||||
SPA_POD_OBJECT_FOREACH(obj, prop)
|
|
||||||
{
|
SPA_POD_OBJECT_FOREACH(obj, prop) {
|
||||||
if (prop->key == SPA_PROP_params) {
|
if (prop->key == SPA_PROP_params)
|
||||||
spa_audio_aec_set_params(impl->aec, &prop->value);
|
set_params(impl, &prop->value);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
|
spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue