diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c
index 36e1ac520d5976bc8d98f9611b1dff41301773ae..617c2baa9e4713b6d5ff1dbce69400e18e053619 100644
--- a/coreapi/linphonecore.c
+++ b/coreapi/linphonecore.c
@@ -1319,7 +1319,7 @@ static void sound_config_read(LinphoneCore *lc) {
 	/*just parse requested stream feature once at start to print out eventual errors*/
 	linphone_core_get_audio_features(lc);
 
-	_linphone_core_set_tone(lc,LinphoneReasonBusy,LinphoneToneBusy,NULL);
+	L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->setTone(LinphoneReasonBusy, LinphoneToneBusy, NULL);
 }
 
 static int _linphone_core_tls_postcheck_callback(void *data, const bctbx_x509_certificate_t *peer_cert){
@@ -2438,7 +2438,6 @@ static void linphone_core_init(LinphoneCore * lc, LinphoneCoreCbs *cbs, LpConfig
 	lc->is_unreffing = FALSE;
 	lc->config=lp_config_ref(config);
 	lc->data=userdata;
-	lc->ringstream_autorelease=TRUE;
 
 	// We need the Sal on the Android platform helper init
 	lc->sal=new Sal(NULL);
@@ -3580,19 +3579,6 @@ void linphone_core_iterate(LinphoneCore *lc){
 		lc_callback_obj_invoke(&lc->preview_finished_cb,lc);
 	}
 
-	if (lc->ringstream && lc->ringstream_autorelease && lc->dmfs_playing_start_time!=0
-		&& (curtime_ms/1000 - (uint64_t)lc->dmfs_playing_start_time)>5){
-		MSPlayerState state;
-		bool_t stop=TRUE;
-		if (lc->ringstream->source && ms_filter_call_method(lc->ringstream->source,MS_PLAYER_GET_STATE,&state)==0){
-			if (state==MSPlayerPlaying) stop=FALSE;
-		}
-		if (stop) {
-			ms_message("Releasing inactive tone player.");
-			linphone_core_stop_dtmf_stream(lc);
-		}
-	}
-
 	lc->sal->iterate();
 	if (lc->msevq) ms_event_queue_pump(lc->msevq);
 	if (linphone_core_get_global_state(lc) == LinphoneGlobalConfiguring)
@@ -3983,25 +3969,6 @@ bool_t linphone_core_incompatible_security(LinphoneCore *lc, SalMediaDescription
 	return linphone_core_is_media_encryption_mandatory(lc) && linphone_core_get_media_encryption(lc)==LinphoneMediaEncryptionSRTP && !sal_media_description_has_srtp(md);
 }
 
-void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call){
-	/* Play the ring if this is the only call*/
-	if (linphone_core_get_calls_nb(lc)==1){
-		MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard;
-		L_GET_PRIVATE_FROM_C_OBJECT(lc)->setCurrentCall(L_GET_CPP_PTR_FROM_C_OBJECT(call));
-		if (lc->ringstream && lc->dmfs_playing_start_time!=0){
-			linphone_core_stop_dtmf_stream(lc);
-		}
-		if (ringcard) {
-			ms_snd_card_set_stream_type(ringcard, MS_SND_CARD_STREAM_RING);
-			linphone_ringtoneplayer_start(lc->factory, lc->ringtoneplayer, ringcard, lc->sound_conf.local_ring, 2000);
-		}
-	}else{
-		/* else play a tone within the context of the current call */
-		L_GET_PRIVATE_FROM_C_OBJECT(call)->setRingingBeep(true);
-		linphone_core_play_named_tone(lc,LinphoneToneCallWaiting);
-	}
-}
-
 LinphoneStatus linphone_core_accept_early_media_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params) {
 	return linphone_call_accept_early_media_with_params(call, params);
 }
@@ -4112,9 +4079,6 @@ int linphone_core_preempt_sound_resources(LinphoneCore *lc){
 		ms_message("Pausing automatically the current call.");
 		err = L_GET_CPP_PTR_FROM_C_OBJECT(current_call)->pause();
 	}
-	if (lc->ringstream){
-		linphone_core_stop_ringing(lc);
-	}
 	return err;
 }
 
@@ -6029,152 +5993,16 @@ void linphone_core_set_record_file(LinphoneCore *lc, const char *file){
 	}
 }
 
-typedef enum{
-	LinphoneToneGenerator,
-	LinphoneLocalPlayer
-}LinphoneAudioResourceType;
-
-static MSFilter *get_audio_resource(LinphoneCore *lc, LinphoneAudioResourceType rtype, MSSndCard *card, bool_t create) {
-	LinphoneCall *call=linphone_core_get_current_call(lc);
-	AudioStream *stream=NULL;
-	RingStream *ringstream;
-	if (call){
-		stream=reinterpret_cast<AudioStream *>(linphone_call_get_stream(call, LinphoneStreamTypeAudio));
-	}else if (linphone_core_is_in_conference(lc)){
-		stream=linphone_conference_get_audio_stream(lc->conf_ctx);
-	}
-	if (stream){
-		if (rtype==LinphoneToneGenerator) return stream->dtmfgen;
-		if (rtype==LinphoneLocalPlayer) return stream->local_player;
-		return NULL;
-	}
-	if (card && lc->ringstream && card != lc->ringstream->card){
-			ring_stop(lc->ringstream);
-			lc->ringstream = NULL;
-	}
-	if (lc->ringstream  == NULL) {
-		float amp=lp_config_get_float(lc->config,"sound","dtmf_player_amp",0.1f);
-		MSSndCard *ringcard=lc->sound_conf.lsd_card
-			? lc->sound_conf.lsd_card
-			: card
-				? card
-				: lc->sound_conf.ring_sndcard;
-
-		if (ringcard == NULL)
-			return NULL;
-		
-		if (!create) return NULL;
-
-		ringstream=lc->ringstream=ring_start(lc->factory, NULL,0,ringcard);
-		ms_filter_call_method(lc->ringstream->gendtmf,MS_DTMF_GEN_SET_DEFAULT_AMPLITUDE,&amp);
-		lc->dmfs_playing_start_time = (time_t)ms_get_cur_time_ms()/1000;
-	}else{
-		ringstream=lc->ringstream;
-		if (lc->dmfs_playing_start_time!=0)
-			lc->dmfs_playing_start_time = (time_t)ms_get_cur_time_ms()/1000;
-	}
-	if (rtype==LinphoneToneGenerator) return ringstream->gendtmf;
-	if (rtype==LinphoneLocalPlayer) return ringstream->source;
-	return NULL;
-}
-
-static MSFilter *get_dtmf_gen(LinphoneCore *lc, MSSndCard *card) {
-	return get_audio_resource(lc,LinphoneToneGenerator, card, TRUE);
-}
-
 void linphone_core_play_dtmf(LinphoneCore *lc, char dtmf, int duration_ms){
-	MSSndCard *card = linphone_core_in_call(lc)
-		? lc->sound_conf.play_sndcard
-		: lc->sound_conf.ring_sndcard;
-	MSFilter *f=get_dtmf_gen(lc, card);
-	if (f==NULL){
-		ms_error("No dtmf generator at this time !");
-		return;
-	}
-
-	if (duration_ms > 0)
-		ms_filter_call_method(f, MS_DTMF_GEN_PLAY, &dtmf);
-	else ms_filter_call_method(f, MS_DTMF_GEN_START, &dtmf);
-}
-
-LinphoneStatus _linphone_core_play_local(LinphoneCore *lc, const char *audiofile, MSSndCard *card) {
-	MSFilter *f=get_audio_resource(lc,LinphoneLocalPlayer, card, TRUE);
-	int loopms=-1;
-	if (!f) return -1;
-	ms_filter_call_method(f,MS_PLAYER_SET_LOOP,&loopms);
-	if (ms_filter_call_method(f,MS_PLAYER_OPEN,(void*)audiofile)!=0){
-		return -1;
-	}
-	ms_filter_call_method_noarg(f,MS_PLAYER_START);
-	return 0;
+	L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->linphoneCorePlayDtmf(dtmf, duration_ms);
 }
 
 LinphoneStatus linphone_core_play_local(LinphoneCore *lc, const char *audiofile) {
-	return _linphone_core_play_local(lc, audiofile, NULL);
-}
-
-void linphone_core_play_named_tone(LinphoneCore *lc, LinphoneToneID toneid){
-	if (linphone_core_tone_indications_enabled(lc)){
-		const char *audiofile=linphone_core_get_tone_file(lc,toneid);
-		if (!audiofile){
-			MSFilter *f=get_dtmf_gen(lc, lc->sound_conf.play_sndcard);
-			MSDtmfGenCustomTone def;
-			if (f==NULL){
-				ms_error("No dtmf generator at this time !");
-				return;
-			}
-			memset(&def,0,sizeof(def));
-			def.amplitude=1;
-			/*these are french tones, excepted the failed one, which is USA congestion tone (does not exist in France)*/
-			switch(toneid){
-				case LinphoneToneCallOnHold:
-				case LinphoneToneCallWaiting:
-					def.duration=300;
-					def.frequencies[0]=440;
-					def.interval=2000;
-				break;
-				case LinphoneToneBusy:
-					def.duration=500;
-					def.frequencies[0]=440;
-					def.interval=500;
-					def.repeat_count=3;
-				break;
-				case LinphoneToneCallLost:
-					def.duration=250;
-					def.frequencies[0]=480;
-					def.frequencies[0]=620;
-					def.interval=250;
-					def.repeat_count=3;
-
-				break;
-				default:
-					ms_warning("Unhandled tone id.");
-			}
-			if (def.duration>0)
-				ms_filter_call_method(f, MS_DTMF_GEN_PLAY_CUSTOM,&def);
-		}else{
-			_linphone_core_play_local(lc,audiofile, lc->sound_conf.play_sndcard);
-		}
-	}
-}
-
-void linphone_core_play_call_error_tone(LinphoneCore *lc, LinphoneReason reason){
-	if (linphone_core_tone_indications_enabled(lc)){
-		LinphoneToneDescription *tone=linphone_core_get_call_error_tone(lc,reason);
-		if (tone){
-			if (tone->audiofile){
-				_linphone_core_play_local(lc, tone->audiofile, lc->sound_conf.play_sndcard);
-			}else if (tone->toneid != LinphoneToneUndefined){
-				linphone_core_play_named_tone(lc, tone->toneid);
-			}
-		}
-	}
+	return L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->linphoneCorePlayLocal(audiofile);
 }
 
 void linphone_core_stop_dtmf(LinphoneCore *lc){
-	MSFilter *f=get_audio_resource(lc, LinphoneToneGenerator, NULL, FALSE);
-	if (f!=NULL)
-		ms_filter_call_method_noarg (f, MS_DTMF_GEN_STOP);
+	L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->linphoneCoreStopDtmf();
 }
 
 void *linphone_core_get_user_data(const LinphoneCore *lc){
@@ -6892,31 +6720,15 @@ bool_t linphone_core_keep_alive_enabled(LinphoneCore* lc) {
 }
 
 void linphone_core_start_dtmf_stream(LinphoneCore* lc) {
-	get_dtmf_gen(lc, lc->sound_conf.ring_sndcard); /*make sure ring stream is started*/
-	lc->ringstream_autorelease=FALSE; /*disable autorelease mode*/
+	L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->linphoneCoreStartDtmfStream();
 }
 
 void linphone_core_stop_ringing(LinphoneCore* lc) {
-	LinphoneCall *call=linphone_core_get_current_call(lc);
-	if (linphone_ringtoneplayer_is_started(lc->ringtoneplayer)) {
-		linphone_ringtoneplayer_stop(lc->ringtoneplayer);
-	}
-	if (lc->ringstream) {
-		ring_stop(lc->ringstream);
-		lc->ringstream=NULL;
-		lc->dmfs_playing_start_time=0;
-		lc->ringstream_autorelease=TRUE;
-	}
-	if (call && L_GET_PRIVATE_FROM_C_OBJECT(call)->getRingingBeep()) {
-		linphone_core_stop_dtmf(lc);
-		L_GET_PRIVATE_FROM_C_OBJECT(call)->setRingingBeep(false);
-	}
+	L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->linphoneCoreStopRinging();
 }
 
 void linphone_core_stop_dtmf_stream(LinphoneCore* lc) {
-	if (lc->dmfs_playing_start_time!=0) {
-		linphone_core_stop_ringing(lc);
-	}
+	L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->linphoneCoreStopDtmfStream();
 }
 
 int linphone_core_get_max_calls(LinphoneCore *lc) {
diff --git a/coreapi/misc.c b/coreapi/misc.c
index 0b3bb54146e613135174d4b789b701b58fea2a17..bcf3082b4df51b69e66c5b7cf3f56f5147fe128e 100644
--- a/coreapi/misc.c
+++ b/coreapi/misc.c
@@ -21,6 +21,7 @@
 #include "linphone/lpconfig.h"
 #include "linphone/wrapper_utils.h"
 #include "mediastreamer2/mediastream.h"
+#include "core/core-p.h"
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -574,44 +575,12 @@ void linphone_tone_description_destroy(LinphoneToneDescription *obj){
 	ms_free(obj);
 }
 
-static LinphoneToneDescription *linphone_core_lookup_tone(const LinphoneCore *lc, LinphoneReason reason, LinphoneToneID id){
-	const bctbx_list_t *elem;
-	for (elem=lc->tones;elem!=NULL;elem=elem->next){
-		LinphoneToneDescription *tone=(LinphoneToneDescription*)elem->data;
-		if (reason == LinphoneReasonNone){
-			if (tone->toneid == id && tone->reason == LinphoneReasonNone) return tone;
-		}else{
-			if (tone->reason==reason) return tone;
-		}
-	}
-	return NULL;
-}
-
-LinphoneToneDescription *linphone_core_get_call_error_tone(const LinphoneCore *lc, LinphoneReason reason){
-	return linphone_core_lookup_tone(lc, reason, LinphoneToneUndefined);
-}
-
-const char *linphone_core_get_tone_file(const LinphoneCore *lc, LinphoneToneID id){
-	LinphoneToneDescription *tone = linphone_core_lookup_tone(lc, LinphoneReasonNone, id);
-	return tone ? tone->audiofile : NULL;
-}
-
-void _linphone_core_set_tone(LinphoneCore *lc, LinphoneReason reason, LinphoneToneID id, const char *audiofile){
-	LinphoneToneDescription *tone = linphone_core_lookup_tone(lc,reason, id);
-	if (tone){
-		lc->tones=bctbx_list_remove(lc->tones,tone);
-		linphone_tone_description_destroy(tone);
-	}
-	tone=linphone_tone_description_new(reason,id,audiofile);
-	lc->tones=bctbx_list_append(lc->tones,tone);
-}
-
 void linphone_core_set_call_error_tone(LinphoneCore *lc, LinphoneReason reason, const char *audiofile){
-	_linphone_core_set_tone(lc,reason,LinphoneToneUndefined, audiofile);
+	L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->setTone(reason, LinphoneToneUndefined, audiofile);
 }
 
 void linphone_core_set_tone(LinphoneCore *lc, LinphoneToneID id, const char *audiofile){
-	_linphone_core_set_tone(lc, LinphoneReasonNone, id, audiofile);
+	L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->setTone(LinphoneReasonNone, id, audiofile);
 }
 
 const MSCryptoSuite * linphone_core_get_srtp_crypto_suites(LinphoneCore *lc){
diff --git a/coreapi/private_functions.h b/coreapi/private_functions.h
index 0ef576f7d34fe0667a793522cf78c6541ac6a1fa..0f0dee401434d96464012fea6d4301f6b2353486 100644
--- a/coreapi/private_functions.h
+++ b/coreapi/private_functions.h
@@ -287,7 +287,6 @@ void linphone_core_start_waiting(LinphoneCore *lc, const char *purpose);
 void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float progresses);
 void linphone_core_stop_waiting(LinphoneCore *lc);
 
-void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call);
 bool_t linphone_core_incompatible_security(LinphoneCore *lc, SalMediaDescription *md);
 extern LinphonePrivate::Sal::Callbacks linphone_sal_callbacks;
 LINPHONE_PUBLIC bool_t linphone_core_rtcp_enabled(const LinphoneCore *lc);
@@ -345,10 +344,6 @@ const LinphoneParticipantImdnState *_linphone_participant_imdn_state_from_cpp_ob
 
 LinphoneToneDescription * linphone_tone_description_new(LinphoneReason reason, LinphoneToneID id, const char *audiofile);
 void linphone_tone_description_destroy(LinphoneToneDescription *obj);
-LinphoneToneDescription *linphone_core_get_call_error_tone(const LinphoneCore *lc, LinphoneReason reason);
-void linphone_core_play_call_error_tone(LinphoneCore *lc, LinphoneReason reason);
-void _linphone_core_set_tone(LinphoneCore *lc, LinphoneReason reason, LinphoneToneID id, const char *audiofile);
-LINPHONE_PUBLIC const char *linphone_core_get_tone_file(const LinphoneCore *lc, LinphoneToneID id);
 
 void linphone_task_list_init(LinphoneTaskList *t);
 void linphone_task_list_add(LinphoneTaskList *t, LinphoneCoreIterateHook hook, void *hook_data);
@@ -419,7 +414,6 @@ void linphone_chat_message_set_message_state_changed_cb_user_data(LinphoneChatMe
 void * linphone_chat_message_get_message_state_changed_cb_user_data(LinphoneChatMessage* msg);
 LinphoneChatRoom *_linphone_core_create_chat_room_from_call(LinphoneCall *call);
 
-void linphone_core_play_named_tone(LinphoneCore *lc, LinphoneToneID id);
 bool_t linphone_core_tone_indications_enabled(LinphoneCore*lc);
 const char *linphone_core_create_uuid(LinphoneCore *lc);
 void linphone_configure_op(LinphoneCore *lc, LinphonePrivate::SalOp *op, const LinphoneAddress *dest, SalCustomHeader *headers, bool_t with_contact);
diff --git a/coreapi/private_structs.h b/coreapi/private_structs.h
index 47c3498d4ee614316daee766468ab912bdd35311..b527ae6548116dc0089d68300947f536b8be122e 100644
--- a/coreapi/private_structs.h
+++ b/coreapi/private_structs.h
@@ -762,7 +762,6 @@ namespace LinphonePrivate {
 	MSList *friends_lists; \
 	MSList *auth_info; \
 	struct _RingStream *ringstream; \
-	time_t dmfs_playing_start_time; \
 	LCCallbackObj preview_finished_cb; \
 	MSList *queued_calls; \
 	MSList *call_logs; \
@@ -805,7 +804,6 @@ namespace LinphonePrivate {
 	bool_t network_reachable_to_be_notified; \
 	bool_t use_preview_window; \
 	bool_t network_last_status; \
-	bool_t ringstream_autorelease; \
 	bool_t vtables_running; \
 	bool_t send_call_stats_periodical_updates; \
 	bool_t forced_ice_relay; \
diff --git a/coreapi/tester_utils.cpp b/coreapi/tester_utils.cpp
index 259352e5e3052ce0966cc859cf530ac54f97e847..afb6ab0f5c5dafbcfff6e26184d724bc5fcd0791 100644
--- a/coreapi/tester_utils.cpp
+++ b/coreapi/tester_utils.cpp
@@ -199,4 +199,15 @@ char * linphone_core_get_device_identity(LinphoneCore *lc) {
 	return identity;
 }
 
+LinphoneCoreToneManagerStats *linphone_core_get_tone_manager_stats(LinphoneCore *lc) {
+	return L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->getStats();
+}
+
+void linphone_core_reset_tone_manager_stats(LinphoneCore *lc) {
+	L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->resetStats();
+}
 
+const char *linphone_core_get_tone_file(LinphoneCore *lc, LinphoneToneID id){
+	LinphoneToneDescription *tone = L_GET_PRIVATE_FROM_C_OBJECT(lc)->getToneManager()->getToneFromId(id);
+	return tone ? tone->audiofile : NULL;
+}
diff --git a/coreapi/tester_utils.h b/coreapi/tester_utils.h
index b1cd952942eed3130bee4a92d63b68a7605e42d2..c8037577dad1599b45d2be141b3e7dd8e7d9c909 100644
--- a/coreapi/tester_utils.h
+++ b/coreapi/tester_utils.h
@@ -42,6 +42,16 @@ typedef enum _LinphoneProxyConfigAddressComparisonResult{
 	LinphoneProxyConfigAddressWeakEqual
 } LinphoneProxyConfigAddressComparisonResult;
 
+typedef struct _LinphoneCoreToneManagerStats {
+	int number_of_startRingbackTone;
+	int number_of_startRingtone;
+	int number_of_startErrorTone;
+	int number_of_startNamedTone;
+	int number_of_stopRingbackTone;
+	int number_of_stopRingtone;
+	int number_of_stopTone;
+} LinphoneCoreToneManagerStats;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -53,7 +63,6 @@ LINPHONE_PUBLIC int linphone_core_get_local_ip_for(int type, const char *dest, c
 LINPHONE_PUBLIC void linphone_core_enable_forced_ice_relay(LinphoneCore *lc, bool_t enable);
 LINPHONE_PUBLIC void linphone_core_set_zrtp_not_available_simulation(LinphoneCore *lc, bool_t enabled);
 LINPHONE_PUBLIC belle_http_provider_t *linphone_core_get_http_provider(const LinphoneCore *lc);
-LINPHONE_PUBLIC const char *linphone_core_get_tone_file(const LinphoneCore *lc, LinphoneToneID id);
 LINPHONE_PUBLIC IceSession * linphone_call_get_ice_session(const LinphoneCall *call);
 LINPHONE_PUBLIC const struct addrinfo *linphone_core_get_stun_server_addrinfo(LinphoneCore *lc);
 LINPHONE_PUBLIC void linphone_core_enable_send_call_stats_periodical_updates(LinphoneCore *lc, bool_t enabled);
@@ -129,6 +138,10 @@ LINPHONE_PUBLIC int linphone_remote_provisioning_load_file( LinphoneCore* lc, co
 
 LINPHONE_PUBLIC char *linphone_core_get_device_identity(LinphoneCore *lc);
 
+LINPHONE_PUBLIC LinphoneCoreToneManagerStats *linphone_core_get_tone_manager_stats(LinphoneCore *lc);
+LINPHONE_PUBLIC void linphone_core_reset_tone_manager_stats(LinphoneCore *lc);
+LINPHONE_PUBLIC const char *linphone_core_get_tone_file(LinphoneCore *lc, LinphoneToneID id);
+
 /**
  * Send an XML-RPC request to delete a Linphone account.
  * @param[in] creator LinphoneAccountCreator object
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b0a6fe8d3131767b655185890641ba1d7958acff..be4b480f6354c2c63f87c9e56a7da57672c3eba2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -165,6 +165,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES
 	conference/session/call-session.h
 	conference/session/media-session.h
 	conference/session/port-config.h
+	conference/session/tone-manager.h
 	containers/lru-cache.h
 	content/content-disposition.h
 	content/content-manager.h
@@ -330,6 +331,7 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES
 	conference/remote-conference.cpp
 	conference/session/call-session.cpp
 	conference/session/media-session.cpp
+	conference/session/tone-manager.cpp
 	content/content-disposition.cpp
 	content/content-manager.cpp
 	content/content-type.cpp
diff --git a/src/call/call-p.h b/src/call/call-p.h
index 2b53c3a3b9e538cd42e828c988c10661e4bff5b8..c2f52fba1f320d177d2a86bb2245617a11c1251d 100644
--- a/src/call/call-p.h
+++ b/src/call/call-p.h
@@ -58,9 +58,6 @@ public:
 	MediaStream *getMediaStream (LinphoneStreamType type) const;
 	SalCallOp *getOp () const;
 
-	bool getRingingBeep () const { return ringingBeep; }
-	void setRingingBeep (bool value) { ringingBeep = value; }
-
 	bool getSpeakerMuted () const;
 	void setSpeakerMuted (bool muted);
 
@@ -109,12 +106,7 @@ private:
 	void onSetCurrentSession (const std::shared_ptr<CallSession> &session) override;
 	void onFirstVideoFrameDecoded (const std::shared_ptr<CallSession> &session) override;
 	void onResetFirstVideoFrameDecoded (const std::shared_ptr<CallSession> &session) override;
-	void onPlayErrorTone (const std::shared_ptr<CallSession> &session, LinphoneReason reason) override;
 	void onRingbackToneRequested (const std::shared_ptr<CallSession> &session, bool requested) override;
-	void onStartRinging (const std::shared_ptr<CallSession> &session) override;
-	void onStopRinging (const std::shared_ptr<CallSession> &session) override;
-	void onStopRingingIfInCall (const std::shared_ptr<CallSession> &session) override;
-	void onStopRingingIfNeeded (const std::shared_ptr<CallSession> &session) override;
 	bool areSoundResourcesAvailable (const std::shared_ptr<CallSession> &session) override;
 	bool isPlayingRingbackTone (const std::shared_ptr<CallSession> &session) override;
 	void onRealTimeTextCharacterReceived (const std::shared_ptr<CallSession> &session, RealtimeTextReceivedCharacter *character) override;
@@ -125,7 +117,6 @@ private:
 
 	CallCallbackObj nextVideoFrameDecoded;
 
-	bool ringingBeep = false;
 	bool playingRingbackTone = false;
 
 	BackgroundTask bgTask;
diff --git a/src/call/call.cpp b/src/call/call.cpp
index b801cf47dbf5d229a35cb70f2fb0eb51e0b05283..a5925302e3d648bc49153ff5b7cc11601f4a258f 100644
--- a/src/call/call.cpp
+++ b/src/call/call.cpp
@@ -190,7 +190,7 @@ void CallPrivate::terminateBecauseOfLostMedia () {
 	lInfo() << "Call [" << q << "]: Media connectivity with " << q->getRemoteAddress().asString()
 		<< " is lost, call is going to be terminated";
 	static_pointer_cast<MediaSession>(getActiveSession())->terminateBecauseOfLostMedia();
-	linphone_core_play_named_tone(q->getCore()->getCCore(), LinphoneToneCallLost);
+	q->getCore()->getPrivate()->getToneManager()->startNamedTone(getActiveSession(), LinphoneToneCallLost);
 }
 
 // -----------------------------------------------------------------------------
@@ -222,16 +222,6 @@ bool CallPrivate::onCallSessionAccepted (const shared_ptr<CallSession> &session)
 	if (q->getCore()->getCurrentCall() != q->getSharedFromThis())
 		linphone_core_preempt_sound_resources(lc);
 
-	// Stop ringing
-	if (linphone_ringtoneplayer_is_started(lc->ringtoneplayer)) {
-		lInfo() << "Stop ringing";
-		linphone_core_stop_ringing(lc);
-		wasRinging = true;
-	}
-	if (ringingBeep) {
-		linphone_core_stop_dtmf(lc);
-		ringingBeep = false;
-	}
 	return wasRinging;
 }
 
@@ -276,10 +266,6 @@ void CallPrivate::onCallSessionSetTerminated (const shared_ptr<CallSession> &ses
 		lError() << "Could not remove the call from the list!!!";
 	if (core->conf_ctx)
 		linphone_conference_on_call_terminating(core->conf_ctx, L_GET_C_BACK_PTR(q));
-	if (ringingBeep) {
-		linphone_core_stop_dtmf(core);
-		ringingBeep = false;
-	}
 #if 0
 	if (lcall->chat_room)
 		linphone_chat_room_set_call(lcall->chat_room, nullptr);
@@ -294,6 +280,8 @@ void CallPrivate::onCallSessionStartReferred (const shared_ptr<CallSession> &ses
 
 void CallPrivate::onCallSessionStateChanged (const shared_ptr<CallSession> &session, CallSession::State state, const string &message) {
 	L_Q();
+	q->getCore()->getPrivate()->getToneManager()->update(session);
+
 	switch(state){
 		case CallSession::State::OutgoingInit:
 		case CallSession::State::IncomingReceived:
@@ -352,7 +340,10 @@ void CallPrivate::onIncomingCallSessionNotified (const shared_ptr<CallSession> &
 
 void CallPrivate::onIncomingCallSessionStarted (const shared_ptr<CallSession> &session) {
 	L_Q();
-	linphone_core_notify_incoming_call(q->getCore()->getCCore(), L_GET_C_BACK_PTR(q));
+	if (linphone_core_get_calls_nb(q->getCore()->getCCore())==1) {
+		L_GET_PRIVATE_FROM_C_OBJECT(q->getCore()->getCCore())->setCurrentCall(q->getSharedFromThis());
+	}
+	q->getCore()->getPrivate()->getToneManager()->startRingtone(session);
 }
 
 void CallPrivate::onIncomingCallSessionTimeoutCheck (const shared_ptr<CallSession> &session, int elapsed, bool oneSecondElapsed) {
@@ -442,11 +433,6 @@ void CallPrivate::requestNotifyNextVideoFrameDecoded(){
 	static_pointer_cast<MediaSession>(getActiveSession())->requestNotifyNextVideoFrameDecoded();
 }
 
-void CallPrivate::onPlayErrorTone (const shared_ptr<CallSession> &session, LinphoneReason reason) {
-	L_Q();
-	linphone_core_play_call_error_tone(q->getCore()->getCCore(), reason);
-}
-
 void CallPrivate::onRingbackToneRequested (const shared_ptr<CallSession> &session, bool requested) {
 	L_Q();
 	if (requested && linphone_core_get_remote_ringback_tone(q->getCore()->getCCore()))
@@ -455,44 +441,6 @@ void CallPrivate::onRingbackToneRequested (const shared_ptr<CallSession> &sessio
 		playingRingbackTone = false;
 }
 
-void CallPrivate::onStartRinging (const shared_ptr<CallSession> &session) {
-	L_Q();
-	LinphoneCore *lc = q->getCore()->getCCore();
-	if (lc->ringstream)
-		return; // Already ringing!
-	startRemoteRing();
-}
-
-void CallPrivate::onStopRinging (const shared_ptr<CallSession> &session) {
-	L_Q();
-	linphone_core_stop_ringing(q->getCore()->getCCore());
-}
-
-void CallPrivate::onStopRingingIfInCall (const shared_ptr<CallSession> &session) {
-	L_Q();
-	LinphoneCore *lc = q->getCore()->getCCore();
-	// We stop the ring only if we have this current call or if we are in call
-	if ((q->getCore()->getCallCount() == 1) || linphone_core_in_call(lc)) {
-		linphone_core_stop_ringing(lc);
-	}
-}
-
-void CallPrivate::onStopRingingIfNeeded (const shared_ptr<CallSession> &session) {
-	L_Q();
-	LinphoneCore *lc = q->getCore()->getCCore();
-	bool stopRinging = true;
-	bool ringDuringEarlyMedia = !!linphone_core_get_ring_during_incoming_early_media(lc);
-	for (const auto &call : q->getCore()->getCalls()) {
-		if ((call->getState() == CallSession::State::IncomingReceived)
-			|| (ringDuringEarlyMedia && call->getState() == CallSession::State::IncomingEarlyMedia)) {
-			stopRinging = false;
-			break;
-		}
-	}
-	if (stopRinging)
-		linphone_core_stop_ringing(lc);
-}
-
 bool CallPrivate::areSoundResourcesAvailable (const shared_ptr<CallSession> &session) {
 	L_Q();
 	LinphoneCore *lc = q->getCore()->getCCore();
diff --git a/src/conference/session/call-session-listener.h b/src/conference/session/call-session-listener.h
index 8d25ae3e83361abe524952ed3fe31b3ec9d5c84a..a1233e800a067ac601ec1110bf9283a78594a703 100644
--- a/src/conference/session/call-session-listener.h
+++ b/src/conference/session/call-session-listener.h
@@ -70,12 +70,7 @@ public:
 	virtual void onFirstVideoFrameDecoded (const std::shared_ptr<CallSession> &session) {}
 	virtual void onResetFirstVideoFrameDecoded (const std::shared_ptr<CallSession> &session) {}
 
-	virtual void onPlayErrorTone (const std::shared_ptr<CallSession> &session, LinphoneReason reason) {}
 	virtual void onRingbackToneRequested (const std::shared_ptr<CallSession> &session, bool requested) {}
-	virtual void onStartRinging (const std::shared_ptr<CallSession> &session) {}
-	virtual void onStopRinging (const std::shared_ptr<CallSession> &session) {}
-	virtual void onStopRingingIfInCall (const std::shared_ptr<CallSession> &session) {}
-	virtual void onStopRingingIfNeeded (const std::shared_ptr<CallSession> &session) {}
 
 	virtual bool areSoundResourcesAvailable (const std::shared_ptr<CallSession> &session) { return true; }
 	virtual bool isPlayingRingbackTone (const std::shared_ptr<CallSession> &session) { return false; }
diff --git a/src/conference/session/call-session.cpp b/src/conference/session/call-session.cpp
index d0656241dd90bd89425dd77102d5dfd9b04d6511..e68a04f26cc4dad33f3766101a9ab0c932c69239 100644
--- a/src/conference/session/call-session.cpp
+++ b/src/conference/session/call-session.cpp
@@ -301,8 +301,6 @@ bool CallSessionPrivate::failure () {
 			else
 				setState(CallSession::State::End, ei->full_string ? ei->full_string : "");
 		}
-		if ((ei->reason != SalReasonNone) && listener)
-			listener->onPlayErrorTone(q->getSharedFromThis(), linphone_reason_from_sal(ei->reason));
 	}
 	if (referer) {
 		// Notify referer of the failure
@@ -344,12 +342,8 @@ void CallSessionPrivate::referred (const Address &referToAddr) {
 }
 
 void CallSessionPrivate::remoteRinging () {
-	L_Q();
 	/* Set privacy */
 	currentParams->setPrivacy((LinphonePrivacyMask)op->getPrivacy());
-	if (listener)
-		listener->onStartRinging(q->getSharedFromThis());
-	lInfo() << "Remote ringing...";
 	setState(CallSession::State::OutgoingRinging, "Remote ringing");
 }
 
@@ -416,8 +410,6 @@ void CallSessionPrivate::terminated () {
 	}
 	if (referPending && listener)
 		listener->onCallSessionStartReferred(q->getSharedFromThis());
-	if (listener)
-		listener->onStopRingingIfInCall(q->getSharedFromThis());
 	setState(CallSession::State::End, "Call ended");
 }
 
@@ -637,6 +629,8 @@ void CallSessionPrivate::setReleased () {
 	referer = nullptr;
 	transferTarget = nullptr;
 
+	q->getCore()->getPrivate()->getToneManager()->removeSession(q->getSharedFromThis());
+
 	if (listener)
 		listener->onCallSessionSetReleased(q->getSharedFromThis());
 }
diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp
index 399e1faebd327d0ada38dc0f16875eeb002d77b8..dc37985a03627c19b336f763d933378013d19ed9 100644
--- a/src/conference/session/media-session.cpp
+++ b/src/conference/session/media-session.cpp
@@ -264,8 +264,11 @@ bool MediaSessionPrivate::failure () {
 			"Automatic CallSession resuming after failed transfer");
 	}
 
-	if (listener)
-		listener->onStopRingingIfNeeded(q->getSharedFromThis());
+	q->getCore()->getPrivate()->getToneManager()->stop(q->getSharedFromThis());
+
+	if (ei->reason != SalReasonNone)
+		q->getCore()->getPrivate()->getToneManager()->startErrorTone(q->getSharedFromThis(), linphone_reason_from_sal(ei->reason));
+
 	stopStreams();
 	return false;
 }
@@ -310,25 +313,21 @@ void MediaSessionPrivate::remoteRinging () {
 		}
 
 		setState(CallSession::State::OutgoingEarlyMedia, "Early media");
-		if (listener)
-			listener->onStopRinging(q->getSharedFromThis());
+		q->getCore()->getPrivate()->getToneManager()->stop(q->getSharedFromThis());
 		lInfo() << "Doing early media...";
 		iceAgent->updateFromRemoteMediaDescription(localDesc, rmd, !op->isOfferer());
 		updateStreams(md, state);
 		if ((q->getCurrentParams()->getAudioDirection() == LinphoneMediaDirectionInactive) && audioStream) {
-			if (listener)
-				listener->onStartRinging(q->getSharedFromThis());
+			q->getCore()->getPrivate()->getToneManager()->startRingbackTone(q->getSharedFromThis());
 		}
 	} else {
-		linphone_core_stop_dtmf_stream(q->getCore()->getCCore());
 		if (state == CallSession::State::OutgoingEarlyMedia) {
 			/* Already doing early media */
 			return;
 		}
-		if (listener)
-			listener->onStartRinging(q->getSharedFromThis());
-		lInfo() << "Remote ringing...";
+
 		setState(CallSession::State::OutgoingRinging, "Remote ringing");
+		q->getCore()->getPrivate()->getToneManager()->startRingbackTone(q->getSharedFromThis());
 	}
 }
 
@@ -372,8 +371,10 @@ void MediaSessionPrivate::telephoneEventReceived (int event) {
 }
 
 void MediaSessionPrivate::terminated () {
+	L_Q();
 	stopStreams();
 	CallSessionPrivate::terminated();
+	q->getCore()->getPrivate()->getToneManager()->stop(q->getSharedFromThis());
 }
 
 /* This callback is called when an incoming re-INVITE/ SIP UPDATE modifies the session */
@@ -782,11 +783,6 @@ void MediaSessionPrivate::restartStream (SalStreamDescription *streamDesc, int s
 
 	startStream(streamDesc, streamIndex, targetState);
 
-	if (streamDesc->type == SalAudio && audioStream) {
-		if ((state == CallSession::State::Pausing) && pausedByApp && (q->getCore()->getCallCount() == 1))
-			linphone_core_play_named_tone(q->getCore()->getCCore(), LinphoneToneCallOnHold);
-	}
-
 	updateStreamFrozenPayloads(streamDesc, &localDesc->streams[streamIndex]);
 }
 
@@ -3659,8 +3655,10 @@ void MediaSessionPrivate::updateFrozenPayloads (SalMediaDescription *result) {
 void MediaSessionPrivate::updateStreams (SalMediaDescription *newMd, CallSession::State targetState) {
 	L_Q();
 
-	if (!((state == CallSession::State::IncomingEarlyMedia) && linphone_core_get_ring_during_incoming_early_media(q->getCore()->getCCore())))
-		linphone_core_stop_ringing(q->getCore()->getCCore());
+	if (state == CallSession::State::Connected || state == CallSession::State::Resuming ||
+		(state == CallSession::State::IncomingEarlyMedia && !linphone_core_get_ring_during_incoming_early_media(q->getCore()->getCCore()))) {
+		q->getCore()->getPrivate()->getToneManager()->goToCall(q->getSharedFromThis());
+	}
 
 	if (!newMd) {
 		lError() << "updateStreams() called with null media description";
@@ -3719,7 +3717,7 @@ void MediaSessionPrivate::updateStreams (SalMediaDescription *newMd, CallSession
 
 						if (newMd->streams[i].type == SalAudio && audioStream) {
 							if ((state == CallSession::State::Pausing) && pausedByApp && (q->getCore()->getCallCount() == 1))
-								linphone_core_play_named_tone(q->getCore()->getCCore(), LinphoneToneCallOnHold);
+								q->getCore()->getPrivate()->getToneManager()->startNamedTone(q->getSharedFromThis(), LinphoneToneCallOnHold);
 						}
 
 						updateStreamFrozenPayloads(&newMd->streams[i], &localDesc->streams[i]);
@@ -3820,8 +3818,9 @@ void MediaSessionPrivate::updateStreams (SalMediaDescription *newMd, CallSession
 
 	startStreams(targetState);
 
-	if ((state == CallSession::State::Pausing) && pausedByApp && (q->getCore()->getCallCount() == 1))
-		linphone_core_play_named_tone(q->getCore()->getCCore(), LinphoneToneCallOnHold);
+	if ((state == CallSession::State::Pausing) && pausedByApp && (q->getCore()->getCallCount() == 1)) {
+		q->getCore()->getPrivate()->getToneManager()->startNamedTone(q->getSharedFromThis(), LinphoneToneCallOnHold);
+	}
 
 	updateFrozenPayloads(newMd);
 
@@ -4143,8 +4142,7 @@ void MediaSessionPrivate::reportBandwidthForStream (MediaStream *ms, LinphoneStr
 
 void MediaSessionPrivate::abort (const string &errorMsg) {
 	L_Q();
-	if (listener)
-		listener->onStopRinging(q->getSharedFromThis());
+	q->getCore()->getPrivate()->getToneManager()->stop(q->getSharedFromThis());
 	stopStreams();
 	CallSessionPrivate::abort(errorMsg);
 }
@@ -4250,9 +4248,7 @@ LinphoneStatus MediaSessionPrivate::startUpdate (const string &subject) {
 
 void MediaSessionPrivate::terminate () {
 	L_Q();
-	if (listener)
-		listener->onStopRingingIfNeeded(q->getSharedFromThis());
-
+	q->getCore()->getPrivate()->getToneManager()->stop(q->getSharedFromThis());
 	stopStreams();
 	CallSessionPrivate::terminate();
 }
diff --git a/src/conference/session/media-session.h b/src/conference/session/media-session.h
index a02d2759be5497197582702872f85f6b285d2846..ff39c48c1ec89e13cfa4929cc88d415984102301 100644
--- a/src/conference/session/media-session.h
+++ b/src/conference/session/media-session.h
@@ -37,6 +37,7 @@ class LINPHONE_PUBLIC MediaSession : public CallSession {
 	friend class Call;
 	friend class CallPrivate;
 	friend class IceAgent;
+	friend class ToneManager;
 
 public:
 	MediaSession (const std::shared_ptr<Core> &core, std::shared_ptr<Participant> me, const CallSessionParams *params, CallSessionListener *listener);
diff --git a/src/conference/session/tone-manager.cpp b/src/conference/session/tone-manager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4f5195a894dcd988f24bc560f179faa4a025e69f
--- /dev/null
+++ b/src/conference/session/tone-manager.cpp
@@ -0,0 +1,623 @@
+/*
+ * Copyright (c) 2010-2019 Belledonne Communications SARL.
+ *
+ * This file is part of Liblinphone.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tone-manager.h"
+#include "linphone/utils/utils.h"
+#include "logger/logger.h"
+#include "call/call-p.h"
+#include "conference/session/media-session.h"
+#include "conference/session/media-session-p.h"
+#include "linphone/utils/general.h"
+#include "core/core-p.h"
+#include "conference_private.h"
+
+LINPHONE_BEGIN_NAMESPACE
+
+ToneManager::ToneManager(std::shared_ptr<Core> core) : CoreAccessor(core) {
+    lInfo() << "[ToneManager] create ToneManager()";
+	mStats = new LinphoneCoreToneManagerStats;
+	*mStats = {0, 0, 0, 0, 0, 0, 0};
+}
+
+ToneManager::~ToneManager() {
+    lInfo() << "[ToneManager] destroy ToneManager()";
+	delete mStats;
+}
+
+std::string ToneManager::stateToString(ToneManager::State state) {
+	switch(state) {
+		case State::None:
+			return "None";
+		case State::Call:
+			return "Call";
+		case State::Ringback:
+			return "Ringback";
+		case State::Ringtone:
+			return "Ringtone";
+		case State::Tone:
+			return "Tone";
+		default:
+			return "";
+	}
+}
+
+void ToneManager::printDebugInfo(const std::shared_ptr<CallSession> &session) {
+	auto callState = session->getState();
+    auto toneState = getState(session);
+	lInfo() << "[ToneManager] [" << session << "] state changed : [" << stateToString(toneState)  << ", " << Utils::toString(callState) << "]";
+}
+
+// ---------------------------------------------------
+// public entrypoints for tones
+// ---------------------------------------------------
+
+void ToneManager::startRingbackTone(const std::shared_ptr<CallSession> &session) {
+	printDebugInfo(session);
+	if (getState(session) == State::Ringback) {
+		return;
+	}
+
+	setState(session, State::Ringback);
+	mStats->number_of_startRingbackTone++;
+
+	if (!isAnotherSessionInState(session, State::Ringback)) {
+		doStopToneToPlaySomethingElse(session);
+		doStartRingbackTone(session);
+	}
+}
+
+void ToneManager::startRingtone(const std::shared_ptr<CallSession> &session) {
+	printDebugInfo(session);
+	setState(session, State::Ringtone);
+	if (!isAnotherSessionInState(session, State::Ringtone) && !isAnotherSessionInState(session, State::Ringback)) {
+		doStopToneToPlaySomethingElse(session);
+		doStartRingtone(session);
+		mStats->number_of_startRingtone++;
+	}
+}
+
+void ToneManager::startErrorTone(const std::shared_ptr<CallSession> &session, LinphoneReason reason) {
+	LinphoneCore *lc = getCore()->getCCore();
+	if (linphone_core_tone_indications_enabled(lc)) {
+		printDebugInfo(session);
+		doStopToneToPlaySomethingElse(session);
+		doStartErrorTone(session, reason);
+		mStats->number_of_startErrorTone++;
+	}
+}
+
+void ToneManager::startNamedTone(const std::shared_ptr<CallSession> &session, LinphoneToneID toneId) {
+	LinphoneCore *lc = getCore()->getCCore();
+	if (linphone_core_tone_indications_enabled(lc)) {
+		printDebugInfo(session);
+		setState(session, State::Tone);
+		doStopToneToPlaySomethingElse(session);
+		doStartNamedTone(toneId);
+		mStats->number_of_startNamedTone++;
+	}
+}
+
+void ToneManager::goToCall(const std::shared_ptr<CallSession> &session) {
+	printDebugInfo(session);
+	lInfo() << "[ToneManager] " << __func__;
+	doStop(session, State::Call);
+}
+
+void ToneManager::stop(const std::shared_ptr<CallSession> &session) {
+	printDebugInfo(session);
+	lInfo() << "[ToneManager] " << __func__;
+	doStop(session, State::None);
+}
+
+void ToneManager::removeSession(const std::shared_ptr<CallSession> &session) {
+	printDebugInfo(session);
+	mSessions.erase(session);
+	lInfo() << "[ToneManager] removeSession mSession size : " <<  mSessions.size();
+}
+
+/**
+ * This start again ringtone when one call is not anymore in Ringtone state but the second call is still in this state
+ * This code can't be called juste after the doStopRingtone because the first call needs to change its context (deletion or start a call)
+ */
+void ToneManager::update(const std::shared_ptr<CallSession> &session) {
+	switch(session->getState()) {
+		case CallSession::State::UpdatedByRemote:
+		case CallSession::State::Updating:
+		case CallSession::State::Released:
+			printDebugInfo(session);
+			if (isAnotherSessionInState(session, State::Ringtone)) {
+				lInfo() << "[ToneManager] start again ringtone";
+				doStartRingtone(nullptr);
+				mStats->number_of_startRingtone++;
+			}
+			return;
+			break;
+		default:
+			break;
+	}
+}
+
+// ---------------------------------------------------
+// linphone core public API entrypoints
+// ---------------------------------------------------
+
+void ToneManager::linphoneCorePlayDtmf(char dtmf, int duration) {
+	lInfo() << "[ToneManager] " << __func__;
+	LinphoneCore *lc = getCore()->getCCore();
+
+	std::shared_ptr<CallSession> session = nullptr;
+	bool hasSession = getSessionInState(State::Tone, session);
+	if (hasSession) {
+		doStop(session, State::None);
+	}
+
+	MSSndCard *card = linphone_core_in_call(lc)
+		? lc->sound_conf.play_sndcard
+		: lc->sound_conf.ring_sndcard;
+	MSFilter *f = getAudioResource(ToneGenerator, card, true);
+
+	if (f == NULL) {
+		lError() << "[ToneManager] No dtmf generator at this time !";
+		return;
+	}
+
+	if (duration > 0) {
+		ms_filter_call_method(f, MS_DTMF_GEN_PLAY, &dtmf);
+	} else {
+		ms_filter_call_method(f, MS_DTMF_GEN_START, &dtmf);
+	}
+}
+
+void ToneManager::linphoneCoreStopDtmf() {
+	lInfo() << "[ToneManager] " << __func__;
+	MSFilter *f = getAudioResource(ToneGenerator, NULL, false);
+	if (f != NULL) {
+		ms_filter_call_method_noarg (f, MS_DTMF_GEN_STOP);
+	}
+}
+
+LinphoneStatus ToneManager::linphoneCorePlayLocal(const char *audiofile) {
+	lInfo() << "[ToneManager] " << __func__;
+	return playFile(audiofile);
+}
+
+void ToneManager::linphoneCoreStartDtmfStream() {
+	lInfo() << "[ToneManager] " << __func__;
+	LinphoneCore *lc = getCore()->getCCore();
+
+	/*make sure ring stream is started*/
+	getAudioResource(ToneGenerator, lc->sound_conf.ring_sndcard, true);
+}
+
+void ToneManager::linphoneCoreStopRinging() {
+	lInfo() << "[ToneManager] " << __func__;
+	doStopRingtone(nullptr);
+}
+
+
+void ToneManager::linphoneCoreStopDtmfStream() {
+	lInfo() << "[ToneManager] " << __func__;
+	doStopRingtone(nullptr);
+}
+
+// ---------------------------------------------------
+// timer
+// ---------------------------------------------------
+
+void ToneManager::createTimerToCleanTonePlayer(unsigned int delay) {
+	lInfo() << "[ToneManager] " << __func__;
+	if (!mTimer) {
+		auto callback = [this] () -> bool {
+			lInfo() << "[ToneManager] callback";
+			auto source = getCore()->getCCore()->ringstream->source;
+			MSPlayerState state;
+			if (source && ms_filter_call_method(source, MS_PLAYER_GET_STATE, &state) == 0) {
+				if (state != MSPlayerPlaying) {
+					deleteTimer();
+					return false;
+				}
+			}
+			return true;
+		};
+
+		mTimer = getCore()->createTimer(callback, delay);
+	}
+}
+
+void ToneManager::deleteTimer() {
+	if (mTimer) {
+		lInfo() << "[ToneManager] " << __func__;
+		doStopTone();
+		mStats->number_of_stopTone++;
+		getCore()->destroyTimer(mTimer);
+		mTimer = nullptr;
+	}
+}
+
+// ---------------------------------------------------
+// setup tones
+// ---------------------------------------------------
+
+LinphoneToneDescription *ToneManager::getToneFromReason(LinphoneReason reason) {
+	const bctbx_list_t *elem;
+	for (elem = getCore()->getCCore()->tones; elem != NULL; elem = elem->next) {
+		LinphoneToneDescription *tone = (LinphoneToneDescription *) elem->data;
+		if (tone->reason==reason) return tone;
+	}
+	return NULL;
+}
+
+LinphoneToneDescription *ToneManager::getToneFromId(LinphoneToneID id) {
+	const bctbx_list_t *elem;
+	for (elem = getCore()->getCCore()->tones; elem != NULL; elem = elem->next) {
+		LinphoneToneDescription *tone = (LinphoneToneDescription *) elem->data;
+		if (tone->toneid == id) return tone;
+	}
+	return NULL;
+}
+
+void ToneManager::setTone(LinphoneReason reason, LinphoneToneID id, const char *audiofile) {
+	LinphoneCore *lc = getCore()->getCCore();
+	LinphoneToneDescription *tone = getToneFromId(id);
+	if (!tone) tone = getToneFromReason(reason);
+
+	if (tone) {
+		lc->tones = bctbx_list_remove(lc->tones, tone);
+		linphone_tone_description_destroy(tone);
+	}
+	tone = linphone_tone_description_new(reason, id, audiofile);
+	lc->tones = bctbx_list_append(lc->tones, tone);
+}
+
+// ---------------------------------------------------
+// callbacks file player
+// ---------------------------------------------------
+
+static void on_file_player_end(void *userData, MSFilter *f, unsigned int eventId, void *arg) {
+	ToneManager *toneManager = (ToneManager *) userData;
+	toneManager->onFilePlayerEnd(eventId);
+}
+
+void ToneManager::onFilePlayerEnd(unsigned int eventId) {
+	switch (eventId) {
+		case MS_PLAYER_EOF:
+			lInfo() << "[ToneManager] " << __func__;
+			doStopTone();
+			break;
+		default:
+			break;
+	}
+}
+
+// ---------------------------------------------------
+// tester
+// ---------------------------------------------------
+
+LinphoneCoreToneManagerStats *ToneManager::getStats() {
+	return mStats;
+}
+
+void ToneManager::resetStats() {
+	*mStats = {0, 0, 0, 0, 0, 0, 0};
+}
+
+// ---------------------------------------------------
+// sessions
+// ---------------------------------------------------
+
+/* set State for the session. Insert session if not present */
+void ToneManager::setState(const std::shared_ptr<CallSession> &session, ToneManager::State newState) {
+	if (mSessions.count(session) == 0) {
+		lInfo() << "[ToneManager] add new session [" << session << "]";
+	}
+	mSessions[session] = newState;
+}
+
+ToneManager::State ToneManager::getState(const std::shared_ptr<CallSession> &session) {
+	auto search = mSessions.find(session);
+    if (search != mSessions.end()) {
+        return search->second;
+    } else {
+		return State::None;
+    }
+}
+
+bool ToneManager::isAnotherSessionInState(const std::shared_ptr<CallSession> &me, ToneManager::State state) {
+	for (auto it = mSessions.begin(); it != mSessions.end(); it++) {
+		if (it->second == state && it->first != me) {
+			return true;
+		}
+	}
+	return false;
+}
+
+bool ToneManager::getSessionInState(ToneManager::State state, std::shared_ptr<CallSession> &session) {
+	for (auto it = mSessions.begin(); it != mSessions.end(); it++) {
+		if (it->second == state) {
+			session = it->first;
+			return true;
+		}
+	}
+	return false;
+}
+
+bool ToneManager::isThereACall() {
+	for (auto it = mSessions.begin(); it != mSessions.end(); it++) {
+		if (it->second == State::Call) {
+			return true;
+		}
+	}
+	return false;
+}
+
+// ---------------------------------------------------
+// start
+// ---------------------------------------------------
+
+void ToneManager::doStartErrorTone(const std::shared_ptr<CallSession> &session, LinphoneReason reason) {
+	lInfo() << "[ToneManager] " << __func__ << " [" << Utils::toString(reason) << "]";
+	LinphoneToneDescription *tone = getToneFromReason(reason);
+
+	if (tone) {
+		if (tone->audiofile) {
+			setState(session, State::Tone);
+			playFile(tone->audiofile);
+		} else if (tone->toneid != LinphoneToneUndefined) {
+			setState(session, State::Tone);
+			MSDtmfGenCustomTone dtmfTone = generateToneFromId(tone->toneid);
+			playTone(dtmfTone);
+		}
+	}
+}
+
+void ToneManager::doStartNamedTone(LinphoneToneID toneId) {
+	lInfo() << "[ToneManager] " << __func__ << " [" << Utils::toString(toneId) << "]";
+	LinphoneToneDescription *tone = getToneFromId(toneId);
+
+	if (tone && tone->audiofile) {
+		playFile(tone->audiofile);
+	} else {
+		MSDtmfGenCustomTone dtmfTone = generateToneFromId(toneId);
+		playTone(dtmfTone);
+	}
+}
+
+void ToneManager::doStartRingbackTone(const std::shared_ptr<CallSession> &session) {
+	lInfo() << "[ToneManager] " << __func__;
+	LinphoneCore *lc = getCore()->getCCore();
+
+	if (!lc->sound_conf.play_sndcard)
+		return;
+
+	MSSndCard *ringCard = lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
+	int maxRate = std::static_pointer_cast<MediaSession>(session)->getPrivate()->getLocalDesc()->streams[0].max_rate;
+	if (maxRate > 0) ms_snd_card_set_preferred_sample_rate(ringCard, maxRate);
+
+	/* We release sound before playing ringback tone */
+	AudioStream *as = reinterpret_cast<AudioStream *>(std::static_pointer_cast<MediaSession>(session)->getPrivate()->getMediaStream(LinphoneStreamTypeAudio));
+	if (as) audio_stream_unprepare_sound(as);
+
+	if (lc->sound_conf.remote_ring) {
+		ms_snd_card_set_stream_type(ringCard, MS_SND_CARD_STREAM_VOICE);
+		lc->ringstream = ring_start(lc->factory, lc->sound_conf.remote_ring, 2000, ringCard);
+	}
+}
+
+void ToneManager::doStartRingtone(const std::shared_ptr<CallSession> &session) {
+	lInfo() << "[ToneManager] " << __func__;
+	LinphoneCore *lc = getCore()->getCCore();
+	if (isAnotherSessionInState(session, State::Call)) {
+		/* play a tone within the context of the current call */
+		doStartNamedTone(LinphoneToneCallWaiting);
+	} else {
+		MSSndCard *ringcard = lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard;
+		if (ringcard) {
+			ms_snd_card_set_stream_type(ringcard, MS_SND_CARD_STREAM_RING);
+			linphone_ringtoneplayer_start(lc->factory, lc->ringtoneplayer, ringcard, lc->sound_conf.local_ring, 2000);
+		}
+	}
+}
+
+// ---------------------------------------------------
+// stop
+// ---------------------------------------------------
+
+void ToneManager::doStop(const std::shared_ptr<CallSession> &session, ToneManager::State newState) {
+	switch (getState(session)) {
+		case ToneManager::Ringback:
+			setState(session, newState);
+			doStopRingbackTone();
+			mStats->number_of_stopRingbackTone++;
+			break;
+		case ToneManager::Ringtone:
+			setState(session, newState);
+			doStopRingtone(session);
+			/* start again ringtone in update() in case another call is in Ringtone state */
+			mStats->number_of_stopRingtone++;
+			break;
+		case ToneManager::Tone:
+			setState(session, newState);
+			doStopTone();
+			mStats->number_of_stopTone++;
+			break;
+		case ToneManager::Call:
+			setState(session, newState);
+			if (isAnotherSessionInState(session, ToneManager::Ringtone)) {
+				doStopTone();
+			}
+			break;
+		default:
+			lInfo() << "[ToneManager] nothing to stop";
+			break;
+	}
+}
+
+void ToneManager::doStopRingbackTone() {
+	lInfo() << "[ToneManager] " << __func__;
+	LinphoneCore *lc = getCore()->getCCore();
+	if (lc->ringstream) {
+		ring_stop(lc->ringstream);
+		lc->ringstream = NULL;
+	}
+}
+
+void ToneManager::doStopTone() {
+	lInfo() << "[ToneManager] " << __func__;
+	LinphoneCore *lc = getCore()->getCCore();
+	if (lc->ringstream) {
+		ring_stop(lc->ringstream);
+		lc->ringstream = NULL;
+	}
+
+	if (isThereACall()) {
+		MSFilter *f = getAudioResource(ToneGenerator, NULL, FALSE);
+		if (f != NULL) ms_filter_call_method_noarg(f, MS_DTMF_GEN_STOP);
+	}
+}
+
+void ToneManager::doStopToneToPlaySomethingElse(const std::shared_ptr<CallSession> &session) {
+	lInfo() << "[ToneManager] " << __func__;
+	if (isAnotherSessionInState(session, State::Tone)) {
+		doStopTone();
+	}
+}
+
+
+void ToneManager::doStopRingtone(const std::shared_ptr<CallSession> &session) {
+	lInfo() << "[ToneManager] " << __func__;
+	if (isAnotherSessionInState(session, State::Call)) {
+		/* stop the tone within the context of the current call */
+		doStopTone();
+	} else {
+		LinphoneCore *lc = getCore()->getCCore();
+		if (linphone_ringtoneplayer_is_started(lc->ringtoneplayer)) {
+			linphone_ringtoneplayer_stop(lc->ringtoneplayer);
+		}
+	}
+}
+
+// ---------------------------------------------------
+// sound
+// ---------------------------------------------------
+
+LinphoneStatus ToneManager::playFile(const char *audiofile) {
+	LinphoneCore *lc = getCore()->getCCore();
+	MSFilter *f = getAudioResource(LocalPlayer, lc->sound_conf.play_sndcard, true);
+	int loopms = -1;
+	if (!f) return -1;
+	ms_filter_call_method(f, MS_PLAYER_SET_LOOP, &loopms);
+	if (ms_filter_call_method(f, MS_PLAYER_OPEN, (void*) audiofile) != 0) {
+		return -1;
+	}
+	ms_filter_call_method_noarg(f, MS_PLAYER_START);
+
+	if (lc->ringstream && lc->ringstream->source) {
+		ms_filter_add_notify_callback(lc->ringstream->source, &on_file_player_end, this, FALSE);
+	}
+	return 0;
+}
+
+MSDtmfGenCustomTone ToneManager::generateToneFromId(LinphoneToneID toneId) {
+	MSDtmfGenCustomTone def;
+	memset(&def, 0, sizeof(def));
+	def.amplitude = 1;
+	/*these are french tones, excepted the failed one, which is USA congestion tone (does not exist in France)*/
+	switch(toneId) {
+		case LinphoneToneCallOnHold:
+		case LinphoneToneCallWaiting:
+			def.duration=300;
+			def.frequencies[0]=440;
+			def.interval=2000;
+		break;
+		case LinphoneToneBusy:
+			def.duration=500;
+			def.frequencies[0]=440;
+			def.interval=500;
+			def.repeat_count=3;
+		break;
+		case LinphoneToneCallLost:
+			def.duration=250;
+			def.frequencies[0]=480;
+			def.frequencies[0]=620;
+			def.interval=250;
+			def.repeat_count=3;
+		break;
+		default:
+			lWarning() << "[ToneManager] Unhandled tone id.";
+	}
+	return def;
+}
+
+void ToneManager::playTone(MSDtmfGenCustomTone tone) {
+    LinphoneCore *lc = getCore()->getCCore();
+	MSFilter *f = getAudioResource(ToneGenerator, lc->sound_conf.play_sndcard, true);
+	if (f == NULL) {
+		lError() << "[ToneManager] No tone generator at this time !";
+		return;
+	}
+	if (tone.duration > 0) {
+		ms_filter_call_method(f, MS_DTMF_GEN_PLAY_CUSTOM, &tone);
+		if (tone.repeat_count > 0) {
+			int delay = (tone.duration + tone.interval) * tone.repeat_count + 1000;
+			createTimerToCleanTonePlayer((unsigned int) delay);
+		}
+	}
+}
+
+MSFilter *ToneManager::getAudioResource(AudioResourceType rtype, MSSndCard *card, bool create) {
+	LinphoneCore *lc = getCore()->getCCore();
+	LinphoneCall *call = linphone_core_get_current_call(lc);
+	AudioStream *stream = NULL;
+	RingStream *ringstream;
+	if (call) {
+		stream = reinterpret_cast<AudioStream *>(linphone_call_get_stream(call, LinphoneStreamTypeAudio));
+	} else if (linphone_core_is_in_conference(lc)) {
+		stream = linphone_conference_get_audio_stream(lc->conf_ctx);
+	}
+	if (stream) {
+		if (rtype == ToneGenerator) return stream->dtmfgen;
+		if (rtype == LocalPlayer) return stream->local_player;
+		return NULL;
+	}
+	if (card && lc->ringstream && card != lc->ringstream->card) {
+		ring_stop(lc->ringstream);
+		lc->ringstream = NULL;
+	}
+	if (lc->ringstream == NULL) {
+		float amp = lp_config_get_float(lc->config, "sound", "dtmf_player_amp", 0.1f);
+		MSSndCard *ringcard = lc->sound_conf.lsd_card
+			? lc->sound_conf.lsd_card
+			: card
+				? card
+				: lc->sound_conf.ring_sndcard;
+
+		if (ringcard == NULL) return NULL;
+		if (!create) return NULL;
+
+		ringstream = lc->ringstream = ring_start(lc->factory, NULL, 0, ringcard);
+		ms_filter_call_method(lc->ringstream->gendtmf, MS_DTMF_GEN_SET_DEFAULT_AMPLITUDE, &amp);
+	} else {
+		ringstream = lc->ringstream;
+	}
+	if (rtype == ToneGenerator) return ringstream->gendtmf;
+	if (rtype == LocalPlayer) return ringstream->source;
+	return NULL;
+}
+
+LINPHONE_END_NAMESPACE
diff --git a/src/conference/session/tone-manager.h b/src/conference/session/tone-manager.h
new file mode 100644
index 0000000000000000000000000000000000000000..b862a7dafa92f598553f21c8e499b1bf9b049499
--- /dev/null
+++ b/src/conference/session/tone-manager.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2010-2019 Belledonne Communications SARL.
+ *
+ * This file is part of Liblinphone.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "conference/session/call-session.h"
+#include "core/core-accessor.h"
+#include "private.h"
+#include "mediastreamer2/dtmfgen.h"
+#include "core/core.h"
+#include "tester_utils.h"
+#include <map>
+
+LINPHONE_BEGIN_NAMESPACE
+
+class ToneManager : public CoreAccessor {
+    public:
+        ToneManager(std::shared_ptr<Core> core);
+        ~ToneManager();
+
+        // public entrypoints for tones
+        void startRingbackTone(const std::shared_ptr<CallSession> &session);
+        void startRingtone(const std::shared_ptr<CallSession> &session);
+        void startErrorTone(const std::shared_ptr<CallSession> &session, LinphoneReason reason);
+        void startNamedTone(const std::shared_ptr<CallSession> &session, LinphoneToneID toneId);
+        void goToCall(const std::shared_ptr<CallSession> &session);
+        void stop(const std::shared_ptr<CallSession> &session);
+        void removeSession(const std::shared_ptr<CallSession> &session);
+        void update(const std::shared_ptr<CallSession> &session);
+
+        // linphone core public API entrypoints
+        void linphoneCorePlayDtmf(char dtmf, int duration);
+        void linphoneCoreStopDtmf();
+        LinphoneStatus linphoneCorePlayLocal(const char *audiofile);
+        void linphoneCoreStartDtmfStream();
+        void linphoneCoreStopRinging();
+        void linphoneCoreStopDtmfStream();
+
+        // callback file player
+        void onFilePlayerEnd(unsigned int eventId);
+
+        // tester
+        LinphoneCoreToneManagerStats *getStats();
+        void resetStats();
+
+        // timer
+        void deleteTimer();
+
+        //tones setup
+        LinphoneToneDescription *getToneFromReason(LinphoneReason reason);
+        LinphoneToneDescription *getToneFromId(LinphoneToneID id);
+        void setTone(LinphoneReason reason, LinphoneToneID id, const char *audiofile);
+
+    private:
+        using AudioResourceType = enum {
+            ToneGenerator,
+            LocalPlayer
+        };
+
+        using State = enum {
+            None,     // No tone played, not in call (the session just started or will end soon)
+            Call,     // Running call
+            Ringback, // Play Ringback tone
+            Ringtone, // Play Ringtone or play a tone over the current call
+            Tone      // Play a DTMF tone or a tone file
+        };
+        std::string stateToString(ToneManager::State state);
+        void printDebugInfo(const std::shared_ptr<CallSession> &session);
+
+        std::map<std::shared_ptr<CallSession>, ToneManager::State> mSessions;
+        belle_sip_source_t *mTimer = nullptr;
+        LinphoneCoreToneManagerStats *mStats;
+
+        // timer
+        void createTimerToCleanTonePlayer(unsigned int delay);
+
+        //sessions
+        void setState(const std::shared_ptr<CallSession> &session, ToneManager::State newState);
+        ToneManager::State getState(const std::shared_ptr<CallSession> &session);
+        bool isAnotherSessionInState(const std::shared_ptr<CallSession> &me, ToneManager::State state);
+        bool getSessionInState(ToneManager::State state, std::shared_ptr<CallSession> &session);
+        bool isThereACall();
+
+        // start
+        void doStartRingbackTone(const std::shared_ptr<CallSession> &session);
+        void doStartRingtone(const std::shared_ptr<CallSession> &session);
+        void doStartErrorTone(const std::shared_ptr<CallSession> &session, LinphoneReason reason);
+        void doStartNamedTone(LinphoneToneID toneId);
+
+        // stop
+        void doStopRingbackTone();
+        void doStopTone();
+        void doStopToneToPlaySomethingElse(const std::shared_ptr<CallSession> &session);
+        void doStopRingtone(const std::shared_ptr<CallSession> &session);
+        void doStop(const std::shared_ptr<CallSession> &session, ToneManager::State newState);
+
+        // sound
+        MSFilter *getAudioResource(AudioResourceType rtype, MSSndCard *card, bool create);
+        LinphoneStatus playFile(const char *audiofile);
+        void playTone(MSDtmfGenCustomTone dtmf);
+        MSDtmfGenCustomTone generateToneFromId(LinphoneToneID toneId);
+};
+
+LINPHONE_END_NAMESPACE
diff --git a/src/core/core-p.h b/src/core/core-p.h
index 245d891d33e2c89ad0202b6f4d3d1b8ef05211ff..9820ccb748cce731c6c060a94e2b1bef08ca9ec1 100644
--- a/src/core/core-p.h
+++ b/src/core/core-p.h
@@ -28,6 +28,7 @@
 #include "object/object-p.h"
 #include "sal/call-op.h"
 #include "auth-info/auth-stack.h"
+#include "conference/session/tone-manager.h"
 
 // =============================================================================
 
@@ -80,6 +81,8 @@ public:
 	void insertChatRoomWithDb (const std::shared_ptr<AbstractChatRoom> &chatRoom, unsigned int notifyId = 0);
 	std::shared_ptr<AbstractChatRoom> createBasicChatRoom (const ConferenceId &conferenceId, AbstractChatRoom::CapabilitiesMask capabilities, const std::shared_ptr<ChatRoomParams> &params);
 
+	std::shared_ptr<ToneManager> getToneManager();
+
 	//Base
 	std::shared_ptr<AbstractChatRoom> createClientGroupChatRoom (
 		const std::string &subject,
@@ -144,6 +147,8 @@ private:
 
 	std::list<std::string> specs;
 
+	std::shared_ptr<ToneManager> toneManager;
+
 	// This is to keep a ref on a clientGroupChatRoom while it is being created
 	// Otherwise the chatRoom will be freed() before it is inserted
 	std::unordered_map<const AbstractChatRoom *, std::shared_ptr<const AbstractChatRoom>> noCreatedClientGroupChatRooms;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 71fd3c678ca9fc38deb0ea064fcf0781773826f5..81a228ef1c1fc575361bf39c9a3ae6db759337ef 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -116,6 +116,8 @@ void CorePrivate::uninit () {
 		ms_usleep(10000);
 	}
 
+	if (toneManager) toneManager->deleteTimer();
+
 	chatRoomsById.clear();
 	noCreatedClientGroupChatRooms.clear();
 	listeners.clear();
@@ -225,6 +227,13 @@ bool CorePrivate::basicToFlexisipChatroomMigrationEnabled()const{
 CorePrivate::CorePrivate() : authStack(*this){
 }
 
+std::shared_ptr<ToneManager> CorePrivate::getToneManager() {
+	L_Q();
+	if (!toneManager) {
+		toneManager = make_shared<ToneManager>(q->getSharedFromThis());
+	}
+	return toneManager;
+}
 
 // =============================================================================
 
@@ -570,5 +579,14 @@ void Core::doLater(const std::function<void ()> &something){
 	getPrivate()->doLater(something);
 }
 
+belle_sip_source_t *Core::createTimer(const std::function<bool ()> &something, unsigned int milliseconds){
+	return belle_sip_main_loop_create_cpp_timeout_2(getPrivate()->getMainLoop(), something, milliseconds, "");
+}
+/* Stop and destroy a timer created by createTimer()*/
+void Core::destroyTimer(belle_sip_source_t *timer){
+	belle_sip_main_loop_remove_source(getPrivate()->getMainLoop(), timer);
+	belle_sip_object_unref(timer);
+}
+
 
 LINPHONE_END_NAMESPACE
diff --git a/src/core/core.h b/src/core/core.h
index e0cd8c0d46a81e9899ea50a2471f5b4823540373..4274e305f7d88d8c202dddf02bec65f7bf16e966 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -31,6 +31,8 @@
 
 L_DECL_C_STRUCT(LinphoneCore);
 
+typedef struct belle_sip_source belle_sip_source_t;
+
 LINPHONE_BEGIN_NAMESPACE
 
 class AbstractChatRoom;
@@ -64,6 +66,8 @@ class LINPHONE_PUBLIC Core : public Object {
 	friend class RemoteConferenceListEventHandler;
 	friend class ServerGroupChatRoom;
 	friend class ServerGroupChatRoomPrivate;
+	friend class CallSessionPrivate;
+	friend class ToneManager;
 
 public:
 	L_OVERRIDE_SHARED_FROM_THIS(Core);
@@ -186,6 +190,13 @@ public:
 	// Execute specified lambda later in main loop. This method can be used from any thread to execute something later on main thread.
 	void doLater(const std::function<void ()> &something);
 
+	/*
+	 * Run supplied std::function as a timer. It should return true if repeated, false otherwise.
+	 * It may be unrefed with (with belle_sip_object_unref()) before expiration, if this timer never needs to be cancelled.
+	 */
+	belle_sip_source_t *createTimer(const std::function<bool ()> &something, unsigned int milliseconds);
+	/* Stop (ie cancel) and destroy a timer created by createTimer() */
+	void destroyTimer(belle_sip_source_t *timer);
 private:
 	Core ();
 
diff --git a/tester/call_multi_tester.c b/tester/call_multi_tester.c
index eed7ec2050f845c7f3ae02781fd3fffa9a9bae9f..12d14eca9b208c948a17777d6e02ecb5870a7956 100644
--- a/tester/call_multi_tester.c
+++ b/tester/call_multi_tester.c
@@ -228,6 +228,7 @@ static void incoming_call_accepted_when_outgoing_call_in_state(LinphoneCallState
 									,pauline->lc
 									,state==LinphoneCallOutgoingEarlyMedia?&marie->stat.number_of_LinphoneCallOutgoingEarlyMedia:&marie->stat.number_of_LinphoneCallOutgoingRinging
 									,1));
+       BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &linphone_core_get_tone_manager_stats(marie->lc)->number_of_startRingbackTone, 1));
 	} else if (state==LinphoneCallOutgoingProgress) {
 		BC_ASSERT_PTR_NOT_NULL(linphone_core_invite_address(marie->lc,pauline->identity));
 	} else {
@@ -237,6 +238,11 @@ static void incoming_call_accepted_when_outgoing_call_in_state(LinphoneCallState
 
 	BC_ASSERT_TRUE(call_with_caller_params(laure,marie,laure_params));
 
+	if (state==LinphoneCallOutgoingRinging) {
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_startRingtone, 0, int, "%d");
+	} else if (state==LinphoneCallOutgoingProgress || state==LinphoneCallOutgoingEarlyMedia) {
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_startRingtone, 1, int, "%d");
+	}
 
 	BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,10000));
 
@@ -1162,6 +1168,7 @@ void do_not_stop_ringing_when_declining_one_of_two_incoming_calls(void) {
 							,&pauline->stat.number_of_LinphoneCallIncomingReceived
 							,1));
 	pauline_called_by_laure=linphone_core_get_current_call(pauline->lc);
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startRingtone, 1, int, "%d");
 
 	BC_ASSERT_PTR_NOT_NULL(linphone_core_invite_address_with_params(marie->lc,pauline->identity,marie_params));
 	linphone_call_params_unref(marie_params);
@@ -1171,16 +1178,23 @@ void do_not_stop_ringing_when_declining_one_of_two_incoming_calls(void) {
 							,&pauline->stat.number_of_LinphoneCallIncomingReceived
 							,2));
 	pauline_called_by_marie=linphone_core_get_current_call(marie->lc);
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startRingtone, 1, int, "%d");
+
 	linphone_call_decline(pauline_called_by_laure, LinphoneReasonDeclined);
 	BC_ASSERT_TRUE(wait_for(laure->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallEnd,1));
 	BC_ASSERT_TRUE(wait_for(laure->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallReleased,1));
 
+	// check that rigntone player restart
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopRingtone, 1, int, "%d");
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startRingtone, 2, int, "%d");
+
 	BC_ASSERT_TRUE(linphone_ringtoneplayer_is_started(linphone_core_get_ringtoneplayer(pauline->lc)));
 	linphone_call_terminate(pauline_called_by_marie);
 	BC_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallEnd,1));
 	BC_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallReleased,1));
 	BC_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallEnd,2));
 	BC_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallReleased,2));
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopRingtone, 2, int, "%d");
 
 	linphone_core_manager_destroy(marie);
 	linphone_core_manager_destroy(pauline);
