source: mediastreamer2/src/speexec.c @ 1003:78319a2c8ec0

Last change on this file since 1003:78319a2c8ec0 was 1003:78319a2c8ec0, checked in by Simon Morlat <simon.morlat@…>, 3 years ago

echo canceller works well.

File size: 6.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 <speex/speex_echo.h>
23#include <speex/speex_preprocess.h>
24
25#ifdef HAVE_CONFIG_H
26#include "mediastreamer-config.h"
27#endif
28
29#ifdef WIN32
30#include <malloc.h> /* for alloca */
31#endif
32
33static const int framesize=128;
34static const int ref_max_delay=80;
35
36typedef struct SpeexECState{
37        SpeexEchoState *ecstate;
38        SpeexPreprocessState *den;
39        MSBufferizer ref;
40        MSBufferizer delayed_ref;
41        MSBufferizer echo;
42        int ref_bytes_limit;
43        int framesize;
44        int filterlength;
45        int samplerate;
46        int delay_ms;
47        int tail_length_ms;
48}SpeexECState;
49
50static void speex_ec_init(MSFilter *f){
51        SpeexECState *s=(SpeexECState *)ms_new(SpeexECState,1);
52
53        s->samplerate=8000;
54        ms_bufferizer_init(&s->ref);
55        ms_bufferizer_init(&s->delayed_ref);
56        ms_bufferizer_init(&s->echo);
57        s->delay_ms=0;
58        s->tail_length_ms=250;
59        s->ecstate=NULL;
60        s->framesize=framesize;
61        s->den = NULL;
62
63        f->data=s;
64}
65
66static void speex_ec_uninit(MSFilter *f){
67        SpeexECState *s=(SpeexECState*)f->data;
68        ms_bufferizer_uninit(&s->ref);
69        ms_bufferizer_uninit(&s->delayed_ref);
70        ms_bufferizer_uninit(&s->delayed_ref);
71
72        ms_free(s);
73}
74
75
76static void speex_ec_preprocess(MSFilter *f){
77        SpeexECState *s=(SpeexECState*)f->data;
78        int delay_samples=0;
79        mblk_t *m;
80
81        s->filterlength=(s->tail_length_ms*s->samplerate)/1000;
82        delay_samples=s->delay_ms*s->samplerate/1000;
83        ms_message("Initializing speex echo canceler with framesize=%i, filterlength=%i, delay_samples=%i",
84                s->framesize,s->filterlength,delay_samples);
85        s->ref_bytes_limit=(2*ref_max_delay*s->samplerate)/1000;
86        s->ecstate=speex_echo_state_init(s->framesize,s->filterlength);
87        s->den = speex_preprocess_state_init(s->framesize, s->samplerate);
88        speex_echo_ctl(s->ecstate, SPEEX_ECHO_SET_SAMPLING_RATE, &s->samplerate);
89        speex_preprocess_ctl(s->den, SPEEX_PREPROCESS_SET_ECHO_STATE, s->ecstate);
90        /* fill with zeroes for the time of the delay*/
91        m=allocb(delay_samples*2,0);
92        ms_bufferizer_put (&s->delayed_ref,m);
93}
94
95/*      inputs[0]= reference signal (sent to soundcard)
96 *      inputs[1]= near speech & echo signal    (read from soundcard)
97 *      outputs[1]=  near end speech, echo removed - towards far end
98*/
99static void speex_ec_process(MSFilter *f){
100        SpeexECState *s=(SpeexECState*)f->data;
101        int nbytes=s->framesize*2;
102        mblk_t *refm;
103        int ref_samples=0;
104        uint8_t *ref,*echo;
105       
106        if (f->inputs[0]!=NULL){
107                while((refm=ms_queue_get(f->inputs[0]))!=NULL){
108                        mblk_t *cp=copymsg(refm);
109                        ref_samples+=msgdsize(refm)/2;
110                        ms_bufferizer_put(&s->ref,refm);
111                        ms_bufferizer_put(&s->delayed_ref,cp);
112                }
113        }
114        if (f->inputs[1]!=NULL){
115                ms_bufferizer_put_from_queue (&s->echo,f->inputs[1]);
116        }
117       
118        ref=(uint8_t*)alloca(nbytes);
119        echo=(uint8_t*)alloca(nbytes);
120        while (ms_bufferizer_read(&s->echo,echo,nbytes)>=nbytes){
121                mblk_t *oref=allocb(nbytes,0);
122                mblk_t *oecho=allocb(nbytes,0);
123                if (ms_bufferizer_read(&s->ref,oref->b_wptr,nbytes)==0){
124                        memset(ref,0,nbytes);
125                        memset(oref->b_wptr,0,nbytes);
126                        /*missing data, use silence instead*/
127                        ms_warning("No ref samples, using silence instead");
128                }else{
129                        ms_bufferizer_read(&s->delayed_ref,ref,nbytes);
130                }
131                oref->b_wptr+=nbytes;
132                ms_queue_put(f->outputs[0],oref);
133                speex_echo_cancellation(s->ecstate,(short*)echo,(short*)ref,(short*)oecho->b_wptr);
134                speex_preprocess_run(s->den, (short*)oecho->b_wptr);
135                oecho->b_wptr+=nbytes;
136                ms_queue_put(f->outputs[1],oecho);
137        }
138        /* do not accumulate too much reference signal */
139        if (ms_bufferizer_get_avail(&s->ref)> s->ref_bytes_limit) {
140                /* reset evrything */
141                ms_warning("purging ref signal");
142                ms_bufferizer_flush(&s->ref);
143                ms_bufferizer_flush(&s->delayed_ref);
144        }
145}
146
147static void speex_ec_postprocess(MSFilter *f){
148        SpeexECState *s=(SpeexECState*)f->data;
149        ms_bufferizer_flush (&s->ref);
150        ms_bufferizer_flush (&s->delayed_ref);
151        ms_bufferizer_flush (&s->echo);
152        if (s->ecstate!=NULL){
153                speex_echo_state_destroy(s->ecstate);
154                s->ecstate=NULL;
155        }
156        if (s->den!=NULL){
157                speex_preprocess_state_destroy(s->den);
158                s->den=NULL;
159        }
160}
161
162static int speex_ec_set_sr(MSFilter *f, void *arg){
163        SpeexECState *s=(SpeexECState*)f->data;
164        s->samplerate = *(int*)arg;
165        return 0;
166}
167
168static int speex_ec_set_framesize(MSFilter *f, void *arg){
169        SpeexECState *s=(SpeexECState*)f->data;
170        s->framesize = *(int*)arg;
171        return 0;
172}
173
174static int speex_ec_set_delay(MSFilter *f, void *arg){
175        SpeexECState *s=(SpeexECState*)f->data;
176        s->delay_ms = *(int*)arg;
177        return 0;
178}
179
180static int speex_ec_set_tail_length(MSFilter *f, void *arg){
181        SpeexECState *s=(SpeexECState*)f->data;
182        s->tail_length_ms=*(int*)arg;
183        return 0;
184}
185
186
187static MSFilterMethod speex_ec_methods[]={
188        {       MS_FILTER_SET_SAMPLE_RATE, speex_ec_set_sr },
189        {       MS_ECHO_CANCELLER_SET_TAIL_LENGTH       ,       speex_ec_set_tail_length        },
190        {       MS_ECHO_CANCELLER_SET_DELAY             ,       speex_ec_set_delay              },
191        {       MS_ECHO_CANCELLER_SET_FRAMESIZE ,       speex_ec_set_framesize          },
192};
193
194#ifdef _MSC_VER
195
196MSFilterDesc ms_speex_ec_desc={
197        MS_SPEEX_EC_ID,
198        "MSSpeexEC",
199        N_("Echo canceller using speex library"),
200        MS_FILTER_OTHER,
201        NULL,
202        2,
203        2,
204        speex_ec_init,
205        speex_ec_preprocess,
206        speex_ec_process,
207        speex_ec_postprocess,
208        speex_ec_uninit,
209        speex_ec_methods
210};
211
212#else
213
214MSFilterDesc ms_speex_ec_desc={
215        .id=MS_SPEEX_EC_ID,
216        .name="MSSpeexEC",
217        .text=N_("Echo canceller using speex library"),
218        .category=MS_FILTER_OTHER,
219        .ninputs=2,
220        .noutputs=2,
221        .init=speex_ec_init,
222        .preprocess=speex_ec_preprocess,
223        .process=speex_ec_process,
224        .postprocess=speex_ec_postprocess,
225        .uninit=speex_ec_uninit,
226        .methods=speex_ec_methods
227};
228
229#endif
230
231MS_FILTER_DESC_EXPORT(ms_speex_ec_desc)
Note: See TracBrowser for help on using the repository browser.