diff --git a/CMakeLists.txt b/CMakeLists.txt
index b492285d68d1671b21dc99879390d9f4fe3e4682..2ddeeec2ba2b71d297d0d120a23cb914db9d3ee4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -135,12 +135,12 @@ if(MSVC)
            "${CMAKE_CURRENT_SOURCE_DIR}/src/jamidht;"
            "${CMAKE_CURRENT_SOURCE_DIR}/src/security;"
            "${CMAKE_CURRENT_SOURCE_DIR}/src/sip;"
-           "${CMAKE_CURRENT_SOURCE_DIR}/compat/msvc;"
            "${CMAKE_CURRENT_SOURCE_DIR}/src/upnp;"
            "${CMAKE_CURRENT_SOURCE_DIR}/src/upnp/igd;"
            "${CMAKE_CURRENT_SOURCE_DIR}/src/upnp/protocol;"
            "${CMAKE_CURRENT_SOURCE_DIR}/src/upnp/mapping;"
            "${CMAKE_CURRENT_SOURCE_DIR}/src/jamidht/eth;"
+           "${CMAKE_CURRENT_SOURCE_DIR}/compat/msvc;"
            "${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc;"
            "${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/include;"
            "${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/msgpack-c/include;"
@@ -152,6 +152,7 @@ if(MSVC)
            "${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjsip/include;"
            "${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/third_party;"
            "${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjmedia/include"
+           "${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/speexdsp/include;"
        )
    endif()
 
@@ -224,39 +225,40 @@ if(MSVC)
    # Dependencies
    ################################################################################
 
-   set(libAdditionalDependencies "${CMAKE_STATIC_LINKER_FLAGS} /LTCG ws2_32.lib 
-   advapi32.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/avcodec.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/avdevice.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/avfilter.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/avformat.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/avutil.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/swresample.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/swscale.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/lib/x64/libgnutls.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/lib/x64/lib_json.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/lib/x64/libopendht.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/argon2/vs2015/Argon2Ref/vs2015/build/Argon2Ref.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/lib/x64/secp256k1.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/yaml-cpp/msvc/Release/libyaml-cppmd.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/lib/x64/portaudio.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/lib/x64/libupnp.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/natpmp/msvc/Release/natpmp.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjsip/lib/pjsip-core-x86_64-x64-vc15-Release.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjsip/lib/pjsip-simple-x86_64-x64-vc15-Release.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjsip/lib/pjsua2-lib-x86_64-x64-vc15-Release.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjsip/lib/pjsua-lib-x86_64-x64-vc15-Release.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjsip/lib/pjsip-ua-x86_64-x64-vc15-Release.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjmedia/lib/pjmedia-x86_64-x64-vc15-Release.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjlib-util/lib/pjlib-util-x86_64-x64-vc15-Release.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjlib/lib/pjlib-x86_64-x64-vc15-Release.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjnath/lib/pjnath-x86_64-x64-vc15-Release.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/fmt/msvc/Release/fmt.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/http_parser/x64/Release/http-parser.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/asio/asio/msvc/x64/Release/asio.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/openssl/out32dll/libeay32.lib 
-   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/openssl/out32dll/ssleay32.lib 
-   /ignore:4006 
+   set(libAdditionalDependencies "${CMAKE_STATIC_LINKER_FLAGS} /LTCG ws2_32.lib
+   advapi32.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/avcodec.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/avdevice.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/avfilter.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/avformat.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/avutil.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/swresample.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/ffmpeg/Build/win32/x64/bin/swscale.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/lib/x64/libgnutls.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/lib/x64/lib_json.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/lib/x64/libopendht.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/argon2/vs2015/Argon2Ref/vs2015/build/Argon2Ref.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/lib/x64/secp256k1.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/yaml-cpp/msvc/Release/libyaml-cppmd.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/lib/x64/portaudio.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/msvc/lib/x64/libupnp.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/natpmp/msvc/Release/natpmp.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjsip/lib/pjsip-core-x86_64-x64-vc15-Release.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjsip/lib/pjsip-simple-x86_64-x64-vc15-Release.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjsip/lib/pjsua2-lib-x86_64-x64-vc15-Release.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjsip/lib/pjsua-lib-x86_64-x64-vc15-Release.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjsip/lib/pjsip-ua-x86_64-x64-vc15-Release.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjmedia/lib/pjmedia-x86_64-x64-vc15-Release.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjlib-util/lib/pjlib-util-x86_64-x64-vc15-Release.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjlib/lib/pjlib-x86_64-x64-vc15-Release.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/pjproject/pjnath/lib/pjnath-x86_64-x64-vc15-Release.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/fmt/msvc/Release/fmt.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/http_parser/x64/Release/http-parser.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/asio/asio/msvc/x64/Release/asio.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/openssl/out32dll/libeay32.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/openssl/out32dll/ssleay32.lib
+   ${CMAKE_CURRENT_SOURCE_DIR}/contrib/build/speexdsp/lib/libspeexdsp.lib
+   /ignore:4006
    "
    )
 
diff --git a/compat/msvc/package.json b/compat/msvc/package.json
index 591abff160ffb376a0f701bd9346bb43e89dc4fa..0aea6554ee0ac515c93945d4f61138a2a01edf5c 100644
--- a/compat/msvc/package.json
+++ b/compat/msvc/package.json
@@ -8,9 +8,10 @@
         "pjproject",
         "portaudio",
         "secp256k1",
+        "speexdsp",
         "upnp",
         "yaml-cpp"
     ],
     "configuration": "ReleaseLib_win32",
     "project_paths": ["ring-daemon.vcxproj"]