diff --git a/tester/call_single_tester.c b/tester/call_single_tester.c
index 2f8bbf5cc90377bf672cb7748569b960d1ae14f5..d5c35602b36ed3805db9eaeae9848c73c243f9e0 100644
--- a/tester/call_single_tester.c
+++ b/tester/call_single_tester.c
@@ -970,6 +970,10 @@ static void cancel_other_device_after_decline(void) {
 	BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallOutgoingInit,1));
 	BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallIncomingReceived, 1));
 	BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallOutgoingProgress, 1));
+
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr->lc)->number_of_startRingtone, 1, int, "%d");
+	BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr->lc, &linphone_core_get_tone_manager_stats(caller_mgr->lc)->number_of_startRingbackTone, 1));
+
 	call_callee = linphone_core_get_current_call(callee_mgr->lc);
 	if (BC_ASSERT_PTR_NOT_NULL(call_callee)) {
 		linphone_call_ref(call_callee);
@@ -978,13 +982,20 @@ static void cancel_other_device_after_decline(void) {
 		call_callee_2 = linphone_core_get_current_call(callee_mgr_2->lc);
 		linphone_call_ref(call_callee_2);
 		BC_ASSERT_PTR_NOT_NULL(call_callee_2);
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr_2->lc)->number_of_startRingtone, 1, int, "%d");
 
 		BC_ASSERT_EQUAL(linphone_call_decline(call_callee, LinphoneReasonDeclined), 0 , int, "%d");
 		BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallEnd,1));
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(caller_mgr->lc)->number_of_stopRingbackTone, 1, int, "%d");
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(caller_mgr->lc)->number_of_startErrorTone, 1, int, "%d");
 		BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallReleased, 1));
