mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
module-echo-cancel: Configure buffersize and play delay
* Possible to set max buffer size from config * New play buffer with possibility to set a delay sent to backend, orignal play buffer is still sent without delay to output Change-Id: If787977305586a40cba4585ab6dad4b7163bee5a
This commit is contained in:
parent
000b448ba3
commit
2e1a08edc2
1 changed files with 45 additions and 5 deletions
|
|
@ -109,6 +109,15 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* .--------. .---------. .--------. .----------. .-------.
|
||||||
|
* | source | --> | capture | --> | | --> | source | --> | app |
|
||||||
|
* '--------' '---------' | echo | '----------' '-------'
|
||||||
|
* | cancel |
|
||||||
|
* .--------. .---------. | | .----------. .--------.
|
||||||
|
* | app | --> | sink | --> | | --> | playback | --> | sink |
|
||||||
|
* '--------' '---------' '--------' '----------' '--------'
|
||||||
|
*/
|
||||||
#define NAME "echo-cancel"
|
#define NAME "echo-cancel"
|
||||||
|
|
||||||
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
|
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
|
||||||
|
|
@ -117,6 +126,7 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
|
||||||
/* Hopefully this is enough for any combination of AEC engine and resampler
|
/* Hopefully this is enough for any combination of AEC engine and resampler
|
||||||
* input requirement for rate matching */
|
* input requirement for rate matching */
|
||||||
#define MAX_BUFSIZE_MS 100
|
#define MAX_BUFSIZE_MS 100
|
||||||
|
#define DELAY_MS 0
|
||||||
|
|
||||||
static const struct spa_dict_item module_props[] = {
|
static const struct spa_dict_item module_props[] = {
|
||||||
{ PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
|
{ PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
|
||||||
|
|
@ -126,6 +136,8 @@ static const struct spa_dict_item module_props[] = {
|
||||||
"[ audio.rate=<sample rate> ] "
|
"[ audio.rate=<sample rate> ] "
|
||||||
"[ audio.channels=<number of channels> ] "
|
"[ audio.channels=<number of channels> ] "
|
||||||
"[ audio.position=<channel map> ] "
|
"[ audio.position=<channel map> ] "
|
||||||
|
"[ buffer.max_size=<max buffer size in ms> ] "
|
||||||
|
"[ buffer.play_delay=<play delay in ms> ] "
|
||||||
"[ aec.method=<aec method> ] "
|
"[ aec.method=<aec method> ] "
|
||||||
"[ aec.args=<aec arguments> ] "
|
"[ aec.args=<aec arguments> ] "
|
||||||
"[ source.props=<properties> ] "
|
"[ source.props=<properties> ] "
|
||||||
|
|
@ -166,6 +178,7 @@ struct impl {
|
||||||
void *play_buffer[SPA_AUDIO_MAX_CHANNELS];
|
void *play_buffer[SPA_AUDIO_MAX_CHANNELS];
|
||||||
uint32_t play_ringsize;
|
uint32_t play_ringsize;
|
||||||
struct spa_ringbuffer play_ring;
|
struct spa_ringbuffer play_ring;
|
||||||
|
struct spa_ringbuffer play_delayed_ring;
|
||||||
struct spa_io_rate_match *play_rate_match;
|
struct spa_io_rate_match *play_rate_match;
|
||||||
|
|
||||||
void *out_buffer[SPA_AUDIO_MAX_CHANNELS];
|
void *out_buffer[SPA_AUDIO_MAX_CHANNELS];
|
||||||
|
|
@ -181,6 +194,9 @@ struct impl {
|
||||||
|
|
||||||
unsigned int do_disconnect:1;
|
unsigned int do_disconnect:1;
|
||||||
unsigned int unloading:1;
|
unsigned int unloading:1;
|
||||||
|
|
||||||
|
uint32_t max_buffer_size;
|
||||||
|
uint32_t buffer_delay;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void do_unload_module(void *obj, void *data, int res, uint32_t id)
|
static void do_unload_module(void *obj, void *data, int res, uint32_t id)
|
||||||
|
|
@ -202,13 +218,15 @@ static void process(struct impl *impl)
|
||||||
struct pw_buffer *pout;
|
struct pw_buffer *pout;
|
||||||
float rec_buf[impl->info.channels][impl->aec_blocksize / sizeof(float)];
|
float rec_buf[impl->info.channels][impl->aec_blocksize / sizeof(float)];
|
||||||
float play_buf[impl->info.channels][impl->aec_blocksize / sizeof(float)];
|
float play_buf[impl->info.channels][impl->aec_blocksize / sizeof(float)];
|
||||||
|
float play_delayed_buf[impl->info.channels][impl->aec_blocksize / sizeof(float)];
|
||||||
float out_buf[impl->info.channels][impl->aec_blocksize / sizeof(float)];
|
float out_buf[impl->info.channels][impl->aec_blocksize / sizeof(float)];
|
||||||
const float *rec[impl->info.channels];
|
const float *rec[impl->info.channels];
|
||||||
const float *play[impl->info.channels];
|
const float *play[impl->info.channels];
|
||||||
|
const float *play_delayed[impl->info.channels];
|
||||||
float *out[impl->info.channels];
|
float *out[impl->info.channels];
|
||||||
struct spa_data *dd;
|
struct spa_data *dd;
|
||||||
uint32_t i, size;
|
uint32_t i, size;
|
||||||
uint32_t rindex, pindex, oindex, avail;
|
uint32_t rindex, pindex, oindex, pdindex, avail;
|
||||||
int32_t stride = 0;
|
int32_t stride = 0;
|
||||||
|
|
||||||
if ((pout = pw_stream_dequeue_buffer(impl->playback)) == NULL) {
|
if ((pout = pw_stream_dequeue_buffer(impl->playback)) == NULL) {
|
||||||
|
|
@ -222,12 +240,15 @@ static void process(struct impl *impl)
|
||||||
|
|
||||||
spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex);
|
spa_ringbuffer_get_read_index(&impl->rec_ring, &rindex);
|
||||||
spa_ringbuffer_get_read_index(&impl->play_ring, &pindex);
|
spa_ringbuffer_get_read_index(&impl->play_ring, &pindex);
|
||||||
|
spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex);
|
||||||
|
|
||||||
for (i = 0; i < impl->info.channels; i++) {
|
for (i = 0; i < impl->info.channels; i++) {
|
||||||
/* captured samples, with echo from sink */
|
/* captured samples, with echo from sink */
|
||||||
rec[i] = &rec_buf[i][0];
|
rec[i] = &rec_buf[i][0];
|
||||||
/* echo from sink */
|
/* echo from sink */
|
||||||
play[i] = &play_buf[i][0];
|
play[i] = &play_buf[i][0];
|
||||||
|
/* echo from sink delayed */
|
||||||
|
play_delayed[i] = &play_delayed_buf[i][0];
|
||||||
/* filtered samples, without echo from sink */
|
/* filtered samples, without echo from sink */
|
||||||
out[i] = &out_buf[i][0];
|
out[i] = &out_buf[i][0];
|
||||||
|
|
||||||
|
|
@ -241,6 +262,11 @@ static void process(struct impl *impl)
|
||||||
impl->play_ringsize, pindex % impl->play_ringsize,
|
impl->play_ringsize, pindex % impl->play_ringsize,
|
||||||
(void *)play[i], size);
|
(void *)play[i], size);
|
||||||
|
|
||||||
|
stride = 0;
|
||||||
|
spa_ringbuffer_read_data(&impl->play_delayed_ring, impl->play_buffer[i],
|
||||||
|
impl->play_ringsize, pdindex % impl->play_ringsize,
|
||||||
|
(void *)play_delayed[i], size);
|
||||||
|
|
||||||
/* output to sink, just copy */
|
/* output to sink, just copy */
|
||||||
dd = &pout->buffer->datas[i];
|
dd = &pout->buffer->datas[i];
|
||||||
memcpy(dd->data, play[i], size);
|
memcpy(dd->data, play[i], size);
|
||||||
|
|
@ -252,11 +278,12 @@ static void process(struct impl *impl)
|
||||||
|
|
||||||
spa_ringbuffer_read_update(&impl->rec_ring, rindex + size);
|
spa_ringbuffer_read_update(&impl->rec_ring, rindex + size);
|
||||||
spa_ringbuffer_read_update(&impl->play_ring, pindex + size);
|
spa_ringbuffer_read_update(&impl->play_ring, pindex + size);
|
||||||
|
spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + size);
|
||||||
|
|
||||||
pw_stream_queue_buffer(impl->playback, pout);
|
pw_stream_queue_buffer(impl->playback, pout);
|
||||||
|
|
||||||
/* Now run the canceller */
|
/* Now run the canceller */
|
||||||
echo_cancel_run(impl->aec_info, impl->aec, rec, play, out, size / sizeof(float));
|
echo_cancel_run(impl->aec_info, impl->aec, 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 */
|
||||||
avail = spa_ringbuffer_get_write_index(&impl->out_ring, &oindex);
|
avail = spa_ringbuffer_get_write_index(&impl->out_ring, &oindex);
|
||||||
|
|
@ -544,6 +571,9 @@ static void sink_process(void *data)
|
||||||
spa_ringbuffer_get_read_index(&impl->play_ring, &rindex);
|
spa_ringbuffer_get_read_index(&impl->play_ring, &rindex);
|
||||||
spa_ringbuffer_read_update(&impl->play_ring, rindex + drop);
|
spa_ringbuffer_read_update(&impl->play_ring, rindex + drop);
|
||||||
|
|
||||||
|
spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &rindex);
|
||||||
|
spa_ringbuffer_read_update(&impl->play_delayed_ring, rindex + drop);
|
||||||
|
|
||||||
avail += drop;
|
avail += drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -604,6 +634,7 @@ static int setup_streams(struct impl *impl)
|
||||||
struct spa_pod_builder b;
|
struct spa_pod_builder b;
|
||||||
struct pw_properties *props;
|
struct pw_properties *props;
|
||||||
const char *str;
|
const char *str;
|
||||||
|
uint32_t index;
|
||||||
|
|
||||||
props = pw_properties_new(
|
props = pw_properties_new(
|
||||||
PW_KEY_NODE_NAME, "echo-cancel-capture",
|
PW_KEY_NODE_NAME, "echo-cancel-capture",
|
||||||
|
|
@ -710,9 +741,9 @@ static int setup_streams(struct impl *impl)
|
||||||
params, n_params)) < 0)
|
params, n_params)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
impl->rec_ringsize = sizeof(float) * MAX_BUFSIZE_MS * impl->info.rate / 1000;
|
impl->rec_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000;
|
||||||
impl->play_ringsize = sizeof(float) * MAX_BUFSIZE_MS * impl->info.rate / 1000;
|
impl->play_ringsize = sizeof(float) * (impl->max_buffer_size + impl->buffer_delay) * impl->info.rate / 1000;
|
||||||
impl->out_ringsize = sizeof(float) * MAX_BUFSIZE_MS * impl->info.rate / 1000;
|
impl->out_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000;
|
||||||
for (i = 0; i < impl->info.channels; i++) {
|
for (i = 0; i < impl->info.channels; i++) {
|
||||||
impl->rec_buffer[i] = malloc(impl->rec_ringsize);
|
impl->rec_buffer[i] = malloc(impl->rec_ringsize);
|
||||||
impl->play_buffer[i] = malloc(impl->play_ringsize);
|
impl->play_buffer[i] = malloc(impl->play_ringsize);
|
||||||
|
|
@ -720,8 +751,14 @@ static int setup_streams(struct impl *impl)
|
||||||
}
|
}
|
||||||
spa_ringbuffer_init(&impl->rec_ring);
|
spa_ringbuffer_init(&impl->rec_ring);
|
||||||
spa_ringbuffer_init(&impl->play_ring);
|
spa_ringbuffer_init(&impl->play_ring);
|
||||||
|
spa_ringbuffer_init(&impl->play_delayed_ring);
|
||||||
spa_ringbuffer_init(&impl->out_ring);
|
spa_ringbuffer_init(&impl->out_ring);
|
||||||
|
|
||||||
|
spa_ringbuffer_get_write_index(&impl->play_ring, &index);
|
||||||
|
spa_ringbuffer_write_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay) * impl->info.rate / 1000));
|
||||||
|
spa_ringbuffer_get_read_index(&impl->play_ring, &index);
|
||||||
|
spa_ringbuffer_read_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay) * impl->info.rate / 1000));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -997,6 +1034,9 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
|
||||||
copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
|
copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
|
||||||
copy_props(impl, props, PW_KEY_NODE_LATENCY);
|
copy_props(impl, props, PW_KEY_NODE_LATENCY);
|
||||||
|
|
||||||
|
impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS);
|
||||||
|
impl->buffer_delay = pw_properties_get_uint32(props,"buffer.play_delay", DELAY_MS);
|
||||||
|
|
||||||
pw_properties_free(props);
|
pw_properties_free(props);
|
||||||
|
|
||||||
pw_proxy_add_listener((struct pw_proxy*)impl->core,
|
pw_proxy_add_listener((struct pw_proxy*)impl->core,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue