dtmfgen.c 7.34 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  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
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "mediastreamer2/dtmfgen.h"
21
#include "mediastreamer2/msticker.h"
aymeric's avatar
aymeric committed
22 23 24 25 26 27 28 29


#include <math.h>

#ifndef M_PI
#define M_PI       3.14159265358979323846
#endif

Simon Morlat's avatar
Simon Morlat committed
30

31
#define NO_SAMPLES_THRESHOLD 100 /*ms*/
Simon Morlat's avatar
Simon Morlat committed
32 33 34 35

#ifdef ANDROID
#define TRAILLING_SILENCE 10000 /*ms*/
#else
36
#define TRAILLING_SILENCE 500 /*ms*/
Simon Morlat's avatar
Simon Morlat committed
37
#endif
38

39

aymeric's avatar
aymeric committed
40 41 42 43 44 45
struct DtmfGenState{
	int rate;
	int dur;
	int pos;
	float highfreq;
	float lowfreq;
46 47
	int nosamples_time;
	int silence;
48
	int amplitude;
aymeric's avatar
aymeric committed
49
	char dtmf;
50
	float default_amplitude;
51
	int interval;
aymeric's avatar
aymeric committed
52 53 54 55 56 57 58 59 60 61
};

typedef struct DtmfGenState DtmfGenState;

static void dtmfgen_init(MSFilter *f){
	DtmfGenState *s=(DtmfGenState *)ms_new(DtmfGenState,1);
	s->rate=8000;
	s->dur=s->rate/10;
	s->pos=0;
	s->dtmf=0;
62 63
	s->nosamples_time=0;
	s->silence=0;
Simon Morlat's avatar
Simon Morlat committed
64
	s->default_amplitude=0.2;
65
	s->amplitude=(s->default_amplitude*0.7*32767);
66
	s->interval=0;
aymeric's avatar
aymeric committed
67 68 69 70 71 72 73 74 75 76
	f->data=s;
}

static void dtmfgen_uninit(MSFilter *f){
	ms_free(f->data);
}

static int dtmfgen_put(MSFilter *f, void *arg){
	DtmfGenState *s=(DtmfGenState*)f->data;
	const char *dtmf=(char*)arg;
77
	
aymeric's avatar
aymeric committed
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
	switch(dtmf[0]){
		case '0':
			s->lowfreq=941;
			s->highfreq=1336;
			break;
		case '1':
			s->lowfreq=697;
			s->highfreq=1209;
			break;
		case '2':
			s->lowfreq=697;
			s->highfreq=1336;
			break;
		case '3':
			s->lowfreq=697;
			s->highfreq=1477;
			break;
		case '4':
			s->lowfreq=770;
			s->highfreq=1209;
			break;
		case '5':
			s->lowfreq=770;
			s->highfreq=1336;
			break;
		case '6':
			s->lowfreq=770;
			s->highfreq=1477;
			break;
		case '7':
			s->lowfreq=852;
			s->highfreq=1209;
			break;
		case '8':
			s->lowfreq=852;
			s->highfreq=1336;
			break;
		case '9':
			s->lowfreq=852;
			s->highfreq=1477;
			break;
		case '*':
			s->lowfreq=941;
			s->highfreq=1209;
			break;
		case '#':
			s->lowfreq=941;
			s->highfreq=1477;
			break;
		case 'A':
			s->lowfreq=697;
			s->highfreq=1633;
			break;
		case 'B':
			s->lowfreq=770;
			s->highfreq=1633;
			break;
		case 'C':
			s->lowfreq=852;
			s->highfreq=1633;
			break;
		case 'D':
			s->lowfreq=941;
			s->highfreq=1633;
142 143 144 145 146
			break;
		case ' ':
			/*ignore*/
			return 0;
			break;
aymeric's avatar
aymeric committed
147 148 149 150
		default:
			ms_warning("Not a dtmf key.");
			return -1;
	}
151
	ms_filter_lock(f);
152
	s->pos=0;
aymeric's avatar
aymeric committed
153 154
	s->lowfreq=s->lowfreq/s->rate;
	s->highfreq=s->highfreq/s->rate;
155 156
	s->dur=s->rate/10; /*100 ms duration */
	s->silence=0;
157
	s->amplitude=s->default_amplitude*32767*0.7;
aymeric's avatar
aymeric committed
158
	s->dtmf=dtmf[0];
159
	s->interval=0;
160 161 162 163
	ms_filter_unlock(f);
	return 0;
}