-}
\ No newline at end of file
+}
diff --git a/contrib/src/speexdsp/package.json b/contrib/src/speexdsp/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..95d427120ba40baebf00b5bb4621418d46a567c6
--- /dev/null
+++ b/contrib/src/speexdsp/package.json
@@ -0,0 +1,15 @@
+{
+    "name": "speexdsp",
+    "version": "SpeexDSP-1.2.0",
+    "url": "https://github.com/xiph/speexdsp/archive/__VERSION__.tar.gz",
+    "deps": [],
+    "patches": ["speexdsp_vs_proj.patch"],
+    "win_patches": [],
+    "project_paths": ["win32/msvc/libspeexdsp/libspeexdsp.vcxproj"],
+    "with_env" : "",
+    "custom_scripts": {
+        "pre_build": [],
+        "build": [],
+        "post_build": []
+    }
+}
\ No newline at end of file
diff --git a/contrib/src/speexdsp/speexdsp_vs_proj.patch b/contrib/src/speexdsp/speexdsp_vs_proj.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8165ab5f66ab93f305a557a3206bb4c96ec11e59
--- /dev/null
+++ b/contrib/src/speexdsp/speexdsp_vs_proj.patch
@@ -0,0 +1,398 @@
+From ba88ffed7a018d37661c9574f98f4246cdf6031c Mon Sep 17 00:00:00 2001
+From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
+Date: Wed, 19 Feb 2020 16:14:02 -0500
+Subject: [PATCH] b
+
+---
+ win32/msvc/libspeexdsp/libspeexdsp.vcxproj | 378 +++++++++++++++++++++++++++++
+ 1 file changed, 378 insertions(+)
+ create mode 100644 win32/msvc/libspeexdsp/libspeexdsp.vcxproj
+
+diff --git a/win32/msvc/libspeexdsp/libspeexdsp.vcxproj b/win32/msvc/libspeexdsp/libspeexdsp.vcxproj
+new file mode 100644
+index 0000000..071c9b3
+--- /dev/null
++++ b/win32/msvc/libspeexdsp/libspeexdsp.vcxproj
+@@ -0,0 +1,378 @@
++<?xml version="1.0" encoding="utf-8"?>
++<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
++  <ItemGroup Label="ProjectConfigurations">
++    <ProjectConfiguration Include="Debug|Win32">
++      <Configuration>Debug</Configuration>
++      <Platform>Win32</Platform>
++    </ProjectConfiguration>
++    <ProjectConfiguration Include="Debug|x64">
++      <Configuration>Debug</Configuration>
++      <Platform>x64</Platform>
++    </ProjectConfiguration>
++    <ProjectConfiguration Include="Release_Dynamic_SSE|Win32">
++      <Configuration>Release_Dynamic_SSE</Configuration>
++      <Platform>Win32</Platform>
++    </ProjectConfiguration>
++    <ProjectConfiguration Include="Release_Dynamic_SSE|x64">
++      <Configuration>Release_Dynamic_SSE</Configuration>
++      <Platform>x64</Platform>
++    </ProjectConfiguration>
++    <ProjectConfiguration Include="Release_Static_SSE|Win32">
++      <Configuration>Release_Static_SSE</Configuration>
++      <Platform>Win32</Platform>
++    </ProjectConfiguration>
++    <ProjectConfiguration Include="Release_Static_SSE|x64">
++      <Configuration>Release_Static_SSE</Configuration>
++      <Platform>x64</Platform>
++    </ProjectConfiguration>
++    <ProjectConfiguration Include="Release|Win32">
++      <Configuration>Release</Configuration>
++      <Platform>Win32</Platform>
++    </ProjectConfiguration>
++    <ProjectConfiguration Include="Release|x64">
++      <Configuration>Release</Configuration>
++      <Platform>x64</Platform>
++    </ProjectConfiguration>
++  </ItemGroup>
++  <PropertyGroup Label="Globals">
++    <ProjectGuid>{03207781-0D1C-4DB3-A71D-45C608F28DBD}</ProjectGuid>
++    <Keyword>Win32Proj</Keyword>
++  </PropertyGroup>
++  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_Static_SSE|Win32'" Label="Configuration">
++    <ConfigurationType>StaticLibrary</ConfigurationType>
++    <PlatformToolset>v141</PlatformToolset>
++    <CharacterSet>MultiByte</CharacterSet>
++    <WholeProgramOptimization>true</WholeProgramOptimization>
++  </PropertyGroup>
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_Static_SSE|x64'" Label="Configuration">
++    <ConfigurationType>StaticLibrary</ConfigurationType>
++    <PlatformToolset>v141</PlatformToolset>
++    <CharacterSet>MultiByte</CharacterSet>
++    <WholeProgramOptimization>true</WholeProgramOptimization>
++  </PropertyGroup>
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_Dynamic_SSE|Win32'" Label="Configuration">
++    <ConfigurationType>DynamicLibrary</ConfigurationType>
++    <PlatformToolset>v141</PlatformToolset>
++    <CharacterSet>MultiByte</CharacterSet>
++    <WholeProgramOptimization>true</WholeProgramOptimization>
++  </PropertyGroup>
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_Dynamic_SSE|x64'" Label="Configuration">
++    <ConfigurationType>DynamicLibrary</ConfigurationType>
++    <PlatformToolset>v141</PlatformToolset>
++    <CharacterSet>MultiByte</CharacterSet>
++    <WholeProgramOptimization>true</WholeProgramOptimization>
++  </PropertyGroup>
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
++    <ConfigurationType>StaticLibrary</ConfigurationType>
++    <PlatformToolset>v141</PlatformToolset>
++    <CharacterSet>MultiByte</CharacterSet>
++    <WholeProgramOptimization>true</WholeProgramOptimization>
++  </PropertyGroup>
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
++    <ConfigurationType>StaticLibrary</ConfigurationType>
++    <PlatformToolset>v141</PlatformToolset>
++    <CharacterSet>MultiByte</CharacterSet>
++    <WholeProgramOptimization>true</WholeProgramOptimization>
++  </PropertyGroup>
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
++    <ConfigurationType>StaticLibrary</ConfigurationType>
++    <PlatformToolset>v141</PlatformToolset>
++    <CharacterSet>MultiByte</CharacterSet>
++  </PropertyGroup>
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
++    <ConfigurationType>StaticLibrary</ConfigurationType>
++    <PlatformToolset>v141</PlatformToolset>
++    <CharacterSet>MultiByte</CharacterSet>
++  </PropertyGroup>
++  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
++  <ImportGroup Label="ExtensionSettings">
++  </ImportGroup>
++  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_Static_SSE|Win32'" Label="PropertySheets">
++    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++  </ImportGroup>
++  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_Static_SSE|x64'" Label="PropertySheets">
++    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++  </ImportGroup>
++  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_Dynamic_SSE|Win32'" Label="PropertySheets">
++    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++  </ImportGroup>
++  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_Dynamic_SSE|x64'" Label="PropertySheets">
++    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++  </ImportGroup>
++  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
++    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++  </ImportGroup>
++  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
++    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++  </ImportGroup>
++  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
++    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++  </ImportGroup>
++  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
++    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
++    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
++  </ImportGroup>
++  <PropertyGroup Label="UserMacros" />
++  <PropertyGroup>
++    <_ProjectFileVersion>15.0.28127.55</_ProjectFileVersion>
++  </PropertyGroup>
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++    <OutDir>Debug\</OutDir>
++    <IntDir>Debug\</IntDir>
++  </PropertyGroup>
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++    <OutDir>$(Configuration)\</OutDir>
++    <IntDir>$(Configuration)\</IntDir>
++  </PropertyGroup>
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_Dynamic_SSE|Win32'">
++    <OutDir>$(Configuration)\</OutDir>
++    <IntDir>$(Configuration)\</IntDir>
++  </PropertyGroup>
++  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_Static_SSE|Win32'">
++    <OutDir>$(Configuration)\</OutDir>
++    <IntDir>$(Configuration)\</IntDir>
++  </PropertyGroup>
++  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
++    <ClCompile>
++      <Optimization>Disabled</Optimization>
++      <AdditionalIncludeDirectories>..\..\..\include;..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++      <MinimalRebuild>true</MinimalRebuild>
++      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
++      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
++      <PrecompiledHeader />
++      <WarningLevel>Level3</WarningLevel>
++      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
++      <CompileAs>CompileAsC</CompileAs>
++    </ClCompile>
++    <Lib>
++      <OutputFile>../../../lib/libspeexdsp.lib</OutputFile>
++    </Lib>
++  </ItemDefinitionGroup>
++  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
++    <ClCompile>
++      <Optimization>Disabled</Optimization>
++      <AdditionalIncludeDirectories>..\..\..\include;..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
++      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
++      <PrecompiledHeader>
++      </PrecompiledHeader>
++      <WarningLevel>Level3</WarningLevel>
++      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++      <CompileAs>CompileAsC</CompileAs>
++    </ClCompile>
++    <Lib>
++      <OutputFile>../../../lib/libspeexdsp.lib</OutputFile>
++    </Lib>
++  </ItemDefinitionGroup>
++  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
++    <ClCompile>
++      <Optimization>Full</Optimization>
++      <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
++      <IntrinsicFunctions>true</IntrinsicFunctions>
++      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
++      <AdditionalIncludeDirectories>..\..\..\include;..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++      <StringPooling>true</StringPooling>
++      <ExceptionHandling />
++      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
++      <BufferSecurityCheck>false</BufferSecurityCheck>
++      <PrecompiledHeader />
++      <WarningLevel>Level3</WarningLevel>
++      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++      <CompileAs>CompileAsC</CompileAs>
++      <DisableSpecificWarnings>4244;4305;4311;4100;4127;%(DisableSpecificWarnings)</DisableSpecificWarnings>
++    </ClCompile>
++    <Lib>
++      <OutputFile>../../../lib/libspeexdsp.lib</OutputFile>
++    </Lib>
++  </ItemDefinitionGroup>
++  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
++    <ClCompile>
++      <Optimization>Full</Optimization>
++      <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
++      <IntrinsicFunctions>true</IntrinsicFunctions>
++      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
++      <AdditionalIncludeDirectories>..\..\..\include;..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++      <StringPooling>true</StringPooling>
++      <ExceptionHandling>
++      </ExceptionHandling>
++      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
++      <BufferSecurityCheck>false</BufferSecurityCheck>
++      <PrecompiledHeader>
++      </PrecompiledHeader>
++      <WarningLevel>Level3</WarningLevel>
++      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++      <CompileAs>CompileAsC</CompileAs>
++      <DisableSpecificWarnings>4244;4305;4311;4100;4127;%(DisableSpecificWarnings)</DisableSpecificWarnings>
++    </ClCompile>
++    <Lib>
++      <OutputFile>../../../lib/libspeexdsp.lib</OutputFile>
++    </Lib>
++  </ItemDefinitionGroup>
++  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_Dynamic_SSE|Win32'">
++    <ClCompile>
++      <Optimization>Full</Optimization>
++      <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
++      <IntrinsicFunctions>true</IntrinsicFunctions>
++      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
++      <AdditionalIncludeDirectories>..\..\..\include;..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++      <PreprocessorDefinitions>USE_SSE;WIN32;NDEBUG;_WINDOWS;_USRDLL;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++      <StringPooling>true</StringPooling>
++      <ExceptionHandling />
++      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
++      <BufferSecurityCheck>false</BufferSecurityCheck>
++      <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
++      <PrecompiledHeader />
++      <WarningLevel>Level3</WarningLevel>
++      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++      <CompileAs>CompileAsC</CompileAs>
++      <DisableSpecificWarnings>4244;4305;4311;4100;4127;%(DisableSpecificWarnings)</DisableSpecificWarnings>
++    </ClCompile>
++    <Link>
++      <OutputFile>../../../bin/libspeexdsp.dll</OutputFile>
++      <ModuleDefinitionFile>..\..\libspeexdsp.def</ModuleDefinitionFile>
++      <SubSystem>Windows</SubSystem>
++      <OptimizeReferences>true</OptimizeReferences>
++      <EnableCOMDATFolding>true</EnableCOMDATFolding>
++      <RandomizedBaseAddress>false</RandomizedBaseAddress>
++      <DataExecutionPrevention />
++      <ImportLibrary>../../../lib/libspeexdsp.lib</ImportLibrary>
++      <TargetMachine>MachineX86</TargetMachine>
++    </Link>
++  </ItemDefinitionGroup>
++  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_Dynamic_SSE|x64'">
++    <ClCompile>
++      <Optimization>Full</Optimization>
++      <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
++      <IntrinsicFunctions>true</IntrinsicFunctions>
++      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
++      <AdditionalIncludeDirectories>..\..\..\include;..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++      <PreprocessorDefinitions>USE_SSE;WIN32;NDEBUG;_WINDOWS;_USRDLL;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++      <StringPooling>true</StringPooling>
++      <ExceptionHandling>
++      </ExceptionHandling>
++      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
++      <BufferSecurityCheck>false</BufferSecurityCheck>
++      <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
++      <PrecompiledHeader>
++      </PrecompiledHeader>
++      <WarningLevel>Level3</WarningLevel>
++      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++      <CompileAs>CompileAsC</CompileAs>
++      <DisableSpecificWarnings>4244;4305;4311;4100;4127;%(DisableSpecificWarnings)</DisableSpecificWarnings>
++    </ClCompile>
++    <Link>
++      <OutputFile>../../../bin/libspeexdsp.dll</OutputFile>
++      <ModuleDefinitionFile>..\..\libspeexdsp.def</ModuleDefinitionFile>
++      <SubSystem>Windows</SubSystem>
++      <OptimizeReferences>true</OptimizeReferences>
++      <EnableCOMDATFolding>true</EnableCOMDATFolding>
++      <RandomizedBaseAddress>false</RandomizedBaseAddress>
++      <DataExecutionPrevention>
++      </DataExecutionPrevention>
++      <ImportLibrary>../../../lib/libspeexdsp.lib</ImportLibrary>
++    </Link>
++  </ItemDefinitionGroup>
++  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_Static_SSE|Win32'">
++    <ClCompile>
++      <Optimization>Full</Optimization>
++      <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
++      <IntrinsicFunctions>true</IntrinsicFunctions>
++      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
++      <AdditionalIncludeDirectories>..\..\..\include;..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++      <PreprocessorDefinitions>USE_SSE;WIN32;NDEBUG;_WINDOWS;_USRDLL;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++      <StringPooling>true</StringPooling>
++      <ExceptionHandling />
++      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
++      <BufferSecurityCheck>false</BufferSecurityCheck>
++      <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
++      <PrecompiledHeader />
++      <WarningLevel>Level3</WarningLevel>
++      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++      <CompileAs>CompileAsC</CompileAs>
++      <DisableSpecificWarnings>4244;4305;4311;4100;4127;%(DisableSpecificWarnings)</DisableSpecificWarnings>
++    </ClCompile>
++    <Lib>
++      <OutputFile>../../../lib/libspeexdsp.lib</OutputFile>
++    </Lib>
++  </ItemDefinitionGroup>
++  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_Static_SSE|x64'">
++    <ClCompile>
++      <Optimization>Full</Optimization>
++      <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
++      <IntrinsicFunctions>true</IntrinsicFunctions>
++      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
++      <AdditionalIncludeDirectories>..\..\..\include;..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
++      <PreprocessorDefinitions>USE_SSE;WIN32;NDEBUG;_WINDOWS;_USRDLL;HAVE_CONFIG_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++      <StringPooling>true</StringPooling>
++      <ExceptionHandling>
++      </ExceptionHandling>
++      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
++      <BufferSecurityCheck>false</BufferSecurityCheck>
++      <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
++      <PrecompiledHeader>
++      </PrecompiledHeader>
++      <WarningLevel>Level3</WarningLevel>
++      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
++      <CompileAs>CompileAsC</CompileAs>
++      <DisableSpecificWarnings>4244;4305;4311;4100;4127;%(DisableSpecificWarnings)</DisableSpecificWarnings>
++    </ClCompile>
++    <Lib>
++      <OutputFile>../../../lib/libspeexdsp.lib</OutputFile>
++    </Lib>
++  </ItemDefinitionGroup>
++  <ItemGroup>
++    <ClCompile Include="..\..\..\libspeexdsp\buffer.c" />
++    <ClCompile Include="..\..\..\libspeexdsp\fftwrap.c" />
++    <ClCompile Include="..\..\..\libspeexdsp\filterbank.c" />
++    <ClCompile Include="..\..\..\libspeexdsp\jitter.c" />
++    <ClCompile Include="..\..\..\libspeexdsp\kiss_fft.c" />
++    <ClCompile Include="..\..\..\libspeexdsp\kiss_fftr.c" />
++    <ClCompile Include="..\..\..\libspeexdsp\mdf.c" />
++    <ClCompile Include="..\..\..\libspeexdsp\preprocess.c" />
++    <ClCompile Include="..\..\..\libspeexdsp\resample.c" />
++    <ClCompile Include="..\..\..\libspeexdsp\scal.c" />
++    <ClCompile Include="..\..\..\libspeexdsp\smallft.c" />
++  </ItemGroup>
++  <ItemGroup>
++    <ClInclude Include="..\..\..\include\speex\speex.h" />
++    <ClInclude Include="..\..\..\include\speex\speex_bits.h" />
++    <ClInclude Include="..\..\..\include\speex\speex_buffer.h" />
++    <ClInclude Include="..\..\..\include\speex\speex_echo.h" />
++    <ClInclude Include="..\..\..\include\speex\speex_jitter.h" />
++    <ClInclude Include="..\..\..\include\speex\speex_preprocess.h" />
++    <ClInclude Include="..\..\..\include\speex\speex_resampler.h" />
++    <ClInclude Include="..\..\..\include\speex\speex_types.h" />
++    <ClInclude Include="..\..\..\libspeexdsp\arch.h" />
++    <ClInclude Include="..\..\..\libspeexdsp\fftwrap.h" />
++    <ClInclude Include="..\..\..\libspeexdsp\filterbank.h" />
++    <ClInclude Include="..\..\..\libspeexdsp\fixed_debug.h" />
++    <ClInclude Include="..\..\..\libspeexdsp\fixed_generic.h" />
++    <ClInclude Include="..\..\..\libspeexdsp\kiss_fft.h" />
++    <ClInclude Include="..\..\..\libspeexdsp\kiss_fftr.h" />
++    <ClInclude Include="..\..\..\libspeexdsp\math_approx.h" />
++    <ClInclude Include="..\..\..\libspeexdsp\os_support.h" />
++    <ClInclude Include="..\..\..\libspeexdsp\pseudofloat.h" />
++    <ClInclude Include="..\..\..\libspeexdsp\smallft.h" />
++    <ClInclude Include="..\..\..\libspeexdsp\_kiss_fft_guts.h" />
++    <ClInclude Include="..\..\config.h" />
++  </ItemGroup>
++  <ItemGroup>
++    <None Include="..\..\libspeexdsp.def" />
++  </ItemGroup>
++  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
++  <ImportGroup Label="ExtensionTargets">
++  </ImportGroup>
++</Project>
+\ No newline at end of file
+-- 
+2.7.4
+
diff --git a/src/client/videomanager.cpp b/src/client/videomanager.cpp
index 2eec2b14a9d2d2306524e5d379da79b11e99389e..672283b8c1ec8992a5a80b17003f2694df1001c1 100644
--- a/src/client/videomanager.cpp
+++ b/src/client/videomanager.cpp
@@ -107,6 +107,22 @@ AudioFrame::setFormat(const jami::AudioFormat& format)
     d->format = format.sampleFormat;
 }
 