+
 		BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallEnd,1));
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr->lc)->number_of_stopRingtone, 1, int, "%d");
 		BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallReleased, 1));
+
 		BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr_2->lc, &callee_mgr_2->stat.number_of_LinphoneCallEnd,1));
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr_2->lc)->number_of_stopRingtone, 1, int, "%d");
 		BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr_2->lc, &callee_mgr_2->stat.number_of_LinphoneCallReleased,1));
 
 		rei = linphone_call_get_error_info(call_callee_2);
@@ -996,6 +1007,9 @@ static void cancel_other_device_after_decline(void) {
 			BC_ASSERT_STRING_EQUAL(linphone_error_info_get_protocol(rei), "SIP");
 		}
 		BC_ASSERT_EQUAL(linphone_call_log_get_status(linphone_call_get_call_log(call_callee_2)), LinphoneCallDeclinedElsewhere, int, "%d");
+
+		// There is currently no tone for LinphoneReasonDeclined, so there is no call to stopTone
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(caller_mgr->lc)->number_of_stopTone, 0, int, "%d");
 	}
 	if (out_call) linphone_call_unref(out_call);
 	if (call_callee) linphone_call_unref(call_callee);
@@ -1208,10 +1222,7 @@ static void early_declined_call(void) {
 	/*wait until flexisip transfers the busy...*/
 	BC_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallError,1,33000));
 	BC_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallError,1, int, "%d");
