ec-calibrator.c 12.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
linphone
Copyright (C) 2011 Belledonne Communications SARL
Author: Simon MORLAT (simon.morlat@linphone.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 20 21 22 23 24 25
*/

#include "private.h"

#include "mediastreamer2/mstonedetector.h"
#include "mediastreamer2/dtmfgen.h"

26
#include "linphone/lpconfig.h"
27
#include "c-wrapper/c-wrapper.h"
28 29 30


static void ecc_init_filters(EcCalibrator *ecc){
31
	unsigned int rate;
32 33
	int channels = 1;
	int ecc_channels = 1;
34 35
	MSTickerParams params;
	memset(&params, 0, sizeof(params));
36 37 38
	params.name="Echo calibrator";
	params.prio=MS_TICKER_PRIO_HIGH;
	ecc->ticker=ms_ticker_new_with_params(&params);
39

40
	ecc->sndread=ms_snd_card_create_reader(ecc->capt_card);
41
	ms_filter_call_method(ecc->sndread,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
42
	ms_filter_call_method(ecc->sndread,MS_FILTER_GET_SAMPLE_RATE,&rate);
43 44
	ms_filter_call_method(ecc->sndread,MS_FILTER_SET_NCHANNELS,&ecc_channels);
	ms_filter_call_method(ecc->sndread,MS_FILTER_GET_NCHANNELS,&channels);
45
	ecc->read_resampler=ms_factory_create_filter(ecc->factory, MS_RESAMPLE_ID);
46 47
	ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_SAMPLE_RATE,&rate);
	ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&ecc->rate);
48 49
	ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_NCHANNELS,&channels);
	ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&ecc_channels);
50 51


52
	ecc->det=ms_factory_create_filter(ecc->factory, MS_TONE_DETECTOR_ID);
53
	ms_filter_call_method(ecc->det,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
54
	ecc->rec=ms_factory_create_filter(ecc->factory, MS_VOID_SINK_ID);
55

56 57
	ms_filter_link(ecc->sndread,0,ecc->read_resampler,0);
	ms_filter_link(ecc->read_resampler,0,ecc->det,0);
58 59
	ms_filter_link(ecc->det,0,ecc->rec,0);

60 61
	ecc->play=ms_factory_create_filter(ecc->factory, MS_VOID_SOURCE_ID);
	ecc->gen=ms_factory_create_filter(ecc->factory, MS_DTMF_GEN_ID);
62
	ms_filter_call_method(ecc->gen,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
63
	ecc->write_resampler=ms_factory_create_filter(ecc->factory, MS_RESAMPLE_ID);
64
	ecc->sndwrite=ms_snd_card_create_writer(ecc->play_card);
65

66
	ms_filter_call_method(ecc->sndwrite,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
jehan's avatar
jehan committed
67
	ms_filter_call_method(ecc->sndwrite,MS_FILTER_GET_SAMPLE_RATE,&rate);
68 69
	ms_filter_call_method(ecc->sndwrite,MS_FILTER_SET_NCHANNELS,&ecc_channels);
	ms_filter_call_method(ecc->sndwrite,MS_FILTER_GET_NCHANNELS,&channels);
70 71
	ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
	ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&rate);
72 73
	ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_NCHANNELS,&ecc_channels);
	ms_filter_call_method(ecc->write_resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&channels);
74 75 76 77

	ms_filter_link(ecc->play,0,ecc->gen,0);
	ms_filter_link(ecc->gen,0,ecc->write_resampler,0);
	ms_filter_link(ecc->write_resampler,0,ecc->sndwrite,0);
78 79

	ms_ticker_attach(ecc->ticker,ecc->sndread);
80
	ms_ticker_attach(ecc->ticker,ecc->play);
81 82 83 84

	if (ecc->audio_init_cb != NULL) {
		(*ecc->audio_init_cb)(ecc->cb_data);
	}
85 86 87
}

