UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
echo.cpp
Go to the documentation of this file.
1
6/*
7 * Copyright (c) 2012-2021 CESNET z.s.p.o.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, is permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of CESNET nor the names of its contributors may be
22 * used to endorse or promote products derived from this software without
23 * specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
27 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
28 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
29 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#ifdef HAVE_CONFIG_H
40#include "config.h"
41#include "config_unix.h"
42#include "config_win32.h"
43#endif /* HAVE_CONFIG_H */
44
45#include "audio/utils.h"
46#include "audio/export.h"
47#include "debug.h"
48#include "echo.h"
49
50#include <speex/speex_echo.h>
51
52#include <stdlib.h>
53#include <mutex>
54#include <memory>
55#include <algorithm>
56#include <chrono>
57#include "utils/ring_buffer.h"
58#include "host.h"
59
60#define SAMPLES_PER_FRAME (1 << 9) //512, about 10ms at 48kHz, power of two for easy FFT
61#define DEFAULT_FILTER_LENGTH (48 * 500)
62
63#define MOD_NAME "[Echo cancel] "
64
65using steady_clock = std::chrono::steady_clock;
66using time_point = steady_clock::time_point;
67using duration = steady_clock::duration;
68
69namespace {
70 struct Echo_state_deleter{
71 void operator()(SpeexEchoState* echo) { speex_echo_state_destroy(echo); }
72 };
73
74 struct Export_state_deleter{
75 void operator()(struct audio_export* e) { audio_export_destroy(e); }
76 };
77}
78
80 std::unique_ptr<SpeexEchoState, Echo_state_deleter> echo_state;
81
84
85 std::unique_ptr<spx_int16_t[]> frame_data;
87
91
92 std::unique_ptr<struct audio_export, Export_state_deleter> exporter;
93
94 std::mutex lock;
95};
96
97ADD_TO_PARAM("echo-cancel-dump-audio", "* echo-cancel-dump-audio\n"
98 " Dump near end, far end and output samples in separate channels to a wav file.\n");
99
100static void reconfigure_echo (struct echo_cancellation *s, int sample_rate, int bps);
101
102static void reconfigure_echo (struct echo_cancellation *s, int sample_rate, int bps)
103{
104 UNUSED(bps);
105
106 s->frame.bps = 2;
107 s->frame.ch_count = 1;
109
112
113 speex_echo_ctl(s->echo_state.get(), SPEEX_ECHO_SET_SAMPLING_RATE, &sample_rate); // should the 3rd parameter be int?
114
115 if(get_commandline_param("echo-cancel-dump-audio")){
116 s->exporter.reset(nullptr); //previous file gets closed
117 s->exporter.reset(audio_export_init("echo_cancel_dump.wav"));
119 }
120}
121
122#define TEXTIFY(a) TEXTIFY2(a)
123#define TEXTIFY2(a) #a
124
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 "
128
129ADD_TO_PARAM("echo-cancel-delay", "* echo-cancel-delay=<samples>\n"
130 " Echo cancellation additional delay added to far end in samples, should be slightly less than output device latency.\n");
131
133{
134 struct echo_cancellation *s = new echo_cancellation();
135
136 int filter_length = DEFAULT_FILTER_LENGTH;
137 if(const char *param = get_commandline_param("echo-cancel-filter-length"); param != nullptr){
138 char *end;
139 int len = strtol(param, &end, 10);
140 if(end != param)
141 filter_length = len;
142 }
143
144 if(const char *param = get_commandline_param("echo-cancel-delay"); param != nullptr){
145 char *end;
146 int len = strtol(param, &end, 10);
147 if(end != param)
148 s->requested_delay = len;
149 }
150
151 s->echo_state.reset(speex_echo_state_init(SAMPLES_PER_FRAME, filter_length));
152
153 s->frame.data = NULL;
154 s->frame.sample_rate = s->frame.bps = 0;
155
156 const int ringbuf_sample_count = 2 << 15; //should be divisable by SAMPLES_PER_FRAME
157 constexpr int bps = 2; //TODO: assuming bps to be 2
158
159 s->far_end_ringbuf.reset(ring_buffer_init(ringbuf_sample_count * bps));
160 s->near_end_ringbuf.reset(ring_buffer_init(ringbuf_sample_count * bps));
161
162 s->frame_data = std::make_unique<spx_int16_t[]>(ringbuf_sample_count);
163 s->frame.data = reinterpret_cast<char *>(s->frame_data.get());
164 s->frame.max_size = ringbuf_sample_count * sizeof(s->frame_data[0]);
165 static_assert(sizeof(s->frame_data[0]) == bps);
166
167 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Echo cancellation initialized with filter length %d samples.\n", filter_length);
168
169 s->prefill = 0;
170
171 return s;
172}
173
175{
176 delete s;
177}
178
180{
181 std::lock_guard lk(s->lock);
182
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");
188 }
189 return;
190 }
191
192 if(s->prefill){
193 int target = std::max(SAMPLES_PER_FRAME, (s->prefill / SAMPLES_PER_FRAME) * SAMPLES_PER_FRAME);
194 int current = ring_get_current_size(s->far_end_ringbuf.get());
195 //buffer can contain small remainder (<SAMPLES_PER_FRAME)
196 int to_fill = target - current;
197 s->prefill = 0;
198 if(to_fill < 0){
199 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Pre fill requested to %d, but the buffer is already %d!\n", target, current);
200 } else {
201 ring_advance_write_idx(s->far_end_ringbuf.get(), to_fill);
202 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Pre filling far end with %d samples\n", to_fill);
203 }
204 }
205
206 size_t samples = frame->data_len / frame->bps;
207 size_t ringbuf_free_samples = ring_get_available_write_size(s->far_end_ringbuf.get()) / 2;
208
209 if(samples > ringbuf_free_samples){
210 samples = ringbuf_free_samples;
211 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Far end ringbuf overflow!\n");
212 }
213
214 if(frame->bps != 2) {
215 void *ptr1;
216 int size1;
217 void *ptr2;
218 int size2;
219 ring_get_write_regions(s->far_end_ringbuf.get(), samples * 2,
220 &ptr1, &size1, &ptr2, &size2);
221
222 assert(size1 % 2 == 0);
223 int in_bytes1 = (size1 / 2) * frame->bps;
224 change_bps(static_cast<char *>(ptr1), 2, frame->data, frame->bps, in_bytes1);
225 if(ptr2){
226 change_bps(static_cast<char *>(ptr2), 2, frame->data + in_bytes1, frame->bps, frame->data_len - in_bytes1);
227 }
228
229 ring_advance_write_idx(s->far_end_ringbuf.get(), samples * 2);
230 } else {
231 ring_buffer_write(s->far_end_ringbuf.get(), frame->data, samples * 2);
232 }
233}
234
236{
237 struct audio_frame *res;
238
239 std::lock_guard lk(s->lock);
240
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");
246 return frame;
247 }
248
249
250 if(frame->sample_rate != s->frame.sample_rate ||
251 frame->bps != s->frame.bps) {
252 reconfigure_echo(s, frame->sample_rate, frame->bps);
253 }
254
255
256 size_t in_frame_samples = frame->data_len / frame->bps;
257
258 size_t ringbuf_free_samples = ring_get_available_write_size(s->near_end_ringbuf.get()) / 2;
259 if(in_frame_samples > ringbuf_free_samples){
260 in_frame_samples = ringbuf_free_samples;
261 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Near end ringbuf overflow\n");
262 }
263
264 if(s->next_expected_near < steady_clock::now()){
265 /* It is possible that the capture thread starts late or
266 * freezes, which could create an unwanted delay between far
267 * and near ends. To partialy protect against this, drop the
268 * contents of far end buffer, when the last frame arrived more
269 * than 1s ago.
270 */
271 auto diff = steady_clock::now() - s->next_expected_near;
272 long long delay = std::chrono::duration_cast<std::chrono::microseconds>(diff).count();
273 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Near samples late by %lldus\n", delay);
274
275 int current = ring_get_current_size(s->far_end_ringbuf.get());
276 //drop only whole frames
277 current = (current / SAMPLES_PER_FRAME) * SAMPLES_PER_FRAME;
278 ring_advance_read_idx(s->far_end_ringbuf.get(), current);
279 }
280 s->next_expected_near = steady_clock::now() + std::chrono::seconds(1);
281
282 if(frame->bps != 2){
283 //Need to change bps, put whole incoming frame into ringbuf
284 void *ptr1;
285 int size1;
286 void *ptr2;
287 int size2;
288 ring_get_write_regions(s->near_end_ringbuf.get(), in_frame_samples * 2,
289 &ptr1, &size1, &ptr2, &size2);
290
291 int in_bytes1 = (size1 / 2) * frame->bps;
292 change_bps(static_cast<char *>(ptr1), 2, frame->data, frame->bps, in_bytes1);
293 if(ptr2){
294 change_bps(static_cast<char *>(ptr2), 2, frame->data + in_bytes1, frame->bps, frame->data_len - in_bytes1);
295 }
296 ring_advance_write_idx(s->near_end_ringbuf.get(), in_frame_samples * 2);
297 } else {
298 ring_buffer_write(s->near_end_ringbuf.get(), frame->data, frame->data_len);
299 }
300
301 size_t near_end_samples = ring_get_current_size(s->near_end_ringbuf.get()) / 2;
302 size_t far_end_samples = ring_get_current_size(s->far_end_ringbuf.get()) / 2;
303
304 if(far_end_samples < near_end_samples){
305 log_msg(LOG_LEVEL_INFO, MOD_NAME "Not enough far end samples (%zu near, %zu far)\n", near_end_samples, far_end_samples);
306
307 //The delay between far end and near end will always be at least
308 //recorded frame length
309 s->prefill = in_frame_samples + s->requested_delay;
310 }
311
312 size_t frames_to_process = near_end_samples / SAMPLES_PER_FRAME;
313 if(!frames_to_process){
314 return NULL;
315 }
316
317 size_t out_size = frames_to_process * SAMPLES_PER_FRAME * 2;
318 assert(static_cast<size_t>(s->frame.max_size) >= out_size);
319 s->frame.data_len = out_size;
320 res = &s->frame;
321
322 spx_int16_t *out_ptr = (spx_int16_t *)(void *) s->frame.data;
323 for(size_t i = 0; i < frames_to_process; i++){
324 spx_int16_t near_arr[SAMPLES_PER_FRAME];
325 spx_int16_t far_arr[SAMPLES_PER_FRAME];
326
327 const void *export_channels[] = {near_arr, far_arr, out_ptr, nullptr};
328 if(far_end_samples >= SAMPLES_PER_FRAME){
329 ring_buffer_read(s->far_end_ringbuf.get(), reinterpret_cast<char *>(far_arr), SAMPLES_PER_FRAME * 2);
330 ring_buffer_read(s->near_end_ringbuf.get(), reinterpret_cast<char *>(near_arr), SAMPLES_PER_FRAME * 2);
331
332 speex_echo_cancellation(s->echo_state.get(), near_arr, far_arr, out_ptr);
333 far_end_samples -= SAMPLES_PER_FRAME;
334
335 } else {
336 ring_buffer_read(s->near_end_ringbuf.get(), reinterpret_cast<char *>(out_ptr), SAMPLES_PER_FRAME * 2);
337 export_channels[0] = out_ptr;
338 export_channels[1] = out_ptr;
339 export_channels[2] = out_ptr;
340 }
341
342 if(s->exporter){
343 audio_export_raw_ch(s->exporter.get(), export_channels, SAMPLES_PER_FRAME);
344 }
345
347 }
348
349 return res;
350}
351
#define UNUSED(x)
int int int sample_rate
struct video_frame * frame
int void size_t * len
struct audio_export * audio_export_init(const char *filename)
Definition export.c:159
void audio_export_destroy(struct audio_export *s)
Definition export.c:182
void audio_export_raw_ch(struct audio_export *s, const void **channels_data, unsigned sample_count)
Definition export.c:262
bool audio_export_configure_raw(struct audio_export *s, int bps, int sample_rate, int ch_count)
Definition export.c:136
Definition misc.h:94
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define error_msg(...)
Definition debug.h:89
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
static void reconfigure_echo(struct echo_cancellation *s, int sample_rate, int bps)
Definition echo.cpp:102
std::chrono::steady_clock steady_clock
Definition echo.cpp:65
struct echo_cancellation * echo_cancellation_init(void)
Definition echo.cpp:132
void echo_play(struct echo_cancellation *s, struct audio_frame *frame)
Definition echo.cpp:179
#define SAMPLES_PER_FRAME
Definition echo.cpp:60
struct audio_frame * echo_cancel(struct echo_cancellation *s, struct audio_frame *frame)
Definition echo.cpp:235
#define TEXTIFY(a)
Definition echo.cpp:122
#define MOD_NAME
Definition echo.cpp:63
steady_clock::duration duration
Definition echo.cpp:67
#define DEFAULT_FILTER_LENGTH
Definition echo.cpp:61
void echo_cancellation_destroy(struct echo_cancellation *s)
Definition echo.cpp:174
steady_clock::time_point time_point
Definition echo.cpp:66
const char * get_commandline_param(const char *key)
Definition host.cpp:894
#define ADD_TO_PARAM(param, doc)
Definition host.h:203
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
const char * param
Definition settings.cpp:195
struct used to comunicate with audio capture/playback device
Definition types.h:99
int sample_rate
Definition types.h:101
char * data
Definition types.h:102
int ch_count
Definition types.h:104
int data_len
Definition types.h:103
int bps
Definition types.h:100
int max_size
Definition types.h:105
time_point next_expected_near
Definition echo.cpp:90
std::unique_ptr< struct audio_export, Export_state_deleter > exporter
Definition echo.cpp:92
int requested_delay
Definition echo.cpp:88
struct audio_frame frame
Definition echo.cpp:86
ring_buffer_uniq far_end_ringbuf
Definition echo.cpp:83
ring_buffer_uniq near_end_ringbuf
Definition echo.cpp:82
std::unique_ptr< spx_int16_t[]> frame_data
Definition echo.cpp:85
std::unique_ptr< SpeexEchoState, Echo_state_deleter > echo_state
Definition echo.cpp:80
std::mutex lock
Definition echo.cpp:94
void change_bps(char *out, int out_bps, const char *in, int in_bps, int in_len)
Definition utils.cpp:258