1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2011  Belledonne Communications SARL.
4 Author: Simon Morlat (simon.morlat@linphone.org)
5 
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 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 #ifdef HAVE_CONFIG_H
22 #include "mediastreamer-config.h"
23 #endif
24 
25 #include "mediastreamer2/mstonedetector.h"
26 #include "mediastreamer2/msticker.h"
27 
28 #include <math.h>
29 
30 #ifndef M_PI
31 #define M_PI       3.14159265358979323846
32 #endif
33 
34 #define MAX_SCANS 10
35 
36 static const float energy_min_threshold=0.01f;
37 
38 typedef struct _GoertzelState{
39 	uint64_t starttime;
40 	int dur;
41 	float coef;
42 	bool_t event_sent;
43 	bool_t pad[3];
44 }GoertzelState;
45 
goertzel_state_init(GoertzelState * gs,int frequency,int sampling_frequency)46 static void goertzel_state_init(GoertzelState *gs, int frequency, int sampling_frequency){
47 	gs->coef=(float)2*(float)cos(2*M_PI*((float)frequency/(float)sampling_frequency));
48 	gs->starttime=0;
49 	gs->dur=0;
50 }
51 
goertzel_state_run(GoertzelState * gs,int16_t * samples,int nsamples,float total_energy)52 static float goertzel_state_run(GoertzelState *gs,int16_t  *samples, int nsamples, float total_energy){
53 	int i;
54 	float tmp;
55 	float q1=0;
56 	float q2=0;
57 	float freq_en;
58 
59 	for(i=0;i<nsamples;++i){
60 		tmp=q1;
61 		q1=(gs->coef*q1) - q2 + (float)samples[i];
62 		q2=tmp;
63 	}
64 
65 	freq_en= (q1*q1) + (q2*q2) - (q1*q2*gs->coef);
66 	/*return a relative frequency energy compared over the total signal energy */
67 	return freq_en/(total_energy*(float)nsamples*0.5f);
68 }
69 
compute_energy(int16_t * samples,int nsamples)70 static float compute_energy(int16_t *samples, int nsamples){
71 	float en=0;
72 	int i;
73 	for(i=0;i<nsamples;++i){
74 		float s=(float)samples[i];
75 		en+=s*s;
76 	}
77 	return en;
78 }
79 
80 typedef struct _DetectorState{
81 	MSToneDetectorDef tone_def[MAX_SCANS];
82 	GoertzelState tone_gs[MAX_SCANS];
83 	int nscans;
84 	MSBufferizer *buf;
85 	int rate;
86 	int framesize;
87 	int frame_ms;
88 }DetectorState;
89 
detector_init(MSFilter * f)90 static void detector_init(MSFilter *f){
91 	DetectorState *s=ms_new0(DetectorState,1);
92 	s->buf=ms_bufferizer_new();
93 	s->rate=8000;
94 	s->frame_ms=20;
95 	s->framesize=2*(s->frame_ms*s->rate)/1000;
96 	f->data=s;
97 }
98 
detector_uninit(MSFilter * f)99 static void detector_uninit(MSFilter *f){
100 	DetectorState *s=(DetectorState *)f->data;
101 	ms_bufferizer_destroy (s->buf);
102 	ms_free(f->data);
103 }
104 
find_free_slot(DetectorState * s)105 static int find_free_slot(DetectorState *s){
106 	int i;
107 	for(i=0;i<MAX_SCANS;++i){
108 		if (s->tone_def[i].frequency==0) return i;
109 	}
110 	ms_error("No more free tone detector scans allowed, maximum reached.");
111 	return -1;
112 }
113 
detector_add_scan(MSFilter * f,void * arg)114 static int detector_add_scan(MSFilter *f, void *arg){
115 	DetectorState *s=(DetectorState *)f->data;
116 	MSToneDetectorDef *def=(MSToneDetectorDef*)arg;
117 	int i=find_free_slot(s);
118 	if (i!=-1){
119 		s->tone_def[i]=*def;
120 		s->nscans++;
121 		goertzel_state_init(&s->tone_gs[i],def->frequency,s->rate);
122 		return 0;
123 	}
124 	return -1;
125 }
126 
detector_clear_scans(MSFilter * f,void * arg)127 static int detector_clear_scans(MSFilter *f, void *arg){
128 	DetectorState *s=(DetectorState *)f->data;
129 	memset(&s->tone_def,0,sizeof(s->tone_def));
130 	s->nscans=0;
131 	return 0;
132 }
133 
detector_set_rate(MSFilter * f,void * arg)134 static int detector_set_rate(MSFilter *f, void *arg){
135 	DetectorState *s=(DetectorState *)f->data;
136 	s->rate = *((int*) arg);
137 	return 0;
138 }
139 
end_all_tones(DetectorState * s)140 static void end_all_tones(DetectorState *s){
141 	int i;
142 	for(i=0;i<s->nscans;++i){
143 		GoertzelState *gs=&s->tone_gs[i];
144 		gs->dur=0;
145 		gs->event_sent=FALSE;
146 	}
147 }
148 
detector_process(MSFilter * f)149 static void detector_process(MSFilter *f){
150 	DetectorState *s=(DetectorState *)f->data;
151 	mblk_t *m;
152 
153 	while ((m=ms_queue_get(f->inputs[0]))!=NULL){
154 		ms_queue_put(f->outputs[0],m);
155 		if (s->nscans>0){
156 			ms_bufferizer_put(s->buf,dupmsg(m));
157 		}
158 	}
159 	if (s->nscans>0){
160 		uint8_t *buf=alloca(s->framesize);
161 
162 		while(ms_bufferizer_read(s->buf,buf,s->framesize)!=0){
163 			float en=compute_energy((int16_t*)buf,s->framesize/2);
164 			if (en>energy_min_threshold*(32767.0*32767.0*0.7)){
165 				int i;
166 				for(i=0;i<s->nscans;++i){
167 					GoertzelState *gs=&s->tone_gs[i];
168 					MSToneDetectorDef *tone_def=&s->tone_def[i];
169 					float freq_en=goertzel_state_run(gs,(int16_t*)buf,s->framesize/2,en);
170 					if (freq_en>=tone_def->min_amplitude){
171 						if (gs->dur==0) gs->starttime=f->ticker->time;
172 						gs->dur+=s->frame_ms;
173 						if (gs->dur>=tone_def->min_duration && !gs->event_sent){
174 							MSToneDetectorEvent event;
175 
176 							strncpy(event.tone_name,tone_def->tone_name,sizeof(event.tone_name));
177 							event.tone_start_time=gs->starttime;
178 							ms_filter_notify(f,MS_TONE_DETECTOR_EVENT,&event);
179 							gs->event_sent=TRUE;
180 						}
181 					}else{
182 						gs->event_sent=FALSE;
183 						gs->dur=0;
184 						gs->starttime=0;
185 					}
186 				}
187 			}else end_all_tones(s);
188 		}
189 	}
190 }
191 
192 static MSFilterMethod detector_methods[]={
193 	{	MS_TONE_DETECTOR_ADD_SCAN, 		detector_add_scan	},
194 	{	MS_TONE_DETECTOR_CLEAR_SCANS,	detector_clear_scans	},
195 	{	MS_FILTER_SET_SAMPLE_RATE,	detector_set_rate	},
196 	{	0	,	NULL}
197 };
198 
199 #ifndef _MSC_VER
200 
201 MSFilterDesc ms_tone_detector_desc={
202 	.id=MS_TONE_DETECTOR_ID,
203 	.name="MSToneDetector",
204 	.text="Custom tone detection filter.",
205 	.category=MS_FILTER_OTHER,
206 	.ninputs=1,
207 	.noutputs=1,
208 	.init=detector_init,
209 	.process=detector_process,
210 	.uninit=detector_uninit,
211 	.methods=detector_methods
212 };
213 
214 #else
215 
216 MSFilterDesc ms_tone_detector_desc={
217 	MS_TONE_DETECTOR_ID,
218 	"MSToneDetector",
219 	"Custom tone detection filter.",
220 	MS_FILTER_OTHER,
221 	NULL,
222 	1,
223 	1,
224 	detector_init,
225 	NULL,
226 	detector_process,
227 	NULL,
228 	detector_uninit,
229 	detector_methods,
230 };
231 
232 #endif
233 
234 
235 MS_FILTER_DESC_EXPORT(ms_tone_detector_desc)
236 
237 
238 
239