static void ecc_deinit_filters(EcCalibrator *ecc){
88 89 90 91
	if (ecc->audio_uninit_cb != NULL) {
		(*ecc->audio_uninit_cb)(ecc->cb_data);
	}

92
	ms_ticker_detach(ecc->ticker,ecc->sndread);
93
	ms_ticker_detach(ecc->ticker,ecc->play);
94 95

	ms_filter_unlink(ecc->play,0,ecc->gen,0);
96 97
	ms_filter_unlink(ecc->gen,0,ecc->write_resampler,0);
	ms_filter_unlink(ecc->write_resampler,0,ecc->sndwrite,0);
98

99 100
	ms_filter_unlink(ecc->sndread,0,ecc->read_resampler,0);
	ms_filter_unlink(ecc->read_resampler,0,ecc->det,0);
101 102 103 104 105 106 107
	ms_filter_unlink(ecc->det,0,ecc->rec,0);

	ms_filter_destroy(ecc->sndread);
	ms_filter_destroy(ecc->det);
	ms_filter_destroy(ecc->rec);
	ms_filter_destroy(ecc->play);
	ms_filter_destroy(ecc->gen);
108 109
	ms_filter_destroy(ecc->read_resampler);
	ms_filter_destroy(ecc->write_resampler);
110 111 112 113 114 115 116 117
	ms_filter_destroy(ecc->sndwrite);

	ms_ticker_destroy(ecc->ticker);
}

static void on_tone_sent(void *data, MSFilter *f, unsigned int event_id, void *arg){
	MSDtmfGenEvent *ev=(MSDtmfGenEvent*)arg;
	EcCalibrator *ecc=(EcCalibrator*)data;
118 119 120 121
	if (ev->tone_name[0] != '\0'){
		ecc->acc-=ev->tone_start_time;
		ms_message("Sent tone at %u",(unsigned int)ev->tone_start_time);
	}
122 123
}

Simon Morlat's avatar
Simon Morlat committed
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
static bool_t is_valid_tone(EcCalibrator *ecc, MSToneDetectorEvent *ev){
	bool_t *toneflag=NULL;
	if (strcmp(ev->tone_name,"freq1")==0){
		toneflag=&ecc->freq1;
	}else if (strcmp(ev->tone_name,"freq2")==0){
		toneflag=&ecc->freq2;
	}else if (strcmp(ev->tone_name,"freq3")==0){
		toneflag=&ecc->freq3;
	}else{
		ms_error("Calibrator bug.");
		return FALSE;
	}
	if (*toneflag){
		ms_message("Duplicated tone event, ignored.");
		return FALSE;
	}
	*toneflag=TRUE;
	return TRUE;
}

144 145 146
static void on_tone_received(void *data, MSFilter *f, unsigned int event_id, void *arg){
	MSToneDetectorEvent *ev=(MSToneDetectorEvent*)arg;
	EcCalibrator *ecc=(EcCalibrator*)data;
Simon Morlat's avatar
Simon Morlat committed
147 148 149 150
	if (is_valid_tone(ecc,ev)){
		ecc->acc+=ev->tone_start_time;
		ms_message("Received tone at %u",(unsigned int)ev->tone_start_time);
	}
151 152 153 154 155
}

static void ecc_play_tones(EcCalibrator *ecc){
	MSDtmfGenCustomTone tone;
	MSToneDetectorDef expected_tone;
156

157 158
	memset(&tone,0,sizeof(tone));
	memset(&expected_tone,0,sizeof(expected_tone));
159

160
	ms_filter_add_notify_callback(ecc->det,on_tone_received,ecc,TRUE);
161

Simon Morlat's avatar
Simon Morlat committed
162
	/* configure the tones to be scanned */
163

Simon Morlat's avatar
Simon Morlat committed
164
	strncpy(expected_tone.tone_name,"freq1",sizeof(expected_tone.tone_name));
Simon Morlat's avatar
Simon Morlat committed
165
	expected_tone.frequency=(int)2349.32;
166
	expected_tone.min_duration=40;
167
	expected_tone.min_amplitude=0.1f;
168 169

	ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone);
170

Simon Morlat's avatar
Simon Morlat committed
171
	strncpy(expected_tone.tone_name,"freq2",sizeof(expected_tone.tone_name));
Simon Morlat's avatar
Simon Morlat committed
172
	expected_tone.frequency=(int)2637.02;
Simon Morlat's avatar
Simon Morlat committed
173
	expected_tone.min_duration=40;
174
	expected_tone.min_amplitude=0.1f;
175

Simon Morlat's avatar
Simon Morlat committed
176
	ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone);
177

Simon Morlat's avatar
Simon Morlat committed
178
	strncpy(expected_tone.tone_name,"freq3",sizeof(expected_tone.tone_name));
Simon Morlat's avatar
Simon Morlat committed
179
	expected_tone.frequency=(int)2093;
Simon Morlat's avatar
Simon Morlat committed
180
	expected_tone.min_duration=40;
181
	expected_tone.min_amplitude=0.1f;
Simon Morlat's avatar
Simon Morlat committed
182 183

	ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone);
184

185
	/*play an initial tone to startup the audio playback/capture*/
186

187
	tone.frequencies[0]=140;
