UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
echo.cpp File Reference
#include "audio/utils.h"
#include "audio/export.h"
#include "debug.h"
#include "echo.h"
#include <speex/speex_echo.h>
#include <stdlib.h>
#include <mutex>
#include <memory>
#include <algorithm>
#include <chrono>
#include "utils/ring_buffer.h"
#include "host.h"
Include dependency graph for echo.cpp:

Go to the source code of this file.

Classes

struct  echo_cancellation
 

Macros

#define DEFAULT_FILTER_LENGTH   (48 * 500)
 
#define MOD_NAME   "[Echo cancel] "
 
#define SAMPLES_PER_FRAME   (1 << 9)
 
#define TEXTIFY(a)
 
#define TEXTIFY2(a)
 

Typedefs

using duration = steady_clock::duration
 
using steady_clock = std::chrono::steady_clock
 
using time_point = steady_clock::time_point
 

Functions

 ADD_TO_PARAM ("echo-cancel-delay", "* echo-cancel-delay=<samples>\n" " Echo cancellation additional delay added to far end in samples, should be slightly less than output device latency.\n")
 
 ADD_TO_PARAM ("echo-cancel-dump-audio", "* echo-cancel-dump-audio\n" " Dump near end, far end and output samples in separate channels to a wav file.\n")
 
 ADD_TO_PARAM ("echo-cancel-filter-length", "* echo-cancel-filter-length=<samples>\n" " Echo cancellation filter length in samples, should be the third of the room's impulse response length. (default " TEXTIFY(DEFAULT_FILTER_LENGTH) ").\n")
 
struct audio_frame * echo_cancel (struct echo_cancellation *s, struct audio_frame *frame)
 
void echo_cancellation_destroy (struct echo_cancellation *s)
 
struct echo_cancellation * echo_cancellation_init (void)
 
void echo_play (struct echo_cancellation *s, struct audio_frame *frame)
 
static void reconfigure_echo (struct echo_cancellation *s, int sample_rate, int bps)
 

Detailed Description

Macro Definition Documentation

◆ DEFAULT_FILTER_LENGTH

#define DEFAULT_FILTER_LENGTH   (48 * 500)

Definition at line 61 of file echo.cpp.

Referenced by echo_cancellation_init().

◆ MOD_NAME

#define MOD_NAME   "[Echo cancel] "

Definition at line 63 of file echo.cpp.

Referenced by echo_cancel(), echo_cancellation_init(), and echo_play().

◆ SAMPLES_PER_FRAME

#define SAMPLES_PER_FRAME   (1 << 9)

Definition at line 60 of file echo.cpp.

Referenced by echo_cancel(), echo_cancellation_init(), and echo_play().

◆ TEXTIFY

#define TEXTIFY ( a)
Value:
#define TEXTIFY2(a)
Definition echo.cpp:123

Definition at line 122 of file echo.cpp.

◆ TEXTIFY2

#define TEXTIFY2 ( a)
Value:
#a

Definition at line 123 of file echo.cpp.

Typedef Documentation

◆ duration

using duration = steady_clock::duration

Definition at line 67 of file echo.cpp.

◆ steady_clock

using steady_clock = std::chrono::steady_clock

Definition at line 65 of file echo.cpp.

◆ time_point

using time_point = steady_clock::time_point

Definition at line 66 of file echo.cpp.

Function Documentation

◆ ADD_TO_PARAM() [1/3]

ADD_TO_PARAM ( "echo-cancel-delay" ,
"* echo-cancel- delay = <samples>\n" "  Echo cancellation additional delay added to far end in samples,
should be slightly less than output device latency.\n"  )

◆ ADD_TO_PARAM() [2/3]

ADD_TO_PARAM ( "echo-cancel-dump-audio" ,
"* echo-cancel-dump-audio\n" " Dump near end,
far end and output samples in separate channels to a wav file.\n"  )

◆ ADD_TO_PARAM() [3/3]

ADD_TO_PARAM ( "echo-cancel-filter-length" ,
"* echo-cancel-filter- length = <samples>\n" "  Echo cancellation filter length in samples,
should be the third of the room 's impulse response length.(default " TEXTIFY(DEFAULT_FILTER_LENGTH) ").\n"  )

◆ echo_cancel()

struct audio_frame * echo_cancel ( struct echo_cancellation * s,
struct audio_frame * frame )

Definition at line 235 of file echo.cpp.

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}
struct video_frame * frame
void audio_export_raw_ch(struct audio_export *s, const void **channels_data, unsigned sample_count)
Definition export.c:262
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_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
#define SAMPLES_PER_FRAME
Definition echo.cpp:60
#define MOD_NAME
Definition echo.cpp:63
int ring_get_current_size(struct ring_buffer *ring)
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)
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)
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 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< 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