+jami::AudioFormat
+AudioFrame::getFormat() const
+{
+    return {
+        (unsigned)frame_->sample_rate,
+        (unsigned)frame_->channels,
+        (AVSampleFormat)frame_->format
+    };
+}
+
+size_t
+AudioFrame::getFrameSize() const
+{
+    return frame_->nb_samples;
+}
+
 void
 AudioFrame::reserve(size_t nb_samples)
 {
diff --git a/src/dring/videomanager_interface.h b/src/dring/videomanager_interface.h
index aa37b776ad0b7dab9a26363bd68e1e37113d19ee..c9e35138f7ef50ef1b07387a1317f6854b523a90 100644
--- a/src/dring/videomanager_interface.h
+++ b/src/dring/videomanager_interface.h
@@ -107,6 +107,8 @@ public:
     ~AudioFrame() {};
     void mix(const AudioFrame& o);
     float calcRMS() const;
+    jami::AudioFormat getFormat() const;
+    size_t getFrameSize() const;
 
 private:
     void setFormat(const jami::AudioFormat& format);
diff --git a/src/media/audio/alsa/alsalayer.cpp b/src/media/audio/alsa/alsalayer.cpp
index ebef086b7b1694e4ba03164a9b21938a769ccaa9..72699878981f71f7582b531293e3df635136e3a5 100644
--- a/src/media/audio/alsa/alsalayer.cpp
+++ b/src/media/audio/alsa/alsalayer.cpp
@@ -99,7 +99,6 @@ AlsaLayer::AlsaLayer(const AudioPreference &pref)
     , is_playback_open_(false)
     , is_capture_open_(false)
     , audioThread_(nullptr)
-    , mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
 {}
 
 AlsaLayer::~AlsaLayer()
@@ -702,10 +701,7 @@ void AlsaLayer::capture()
     const int framesPerBufferAlsa = 2048;
     toGetFrames = std::min(framesPerBufferAlsa, toGetFrames);
     if (auto r = read(toGetFrames)) {
-        if (isCaptureMuted_)
-            libav_utils::fillWithSilence(r->pointer());
-        //dcblocker_.process(captureBuff_);
-        mainRingBuffer_->put(std::move(r));
+        putRecorded(std::move(r));
     } else
         JAMI_ERR("ALSA MIC : Couldn't read!");
 }
diff --git a/src/media/audio/alsa/alsalayer.h b/src/media/audio/alsa/alsalayer.h
index 6837fe0505f5a5996c6354f77b106180653c2707..e28aaf2e51bbf2f6a22b16919a26c4fa6636eed9 100644
--- a/src/media/audio/alsa/alsalayer.h
+++ b/src/media/audio/alsa/alsalayer.h
@@ -250,7 +250,6 @@ class AlsaLayer : public AudioLayer {
         bool is_capture_open_;
 
         std::unique_ptr<AlsaThread> audioThread_;
-        std::shared_ptr<RingBuffer> mainRingBuffer_;
 };
 
 } // namespace jami
diff --git a/src/media/audio/audio_frame_resizer.cpp b/src/media/audio/audio_frame_resizer.cpp
index 5e05749f11389b2905a74c7271bda22d20e6e389..43dfbf4e475e8cb62cc0e54002fdd7fb920a105f 100644
--- a/src/media/audio/audio_frame_resizer.cpp
+++ b/src/media/audio/audio_frame_resizer.cpp
@@ -123,7 +123,7 @@ AudioFrameResizer::dequeue()
     if (samples() < frameSize_)
         return {};
 
-    auto frame = std::make_unique<AudioFrame>(format_, frameSize_);
+    auto frame = std::make_shared<AudioFrame>(format_, frameSize_);
     int ret;
     if ((ret = av_audio_fifo_read(queue_, reinterpret_cast<void**>(frame->pointer()->data), frameSize_)) < 0) {
         JAMI_ERR() << "Could not read samples from queue: " << libav_utils::getError(ret);
diff --git a/src/media/audio/audio_input.cpp b/src/media/audio/audio_input.cpp
index dfecac6cec8e92796c5567a299ca078ce59a5d55..1d07c5d51b32742afcc4b069681b871dadc1dc0d 100644
--- a/src/media/audio/audio_input.cpp
+++ b/src/media/audio/audio_input.cpp
@@ -323,7 +323,7 @@ AudioInput::createDecoder()
     }
 
     auto decoder = std::make_unique<MediaDecoder>([this](std::shared_ptr<MediaFrame>&& frame) {
-        fileBuf_->put(std::move(std::static_pointer_cast<AudioFrame>(frame)));
+        fileBuf_->put(std::static_pointer_cast<AudioFrame>(frame));
     });
 
     // NOTE don't emulate rate, file is read as frames are needed
diff --git a/src/media/audio/audio_receive_thread.cpp b/src/media/audio/audio_receive_thread.cpp
index 895978bf7bfcb105dea56c01f7379686c21a7d66..a5937217beac8c45f627d357a6e96b5b00d4f07f 100644
--- a/src/media/audio/audio_receive_thread.cpp
+++ b/src/media/audio/audio_receive_thread.cpp
@@ -59,7 +59,7 @@ AudioReceiveThread::setup()
 {
     audioDecoder_.reset(new MediaDecoder([this](std::shared_ptr<MediaFrame>&& frame) mutable {
         notify(frame);
-        ringbuffer_->put(std::move(std::static_pointer_cast<AudioFrame>(frame)));
+        ringbuffer_->put(std::static_pointer_cast<AudioFrame>(frame));
     }));
     audioDecoder_->setInterruptCallback(interruptCb, this);
 
diff --git a/src/media/audio/audiolayer.cpp b/src/media/audio/audiolayer.cpp
index 0cc5f2ec18e6a58676a01ad15ac2047b761b4452..80ddb2ef7c77ba7bfaf5d80b59335f35561f7d06 100644
--- a/src/media/audio/audiolayer.cpp
+++ b/src/media/audio/audiolayer.cpp
@@ -28,21 +28,88 @@
 #include "tonecontrol.h"
 #include "client/ring_signal.h"
 
+extern "C" {
+#include <speex/speex_echo.h>
+}
+
 #include <ctime>
 #include <algorithm>
 
 namespace jami {
 
+struct AudioLayer::EchoState
+{
+    EchoState(AudioFormat format, unsigned frameSize, unsigned tailLength)
+        : state(speex_echo_state_init_mc(
+                    frameSize, frameSize * 16,
+                    format.nb_channels, format.nb_channels), &speex_echo_state_destroy)
+        , playbackQueue(format, frameSize)
+        , recordQueue(format, frameSize) {
+            int sr = format.sample_rate;
+            speex_echo_ctl(state.get(), SPEEX_ECHO_SET_SAMPLING_RATE, &sr);
+        }
+
+    void putRecorded(std::shared_ptr<AudioFrame>&& in) {
+        // JAMI_DBG("putRecorded %s %d", in->getFormat().toString().c_str(), in->getFrameSize());
+        recordQueue.enqueue(std::move(in));
+    }
+
+    void putPlayback(const std::shared_ptr<AudioFrame>& in) {
+        // JAMI_DBG("putPlayback %s %d", in->getFormat().toString().c_str(), in->getFrameSize());
+        auto c = in;
+        playbackQueue.enqueue(std::move(c));
+    }
+
+    std::shared_ptr<AudioFrame> getRecorded() {
+        if (playbackQueue.samples() < playbackQueue.frameSize()
+           or recordQueue.samples() < recordQueue.frameSize()) {
+            /* JAMI_DBG("getRecorded underflow %d / %d, %d / %d",
+                playbackQueue.samples(), playbackQueue.frameSize(),
+                recordQueue.samples(), recordQueue.frameSize()); */
+            return {};
+        }
+        if (recordQueue.samples() > 2 * recordQueue.frameSize() && playbackQueue.samples() == 0) {
+            JAMI_DBG("getRecorded PLAYBACK underflow");
+            return recordQueue.dequeue();
+        }
+        while (playbackQueue.samples() > 10 * playbackQueue.frameSize()) {
+            JAMI_DBG("getRecorded playback underflow");
+            playbackQueue.dequeue();
+        }
+        while (recordQueue.samples() > 4 * recordQueue.frameSize()) {
+            JAMI_DBG("getRecorded record underflow");
+            recordQueue.dequeue();
+        }
+        auto playback = playbackQueue.dequeue();
+        auto record = recordQueue.dequeue();
+        if (playback and record) {
+            auto ret = std::make_shared<AudioFrame>(record->getFormat(), record->getFrameSize());
+            speex_echo_cancellation(state.get(),
+                (const int16_t*)record->pointer()->data[0],
+                (const int16_t*)playback->pointer()->data[0],
+                (int16_t*)ret->pointer()->data[0]);
+            return ret;
+        }
+        return {};
+    }
+
+private:
+    using SpeexEchoStatePtr = std::unique_ptr<SpeexEchoState, void(*)(SpeexEchoState*)>;
+    SpeexEchoStatePtr state;
+    AudioFrameResizer playbackQueue;
+    AudioFrameResizer recordQueue;
+};
+
 AudioLayer::AudioLayer(const AudioPreference &pref)
     : isCaptureMuted_(pref.getCaptureMuted())
     , isPlaybackMuted_(pref.getPlaybackMuted())
     , captureGain_(pref.getVolumemic())
     , playbackGain_(pref.getVolumespkr())
+    , mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
     , audioFormat_(Manager::instance().getRingBufferPool().getInternalAudioFormat())
     , audioInputFormat_(Manager::instance().getRingBufferPool().getInternalAudioFormat())
     , urgentRingBuffer_("urgentRingBuffer_id", SIZEBUF, audioFormat_)
     , resampler_(new Resampler)
-    , inputResampler_(new Resampler)
     , lastNotificationTime_()
 {
     urgentRingBuffer_.createReadOffset(RingBufferPool::DEFAULT_ID);
@@ -51,11 +118,12 @@ AudioLayer::AudioLayer(const AudioPreference &pref)
 AudioLayer::~AudioLayer()
 {}
 
-void AudioLayer::hardwareFormatAvailable(AudioFormat playback)
+void AudioLayer::hardwareFormatAvailable(AudioFormat playback, size_t bufSize)
 {
-    JAMI_DBG("Hardware audio format available : %s", playback.toString().c_str());
+    JAMI_DBG("Hardware audio format available : %s %zu", playback.toString().c_str(), bufSize);
     audioFormat_ = Manager::instance().hardwareAudioFormatChanged(playback);
     urgentRingBuffer_.setFormat(audioFormat_);
+    nativeFrameSize_ = bufSize;
 }
 
 void AudioLayer::hardwareInputFormatAvailable(AudioFormat capture)
@@ -86,6 +154,37 @@ void AudioLayer::flush()
     urgentRingBuffer_.flushAll();
 }
 
+void AudioLayer::playbackChanged(bool started)
+{
+    playbackStarted_ = started;
+    checkAEC();
+}
+
+void AudioLayer::recordChanged(bool started)
+{
+    recordStarted_ = started;
+    checkAEC();
+}
+
+void AudioLayer::setHasNativeAEC(bool hasEAC)
+{
+    hasNativeAEC_ = hasEAC;
+    checkAEC();
+}
+
+void AudioLayer::checkAEC()
+{
+    bool shouldSoftAEC = not hasNativeAEC_ and playbackStarted_ and recordStarted_;
+
+    if (not echoState_ and shouldSoftAEC) {
+        JAMI_WARN("Starting AEC");
+        echoState_.reset(new EchoState(audioFormat_, nativeFrameSize_, audioFormat_.sample_rate / 4));
+    } else if (echoState_ and not shouldSoftAEC) {
+        JAMI_WARN("Stopping AEC");
+        echoState_.reset();
+    }
+}
+
 void AudioLayer::putUrgent(AudioBuffer& buffer)
 {
     std::lock_guard<std::mutex> lock(mutex_);
@@ -153,19 +252,44 @@ AudioLayer::getToPlay(AudioFormat format, size_t writableSamples)
 
     std::shared_ptr<AudioFrame> playbackBuf {};
     while (!(playbackBuf = playbackQueue_->dequeue())) {
+        std::shared_ptr<AudioFrame> resampled;
+
         if (auto urgentSamples = urgentRingBuffer_.get(RingBufferPool::DEFAULT_ID)) {
             bufferPool.discard(1, RingBufferPool::DEFAULT_ID);
-            playbackQueue_->enqueue(resampler_->resample(std::move(urgentSamples),format));
+            resampled = resampler_->resample(std::move(urgentSamples),format);
         } else if (auto toneToPlay = Manager::instance().getTelephoneTone()) {
-            playbackQueue_->enqueue(resampler_->resample(toneToPlay->getNext(), format));
+            resampled = resampler_->resample(toneToPlay->getNext(), format);
         } else if (auto buf = bufferPool.getData(RingBufferPool::DEFAULT_ID)) {
-            playbackQueue_->enqueue(resampler_->resample(std::move(buf), format));
+            resampled = resampler_->resample(std::move(buf), format);
         } else {
             break;
         }
+
+        if (resampled) {
+            if (echoState_)  {
+                echoState_->putPlayback(resampled);
+            }
+            playbackQueue_->enqueue(std::move(resampled));
+        } else
+            break;
     }
 
     return playbackBuf;
 }
 
+void
+AudioLayer::putRecorded(std::shared_ptr<AudioFrame>&& frame)
+{
+    //if (isCaptureMuted_)
+    //    libav_utils::fillWithSilence(frame->pointer());
+
+    if (echoState_) {
+        echoState_->putRecorded(std::move(frame));
+        while (auto rec = echoState_->getRecorded())
+            mainRingBuffer_->put(std::move(rec));
+    } else {
+        mainRingBuffer_->put(std::move(frame));
+    }
+}
+
 } // namespace jami
diff --git a/src/media/audio/audiolayer.h b/src/media/audio/audiolayer.h
index 039ee5e5a72394e963f28a6508fa9acb19a5ab04..00b8525a0317f2dc5211a78bcb68f5a57c60155b 100644
--- a/src/media/audio/audiolayer.h
+++ b/src/media/audio/audiolayer.h
@@ -33,6 +33,11 @@
 #include <atomic>
 #include <condition_variable>
 
+extern "C" {
+struct SpeexEchoState_;
+typedef struct SpeexEchoState_ SpeexEchoState;
+}
+
 /**
  * @file  audiolayer.h
  * @brief Main sound class. Manages the data transfers between the application and the hardware.
@@ -223,7 +228,7 @@ protected:
     /**
      * Callback to be called by derived classes when the audio output is opened.
      */
-    void hardwareFormatAvailable(AudioFormat playback);
+    void hardwareFormatAvailable(AudioFormat playback, size_t bufSize = 0);
 
     /**
      * Set the input format on necessary objects.
@@ -232,6 +237,10 @@ protected:
 
     void devicesChanged();
 
+    void playbackChanged(bool started);
+    void recordChanged(bool started);
+    void setHasNativeAEC(bool hasEAC);
+
     std::shared_ptr<AudioFrame> getToPlay(AudioFormat format, size_t writableSamples);
 
     std::shared_ptr<AudioFrame> getToRing(AudioFormat format, size_t writableSamples);
@@ -242,6 +251,8 @@ protected:
         return ringBuff ? ringBuff : playBuff;
     }
 
+    void putRecorded(std::shared_ptr<AudioFrame>&& frame);
+
     void flush();
 
     /**
@@ -259,6 +270,10 @@ protected:
      */
     bool isRingtoneMuted_ {false};
 
+    bool playbackStarted_ {false};
+    bool recordStarted_ {false};
+    bool hasNativeAEC_ {true};
+
     /**
      * Gain applied to mic signal
      */
@@ -272,10 +287,8 @@ protected:
     /**
      * Buffers for audio processing
      */
-    AudioBuffer playbackBuffer_;
-    AudioBuffer playbackResampleBuffer_;
+    std::shared_ptr<RingBuffer> mainRingBuffer_;
     AudioBuffer ringtoneBuffer_;
-    AudioBuffer ringtoneResampleBuffer_;
     std::unique_ptr<AudioFrameResizer> playbackQueue_;
 
     /**
@@ -294,6 +307,8 @@ protected:
      */
     AudioFormat audioInputFormat_;
 
+    size_t nativeFrameSize_ {0};
+
     /**
      * Urgent ring buffer used for ringtones
      */
@@ -314,12 +329,11 @@ protected:
      */
     std::unique_ptr<Resampler> resampler_;
 
-    /**
-     * Manage input sampling rate conversions
-     */
-    std::unique_ptr<Resampler> inputResampler_;
+    struct EchoState;
+    std::unique_ptr<EchoState> echoState_;
 
 private:
+    void checkAEC();
 
     /**
      * Time of the last incoming call notification
diff --git a/src/media/audio/coreaudio/ios/corelayer.h b/src/media/audio/coreaudio/ios/corelayer.h
index 90fccb49a67179f4821bd740d37067f548f4032a..55ac4fbfcac73aa5e5e95cb5e8ce84823784ee32 100644
--- a/src/media/audio/coreaudio/ios/corelayer.h
+++ b/src/media/audio/coreaudio/ios/corelayer.h
@@ -165,8 +165,6 @@ class CoreLayer : public AudioLayer {
         UInt32 inChannelsPerFrame_;
         Float64 outSampleRate_;
         UInt32 outChannelsPerFrame_;
-
-        std::shared_ptr<RingBuffer> mainRingBuffer_;
 };
 
 } // namespace jami
diff --git a/src/media/audio/coreaudio/ios/corelayer.mm b/src/media/audio/coreaudio/ios/corelayer.mm
index d547c3f87968661f73e0075f63cbe977571beba8..2188f27e64784935a904dfb81e1afe64ba2fcada 100644
--- a/src/media/audio/coreaudio/ios/corelayer.mm
+++ b/src/media/audio/coreaudio/ios/corelayer.mm
@@ -43,7 +43,6 @@ CoreLayer::CoreLayer(const AudioPreference &pref)
     , indexOut_(pref.getAlsaCardout())
     , indexRing_(pref.getAlsaCardring())
     , playbackBuff_(0, audioFormat_)
-    , mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
 {}
 
 CoreLayer::~CoreLayer()
@@ -440,7 +439,7 @@ CoreLayer::read(AudioUnitRenderActionFlags* ioActionFlags,
 
     auto format = audioInputFormat_;
     format.sampleFormat = AV_SAMPLE_FMT_FLTP;
-    auto inBuff = std::make_unique<AudioFrame>(format, inNumberFrames);
+    auto inBuff = std::make_shared<AudioFrame>(format, inNumberFrames);
     if (isCaptureMuted_) {
         libav_utils::fillWithSilence(inBuff->pointer());
     } else {
@@ -448,7 +447,7 @@ CoreLayer::read(AudioUnitRenderActionFlags* ioActionFlags,
         for (unsigned i = 0; i < inChannelsPerFrame_; ++i)
             std::copy_n((Float32*)captureBuff_->mBuffers[i].mData, inNumberFrames, (Float32*)in.extended_data[i]);
     }
-    mainRingBuffer_->put(std::move(inBuff));
+    putRecorded(std::move(inBuff));
 }
 
 void CoreLayer::updatePreference(AudioPreference &preference, int index, DeviceType type)
diff --git a/src/media/audio/coreaudio/osx/corelayer.cpp b/src/media/audio/coreaudio/osx/corelayer.cpp
index 853a648db740bb85a8dd8526fc1dff08fdfffb6e..72e8242b9f20ddd2f816189481ebbac2e2cc0c58 100644
--- a/src/media/audio/coreaudio/osx/corelayer.cpp
+++ b/src/media/audio/coreaudio/osx/corelayer.cpp
@@ -38,7 +38,6 @@ CoreLayer::CoreLayer(const AudioPreference &pref)
     , indexOut_(pref.getAlsaCardout())
     , indexRing_(pref.getAlsaCardring())
     , playbackBuff_(0, audioFormat_)
-    , mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
 {}
 
 CoreLayer::~CoreLayer()
@@ -345,7 +344,7 @@ CoreLayer::read(AudioUnitRenderActionFlags* ioActionFlags,
 
     auto format = audioInputFormat_;
     format.sampleFormat = AV_SAMPLE_FMT_FLTP;
-    auto inBuff = std::make_unique<AudioFrame>(format, inNumberFrames);
+    auto inBuff = std::make_shared<AudioFrame>(format, inNumberFrames);
     if (isCaptureMuted_) {
         libav_utils::fillWithSilence(inBuff->pointer());
     } else {
@@ -353,7 +352,7 @@ CoreLayer::read(AudioUnitRenderActionFlags* ioActionFlags,
         for (unsigned i = 0; i < inChannelsPerFrame_; ++i)
             std::copy_n((Float32*)captureBuff_->mBuffers[i].mData, inNumberFrames, (Float32*)in.extended_data[i]);
     }
-    mainRingBuffer_->put(std::move(inBuff));
+    putRecorded(std::move(inBuff));
 }
 
 void CoreLayer::updatePreference(AudioPreference &preference, int index, DeviceType type)
diff --git a/src/media/audio/coreaudio/osx/corelayer.h b/src/media/audio/coreaudio/osx/corelayer.h
index 9d447b2ec52ed8606172153e539fd69561075bcc..e7a827625f3ae0706486bb1ea1764a553944cefd 100644
--- a/src/media/audio/coreaudio/osx/corelayer.h
+++ b/src/media/audio/coreaudio/osx/corelayer.h
@@ -172,8 +172,6 @@ class CoreLayer : public AudioLayer {
         Float64 inSampleRate_;
         UInt32 inChannelsPerFrame_;
 
-        std::shared_ptr<RingBuffer> mainRingBuffer_;
-
         std::vector<AudioDevice> getDeviceList(bool getCapture) const;
 };
 
diff --git a/src/media/audio/jack/jacklayer.cpp b/src/media/audio/jack/jacklayer.cpp
index dc34dd3caeda3b938ea87a592139b4178907d1b8..3f175ba3862c02a3ac2e91ddf9e1eefb8dca9fa9 100644
--- a/src/media/audio/jack/jacklayer.cpp
+++ b/src/media/audio/jack/jacklayer.cpp
@@ -209,8 +209,7 @@ createPorts(jack_client_t *client, std::vector<jack_port_t *> &ports,
 JackLayer::JackLayer(const AudioPreference &p) :
     AudioLayer(p),
     captureClient_(nullptr),
-    playbackClient_(nullptr),
-    mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
+    playbackClient_(nullptr)
 {
     playbackClient_ = jack_client_open(PACKAGE_NAME,
             (jack_options_t) (JackNullOption | JackNoStartServer), NULL);
diff --git a/src/media/audio/jack/jacklayer.h b/src/media/audio/jack/jacklayer.h
index dde32e50e444f4e4c5d4325829da2c299971765f..353ed2ecf6885bbca914b66dfc7226c95837b5ae 100644
--- a/src/media/audio/jack/jacklayer.h
+++ b/src/media/audio/jack/jacklayer.h
@@ -49,7 +49,6 @@ private:
     std::thread ringbuffer_thread_;
     std::mutex ringbuffer_thread_mutex_;
     std::condition_variable data_ready_;
-    std::shared_ptr<RingBuffer> mainRingBuffer_;
 
     static int process_capture(jack_nframes_t frames, void *arg);
     static int process_playback(jack_nframes_t frames, void *arg);
diff --git a/src/media/audio/opensl/audio_player.cpp b/src/media/audio/opensl/audio_player.cpp
index dfd7b3be5ef755484236d6e61ca97b46d55c1bc9..b80394e5f46abce46f23cdeddf4332c1e9ee5cb6 100644
--- a/src/media/audio/opensl/audio_player.cpp
+++ b/src/media/audio/opensl/audio_player.cpp
@@ -40,8 +40,6 @@ void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *ctx) {
     (static_cast<AudioPlayer *>(ctx))->processSLCallback(bq);
 }
 void AudioPlayer::processSLCallback(SLAndroidSimpleBufferQueueItf bq) {
-    std::lock_guard<std::mutex> lk(m_);
-
     // retrieve the finished device buf and put onto the free queue
     // so recorder could re-use it
     sample_buf *buf;
diff --git a/src/media/audio/opensl/audio_player.h b/src/media/audio/opensl/audio_player.h
index 7fc470b8c67a9f5920cc14fe591313f09dce3f65..0e9383c8963053979fe2324f929511e220349963 100644
--- a/src/media/audio/opensl/audio_player.h
+++ b/src/media/audio/opensl/audio_player.h
@@ -58,7 +58,6 @@ public:
     void registerCallback(EngineCallback cb) {callback_ = cb;}
     size_t dbgGetDevBufCount();
 
-    std::mutex m_;
     std::atomic_bool waiting_ {false};
 };
 
diff --git a/src/media/audio/opensl/audio_recorder.cpp b/src/media/audio/opensl/audio_recorder.cpp
index ff6e55d5f8cda0cc052e879ef80ec8a4e33b1969..699e1c60bc8989b7bc89ee326fd6ca9f6b6eb7ea 100644
--- a/src/media/audio/opensl/audio_recorder.cpp
+++ b/src/media/audio/opensl/audio_recorder.cpp
@@ -59,7 +59,7 @@ void AudioRecorder::processSLCallback(SLAndroidSimpleBufferQueueItf bq) {
 AudioRecorder::AudioRecorder(jami::AudioFormat sampleFormat, SLEngineItf slEngine) :
         sampleInfo_(sampleFormat)
 {
-    // configure audio source
+    // configure audio source/
     SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE,
                                       SL_IODEVICE_AUDIOINPUT,
                                       SL_DEFAULTDEVICEID_AUDIOINPUT,
@@ -76,7 +76,13 @@ AudioRecorder::AudioRecorder(jami::AudioFormat sampleFormat, SLEngineItf slEngin
 
     // create audio recorder
     // (requires the RECORD_AUDIO permission)
-    const SLInterfaceID ids[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
+    const SLInterfaceID ids[] = {
+        SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+        SL_IID_ANDROIDCONFIGURATION,
+        SL_IID_ANDROIDACOUSTICECHOCANCELLATION,
+        SL_IID_ANDROIDAUTOMATICGAINCONTROL,
+        SL_IID_ANDROIDNOISESUPPRESSION
+    };
     const SLboolean req[1] = {SL_BOOLEAN_TRUE};
     SLresult result;
     result = (*slEngine)->CreateAudioRecorder(slEngine,
@@ -91,11 +97,85 @@ AudioRecorder::AudioRecorder(jami::AudioFormat sampleFormat, SLEngineItf slEngin
     result = (*recObjectItf_)->GetInterface(recObjectItf_, SL_IID_ANDROIDCONFIGURATION, &recordConfig);
     result = (*recordConfig)->SetConfiguration(recordConfig, SL_ANDROID_KEY_RECORDING_PRESET, &streamType, sizeof(SLint32));
 
+    bool aec{true}, agc(true), ns(true);
+
+
     result = (*recObjectItf_)->Realize(recObjectItf_, SL_BOOLEAN_FALSE);
     SLASSERT(result);
     result = (*recObjectItf_)->GetInterface(recObjectItf_, SL_IID_RECORD, &recItf_);
     SLASSERT(result);
 
+
+    /* Check actual performance mode granted*/
+    SLuint32 modeRetrieved = SL_ANDROID_PERFORMANCE_NONE;
+    SLuint32 modeSize = sizeof(SLuint32);
+    result = (*recordConfig)->GetConfiguration(recordConfig, SL_ANDROID_KEY_PERFORMANCE_MODE,
+            &modeSize, (void*)&modeRetrieved);
+    SLASSERT(result);
+    JAMI_WARN("Actual performance mode is %u\n", modeRetrieved);
+
+    /* Enable AEC if requested */
+    if (aec) {
+        SLAndroidAcousticEchoCancellationItf aecItf;
+        result = (*recObjectItf_)->GetInterface(recObjectItf_, SL_IID_ANDROIDACOUSTICECHOCANCELLATION, (void*)&aecItf);
+        JAMI_WARN("AEC is %savailable\n", SL_RESULT_SUCCESS == result ? "" : "not ");
+        if (SL_RESULT_SUCCESS == result) {
+            SLboolean enabled;
+            result = (*aecItf)->IsEnabled(aecItf, &enabled);
+            SLASSERT(result);
+            JAMI_WARN("AEC was %s\n", enabled ? "enabled" : "not enabled");
+
+            result = (*aecItf)->SetEnabled(aecItf, true);
+            SLASSERT(result);
+
+            result = (*aecItf)->IsEnabled(aecItf, &enabled);
+            SLASSERT(result);
+            JAMI_WARN("AEC is now %s\n", enabled ? "enabled" : "not enabled");
+
+            hasNativeAEC_ = enabled;
+        }
+    }
+    /* Enable AGC if requested */
+    if (agc) {
+        SLAndroidAutomaticGainControlItf agcItf;
+        result = (*recObjectItf_)->GetInterface(
+                recObjectItf_, SL_IID_ANDROIDAUTOMATICGAINCONTROL, (void*)&agcItf);
+        JAMI_WARN("AGC is %savailable\n", SL_RESULT_SUCCESS == result ? "" : "not ");
+        if (SL_RESULT_SUCCESS == result) {
+            SLboolean enabled;
+            result = (*agcItf)->IsEnabled(agcItf, &enabled);
+            SLASSERT(result);
+            JAMI_WARN("AGC was %s\n", enabled ? "enabled" : "not enabled");
+
+            result = (*agcItf)->SetEnabled(agcItf, true);
+            SLASSERT(result);
+
+            result = (*agcItf)->IsEnabled(agcItf, &enabled);
+            SLASSERT(result);
+            JAMI_WARN("AGC is now %s\n", enabled ? "enabled" : "not enabled");
+        }
+    }
+    /* Enable NS if requested */
+    if (ns) {
+        SLAndroidNoiseSuppressionItf nsItf;
+        result = (*recObjectItf_)->GetInterface(
+                recObjectItf_, SL_IID_ANDROIDNOISESUPPRESSION, (void*)&nsItf);
+        JAMI_WARN("NS is %savailable\n", SL_RESULT_SUCCESS == result ? "" : "not ");
+        if (SL_RESULT_SUCCESS == result) {
+            SLboolean enabled;
+            result = (*nsItf)->IsEnabled(nsItf, &enabled);
+            SLASSERT(result);
+            JAMI_WARN("NS was %s\n", enabled ? "enabled" : "not enabled");
+
+            result = (*nsItf)->SetEnabled(nsItf, true);
+            SLASSERT(result);
+
+            result = (*nsItf)->IsEnabled(nsItf, &enabled);
+            SLASSERT(result);
+            JAMI_WARN("NS is now %s\n", enabled ? "enabled" : "not enabled");
+        }
+    }
+
     result = (*recObjectItf_)->GetInterface(recObjectItf_, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recBufQueueItf_);
     SLASSERT(result);
 
diff --git a/src/media/audio/opensl/audio_recorder.h b/src/media/audio/opensl/audio_recorder.h
index 85cf433f2d62b0a24b01e28a9209a3e1cc7369fc..7e86bf23c5a7fd4ee2f6e871b9ff8a831438a5e5 100644
--- a/src/media/audio/opensl/audio_recorder.h
+++ b/src/media/audio/opensl/audio_recorder.h
@@ -40,6 +40,7 @@ class AudioRecorder {
     uint32_t    audioBufCount;
 
     EngineCallback callback_ {};
+    bool hasNativeAEC_ {false};
 
 public:
     explicit AudioRecorder(jami::AudioFormat, SLEngineItf engineEngine);
@@ -52,6 +53,8 @@ public:
     void processSLCallback(SLAndroidSimpleBufferQueueItf bq);
     void registerCallback(EngineCallback cb) {callback_ = cb;}
     size_t dbgGetDevBufCount();
+
+    bool hasNativeAEC() const { return hasNativeAEC_; }
 };
 
 }