Simon Morlat's avatar
Simon Morlat committed
188 189 190
	tone.duration=1000;
	tone.amplitude=0.5;

191
	ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
Simon Morlat's avatar
Simon Morlat committed
192
	ms_sleep(2);
193

194
	ms_filter_add_notify_callback(ecc->gen,on_tone_sent,ecc,TRUE);
195

Simon Morlat's avatar
Simon Morlat committed
196
	/* play the three tones*/
197 198


199 200 201 202 203 204
	if (ecc->play_cool_tones){
		strncpy(tone.tone_name, "D", sizeof(tone.tone_name));
		tone.frequencies[0]=(int)2349.32;
		tone.duration=100;
		ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
		ms_usleep(300000);
205

206 207 208 209 210
		strncpy(tone.tone_name, "E", sizeof(tone.tone_name));
		tone.frequencies[0]=(int)2637.02;
		tone.duration=100;
		ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
		ms_usleep(300000);
211

212 213 214 215 216 217 218 219 220 221 222
		strncpy(tone.tone_name, "C", sizeof(tone.tone_name));
		tone.frequencies[0]=(int)2093;
		tone.duration=100;
		ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
		ms_usleep(300000);
	}else{
		strncpy(tone.tone_name, "C", sizeof(tone.tone_name));
		tone.frequencies[0]=(int)2093;
		tone.duration=100;
		ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
		ms_usleep(300000);
223

224 225 226 227 228
		strncpy(tone.tone_name, "D", sizeof(tone.tone_name));
		tone.frequencies[0]=(int)2349.32;
		tone.duration=100;
		ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
		ms_usleep(300000);
229

230 231 232 233 234 235
		strncpy(tone.tone_name, "E", sizeof(tone.tone_name));
		tone.frequencies[0]=(int)2637.02;
		tone.duration=100;
		ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
		ms_usleep(300000);
	}
236

237
	/*these two next ones are for lyrism*/
238 239 240 241 242 243
	if (ecc->play_cool_tones){
		tone.tone_name[0]='\0';
		tone.frequencies[0]=(int)1046.5;
		tone.duration=400;
		ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
		ms_usleep(300000);
244

245 246 247 248 249
		tone.tone_name[0]='\0';
		tone.frequencies[0]=(int)1567.98;
		tone.duration=400;
		ms_filter_call_method(ecc->gen,MS_DTMF_GEN_PLAY_CUSTOM,&tone);
	}
250

Simon Morlat's avatar
Simon Morlat committed
251
	ms_sleep(1);
252

Simon Morlat's avatar
Simon Morlat committed
253
	if (ecc->freq1 && ecc->freq2 && ecc->freq3) {
254
		int delay=(int)(ecc->acc/3);
Simon Morlat's avatar
Simon Morlat committed
255 256 257 258 259 260 261 262 263
		if (delay<0){
			ms_error("Quite surprising calibration result, delay=%i",delay);
			ecc->status=LinphoneEcCalibratorFailed;
		}else{
			ms_message("Echo calibration estimated delay to be %i ms",delay);
			ecc->delay=delay;
			ecc->status=LinphoneEcCalibratorDone;
		}
	} else if ((ecc->freq1 || ecc->freq2 || ecc->freq3)==FALSE) {
264 265
			ms_message("Echo calibration succeeded, no echo has been detected");
			ecc->status = LinphoneEcCalibratorDoneNoEcho;
Simon Morlat's avatar
Simon Morlat committed
266
	} else {
267
			ecc->status = LinphoneEcCalibratorFailed;
268
	}
Simon Morlat's avatar
Simon Morlat committed
269

270
	if (ecc->status == LinphoneEcCalibratorFailed) {
Simon Morlat's avatar
Simon Morlat committed
271
		ms_error("Echo calibration failed.");
272
	}
273 274 275 276
}

static void  * ecc_thread(void *p){
	EcCalibrator *ecc=(EcCalibrator*)p;
277

278 279 280
	ecc_init_filters(ecc);
	ecc_play_tones(ecc);
	ecc_deinit_filters(ecc);
jehan's avatar
jehan committed
281
	ms_thread_exit(NULL);
282 283 284
	return NULL;
}