References audio_export_raw_ch(), audio_frame::bps, change_bps(), audio_frame::data, audio_frame::data_len, echo_cancellation::echo_state, error_msg, echo_cancellation::exporter, echo_cancellation::far_end_ringbuf, echo_cancellation::frame, frame, echo_cancellation::lock, LOG_LEVEL_INFO, LOG_LEVEL_WARNING, log_msg(), audio_frame::max_size, MOD_NAME, echo_cancellation::near_end_ringbuf, echo_cancellation::next_expected_near, echo_cancellation::prefill, reconfigure_echo(), echo_cancellation::requested_delay, ring_advance_read_idx(), ring_advance_write_idx(), ring_buffer_read(), ring_buffer_write(), ring_get_available_write_size(), ring_get_current_size(), ring_get_write_regions(), audio_frame::sample_rate, and SAMPLES_PER_FRAME.

Referenced by audio_sender_thread().

Here is the call graph for this function:

◆ echo_cancellation_destroy()

void echo_cancellation_destroy ( struct echo_cancellation * s)

Definition at line 174 of file echo.cpp.

175{
176 delete s;
177}

◆ echo_cancellation_init()

struct echo_cancellation * echo_cancellation_init ( void )

Definition at line 132 of file echo.cpp.

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}
int void size_t * len
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define DEFAULT_FILTER_LENGTH
Definition echo.cpp:61
const char * get_commandline_param(const char *key)
Definition host.cpp:894
struct ring_buffer * ring_buffer_init(int size)
const char * param
Definition settings.cpp:195
std::unique_ptr< spx_int16_t[]> frame_data
Definition echo.cpp:85

References audio_frame::bps, audio_frame::data, DEFAULT_FILTER_LENGTH, echo_cancellation::echo_state, echo_cancellation::far_end_ringbuf, echo_cancellation::frame, echo_cancellation::frame_data, get_commandline_param(), len, LOG_LEVEL_NOTICE, log_msg(), audio_frame::max_size, MOD_NAME, echo_cancellation::near_end_ringbuf, param, echo_cancellation::prefill, echo_cancellation::requested_delay, ring_buffer_init(), audio_frame::sample_rate, and SAMPLES_PER_FRAME.

Referenced by audio_init().

Here is the call graph for this function:

◆ echo_play()

void echo_play ( struct echo_cancellation * s,
struct audio_frame * frame )

Definition at line 179 of file echo.cpp.

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}

References change_bps(), error_msg, echo_cancellation::far_end_ringbuf, frame, echo_cancellation::lock, LOG_LEVEL_NOTICE, LOG_LEVEL_WARNING, log_msg(), MOD_NAME, echo_cancellation::prefill, ring_advance_write_idx(), ring_buffer_write(), ring_get_available_write_size(), ring_get_current_size(), ring_get_write_regions(), and SAMPLES_PER_FRAME.

Referenced by audio_receiver_thread().

Here is the call graph for this function:

◆ reconfigure_echo()

static void reconfigure_echo ( struct echo_cancellation * s,
int sample_rate,
int bps )
static

Definition at line 102 of file echo.cpp.

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}
#define UNUSED(x)
int int int sample_rate
struct audio_export * audio_export_init(const char *filename)
Definition export.c:159
bool audio_export_configure_raw(struct audio_export *s, int bps, int sample_rate, int ch_count)
Definition export.c:136
void ring_buffer_flush(struct ring_buffer *buf)
int ch_count
Definition types.h:104

References audio_export_configure_raw(), audio_export_init(), audio_frame::bps, audio_frame::ch_count, echo_cancellation::echo_state, echo_cancellation::exporter, echo_cancellation::far_end_ringbuf, echo_cancellation::frame, get_commandline_param(), echo_cancellation::near_end_ringbuf, ring_buffer_flush(), audio_frame::sample_rate, sample_rate, and UNUSED.

Referenced by echo_cancel().

Here is the call graph for this function: