diff --git a/contrib/src/portaudio/fetch_and_patch.bat b/contrib/src/portaudio/fetch_and_patch.bat
index b0568700006006c76ca74fdf4b21d47268de9007..108e029a7313484e01d56f44a591528246f6713d 100644
--- a/contrib/src/portaudio/fetch_and_patch.bat
+++ b/contrib/src/portaudio/fetch_and_patch.bat
@@ -20,6 +20,9 @@ cd %BUILD%\portaudio
 
 if "%1"=="uwp" (
     %APPLY_CMD% %SRC%\portaudio\pa-uwp.patch
+) else (
+    %APPLY_CMD% %SRC%\portaudio\pa-dsound-aecns.patch
+    %APPLY_CMD% %SRC%\portaudio\pa-dsound.patch
 )
 
 cd %SRC%
\ No newline at end of file
diff --git a/contrib/src/portaudio/pa-dsound-aecns.patch b/contrib/src/portaudio/pa-dsound-aecns.patch
new file mode 100644
index 0000000000000000000000000000000000000000..80d0bcd18d595948512d04c517961abde8472788
--- /dev/null
+++ b/contrib/src/portaudio/pa-dsound-aecns.patch
@@ -0,0 +1,65 @@
+--- a/src/hostapi/dsound/pa_win_ds.c
++++ b/src/hostapi/dsound/pa_win_ds.c
+@@ -1520,12 +1520,33 @@ static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
+                 hostInputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostInputSampleFormat ),
+                 nFrameRate, inputChannelMask );
+ 
++    // aec and noise suppression
++    DSCEFFECTDESC dscfx[2];
++    ZeroMemory(&dscfx[0], sizeof(DSCEFFECTDESC));
++    dscfx[0].dwSize = sizeof(DSCEFFECTDESC);
++    dscfx[0].dwFlags = DSCFX_LOCSOFTWARE;
++    dscfx[0].guidDSCFXClass = GUID_DSCFX_CLASS_AEC;
++    dscfx[0].guidDSCFXInstance = GUID_DSCFX_MS_AEC;
++    dscfx[0].dwReserved1 = 0;
++    dscfx[0].dwReserved2 = 0;
++
++    ZeroMemory(&dscfx[1], sizeof(DSCEFFECTDESC));
++    dscfx[1].dwSize = sizeof(DSCEFFECTDESC);
++    dscfx[1].dwFlags = DSCFX_LOCSOFTWARE;
++    dscfx[1].guidDSCFXClass = GUID_DSCFX_CLASS_NS;
++    dscfx[1].guidDSCFXInstance = GUID_DSCFX_MS_NS;
++    dscfx[1].dwReserved1 = 0;
++    dscfx[1].dwReserved2 = 0;
++
+     ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
+     captureDesc.dwSize = sizeof(DSCBUFFERDESC);
+-    captureDesc.dwFlags = 0;
+     captureDesc.dwBufferBytes = bytesPerInputBuffer;
+     captureDesc.lpwfxFormat = (WAVEFORMATEX*)&captureWaveFormat;
+ 
++    captureDesc.dwFlags = DSCBCAPS_CTRLFX;
++    captureDesc.dwFXCount = 2;
++    captureDesc.lpDSCFXDesc = dscfx;
++
+     // render buffer description
+ 
+     PaWin_InitializeWaveFormatExtensible( &renderWaveFormat, outputChannelCount, 
+@@ -1551,6 +1572,24 @@ static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
+             NULL /* pUnkOuter must be NULL */ 
+         );
+ 
++    if (hr != DS_OK) {
++        PA_DEBUG(("DirectSoundFullDuplexCreate(with AEC/NS) failed. hr=%d\n", hr));
++        // try removing AEC/NS
++        captureDesc.dwFlags = 0;
++        captureDesc.dwFXCount = 0;
++        captureDesc.lpDSCFXDesc = NULL;
++        hr = paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8(
++            inputDevice->lpGUID, outputDevice->lpGUID,
++            &captureDesc, &secondaryRenderDesc,
++            GetDesktopWindow(), /* see InitOutputBuffer() for a discussion of whether this is a good idea */
++            DSSCL_EXCLUSIVE,
++            &stream->pDirectSoundFullDuplex8,
++            &pCaptureBuffer8,
++            &pRenderBuffer8,
++            NULL /* pUnkOuter must be NULL */
++        );
++    }
++
+     if( hr == DS_OK )
+     {
+         PA_DEBUG(("DirectSoundFullDuplexCreate succeeded!\n"));
+-- 
+2.19.0.windows.1
+
diff --git a/contrib/src/portaudio/pa-dsound.patch b/contrib/src/portaudio/pa-dsound.patch
new file mode 100644
index 0000000000000000000000000000000000000000..fdb224e464d3f04145e40678d978b524c734a925
--- /dev/null
+++ b/contrib/src/portaudio/pa-dsound.patch
@@ -0,0 +1,23 @@
+--- a/MSVC/portaudio.vcxproj
++++ b/MSVC/portaudio.vcxproj
+@@ -109,7 +109,7 @@
+       <IntrinsicFunctions>true</IntrinsicFunctions>
+       <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+       <AdditionalIncludeDirectories>..\src\common;..\include;.\;..\src\os\win;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+-      <PreprocessorDefinitions>PA_WDMKS_NO_KSGUID_LIB;WIN32;NDEBUG;_USRDLL;PA_ENABLE_DEBUG_OUTPUT;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_ASIO=0;PA_USE_DS=0;PA_USE_WMME=1;PA_USE_WASAPI=1;PA_USE_WDMKS=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++      <PreprocessorDefinitions>PA_ENABLE_DEBUG_OUTPUT;PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE;PA_WDMKS_NO_KSGUID_LIB;WIN32;NDEBUG;_USRDLL;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_ASIO=0;PA_USE_DS=1;PA_USE_WMME=0;PA_USE_WASAPI=0;PA_USE_WDMKS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+       <StringPooling>true</StringPooling>
+       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+       <FunctionLevelLinking>true</FunctionLevelLinking>
+@@ -161,7 +161,7 @@ xcopy /S /Y $(ProjectDir)..\include\*.h "$(OutDir)"include</Command>
+       <IntrinsicFunctions>true</IntrinsicFunctions>
+       <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+       <AdditionalIncludeDirectories>..\src\common;..\include;.\;..\src\os\win;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+-      <PreprocessorDefinitions>PA_WDMKS_NO_KSGUID_LIB;_WIN64;NDEBUG;_USRDLL;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_ASIO=0;PA_USE_DS=0;PA_USE_WMME=1;PA_USE_WASAPI=1;PA_USE_WDMKS=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
++      <PreprocessorDefinitions>PA_ENABLE_DEBUG_OUTPUT;PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE;PA_WDMKS_NO_KSGUID_LIB;_WIN64;NDEBUG;_USRDLL;_CRT_SECURE_NO_DEPRECATE;PAWIN_USE_WDMKS_DEVICE_INFO;PA_USE_ASIO=0;PA_USE_DS=1;PA_USE_WMME=0;PA_USE_WASAPI=0;PA_USE_WDMKS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+       <StringPooling>true</StringPooling>
+       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+       <FunctionLevelLinking>true</FunctionLevelLinking>
+-- 
+2.19.0.windows.1
+
diff --git a/src/media/audio/portaudio/portaudiolayer.cpp b/src/media/audio/portaudio/portaudiolayer.cpp
index 3a1e623fe8e302dde3bb4a188b320c10421601c3..cc5ebc28293bdf707301c66bd9dc29b9953eed1f 100644
--- a/src/media/audio/portaudio/portaudiolayer.cpp
+++ b/src/media/audio/portaudio/portaudiolayer.cpp
@@ -3,6 +3,7 @@
  *
  *  Author: Edric Ladent-Milaret <edric.ladent-milaret@savoirfairelinux.com>
  *  Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
+ *  Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -33,7 +34,7 @@
 
 namespace ring {
 
-enum Direction {Input=0, Output=1, End=2};
+enum Direction { Input = 0, Output = 1, IO = 2, End = 3 };
 
 struct PortAudioLayer::PortAudioLayerImpl
 {
@@ -69,6 +70,13 @@ struct PortAudioLayer::PortAudioLayerImpl
                         unsigned long framesPerBuffer,
                         const PaStreamCallbackTimeInfo* timeInfo,
                         PaStreamCallbackFlags statusFlags);
+
+    int paIOCallback(PortAudioLayer& parent,
+                     const AudioSample* inputBuffer,
+                     AudioSample* outputBuffer,
+                     unsigned long framesPerBuffer,
+                     const PaStreamCallbackTimeInfo* timeInfo,
+                     PaStreamCallbackFlags statusFlags);
 };
 
 //##################################################################################################
@@ -162,6 +170,9 @@ PortAudioLayer::stopStream()
     RING_DBG("Stop PortAudio Streams");
 
     for (auto& st_ptr : pimpl_->streams_) {
+        if (!st_ptr)
+            continue;
+
         auto err = Pa_StopStream(st_ptr);
         if (err != paNoError)
             RING_ERR("Pa_StopStream error : %s", Pa_GetErrorText(err));
@@ -255,59 +266,6 @@ PortAudioLayer::PortAudioLayerImpl::getDeviceByType(bool playback) const
     return ret;
 }
 
-int
-PortAudioLayer::PortAudioLayerImpl::paOutputCallback(PortAudioLayer& parent,
-                                                     const AudioSample* inputBuffer,
-                                                     AudioSample* outputBuffer,
-                                                     unsigned long framesPerBuffer,
-                                                     const PaStreamCallbackTimeInfo* timeInfo,
-                                                     PaStreamCallbackFlags statusFlags)
-{
-    // unused arguments
-    (void) inputBuffer;
-    (void) timeInfo;
-    (void) statusFlags;
-
-    auto toPlay = parent.getPlayback(parent.audioFormat_, framesPerBuffer);
-    if (!toPlay) {
-        std::fill_n(outputBuffer, framesPerBuffer * parent.audioFormat_.nb_channels, 0);
-        return paContinue;
-    }
-
-    auto nFrames = toPlay->pointer()->nb_samples * toPlay->pointer()->channels;
-    std::copy_n((AudioSample*)toPlay->pointer()->extended_data[0], nFrames, outputBuffer);
-
-    return paContinue;
-}
-
-int
-PortAudioLayer::PortAudioLayerImpl::paInputCallback(PortAudioLayer& parent,
-                                                    const AudioSample* inputBuffer,
-                                                    AudioSample* outputBuffer,
-                                                    unsigned long framesPerBuffer,
-                                                    const PaStreamCallbackTimeInfo* timeInfo,
-                                                    PaStreamCallbackFlags statusFlags)
-{
-    // unused arguments
-    (void) outputBuffer;
-    (void) timeInfo;
-    (void) statusFlags;
-
-    if (framesPerBuffer == 0) {
-        RING_WARN("No frames for input.");
-        return paContinue;
-    }
-
-    auto inBuff = std::make_unique<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));
-    return paContinue;
-}
-
 void
 PortAudioLayer::PortAudioLayerImpl::init(PortAudioLayer& parent)
 {
@@ -382,57 +340,119 @@ openStreamDevice(PaStream** stream,
         RING_ERR("PortAudioLayer error : %s", Pa_GetErrorText(err));
 }
 
+static void
+openFullDuplexStream(PaStream** stream,
+    PaDeviceIndex inputDeviceIndex, PaDeviceIndex ouputDeviceIndex,
+    PaStreamCallback* callback, void* user_data)
+{
+    auto input_device_info = Pa_GetDeviceInfo(inputDeviceIndex);
+    auto output_device_info = Pa_GetDeviceInfo(ouputDeviceIndex);
+
+    PaStreamParameters inputParams;
+    inputParams.device = inputDeviceIndex;
+    inputParams.channelCount = input_device_info->maxInputChannels;
+    inputParams.sampleFormat = paInt16;
+    inputParams.suggestedLatency = input_device_info->defaultLowInputLatency;
+    inputParams.hostApiSpecificStreamInfo = nullptr;
+
+    PaStreamParameters outputParams;
+    outputParams.device = ouputDeviceIndex;
+    outputParams.channelCount = output_device_info->maxOutputChannels;
+    outputParams.sampleFormat = paInt16;
+    outputParams.suggestedLatency = output_device_info->defaultLowOutputLatency;
+    outputParams.hostApiSpecificStreamInfo = nullptr;
+
+    auto err = Pa_OpenStream(
+        stream,
+        &inputParams,
+        &outputParams,
+        std::min(input_device_info->defaultSampleRate, input_device_info->defaultSampleRate),
+        paFramesPerBufferUnspecified,
+        paNoFlag,
+        callback,
+        user_data);
+
+    if (err != paNoError)
+        RING_ERR("PortAudioLayer error : %s", Pa_GetErrorText(err));
+}
+
 void
 PortAudioLayer::PortAudioLayerImpl::initStream(PortAudioLayer& parent)
 {
     parent.dcblocker_.reset();
 
-    RING_DBG("Open PortAudio Output Stream");
-    if (indexOut_ != paNoDevice) {
-        openStreamDevice(&streams_[Direction::Output],
-                         indexOut_,
-                         Direction::Output,
-                         [](const void* inputBuffer,
-                            void* outputBuffer,
-                            unsigned long framesPerBuffer,
-                            const PaStreamCallbackTimeInfo* timeInfo,
-                            PaStreamCallbackFlags statusFlags,
-                            void* userData) -> int {
-                             auto layer = static_cast<PortAudioLayer*>(userData);
-                             return layer->pimpl_->paOutputCallback(*layer,
-                                                                    static_cast<const AudioSample*>(inputBuffer),
-                                                                    static_cast<AudioSample*>(outputBuffer),
-                                                                    framesPerBuffer,
-                                                                    timeInfo,
-                                                                    statusFlags);
-                         },
-                         &parent);
+    auto apiIndex = Pa_GetDefaultHostApi();
+    auto apiInfo = Pa_GetHostApiInfo(apiIndex);
+    RING_DBG() << "Initializing Portaudio streams using: " << apiInfo->name;
+
+    RING_DBG("Open PortAudio Full-duplex input/output stream");
+    if (indexOut_ != paNoDevice && indexIn_ != paNoDevice) {
+        openFullDuplexStream(&streams_[Direction::IO],
+            indexIn_,
+            indexOut_,
+            [](const void* inputBuffer,
+                void* outputBuffer,
+                unsigned long framesPerBuffer,
+                const PaStreamCallbackTimeInfo* timeInfo,
+                PaStreamCallbackFlags statusFlags,
+                void* userData) -> int {
+                    auto layer = static_cast<PortAudioLayer*>(userData);
+                    return layer->pimpl_->paIOCallback(*layer,
+                        static_cast<const AudioSample*>(inputBuffer),
+                        static_cast<AudioSample*>(outputBuffer),
+                        framesPerBuffer,
+                        timeInfo,
+                        statusFlags);
+            },
+            &parent);
     } else {
-        RING_ERR("Error: No valid output device. There will be no sound.");
-    }
+        RING_DBG("Open PortAudio Output Stream");
+        if (indexOut_ != paNoDevice) {
+            openStreamDevice(&streams_[Direction::Output],
+                indexOut_,
+                Direction::Output,
+                [](const void* inputBuffer,
+                    void* outputBuffer,
+                    unsigned long framesPerBuffer,
+                    const PaStreamCallbackTimeInfo* timeInfo,
+                    PaStreamCallbackFlags statusFlags,
+                    void* userData) -> int {
+                        auto layer = static_cast<PortAudioLayer*>(userData);
+                        return layer->pimpl_->paOutputCallback(*layer,
+                            static_cast<const AudioSample*>(inputBuffer),
+                            static_cast<AudioSample*>(outputBuffer),
+                            framesPerBuffer,
+                            timeInfo,
+                            statusFlags);
+                },
+                &parent);
+        } else {
+            RING_ERR("Error: No valid output device. There will be no sound.");
+        }
 
-    RING_DBG("Open PortAudio Input Stream");
-    if (indexIn_ != paNoDevice) {
-        openStreamDevice(&streams_[Direction::Input],
-                         indexIn_,
-                         Direction::Input,
-                         [](const void* inputBuffer,
-                            void* outputBuffer,
-                            unsigned long framesPerBuffer,
-                            const PaStreamCallbackTimeInfo* timeInfo,
-                            PaStreamCallbackFlags statusFlags,
-                            void* userData) -> int {
-                             auto layer = static_cast<PortAudioLayer*>(userData);
-                             return layer->pimpl_->paInputCallback(*layer,
-                                                                   static_cast<const AudioSample*>(inputBuffer),
-                                                                   static_cast<AudioSample*>(outputBuffer),
-                                                                   framesPerBuffer,
-                                                                   timeInfo,
-                                                                   statusFlags);
-                         },
-                         &parent);
-    } else {
-        RING_ERR("Error: No valid input device. There will be no mic.");
+        RING_DBG("Open PortAudio Input Stream");
+        if (indexIn_ != paNoDevice) {
+            openStreamDevice(&streams_[Direction::Input],
+                indexIn_,
+                Direction::Input,
+                [](const void* inputBuffer,
+                    void* outputBuffer,
+                    unsigned long framesPerBuffer,
+                    const PaStreamCallbackTimeInfo* timeInfo,
+                    PaStreamCallbackFlags statusFlags,
+                    void* userData) -> int {
+                        auto layer = static_cast<PortAudioLayer*>(userData);
+                        return layer->pimpl_->paInputCallback(*layer,
+                            static_cast<const AudioSample*>(inputBuffer),
+                            static_cast<AudioSample*>(outputBuffer),
+                            framesPerBuffer,
+                            timeInfo,
+                            statusFlags);
+                },
+                &parent);
+        } else {
+            RING_ERR("Error: No valid input device. There will be no mic.");
+        }
     }
 
     RING_DBG("Start PortAudio Streams");
@@ -445,4 +465,70 @@ PortAudioLayer::PortAudioLayerImpl::initStream(PortAudioLayer& parent)
     }
 }
 
+int
+PortAudioLayer::PortAudioLayerImpl::paOutputCallback(PortAudioLayer& parent,
+    const AudioSample* inputBuffer,
+    AudioSample* outputBuffer,
+    unsigned long framesPerBuffer,
+    const PaStreamCallbackTimeInfo* timeInfo,
+    PaStreamCallbackFlags statusFlags)
+{
+    // unused arguments
+    (void)inputBuffer;
+    (void)timeInfo;
+    (void)statusFlags;
+
+    auto toPlay = parent.getPlayback(parent.audioFormat_, framesPerBuffer);
+    if (!toPlay) {
+        std::fill_n(outputBuffer, framesPerBuffer * parent.audioFormat_.nb_channels, 0);
+        return paContinue;
+    }
+
+    auto nFrames = toPlay->pointer()->nb_samples * toPlay->pointer()->channels;
+    std::copy_n((AudioSample*)toPlay->pointer()->extended_data[0], nFrames, outputBuffer);
+
+    return paContinue;
+}
+
+int
+PortAudioLayer::PortAudioLayerImpl::paInputCallback(PortAudioLayer& parent,
+    const AudioSample* inputBuffer,
+    AudioSample* outputBuffer,
+    unsigned long framesPerBuffer,
+    const PaStreamCallbackTimeInfo* timeInfo,
+    PaStreamCallbackFlags statusFlags)
+{
+    // unused arguments
+    (void)outputBuffer;
+    (void)timeInfo;
+    (void)statusFlags;
+
+    if (framesPerBuffer == 0) {
+        RING_WARN("No frames for input.");
+        return paContinue;
+    }
+
+    auto inBuff = std::make_unique<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));
+    return paContinue;
+}
+
+int
+PortAudioLayer::PortAudioLayerImpl::paIOCallback(PortAudioLayer& parent,
+    const AudioSample* inputBuffer,
+    AudioSample* outputBuffer,
+    unsigned long framesPerBuffer,
+    const PaStreamCallbackTimeInfo* timeInfo,
+    PaStreamCallbackFlags statusFlags)
+{
+    paInputCallback(parent, inputBuffer, nullptr, framesPerBuffer, timeInfo, statusFlags);
+    paOutputCallback(parent, nullptr, outputBuffer, framesPerBuffer, timeInfo, statusFlags);
+    return paContinue;
+}
+
 } // namespace ring