285
EcCalibrator * ec_calibrator_new(MSFactory *factory, MSSndCard *play_card, MSSndCard *capt_card, unsigned int rate, LinphoneEcCalibrationCallback cb,
286
				 LinphoneEcCalibrationAudioInit audio_init_cb, LinphoneEcCalibrationAudioUninit audio_uninit_cb, void *cb_data){
287 288
	EcCalibrator *ecc=ms_new0(EcCalibrator,1);

289
	ecc->rate=rate;
290 291
	ecc->cb=cb;
	ecc->cb_data=cb_data;
292 293
	ecc->audio_init_cb=audio_init_cb;
	ecc->audio_uninit_cb=audio_uninit_cb;
294 295
	ecc->capt_card=capt_card;
	ecc->play_card=play_card;
296
	ecc->factory=factory;
297 298 299
	return ecc;
}

300 301 302 303
void ec_calibrator_start(EcCalibrator *ecc){
	ms_thread_create(&ecc->thread,NULL,ecc_thread,ecc);
}

304 305 306 307 308
LinphoneEcCalibratorStatus ec_calibrator_get_status(EcCalibrator *ecc){
	return ecc->status;
}

void ec_calibrator_destroy(EcCalibrator *ecc){
309
	if (ecc->thread != 0) ms_thread_join(ecc->thread,NULL);
310 311 312
	ms_free(ecc);
}

313 314
int linphone_core_start_echo_calibration(LinphoneCore *lc, LinphoneEcCalibrationCallback cb,
					 LinphoneEcCalibrationAudioInit audio_init_cb, LinphoneEcCalibrationAudioUninit audio_uninit_cb, void *cb_data){
315 316
	unsigned int rate;

317 318 319 320
	if (lc->ecc!=NULL){
		ms_error("Echo calibration is still on going !");
		return -1;
	}
Benjamin REIS's avatar
Benjamin REIS committed
321
	rate = (unsigned int)lp_config_get_int(lc->config,"sound","echo_cancellation_rate",8000);
322
	lc->ecc=ec_calibrator_new(lc->factory, lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard,rate,cb,audio_init_cb,audio_uninit_cb,cb_data);
323
	lc->ecc->play_cool_tones = !!lp_config_get_int(lc->config, "sound", "ec_calibrator_cool_tones", 0);
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
	ec_calibrator_start(lc->ecc);
	return 0;
}

static void _ec_calibration_result_cb(LinphoneCore *lc, LinphoneEcCalibratorStatus status, int delay_ms, void *user_data) {
	linphone_core_notify_ec_calibration_result(lc, status, delay_ms);
}

static void _ec_calibration_audio_init_cb(void *user_data) {
	LinphoneCore *lc = (LinphoneCore *)user_data;
	linphone_core_notify_ec_calibration_audio_init(lc);
}

static void _ec_calibration_audio_uninit_cb(void *user_data) {
	LinphoneCore *lc = (LinphoneCore *)user_data;
	linphone_core_notify_ec_calibration_audio_uninit(lc);
}

LinphoneStatus linphone_core_start_echo_canceller_calibration(LinphoneCore *lc) {
	unsigned int rate;
	
	if (lc->ecc!=NULL){
		ms_error("Echo calibration is still on going !");
		return -1;
	}
	rate = (unsigned int)lp_config_get_int(lc->config,"sound","echo_cancellation_rate",8000);
	lc->ecc=ec_calibrator_new(lc->factory, lc->sound_conf.play_sndcard, lc->sound_conf.capt_sndcard, rate,
		_ec_calibration_result_cb,
		_ec_calibration_audio_init_cb,
		_ec_calibration_audio_uninit_cb, lc);
	lc->ecc->play_cool_tones = !!lp_config_get_int(lc->config, "sound", "ec_calibrator_cool_tones", 0);
355
	ec_calibrator_start(lc->ecc);
356 357
	return 0;
}
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376

bool_t linphone_core_has_builtin_echo_canceller(LinphoneCore *lc) {
    MSFactory * factory = linphone_core_get_ms_factory(lc);
	MSDevicesInfo *devices = ms_factory_get_devices_info(factory);
	SoundDeviceDescription *sound_description = ms_devices_info_get_sound_device_description(devices);
	if (sound_description == NULL) return FALSE;
	if (sound_description->flags & DEVICE_HAS_BUILTIN_AEC) return TRUE;
	return FALSE;
}

bool_t linphone_core_is_echo_canceller_calibration_required(LinphoneCore *lc) {
    MSFactory * factory = linphone_core_get_ms_factory(lc);
	MSDevicesInfo *devices = ms_factory_get_devices_info(factory);
	SoundDeviceDescription *sound_description = ms_devices_info_get_sound_device_description(devices);
	if (sound_description == NULL) return TRUE;
	if (sound_description->flags & DEVICE_HAS_BUILTIN_AEC) return FALSE;
	if (sound_description->delay != 0) return FALSE;
	return TRUE;
}