-	/* FIXME http://git.linphone.org/mantis/view.php?id=757
-
 	BC_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonBusy, int, "%d");
-	 */
 	if (bctbx_list_size(linphone_core_get_call_logs(pauline->lc))>0) {
 		BC_ASSERT_PTR_NOT_NULL(out_call_log=(LinphoneCallLog*)(linphone_core_get_call_logs(pauline->lc)->data));
 		BC_ASSERT_EQUAL(linphone_call_log_get_status(out_call_log),LinphoneCallAborted, int, "%d");
@@ -1366,10 +1377,15 @@ static void call_declined_base(bool_t use_timeout) {
 	BC_ASSERT_PTR_NOT_NULL(in_call=linphone_core_get_current_call(marie->lc));
 	if (in_call) {
 		linphone_call_ref(in_call);
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_startRingtone, 1, int, "%d");
+		BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startRingbackTone,1));
 		if (!use_timeout)
 			linphone_call_terminate(in_call);
 		BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallReleased,1));
 		BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallReleased,1));
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_stopRingtone, 1, int, "%d");
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopRingbackTone, 1, int, "%d");
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startErrorTone, 1, int, "%d");
 		BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallEnd,1, int, "%d");
 		BC_ASSERT_EQUAL(use_timeout ? pauline->stat.number_of_LinphoneCallError : pauline->stat.number_of_LinphoneCallEnd,1, int, "%d");
 		BC_ASSERT_EQUAL(linphone_call_get_reason(in_call),LinphoneReasonDeclined, int, "%d");
