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