diff --git a/src/modules/module-echo-cancel.c b/src/modules/module-echo-cancel.c index 17328f300..82e779676 100644 --- a/src/modules/module-echo-cancel.c +++ b/src/modules/module-echo-cancel.c @@ -183,7 +183,9 @@ struct impl { struct spa_hook core_proxy_listener; struct spa_hook core_listener; - struct spa_audio_info_raw info; + struct spa_audio_info_raw rec_info; + struct spa_audio_info_raw out_info; + struct spa_audio_info_raw play_info; struct pw_properties *capture_props; struct pw_stream *capture; @@ -247,10 +249,13 @@ static inline void aec_run(struct impl *impl, const float *rec[], const float *p if (impl->wav_file == NULL) { struct wav_file_info info; - info.info.info.raw = impl->info; - info.info.info.raw.channels *= 3; + spa_zero(info); info.info.media_type = SPA_MEDIA_TYPE_audio; info.info.media_subtype = SPA_MEDIA_SUBTYPE_raw; + info.info.info.raw.format = SPA_AUDIO_FORMAT_F32P; + info.info.info.raw.rate = impl->rec_info.rate; + info.info.info.raw.channels = impl->play_info.channels + + impl->rec_info.channels + impl->out_info.channels; impl->wav_file = wav_file_open(impl->wav_path, "w", &info); @@ -259,14 +264,17 @@ static inline void aec_run(struct impl *impl, const float *rec[], const float *p impl->wav_path); } if (impl->wav_file) { - uint32_t i, c = impl->info.channels; - const float *data[c * 3]; + uint32_t i, n, c = impl->play_info.channels + + impl->rec_info.channels + impl->out_info.channels; + const float *data[c]; + + for (i = n = 0; i < impl->play_info.channels; i++) + data[n++] = play[i]; + for (i = 0; i < impl->rec_info.channels; i++) + data[n++] = rec[i]; + for (i = 0; i < impl->out_info.channels; i++) + data[n++] = out[i]; - 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); @@ -281,18 +289,17 @@ static void process(struct impl *impl) { struct pw_buffer *cout; struct pw_buffer *pout = NULL; - float rec_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)]; - const float *rec[impl->info.channels]; - const float *play[impl->info.channels]; - const float *play_delayed[impl->info.channels]; - float *out[impl->info.channels]; + float rec_buf[impl->rec_info.channels][impl->aec_blocksize / sizeof(float)]; + float play_buf[impl->play_info.channels][impl->aec_blocksize / sizeof(float)]; + float play_delayed_buf[impl->play_info.channels][impl->aec_blocksize / sizeof(float)]; + float out_buf[impl->out_info.channels][impl->aec_blocksize / sizeof(float)]; + const float *rec[impl->rec_info.channels]; + const float *play[impl->play_info.channels]; + const float *play_delayed[impl->play_info.channels]; + float *out[impl->out_info.channels]; struct spa_data *dd; uint32_t i, size; uint32_t rindex, pindex, oindex, pdindex, avail; - int32_t stride = 0; if (impl->playback != NULL && (pout = pw_stream_dequeue_buffer(impl->playback)) == NULL) { pw_log_debug("out of playback buffers: %m"); @@ -302,32 +309,37 @@ static void process(struct impl *impl) size = impl->aec_blocksize; /* First read a block from the playback and capture ring buffers */ - 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_delayed_ring, &pdindex); - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->rec_info.channels; i++) { /* captured samples, with echo from sink */ rec[i] = &rec_buf[i][0]; - /* echo from sink */ - play[i] = &play_buf[i][0]; - /* echo from sink delayed */ - play_delayed[i] = &play_delayed_buf[i][0]; - /* filtered samples, without echo from sink */ - out[i] = &out_buf[i][0]; - stride = 0; spa_ringbuffer_read_data(&impl->rec_ring, impl->rec_buffer[i], impl->rec_ringsize, rindex % impl->rec_ringsize, (void*)rec[i], size); - stride = 0; + } + spa_ringbuffer_read_update(&impl->rec_ring, rindex + size); + + for (i = 0; i < impl->out_info.channels; i++) { + /* filtered samples, without echo from sink */ + out[i] = &out_buf[i][0]; + } + + spa_ringbuffer_get_read_index(&impl->play_ring, &pindex); + spa_ringbuffer_get_read_index(&impl->play_delayed_ring, &pdindex); + + for (i = 0; i < impl->play_info.channels; i++) { + /* echo from sink */ + play[i] = &play_buf[i][0]; + /* echo from sink delayed */ + play_delayed[i] = &play_delayed_buf[i][0]; + spa_ringbuffer_read_data(&impl->play_ring, impl->play_buffer[i], impl->play_ringsize, pindex % impl->play_ringsize, (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); @@ -339,11 +351,9 @@ static void process(struct impl *impl) dd->chunk->offset = 0; dd->chunk->size = size; - dd->chunk->stride = stride; + dd->chunk->stride = sizeof(float); } } - - spa_ringbuffer_read_update(&impl->rec_ring, rindex + size); spa_ringbuffer_read_update(&impl->play_ring, pindex + size); spa_ringbuffer_read_update(&impl->play_delayed_ring, pdindex + size); @@ -357,19 +367,20 @@ static void process(struct impl *impl) /* don't run the canceller until play_buffer has been filled, * copy silence to output in the meantime */ silence_size = SPA_MIN(size, delay_left * sizeof(float)); - for (i = 0; i < impl->info.channels; i++) + for (i = 0; i < impl->out_info.channels; i++) memset(out[i], 0, silence_size); impl->current_delay += silence_size / sizeof(float); pw_log_debug("current_delay %d", impl->current_delay); if (silence_size != size) { - const float *pd[impl->info.channels]; - float *o[impl->info.channels]; + const float *pd[impl->play_info.channels]; + float *o[impl->out_info.channels]; - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->play_info.channels; i++) pd[i] = play_delayed[i] + delay_left; + for (i = 0; i < impl->out_info.channels; i++) o[i] = out[i] + delay_left; - } + aec_run(impl, rec, pd, o, size / sizeof(float) - delay_left); } } else { @@ -393,7 +404,7 @@ static void process(struct impl *impl) avail += drop; } - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->out_info.channels; i++) { /* captured samples, with echo from sink */ spa_ringbuffer_write_data(&impl->out_ring, impl->out_buffer[i], impl->out_ringsize, oindex % impl->out_ringsize, @@ -412,14 +423,14 @@ static void process(struct impl *impl) break; } - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->out_info.channels; i++) { dd = &cout->buffer->datas[i]; spa_ringbuffer_read_data(&impl->out_ring, impl->out_buffer[i], impl->out_ringsize, oindex % impl->out_ringsize, (void *)dd->data, size); dd->chunk->offset = 0; dd->chunk->size = size; - dd->chunk->stride = 0; + dd->chunk->stride = sizeof(float); } pw_stream_queue_buffer(impl->source, cout); @@ -482,7 +493,7 @@ static void capture_process(void *data) pw_log_debug("Setting AEC block size to %u", impl->aec_blocksize); } - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->rec_info.channels; i++) { /* captured samples, with echo from sink */ d = &buf->buffer->datas[i]; @@ -573,11 +584,12 @@ static void reset_buffers(struct impl *impl) spa_ringbuffer_init(&impl->play_delayed_ring); spa_ringbuffer_init(&impl->out_ring); - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->rec_info.channels; i++) memset(impl->rec_buffer[i], 0, impl->rec_ringsize); + for (i = 0; i < impl->play_info.channels; i++) memset(impl->play_buffer[i], 0, impl->play_ringsize); + for (i = 0; i < impl->out_info.channels; i++) memset(impl->out_buffer[i], 0, impl->out_ringsize); - } spa_ringbuffer_get_write_index(&impl->play_ring, &index); spa_ringbuffer_write_update(&impl->play_ring, index + (sizeof(float) * (impl->buffer_delay))); @@ -856,7 +868,7 @@ static void sink_process(void *data) pw_log_debug("Setting AEC block size to %u", impl->aec_blocksize); } - for (i = 0; i < impl->info.channels; i++) { + for (i = 0; i < impl->play_info.channels; i++) { /* echo from sink */ d = &buf->buffer->datas[i]; @@ -1034,14 +1046,15 @@ static int setup_streams(struct impl *impl) spa_pod_dynamic_builder_clean(&b); - impl->rec_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000; - impl->play_ringsize = sizeof(float) * ((impl->max_buffer_size * impl->info.rate / 1000) + impl->buffer_delay); - impl->out_ringsize = sizeof(float) * impl->max_buffer_size * impl->info.rate / 1000; - for (i = 0; i < impl->info.channels; i++) { + impl->rec_ringsize = sizeof(float) * impl->max_buffer_size * impl->rec_info.rate / 1000; + impl->play_ringsize = sizeof(float) * ((impl->max_buffer_size * impl->play_info.rate / 1000) + impl->buffer_delay); + impl->out_ringsize = sizeof(float) * impl->max_buffer_size * impl->out_info.rate / 1000; + for (i = 0; i < impl->rec_info.channels; i++) impl->rec_buffer[i] = malloc(impl->rec_ringsize); + for (i = 0; i < impl->play_info.channels; i++) impl->play_buffer[i] = malloc(impl->play_ringsize); + for (i = 0; i < impl->out_info.channels; i++) impl->out_buffer[i] = malloc(impl->out_ringsize); - } reset_buffers(impl); @@ -1101,14 +1114,12 @@ static void impl_destroy(struct impl *impl) pw_properties_free(impl->playback_props); pw_properties_free(impl->sink_props); - for (i = 0; i < impl->info.channels; i++) { - if (impl->rec_buffer[i]) - free(impl->rec_buffer[i]); - if (impl->play_buffer[i]) - free(impl->play_buffer[i]); - if (impl->out_buffer[i]) - free(impl->out_buffer[i]); - } + for (i = 0; i < impl->rec_info.channels; i++) + free(impl->rec_buffer[i]); + for (i = 0; i < impl->play_info.channels; i++) + free(impl->play_buffer[i]); + for (i = 0; i < impl->out_info.channels; i++) + free(impl->out_buffer[i]); free(impl); } @@ -1189,6 +1200,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) { struct pw_context *context = pw_impl_module_get_context(module); struct pw_properties *props, *aec_props; + struct spa_audio_info_raw info; struct impl *impl; uint32_t id = pw_global_get_id(pw_impl_module_get_global(module)); uint32_t pid = getpid(); @@ -1243,12 +1255,12 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) if (pw_properties_get(props, "resample.prefill") == NULL) pw_properties_set(props, "resample.prefill", "true"); - parse_audio_info(props, &impl->info); + parse_audio_info(props, &info); - impl->capture_info = impl->info; - impl->source_info = impl->info; - impl->sink_info = impl->info; - impl->playback_info = impl->info; + impl->capture_info = info; + impl->source_info = info; + impl->sink_info = info; + impl->playback_info = info; if ((str = pw_properties_get(props, "capture.props")) != NULL) pw_properties_update_string(impl->capture_props, str, strlen(str)); @@ -1296,6 +1308,48 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) pw_properties_set(impl->sink_props, PW_KEY_STREAM_CAPTURE_SINK, "true"); } + copy_props(impl, props, PW_KEY_NODE_GROUP); + copy_props(impl, props, PW_KEY_NODE_LINK_GROUP); + copy_props(impl, props, PW_KEY_NODE_VIRTUAL); + copy_props(impl, props, SPA_KEY_AUDIO_CHANNELS); + copy_props(impl, props, SPA_KEY_AUDIO_POSITION); + copy_props(impl, props, "resample.prefill"); + + impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS); + + if ((str = pw_properties_get(props, "buffer.play_delay")) != NULL) { + int req_num, req_denom; + if (sscanf(str, "%u/%u", &req_num, &req_denom) == 2) { + if (req_denom != 0) { + impl->buffer_delay = (info.rate * req_num) / req_denom; + } else { + impl->buffer_delay = DELAY_MS * info.rate / 1000; + pw_log_warn("Sample rate for buffer.play_delay is 0 using default"); + } + } else { + impl->buffer_delay = DELAY_MS * info.rate / 1000; + pw_log_warn("Wrong value/format for buffer.play_delay using default"); + } + } else { + impl->buffer_delay = DELAY_MS * info.rate / 1000; + } + + if ((str = pw_properties_get(impl->capture_props, SPA_KEY_AUDIO_POSITION)) != NULL) { + parse_position(&impl->capture_info, str, strlen(str)); + } + if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL) { + parse_position(&impl->source_info, str, strlen(str)); + } + if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL) { + parse_position(&impl->sink_info, str, strlen(str)); + impl->playback_info = impl->sink_info; + } + if ((str = pw_properties_get(impl->playback_props, SPA_KEY_AUDIO_POSITION)) != NULL) { + parse_position(&impl->playback_info, str, strlen(str)); + if (impl->playback_info.channels != impl->sink_info.channels) + impl->playback_info = impl->sink_info; + } + if ((str = pw_properties_get(props, "aec.method")) != NULL) pw_log_warn("aec.method is not supported anymore use library.name"); @@ -1313,12 +1367,12 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) return -EINVAL; } - struct spa_dict_item info_items[] = { + struct spa_dict_item dict_items[] = { { SPA_KEY_LIBRARY_NAME, path }, }; - struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items); + struct spa_dict dict = SPA_DICT_INIT_ARRAY(dict_items); - handle = spa_plugin_loader_load(impl->loader, SPA_NAME_AEC, &info); + handle = spa_plugin_loader_load(impl->loader, SPA_NAME_AEC, &dict); if (handle == NULL) { pw_log_error("aec plugin %s not available library.name %s", SPA_NAME_AEC, path); return -ENOENT; @@ -1346,7 +1400,39 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) else aec_props = pw_properties_new(NULL, NULL); - res = spa_audio_aec_init(impl->aec, &aec_props->dict, &impl->info); + + if (spa_interface_callback_check(&impl->aec->iface, struct spa_audio_aec_methods, init2, 3)) { + impl->rec_info = impl->capture_info; + impl->out_info = impl->source_info; + impl->play_info = impl->sink_info; + + res = spa_audio_aec_init2(impl->aec, &aec_props->dict, + &impl->rec_info, &impl->out_info, &impl->play_info); + + if (impl->sink_info.channels != impl->play_info.channels) + impl->sink_info = impl->play_info; + if (impl->playback_info.channels != impl->play_info.channels) + impl->playback_info = impl->play_info; + if (impl->capture_info.channels != impl->rec_info.channels) + impl->capture_info = impl->rec_info; + if (impl->source_info.channels != impl->out_info.channels) + impl->source_info = impl->out_info; + } else { + if (impl->source_info.channels != impl->sink_info.channels) + impl->source_info = impl->sink_info; + if (impl->capture_info.channels != impl->source_info.channels) + impl->capture_info = impl->source_info; + if (impl->playback_info.channels != impl->sink_info.channels) + impl->playback_info = impl->sink_info; + + info = impl->playback_info; + + res = spa_audio_aec_init(impl->aec, &aec_props->dict, &info); + + impl->rec_info.channels = info.channels; + impl->out_info.channels = info.channels; + impl->play_info.channels = info.channels; + } pw_properties_free(aec_props); @@ -1372,17 +1458,19 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) if (factor == 0 || new_num == 0) { pw_log_info("Setting node latency to %s", impl->aec->latency); pw_properties_set(props, PW_KEY_NODE_LATENCY, impl->aec->latency); - impl->aec_blocksize = sizeof(float) * impl->info.rate * num / denom; + impl->aec_blocksize = sizeof(float) * info.rate * num / denom; } else { pw_log_info("Setting node latency to %u/%u", new_num, req_denom); pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", new_num, req_denom); - impl->aec_blocksize = sizeof(float) * impl->info.rate * num / denom * factor; + impl->aec_blocksize = sizeof(float) * info.rate * num / denom * factor; } } else { /* Implementation doesn't care about the block size */ impl->aec_blocksize = 0; } + copy_props(impl, props, PW_KEY_NODE_LATENCY); + impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core); if (impl->core == NULL) { str = pw_properties_get(props, PW_KEY_REMOTE_NAME); @@ -1399,51 +1487,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) goto error; } - copy_props(impl, props, PW_KEY_NODE_GROUP); - copy_props(impl, props, PW_KEY_NODE_LINK_GROUP); - copy_props(impl, props, PW_KEY_NODE_VIRTUAL); - copy_props(impl, props, PW_KEY_NODE_LATENCY); - copy_props(impl, props, SPA_KEY_AUDIO_CHANNELS); - copy_props(impl, props, SPA_KEY_AUDIO_POSITION); - copy_props(impl, props, "resample.prefill"); - - if ((str = pw_properties_get(impl->capture_props, SPA_KEY_AUDIO_POSITION)) != NULL) - parse_position(&impl->capture_info, str, strlen(str)); - if ((str = pw_properties_get(impl->source_props, SPA_KEY_AUDIO_POSITION)) != NULL) - parse_position(&impl->source_info, str, strlen(str)); - if ((str = pw_properties_get(impl->sink_props, SPA_KEY_AUDIO_POSITION)) != NULL) - parse_position(&impl->sink_info, str, strlen(str)); - if ((str = pw_properties_get(impl->playback_props, SPA_KEY_AUDIO_POSITION)) != NULL) - parse_position(&impl->playback_info, str, strlen(str)); - - if (impl->capture_info.channels != impl->info.channels) - impl->capture_info = impl->info; - if (impl->source_info.channels != impl->info.channels) - impl->source_info = impl->info; - if (impl->sink_info.channels != impl->info.channels) - impl->sink_info = impl->info; - if (impl->playback_info.channels != impl->info.channels) - impl->playback_info = impl->info; - - impl->max_buffer_size = pw_properties_get_uint32(props,"buffer.max_size", MAX_BUFSIZE_MS); - - if ((str = pw_properties_get(props, "buffer.play_delay")) != NULL) { - int req_num, req_denom; - if (sscanf(str, "%u/%u", &req_num, &req_denom) == 2) { - if (req_denom != 0) { - impl->buffer_delay = (impl->info.rate*req_num)/req_denom; - } else { - impl->buffer_delay = DELAY_MS * impl->info.rate / 1000; - pw_log_warn("Sample rate for buffer.play_delay is 0 using default"); - } - } else { - impl->buffer_delay = DELAY_MS * impl->info.rate / 1000; - pw_log_warn("Wrong value/format for buffer.play_delay using default"); - } - } else { - impl->buffer_delay = DELAY_MS * impl->info.rate / 1000; - } - pw_properties_free(props); pw_proxy_add_listener((struct pw_proxy*)impl->core,