source: mediastreamer2/src/speexec.c @ 1331:498cc1c1ce41

Last change on this file since 1331:498cc1c1ce41 was 1331:498cc1c1ce41, checked in by Simon Morlat <simon.morlat@…>, 2 years ago

speex echo improvements about delay

File size: 8.2 KB
Line 
1/*
2mediastreamer2 library - modular sound and video processing and streaming
3Copyright (C) 2010  Belledonne Communications SARL
4Author: Simon Morlat <simon.morlat@linphone.org>
5
6This program is free software; you can redistribute it and/or
7modify it under the terms of the GNU General Public License
8as published by the Free Software Foundation; either version 2
9of the License, or (at your option) any later version.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19*/
20
21#include "mediastreamer2/msfilter.h"
22#include "mediastreamer2/msticker.h"
23#include <speex/speex_echo.h>
24#include <speex/speex_preprocess.h>
25
26#ifdef HAVE_CONFIG_H
27#include "mediastreamer-config.h"
28#endif
29
30#ifdef WIN32
31#include <malloc.h> /* for alloca */
32#endif
33
34//#define EC_DUMP 1
35
36#define EC_DUMP_PREFIX "/sdcard"
37
38static const float smooth_factor=0.05;
39static const int framesize=128;
40
41
42typedef struct SpeexECState{
43        SpeexEchoState *ecstate;
44        SpeexPreprocessState *den;
45        MSBufferizer delayed_ref;
46        MSBufferizer echo;
47        int framesize;
48        int filterlength;
49        int samplerate;
50        int delay_ms;
51        int tail_length_ms;
52        int nominal_ref_samples;
53        int min_ref_samples;
54#ifdef EC_DUMP
55        FILE *echofile;
56        FILE *reffile;
57#endif
58        bool_t echostarted;
59        bool_t bypass_mode;
60        bool_t using_zeroes;
61}SpeexECState;
62
63static void speex_ec_init(MSFilter *f){
64        SpeexECState *s=(SpeexECState *)ms_new(SpeexECState,1);
65
66        s->samplerate=8000;
67        ms_bufferizer_init(&s->delayed_ref);
68        ms_bufferizer_init(&s->echo);
69        s->delay_ms=0;
70        s->tail_length_ms=250;
71        s->ecstate=NULL;
72        s->framesize=framesize;
73        s->den = NULL;
74        s->using_zeroes=FALSE;
75        s->echostarted=FALSE;
76        s->bypass_mode=FALSE;
77
78#ifdef EC_DUMP
79        {
80                char *fname=ms_strdup_printf("%s/msspeexec-%p-echo.raw", EC_DUMP_PREFIX,f);
81                s->echofile=fopen(fname,"w");
82                ms_free(fname);
83                fname=ms_strdup_printf("%s/msspeexec-%p-ref.raw", EC_DUMP_PREFIX,f);
84                s->reffile=fopen(fname,"w");
85                ms_free(fname);
86        }
87#endif
88       
89        f->data=s;
90}
91
92static void speex_ec_uninit(MSFilter *f){
93        SpeexECState *s=(SpeexECState*)f->data;
94        ms_bufferizer_uninit(&s->delayed_ref);
95#ifdef EC_DUMP
96        if (s->echofile)
97                fclose(s->echofile);
98        if (s->reffile)
99                fclose(s->reffile);
100#endif
101        ms_free(s);
102}
103
104
105static void speex_ec_preprocess(MSFilter *f){
106        SpeexECState *s=(SpeexECState*)f->data;
107        int delay_samples=0;
108        mblk_t *m;
109
110        s->echostarted=FALSE;
111        s->filterlength=(s->tail_length_ms*s->samplerate)/1000;
112        delay_samples=s->delay_ms*s->samplerate/1000;
113        ms_message("Initializing speex echo canceler with framesize=%i, filterlength=%i, delay_samples=%i",
114                s->framesize,s->filterlength,delay_samples);
115       
116        s->ecstate=speex_echo_state_init(s->framesize,s->filterlength);
117        s->den = speex_preprocess_state_init(s->framesize, s->samplerate);
118        speex_echo_ctl(s->ecstate, SPEEX_ECHO_SET_SAMPLING_RATE, &s->samplerate);
119        speex_preprocess_ctl(s->den, SPEEX_PREPROCESS_SET_ECHO_STATE, s->ecstate);
120        /* fill with zeroes for the time of the delay*/
121        m=allocb(delay_samples*2,0);
122        m->b_wptr+=delay_samples*2;
123        ms_bufferizer_put (&s->delayed_ref,m);
124        s->min_ref_samples=-1;
125}
126
127/*      inputs[0]= reference signal from far end (sent to soundcard)
128 *      inputs[1]= near speech & echo signal    (read from soundcard)
129 *      outputs[0]=  is a copy of inputs[0] to be sent to soundcard
130 *      outputs[1]=  near end speech, echo removed - towards far end
131*/
132static void speex_ec_process(MSFilter *f){
133        SpeexECState *s=(SpeexECState*)f->data;
134        int nbytes=s->framesize*2;
135        mblk_t *refm;
136        uint8_t *ref,*echo;
137       
138        if (s->bypass_mode) {
139                while((refm=ms_queue_get(f->inputs[0]))!=NULL){
140                        ms_queue_put(f->outputs[0],refm);
141                }
142                while((refm=ms_queue_get(f->inputs[1]))!=NULL){
143                        ms_queue_put(f->outputs[1],refm);
144                }
145                return;
146        }
147       
148        if (f->inputs[0]!=NULL){
149                while((refm=ms_queue_get(f->inputs[0]))!=NULL){
150                        mblk_t *cp=dupmsg(refm);
151                        ms_bufferizer_put(&s->delayed_ref,cp);
152                        ms_queue_put(f->outputs[0],refm);
153                }
154        }
155
156        ms_bufferizer_put_from_queue(&s->echo,f->inputs[1]);
157       
158        ref=(uint8_t*)alloca(nbytes);
159        echo=(uint8_t*)alloca(nbytes);
160        while (ms_bufferizer_read(&s->echo,echo,nbytes)>=nbytes){
161                mblk_t *oecho=allocb(nbytes,0);
162                int avail;
163       
164                if ((avail=ms_bufferizer_get_avail(&s->delayed_ref))<(s->nominal_ref_samples+nbytes)){
165                        /*we don't have enough to read in a reference signal buffer, inject silence instead*/
166                        refm=allocb(nbytes,0);
167                        memset(refm->b_wptr,0,nbytes);
168                        refm->b_wptr+=nbytes;
169                        ms_bufferizer_put(&s->delayed_ref,refm);
170                        if (!s->using_zeroes){
171                                ms_warning("Not enough ref samples, using zeroes");
172                                s->using_zeroes=TRUE;
173                        }
174                }else if (s->using_zeroes){
175                        ms_message("Samples are back.");
176                        s->using_zeroes=FALSE;
177                }
178
179                /*now read a valid buffer of ref samples*/
180                if (ms_bufferizer_read(&s->delayed_ref,ref,nbytes)==0){
181                        ms_fatal("Should never happen");
182                }
183                avail-=nbytes;
184                if (avail<s->min_ref_samples || s->min_ref_samples==-1){
185                        s->min_ref_samples=avail;
186                }
187               
188#ifdef EC_DUMP
189                if (s->reffile)
190                        fwrite(ref,nbytes,1,s->reffile);
191                if (s->echofile)
192                        fwrite(echo,nbytes,1,s->echofile);
193#endif
194                speex_echo_cancellation(s->ecstate,(short*)echo,(short*)ref,(short*)oecho->b_wptr);
195                speex_preprocess_run(s->den, (short*)oecho->b_wptr);
196                oecho->b_wptr+=nbytes;
197                ms_queue_put(f->outputs[1],oecho);
198        }
199
200        /*verify our ref buffer does not become too big, meaning that we are receiving more samples than we are sending*/
201        if (f->ticker->time % 5000 == 0 && s->min_ref_samples!=-1){
202                int diff=s->min_ref_samples-s->nominal_ref_samples;
203                if (diff>nbytes){
204                        ms_warning("echo canceller: we are accumulating too much reference signal, purging now %i bytes",nbytes);
205                        ms_bufferizer_skip_bytes(&s->delayed_ref,nbytes);
206                }
207                s->min_ref_samples=-1;
208        }
209}
210
211static void speex_ec_postprocess(MSFilter *f){
212        SpeexECState *s=(SpeexECState*)f->data;
213        ms_bufferizer_flush (&s->delayed_ref);
214        ms_bufferizer_flush (&s->echo);
215        if (s->ecstate!=NULL){
216                speex_echo_state_destroy(s->ecstate);
217                s->ecstate=NULL;
218        }
219        if (s->den!=NULL){
220                speex_preprocess_state_destroy(s->den);
221                s->den=NULL;
222        }
223}
224
225static int speex_ec_set_sr(MSFilter *f, void *arg){
226        SpeexECState *s=(SpeexECState*)f->data;
227        s->samplerate = *(int*)arg;
228        return 0;
229}
230
231static int speex_ec_set_framesize(MSFilter *f, void *arg){
232        SpeexECState *s=(SpeexECState*)f->data;
233        s->framesize = *(int*)arg;
234        return 0;
235}
236
237static int speex_ec_set_delay(MSFilter *f, void *arg){
238        SpeexECState *s=(SpeexECState*)f->data;
239        s->delay_ms = *(int*)arg;
240        return 0;
241}
242
243static int speex_ec_set_tail_length(MSFilter *f, void *arg){
244        SpeexECState *s=(SpeexECState*)f->data;
245        s->tail_length_ms=*(int*)arg;
246        return 0;
247}
248static int speex_ec_set_bypass_mode(MSFilter *f, void *arg) {
249        SpeexECState *s=(SpeexECState*)f->data;
250        s->bypass_mode=*(bool_t*)arg;
251        ms_message("set EC bypass mode to [%i]",s->bypass_mode);
252        return 0;
253}
254static int speex_ec_get_bypass_mode(MSFilter *f, void *arg) {
255        SpeexECState *s=(SpeexECState*)f->data;
256        *(bool_t*)arg=s->bypass_mode;
257        return 0;
258}
259
260static MSFilterMethod speex_ec_methods[]={
261        {       MS_FILTER_SET_SAMPLE_RATE, speex_ec_set_sr },
262        {       MS_ECHO_CANCELLER_SET_TAIL_LENGTH       ,       speex_ec_set_tail_length        },
263        {       MS_ECHO_CANCELLER_SET_DELAY             ,       speex_ec_set_delay              },
264        {       MS_ECHO_CANCELLER_SET_FRAMESIZE ,       speex_ec_set_framesize          },
265        {       MS_ECHO_CANCELLER_SET_BYPASS_MODE       ,       speex_ec_set_bypass_mode                },
266        {       MS_ECHO_CANCELLER_GET_BYPASS_MODE       ,       speex_ec_get_bypass_mode                },
267};
268
269#ifdef _MSC_VER
270
271MSFilterDesc ms_speex_ec_desc={
272        MS_SPEEX_EC_ID,
273        "MSSpeexEC",
274        N_("Echo canceller using speex library"),
275        MS_FILTER_OTHER,
276        NULL,
277        2,
278        2,
279        speex_ec_init,
280        speex_ec_preprocess,
281        speex_ec_process,
282        speex_ec_postprocess,
283        speex_ec_uninit,
284        speex_ec_methods
285};
286
287#else
288
289MSFilterDesc ms_speex_ec_desc={
290        .id=MS_SPEEX_EC_ID,
291        .name="MSSpeexEC",
292        .text=N_("Echo canceller using speex library"),
293        .category=MS_FILTER_OTHER,
294        .ninputs=2,
295        .noutputs=2,
296        .init=speex_ec_init,
297        .preprocess=speex_ec_preprocess,
298        .process=speex_ec_process,
299        .postprocess=speex_ec_postprocess,
300        .uninit=speex_ec_uninit,
301        .methods=speex_ec_methods
302};
303
304#endif
305
306MS_FILTER_DESC_EXPORT(ms_speex_ec_desc)
307
Note: See TracBrowser for help on using the repository browser.