164 165 166
static int dtmfgen_play_tone(MSFilter *f, void *arg){
	DtmfGenState *s=(DtmfGenState*)f->data;
	MSDtmfGenCustomTone *def=(MSDtmfGenCustomTone*)arg;
167 168 169 170
	if (def->interval > 0)
		ms_message("Playing tones of frequency %i Hz, duration=%i, amplitude=%f with interval %i",def->frequency,def->duration,def->amplitude, def->interval);
	else
		ms_message("Playing tone of frequency %i Hz, duration=%i, amplitude=%f",def->frequency,def->duration,def->amplitude);
171 172
	ms_filter_lock(f);
	s->pos=0;
173
	s->dur=(s->rate*def->duration)/1000;
Simon Morlat's avatar
Simon Morlat committed
174
	s->lowfreq=((float)def->frequency)/(float)s->rate;
175 176
	s->highfreq=0;
	s->silence=0;
177
	s->amplitude=((float)def->amplitude)* 0.7*32767.0;
178
	s->dtmf='?';
179
	s->interval=def->interval;
180 181
	ms_filter_unlock(f);
	
182 183 184
	return 0;
}

185 186 187 188
static int dtmfgen_start(MSFilter *f, void *arg){
	if (dtmfgen_put(f,arg)==0){
		DtmfGenState *s=(DtmfGenState*)f->data;
		s->dur=5*s->rate;
189
		s->interval=0;
190 191 192 193 194 195 196
		return 0;
	}
	return -1;
}

static int dtmfgen_stop(MSFilter *f, void *arg){
	DtmfGenState *s=(DtmfGenState*)f->data;
197
	int min_duration=(100*s->rate)/1000; /*wait at least 100 ms*/
198
	ms_filter_lock(f);
199 200 201
	if (s->pos<min_duration)
		s->dur=min_duration;
	else s->dur=0;
202
	s->interval=0;
203
	ms_filter_unlock(f);
aymeric's avatar
aymeric committed
204 205 206 207 208 209
	return 0;
}

static int dtmfgen_set_rate(MSFilter *f, void *arg){
	DtmfGenState *s=(DtmfGenState*)f->data;
	s->rate=*((int*)arg);
210
	
aymeric's avatar
aymeric committed
211 212 213
	return 0;
}

214 215 216 217 218 219
static int dtmfgen_set_amp(MSFilter *f, void *arg){
	DtmfGenState *s=(DtmfGenState*)f->data;
	s->default_amplitude=*(float*)arg;
	return 0;
}

220 221 222 223

static void write_dtmf(DtmfGenState *s , int16_t *sample, int nsamples){
	int i;
	for (i=0;i<nsamples && s->pos<s->dur;i++,s->pos++){
224 225
		sample[i]=(int16_t)(((float)s->amplitude)*sin(2*M_PI*(float)s->pos*s->lowfreq));
		if (s->highfreq!=0) sample[i]+=(int16_t)(((float)s->amplitude)*sin(2*M_PI*(float)s->pos*s->highfreq));
226
	}
227 228 229 230
	for (;i<nsamples;++i){
		sample[i]=0;
	}
	if (s->pos>=s->dur){
231
		s->pos=0;
232 233 234 235 236 237
		if (s->interval > 0) {
			s->silence=s->interval;
		} else {
			s->dtmf=0;
			s->silence=TRAILLING_SILENCE;
		}
238 239 240
	}
}