diff --git a/src/media/audio/opensl/opensllayer.cpp b/src/media/audio/opensl/opensllayer.cpp
index 79ca4f56f5f37c31c202d5f62b2b76fb15412989..445b0603f53ac2eb3030b7345382b1e42db5697b 100644
--- a/src/media/audio/opensl/opensllayer.cpp
+++ b/src/media/audio/opensl/opensllayer.cpp
@@ -30,25 +30,19 @@
 #include "logger.h"
 #include "array_size.h"
 
+#include <SLES/OpenSLES_AndroidConfiguration.h>
+
 #include <thread>
 #include <chrono>
 #include <cstdio>
 #include <cassert>
 #include <unistd.h>
 
-#include "SLES/OpenSLES_AndroidConfiguration.h"
-
-/* available only from api 14 */
-#ifndef SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION
-#define SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION ((SLuint32) 0x00000004)
-#endif
-
 namespace jami {
 
 // Constructor
 OpenSLLayer::OpenSLLayer(const AudioPreference &pref)
-    : AudioLayer(pref),
-     mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
+    : AudioLayer(pref)
 {}
 
 // Destructor
@@ -81,7 +75,7 @@ OpenSLLayer::startStream(AudioStreamType stream)
     emitSignal<DRing::ConfigurationSignal::GetHardwareAudioFormat>(&hw_infos);
     hardwareFormat_ = AudioFormat(hw_infos[0], 1); // Mono on Android
     hardwareBuffSize_ = hw_infos[1];
-    hardwareFormatAvailable(hardwareFormat_);
+    hardwareFormatAvailable(hardwareFormat_, hardwareBuffSize_);
 
     startThread_ = std::thread([this](){
         init();
@@ -216,8 +210,9 @@ OpenSLLayer::engineServicePlay(bool waiting) {
                 break;
             } else
                 freePlayBufQueue_.pop();
-        } else
+        } else {
             break;
+        }
     }
 }
 
