50#include <speex/speex_echo.h>
60#define SAMPLES_PER_FRAME (1 << 9)
61#define DEFAULT_FILTER_LENGTH (48 * 500)
63#define MOD_NAME "[Echo cancel] "
70 struct Echo_state_deleter{
71 void operator()(SpeexEchoState* echo) { speex_echo_state_destroy(echo); }
74 struct Export_state_deleter{
80 std::unique_ptr<SpeexEchoState, Echo_state_deleter>
echo_state;
92 std::unique_ptr<struct audio_export, Export_state_deleter>
exporter;
98 " Dump near end, far end and output samples in separate channels to a wav file.\n");
122#define TEXTIFY(a) TEXTIFY2(a)
123#define TEXTIFY2(a) #a
125ADD_TO_PARAM(
"echo-cancel-filter-length",
"* echo-cancel-filter-length=<samples>\n"
126 " Echo cancellation filter length in samples, should be the third of the room's impulse response length. (default "
130 " Echo cancellation additional delay added to far end in samples, should be slightly less than output device latency.\n");
156 const int ringbuf_sample_count = 2 << 15;
157 constexpr int bps = 2;
162 s->
frame_data = std::make_unique<spx_int16_t[]>(ringbuf_sample_count);
165 static_assert(
sizeof(s->
frame_data[0]) == bps);
181 std::lock_guard lk(s->
lock);
183 if(
frame->ch_count != 1) {
184 static int prints = 0;
185 if(prints++ % 100 == 0) {
186 error_msg(
MOD_NAME "Echo cancellation needs 1 played channel. Disabling echo cancellation.\n"
187 "Use channel mapping and let only one channel played to enable this feature.\n");
196 int to_fill = target - current;
206 size_t samples =
frame->data_len /
frame->bps;
209 if(samples > ringbuf_free_samples){
210 samples = ringbuf_free_samples;
214 if(
frame->bps != 2) {
220 &ptr1, &size1, &ptr2, &size2);
222 assert(size1 % 2 == 0);
223 int in_bytes1 = (size1 / 2) *
frame->bps;
239 std::lock_guard lk(s->
lock);
241 if(
frame->ch_count != 1) {
242 static int prints = 0;
243 if(prints++ % 100 == 0)
244 error_msg(
MOD_NAME "Echo cancellation needs 1 captured channel. Disabling echo cancellation.\n"
245 "Use '--audio-capture-channels 1' parameter to capture single channel.\n");
256 size_t in_frame_samples =
frame->data_len /
frame->bps;
259 if(in_frame_samples > ringbuf_free_samples){
260 in_frame_samples = ringbuf_free_samples;
272 long long delay = std::chrono::duration_cast<std::chrono::microseconds>(diff).count();
289 &ptr1, &size1, &ptr2, &size2);
291 int in_bytes1 = (size1 / 2) *
frame->bps;
304 if(far_end_samples < near_end_samples){
313 if(!frames_to_process){
323 for(
size_t i = 0; i < frames_to_process; i++){
327 const void *export_channels[] = {near_arr, far_arr,
out_ptr,
nullptr};
struct video_frame * frame
struct audio_export * audio_export_init(const char *filename)
void audio_export_destroy(struct audio_export *s)
void audio_export_raw_ch(struct audio_export *s, const void **channels_data, unsigned sample_count)
bool audio_export_configure_raw(struct audio_export *s, int bps, int sample_rate, int ch_count)
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_NOTICE
information that may be interesting
#define LOG_LEVEL_INFO
normal reporting
#define LOG_LEVEL_WARNING
less severe errors
static void reconfigure_echo(struct echo_cancellation *s, int sample_rate, int bps)
std::chrono::steady_clock steady_clock
struct echo_cancellation * echo_cancellation_init(void)
void echo_play(struct echo_cancellation *s, struct audio_frame *frame)
#define SAMPLES_PER_FRAME
struct audio_frame * echo_cancel(struct echo_cancellation *s, struct audio_frame *frame)
steady_clock::duration duration
#define DEFAULT_FILTER_LENGTH
void echo_cancellation_destroy(struct echo_cancellation *s)
steady_clock::time_point time_point
const char * get_commandline_param(const char *key)
#define ADD_TO_PARAM(param, doc)
int ring_get_current_size(struct ring_buffer *ring)
struct ring_buffer * ring_buffer_init(int size)
bool ring_advance_write_idx(struct ring_buffer *ring, int amount)
int ring_get_write_regions(struct ring_buffer *ring, int requested_len, void **ptr1, int *size1, void **ptr2, int *size2)
void ring_advance_read_idx(struct ring_buffer *ring, int amount)
void ring_buffer_flush(struct ring_buffer *buf)
int ring_buffer_read(struct ring_buffer *ring, char *out, int max_len)
void ring_buffer_write(struct ring_buffer *ring, const char *in, int len)
int ring_get_available_write_size(struct ring_buffer *ring)
std::unique_ptr< ring_buffer, ring_buf_deleter > ring_buffer_uniq
struct used to comunicate with audio capture/playback device
time_point next_expected_near
std::unique_ptr< struct audio_export, Export_state_deleter > exporter
ring_buffer_uniq far_end_ringbuf
ring_buffer_uniq near_end_ringbuf
std::unique_ptr< spx_int16_t[]> frame_data
std::unique_ptr< SpeexEchoState, Echo_state_deleter > echo_state
void change_bps(char *out, int out_bps, const char *in, int in_bps, int in_len)