aymeric's avatar
aymeric committed
241 242 243
static void dtmfgen_process(MSFilter *f){
	mblk_t *m;
	DtmfGenState *s=(DtmfGenState*)f->data;
244
	int nsamples;
aymeric's avatar
aymeric committed
245

246 247 248 249 250 251 252 253 254
	ms_filter_lock(f);
	if (ms_queue_empty(f->inputs[0])){
		s->nosamples_time+=f->ticker->interval;
		if ((s->dtmf!=0 || s->silence!=0) && s->nosamples_time>NO_SAMPLES_THRESHOLD){
			/*after 100 ms without stream we decide to generate our own sample
			 instead of writing into incoming stream samples*/
			nsamples=(f->ticker->interval*s->rate)/1000;
			m=allocb(nsamples*2,0);
			if (s->silence==0){
255 256 257 258 259 260 261
				if (s->pos==0){
					MSDtmfGenEvent ev;
					ev.tone_start_time=f->ticker->time;
					ev.tone_name[0]=s->dtmf;
					ev.tone_name[1]='\0';
					ms_filter_notify(f,MS_DTMF_GEN_EVENT,&ev);
				}
262 263 264 265 266
				write_dtmf(s,(int16_t*)m->b_wptr,nsamples);
			}else{
				memset(m->b_wptr,0,nsamples*2);
				s->silence-=f->ticker->interval;
				if (s->silence<0) s->silence=0;
aymeric's avatar
aymeric committed
267
			}
268 269 270 271 272
			m->b_wptr+=nsamples*2;
			ms_queue_put(f->outputs[0],m);
		}
	}else{
		s->nosamples_time=0;
273 274 275 276
		if (s->interval > 0) {
			s->silence-=f->ticker->interval;
			if (s->silence<0) s->silence=0;
		} else s->silence=0;
277
		while((m=ms_queue_get(f->inputs[0]))!=NULL){
278
			if (s->dtmf!=0 && s->silence==0){
279 280
				nsamples=(m->b_wptr-m->b_rptr)/2;
				write_dtmf(s, (int16_t*)m->b_rptr,nsamples);
aymeric's avatar
aymeric committed
281
			}
282
			ms_queue_put(f->outputs[0],m);
aymeric's avatar
aymeric committed
283 284
		}
	}
285
	ms_filter_unlock(f);
aymeric's avatar
aymeric committed
286 287 288 289
}

MSFilterMethod dtmfgen_methods[]={
	{	MS_FILTER_SET_SAMPLE_RATE	,	dtmfgen_set_rate	},
290 291 292
	{	MS_DTMF_GEN_PLAY			,	dtmfgen_put		},
	{  MS_DTMF_GEN_START		,   dtmfgen_start },
	{  MS_DTMF_GEN_STOP		, 	dtmfgen_stop },
293
	{	MS_DTMF_GEN_PLAY_CUSTOM, dtmfgen_play_tone },
294
	{	MS_DTMF_GEN_SET_DEFAULT_AMPLITUDE, dtmfgen_set_amp },
aymeric's avatar
aymeric committed
295 296 297 298 299 300 301 302
	{	0				,	NULL			}
};

#ifdef _MSC_VER

MSFilterDesc ms_dtmf_gen_desc={
	MS_DTMF_GEN_ID,
	"MSDtmfGen",
303
	N_("DTMF generator"),
aymeric's avatar
aymeric committed
304 305 306 307 308 309 310 311 312
	MS_FILTER_OTHER,
	NULL,
    1,
	1,
	dtmfgen_init,
	NULL,
    dtmfgen_process,
	NULL,
    dtmfgen_uninit,
313 314
	dtmfgen_methods,
	MS_FILTER_IS_PUMP
aymeric's avatar
aymeric committed
315 316 317 318 319 320 321
};

#else

MSFilterDesc ms_dtmf_gen_desc={
	.id=MS_DTMF_GEN_ID,
	.name="MSDtmfGen",
322
	.text=N_("DTMF generator"),
aymeric's avatar
aymeric committed
323 324 325 326 327 328
	.category=MS_FILTER_OTHER,
	.ninputs=1,
	.noutputs=1,
	.init=dtmfgen_init,
	.process=dtmfgen_process,
	.uninit=dtmfgen_uninit,
329 330
	.methods=dtmfgen_methods,
	.flags=MS_FILTER_IS_PUMP
aymeric's avatar
aymeric committed
331 332 333 334 335 336
};

#endif

MS_FILTER_DESC_EXPORT(ms_dtmf_gen_desc)