@@ -284,6 +279,7 @@ OpenSLLayer::initAudioCapture()
         recorder_.reset(new opensl::AudioRecorder(hardwareFormat_, engineInterface_));
         recorder_->setBufQueues(&freeRecBufQueue_, &recBufQueue_);
         recorder_->registerCallback(std::bind(&OpenSLLayer::engineServiceRec, this, _1));
+        setHasNativeAEC(recorder_->hasNativeAEC());
     } catch (const std::exception& e) {
         JAMI_ERR("Error initializing audio capture: %s", e.what());
     }
@@ -301,24 +297,26 @@ OpenSLLayer::startAudioPlayback()
     if (ringtone_)
         ringtone_->start();
     playThread = std::thread([&]() {
+        playbackChanged(true);
         std::unique_lock<std::mutex> lck(playMtx);
         while (player_ || ringtone_) {
             playCv.wait_for(lck, std::chrono::seconds(1));
             if (player_ && player_->waiting_) {
-                std::lock_guard<std::mutex> lk(player_->m_);
+                //std::lock_guard<std::mutex> lk(player_->m_);
                 engineServicePlay(false);
                 auto n = playBufQueue_.size();
                 if (n >= PLAY_KICKSTART_BUFFER_COUNT)
                     player_->playAudioBuffers(n);
             }
             if (ringtone_ && ringtone_->waiting_) {
-                std::lock_guard<std::mutex> lk(ringtone_->m_);
+                //std::lock_guard<std::mutex> lk(ringtone_->m_);
                 engineServiceRing(false);
                 auto n = ringBufQueue_.size();
                 if (n >= PLAY_KICKSTART_BUFFER_COUNT)
                     ringtone_->playAudioBuffers(n);
             }
         }
+        playbackChanged(false);
     });
     JAMI_WARN("Audio playback started");
 }
