Speex Echo Cancellation with NAudio?

Jun 19, 2011 at 1:22 PM
Edited Jun 20, 2011 at 1:23 PM

Has anyone tried using (www.speex.org) Speex Accoustic Echo Cancellation filter with NAudio?

SpeexEchoState *speex_echo_state_init(int frame_size, int filter_length);
void speex_echo_cancellation(SpeexEchoState *st, const spx_int16_t *rec, const spx_int16_t *play, spx_int16_t *out);

Its a C API so we should be able to DllImport these in C# but due to my lack of Interop and C++ experience; I'm having a hard time interoping with this. Does anyone have a working example of applying this to byte[] array received from waveIn?

Jun 19, 2011 at 6:49 PM
Edited Jul 14, 2011 at 3:28 PM

Figured it out

 

    class EchoFilter : IDisposable
    {
        [DllImport("libspeex.dll")]
        static extern IntPtr speex_echo_state_init(int frame_size, int filter_length);

        [DllImport("libspeex.dll")]
        static extern void speex_echo_cancellation(IntPtr state, byte[] recorded, byte[] played, byte[] output);

        [DllImport("libspeex.dll")]
        static extern void speex_echo_state_destroy(IntPtr state);

        IntPtr state;

        public EchoFilter(int frameSize, int filterLength)
        {
            state = speex_echo_state_init(frameSize, filterLength);
        }

        public void Filter(byte[] remoteSoundAndEcho, byte[] localVoice, byte[] output)
        {
            speex_echo_cancellation(state, remoteSoundAndEcho, localVoice, output);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        void Dispose(bool disposing)
        {
            if (state != IntPtr.Zero)
            {
                speex_echo_state_destroy(state);
                state = IntPtr.Zero;
            }
        }

        ~EchoFilter()
        {
            Dispose(false);
        }
    }
Jun 19, 2011 at 8:04 PM
Edited Jul 14, 2011 at 3:29 PM

And here is a the Echo filtering wave provider

 

    class EchoFilterWaveProvider : IWaveProvider, IDisposable
    {
        BufferedWaveProvider localSound;
        BufferedWaveProvider remoteSoundAndEcho;
        BufferedWaveProvider filtered;
        int frameBytes;
        EchoFilter filter;
        byte[] remoteFrame;
        byte[] localFrame;
        byte[] outputFrame;
        object syncRoot = new object();

        public EchoFilterWaveProvider(WaveFormat format, int frameSize, int filterLength)
        {
            frameBytes = frameSize * 2;
            remoteFrame = new byte[frameBytes];
            localFrame = new byte[frameBytes];
            outputFrame = new byte[frameBytes];

            filter = new EchoFilter(frameSize, filterLength);
            localSound = new BufferedWaveProvider(format);
            remoteSoundAndEcho = new BufferedWaveProvider(format);
            filtered = new BufferedWaveProvider(format);
            localSound.DiscardOnBufferOverflow = remoteSoundAndEcho.DiscardOnBufferOverflow
                                             = filtered.DiscardOnBufferOverflow
                                             = true;
        }

        public void AddLocalSamples(byte[] buffer, int offset, int count)
        {
            lock (syncRoot)
                localSound.AddSamples(buffer, offset, count);
        }

        public void AddRemoteSamples(byte[] buffer, int offset, int count)
        {
            lock (syncRoot)
                remoteSoundAndEcho.AddSamples(buffer, offset, count);
        }       

        public WaveFormat WaveFormat
        {
            get { return remoteSoundAndEcho.WaveFormat; }
        }

        public int Read(byte[] buffer, int offset, int count)
        {
            lock (syncRoot)
            {
                while (remoteSoundAndEcho.BufferedBytes >= frameBytes && localSound.BufferedBytes >= frameBytes)
                {
                    remoteSoundAndEcho.Read(remoteFrame, 0, frameBytes);
                    localSound.Read(localFrame, 0, frameBytes);
                    filter.Filter(remoteFrame, localFrame, outputFrame);
                    filtered.AddSamples(outputFrame, 0, outputFrame.Length);
                }
                return filtered.Read(buffer, offset, count);
            }
        }

        public void Dispose()
        {
            filter.Dispose();
        }
    }
Coordinator
Jun 22, 2011 at 8:13 AM

cool, thanks for sharing the code.

Jul 14, 2011 at 3:49 PM

Fixed bug in the EchoFilter where remote sound was being passed to filter in place of local sound causing only local echo to be played back. The code in the posts above has been updated.

Nov 29, 2012 at 11:26 AM

hi

Could you really get it to work? I did lots of try but I just hear all of the voice with echo, like no any echo canceller is there.

Just a difference is that I changed libspeex.dll to libspeexdsp.dll. because it couldn't find functions names in libspeex.dll.

It will be nice of you if you can help me.

Thanks

Dec 18, 2012 at 1:33 PM

I could fix the problem. It was all about frame length.

Now it works great. Just there's a problem. I can say that it works great with win7(I have totally no any echo). But with win XP, it doesn't work stable. It just reduces the echo a little and it shows strange behavior(like it works for some seconds and some seconds not). It's like a sample rate difference between input and output sound or something like this.

Is there any difference between win xp and win 7 in their audio device handling?

Any idea?

Dec 18, 2012 at 2:24 PM

omatrix what frame length are you using? I'm not sure what difference is there between windows xp and windows 7.

Dec 18, 2012 at 3:32 PM

Thanks for reply

I used 256 for frame size and 4096 for filter length. Also the sample rate is 8000.

Thanks again

Jan 14, 2014 at 9:32 AM
hi

omatix I used EchoFilterWaveProvider instead of waveProvider in networkChat code in NAudioDemo,

waveProvider have waveProvider.AddSample method to add voice byte array to waveOut,

but EchoFilterWaveProvider have two methods : AddRemoteSample and AddLocalSample,

I used both of them in my code but I still have echo!

could you explain about your modifications on code?