@@ -1379,6 +1395,13 @@ static void call_declined_base(bool_t use_timeout) {
 		linphone_call_unref(in_call);
 	}
 	linphone_call_unref(out_call);
+
+	// Make sure the error tone ends by calling linphone_core_stop
+	linphone_core_stop(pauline->lc);
+	// In case of LinphoneReasonDeclined no tone is played, so there is no tone to stop
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopTone, use_timeout? 1 : 0, int, "%d");
+	linphone_core_start(pauline->lc);
+
 	linphone_core_manager_destroy(marie);
 	linphone_core_manager_destroy(pauline);
 }
@@ -1973,6 +1996,7 @@ void call_paused_resumed_base(bool_t multicast, bool_t with_losses) {
 	BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallPausedByRemote,1));
 	BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallPaused,1));
 
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startNamedTone, 1, int, "%d");
 	/*stay in pause a little while in order to generate traffic*/
 	wait_for_until(pauline->lc, marie->lc, NULL, 5, 2000);
 
@@ -1980,6 +2004,7 @@ void call_paused_resumed_base(bool_t multicast, bool_t with_losses) {
 
 	BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,2));
 	BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,2));
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopTone, 1, int, "%d");
 	/*same here: wait a while for a bit of a traffic, we need to receive a RTCP packet*/
 	wait_for_until(pauline->lc, marie->lc, NULL, 5, 5000);
 
@@ -2420,6 +2445,9 @@ static void call_with_file_player(void) {
 	for (attempts=0; attempts<3; attempts++){
 		reset_counters(&marie->stat);
 		reset_counters(&pauline->stat);
+		linphone_core_reset_tone_manager_stats(marie->lc);
+		linphone_core_reset_tone_manager_stats(pauline->lc);
+
 		/*make sure the record file doesn't already exists, otherwise this test will append new samples to it*/
 		unlink(recordpath);
 		/*caller uses files instead of soundcard in order to avoid mixing soundcard input with file played using call's player*/
@@ -2735,6 +2763,8 @@ static void early_media_call_with_ringing_base(bool_t network_change){
 
 	BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived,1,3000));
 	BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingRinging,1,1000));
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startRingtone, 1, int, "%d");
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_startRingbackTone, 1, int, "%d");
 
 	if (linphone_core_inc_invite_pending(pauline->lc)) {
 		/* send a 183 to initiate the early media */
@@ -2744,6 +2774,10 @@ static void early_media_call_with_ringing_base(bool_t network_change){
 		BC_ASSERT_TRUE( wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingEarlyMedia,1,2000) );
 		BC_ASSERT_TRUE(linphone_call_get_all_muted(marie_call));
 
+		bool_t ringWithEarlyMedia = linphone_core_get_ring_during_incoming_early_media(pauline->lc);
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopRingtone, ringWithEarlyMedia ? 0 : 1, int, "%d");
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_stopRingbackTone, 1, int, "%d");
+
 		liblinphone_tester_check_rtcp(marie, pauline);
 
 		/* this is a hack to simulate an incoming OK with a different IP address
@@ -2759,6 +2793,7 @@ static void early_media_call_with_ringing_base(bool_t network_change){
 				BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallConnected, 1,1000));
 				connected_time=ms_get_cur_time_ms();
 				BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 1,1000));
+				BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopRingtone, 1, int, "%d");
 
 				BC_ASSERT_PTR_EQUAL(marie_call, linphone_core_get_current_call(marie->lc));
 				BC_ASSERT_FALSE(linphone_call_get_all_muted(marie_call));
@@ -2807,6 +2842,8 @@ static void early_media_call_with_update_base(bool_t media_change){
 
 	BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived,1,5000));
 	BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingRinging,1,5000));
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_startRingtone, 1, int, "%d");
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_startRingbackTone, 1, int, "%d");
 
 	pauline_call = linphone_core_get_current_call(pauline->lc);
 	if (!pauline_call) goto end;
@@ -2816,6 +2853,10 @@ static void early_media_call_with_update_base(bool_t media_change){
 	BC_ASSERT_TRUE( wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingEarlyMedia,1,5000) );
 	BC_ASSERT_TRUE(linphone_call_get_all_muted(marie_call));
 
+	bool_t ringWithEarlyMedia = linphone_core_get_ring_during_incoming_early_media(pauline->lc);
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(pauline->lc)->number_of_stopRingtone, ringWithEarlyMedia ? 0 : 1, int, "%d");
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(marie->lc)->number_of_stopRingbackTone, 1, int, "%d");
+
 	pauline_params = linphone_call_params_copy(linphone_call_get_current_params(pauline_call));
 
 	if (media_change) {
@@ -4131,6 +4172,8 @@ static void call_with_rtp_io_mode(void) {
 		unlink(recordpath);
 		reset_counters(&marie->stat);
 		reset_counters(&pauline->stat);
+		linphone_core_reset_tone_manager_stats(marie->lc);
+		linphone_core_reset_tone_manager_stats(pauline->lc);
 
 		/* The caller uses files instead of soundcard in order to avoid mixing soundcard input with file played using call's player. */
 		linphone_core_use_files(marie->lc, TRUE);
@@ -4701,11 +4744,15 @@ static void call_logs_sqlite_storage(void) {
 
 	reset_counters(&marie->stat);
 	reset_counters(&pauline->stat);
+	linphone_core_reset_tone_manager_stats(marie->lc);
+	linphone_core_reset_tone_manager_stats(pauline->lc);
 	BC_ASSERT_TRUE(call(marie, pauline));
 	end_call(marie, pauline);
 
 	reset_counters(&marie->stat);
 	reset_counters(&pauline->stat);
+	linphone_core_reset_tone_manager_stats(marie->lc);
+	linphone_core_reset_tone_manager_stats(pauline->lc);
 	BC_ASSERT_TRUE(call(marie, pauline));
 	end_call(marie, pauline);
 	BC_ASSERT_TRUE(linphone_core_get_call_history_size(marie->lc) == 2);
diff --git a/tester/tester.c b/tester/tester.c
index f9722196899308b773d71a6c802fffc93b70ee02..b44708a443221c0d598a279bbc4116058e71ad5b 100644
--- a/tester/tester.c
+++ b/tester/tester.c
@@ -1744,6 +1744,15 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr
 		linphone_address_unref(callee_from);
 	}
 
+	LinphoneCoreToneManagerStats *callee_stats = linphone_core_get_tone_manager_stats(callee_mgr->lc);
+	if (callee_stats->number_of_startRingbackTone == callee_stats->number_of_stopRingbackTone) {
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr->lc)->number_of_startRingtone, callee_mgr->stat.number_of_LinphoneCallIncomingReceived, int, "%d");
+	} else {
+		// in this case, the call is currently in RingbackTone so the Ringtone should not start
+		BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr->lc)->number_of_startRingtone, callee_mgr->stat.number_of_LinphoneCallIncomingReceived-1, int, "%d");
+	}
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(caller_mgr->lc)->number_of_startRingbackTone, caller_mgr->stat.number_of_LinphoneCallOutgoingRinging, int, "%d");
+
 
 	if (callee_params){
 		linphone_call_accept_with_params(callee_call,callee_params);
@@ -1765,6 +1774,9 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr
 			&&
 			wait_for_until(callee_mgr->lc,caller_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallStreamsRunning,initial_callee.number_of_LinphoneCallStreamsRunning+1, 2000);
 
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(callee_mgr->lc)->number_of_stopRingtone, callee_mgr->stat.number_of_LinphoneCallIncomingReceived, int, "%d");
+	BC_ASSERT_EQUAL(linphone_core_get_tone_manager_stats(caller_mgr->lc)->number_of_stopRingbackTone, caller_mgr->stat.number_of_LinphoneCallOutgoingRinging, int, "%d");
+
 	if (linphone_core_get_media_encryption(caller_mgr->lc) != LinphoneMediaEncryptionNone
 		|| linphone_core_get_media_encryption(callee_mgr->lc) != LinphoneMediaEncryptionNone) {
 		/*wait for encryption to be on, in case of zrtp or dtls, it can take a few seconds*/