ec-calibrator.c 10.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 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 36 37
	MSTickerParams params={0};
	params.name="Echo calibrator";
	params.prio=MS_TICKER_PRIO_HIGH;
	ecc->ticker=ms_ticker_new_with_params(&params);
38

39
	ecc->sndread=ms_snd_card_create_reader(ecc->capt_card);
40
	ms_filter_call_method(ecc->sndread,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
41
	ms_filter_call_method(ecc->sndread,MS_FILTER_GET_SAMPLE_RATE,&rate);
42 43
	ms_filter_call_method(ecc->sndread,MS_FILTER_SET_NCHANNELS,&ecc_channels);
	ms_filter_call_method(ecc->sndread,MS_FILTER_GET_NCHANNELS,&channels);
44
	ecc->read_resampler=ms_factory_create_filter(ecc->factory, MS_RESAMPLE_ID);
45 46
	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);
47 48
	ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_NCHANNELS,&ecc_channels);
	ms_filter_call_method(ecc->read_resampler,MS_FILTER_SET_OUTPUT_NCHANNELS,&channels);
49 50
	
	
51
	ecc->det=ms_factory_create_filter(ecc->factory, MS_TONE_DETECTOR_ID);
52
	ms_filter_call_method(ecc->det,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
53
	ecc->rec=ms_factory_create_filter(ecc->factory, MS_VOID_SINK_ID);
54

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

59 60
	ecc->play=ms_factory_create_filter(ecc->factory, MS_VOID_SOURCE_ID);
	ecc->gen=ms_factory_create_filter(ecc->factory, MS_DTMF_GEN_ID);
61
	ms_filter_call_method(ecc->gen,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
62
	ecc->write_resampler=ms_factory_create_filter(ecc->factory, MS_RESAMPLE_ID);
63
	ecc->sndwrite=ms_snd_card_create_writer(ecc->play_card);
64
	
65
	ms_filter_call_method(ecc->sndwrite,MS_FILTER_SET_SAMPLE_RATE,&ecc->rate);
jehan's avatar
jehan committed
66
	ms_filter_call_method(ecc->sndwrite,MS_FILTER_GET_SAMPLE_RATE,&rate);
67 68
	ms_filter_call_method(ecc->sndwrite,MS_FILTER_SET_NCHANNELS,&ecc_channels);
	ms_filter_call_method(ecc->sndwrite,MS_FILTER_GET_NCHANNELS,&channels);
69 70
	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);
71 72
	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);
73 74 75 76

	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);
77 78

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

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

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

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

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

98 99
	ms_filter_unlink(ecc->sndread,0,ecc->read_resampler,0);
	ms_filter_unlink(ecc->read_resampler,0,ecc->det,0);
100 101 102 103 104 105 106
	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);
107 108
	ms_filter_destroy(ecc->read_resampler);
	ms_filter_destroy(ecc->write_resampler);
109 110 111 112 113 114 115 116
	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;
117 118 119 120
	if (ev->tone_name[0] != '\0'){
		ecc->acc-=ev->tone_start_time;
		ms_message("Sent tone at %u",(unsigned int)ev->tone_start_time);
	}
121 122
}

Simon Morlat's avatar
Simon Morlat committed
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
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;
}

143 144 145
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
146 147 148 149
	if (is_valid_tone(ecc,ev)){
		ecc->acc+=ev->tone_start_time;
		ms_message("Received tone at %u",(unsigned int)ev->tone_start_time);
	}
150 151 152 153 154
}

static void ecc_play_tones(EcCalibrator *ecc){
	MSDtmfGenCustomTone tone;
	MSToneDetectorDef expected_tone;
155 156 157
	
	memset(&tone,0,sizeof(tone));
	memset(&expected_tone,0,sizeof(expected_tone));
158

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

Simon Morlat's avatar
Simon Morlat committed
161 162 163
	/* configure the tones to be scanned */
	
	strncpy(expected_tone.tone_name,"freq1",sizeof(expected_tone.tone_name));
Simon Morlat's avatar
Simon Morlat committed
164
	expected_tone.frequency=(int)2349.32;
165
	expected_tone.min_duration=40;
166
	expected_tone.min_amplitude=0.1f;
167 168 169

	ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone);
	
Simon Morlat's avatar
Simon Morlat committed
170
	strncpy(expected_tone.tone_name,"freq2",sizeof(expected_tone.tone_name));
Simon Morlat's avatar
Simon Morlat committed
171
	expected_tone.frequency=(int)2637.02;
Simon Morlat's avatar
Simon Morlat committed
172
	expected_tone.min_duration=40;
173
	expected_tone.min_amplitude=0.1f;
174

Simon Morlat's avatar
Simon Morlat committed
175 176 177
	ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone);
	
	strncpy(expected_tone.tone_name,"freq3",sizeof(expected_tone.tone_name));
Simon Morlat's avatar
Simon Morlat committed
178
	expected_tone.frequency=(int)2093;
Simon Morlat's avatar
Simon Morlat committed
179
	expected_tone.min_duration=40;
180
	expected_tone.min_amplitude=0.1f;
Simon Morlat's avatar
Simon Morlat committed
181 182 183

	ms_filter_call_method (ecc->det,MS_TONE_DETECTOR_ADD_SCAN,&expected_tone);
	
184
	/*play an initial tone to startup the audio playback/capture*/
Simon Morlat's avatar
Simon Morlat committed
185
	
186
	tone.frequencies[0]=140;
Simon Morlat's avatar
Simon Morlat committed
187 188 189
	tone.duration=1000;
	tone.amplitude=0.5;

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

193
	ms_filter_add_notify_callback(ecc->gen,on_tone_sent,ecc,TRUE);
Simon Morlat's avatar
Simon Morlat committed
194 195 196 197
	
	/* play the three tones*/
	
	
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
	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);
		
		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);
		
		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);
		
		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);
		
		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);
	}
235 236
	
	/*these two next ones are for lyrism*/
237 238 239 240 241 242 243 244 245 246 247 248
	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);
		
		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);
	}
249
	
Simon Morlat's avatar
Simon Morlat committed
250
	ms_sleep(1);
Simon Morlat's avatar
Simon Morlat committed
251 252
	
	if (ecc->freq1 && ecc->freq2 && ecc->freq3) {
253
		int delay=(int)(ecc->acc/3);
Simon Morlat's avatar
Simon Morlat committed
254 255 256 257 258 259 260 261 262
		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) {
263 264
			ms_message("Echo calibration succeeded, no echo has been detected");
			ecc->status = LinphoneEcCalibratorDoneNoEcho;
Simon Morlat's avatar
Simon Morlat committed
265
	} else {
266
			ecc->status = LinphoneEcCalibratorFailed;
267
	}
Simon Morlat's avatar
Simon Morlat committed
268

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

static void  * ecc_thread(void *p){
	EcCalibrator *ecc=(EcCalibrator*)p;
	
	ecc_init_filters(ecc);
	ecc_play_tones(ecc);
	ecc_deinit_filters(ecc);
jehan's avatar
jehan committed
280
	ms_thread_exit(NULL);
281 282 283
	return NULL;
}

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

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

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

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

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

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

316 317 318 319
	if (lc->ecc!=NULL){
		ms_error("Echo calibration is still on going !");
		return -1;
	}
320
	rate = lp_config_get_int(lc->config,"sound","echo_cancellation_rate",8000);
321
	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);
322 323
	lc->ecc->play_cool_tones = lp_config_get_int(lc->config, "sound", "ec_calibrator_cool_tones", 0);
	ec_calibrator_start(lc->ecc);
324 325
	return 0;
}
326