@@ -332,6 +330,7 @@ OpenSLLayer::startAudioCapture()
 
     recorder_->start();
     recThread = std::thread([&]() {
+        recordChanged(true);
         std::unique_lock<std::mutex> lck(recMtx);
         while (recorder_) {
             recCv.wait_for(lck, std::chrono::seconds(1));
@@ -342,18 +341,18 @@ OpenSLLayer::startAudioCapture()
                 recBufQueue_.pop();
                 if (buf->size_ > 0) {
                     auto nb_samples = buf->size_ / hardwareFormat_.getBytesPerFrame();
-                    auto out = std::make_unique<AudioFrame>(hardwareFormat_, nb_samples);
+                    auto out = std::make_shared<AudioFrame>(hardwareFormat_, nb_samples);
                     if (isCaptureMuted_)
                         libav_utils::fillWithSilence(out->pointer());
                     else
                         std::copy_n((const AudioSample*)buf->buf_, nb_samples, (AudioSample*)out->pointer()->data[0]);
-                    // dcblocker_.process(buffer);
-                    mainRingBuffer_->put(std::move(out));
+                    putRecorded(std::move(out));
                 }
                 buf->size_ = 0;
                 freeRecBufQueue_.push(buf);
             }
         }
+        recordChanged(false);
     });
 
     JAMI_DBG("Audio capture started");
diff --git a/src/media/audio/opensl/opensllayer.h b/src/media/audio/opensl/opensllayer.h
index 239537b4f7b56426e1c3542b0bdb20f8879ad896..cdd8dc27cfd849c2888762a7d818cb736d2511aa 100644
--- a/src/media/audio/opensl/opensllayer.h
+++ b/src/media/audio/opensl/opensllayer.h
@@ -18,8 +18,7 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
  */
 
-#ifndef _OPENSL_LAYER_H
-#define _OPENSL_LAYER_H
+#pragma once
 
 #include <SLES/OpenSLES.h>
 #include <SLES/OpenSLES_Android.h>
@@ -52,147 +51,144 @@ class RingBuffer;
  */
 
 class OpenSLLayer : public AudioLayer {
-    public:
-        /**
-         * Constructor
-         */
-        OpenSLLayer(const AudioPreference &pref);
+public:
+    /**
+     * Constructor
+     */
+    OpenSLLayer(const AudioPreference &pref);
 
-        /**
-         * Destructor
-         */
-        ~OpenSLLayer();
+    /**
+     * Destructor
+     */
+    ~OpenSLLayer();
 
-        /**
-         * Start the capture stream and prepare the playback stream.
-         * The playback starts accordingly to its threshold
-         */
-        virtual void startStream(AudioStreamType stream = AudioStreamType::DEFAULT);
-
-        /**
-         * Stop the playback and capture streams.
-         * Drops the pending frames and put the capture and playback handles to PREPARED state
-         */
-        virtual void stopStream();
+    /**
+     * Start the capture stream and prepare the playback stream.
+     * The playback starts accordingly to its threshold
+     */
+    virtual void startStream(AudioStreamType stream = AudioStreamType::DEFAULT);
+
+    /**
+     * Stop the playback and capture streams.
+     * Drops the pending frames and put the capture and playback handles to PREPARED state
+     */
+    virtual void stopStream();
 
-        /**
-         * Scan the sound card available for capture on the system
-         * @return std::vector<std::string> The vector containing the string description of the card
-         */
-        virtual std::vector<std::string> getCaptureDeviceList() const;
+    /**
+     * Scan the sound card available for capture on the system
+     * @return std::vector<std::string> The vector containing the string description of the card
+     */
+    virtual std::vector<std::string> getCaptureDeviceList() const;
 
-        /**
-         * Scan the sound card available for capture on the system
-         * @return std::vector<std::string> The vector containing the string description of the card
-         */
-        virtual std::vector<std::string> getPlaybackDeviceList() const;
+    /**
+     * Scan the sound card available for capture on the system
+     * @return std::vector<std::string> The vector containing the string description of the card
+     */
+    virtual std::vector<std::string> getPlaybackDeviceList() const;
 
-        void init();
+    void init();
 
-        void initAudioEngine();
+    void initAudioEngine();
 
-        void shutdownAudioEngine();
+    void shutdownAudioEngine();
 
-        void initAudioPlayback();
+    void initAudioPlayback();
 
-        void initAudioCapture();
+    void initAudioCapture();
 
-        void startAudioPlayback();
+    void startAudioPlayback();
 
-        void startAudioCapture();
+    void startAudioCapture();
 
-        void stopAudioPlayback();
+    void stopAudioPlayback();
 
-        void stopAudioCapture();
+    void stopAudioCapture();
 
-        virtual int getAudioDeviceIndex(const std::string&, DeviceType) const {
-            return 0;
-        }
+    virtual int getAudioDeviceIndex(const std::string&, DeviceType) const {
+        return 0;
+    }
 
-        virtual std::string getAudioDeviceName(int, DeviceType) const {
-            return "";
-        }
+    virtual std::string getAudioDeviceName(int, DeviceType) const {
+        return "";
+    }
 
-        void engineServicePlay(bool waiting);
-        void engineServiceRing(bool waiting);
-        void engineServiceRec(bool waiting);
+    void engineServicePlay(bool waiting);
+    void engineServiceRing(bool waiting);
+    void engineServiceRec(bool waiting);
 
-    private:
-        /**
-         * Get the index of the audio card for capture
-         * @return int The index of the card used for capture
-         *                     0 for the first available card on the system, 1 ...
-         */
-        virtual int getIndexCapture() const {
-            return 0;
-        }
+private:
+    /**
+     * Get the index of the audio card for capture
+     * @return int The index of the card used for capture
+     *                     0 for the first available card on the system, 1 ...
+     */
+    virtual int getIndexCapture() const {
+        return 0;
+    }
 
-        /**
-         * Get the index of the audio card for playback
-         * @return int The index of the card used for playback
-         *                     0 for the first available card on the system, 1 ...
-         */
-        virtual int getIndexPlayback() const {
-            return 0;
-        }
+    /**
+     * Get the index of the audio card for playback
+     * @return int The index of the card used for playback
+     *                     0 for the first available card on the system, 1 ...
+     */
+    virtual int getIndexPlayback() const {
+        return 0;
+    }
 
-        /**
-         * Get the index of the audio card for ringtone (could be differnet from playback)
-         * @return int The index of the card used for ringtone
-         *                 0 for the first available card on the system, 1 ...
-         */
-        virtual int getIndexRingtone() const {
-            return 0;
-        }
+    /**
+     * Get the index of the audio card for ringtone (could be differnet from playback)
+     * @return int The index of the card used for ringtone
+     *                 0 for the first available card on the system, 1 ...
+     */
+    virtual int getIndexRingtone() const {
+        return 0;
+    }
 
-        uint32_t dbgEngineGetBufCount();
+    uint32_t dbgEngineGetBufCount();
 
-        void dumpAvailableEngineInterfaces();
+    void dumpAvailableEngineInterfaces();
 
-        NON_COPYABLE(OpenSLLayer);
+    NON_COPYABLE(OpenSLLayer);
 
-        virtual void updatePreference(AudioPreference &pref, int index, DeviceType type);
+    virtual void updatePreference(AudioPreference &pref, int index, DeviceType type);
 
-        /**
-         * OpenSL standard object interface
-         */
-        SLObjectItf engineObject_ {nullptr};
+    /**
+     * OpenSL standard object interface
+     */
+    SLObjectItf engineObject_ {nullptr};
 
-        /**
-         * OpenSL sound engine interface
-         */
-        SLEngineItf engineInterface_ {nullptr};
+    /**
+     * OpenSL sound engine interface
+     */
+    SLEngineItf engineInterface_ {nullptr};
 
-        std::unique_ptr<opensl::AudioPlayer> player_ {};
-        std::unique_ptr<opensl::AudioPlayer> ringtone_ {};
-        std::unique_ptr<opensl::AudioRecorder> recorder_ {};
+    std::unique_ptr<opensl::AudioPlayer> player_ {};
+    std::unique_ptr<opensl::AudioPlayer> ringtone_ {};
+    std::unique_ptr<opensl::AudioRecorder> recorder_ {};
 
-        AudioQueue     freePlayBufQueue_ {BUF_COUNT};
-        AudioQueue     playBufQueue_ {BUF_COUNT};
+    AudioQueue     freePlayBufQueue_ {BUF_COUNT};
+    AudioQueue     playBufQueue_ {BUF_COUNT};
 
-        AudioQueue     freeRingBufQueue_ {BUF_COUNT};
-        AudioQueue     ringBufQueue_ {BUF_COUNT};
+    AudioQueue     freeRingBufQueue_ {BUF_COUNT};
+    AudioQueue     ringBufQueue_ {BUF_COUNT};
 
-        std::mutex     playMtx {};
-        std::condition_variable playCv {};
-        std::thread    playThread {};
+    std::mutex     playMtx {};
+    std::condition_variable playCv {};
+    std::thread    playThread {};
 
-        AudioQueue     freeRecBufQueue_ {BUF_COUNT};    //Owner of the queue
-        AudioQueue     recBufQueue_ {BUF_COUNT};     //Owner of the queue
+    AudioQueue     freeRecBufQueue_ {BUF_COUNT};    //Owner of the queue
+    AudioQueue     recBufQueue_ {BUF_COUNT};     //Owner of the queue
 
-        std::mutex     recMtx {};
-        std::condition_variable recCv {};
-        std::thread    recThread {};
+    std::mutex     recMtx {};
+    std::condition_variable recCv {};
+    std::thread    recThread {};
 
-        std::vector<sample_buf> bufs_ {};
+    std::vector<sample_buf> bufs_ {};
 
-        AudioFormat hardwareFormat_ {AudioFormat::MONO()};
-        size_t hardwareBuffSize_ {BUFFER_SIZE};
+    AudioFormat hardwareFormat_ {AudioFormat::MONO()};
+    size_t hardwareBuffSize_ {BUFFER_SIZE};
 
-        std::shared_ptr<RingBuffer> mainRingBuffer_;
-        std::thread startThread_;
+    std::thread startThread_;
 };
 
 }
-
-#endif // _OPENSL_LAYER_H_
diff --git a/src/media/audio/portaudio/portaudiolayer.cpp b/src/media/audio/portaudio/portaudiolayer.cpp
index d567dad89ffac0156009923f28510c0d2556be7b..d36722d208ad8d541fcc84124d972ed12ddeaf3c 100644
--- a/src/media/audio/portaudio/portaudiolayer.cpp
+++ b/src/media/audio/portaudio/portaudiolayer.cpp
@@ -33,6 +33,8 @@
 #include <cmath>
 
 namespace jami {
+    
+struct AudioLayer::EchoState;
 
 enum Direction { Input = 0, Output = 1, IO = 2, End = 3 };
 
@@ -55,8 +57,6 @@ struct PortAudioLayer::PortAudioLayerImpl
 
     AudioBuffer playbackBuff_;
 
-    std::shared_ptr<RingBuffer> mainRingBuffer_;
-
     std::array<PaStream*, static_cast<int>(Direction::End)> streams_;
 
     int paOutputCallback(PortAudioLayer& parent,
@@ -226,7 +226,6 @@ PortAudioLayer::PortAudioLayerImpl::PortAudioLayerImpl(PortAudioLayer& parent, c
     , indexOut_ {pref.getAlsaCardout()}
     , indexRing_ {pref.getAlsaCardring()}
     , playbackBuff_ {0, parent.audioFormat_}
-    , mainRingBuffer_ {Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID)}
 {
     init(parent);
 }
@@ -562,13 +561,13 @@ PortAudioLayer::PortAudioLayerImpl::paInputCallback(PortAudioLayer& parent,
         return paContinue;
     }
 
-    auto inBuff = std::make_unique<AudioFrame>(parent.audioInputFormat_, framesPerBuffer);
+    auto inBuff = std::make_shared<AudioFrame>(parent.audioInputFormat_, framesPerBuffer);
     auto nFrames = framesPerBuffer * parent.audioInputFormat_.nb_channels;
     if (parent.isCaptureMuted_)
         libav_utils::fillWithSilence(inBuff->pointer());
     else
         std::copy_n(inputBuffer, nFrames, (AudioSample*)inBuff->pointer()->extended_data[0]);
-    mainRingBuffer_->put(std::move(inBuff));
+    parent.putRecorded(std::move(inBuff));
     return paContinue;
 }
 
diff --git a/src/media/audio/pulseaudio/pulselayer.cpp b/src/media/audio/pulseaudio/pulselayer.cpp
index 90dea49b20ac34b20fd3492f03a957999789ee8a..780d6c7c1a68d80b7b3be3e6c29eb3dc42b9f20a 100644
--- a/src/media/audio/pulseaudio/pulselayer.cpp
+++ b/src/media/audio/pulseaudio/pulselayer.cpp
@@ -65,7 +65,6 @@ PulseLayer::PulseLayer(AudioPreference &pref)
     , ringtone_()
     , mainloop_(pa_threaded_mainloop_new(), pa_threaded_mainloop_free)
     , preference_(pref)
-    , mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
 {
     if (!mainloop_)
         throw std::runtime_error("Couldn't create pulseaudio mainloop");
@@ -456,7 +455,7 @@ void PulseLayer::readFromMic()
     size_t sample_size = record_->frameSize();
     const size_t samples = bytes / sample_size;
 
-    auto out = std::make_unique<AudioFrame>(record_->format(), samples);
+    auto out = std::make_shared<AudioFrame>(record_->format(), samples);
     if (isCaptureMuted_)
         libav_utils::fillWithSilence(out->pointer());
     else
@@ -465,8 +464,7 @@ void PulseLayer::readFromMic()
     if (pa_stream_drop(record_->stream()) < 0)
         JAMI_ERR("Capture stream drop failed: %s" , pa_strerror(pa_context_errno(context_)));
 
-    //dcblocker_.process(*out);
-    mainRingBuffer_->put(std::move(out));
+    putRecorded(std::move(out));
 }
 
 void PulseLayer::ringtoneToSpeaker()
@@ -710,20 +708,20 @@ void PulseLayer::updatePreference(AudioPreference &preference, int index, Device
     const std::string devName(getAudioDeviceName(index, type));
 
     switch (type) {
-        case DeviceType::PLAYBACK:
-            JAMI_DBG("setting %s for playback", devName.c_str());
-            preference.setPulseDevicePlayback(devName);
-            break;
+    case DeviceType::PLAYBACK:
+        JAMI_DBG("setting %s for playback", devName.c_str());
+        preference.setPulseDevicePlayback(devName);
+        break;
 
-        case DeviceType::CAPTURE:
-            JAMI_DBG("setting %s for capture", devName.c_str());
-            preference.setPulseDeviceRecord(devName);
-            break;
+    case DeviceType::CAPTURE:
+        JAMI_DBG("setting %s for capture", devName.c_str());
+        preference.setPulseDeviceRecord(devName);
+        break;
 
-        case DeviceType::RINGTONE:
-            JAMI_DBG("setting %s for ringer", devName.c_str());
-            preference.setPulseDeviceRingtone(devName);
-            break;
+    case DeviceType::RINGTONE:
+        JAMI_DBG("setting %s for ringer", devName.c_str());
+        preference.setPulseDeviceRingtone(devName);
+        break;
     }
 }
 
diff --git a/src/media/audio/pulseaudio/pulselayer.h b/src/media/audio/pulseaudio/pulselayer.h
index 12e5601a18d4a79e7b818836e3fe8ad737ccf7a2..30541bad631976c37bb78d3722fbfdc413b135b7 100644
--- a/src/media/audio/pulseaudio/pulselayer.h
+++ b/src/media/audio/pulseaudio/pulselayer.h
@@ -206,18 +206,6 @@ class PulseLayer : public AudioLayer {
          */
         std::vector<PaDeviceInfos> sourceList_ {};
 
-        /*
-         * Buffers used to avoid doing malloc/free in the audio thread
-         */
-        AudioBuffer micBuffer_;
-        AudioBuffer micResampleBuffer_;
-
-        AudioBuffer playbackBuffer_;
-        AudioBuffer playbackResampleBuffer_;
-
-        AudioBuffer ringtoneBuffer_;
-        AudioBuffer ringtoneResampleBuffer_;
-
         /** PulseAudio server defaults */
         AudioFormat defaultAudioFormat_ {AudioFormat::MONO()};
         std::string defaultSink_ {};
@@ -235,7 +223,6 @@ class PulseLayer : public AudioLayer {
         std::thread streamStarter_ {};
 
         AudioPreference &preference_;
-        std::shared_ptr<RingBuffer> mainRingBuffer_;
 
         pa_operation* subscribeOp_ {nullptr};
         friend class AudioLayerTest;
diff --git a/src/media/audio/ringbuffer.h b/src/media/audio/ringbuffer.h
index 46032e254336064c8ab4e7129ed6ae8fffd10950..938e9c5cd84a961ef57b6c2df868c3e533a0eddf 100644
--- a/src/media/audio/ringbuffer.h
+++ b/src/media/audio/ringbuffer.h
@@ -91,7 +91,7 @@ public:
      * @param buffer Data to copied
      * @param toCopy Number of bytes to copy
      */
-        void put(std::shared_ptr<AudioFrame>&& data);
+    void put(std::shared_ptr<AudioFrame>&& data);
 
     /**
      * To get how much samples are available in the buffer to read in
@@ -130,6 +130,10 @@ public:
         return putLength() == 0;
     }
 
+    inline void setFrameSize(int nb_samples) {
+        resizer_.setFrameSize(nb_samples);
+    }
+
     /**
      * Blocks until min_data_length samples of data is available, or until deadline has passed.
      *
diff --git a/src/media/audio/ringbufferpool.cpp b/src/media/audio/ringbufferpool.cpp
index a89e55972b79ded2a1fe543bee1e23d653da4e02..71062223e9ff3f5cd4e625020417ec7e1aa9a2e8 100644
--- a/src/media/audio/ringbufferpool.cpp
+++ b/src/media/audio/ringbufferpool.cpp
@@ -271,7 +271,7 @@ RingBufferPool::getData(const std::string& call_id)
     if (bindings->size() == 1)
         return (*bindings->cbegin())->get(call_id);
 
-    auto mixBuffer = std::make_unique<AudioFrame>(internalAudioFormat_);
+    auto mixBuffer = std::make_shared<AudioFrame>(internalAudioFormat_);
     for (const auto& rbuf : *bindings) {
         if (auto b = rbuf->get(call_id)) {
             mixBuffer->mix(*b);
@@ -326,7 +326,7 @@ RingBufferPool::getAvailableData(const std::string& call_id)
     if (availableFrames == 0)
         return {};
 
-    auto buf = std::make_unique<AudioFrame>(internalAudioFormat_);
+    auto buf = std::make_shared<AudioFrame>(internalAudioFormat_);
     for (const auto &rbuf : *bindings) {
         if (auto b = rbuf->get(call_id))
             buf->mix(*b);