Index: /trunk/src/VBox/Devices/Audio/DrvAudio.h
===================================================================
--- /trunk/src/VBox/Devices/Audio/DrvAudio.h	(revision 54433)
+++ /trunk/src/VBox/Devices/Audio/DrvAudio.h	(revision 54433)
@@ -0,0 +1,204 @@
+/* $Id$ */
+/** @file
+ * Intermediate audio driver header.
+ */
+
+/*
+ * Copyright (C) 2006-2014 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ * --------------------------------------------------------------------
+ *
+ * This code is based on: audio.h
+ *
+ * QEMU Audio subsystem header
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef DRV_AUDIO_H
+#define DRV_AUDIO_H
+
+#include <limits.h>
+
+#include <iprt/circbuf.h>
+
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdm.h>
+#include <VBox/vmm/pdmaudioifs.h>
+
+typedef enum
+{
+    AUD_OPT_INT,
+    AUD_OPT_FMT,
+    AUD_OPT_STR,
+    AUD_OPT_BOOL
+} audio_option_tag_e;
+
+typedef struct audio_option
+{
+    const char *name;
+    audio_option_tag_e tag;
+    void *valp;
+    const char *descr;
+    int *overridenp;
+    int overriden;
+} audio_option;
+
+/**
+ * Audio driver instance data.
+ *
+ * @implements PDMIAUDIOCONNECTOR
+ */
+typedef struct DRVAUDIO
+{
+    /** Input/output processing thread. */
+    RTTHREAD                hThread;
+    /** Event for input/ouput processing. */
+    RTSEMEVENT              hEvent;
+    /** Shutdown indicator. */
+    bool                    fTerminate;
+    /** The audio interface presented to the device/driver above us. */
+    PDMIAUDIOCONNECTOR      IAudioConnector;
+    /** Pointer to the driver instance. */
+    PPDMDRVINS              pDrvIns;
+    /** Pointer to audio driver below us. */
+    PPDMIHOSTAUDIO          pHostDrvAudio;
+    RTLISTANCHOR            lstHstStrmIn;
+    RTLISTANCHOR            lstHstStrmOut;
+    /** Max. number of free input streams. */
+    uint8_t                 cFreeInputStreams;
+    /** Max. number of free output streams. */
+    uint8_t                 cFreeOutputStreams;
+    /** Audio configuration settings retrieved
+     *  from the backend. */
+    PDMAUDIOBACKENDCFG      BackendCfg;
+
+} DRVAUDIO, *PDRVAUDIO;
+
+/** Makes a PDRVBLOCK out of a PPDMIBLOCK. */
+#define PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface) \
+    ( (PDRVAUDIO)((uintptr_t)pInterface - RT_OFFSETOF(DRVAUDIO, IAudioConnector)) )
+
+//const char *drvAudioHlpFormatToString(PDMAUDIOFMT fmt);
+const char *drvAudioRecSourceToString(PDMAUDIORECSOURCE enmRecSource);
+PDMAUDIOFMT drvAudioHlpStringToFormat(const char *pszFormat);
+
+bool drvAudioPCMPropsAreEqual(PPDMPCMPROPS info, PPDMAUDIOSTREAMCFG pCfg);
+int drvAudioStreamCfgToProps(PPDMAUDIOSTREAMCFG pCfg, PPDMPCMPROPS pProps);
+void drvAudioStreamCfgPrint(PPDMAUDIOSTREAMCFG pCfg);
+
+/* AUDIO IN function declarations. */
+void drvAudioHlpPcmSwFreeResourcesIn(PPDMAUDIOGSTSTRMIN pGstStrmIn);
+void drvAudioGstInFreeRes(PPDMAUDIOGSTSTRMIN pGstStrmIn);
+void drvAudioGstInRemove(PPDMAUDIOGSTSTRMIN pGstStrmIn);
+uint32_t drvAudioHstInFindMinCaptured(PPDMAUDIOHSTSTRMIN pHstStrmIn);
+void drvAudioHstInFreeRes(PPDMAUDIOHSTSTRMIN pHstStrmIn);
+uint32_t drvAudioHstInGetFree(PPDMAUDIOHSTSTRMIN pHstStrmIn);
+uint32_t drvAudioHstInGetLive(PPDMAUDIOHSTSTRMIN pHstStrmIn);
+void drvAudioGstInRemove(PPDMAUDIOGSTSTRMIN pGstStrmIn);
+int  drvAudioGstInInit(PPDMAUDIOGSTSTRMIN pGstStrmIn, PPDMAUDIOHSTSTRMIN pHstStrmIn, const char *pszName, PPDMAUDIOSTREAMCFG pCfg);
+
+PPDMAUDIOHSTSTRMIN drvAudioFindNextHstIn(PDRVAUDIO pDrvAudio, PPDMAUDIOHSTSTRMIN pHstStrmIn);
+PPDMAUDIOHSTSTRMIN drvAudioFindNextEnabledHstIn(PDRVAUDIO pDrvAudio, PPDMAUDIOHSTSTRMIN pHstStrmIn);
+PPDMAUDIOHSTSTRMIN drvAudioFindNextEqHstIn(PDRVAUDIO pDrvAudio, PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg);
+
+/* AUDIO OUT function declarations. */
+int  drvAudioGstOutAlloc(PPDMAUDIOGSTSTRMOUT pGstStrmOut);
+void drvAudioGstOutFreeRes(PPDMAUDIOGSTSTRMOUT pGstStrmOut);
+void drvAudioHstOutFreeRes(PPDMAUDIOHSTSTRMOUT pHstStrmOut);
+int  drvAudioDestroyGstOut(PDRVAUDIO pDrvAudio, PPDMAUDIOGSTSTRMOUT pGstStrmOut);
+void drvAudioDestroyHstOut(PDRVAUDIO pDrvAudio, PDMAUDIOHSTSTRMOUT pHstStrmOut);
+int  drvAudioGstOutInit(PPDMAUDIOGSTSTRMOUT pGstStrmOut, PPDMAUDIOHSTSTRMOUT pHstStrmOut, const char *pszName, PPDMAUDIOSTREAMCFG pCfg);
+
+PPDMAUDIOHSTSTRMOUT drvAudioFindAnyHstOut(PDRVAUDIO pDrvAudio, PPDMAUDIOHSTSTRMOUT pHstStrmOut);
+PPDMAUDIOHSTSTRMOUT drvAudioHstFindAnyEnabledOut(PDRVAUDIO pDrvAudio, PPDMAUDIOHSTSTRMOUT pHstStrmOut);
+PPDMAUDIOHSTSTRMOUT drvAudioFindSpecificOut(PDRVAUDIO pDrvAudio, PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg);
+int drvAudioAllocHstOut(PDRVAUDIO pDrvAudio, const char *pszName, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOHSTSTRMOUT *ppHstStrmOut);
+int drvAudioHlpPcmHwAddOut(PDRVAUDIO pDrvAudio, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOHSTSTRMOUT *ppHstStrmOut);
+int drvAudioHlpPcmCreateVoicePairOut(PDRVAUDIO pDrvAudio, const char *pszName, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMOUT *ppGstStrmOut);
+
+/* Common functions between DrvAudio and backends (host audio drivers). */
+int  drvAudioAttachCapture(PDRVAUDIO pDrvAudio, PPDMAUDIOHSTSTRMOUT pHstStrmOut);
+void drvAudioDetachCapture(PPDMAUDIOHSTSTRMOUT pHstStrmOut);
+uint32_t drvAudioHstOutSamplesLive(PPDMAUDIOHSTSTRMOUT pHstStrmOut, uint32_t *pcStreamsLive);
+
+void audio_pcm_info_clear_buf(PPDMPCMPROPS pPCMInfo, void *pvBuf, int len);
+
+/* Externals. */
+extern volume_t nominal_volume;
+extern volume_t pcm_in_volume;
+
+typedef struct fixed_settings
+{
+    int enabled;
+    int cStreams;
+    int greedy;
+    PDMAUDIOSTREAMCFG settings;
+} fixed_settings;
+
+static struct {
+    struct fixed_settings fixed_out;
+    struct fixed_settings fixed_in;
+    union {
+        int hz;
+        int64_t ticks;
+    } period;
+    int plive;
+} conf = {
+
+    /* Fixed output settings. */
+    {                           /* DAC fixed settings */
+        1,                      /* enabled */
+        1,                      /* cStreams */
+        1,                      /* greedy */
+        {
+            44100,              /* freq */
+            2,                  /* nchannels */
+            AUD_FMT_S16,        /* fmt */
+            PDMAUDIOHOSTENDIANESS
+        }
+    },
+
+    /* Fixed input settings. */
+    {                           /* ADC fixed settings */
+        1,                      /* enabled */
+        1,                      /* cStreams */
+        1,                      /* greedy */
+        {
+            44100,              /* freq */
+            2,                  /* nchannels */
+            AUD_FMT_S16,        /* fmt */
+            PDMAUDIOHOSTENDIANESS
+        }
+    },
+
+    { 200 },                    /* frequency (in Hz) */
+    0,                          /* plive */ /** @todo Disable pending live? */
+};
+#endif /* DRV_AUDIO_H */
+
Index: /trunk/src/VBox/Devices/Audio/DrvHostALSAAudio.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DrvHostALSAAudio.cpp	(revision 54433)
+++ /trunk/src/VBox/Devices/Audio/DrvHostALSAAudio.cpp	(revision 54433)
@@ -0,0 +1,1392 @@
+/* $Id$ */
+/** @file
+ * VBox audio devices: ALSA audio driver.
+ */
+
+/*
+ * Copyright (C) 2006-2015 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ * --------------------------------------------------------------------
+ *
+ * This code is based on: alsaaudio.c
+ *
+ * QEMU ALSA audio driver
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+
+#include <iprt/alloc.h>
+#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
+#include <VBox/vmm/pdmaudioifs.h>
+
+RT_C_DECLS_BEGIN
+ #include "alsa_stubs.h"
+ #include "alsa_mangling.h"
+RT_C_DECLS_END
+
+#include <alsa/asoundlib.h>
+
+#include "DrvAudio.h"
+#include "AudioMixBuffer.h"
+
+#include "VBoxDD.h"
+#include "vl_vbox.h"
+
+
+
+#ifdef LOG_GROUP
+# undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_DEV_AUDIO
+#include <VBox/log.h>
+
+typedef struct ALSAAUDIOSTREAMIN
+{
+    PDMAUDIOHSTSTRMIN   pStreamIn;
+    snd_pcm_t          *phPCM;
+    void               *pvBuf;
+    size_t              cbBuf;
+} ALSAAUDIOSTREAMIN, *PALSAAUDIOSTREAMIN;
+
+typedef struct ALSAAUDIOSTREAMOUT
+{
+    PDMAUDIOHSTSTRMOUT  pStreamOut;
+    snd_pcm_t          *phPCM;
+    void               *pvBuf;
+    size_t              cbBuf;
+} ALSAAUDIOSTREAMOUT, *PALSAAUDIOSTREAMOUT;
+
+/* latency = period_size * periods / (rate * bytes_per_frame) */
+
+typedef struct ALSAAUDIOCFG
+{
+    int size_in_usec_in;
+    int size_in_usec_out;
+    const char *pcm_name_in;
+    const char *pcm_name_out;
+    unsigned int buffer_size_in;
+    unsigned int period_size_in;
+    unsigned int buffer_size_out;
+    unsigned int period_size_out;
+    unsigned int threshold;
+
+    int buffer_size_in_overriden;
+    int period_size_in_overriden;
+
+    int buffer_size_out_overriden;
+    int period_size_out_overriden;
+
+} ALSAAUDIOCFG, *PALSAAUDIOCFG;
+
+static int drvHostALSAAudioRecover(snd_pcm_t *phPCM);
+
+static ALSAAUDIOCFG s_ALSAConf =
+{
+#ifdef HIGH_LATENCY
+    1,
+    1,
+#else
+    0,
+    0,
+#endif
+    "default",
+    "default",
+#ifdef HIGH_LATENCY
+    400000,
+    400000 / 4,
+    400000,
+    400000 / 4,
+#else
+# define DEFAULT_BUFFER_SIZE 1024
+# define DEFAULT_PERIOD_SIZE 256
+    DEFAULT_BUFFER_SIZE * 4,
+    DEFAULT_PERIOD_SIZE * 4,
+    DEFAULT_BUFFER_SIZE,
+    DEFAULT_PERIOD_SIZE,
+#endif
+    0,
+    0,
+    0,
+    0,
+    0
+};
+
+/**
+ * Host Alsa audio driver instance data.
+ * @implements PDMIAUDIOCONNECTOR
+ */
+typedef struct DRVHOSTALSAAUDIO
+{
+    /** Pointer to the driver instance structure. */
+    PPDMDRVINS         pDrvIns;
+    /** Pointer to host audio interface. */
+    PDMIHOSTAUDIO      IHostAudio;
+    /** Error count for not flooding the release log.
+     *  UINT32_MAX for unlimited logging. */
+    uint32_t           cLogErrors;
+} DRVHOSTALSAAUDIO, *PDRVHOSTALSAAUDIO;
+
+typedef struct ALSAAUDIOSTREAMCFG
+{
+    unsigned int freq;
+    snd_pcm_format_t fmt;
+    int nchannels;
+    unsigned long buffer_size;
+    unsigned long period_size;
+    snd_pcm_uframes_t samples;
+} ALSAAUDIOSTREAMCFG, *PALSAAUDIOSTREAMCFG;
+
+static int drvHostALSAAudioClose(snd_pcm_t **pphPCM)
+{
+    if (!pphPCM || !*pphPCM)
+        return VINF_SUCCESS;
+
+    int rc;
+    int rc2 = snd_pcm_close(*pphPCM);
+    if (rc2)
+    {
+        LogRel(("ALSA: Closing PCM descriptor failed: %s\n",
+                 snd_strerror(rc2)));
+        rc = VERR_GENERAL_FAILURE; /** @todo */
+    }
+    else
+    {
+        *pphPCM = NULL;
+        rc = VINF_SUCCESS;
+    }
+
+    return rc;
+}
+
+static snd_pcm_format_t drvHostALSAAudioFmtToALSA(PDMAUDIOFMT fmt)
+{
+    switch (fmt)
+    {
+        case AUD_FMT_S8:
+            return SND_PCM_FORMAT_S8;
+
+        case AUD_FMT_U8:
+            return SND_PCM_FORMAT_U8;
+
+        case AUD_FMT_S16:
+            return SND_PCM_FORMAT_S16_LE;
+
+        case AUD_FMT_U16:
+            return SND_PCM_FORMAT_U16_LE;
+
+        case AUD_FMT_S32:
+            return SND_PCM_FORMAT_S32_LE;
+
+        case AUD_FMT_U32:
+            return SND_PCM_FORMAT_U32_LE;
+
+        default:
+            break;
+    }
+
+    AssertMsgFailed(("Format %ld not supported\n", fmt));
+    return SND_PCM_FORMAT_U8;
+}
+
+static int drvHostALSAAudioALSAToFmt(snd_pcm_format_t fmt,
+                                     PDMAUDIOFMT *pFmt, PDMAUDIOENDIANESS *pEndianness)
+{
+    AssertPtrReturn(pFmt, VERR_INVALID_POINTER);
+    /* pEndianness is optional. */
+
+    switch (fmt)
+    {
+        case SND_PCM_FORMAT_S8:
+            *pFmt = AUD_FMT_S8;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANESS_LITTLE;
+            break;
+
+        case SND_PCM_FORMAT_U8:
+            *pFmt = AUD_FMT_U8;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANESS_LITTLE;
+            break;
+
+        case SND_PCM_FORMAT_S16_LE:
+            *pFmt = AUD_FMT_S16;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANESS_LITTLE;
+            break;
+
+        case SND_PCM_FORMAT_U16_LE:
+            *pFmt = AUD_FMT_U16;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANESS_LITTLE;
+            break;
+
+        case SND_PCM_FORMAT_S16_BE:
+            *pFmt = AUD_FMT_S16;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANESS_BIG;
+            break;
+
+        case SND_PCM_FORMAT_U16_BE:
+            *pFmt = AUD_FMT_U16;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANESS_BIG;
+            break;
+
+        case SND_PCM_FORMAT_S32_LE:
+            *pFmt = AUD_FMT_S32;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANESS_LITTLE;
+            break;
+
+        case SND_PCM_FORMAT_U32_LE:
+            *pFmt = AUD_FMT_U32;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANESS_LITTLE;
+            break;
+
+        case SND_PCM_FORMAT_S32_BE:
+            *pFmt = AUD_FMT_S32;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANESS_BIG;
+            break;
+
+        case SND_PCM_FORMAT_U32_BE:
+            *pFmt = AUD_FMT_U32;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANESS_BIG;
+            break;
+
+        default:
+            AssertMsgFailed(("Format %ld not supported\n", fmt));
+            return VERR_NOT_SUPPORTED;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static int drvHostALSAAudioALSAGetShift(snd_pcm_format_t fmt, unsigned *puShift)
+{
+    AssertPtrReturn(puShift, VERR_INVALID_POINTER);
+
+    switch (fmt)
+    {
+        case SND_PCM_FORMAT_S8:
+        case SND_PCM_FORMAT_U8:
+            *puShift = 0;
+            break;
+
+        case SND_PCM_FORMAT_S16_LE:
+        case SND_PCM_FORMAT_U16_LE:
+        case SND_PCM_FORMAT_S16_BE:
+        case SND_PCM_FORMAT_U16_BE:
+            *puShift = 1;
+            break;
+
+        case SND_PCM_FORMAT_S32_LE:
+        case SND_PCM_FORMAT_U32_LE:
+        case SND_PCM_FORMAT_S32_BE:
+        case SND_PCM_FORMAT_U32_BE:
+            *puShift = 2;
+            break;
+
+        default:
+            AssertMsgFailed(("Format %ld not supported\n", fmt));
+            return VERR_NOT_SUPPORTED;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static int drvHostALSAAudioSetThreshold(snd_pcm_t *phPCM,
+                                        snd_pcm_uframes_t threshold)
+{
+    snd_pcm_sw_params_t *pSWParms = NULL;
+    snd_pcm_sw_params_alloca(&pSWParms);
+    if (!pSWParms)
+        return VERR_NO_MEMORY;
+
+    int rc;
+    do
+    {
+        int err = snd_pcm_sw_params_current(phPCM, pSWParms);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to get current software parameters for threshold: %s\n",
+                    snd_strerror(err)));
+            rc = VERR_ACCESS_DENIED;
+            break;
+        }
+
+        err = snd_pcm_sw_params_set_start_threshold(phPCM, pSWParms, threshold);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to set software threshold to %ld: %s\n",
+                    threshold, snd_strerror(err)));
+            rc = VERR_ACCESS_DENIED;
+            break;
+        }
+
+        err = snd_pcm_sw_params(phPCM, pSWParms);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to set new software parameters for threshold: %s\n",
+                    snd_strerror(err)));
+            rc = VERR_ACCESS_DENIED;
+            break;
+        }
+
+        LogFlowFunc(("Setting threshold to %RU32\n", threshold));
+        rc = VINF_SUCCESS;
+    }
+    while (0);
+
+    return rc;
+}
+
+static int drvHostALSAAudioOpen(bool fIn,
+                                PALSAAUDIOSTREAMCFG pCfgReq,
+                                PALSAAUDIOSTREAMCFG pCfgObt,
+                                snd_pcm_t **pphPCM)
+{
+    snd_pcm_t *phPCM = NULL;
+    int rc;
+
+    unsigned int cChannels = pCfgReq->nchannels;
+    unsigned int uFreq = pCfgReq->freq;
+    snd_pcm_uframes_t obt_buffer_size;
+
+    do
+    {
+        const char *pszDev = fIn ? s_ALSAConf.pcm_name_in : s_ALSAConf.pcm_name_out;
+        if (!pszDev)
+        {
+            LogRel(("ALSA: Invalid or no %s device name set\n",
+                    fIn ? "input" : "output"));
+            rc = VERR_INVALID_PARAMETER;
+            break;
+        }
+
+        int err = snd_pcm_open(&phPCM, pszDev,
+                               fIn ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
+                               SND_PCM_NONBLOCK);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to open \"%s\" as %s: %s\n", pszDev,
+                    fIn ? "ADC" : "DAC", snd_strerror(err)));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        snd_pcm_hw_params_t *pHWParms;
+        snd_pcm_hw_params_alloca(&pHWParms); /** @todo Check for successful allocation? */
+        err = snd_pcm_hw_params_any(phPCM, pHWParms);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to initialize hardware parameters: %s\n",
+                    snd_strerror(err)));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        err = snd_pcm_hw_params_set_access(phPCM, pHWParms,
+                                           SND_PCM_ACCESS_RW_INTERLEAVED);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to set access type: %s\n", snd_strerror(err)));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        err = snd_pcm_hw_params_set_format(phPCM, pHWParms, pCfgReq->fmt);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to set audio format to %d: %s\n",
+                    pCfgReq->fmt, snd_strerror(err)));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        err = snd_pcm_hw_params_set_rate_near(phPCM, pHWParms, &uFreq, 0);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to set frequency to %dHz: %s\n",
+                    pCfgReq->freq, snd_strerror(err)));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        err = snd_pcm_hw_params_set_channels_near(phPCM, pHWParms, &cChannels);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to set number of channels to %d\n", pCfgReq->nchannels));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        if (   cChannels != 1
+            && cChannels != 2)
+        {
+            LogRel(("ALSA: Number of audio channels (%u) not supported\n", cChannels));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        unsigned int period_size = pCfgReq->period_size;
+        unsigned int buffer_size = pCfgReq->buffer_size;
+
+        if (   !((fIn && s_ALSAConf.size_in_usec_in)
+            ||  (!fIn && s_ALSAConf.size_in_usec_out)))
+        {
+            if (!buffer_size)
+            {
+                buffer_size = DEFAULT_BUFFER_SIZE;
+                period_size = DEFAULT_PERIOD_SIZE;
+            }
+        }
+
+        if (buffer_size)
+        {
+            if (   ( fIn && s_ALSAConf.size_in_usec_in)
+                || (!fIn && s_ALSAConf.size_in_usec_out))
+            {
+                if (period_size)
+                {
+                    err = snd_pcm_hw_params_set_period_time_near(phPCM, pHWParms,
+                                                                 &period_size, 0);
+                    if (err < 0)
+                    {
+                        LogRel(("ALSA: Failed to set period time %d\n", pCfgReq->period_size));
+                        rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+                        break;
+                    }
+                }
+
+                err = snd_pcm_hw_params_set_buffer_time_near(phPCM, pHWParms,
+                                                             &buffer_size, 0);
+                if (err < 0)
+                {
+                    LogRel(("ALSA: Failed to set buffer time %d\n", pCfgReq->buffer_size));
+                    rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+                    break;
+                }
+            }
+            else
+            {
+                snd_pcm_uframes_t period_size_f = (snd_pcm_uframes_t)period_size;
+                snd_pcm_uframes_t buffer_size_f = (snd_pcm_uframes_t)buffer_size;
+
+                snd_pcm_uframes_t minval;
+
+                if (period_size_f)
+                {
+                    minval = period_size_f;
+
+                    int dir = 0;
+                    err = snd_pcm_hw_params_get_period_size_min(pHWParms,
+                                                                &minval, &dir);
+                    if (err < 0)
+                    {
+                        LogRel(("ALSA: Could not determine minimal period size\n"));
+                        rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+                        break;
+                    }
+                    else
+                    {
+                        LogFunc(("Minimal period size is: %ld\n", minval));
+                        if (period_size_f < minval)
+                        {
+                            if (   ( fIn && s_ALSAConf.period_size_in_overriden)
+                                || (!fIn && s_ALSAConf.period_size_out_overriden))
+                            {
+                                LogFunc(("Period size %RU32 is less than minimal period size %RU32\n",
+                                         period_size_f, minval));
+                            }
+
+                            period_size_f = minval;
+                        }
+                    }
+
+                    err = snd_pcm_hw_params_set_period_size_near(phPCM, pHWParms,
+                                                                 &period_size_f, 0);
+                    LogFunc(("Period size is: %RU32\n", period_size_f));
+                    if (err < 0)
+                    {
+                        LogRel(("ALSA: Failed to set period size %d (%s)\n",
+                                period_size_f, snd_strerror(err)));
+                        rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+                        break;
+                    }
+                }
+
+                /* Calculate default buffer size here since it might have been changed
+                 * in the _near functions */
+                buffer_size_f = 4 * period_size_f;
+
+                minval = buffer_size_f;
+                err = snd_pcm_hw_params_get_buffer_size_min(pHWParms, &minval);
+                if (err < 0)
+                {
+                    LogRel(("ALSA: Could not retrieve minimal buffer size\n"));
+                    rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+                    break;
+                }
+                else
+                {
+                    LogFunc(("Minimal buffer size is: %RU32\n", minval));
+                    if (buffer_size_f < minval)
+                    {
+                        if (   ( fIn && s_ALSAConf.buffer_size_in_overriden)
+                            || (!fIn && s_ALSAConf.buffer_size_out_overriden))
+                        {
+                            LogFunc(("Buffer size %RU32 is less than minimal buffer size %RU32\n",
+                                     buffer_size_f, minval));
+                        }
+
+                        buffer_size_f = minval;
+                    }
+                }
+
+                err = snd_pcm_hw_params_set_buffer_size_near(phPCM,
+                                                             pHWParms, &buffer_size_f);
+                LogFunc(("Buffer size is: %RU32\n", buffer_size_f));
+                if (err < 0)
+                {
+                    LogRel(("ALSA: Failed to set buffer size %d: %s\n",
+                            buffer_size_f, snd_strerror(err)));
+                    rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+                    break;
+                }
+            }
+        }
+        else
+            LogFunc(("Warning: Buffer size is not set\n"));
+
+        err = snd_pcm_hw_params(phPCM, pHWParms);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to apply audio parameters\n"));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        err = snd_pcm_hw_params_get_buffer_size(pHWParms, &obt_buffer_size);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to get buffer size\n"));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        snd_pcm_uframes_t obt_period_size;
+        int dir = 0;
+        err = snd_pcm_hw_params_get_period_size(pHWParms, &obt_period_size, &dir);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to get period size\n"));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        LogFunc(("Freq=%dHz, period size=%RU32, buffer size=%RU32\n",
+                 pCfgReq->freq, obt_period_size, obt_buffer_size));
+
+        err = snd_pcm_prepare(phPCM);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Could not prepare hPCM %p\n", (void *)phPCM));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        if (   !fIn
+            && s_ALSAConf.threshold)
+        {
+            unsigned uShift;
+            rc = drvHostALSAAudioALSAGetShift(pCfgReq->fmt, &uShift);
+            if (RT_SUCCESS(rc))
+            {
+                int bytes_per_sec = uFreq
+                    << (cChannels == 2)
+                    << uShift;
+
+                snd_pcm_uframes_t threshold
+                    = (s_ALSAConf.threshold * bytes_per_sec) / 1000;
+
+                rc = drvHostALSAAudioSetThreshold(phPCM, threshold);
+            }
+        }
+        else
+            rc = VINF_SUCCESS;
+    }
+    while (0);
+
+    if (RT_SUCCESS(rc))
+    {
+        pCfgObt->fmt       = pCfgReq->fmt;
+        pCfgObt->nchannels = cChannels;
+        pCfgObt->freq      = uFreq;
+        pCfgObt->samples   = obt_buffer_size;
+
+        *pphPCM = phPCM;
+    }
+    else
+        drvHostALSAAudioClose(&phPCM);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+#ifdef DEBUG
+static void drvHostALSAAudioErrorHandler(const char *file, int line, const char *function,
+                                         int err, const char *fmt, ...)
+{
+    /** @todo Implement me! */
+}
+#endif
+
+static int drvHostALSAAudioGetAvail(snd_pcm_t *phPCM, snd_pcm_sframes_t *pFramesAvail)
+{
+    AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
+    AssertPtrReturn(pFramesAvail, VERR_INVALID_POINTER);
+
+    int rc;
+
+    snd_pcm_sframes_t framesAvail;
+    framesAvail = snd_pcm_avail_update(phPCM);
+    if (framesAvail < 0)
+    {
+        if (framesAvail == -EPIPE)
+        {
+            rc = drvHostALSAAudioRecover(phPCM);
+            if (RT_SUCCESS(rc))
+                framesAvail = snd_pcm_avail_update(phPCM);
+        }
+        else
+            rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
+    }
+    else
+        rc = VINF_SUCCESS;
+
+    if (framesAvail >= 0)
+        *pFramesAvail = framesAvail;
+
+    return rc;
+}
+
+static int drvHostALSAAudioRecover(snd_pcm_t *phPCM)
+{
+    AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
+
+    int err = snd_pcm_prepare(phPCM);
+    if (err < 0)
+    {
+        LogFunc(("Failed to recover stream %p: %s\n",
+                 phPCM, snd_strerror(err)));
+        return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
+    }
+
+    return VINF_SUCCESS;
+}
+
+static int drvHostALSAAudioResume(snd_pcm_t *phPCM)
+{
+    AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
+
+    int err = snd_pcm_resume(phPCM);
+    if (err < 0)
+    {
+        LogFunc(("Failed to resume stream %p: %s\n",
+                 phPCM, snd_strerror(err)));
+        return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
+    }
+
+    return VINF_SUCCESS;
+}
+
+static int drvHostALSAAudioStreamCtl(snd_pcm_t *phPCM, bool fPause)
+{
+    int err;
+    if (fPause)
+    {
+        err = snd_pcm_drop(phPCM);
+        if (err < 0)
+        {
+            LogFlow(("Error stopping stream %p: %s\n",
+                     phPCM, snd_strerror(err)));
+            return VERR_ACCESS_DENIED;
+        }
+    }
+    else
+    {
+        err = snd_pcm_prepare (phPCM);
+        if (err < 0)
+        {
+            LogFlow(("Error preparing stream %p: %s\n",
+                     phPCM, snd_strerror(err)));
+            return VERR_ACCESS_DENIED;
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostALSAAudioInit(PPDMIHOSTAUDIO pInterface)
+{
+    NOREF(pInterface);
+
+    LogFlowFuncEnter();
+
+    int rc = audioLoadAlsaLib();
+    if (RT_FAILURE(rc))
+        LogRel(("ALSA: Failed to load the ALSA shared library, rc=%Rrc\n", rc));
+    else
+    {
+#ifdef DEBUG
+        snd_lib_error_set_handler(drvHostALSAAudioErrorHandler);
+#endif
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostALSAAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                   uint32_t *pcSamplesCaptured)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+
+    PALSAAUDIOSTREAMIN pThisStrmIn = (PALSAAUDIOSTREAMIN)pHstStrmIn;
+
+    snd_pcm_sframes_t cAvail;
+    int rc = drvHostALSAAudioGetAvail(pThisStrmIn->phPCM, &cAvail);
+    if (RT_FAILURE(rc))
+    {
+        LogFunc(("Error getting number of captured frames, rc=%Rrc\n", rc));
+        return rc;
+    }
+
+    if (!cAvail) /* No data yet? */
+    {
+        snd_pcm_state_t state = snd_pcm_state(pThisStrmIn->phPCM);
+        switch (state)
+        {
+            case SND_PCM_STATE_PREPARED:
+                cAvail = audioMixBufFree(&pHstStrmIn->MixBuf);
+                break;
+
+            case SND_PCM_STATE_SUSPENDED:
+            {
+                rc = drvHostALSAAudioResume(pThisStrmIn->phPCM);
+                if (RT_FAILURE(rc))
+                    break;
+
+                LogFlow(("Resuming suspended input stream\n"));
+                break;
+            }
+
+            default:
+                LogFlow(("No frames available, state=%d\n", state));
+                break;
+        }
+
+        if (!cAvail)
+        {
+            if (pcSamplesCaptured)
+                *pcSamplesCaptured = 0;
+            return VINF_SUCCESS;
+        }
+    }
+
+    Assert(cAvail);
+    size_t cbToRead = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cAvail);
+
+    LogFlowFunc(("cbToRead=%zu, cAvail=%RI32\n", cbToRead, cAvail));
+
+    uint32_t cWrittenTotal = 0;
+    snd_pcm_uframes_t cToRead;
+    snd_pcm_sframes_t cRead;
+
+    while (cbToRead)
+    {
+        cToRead = RT_MIN(AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, cbToRead),
+                         AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, pThisStrmIn->cbBuf));
+        AssertBreakStmt(cToRead, rc = VERR_NO_DATA);
+        cRead = snd_pcm_readi(pThisStrmIn->phPCM, pThisStrmIn->pvBuf, cToRead);
+        if (cRead <= 0)
+        {
+            switch (cRead)
+            {
+                case 0:
+                {
+                    LogFunc(("Failed to read %RI32 frames\n", cRead));
+                    rc = VERR_ACCESS_DENIED;
+                    break;
+                }
+
+                case -EAGAIN:
+                    break;
+
+                case -EPIPE:
+                {
+                    rc = drvHostALSAAudioRecover(pThisStrmIn->phPCM);
+                    if (RT_FAILURE(rc))
+                        break;
+
+                    LogFlowFunc(("Recovered from capturing\n"));
+                    continue;
+                }
+
+                default:
+                    LogFlowFunc(("Failed to read %RI32 input frames, rc=%Rrc\n",
+                                 cRead, rc));
+                    rc = VERR_GENERAL_FAILURE; /** @todo */
+                    break;
+            }
+        }
+        else
+        {
+            uint32_t cWritten;
+            rc = audioMixBufWriteCirc(&pHstStrmIn->MixBuf,
+                                      pThisStrmIn->pvBuf, AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cRead),
+                                      &cWritten);
+            if (RT_FAILURE(rc))
+                break;
+
+            uint32_t cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
+
+            Assert(cbToRead >= cbWritten);
+            cbToRead -= cbWritten;
+            cWrittenTotal += cWritten;
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        uint32_t cProcessed = 0;
+        if (cWrittenTotal)
+            rc = audioMixBufMixToParent(&pHstStrmIn->MixBuf, cWrittenTotal,
+                                        &cProcessed);
+
+        if (pcSamplesCaptured)
+            *pcSamplesCaptured = cWrittenTotal;
+
+        LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
+                     cWrittenTotal, cProcessed, rc));
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostALSAAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                                 uint32_t *pcSamplesPlayed)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+
+    PALSAAUDIOSTREAMOUT pThisStrmOut = (PALSAAUDIOSTREAMOUT)pHstStrmOut;
+
+    int rc = VINF_SUCCESS;
+    uint32_t cbReadTotal = 0;
+
+    do
+    {
+        snd_pcm_sframes_t cAvail;
+        rc = drvHostALSAAudioGetAvail(pThisStrmOut->phPCM, &cAvail);
+        if (RT_FAILURE(rc))
+        {
+            LogFunc(("Error getting number of playback frames, rc=%Rrc\n", rc));
+            break;
+        }
+
+        size_t cbToRead = RT_MIN(AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf,
+                                                 cAvail),
+                                 AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf,
+                                                 drvAudioHstOutSamplesLive(pHstStrmOut, NULL /* pcStreamsLive */)));
+        LogFlowFunc(("cbToRead=%zu, cbAvail=%zu\n",
+                     cbToRead, AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cAvail)));
+
+        uint32_t cRead, cbRead;
+        snd_pcm_sframes_t cWritten;
+        while (cbToRead)
+        {
+            rc = audioMixBufReadCirc(&pHstStrmOut->MixBuf, pThisStrmOut->pvBuf, cbToRead, &cRead);
+            if (RT_FAILURE(rc))
+                break;
+
+            cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
+            AssertBreak(cbRead);
+
+            cWritten = snd_pcm_writei(pThisStrmOut->phPCM, pThisStrmOut->pvBuf, cRead);
+            if (cWritten <= 0)
+            {
+                switch (cWritten)
+                {
+                    case 0:
+                    {
+                        LogFunc(("Failed to write %RI32 frames\n", cRead));
+                        rc = VERR_ACCESS_DENIED;
+                        break;
+                    }
+
+                    case -EPIPE:
+                    {
+                        rc = drvHostALSAAudioRecover(pThisStrmOut->phPCM);
+                        if (RT_FAILURE(rc))
+                            break;
+
+                        LogFlowFunc(("Recovered from playback\n"));
+                        continue;
+                    }
+
+                    case -ESTRPIPE:
+                    {
+                        /* Stream was suspended and waiting for a recovery. */
+                        rc = drvHostALSAAudioResume(pThisStrmOut->phPCM);
+                        if (RT_FAILURE(rc))
+                        {
+                            LogRel(("ALSA: Failed to resume output stream\n"));
+                            break;
+                        }
+
+                        LogFlowFunc(("Resumed suspended output stream\n"));
+                        continue;
+                    }
+
+                    default:
+                        LogFlowFunc(("Failed to write %RI32 output frames, rc=%Rrc\n",
+                                     cRead, rc));
+                        rc = VERR_GENERAL_FAILURE; /** @todo */
+                        break;
+                }
+            }
+
+            if (RT_FAILURE(rc))
+                break;
+
+            Assert(cbToRead >= cRead);
+            cbToRead -= cbRead;
+            cbReadTotal += cbRead;
+        }
+    }
+    while (0);
+
+    if (RT_SUCCESS(rc))
+    {
+        uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
+        if (cReadTotal)
+            audioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
+
+        if (pcSamplesPlayed)
+            *pcSamplesPlayed = cReadTotal;
+
+        LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n",
+                     cReadTotal, cbReadTotal, rc));
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostALSAAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+
+    PALSAAUDIOSTREAMIN pThisStrmIn = (PALSAAUDIOSTREAMIN)pHstStrmIn;
+
+    drvHostALSAAudioClose(&pThisStrmIn->phPCM);
+
+    if (pThisStrmIn->pvBuf)
+    {
+        RTMemFree(pThisStrmIn->pvBuf);
+        pThisStrmIn->pvBuf = NULL;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostALSAAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+
+    PALSAAUDIOSTREAMOUT pThisStrmOut = (PALSAAUDIOSTREAMOUT)pHstStrmOut;
+
+    drvHostALSAAudioClose(&pThisStrmOut->phPCM);
+
+    if (pThisStrmOut->pvBuf)
+    {
+        RTMemFree(pThisStrmOut->pvBuf);
+        pThisStrmOut->pvBuf = NULL;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostALSAAudioInitOut(PPDMIHOSTAUDIO pInterface,
+                                                 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
+                                                 uint32_t *pcSamples)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    PALSAAUDIOSTREAMOUT pThisStrmOut = (PALSAAUDIOSTREAMOUT)pHstStrmOut;
+    snd_pcm_t *phPCM = NULL;
+
+    int rc;
+
+    do
+    {
+        ALSAAUDIOSTREAMCFG req;
+        req.fmt         = drvHostALSAAudioFmtToALSA(pCfg->enmFormat);
+        req.freq        = pCfg->uHz;
+        req.nchannels   = pCfg->cChannels;
+        req.period_size = s_ALSAConf.period_size_out;
+        req.buffer_size = s_ALSAConf.buffer_size_out;
+
+        ALSAAUDIOSTREAMCFG obt;
+        rc = drvHostALSAAudioOpen(false /* false */, &req, &obt, &phPCM);
+        if (RT_FAILURE(rc))
+            break;
+
+        PDMAUDIOFMT enmFormat;
+        PDMAUDIOENDIANESS enmEnd;
+        rc = drvHostALSAAudioALSAToFmt(obt.fmt, &enmFormat, &enmEnd);
+        if (RT_FAILURE(rc))
+            break;
+
+        PDMAUDIOSTREAMCFG streamCfg;
+        streamCfg.uHz           = obt.freq;
+        streamCfg.cChannels     = obt.nchannels;
+        streamCfg.enmFormat     = enmFormat;
+        streamCfg.enmEndianness = enmEnd;
+
+        rc = drvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props);
+        if (RT_FAILURE(rc))
+            break;
+
+        AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER);
+        size_t cbBuf = obt.samples * (1 << pHstStrmOut->Props.cShift);
+        AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER);
+        pThisStrmOut->pvBuf = RTMemAlloc(cbBuf);
+        if (!pThisStrmOut->pvBuf)
+        {
+            LogRel(("ALSA: Not enough memory for output DAC buffer (%RU32 samples, each %d bytes)\n",
+                    obt.samples, 1 << pHstStrmOut->Props.cShift));
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        pThisStrmOut->cbBuf       = cbBuf;
+        pThisStrmOut->phPCM       = phPCM;
+
+        if (pcSamples)
+            *pcSamples = obt.samples;
+    }
+    while (0);
+
+    if (RT_FAILURE(rc))
+        drvHostALSAAudioClose(&phPCM);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostALSAAudioInitIn(PPDMIHOSTAUDIO pInterface,
+                                                PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
+                                                PDMAUDIORECSOURCE enmRecSource,
+                                                uint32_t *pcSamples)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    int rc;
+
+    PALSAAUDIOSTREAMIN pThisStrmIn = (PALSAAUDIOSTREAMIN)pHstStrmIn;
+    snd_pcm_t *phPCM = NULL;
+
+    do
+    {
+        ALSAAUDIOSTREAMCFG req;
+        req.fmt         = drvHostALSAAudioFmtToALSA(pCfg->enmFormat);
+        req.freq        = pCfg->uHz;
+        req.nchannels   = pCfg->cChannels;
+        req.period_size = s_ALSAConf.period_size_in;
+        req.buffer_size = s_ALSAConf.buffer_size_in;
+
+        ALSAAUDIOSTREAMCFG obt;
+        rc = drvHostALSAAudioOpen(true /* fIn */, &req, &obt, &phPCM);
+        if (RT_FAILURE(rc))
+            break;
+
+        PDMAUDIOFMT enmFormat;
+        PDMAUDIOENDIANESS enmEnd;
+        rc = drvHostALSAAudioALSAToFmt(obt.fmt, &enmFormat, &enmEnd);
+        if (RT_FAILURE(rc))
+            break;
+
+        PDMAUDIOSTREAMCFG streamCfg;
+        streamCfg.uHz           = obt.freq;
+        streamCfg.cChannels     = obt.nchannels;
+        streamCfg.enmFormat     = enmFormat;
+        streamCfg.enmEndianness = enmEnd;
+
+        rc = drvAudioStreamCfgToProps(&streamCfg, &pHstStrmIn->Props);
+        if (RT_FAILURE(rc))
+            break;
+
+        AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER);
+        size_t cbBuf = obt.samples * (1 << pHstStrmIn->Props.cShift);
+        AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER);
+        pThisStrmIn->pvBuf = RTMemAlloc(cbBuf);
+        if (!pThisStrmIn->pvBuf)
+        {
+            LogRel(("ALSA: Not enough memory for input ADC buffer (%RU32 samples, each %d bytes)\n",
+                    obt.samples, 1 << pHstStrmIn->Props.cShift));
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        pThisStrmIn->cbBuf       = cbBuf;
+        pThisStrmIn->phPCM       = phPCM;
+
+        if (pcSamples)
+            *pcSamples = obt.samples;
+    }
+    while (0);
+
+    if (RT_FAILURE(rc))
+        drvHostALSAAudioClose(&phPCM);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(bool) drvHostALSAAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
+{
+    NOREF(pInterface);
+    NOREF(enmDir);
+    return true; /* Always all enabled. */
+}
+
+static DECLCALLBACK(int) drvHostALSAAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                   PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+    PALSAAUDIOSTREAMIN pThisStrmIn = (PALSAAUDIOSTREAMIN)pHstStrmIn;
+
+    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
+
+    int rc;
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+            rc = drvHostALSAAudioStreamCtl(pThisStrmIn->phPCM, false /* fStop */);
+            break;
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+            rc = drvHostALSAAudioStreamCtl(pThisStrmIn->phPCM, true /* fStop */);
+            break;
+
+        default:
+            AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
+            rc = VERR_INVALID_PARAMETER;
+            break;
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostALSAAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                                    PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+    PALSAAUDIOSTREAMOUT pThisStrmOut = (PALSAAUDIOSTREAMOUT)pHstStrmOut;
+
+    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
+
+    int rc;
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+            rc = drvHostALSAAudioStreamCtl(pThisStrmOut->phPCM, false /* fStop */);
+            break;
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+            rc = drvHostALSAAudioStreamCtl(pThisStrmOut->phPCM, true /* fStop */);
+            break;
+
+        default:
+            AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
+            rc = VERR_INVALID_PARAMETER;
+            break;
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostALSAAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    pCfg->cbStreamOut = sizeof(ALSAAUDIOSTREAMOUT);
+    pCfg->cbStreamIn = sizeof(ALSAAUDIOSTREAMIN);
+    pCfg->cMaxHstStrmsOut = INT_MAX;
+    pCfg->cMaxHstStrmsIn = INT_MAX;
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvHostALSAAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTALSAAUDIO  pThis   = PDMINS_2_DATA(pDrvIns, PDRVHOSTALSAAUDIO);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
+
+    return NULL;
+}
+
+static DECLCALLBACK(void) drvHostAlsaAudioDestruct(PPDMDRVINS pDrvIns)
+{
+}
+
+/**
+ * Construct a DirectSound Audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvHostAlsaAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+    PDRVHOSTALSAAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTALSAAUDIO);
+    LogRel(("Audio: Initializing ALSA driver\n"));
+
+    /*
+     * Init the static parts.
+     */
+    pThis->pDrvIns                   = pDrvIns;
+    /* IBase */
+    pDrvIns->IBase.pfnQueryInterface = drvHostALSAAudioQueryInterface;
+    /* IHostAudio */
+    PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostALSAAudio);
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Char driver registration record.
+ */
+const PDMDRVREG g_DrvHostALSAAudio =
+{
+    /* u32Version */
+    PDM_DRVREG_VERSION,
+    /* szName */
+    "ALSAAudio",
+    /* szRCMod */
+    "",
+    /* szR0Mod */
+    "",
+    /* pszDescription */
+    "ALSA host audio driver",
+    /* fFlags */
+     PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+    /* fClass. */
+    PDM_DRVREG_CLASS_AUDIO,
+    /* cMaxInstances */
+    ~0U,
+    /* cbInstance */
+    sizeof(DRVHOSTALSAAUDIO),
+    /* pfnConstruct */
+    drvHostAlsaAudioConstruct,
+    /* pfnDestruct */
+    drvHostAlsaAudioDestruct,
+    /* pfnRelocate */
+    NULL,
+    /* pfnIOCtl */
+    NULL,
+    /* pfnPowerOn */
+    NULL,
+    /* pfnReset */
+    NULL,
+    /* pfnSuspend */
+    NULL,
+    /* pfnResume */
+    NULL,
+    /* pfnAttach */
+    NULL,
+    /* pfnDetach */
+    NULL,
+    /* pfnPowerOff */
+    NULL,
+    /* pfnSoftReset */
+    NULL,
+    /* u32EndVersion */
+    PDM_DRVREG_VERSION
+};
+
+static struct audio_option alsa_options[] =
+{
+    {"DACSizeInUsec", AUD_OPT_BOOL, &s_ALSAConf.size_in_usec_out,
+     "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
+    {"DACPeriodSize", AUD_OPT_INT, &s_ALSAConf.period_size_out,
+     "DAC period size", &s_ALSAConf.period_size_out_overriden, 0},
+    {"DACBufferSize", AUD_OPT_INT, &s_ALSAConf.buffer_size_out,
+     "DAC buffer size", &s_ALSAConf.buffer_size_out_overriden, 0},
+
+    {"ADCSizeInUsec", AUD_OPT_BOOL, &s_ALSAConf.size_in_usec_in,
+     "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
+    {"ADCPeriodSize", AUD_OPT_INT, &s_ALSAConf.period_size_in,
+     "ADC period size", &s_ALSAConf.period_size_in_overriden, 0},
+    {"ADCBufferSize", AUD_OPT_INT, &s_ALSAConf.buffer_size_in,
+     "ADC buffer size", &s_ALSAConf.buffer_size_in_overriden, 0},
+
+    {"Threshold", AUD_OPT_INT, &s_ALSAConf.threshold,
+     "(undocumented)", NULL, 0},
+
+    {"DACDev", AUD_OPT_STR, &s_ALSAConf.pcm_name_out,
+     "DAC device name (for instance dmix)", NULL, 0},
+
+    {"ADCDev", AUD_OPT_STR, &s_ALSAConf.pcm_name_in,
+     "ADC device name", NULL, 0},
+
+    NULL
+};
+
Index: /trunk/src/VBox/Devices/Audio/DrvHostCoreAudio.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DrvHostCoreAudio.cpp	(revision 54433)
+++ /trunk/src/VBox/Devices/Audio/DrvHostCoreAudio.cpp	(revision 54433)
@@ -0,0 +1,2012 @@
+/* $Id$ */
+/** @file
+ * VBox audio devices: Mac OS X CoreAudio audio driver.
+ */
+
+/*
+ * Copyright (C) 2010-2015 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#include "DrvAudio.h"
+#include "AudioMixBuffer.h"
+
+#include <iprt/asm.h>
+#include <iprt/cdefs.h>
+#include <iprt/circbuf.h>
+#include <iprt/mem.h>
+
+#include "vl_vbox.h"
+#include <iprt/uuid.h>
+
+#include <CoreAudio/CoreAudio.h>
+#include <CoreServices/CoreServices.h>
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioConverter.h>
+
+#ifdef LOG_GROUP
+# undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_DEV_AUDIO
+#include <VBox/log.h>
+
+/* TODO:
+ * - Maybe make sure the threads are immediately stopped if playing/recording stops.
+ */
+
+/*
+ * Most of this is based on:
+ * http://developer.apple.com/mac/library/technotes/tn2004/tn2097.html
+ * http://developer.apple.com/mac/library/technotes/tn2002/tn2091.html
+ * http://developer.apple.com/mac/library/qa/qa2007/qa1533.html
+ * http://developer.apple.com/mac/library/qa/qa2001/qa1317.html
+ * http://developer.apple.com/mac/library/documentation/AudioUnit/Reference/AUComponentServicesReference/Reference/reference.html
+ */
+
+/**
+ * Host Coreaudio driver instance data.
+ * @implements PDMIAUDIOCONNECTOR
+ */
+typedef struct DRVHOSTCOREAUDIO
+{
+    /** Pointer to the driver instance structure. */
+    PPDMDRVINS    pDrvIns;
+    /** Pointer to host audio interface. */
+    PDMIHOSTAUDIO IHostAudio;
+} DRVHOSTCOREAUDIO, *PDRVHOSTCOREAUDIO;
+
+/*******************************************************************************
+ *
+ * Helper function section
+ *
+ ******************************************************************************/
+
+#ifdef DEBUG
+static void drvHostCoreAudioPrintASBDesc(const char *pszDesc, const AudioStreamBasicDescription *pStreamDesc)
+{
+    char pszSampleRate[32];
+    Log(("%s AudioStreamBasicDescription:\n", pszDesc));
+    LogFlowFunc(("Format ID: %RU32 (%c%c%c%c)\n", pStreamDesc->mFormatID,
+                 RT_BYTE4(pStreamDesc->mFormatID), RT_BYTE3(pStreamDesc->mFormatID),
+                 RT_BYTE2(pStreamDesc->mFormatID), RT_BYTE1(pStreamDesc->mFormatID)));
+    LogFlowFunc(("Flags: %RU32", pStreamDesc->mFormatFlags));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsFloat)
+        Log((" Float"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsBigEndian)
+        Log((" BigEndian"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsSignedInteger)
+        Log((" SignedInteger"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsPacked)
+        Log((" Packed"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsAlignedHigh)
+        Log((" AlignedHigh"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsNonInterleaved)
+        Log((" NonInterleaved"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsNonMixable)
+        Log((" NonMixable"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagsAreAllClear)
+        Log((" AllClear"));
+    Log(("\n"));
+    snprintf(pszSampleRate, 32, "%.2f", (float)pStreamDesc->mSampleRate); /** @todo r=andy Use RTStrPrint*. */
+    LogFlowFunc(("SampleRate      : %s\n", pszSampleRate));
+    LogFlowFunc(("ChannelsPerFrame: %RU32\n", pStreamDesc->mChannelsPerFrame));
+    LogFlowFunc(("FramesPerPacket : %RU32\n", pStreamDesc->mFramesPerPacket));
+    LogFlowFunc(("BitsPerChannel  : %RU32\n", pStreamDesc->mBitsPerChannel));
+    LogFlowFunc(("BytesPerFrame   : %RU32\n", pStreamDesc->mBytesPerFrame));
+    LogFlowFunc(("BytesPerPacket  : %RU32\n", pStreamDesc->mBytesPerPacket));
+}
+#endif /* DEBUG */
+
+static void drvHostCoreAudioPCMInfoToASBDesc(PDMPCMPROPS *pPcmProperties, AudioStreamBasicDescription *pStreamDesc)
+{
+    pStreamDesc->mFormatID         = kAudioFormatLinearPCM;
+    pStreamDesc->mFormatFlags      = kAudioFormatFlagIsPacked;
+    pStreamDesc->mFramesPerPacket  = 1;
+    pStreamDesc->mSampleRate       = (Float64)pPcmProperties->uHz;
+    pStreamDesc->mChannelsPerFrame = pPcmProperties->cChannels;
+    pStreamDesc->mBitsPerChannel   = pPcmProperties->cBits;
+    if (pPcmProperties->fSigned)
+        pStreamDesc->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
+    pStreamDesc->mBytesPerFrame    = pStreamDesc->mChannelsPerFrame * (pStreamDesc->mBitsPerChannel / 8);
+    pStreamDesc->mBytesPerPacket   = pStreamDesc->mFramesPerPacket * pStreamDesc->mBytesPerFrame;
+}
+
+static OSStatus drvHostCoreAudioSetFrameBufferSize(AudioDeviceID deviceID, bool fInput, UInt32 cReqSize, UInt32 *pcActSize)
+{
+    AudioObjectPropertyScope propScope = fInput
+                                       ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
+    AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyBufferFrameSize, propScope,
+                                           kAudioObjectPropertyElementMaster };
+
+    /* First try to set the new frame buffer size. */
+    OSStatus err = AudioObjectSetPropertyData(deviceID, &propAdr, NULL, 0, sizeof(cReqSize), &cReqSize);
+
+    /* Check if it really was set. */
+    UInt32 cSize = sizeof(*pcActSize);
+    err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pcActSize);
+    if (RT_UNLIKELY(err != noErr))
+        return err;
+
+    /* If both sizes are the same, we are done. */
+    if (cReqSize == *pcActSize)
+        return noErr;
+
+    /* If not we have to check the limits of the device. First get the size of
+       the buffer size range property. */
+    propAdr.mSelector = kAudioDevicePropertyBufferSizeRange;
+    err = AudioObjectGetPropertyDataSize(deviceID, &propAdr, 0, NULL, &cSize);
+    if (RT_UNLIKELY(err != noErr))
+        return err;
+
+    Assert(cSize);
+    AudioValueRange *pRange = (AudioValueRange *)RTMemAllocZ(cSize);
+    if (pRange)
+    {
+        err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pRange);
+        if (err == noErr)
+        {
+            Float64 cMin = -1;
+            Float64 cMax = -1;
+            for (size_t a = 0; a < cSize / sizeof(AudioValueRange); a++)
+            {
+                /* Search for the absolute minimum. */
+                if (   pRange[a].mMinimum < cMin
+                    || cMin == -1)
+                    cMin = pRange[a].mMinimum;
+
+                /* Search for the best maximum which isn't bigger than cReqSize. */
+                if (pRange[a].mMaximum < cReqSize)
+                {
+                    if (pRange[a].mMaximum > cMax)
+                        cMax = pRange[a].mMaximum;
+                }
+            }
+            if (cMax == -1)
+                cMax = cMin;
+            cReqSize = cMax;
+
+            /* First try to set the new frame buffer size. */
+            propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
+            err = AudioObjectSetPropertyData(deviceID, &propAdr, 0, NULL, sizeof(cReqSize), &cReqSize);
+            if (err == noErr)
+            {
+                /* Check if it really was set. */
+                cSize = sizeof(*pcActSize);
+                err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &cSize, pcActSize);
+            }
+        }
+
+        RTMemFree(pRange);
+    }
+    else
+        err = notEnoughMemoryErr;
+
+    return err;
+}
+
+DECL_FORCE_INLINE(bool) drvHostCoreAudioIsRunning(AudioDeviceID deviceID)
+{
+    AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsRunning, kAudioObjectPropertyScopeGlobal,
+                                           kAudioObjectPropertyElementMaster };
+    UInt32 uFlag = 0;
+    UInt32 uSize = sizeof(uFlag);
+    OSStatus err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &uFlag);
+    if (err != kAudioHardwareNoError)
+        LogRel(("CoreAudio: Could not determine whether the device is running (%RI32)\n", err));
+
+    return (uFlag >= 1);
+}
+
+static int drvHostCoreAudioCFStringToCString(const CFStringRef pCFString, char **ppszString)
+{
+    CFIndex cLen = CFStringGetLength(pCFString) + 1;
+    char *pszResult = (char *)RTMemAllocZ(cLen * sizeof(char));
+    if (!CFStringGetCString(pCFString, pszResult, cLen, kCFStringEncodingUTF8))
+    {
+        RTMemFree(pszResult);
+        return VERR_NOT_FOUND;
+    }
+
+    *ppszString = pszResult;
+    return VINF_SUCCESS;
+}
+
+static AudioDeviceID drvHostCoreAudioDeviceUIDtoID(const char* pszUID)
+{
+    /* Create a CFString out of our CString. */
+    CFStringRef strUID = CFStringCreateWithCString(NULL, pszUID, kCFStringEncodingMacRoman);
+
+    /* Fill the translation structure. */
+    AudioDeviceID deviceID;
+
+    AudioValueTranslation translation;
+    translation.mInputData      = &strUID;
+    translation.mInputDataSize  = sizeof(CFStringRef);
+    translation.mOutputData     = &deviceID;
+    translation.mOutputDataSize = sizeof(AudioDeviceID);
+
+    /* Fetch the translation from the UID to the device ID. */
+    AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDeviceForUID, kAudioObjectPropertyScopeGlobal,
+                                           kAudioObjectPropertyElementMaster };
+
+    UInt32 uSize = sizeof(AudioValueTranslation);
+    OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &translation);
+
+    /* Release the temporary CFString */
+    CFRelease(strUID);
+
+    if (RT_LIKELY(err == noErr))
+        return deviceID;
+
+    /* Return the unknown device on error. */
+    return kAudioDeviceUnknown;
+}
+
+/*******************************************************************************
+ *
+ * Global structures section
+ *
+ ******************************************************************************/
+
+/* Initialization status indicator used for the recreation of the AudioUnits. */
+#define CA_STATUS_UNINIT    UINT32_C(0) /* The device is uninitialized */
+#define CA_STATUS_IN_INIT   UINT32_C(1) /* The device is currently initializing */
+#define CA_STATUS_INIT      UINT32_C(2) /* The device is initialized */
+#define CA_STATUS_IN_UNINIT UINT32_C(3) /* The device is currently uninitializing */
+#define CA_STATUS_REINIT    UINT32_C(4) /* The device has to be reinitialized */
+
+/* Error code which indicates "End of data" */
+static const OSStatus caConverterEOFDErr = 0x656F6664; /* 'eofd' */
+
+typedef struct COREAUDIOSTREAMOUT
+{
+    /** Host stream out. */
+    PDMAUDIOHSTSTRMOUT streamOut;
+    /* Stream description which is default on the device */
+    AudioStreamBasicDescription deviceFormat;
+    /* Stream description which is selected for using by VBox */
+    AudioStreamBasicDescription streamFormat;
+    /* The audio device ID of the currently used device */
+    AudioDeviceID deviceID;
+    /* The AudioUnit used */
+    AudioUnit audioUnit;
+    /* A ring buffer for transferring data to the playback thread. */
+    PRTCIRCBUF pBuf;
+    /* Temporary buffer for copying over audio data into Core Audio. */
+    void *pvPCMBuf;
+    /** Size of the temporary buffer. */
+    size_t cbPCMBuf;
+    /* Initialization status tracker. Used when some of the device parameters
+     * or the device itself is changed during the runtime. */
+    volatile uint32_t status;
+} COREAUDIOSTREAMOUT, *PCOREAUDIOSTREAMOUT;
+
+typedef struct COREAUDIOSTREAMIN
+{
+    /** Host stream in. */
+    PDMAUDIOHSTSTRMIN streamIn;
+    /* Stream description which is default on the device */
+    AudioStreamBasicDescription deviceFormat;
+    /* Stream description which is selected for using by VBox */
+    AudioStreamBasicDescription streamFormat;
+    /* The audio device ID of the currently used device */
+    AudioDeviceID deviceID;
+    /* The AudioUnit used */
+    AudioUnit audioUnit;
+    /* The audio converter if necessary */
+    AudioConverterRef converter;
+    /* A temporary position value used in the caConverterCallback function */
+    uint32_t rpos;
+    /* The ratio between the device & the stream sample rate */
+    Float64 sampleRatio;
+    /* An extra buffer used for render the audio data in the recording thread */
+    AudioBufferList bufferList;
+    /* A ring buffer for transferring data from the recording thread */
+    PRTCIRCBUF pBuf;
+    /* Initialization status tracker. Used when some of the device parameters
+     * or the device itself is changed during the runtime. */
+    volatile uint32_t status;
+} COREAUDIOSTREAMIN, *PCOREAUDIOSTREAMIN;
+
+static int drvHostCoreAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn, PDMAUDIOSTREAMCMD enmStreamCmd);
+static int drvHostCoreAudioInitInput(PPDMAUDIOHSTSTRMIN pHstStrmIn, uint32_t *pcSamples);
+static int drvHostCoreAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn);
+static int drvHostCoreAudioReinitInput(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn);
+
+static int drvHostCoreAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd);
+static int drvHostCoreAudioInitOutput(PPDMAUDIOHSTSTRMOUT pHstStrmOut, uint32_t *pcSamples);
+static int drvHostCoreAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut);
+static int drvHostCoreAudioReinitOutput(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut);
+static OSStatus drvHostCoreAudioPlaybackAudioDevicePropertyChanged(AudioObjectID propertyID, UInt32 nAddresses, const AudioObjectPropertyAddress properties[], void *pvUser);
+static OSStatus drvHostCoreAudioPlaybackCallback(void *pvUser, AudioUnitRenderActionFlags *pActionFlags, const AudioTimeStamp *pAudioTS, UInt32 uBusID, UInt32 cFrames, AudioBufferList* pBufData);
+
+/* Callback for getting notified when the default input/output device has been changed. */
+static DECLCALLBACK(OSStatus) drvHostCoreAudioDefaultDeviceChanged(AudioObjectID propertyID,
+                                                                   UInt32 nAddresses,
+                                                                   const AudioObjectPropertyAddress properties[],
+                                                                   void *pvUser)
+{
+    PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
+
+    OSStatus err = noErr;
+
+    switch (propertyID)
+    {
+        case kAudioHardwarePropertyDefaultInputDevice:
+        {
+            /* This listener is called on every change of the hardware
+             * device. So check if the default device has really changed. */
+            AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice,
+                                                   kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+
+            UInt32 uSize = sizeof(pStreamIn->deviceID);
+            UInt32 uResp;
+            err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &uResp);
+
+            if (err == noErr)
+            {
+                if (pStreamIn->deviceID != uResp)
+                {
+                    LogRel(("CoreAudio: Default input device has changed\n"));
+
+                    /* We move the reinitialization to the next input event.
+                     * This make sure this thread isn't blocked and the
+                     * reinitialization is done when necessary only. */
+                    ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT);
+                }
+            }
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    return noErr;
+}
+
+static int drvHostCoreAudioReinitInput(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    PPDMDRVINS pDrvIns      = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
+
+    PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pHstStrmIn;
+
+    drvHostCoreAudioFiniIn(pInterface, &pStreamIn->streamIn);
+
+    drvHostCoreAudioInitInput(&pStreamIn->streamIn, NULL /* pcSamples */);
+    drvHostCoreAudioControlIn(pInterface, &pStreamIn->streamIn, PDMAUDIOSTREAMCMD_ENABLE);
+
+    return VINF_SUCCESS;
+}
+
+static int drvHostCoreAudioReinitOutput(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    PPDMDRVINS pDrvIns      = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
+
+    PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
+
+    drvHostCoreAudioFiniOut(pInterface, &pStreamOut->streamOut);
+
+    drvHostCoreAudioInitOutput(&pStreamOut->streamOut, NULL /* pcSamples */);
+    drvHostCoreAudioControlOut(pInterface, &pStreamOut->streamOut, PDMAUDIOSTREAMCMD_ENABLE);
+
+    return VINF_SUCCESS;
+}
+
+/* Callback for getting notified when some of the properties of an audio device has changed. */
+static DECLCALLBACK(OSStatus) drvHostCoreAudioRecordingAudioDevicePropertyChanged(AudioObjectID                     propertyID,
+                                                                                  UInt32                            cAdresses,
+                                                                                  const AudioObjectPropertyAddress  aProperties[],
+                                                                                  void                             *pvUser)
+{
+    PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
+
+    switch (propertyID)
+    {
+#ifdef DEBUG
+        case kAudioDeviceProcessorOverload:
+        {
+            LogFunc(("Processor overload detected!\n"));
+            break;
+        }
+#endif /* DEBUG */
+        case kAudioDevicePropertyNominalSampleRate:
+        {
+            LogRel(("CoreAudio: Recording sample rate changed\n"));
+
+            /* We move the reinitialization to the next input event.
+             * This make sure this thread isn't blocked and the
+             * reinitialization is done when necessary only. */
+            ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT);
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    return noErr;
+}
+
+/* Callback to convert audio input data from one format to another. */
+static DECLCALLBACK(OSStatus) drvHostCoreAudioConverterCallback(AudioConverterRef              converterID,
+                                                                UInt32                        *pcPackets,
+                                                                AudioBufferList               *pBufData,
+                                                                AudioStreamPacketDescription **ppPacketDesc,
+                                                                void                          *pvUser)
+{
+    /** @todo Check incoming pointers. */
+
+    PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
+
+    /** @todo Check converter ID? */
+
+    const AudioBufferList *pBufferList = &pStreamIn->bufferList;
+
+    if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT)
+        return noErr;
+
+    /** @todo In principle we had to check here if the source is non interleaved, and if so,
+     *        so go through all buffers not only the first one like now. */
+
+    /* Use the lower one of the packets to process & the available packets in the buffer. */
+    Assert(pBufferList->mBuffers[0].mDataByteSize >= pStreamIn->rpos);
+    UInt32 cSize = RT_MIN(*pcPackets * pStreamIn->deviceFormat.mBytesPerPacket,
+                          pBufferList->mBuffers[0].mDataByteSize - pStreamIn->rpos);
+
+    /* Set the new size on output, so the caller know what we have processed. */
+    Assert(pStreamIn->deviceFormat.mBytesPerPacket);
+    *pcPackets = cSize / pStreamIn->deviceFormat.mBytesPerPacket;
+
+    OSStatus err;
+
+    /* If no data is available anymore we return with an error code. This error code will be returned
+     * from AudioConverterFillComplexBuffer. */
+    if (*pcPackets == 0)
+    {
+        pBufData->mBuffers[0].mDataByteSize = 0;
+        pBufData->mBuffers[0].mData         = NULL;
+
+        err = caConverterEOFDErr;
+    }
+    else
+    {
+        pBufData->mBuffers[0].mNumberChannels = pBufferList->mBuffers[0].mNumberChannels;
+        pBufData->mBuffers[0].mDataByteSize   = cSize;
+        pBufData->mBuffers[0].mData           = (uint8_t *)pBufferList->mBuffers[0].mData + pStreamIn->rpos;
+
+        pStreamIn->rpos += cSize;
+
+        err = noErr;
+    }
+
+    return err;
+}
+
+/* Callback to feed audio input buffer. */
+static DECLCALLBACK(OSStatus) drvHostCoreAudioRecordingCallback(void                       *pvUser,
+                                                                AudioUnitRenderActionFlags *pActionFlags,
+                                                                const AudioTimeStamp       *pAudioTS,
+                                                                UInt32                      uBusID,
+                                                                UInt32                      cFrames,
+                                                                AudioBufferList            *pBufData)
+{
+    PCOREAUDIOSTREAMIN pStreamIn  = (PCOREAUDIOSTREAMIN)pvUser;
+    PPDMAUDIOHSTSTRMIN pHstStrmIN = &pStreamIn->streamIn;
+
+    if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT)
+        return noErr;
+
+    /* If nothing is pending return immediately. */
+    if (cFrames == 0)
+        return noErr;
+
+    OSStatus err = noErr;
+    int rc = VINF_SUCCESS;
+
+    do
+    {
+        /* Are we using a converter? */
+        if (pStreamIn->converter)
+        {
+            /* First, render the data as usual. */
+            pStreamIn->bufferList.mBuffers[0].mNumberChannels = pStreamIn->deviceFormat.mChannelsPerFrame;
+            pStreamIn->bufferList.mBuffers[0].mDataByteSize   = pStreamIn->deviceFormat.mBytesPerFrame * cFrames;
+            AssertBreakStmt(pStreamIn->bufferList.mBuffers[0].mDataByteSize, rc = VERR_INVALID_PARAMETER);
+            pStreamIn->bufferList.mBuffers[0].mData           = RTMemAlloc(pStreamIn->bufferList.mBuffers[0].mDataByteSize);
+            if (!pStreamIn->bufferList.mBuffers[0].mData)
+            {
+                rc = VERR_NO_MEMORY;
+                break;
+            }
+
+            err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames, &pStreamIn->bufferList);
+            if (err != noErr)
+            {
+                LogFlowFunc(("Failed rendering audio data (%RI32)\n", err));
+                rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */
+                break;
+            }
+
+            size_t cbAvail = RT_MIN(RTCircBufFree(pStreamIn->pBuf), pStreamIn->bufferList.mBuffers[0].mDataByteSize);
+
+            /* Initialize the temporary output buffer */
+            AudioBufferList tmpList;
+            tmpList.mNumberBuffers = 1;
+            tmpList.mBuffers[0].mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;
+
+            /* Iterate as long as data is available. */
+            uint8_t *puDst = NULL;
+            while (cbAvail)
+            {
+                /* Try to acquire the necessary space from the ring buffer. */
+                size_t cbToWrite = 0;
+                RTCircBufAcquireWriteBlock(pStreamIn->pBuf, cbAvail, (void **)&puDst, &cbToWrite);
+                if (!cbToWrite)
+                    break;
+
+                /* Now set how much space is available for output. */
+                Assert(pStreamIn->streamFormat.mBytesPerPacket);
+
+                UInt32 ioOutputDataPacketSize = cbToWrite / pStreamIn->streamFormat.mBytesPerPacket;
+
+                /* Set our ring buffer as target. */
+                tmpList.mBuffers[0].mDataByteSize = cbToWrite;
+                tmpList.mBuffers[0].mData         = puDst;
+
+                AudioConverterReset(pStreamIn->converter);
+
+                err = AudioConverterFillComplexBuffer(pStreamIn->converter, drvHostCoreAudioConverterCallback, pStreamIn,
+                                                      &ioOutputDataPacketSize, &tmpList, NULL);
+                if(   err != noErr
+                   && err != caConverterEOFDErr)
+                {
+                    LogFlowFunc(("Failed to convert audio data (%RI32:%c%c%c%c)\n", err,
+                                 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err)));
+                    rc = VERR_IO_GEN_FAILURE;
+                    break;
+                }
+
+                /* Check in any case what processed size is returned. It could be less than we expected. */
+                cbToWrite = ioOutputDataPacketSize * pStreamIn->streamFormat.mBytesPerPacket;
+
+                /* Release the ring buffer, so the main thread could start reading this data. */
+                RTCircBufReleaseWriteBlock(pStreamIn->pBuf, cbToWrite);
+
+                /* If the error is "End of Data" it means there is no data anymore
+                 * which could be converted. So end here now. */
+                if (err == caConverterEOFDErr)
+                    break;
+
+                Assert(cbAvail >= cbToWrite);
+                cbAvail -= cbToWrite;
+            }
+        }
+        else /* No converter being used. */
+        {
+            pStreamIn->bufferList.mBuffers[0].mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;
+            pStreamIn->bufferList.mBuffers[0].mDataByteSize   = pStreamIn->streamFormat.mBytesPerFrame * cFrames;
+            AssertBreakStmt(pStreamIn->bufferList.mBuffers[0].mDataByteSize, rc = VERR_INVALID_PARAMETER);
+            pStreamIn->bufferList.mBuffers[0].mData           = RTMemAlloc(pStreamIn->bufferList.mBuffers[0].mDataByteSize);
+            if (!pStreamIn->bufferList.mBuffers[0].mData)
+            {
+                rc = VERR_NO_MEMORY;
+                break;
+            }
+
+            err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames, &pStreamIn->bufferList);
+            if (err != noErr)
+            {
+                LogFlowFunc(("Failed rendering audio data (%RI32)\n", err));
+                rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */
+                break;
+            }
+
+            size_t cbAvail = RT_MIN(RTCircBufFree(pStreamIn->pBuf), pStreamIn->bufferList.mBuffers[0].mDataByteSize);
+
+            /* Iterate as long as data is available. */
+            uint8_t *puDst = NULL;
+            uint32_t cbWrittenTotal = 0;
+            while(cbAvail)
+            {
+                /* Try to acquire the necessary space from the ring buffer. */
+                size_t cbToWrite = 0;
+                RTCircBufAcquireWriteBlock(pStreamIn->pBuf, cbAvail, (void **)&puDst, &cbToWrite);
+                if (!cbToWrite)
+                    break;
+
+                /* Copy the data from the core audio buffer to the ring buffer. */
+                memcpy(puDst, (uint8_t *)pStreamIn->bufferList.mBuffers[0].mData + cbWrittenTotal, cbToWrite);
+
+                /* Release the ring buffer, so the main thread could start reading this data. */
+                RTCircBufReleaseWriteBlock(pStreamIn->pBuf, cbToWrite);
+
+                cbWrittenTotal += cbToWrite;
+
+                Assert(cbAvail >= cbToWrite);
+                cbAvail -= cbToWrite;
+            }
+        }
+
+        if (pStreamIn->bufferList.mBuffers[0].mData)
+        {
+            RTMemFree(pStreamIn->bufferList.mBuffers[0].mData);
+            pStreamIn->bufferList.mBuffers[0].mData = NULL;
+        }
+
+    } while (0);
+
+    return err;
+}
+
+/** @todo Eventually split up this function, as this already is huge! */
+static int drvHostCoreAudioInitInput(PPDMAUDIOHSTSTRMIN pHstStrmIn, uint32_t *pcSamples)
+{
+    OSStatus err = noErr;
+
+    UInt32 cFrames; /* default frame count */
+    UInt32 cSamples; /* samples count */
+
+    PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pHstStrmIn;
+
+    ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_INIT);
+
+    UInt32 uSize = 0;
+    if (pStreamIn->deviceID == kAudioDeviceUnknown)
+    {
+        /* Fetch the default audio input device currently in use. */
+        AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice,
+                                               kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+        uSize = sizeof(pStreamIn->deviceID);
+        err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize,  &pStreamIn->deviceID);
+        if (err != noErr)
+        {
+            LogRel(("CoreAudio: Unable to determine default input device (%RI32)\n", err));
+            return VERR_NOT_FOUND;
+        }
+    }
+
+    /*
+     * Try to get the name of the input device and log it. It's not fatal if it fails.
+     */
+    CFStringRef strTemp;
+
+    AudioObjectPropertyAddress propAdr = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal,
+                                           kAudioObjectPropertyElementMaster };
+    uSize = sizeof(CFStringRef);
+    err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
+    if (err == noErr)
+    {
+        char *pszDevName = NULL;
+        err = drvHostCoreAudioCFStringToCString(strTemp, &pszDevName);
+        if (err == noErr)
+        {
+            CFRelease(strTemp);
+
+            /* Get the device' UUID. */
+            propAdr.mSelector = kAudioObjectPropertyName;
+            err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
+            if (err == noErr)
+            {
+                char *pszUID = NULL;
+                err = drvHostCoreAudioCFStringToCString(strTemp, &pszUID);
+                if (err == noErr)
+                {
+                    CFRelease(strTemp);
+                    LogRel(("CoreAudio: Using input device: %s (UID: %s)\n", pszDevName, pszUID));
+
+                    RTMemFree(pszUID);
+                }
+            }
+
+            RTMemFree(pszDevName);
+        }
+    }
+    else
+        LogRel(("CoreAudio: Unable to determine input device name (%RI32)\n", err));
+
+    /* Get the default frames buffer size, so that we can setup our internal buffers. */
+    uSize = sizeof(cFrames);
+    propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
+    propAdr.mScope    = kAudioDevicePropertyScopeInput;
+    err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &cFrames);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to determine frame buffer size of the audio input device (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */
+    err = drvHostCoreAudioSetFrameBufferSize(pStreamIn->deviceID, true /* fInput */, cFrames, &cFrames);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to set frame buffer size for the audio input device (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    ComponentDescription cd;
+    RT_ZERO(cd);
+    cd.componentType         = kAudioUnitType_Output;
+    cd.componentSubType      = kAudioUnitSubType_HALOutput;
+    cd.componentManufacturer = kAudioUnitManufacturer_Apple;
+
+    /* Try to find the default HAL output component. */
+    Component cp = FindNextComponent(NULL, &cd);
+    if (cp == 0)
+    {
+        LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Open the default HAL output component. */
+    err = OpenAComponent(cp, &pStreamIn->audioUnit);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Switch the I/O mode for input to on. */
+    UInt32 uFlag = 1;
+    err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input,
+                               1, &uFlag, sizeof(uFlag));
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to disable input I/O mode for input stream (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Switch the I/O mode for input to off. This is important, as this is a pure input stream. */
+    uFlag = 0;
+    err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
+                               0, &uFlag, sizeof(uFlag));
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to disable output I/O mode for input stream (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Set the default audio input device as the device for the new AudioUnit. */
+    err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
+                               0, &pStreamIn->deviceID, sizeof(pStreamIn->deviceID));
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to set current device (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /*
+     * CoreAudio will inform us on a second thread for new incoming audio data.
+     * Therefor register a callback function which will process the new data.
+     */
+    AURenderCallbackStruct cb;
+    RT_ZERO(cb);
+    cb.inputProc       = drvHostCoreAudioRecordingCallback;
+    cb.inputProcRefCon = pStreamIn;
+
+    err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global,
+                               0, &cb, sizeof(cb));
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to register input callback (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Fetch the current stream format of the device. */
+    uSize = sizeof(pStreamIn->deviceFormat);
+    err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
+                               1, &pStreamIn->deviceFormat, &uSize);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to get device format (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Create an AudioStreamBasicDescription based on our required audio settings. */
+    drvHostCoreAudioPCMInfoToASBDesc(&pStreamIn->streamIn.Props, &pStreamIn->streamFormat);
+
+#ifdef DEBUG
+    drvHostCoreAudioPrintASBDesc("CoreAudio: Input device", &pStreamIn->deviceFormat);
+    drvHostCoreAudioPrintASBDesc("CoreAudio: Input stream", &pStreamIn->streamFormat);
+#endif /* DEBUG */
+
+    /* If the frequency of the device is different from the requested one we
+     * need a converter. The same count if the number of channels is different. */
+    if (   pStreamIn->deviceFormat.mSampleRate       != pStreamIn->streamFormat.mSampleRate
+        || pStreamIn->deviceFormat.mChannelsPerFrame != pStreamIn->streamFormat.mChannelsPerFrame)
+    {
+        err = AudioConverterNew(&pStreamIn->deviceFormat, &pStreamIn->streamFormat, &pStreamIn->converter);
+        if (RT_UNLIKELY(err != noErr))
+        {
+            LogRel(("CoreAudio: Failed to create the audio converte(%RI32). Input Format=%d, Output Foramt=%d\n",
+                     err, pStreamIn->deviceFormat, pStreamIn->streamFormat));
+            return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+        }
+
+        if (   pStreamIn->deviceFormat.mChannelsPerFrame == 1 /* Mono */
+            && pStreamIn->streamFormat.mChannelsPerFrame == 2 /* Stereo */)
+        {
+            /*
+             * If the channel count is different we have to tell this the converter
+             * and supply a channel mapping. For now we only support mapping
+             * from mono to stereo. For all other cases the core audio defaults
+             * are used, which means dropping additional channels in most
+             * cases.
+             */
+            const SInt32 channelMap[2] = {0, 0}; /* Channel map for mono -> stereo, */
+
+            err = AudioConverterSetProperty(pStreamIn->converter, kAudioConverterChannelMap, sizeof(channelMap), channelMap);
+            if (err != noErr)
+            {
+                LogRel(("CoreAudio: Failed to set channel mapping for the audio input converter (%RI32)\n", err));
+                return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+            }
+        }
+
+        /* Set the new input format description for the stream. */
+        err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
+                                   1, &pStreamIn->deviceFormat, sizeof(pStreamIn->deviceFormat));
+        if (err != noErr)
+        {
+            LogRel(("CoreAudio: Failed to set input format for input stream (%RI32)\n", err));
+            return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+        }
+#if 0
+        /* Set sample rate converter quality to maximum */
+        uFlag = kAudioConverterQuality_Max;
+        err = AudioConverterSetProperty(pStreamIn->converter, kAudioConverterSampleRateConverterQuality,
+                                        sizeof(uFlag), &uFlag);
+        if (err != noErr)
+            LogRel(("CoreAudio: Failed to set input audio converter quality to the maximum (%RI32)\n", err));
+#endif
+        LogRel(("CoreAudio: Input converter is active\n"));
+    }
+
+    /* Set the new output format description for the stream. */
+    err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
+                               1, &pStreamIn->deviceFormat, sizeof(pStreamIn->deviceFormat));
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to set output format for input stream (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /*
+     * Also set the frame buffer size off the device on our AudioUnit. This
+     * should make sure that the frames count which we receive in the render
+     * thread is as we like.
+     */
+    err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
+                               1, &cFrames, sizeof(cFrames));
+    if (err != noErr)    {
+        LogRel(("CoreAudio: Failed to set maximum frame buffer size for input stream (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Finally initialize the new AudioUnit. */
+    err = AudioUnitInitialize(pStreamIn->audioUnit);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to initialize audio unit for input stream (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    uSize = sizeof(pStreamIn->deviceFormat);
+    err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
+                               1, &pStreamIn->deviceFormat, &uSize);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to get input device format (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /*
+     * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
+     * the frame buffer size set in the previous calls. So finally get the
+     * frame buffer size after the AudioUnit was initialized.
+     */
+    uSize = sizeof(cFrames);
+    err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice,kAudioUnitScope_Global,
+                               0, &cFrames, &uSize);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to get maximum frame buffer size from input audio device (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Calculate the ratio between the device and the stream sample rate. */
+    pStreamIn->sampleRatio = pStreamIn->streamFormat.mSampleRate / pStreamIn->deviceFormat.mSampleRate;
+
+    /* Set to zero first */
+    pStreamIn->pBuf = NULL;
+    /* Create the AudioBufferList structure with one buffer. */
+    pStreamIn->bufferList.mNumberBuffers = 1;
+    /* Initialize the buffer to nothing. */
+    pStreamIn->bufferList.mBuffers[0].mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;
+    pStreamIn->bufferList.mBuffers[0].mDataByteSize = 0;
+    pStreamIn->bufferList.mBuffers[0].mData = NULL;
+
+    /* Make sure that the ring buffer is big enough to hold the recording
+     * data. Compare the maximum frames per slice value with the frames
+     * necessary when using the converter where the sample rate could differ.
+     * The result is always multiplied by the channels per frame to get the
+     * samples count. */
+    cSamples = RT_MAX(cFrames,
+                      (cFrames * pStreamIn->deviceFormat.mBytesPerFrame * pStreamIn->sampleRatio) / pStreamIn->streamFormat.mBytesPerFrame)
+               * pStreamIn->streamFormat.mChannelsPerFrame;
+
+    int rc = VINF_SUCCESS;
+#if 0
+    if (   pHstStrmIn->cSamples != 0
+        && pHstStrmIn->cSamples != (int32_t)cSamples)
+        LogRel(("CoreAudio: Warning! After recreation, the CoreAudio ring buffer doesn't has the same size as the device buffer (%RU32 vs. %RU32).\n", cSamples, (uint32_t)pHstStrmIn->cSamples));
+
+    /* Create the internal ring buffer. */
+    RTCircBufCreate(&pStreamIn->pBuf, cSamples << pHstStrmIn->Props.cShift);
+    if (RT_VALID_PTR(pStreamIn->pBuf))
+        rc = 0;
+    else
+        LogRel(("CoreAudio: Failed to create internal ring buffer\n"));
+#endif
+
+    if (RT_FAILURE(rc))
+    {
+        if (pStreamIn->pBuf)
+            RTCircBufDestroy(pStreamIn->pBuf);
+
+        AudioUnitUninitialize(pStreamIn->audioUnit);
+        return rc;
+    }
+
+#ifdef DEBUG
+    propAdr.mSelector = kAudioDeviceProcessorOverload;
+    propAdr.mScope    = kAudioUnitScope_Global;
+    err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr,
+                                         drvHostCoreAudioRecordingAudioDevicePropertyChanged, (void *)pStreamIn);
+    if (RT_UNLIKELY(err != noErr))
+        LogRel(("CoreAudio: Failed to add the processor overload listener for input stream (%RI32)\n", err));
+#endif /* DEBUG */
+    propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
+    propAdr.mScope    = kAudioUnitScope_Global;
+    err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr,
+                                         drvHostCoreAudioRecordingAudioDevicePropertyChanged, (void *)pStreamIn);
+    /* Not fatal. */
+    if (RT_UNLIKELY(err != noErr))
+        LogRel(("CoreAudio: Failed to register sample rate changed listener for input stream (%RI32)\n", err));
+
+    ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_INIT);
+
+    if (pcSamples)
+        *pcSamples = cSamples;
+
+    LogFunc(("cSamples=%RU32\n", cSamples));
+    return VINF_SUCCESS;
+}
+
+/** @todo Eventually split up this function, as this already is huge! */
+static int drvHostCoreAudioInitOutput(PPDMAUDIOHSTSTRMOUT pHstStrmOut, uint32_t *pcSamples)
+{
+    OSStatus err = noErr;
+
+    UInt32 cSamples; /* samples count */
+
+    PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
+
+    ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_INIT);
+
+    UInt32 uSize = 0;
+    if (pStreamOut->deviceID == kAudioDeviceUnknown)
+    {
+        /* Fetch the default audio input device currently in use. */
+        AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice,
+                                               kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+        uSize = sizeof(pStreamOut->deviceID);
+        err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize,  &pStreamOut->deviceID);
+        if (err != noErr)
+        {
+            LogRel(("CoreAudio: Unable to determine default output device (%RI32)\n", err));
+            return VERR_NOT_FOUND;
+        }
+    }
+
+    /*
+     * Try to get the name of the input device and log it. It's not fatal if it fails.
+     */
+    CFStringRef strTemp;
+
+    AudioObjectPropertyAddress propAdr = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal,
+                                           kAudioObjectPropertyElementMaster };
+    uSize = sizeof(CFStringRef);
+    err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
+    if (err == noErr)
+    {
+        char *pszDevName = NULL;
+        err = drvHostCoreAudioCFStringToCString(strTemp, &pszDevName);
+        if (err == noErr)
+        {
+            CFRelease(strTemp);
+
+            /* Get the device' UUID. */
+            propAdr.mSelector = kAudioObjectPropertyName;
+            err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &strTemp);
+            if (err == noErr)
+            {
+                char *pszUID = NULL;
+                err = drvHostCoreAudioCFStringToCString(strTemp, &pszUID);
+                if (err == noErr)
+                {
+                    CFRelease(strTemp);
+                    LogRel(("CoreAudio: Using output device: %s (UID: %s)\n", pszDevName, pszUID));
+
+                    RTMemFree(pszUID);
+                }
+            }
+
+            RTMemFree(pszDevName);
+        }
+    }
+    else
+        LogRel(("CoreAudio: Unable to determine output device name (%RI32)\n", err));
+
+    /* Get the default frames buffer size, so that we can setup our internal buffers. */
+    UInt32 cFrames;
+    uSize = sizeof(cFrames);
+    propAdr.mSelector = kAudioDevicePropertyBufferFrameSize;
+    propAdr.mScope    = kAudioDevicePropertyScopeInput;
+    err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &cFrames);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to determine frame buffer size of the audio output device (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */
+    err = drvHostCoreAudioSetFrameBufferSize(pStreamOut->deviceID, false /* fInput */, cFrames, &cFrames);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to set frame buffer size for the audio output device (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    ComponentDescription cd;
+    RT_ZERO(cd);
+    cd.componentType         = kAudioUnitType_Output;
+    cd.componentSubType      = kAudioUnitSubType_HALOutput;
+    cd.componentManufacturer = kAudioUnitManufacturer_Apple;
+
+    /* Try to find the default HAL output component. */
+    Component cp = FindNextComponent(NULL, &cd);
+    if (cp == 0)
+    {
+        LogRel(("CoreAudio: Failed to find HAL output component\n")); /** @todo Return error value? */
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Open the default HAL output component. */
+    err = OpenAComponent(cp, &pStreamOut->audioUnit);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to open output component (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Switch the I/O mode for output to on. */
+    UInt32 uFlag = 1;
+    err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
+                               0, &uFlag, sizeof(uFlag));
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to disable I/O mode for output stream (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Set the default audio output device as the device for the new AudioUnit. */
+    err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
+                               0, &pStreamOut->deviceID, sizeof(pStreamOut->deviceID));
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to set current device for output stream (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /*
+     * CoreAudio will inform us on a second thread for new incoming audio data.
+     * Therefor register a callback function which will process the new data.
+     */
+    AURenderCallbackStruct cb;
+    RT_ZERO(cb);
+    cb.inputProc       = drvHostCoreAudioPlaybackCallback; /* pvUser */
+    cb.inputProcRefCon = pStreamOut;
+
+    err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
+                               0, &cb, sizeof(cb));
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to register output callback (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Fetch the current stream format of the device. */
+    uSize = sizeof(pStreamOut->deviceFormat);
+    err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
+                               0, &pStreamOut->deviceFormat, &uSize);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to get device format (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Create an AudioStreamBasicDescription based on our required audio settings. */
+    drvHostCoreAudioPCMInfoToASBDesc(&pStreamOut->streamOut.Props, &pStreamOut->streamFormat);
+
+#ifdef DEBUG
+    drvHostCoreAudioPrintASBDesc("CoreAudio: Output device", &pStreamOut->deviceFormat);
+    drvHostCoreAudioPrintASBDesc("CoreAudio: Output format", &pStreamOut->streamFormat);
+#endif /* DEBUG */
+
+    /* Set the new output format description for the stream. */
+    err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
+                               0, &pStreamOut->streamFormat, sizeof(pStreamOut->streamFormat));
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to set stream format for output stream (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    uSize = sizeof(pStreamOut->deviceFormat);
+    err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
+                               0, &pStreamOut->deviceFormat, &uSize);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to retrieve device format for output stream (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /*
+     * Also set the frame buffer size off the device on our AudioUnit. This
+     * should make sure that the frames count which we receive in the render
+     * thread is as we like.
+     */
+    err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
+                               0, &cFrames, sizeof(cFrames));
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to set maximum frame buffer size for output AudioUnit (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* Finally initialize the new AudioUnit. */
+    err = AudioUnitInitialize(pStreamOut->audioUnit);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to initialize the output audio device (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /*
+     * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor
+     * the frame buffer size set in the previous calls. So finally get the
+     * frame buffer size after the AudioUnit was initialized.
+     */
+    uSize = sizeof(cFrames);
+    err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
+                               0, &cFrames, &uSize);
+    if (err != noErr)
+    {
+        LogRel(("CoreAudio: Failed to get maximum frame buffer size from output audio device (%RI32)\n", err));
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    int rc = VINF_SUCCESS;
+#if 0
+    /* Calculate the ratio between the device and the stream sample rate. */
+    pStreamOut->sampleRatio = pStreamOut->streamFormat.mSampleRate / pStreamOut->deviceFormat.mSampleRate;
+
+    /* Set to zero first */
+    pStreamOut->pBuf = NULL;
+    /* Create the AudioBufferList structure with one buffer. */
+    pStreamOut->bufferList.mNumberBuffers = 1;
+    /* Initialize the buffer to nothing. */
+    pStreamOut->bufferList.mBuffers[0].mNumberChannels = pStreamOut->streamFormat.mChannelsPerFrame;
+    pStreamOut->bufferList.mBuffers[0].mDataByteSize = 0;
+    pStreamOut->bufferList.mBuffers[0].mData = NULL;
+
+    /* Make sure that the ring buffer is big enough to hold the recording
+     * data. Compare the maximum frames per slice value with the frames
+     * necessary when using the converter where the sample rate could differ.
+     * The result is always multiplied by the channels per frame to get the
+     * samples count. */
+    cSamples = RT_MAX(cFrames,
+                      (cFrames * pStreamOut->deviceFormat.mBytesPerFrame * pStreamOut->sampleRatio) / pStreamOut->streamFormat.mBytesPerFrame)
+               * pStreamOut->streamFormat.mChannelsPerFrame;
+
+    if (   pHstStrmIn->cSamples != 0
+        && pHstStrmIn->cSamples != (int32_t)cSamples)
+        LogRel(("CoreAudio: Warning! After recreation, the CoreAudio ring buffer doesn't has the same size as the device buffer (%RU32 vs. %RU32).\n", cSamples, (uint32_t)pHstStrmIn->cSamples));
+
+    /* Create the internal ring buffer. */
+    RTCircBufCreate(&pStreamIn->pBuf, cSamples << pHstStrmIn->Props.cShift);
+    if (RT_VALID_PTR(pStreamIn->pBuf))
+        rc = 0;
+    else
+        LogRel(("CoreAudio: Failed to create internal ring buffer\n"));
+#endif
+
+    if (RT_FAILURE(rc))
+    {
+        /*if (pStreamOut->pBuf)
+            RTCircBufDestroy(pStreamOut->pBuf);*/
+
+        AudioUnitUninitialize(pStreamOut->audioUnit);
+        return rc;
+    }
+
+#ifdef DEBUG
+    propAdr.mSelector = kAudioDeviceProcessorOverload;
+    propAdr.mScope    = kAudioUnitScope_Global;
+    err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr,
+                                         drvHostCoreAudioPlaybackAudioDevicePropertyChanged, (void *)pStreamOut);
+    if (RT_UNLIKELY(err != noErr))
+        LogRel(("CoreAudio: Failed to register processor overload listener for output stream (%RI32)\n", err));
+#endif /* DEBUG */
+    propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
+    propAdr.mScope    = kAudioUnitScope_Global;
+    err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr,
+                                         drvHostCoreAudioPlaybackAudioDevicePropertyChanged, (void *)pStreamOut);
+    /* Not fatal. */
+    if (RT_UNLIKELY(err != noErr))
+        LogRel(("CoreAudio: Failed to register sample rate changed listener for output stream (%RI32)\n", err));
+
+    /* Allocate temporary buffer. */
+    pStreamOut->cbPCMBuf = _4K; /** @todo Make this configurable. */
+    pStreamOut->pvPCMBuf = RTMemAlloc(pStreamOut->cbPCMBuf);
+    if (!pStreamOut->pvPCMBuf)
+        rc = VERR_NO_MEMORY;
+
+    if (RT_SUCCESS(rc))
+    {
+        ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_INIT);
+
+        if (pcSamples)
+            *pcSamples = cSamples;
+
+        LogFunc(("cSamples=%RU32\n", cSamples));
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioInit(PPDMIHOSTAUDIO pInterface)
+{
+    NOREF(pInterface);
+
+    LogFlowFuncEnter();
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                   uint32_t *pcSamplesCaptured)
+{
+    PPDMDRVINS pDrvIns      = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
+
+    PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pHstStrmIn;
+
+    size_t csReads = 0;
+    char *pcSrc;
+    PPDMAUDIOSAMPLE psDst;
+
+    /* Check if the audio device should be reinitialized. If so do it. */
+    if (ASMAtomicReadU32(&pStreamIn->status) == CA_STATUS_REINIT)
+        drvHostCoreAudioReinitInput(pInterface, &pStreamIn->streamIn);
+
+    if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT)
+    {
+        if (pcSamplesCaptured)
+            *pcSamplesCaptured = 0;
+        return VINF_SUCCESS;
+    }
+
+    int rc = VINF_SUCCESS;
+    uint32_t cbWrittenTotal = 0;
+
+    do
+    {
+        size_t cbBuf = audioMixBufSizeBytes(&pHstStrmIn->MixBuf);
+        size_t cbToWrite = RT_MIN(cbBuf, RTCircBufFree(pStreamIn->pBuf));
+        LogFlowFunc(("cbToWrite=%zu\n", cbToWrite));
+
+        uint32_t cWritten, cbWritten;
+        uint8_t *puBuf;
+        size_t   cbToRead;
+
+        while (cbToWrite)
+        {
+            /* Try to acquire the necessary block from the ring buffer. */
+            RTCircBufAcquireReadBlock(pStreamIn->pBuf, cbToWrite, (void **)&puBuf, &cbToRead);
+            if (!cbToRead)
+            {
+                RTCircBufReleaseReadBlock(pStreamIn->pBuf, cbToRead);
+                break;
+            }
+
+            rc = audioMixBufWriteCirc(&pHstStrmIn->MixBuf, puBuf, cbToRead, &cWritten);
+            if (RT_FAILURE(rc))
+                break;
+
+            cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
+
+            /* Release the read buffer, so it could be used for new data. */
+            RTCircBufReleaseReadBlock(pStreamIn->pBuf, cbWritten);
+
+            Assert(cbToWrite >= cbWritten);
+            cbToWrite -= cbWritten;
+            cbWrittenTotal += cbWritten;
+        }
+    }
+    while (0);
+
+    if (RT_SUCCESS(rc))
+    {
+        uint32_t cWrittenTotal = AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, cbWrittenTotal);
+        if (cWrittenTotal)
+            audioMixBufFinish(&pHstStrmIn->MixBuf, cWrittenTotal);
+
+        LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 bytes)\n", cWrittenTotal, cbWrittenTotal));
+
+        if (pcSamplesCaptured)
+            *pcSamplesCaptured = cWrittenTotal;
+    }
+
+    return rc;
+}
+
+/* Callback for getting notified when some of the properties of an audio device has changed. */
+static DECLCALLBACK(OSStatus) drvHostCoreAudioPlaybackAudioDevicePropertyChanged(AudioObjectID propertyID,
+                                                                                 UInt32 nAddresses,
+                                                                                 const AudioObjectPropertyAddress properties[],
+                                                                                 void *pvUser)
+{
+    switch (propertyID)
+    {
+#ifdef DEBUG
+        case kAudioDeviceProcessorOverload:
+        {
+            Log2(("CoreAudio: [Output] Processor overload detected!\n"));
+            break;
+        }
+#endif /* DEBUG */
+        default:
+            break;
+    }
+
+    return noErr;
+}
+
+/* Callback to feed audio output buffer. */
+static DECLCALLBACK(OSStatus) drvHostCoreAudioPlaybackCallback(void                       *pvUser,
+                                                               AudioUnitRenderActionFlags *pActionFlags,
+                                                               const AudioTimeStamp       *pAudioTS,
+                                                               UInt32                      uBusID,
+                                                               UInt32                      cFrames,
+                                                               AudioBufferList            *pBufData)
+{
+    PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pvUser;
+    PPDMAUDIOHSTSTRMOUT pHstStrmOut = &pStreamOut->streamOut;
+
+    if (ASMAtomicReadU32(&pStreamOut->status) != CA_STATUS_INIT)
+    {
+        pBufData->mBuffers[0].mDataByteSize = 0;
+        return noErr;
+    }
+
+    /* How much space is used in the ring buffer? */
+    size_t cbDataAvail = RT_MIN(RTCircBufUsed(pStreamOut->pBuf), pBufData->mBuffers[0].mDataByteSize);
+    if (!cbDataAvail)
+    {
+        pBufData->mBuffers[0].mDataByteSize = 0;
+        return noErr;
+    }
+
+    uint8_t *pbSrc = NULL;
+    size_t cbRead = 0;
+    size_t cbToRead;
+    while (cbDataAvail)
+    {
+        /* Try to acquire the necessary block from the ring buffer. */
+        RTCircBufAcquireReadBlock(pStreamOut->pBuf, cbDataAvail, (void **)&pbSrc, &cbToRead);
+
+        /* Break if nothing is used anymore. */
+        if (!cbToRead == 0)
+            break;
+
+        /* Copy the data from our ring buffer to the core audio buffer. */
+        memcpy((uint8_t *)pBufData->mBuffers[0].mData + cbRead, pbSrc, cbToRead);
+
+        /* Release the read buffer, so it could be used for new data. */
+        RTCircBufReleaseReadBlock(pStreamOut->pBuf, cbToRead);
+
+        /* Move offset. */
+        cbRead += cbToRead;
+    }
+
+    /* Write the bytes to the core audio buffer which where really written. */
+    pBufData->mBuffers[0].mDataByteSize = cbRead;
+
+    LogFlowFunc(("CoreAudio: [Output] Read %zu / %zu bytes\n", cbRead, cbDataAvail));
+
+    return noErr;
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                                 uint32_t *pcSamplesPlayed)
+{
+    PPDMDRVINS pDrvIns      = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
+
+    PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
+
+    /* Check if the audio device should be reinitialized. If so do it. */
+    if (ASMAtomicReadU32(&pStreamOut->status) == CA_STATUS_REINIT)
+        drvHostCoreAudioReinitOutput(pInterface, &pStreamOut->streamOut);
+
+    /* Not much else to do here. */
+
+    uint32_t cLive = drvAudioHstOutSamplesLive(pHstStrmOut, NULL /* pcStreamsLive */);
+    if (!cLive) /* Not samples to play? Bail out. */
+    {
+        if (pcSamplesPlayed)
+            *pcSamplesPlayed = 0;
+        return VINF_SUCCESS;
+    }
+
+    int rc = VINF_SUCCESS;
+    uint32_t cbReadTotal = 0;
+
+    do
+    {
+        size_t cbBuf = RT_MIN(audioMixBufSizeBytes(&pHstStrmOut->MixBuf), pStreamOut->cbPCMBuf);
+        size_t cbToRead = RT_MIN(cbBuf, RTCircBufFree(pStreamOut->pBuf));
+        LogFlowFunc(("cbToRead=%zu\n", cbToRead));
+
+        uint32_t cRead, cbRead;
+        uint8_t *puBuf;
+        size_t   cbToWrite;
+
+        while (cbToRead)
+        {
+            rc = audioMixBufReadCirc(&pHstStrmOut->MixBuf,
+                                     pStreamOut->pvPCMBuf, cbToRead, &cRead);
+            if (RT_FAILURE(rc))
+                break;
+
+            cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
+
+            /* Try to acquire the necessary space from the ring buffer. */
+            RTCircBufAcquireWriteBlock(pStreamOut->pBuf, cbRead, (void **)&puBuf, &cbToWrite);
+            if (!cbToWrite)
+            {
+                RTCircBufReleaseWriteBlock(pStreamOut->pBuf, cbToWrite);
+                break;
+            }
+
+            /* Transfer data into stream's own ring buffer. The playback will operate on this
+             * own ring buffer separately. */
+            Assert(cbToWrite <= cRead);
+            memcpy(puBuf, pStreamOut->pvPCMBuf, cbToWrite);
+
+            /* Release the ring buffer, so the read thread could start reading this data. */
+            RTCircBufReleaseWriteBlock(pStreamOut->pBuf, cbToWrite);
+
+            Assert(cbToRead >= cRead);
+            cbToRead -= cbRead;
+            cbReadTotal += cbRead;
+        }
+    }
+    while (0);
+
+    if (RT_SUCCESS(rc))
+    {
+        uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
+        if (cReadTotal)
+            audioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
+
+        LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes)\n", cReadTotal, cbReadTotal));
+
+        if (pcSamplesPlayed)
+            *pcSamplesPlayed = cReadTotal;
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                                    PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
+
+    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
+
+    uint32_t uStatus = ASMAtomicReadU32(&pStreamOut->status);
+    if (!(   uStatus == CA_STATUS_INIT
+          || uStatus == CA_STATUS_REINIT))
+    {
+        return VINF_SUCCESS;
+    }
+
+    int rc = VINF_SUCCESS;
+
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        {
+            /* Only start the device if it is actually stopped */
+            if (!drvHostCoreAudioIsRunning(pStreamOut->deviceID))
+            {
+                OSStatus err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
+                if (err != noErr)
+                {
+                    LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
+                    /* Keep going. */
+                }
+                RTCircBufReset(pStreamOut->pBuf);
+
+                err = AudioOutputUnitStart(pStreamOut->audioUnit);
+                if (RT_UNLIKELY(err != noErr))
+                {
+                    LogRel(("CoreAudio: Failed to start playback (%RI32)\n", err));
+                    return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+                }
+            }
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        {
+            /* Only stop the device if it is actually running */
+            if (drvHostCoreAudioIsRunning(pStreamOut->deviceID))
+            {
+                OSStatus err = AudioOutputUnitStop(pStreamOut->audioUnit);
+                if (err != noErr)
+                {
+                    LogRel(("CoreAudio: Failed to stop playback (%RI32)\n", err));
+                    return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+                }
+
+                err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
+                if (err != noErr)
+                {
+                    LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
+                    return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+                }
+            }
+            break;
+        }
+
+        default:
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                   PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pHstStrmIn;
+
+    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
+
+    uint32_t uStatus = ASMAtomicReadU32(&pStreamIn->status);
+    if (!(   uStatus == CA_STATUS_INIT
+          || uStatus == CA_STATUS_REINIT))
+    {
+        return VINF_SUCCESS;
+    }
+
+    int rc = VINF_SUCCESS;
+
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        {
+            OSStatus err;
+
+            /* Only start the device if it is actually stopped */
+            if (!drvHostCoreAudioIsRunning(pStreamIn->deviceID))
+            {
+                RTCircBufReset(pStreamIn->pBuf);
+                err = AudioOutputUnitStart(pStreamIn->audioUnit);
+            }
+
+            if (err != noErr)
+            {
+                LogRel(("CoreAudio: Failed to start recording (%RI32)\n", err));
+                rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
+            }
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        {
+            /* Only stop the device if it is actually running */
+            if (drvHostCoreAudioIsRunning(pStreamIn->deviceID))
+            {
+                OSStatus err = AudioOutputUnitStop(pStreamIn->audioUnit);
+                if (RT_UNLIKELY(err != noErr))
+                {
+                    LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));
+                    return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+                }
+
+                err = AudioUnitReset(pStreamIn->audioUnit, kAudioUnitScope_Input, 0);
+                if (RT_UNLIKELY(err != noErr))
+                {
+                    LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
+                    return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+                }
+            }
+            break;
+        }
+
+        default:
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    int rc = 0;
+    OSStatus err = noErr;
+    uint32_t status;
+
+    PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN) pHstStrmIn;
+
+    PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTCOREAUDIO  pThis   = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
+
+    LogFlow(("drvHostCoreAudioFiniIn \n"));
+
+    status = ASMAtomicReadU32(&pStreamIn->status);
+    if (!(   status == CA_STATUS_INIT
+          || status == CA_STATUS_REINIT))
+    {
+        return VINF_SUCCESS;
+    }
+
+    rc = drvHostCoreAudioControlIn(pInterface, &pStreamIn->streamIn, PDMAUDIOSTREAMCMD_DISABLE);
+    if (RT_SUCCESS(rc))
+    {
+        ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_UNINIT);
+        AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioUnitScope_Global,
+                                               kAudioObjectPropertyElementMaster };
+#ifdef DEBUG
+        err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
+                                                drvHostCoreAudioRecordingAudioDevicePropertyChanged, NULL);
+        /* Not Fatal */
+        if (RT_UNLIKELY(err != noErr))
+            LogRel(("CoreAudio: Failed to remove the processor overload listener (%RI32)\n", err));
+#endif /* DEBUG */
+        propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;
+        err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
+                                                drvHostCoreAudioRecordingAudioDevicePropertyChanged, NULL);
+        /* Not Fatal */
+        if (RT_UNLIKELY(err != noErr))
+            LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err));
+        if (pStreamIn->converter)
+        {
+            AudioConverterDispose(pStreamIn->converter);
+            pStreamIn->converter = NULL;
+        }
+        err = AudioUnitUninitialize(pStreamIn->audioUnit);
+        if (RT_LIKELY(err == noErr))
+        {
+            err = CloseComponent(pStreamIn->audioUnit);
+            if (RT_LIKELY(err == noErr))
+            {
+                RTCircBufDestroy(pStreamIn->pBuf);
+                pStreamIn->audioUnit   = NULL;
+                pStreamIn->deviceID    = kAudioDeviceUnknown;
+                pStreamIn->pBuf        = NULL;
+                pStreamIn->sampleRatio = 1;
+                pStreamIn->rpos        = 0;
+                ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT);
+            }
+            else
+                LogRel(("CoreAudio: Failed to close the AudioUnit (%RI32)\n", err));
+        }
+        else
+            LogRel(("CoreAudio: Failed to uninitialize the AudioUnit (%RI32)\n", err));
+    }
+    else
+        LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    PPDMDRVINS pDrvIns      = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
+
+    PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
+
+    LogFlowFuncEnter();
+
+    uint32_t status = ASMAtomicReadU32(&pStreamOut->status);
+    if (!(   status == CA_STATUS_INIT
+          || status == CA_STATUS_REINIT))
+    {
+        return VINF_SUCCESS;
+    }
+
+    int rc = drvHostCoreAudioControlOut(pInterface, &pStreamOut->streamOut, PDMAUDIOSTREAMCMD_DISABLE);
+    if (RT_SUCCESS(rc))
+    {
+        ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_UNINIT);
+#if 0
+        err = AudioDeviceRemovePropertyListener(pStreamOut->audioDeviceId, 0, false,
+                                                kAudioDeviceProcessorOverload, drvHostCoreAudioPlaybackAudioDevicePropertyChanged);
+        /* Not Fatal */
+        if (RT_UNLIKELY(err != noErr))
+            LogRel(("CoreAudio: Failed to remove the processor overload listener (%RI32)\n", err));
+#endif /* DEBUG */
+        OSStatus err = AudioUnitUninitialize(pStreamOut->audioUnit);
+        if (err == noErr)
+        {
+            err = CloseComponent(pStreamOut->audioUnit);
+            if (err == noErr)
+            {
+                RTCircBufDestroy(pStreamOut->pBuf);
+                pStreamOut->pBuf      = NULL;
+
+                pStreamOut->audioUnit = NULL;
+                pStreamOut->deviceID  = kAudioDeviceUnknown;
+
+                if (pStreamOut->pvPCMBuf)
+                {
+                    RTMemFree(pStreamOut->pvPCMBuf);
+                    pStreamOut->pvPCMBuf = NULL;
+                    pStreamOut->cbPCMBuf = 0;
+                }
+
+                ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_UNINIT);
+            }
+            else
+                LogRel(("CoreAudio: Failed to close the AudioUnit (%RI32)\n", err));
+        }
+        else
+            LogRel(("CoreAudio: Failed to uninitialize the AudioUnit (%RI32)\n", err));
+    }
+    else
+        LogRel(("CoreAudio: Failed to stop playback, rc=%Rrc\n", rc));
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioInitIn(PPDMIHOSTAUDIO pInterface,
+                                                PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
+                                                PDMAUDIORECSOURCE enmRecSource,
+                                                uint32_t *pcSamples)
+{
+    OSStatus err = noErr;
+    int rc = -1;
+    bool fDeviceByUser = false;
+
+    PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pHstStrmIn;
+
+    LogFlow(("drvHostCoreAudioInitIn \n"));
+
+    ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT);
+    pStreamIn->audioUnit = NULL;
+    pStreamIn->deviceID = kAudioDeviceUnknown;
+    pStreamIn->converter = NULL;
+    pStreamIn->sampleRatio = 1;
+    pStreamIn->rpos = 0;
+
+    LogFlow(("drvHostCoreAudioInitIn \n"));
+    /* Initialize the hardware info section with the audio settings */
+    drvAudioStreamCfgToProps(pCfg, &pStreamIn->streamIn.Props);
+
+#if 0
+    /* Try to find the audio device set by the user */
+    if (DeviceUID.pszInputDeviceUID)
+    {
+        pStreamIn->deviceID = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszInputDeviceUID);
+        /* Not fatal */
+        if (pStreamIn->deviceID == kAudioDeviceUnknown)
+            LogRel(("CoreAudio: Unable to find input device %s. Falling back to the default audio device. \n", DeviceUID.pszInputDeviceUID));
+        else
+            fDeviceByUser = true;
+    }
+#endif
+
+    rc = drvHostCoreAudioInitInput(&pStreamIn->streamIn, pcSamples);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /* When the devices isn't forced by the user, we want default device change notifications. */
+    if (!fDeviceByUser)
+    {
+        AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
+                                               kAudioObjectPropertyElementMaster };
+        err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
+                                             drvHostCoreAudioDefaultDeviceChanged, (void *)pStreamIn);
+        /* Not fatal. */
+        if (err != noErr)
+            LogRel(("CoreAudio: Failed to register the default input device changed listener (%RI32)\n", err));
+    }
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioInitOut(PPDMIHOSTAUDIO pInterface,
+                                                 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
+                                                 uint32_t *pcSamples)
+{
+    PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
+
+    OSStatus err = noErr;
+    int rc = 0;
+    bool fDeviceByUser = false; /* use we a device which was set by the user? */
+
+    LogFlow(("drvHostCoreAudioInitOut\n"));
+
+    ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_UNINIT);
+
+    pStreamOut->audioUnit = NULL;
+    pStreamOut->deviceID = kAudioDeviceUnknown;
+
+    /* Initialize the hardware info section with the audio settings */
+    drvAudioStreamCfgToProps(pCfg, &pStreamOut->streamOut.Props);
+
+#if 0
+    /* Try to find the audio device set by the user. Use
+     * export VBOX_COREAUDIO_OUTPUT_DEVICE_UID=AppleHDAEngineOutput:0
+     * to set it. */
+    if (DeviceUID.pszOutputDeviceUID)
+    {
+        pStreamOut->audioDeviceId = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszOutputDeviceUID);
+        /* Not fatal */
+        if (pStreamOut->audioDeviceId == kAudioDeviceUnknown)
+            LogRel(("CoreAudio: Unable to find output device %s. Falling back to the default audio device. \n", DeviceUID.pszOutputDeviceUID));
+        else
+            fDeviceByUser = true;
+    }
+#endif
+
+    rc = drvHostCoreAudioInitOutput(pHstStrmOut, pcSamples);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /* When the devices isn't forced by the user, we want default device change notifications. */
+    if (!fDeviceByUser)
+    {
+        AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
+                                               kAudioObjectPropertyElementMaster };
+        err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
+                                             drvHostCoreAudioDefaultDeviceChanged, (void *)pStreamOut);
+        /* Not fatal. */
+        if (err != noErr)
+            LogRel(("CoreAudio: Failed to register default device changed listener (%RI32)\n", err));
+    }
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(bool) drvHostCoreAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
+{
+    NOREF(pInterface);
+    NOREF(enmDir);
+    return true; /* Always all enabled. */
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pAudioConf)
+{
+    pAudioConf->cbStreamOut     = sizeof(COREAUDIOSTREAMOUT);
+    pAudioConf->cbStreamIn      = sizeof(COREAUDIOSTREAMIN);
+    pAudioConf->cMaxHstStrmsOut = 1;
+    pAudioConf->cMaxHstStrmsIn  = 2;
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(void *) drvHostCoreAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTCOREAUDIO  pThis   = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
+
+    return NULL;
+}
+
+static DECLCALLBACK(void) drvHostCoreAudioDestruct(PPDMDRVINS pDrvIns)
+{
+}
+
+ /* Construct a DirectSound Audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvHostCoreAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+    PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
+    LogRel(("Audio: Initializing Core Audio driver\n"));
+
+    /*
+     * Init the static parts.
+     */
+    pThis->pDrvIns                   = pDrvIns;
+    /* IBase */
+    pDrvIns->IBase.pfnQueryInterface = drvHostCoreAudioQueryInterface;
+    /* IHostAudio */
+    PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostCoreAudio);
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Char driver registration record.
+ */
+const PDMDRVREG g_DrvHostCoreAudio =
+{
+    /* u32Version */
+    PDM_DRVREG_VERSION,
+    /* szName */
+    "CoreAudio",
+    /* szRCMod */
+    "",
+    /* szR0Mod */
+    "",
+    /* pszDescription */
+    "Core Audio host driver",
+    /* fFlags */
+     PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+    /* fClass. */
+    PDM_DRVREG_CLASS_AUDIO,
+    /* cMaxInstances */
+    ~0U,
+    /* cbInstance */
+    sizeof(DRVHOSTCOREAUDIO),
+    /* pfnConstruct */
+    drvHostCoreAudioConstruct,
+    /* pfnDestruct */
+    drvHostCoreAudioDestruct,
+    /* pfnRelocate */
+    NULL,
+    /* pfnIOCtl */
+    NULL,
+    /* pfnPowerOn */
+    NULL,
+    /* pfnReset */
+    NULL,
+    /* pfnSuspend */
+    NULL,
+    /* pfnResume */
+    NULL,
+    /* pfnAttach */
+    NULL,
+    /* pfnDetach */
+    NULL,
+    /* pfnPowerOff */
+    NULL,
+    /* pfnSoftReset */
+    NULL,
+    /* u32EndVersion */
+    PDM_DRVREG_VERSION
+};
+
Index: /trunk/src/VBox/Devices/Audio/DrvHostDSound.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DrvHostDSound.cpp	(revision 54433)
+++ /trunk/src/VBox/Devices/Audio/DrvHostDSound.cpp	(revision 54433)
@@ -0,0 +1,1699 @@
+/* $Id$ */
+/** @file
+ * Windows host backend driver using DirectSound.
+ */
+
+/*
+ * Copyright (C) 2006-2015 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ * --------------------------------------------------------------------
+ *
+ * This code is based on: dsoundaudio.c
+ *
+ * QEMU DirectSound audio driver
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <dsound.h>
+
+#include <iprt/alloc.h>
+#include <iprt/uuid.h>
+
+#include "AudioMixBuffer.h"
+#include "DrvAudio.h"
+#include "VBoxDD.h"
+
+#ifdef LOG_GROUP
+# undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_DEV_AUDIO
+#include <VBox/log.h>
+
+#define DSLOG(a) do { LogRel2(a); } while(0)
+#define DSLOGREL(a)                 \
+    do {                            \
+        static int8_t scLogged = 0; \
+        if (scLogged < 8) {         \
+            ++scLogged;             \
+            LogRel(a);              \
+        }                           \
+        else {                      \
+            DSLOG(a);               \
+        }                           \
+    } while (0)
+
+typedef struct DSOUNDHOSTCFG
+{
+    int     cbBufferIn;
+    int     cbBufferOut;
+    RTUUID  uuidPlay;
+    LPCGUID pGuidPlay;
+    RTUUID  uuidCapture;
+    LPCGUID pGuidCapture;
+} DSOUNDHOSTCFG, *PDSOUNDHOSTCFG;
+
+typedef struct DRVHOSTDSOUND
+{
+    /** Pointer to the driver instance structure. */
+    PPDMDRVINS    pDrvIns;
+    /** Pointer to host audio interface. */
+    PDMIHOSTAUDIO IHostAudio;
+    /** List of found host input devices. */
+    RTLISTANCHOR  lstDevInput;
+    /** List of found host output devices. */
+    RTLISTANCHOR  lstDevOutput;
+    /** Configuration options. */
+    DSOUNDHOSTCFG cfg;
+} DRVHOSTDSOUND, *PDRVHOSTDSOUND;
+
+typedef struct DSOUNDSTREAMOUT
+{
+    PDMAUDIOHSTSTRMOUT  hw; /* Always must come first! */
+    LPDIRECTSOUND       pDS;
+    LPDIRECTSOUNDBUFFER pDSB;
+    DWORD               cbPlayWritePos;
+    DWORD               csPlaybackBufferSize;
+    bool                fReinitPlayPos;
+    PDMAUDIOSTREAMCFG   streamCfg;
+} DSOUNDSTREAMOUT, *PDSOUNDSTREAMOUT;
+
+typedef struct DSOUNDSTREAMIN
+{
+    PDMAUDIOHSTSTRMIN          hw; /* Always must come first! */
+    LPDIRECTSOUNDCAPTURE       pDSC;
+    LPDIRECTSOUNDCAPTUREBUFFER pDSCB;
+    DWORD                      csCaptureReadPos;
+    DWORD                      csCaptureBufferSize;
+    HRESULT                    hrLastCaptureIn;
+    PDMAUDIORECSOURCE          enmRecSource;
+    PDMAUDIOSTREAMCFG          streamCfg;
+} DSOUNDSTREAMIN, *PDSOUNDSTREAMIN;
+
+/**
+ * Callback context for enumeration callbacks
+ */
+typedef struct DSOUNDENUMCBCTX
+{
+    PDRVHOSTDSOUND      pDrv;
+    PPDMAUDIOBACKENDCFG pCfg;
+} DSOUNDENUMCBCTX, *PDSOUNDENUMCBCTX;
+
+typedef struct DSOUNDDEV
+{
+    RTLISTNODE  Node;
+    char       *pszName;
+    GUID        Guid;
+} DSOUNDDEV, *PDSOUNDDEV;
+
+/** Makes DRVHOSTDSOUND out of PDMIHOSTAUDIO. */
+#define PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface) \
+    ( (PDRVHOSTDSOUND)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTDSOUND, IHostAudio)) )
+
+static void dsoundDevRemove(PDSOUNDDEV pDev);
+
+static size_t dsoundRingDistance(size_t offEnd, size_t offBegin, size_t cSize)
+{
+    return offEnd >= offBegin ? offEnd - offBegin: cSize - offBegin + offEnd;
+}
+
+static int dsoundWaveFmtFromCfg(PPDMAUDIOSTREAMCFG pCfg, PWAVEFORMATEX pFmt)
+{
+    RT_BZERO(pFmt, sizeof(WAVEFORMATEX));
+    pFmt->wFormatTag      = WAVE_FORMAT_PCM;
+    pFmt->nChannels       = pCfg->cChannels;
+    pFmt->nSamplesPerSec  = pCfg->uHz;
+    pFmt->nAvgBytesPerSec = pCfg->uHz << (pCfg->cChannels == 2 ? 1: 0);
+    pFmt->nBlockAlign     = 1 << (pCfg->cChannels == 2 ? 1: 0);
+    pFmt->cbSize          = 0; /* No extra data specified. */
+
+    switch (pCfg->enmFormat)
+    {
+        case AUD_FMT_S8:
+        case AUD_FMT_U8:
+            pFmt->wBitsPerSample = 8;
+            break;
+
+        case AUD_FMT_S16:
+        case AUD_FMT_U16:
+            pFmt->wBitsPerSample = 16;
+            pFmt->nAvgBytesPerSec <<= 1;
+            pFmt->nBlockAlign <<= 1;
+            break;
+
+        case AUD_FMT_S32:
+        case AUD_FMT_U32:
+            pFmt->wBitsPerSample = 32;
+            pFmt->nAvgBytesPerSec <<= 2;
+            pFmt->nBlockAlign <<= 2;
+            break;
+
+        default:
+            AssertMsgFailed(("Wave format %ld not supported\n", pCfg->enmFormat));
+            return VERR_NOT_SUPPORTED;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static void dsoundFreeDeviceLists(PDRVHOSTDSOUND pThis)
+{
+    PDSOUNDDEV pDev;
+    while (!RTListIsEmpty(&pThis->lstDevInput))
+    {
+        pDev = RTListGetFirst(&pThis->lstDevInput, DSOUNDDEV, Node);
+        dsoundDevRemove(pDev);
+    }
+
+    while (!RTListIsEmpty(&pThis->lstDevOutput))
+    {
+        pDev = RTListGetFirst(&pThis->lstDevOutput, DSOUNDDEV, Node);
+        dsoundDevRemove(pDev);
+    }
+}
+
+static int dsoundPlayRestore(LPDIRECTSOUNDBUFFER pDSB)
+{
+    HRESULT hr = IDirectSoundBuffer_Restore(pDSB);
+    if (SUCCEEDED(hr))
+        return VINF_SUCCESS;
+
+    DSLOGREL(("DSound: restore playback buffer %Rhrc\n", hr));
+    return VERR_INVALID_STATE;
+}
+
+static int dsoundUnlockOutput(LPDIRECTSOUNDBUFFER pDSB,
+                              LPVOID pv1, LPVOID pv2,
+                              DWORD cb1, DWORD cb2)
+{
+    HRESULT hr = IDirectSoundBuffer_Unlock(pDSB, pv1, cb1, pv2, cb2);
+    if (SUCCEEDED(hr))
+        return VINF_SUCCESS;
+
+    DSLOG(("DSound: Unable to unlock output buffer, hr=%Rhrc\n", hr));
+    return VERR_ACCESS_DENIED;
+}
+
+static int dsoundUnlockInput(LPDIRECTSOUNDCAPTUREBUFFER pDSCB,
+                             LPVOID pv1, LPVOID pv2,
+                             DWORD cb1, DWORD cb2)
+{
+    HRESULT hr = IDirectSoundCaptureBuffer_Unlock(pDSCB, pv1, cb1, pv2, cb2);
+    if (SUCCEEDED(hr))
+        return VINF_SUCCESS;
+
+    DSLOG(("DSound: Unable to unlock input buffer, hr=%Rhrc\n", hr));
+    return VERR_ACCESS_DENIED;
+}
+
+static int dsoundLockOutput(LPDIRECTSOUNDBUFFER pDSB, PDMPCMPROPS *pProps,
+                            DWORD dwOffset, DWORD dwBytes,
+                            LPVOID *ppv1, LPVOID *ppv2,
+                            DWORD *pcb1, DWORD *pcb2,
+                            DWORD dwFlags)
+{
+    int rc = VINF_SUCCESS;
+
+    LPVOID pv1 = NULL;
+    LPVOID pv2 = NULL;
+    DWORD cb1 = 0;
+    DWORD cb2 = 0;
+
+    HRESULT hr = IDirectSoundBuffer_Lock(pDSB, dwOffset, dwBytes, &pv1, &cb1, &pv2, &cb2, dwFlags);
+    if (hr == DSERR_BUFFERLOST)
+    {
+        rc = dsoundPlayRestore(pDSB);
+        if (RT_SUCCESS(rc))
+        {
+            hr = IDirectSoundBuffer_Lock(pDSB, dwOffset, dwBytes, &pv1, &cb1, &pv2, &cb2, dwFlags);
+        }
+    }
+
+    if (FAILED(hr) || RT_FAILURE(rc))
+    {
+        DSLOG(("DSound: Unable to lock output buffer, hr=%Rhrc\n", hr));
+        return RT_SUCCESS(rc) ? VERR_ACCESS_DENIED: rc;
+    }
+
+    if (   (pv1 && (cb1 & pProps->uAlign))
+        || (pv2 && (cb2 & pProps->uAlign)))
+    {
+        DSLOG(("DSound: lock output returned misaligned buffer: cb1=%d, cb2=%d\n", cb1, cb2));
+        dsoundUnlockOutput(pDSB, pv1, pv2, cb1, cb2);
+        return VERR_INVALID_STATE;
+    }
+
+    *ppv1 = pv1;
+    *ppv2 = pv2;
+    *pcb1 = cb1;
+    *pcb2 = cb2;
+
+    return VINF_SUCCESS;
+}
+
+static int dsoundLockInput(LPDIRECTSOUNDCAPTUREBUFFER pDSCB, PPDMPCMPROPS pProps,
+                           DWORD dwOffset, DWORD dwBytes,
+                           LPVOID *ppv1, LPVOID *ppv2,
+                           DWORD *pcb1, DWORD *pcb2,
+                           DWORD dwFlags)
+{
+    LPVOID pv1 = NULL;
+    LPVOID pv2 = NULL;
+    DWORD cb1 = 0;
+    DWORD cb2 = 0;
+
+    HRESULT hr = IDirectSoundCaptureBuffer_Lock(pDSCB, dwOffset, dwBytes,
+                                            &pv1, &cb1, &pv2, &cb2, dwFlags);
+    if (FAILED(hr))
+    {
+        DSLOG(("DSound: Unable to lock capturing buffer, hr=%Rhrc\n", hr));
+        return VERR_ACCESS_DENIED;
+    }
+
+    if (   (pv1 && (cb1 & pProps->uAlign))
+        || (pv2 && (cb2 & pProps->uAlign)))
+    {
+        DSLOG(("DSound: lock input returned misaligned buffer: cb1=%d, cb2=%d\n", cb1, cb2));
+        dsoundUnlockInput(pDSCB, pv1, pv2, cb1, cb2);
+        return VERR_INVALID_PARAMETER;
+    }
+
+    *ppv1 = pv1;
+    *ppv2 = pv2;
+    *pcb1 = cb1;
+    *pcb2 = cb2;
+
+    return VINF_SUCCESS;
+}
+
+
+/*
+ * DirectSound playback
+ */
+
+static void dsoundPlayInterfaceRelease(PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    if (pDSoundStrmOut->pDS)
+    {
+        IDirectSound_Release(pDSoundStrmOut->pDS);
+        pDSoundStrmOut->pDS = NULL;
+    }
+}
+
+static int dsoundPlayInterfaceCreate(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    if (pDSoundStrmOut->pDS != NULL)
+    {
+        DSLOG(("DSound: DirectSound instance already exists\n"));
+        return VINF_SUCCESS;
+    }
+
+    HRESULT hr = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_ALL,
+                                  IID_IDirectSound, (void **)&pDSoundStrmOut->pDS);
+    if (FAILED(hr))
+    {
+        DSLOGREL(("DSound: DirectSound create instance %Rhrc\n", hr));
+    }
+    else
+    {
+        hr = IDirectSound_Initialize(pDSoundStrmOut->pDS, pThis->cfg.pGuidPlay);
+        if (SUCCEEDED(hr))
+        {
+            HWND hwnd = GetDesktopWindow();
+            hr = IDirectSound_SetCooperativeLevel(pDSoundStrmOut->pDS, hwnd, DSSCL_PRIORITY);
+            if (FAILED (hr))
+            {
+                DSLOGREL(("DSound: set cooperative level for window %p %Rhrc\n", hwnd, hr));
+            }
+        }
+        if (FAILED(hr))
+        {
+            if (hr == DSERR_NODRIVER)
+            {
+                DSLOGREL(("DSound: DirectSound playback is currently unavailable\n"));
+            }
+            else
+            {
+                DSLOGREL(("DSound: DirectSound initialize %Rhrc\n", hr));
+            }
+            dsoundPlayInterfaceRelease(pDSoundStrmOut);
+        }
+    }
+
+    return SUCCEEDED(hr) ? VINF_SUCCESS: VERR_NOT_SUPPORTED;
+}
+
+static void dsoundPlayClose(PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    DSLOG(("DSound: playback close %p buffer %p\n", pDSoundStrmOut, pDSoundStrmOut->pDSB));
+
+    if (pDSoundStrmOut->pDSB)
+    {
+        HRESULT hr = IDirectSoundBuffer_Stop(pDSoundStrmOut->pDSB);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: playback close Stop %Rhrc\n", hr));
+        }
+
+        IDirectSoundBuffer_Release(pDSoundStrmOut->pDSB);
+        pDSoundStrmOut->pDSB = NULL;
+    }
+
+    dsoundPlayInterfaceRelease(pDSoundStrmOut);
+}
+
+static int dsoundPlayOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    DSLOG(("DSound: playback open %p size %d bytes, freq %d, chan %d, bits %d, sign %d\n",
+           pDSoundStrmOut,
+           pThis->cfg.cbBufferOut,
+           pDSoundStrmOut->hw.Props.uHz,
+           pDSoundStrmOut->hw.Props.cChannels,
+           pDSoundStrmOut->hw.Props.cBits,
+           pDSoundStrmOut->hw.Props.fSigned));
+
+    if (pDSoundStrmOut->pDSB != NULL)
+    {
+        /* Should not happen but be forgiving. */
+        DSLOGREL(("DSound: DirectSoundBuffer already exists\n"));
+        dsoundPlayClose(pDSoundStrmOut);
+    }
+
+    WAVEFORMATEX wfx;
+    int rc = dsoundWaveFmtFromCfg(&pDSoundStrmOut->streamCfg, &wfx);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = dsoundPlayInterfaceCreate(pThis, pDSoundStrmOut);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    HRESULT hr = S_OK;
+
+    do /* To use breaks. */
+    {
+        DSBUFFERDESC bd;
+        RT_ZERO(bd);
+        bd.dwSize = sizeof(bd);
+        bd.lpwfxFormat = &wfx;
+        bd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
+        bd.dwBufferBytes = pThis->cfg.cbBufferOut;
+        hr = IDirectSound_CreateSoundBuffer(pDSoundStrmOut->pDS,
+                                            &bd, &pDSoundStrmOut->pDSB, NULL);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: playback CreateSoundBuffer %Rhrc\n", hr));
+            break;
+        }
+
+        /* Query the actual parameters. */
+
+        hr = IDirectSoundBuffer_GetFormat(pDSoundStrmOut->pDSB, &wfx, sizeof(wfx), NULL);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: playback GetFormat %Rhrc\n", hr));
+            break;
+        }
+
+        DSBCAPS bc;
+        RT_ZERO(bc);
+        bc.dwSize = sizeof(bc);
+        hr = IDirectSoundBuffer_GetCaps(pDSoundStrmOut->pDSB, &bc);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: playback GetCaps %Rhrc\n", hr));
+            break;
+        }
+
+        DSLOG(("DSound: playback format: size %d bytes\n"
+                "  tag             = %d\n"
+                "  nChannels       = %d\n"
+                "  nSamplesPerSec  = %d\n"
+                "  nAvgBytesPerSec = %d\n"
+                "  nBlockAlign     = %d\n"
+                "  wBitsPerSample  = %d\n"
+                "  cbSize          = %d\n",
+                bc.dwBufferBytes,
+                wfx.wFormatTag,
+                wfx.nChannels,
+                wfx.nSamplesPerSec,
+                wfx.nAvgBytesPerSec,
+                wfx.nBlockAlign,
+                wfx.wBitsPerSample,
+                wfx.cbSize));
+
+        if (bc.dwBufferBytes & pDSoundStrmOut->hw.Props.uAlign)
+        {
+            DSLOGREL(("DSound: playback GetCaps returned misaligned buffer size %ld, alignment %d\n",
+                      bc.dwBufferBytes, pDSoundStrmOut->hw.Props.uAlign + 1));
+        }
+
+        if (bc.dwBufferBytes != pThis->cfg.cbBufferOut)
+        {
+            DSLOGREL(("DSound: playback buffer size mismatch dsound %d, requested %d bytes\n",
+                      bc.dwBufferBytes, pThis->cfg.cbBufferOut));
+        }
+
+        /* Initial state.
+         * dsoundPlayStart initializes part of it to make sure that Stop/Start continues with a correct
+         * playback buffer position.
+         */
+        pDSoundStrmOut->csPlaybackBufferSize = bc.dwBufferBytes >> pDSoundStrmOut->hw.Props.cShift;
+        DSLOG(("DSound: playback open csPlaybackBufferSize %d samples\n", pDSoundStrmOut->csPlaybackBufferSize));
+    } while (0);
+
+    if (SUCCEEDED(hr))
+        return VINF_SUCCESS;
+
+    dsoundPlayClose(pDSoundStrmOut);
+    return VERR_NOT_SUPPORTED;
+}
+
+static void dsoundPlayClearSamples(PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    LPVOID pv1, pv2;
+    DWORD cb1, cb2;
+    int rc = dsoundLockOutput(pDSoundStrmOut->pDSB, &pDSoundStrmOut->hw.Props,
+                              0, pDSoundStrmOut->csPlaybackBufferSize << pDSoundStrmOut->hw.Props.cShift,
+                              &pv1, &pv2, &cb1, &cb2, DSBLOCK_ENTIREBUFFER);
+    if (RT_SUCCESS(rc))
+    {
+        int len1 = cb1 >> pDSoundStrmOut->hw.Props.cShift;
+        int len2 = cb2 >> pDSoundStrmOut->hw.Props.cShift;
+
+        if (pv1 && len1)
+            audio_pcm_info_clear_buf(&pDSoundStrmOut->hw.Props, pv1, len1);
+
+        if (pv2 && len2)
+            audio_pcm_info_clear_buf(&pDSoundStrmOut->hw.Props, pv2, len2);
+
+        dsoundUnlockOutput(pDSoundStrmOut->pDSB, pv1, pv2, cb1, cb2);
+    }
+}
+
+static int dsoundPlayGetStatus(LPDIRECTSOUNDBUFFER pDSB, DWORD *pStatus)
+{
+    int rc = VINF_SUCCESS;
+
+    DWORD dwStatus = 0;
+    HRESULT hr = IDirectSoundBuffer_GetStatus(pDSB, &dwStatus);
+    if (SUCCEEDED(hr))
+    {
+        if ((dwStatus & DSBSTATUS_BUFFERLOST) != 0)
+        {
+            rc = dsoundPlayRestore(pDSB);
+            if (RT_SUCCESS(rc))
+            {
+                hr = IDirectSoundBuffer_GetStatus(pDSB, &dwStatus);
+            }
+        }
+    }
+
+    if (FAILED(hr))
+    {
+        DSLOG(("DSound: playback GetStatus %Rhrc\n", hr));
+        if (RT_SUCCESS(rc))
+        {
+            rc = VERR_NOT_SUPPORTED;
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        *pStatus = dwStatus;
+    }
+
+    return rc;
+}
+
+static void dsoundPlayStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    if (pDSoundStrmOut->pDSB != NULL)
+    {
+        DWORD dwStatus;
+        /* This performs some restore, so call it anyway and ignore result. */
+        dsoundPlayGetStatus(pDSoundStrmOut->pDSB, &dwStatus);
+
+        DSLOG(("DSound: playback stop\n"));
+
+        HRESULT hr = IDirectSoundBuffer_Stop(pDSoundStrmOut->pDSB);
+        if (FAILED(hr))
+        {
+            DSLOG(("DSound: stop playback buffer %Rhrc\n", hr));
+        }
+    }
+}
+
+static int dsoundPlayStart(PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    int rc = VINF_SUCCESS;
+
+    if (pDSoundStrmOut->pDSB != NULL)
+    {
+        DWORD dwStatus;
+        int rc = dsoundPlayGetStatus(pDSoundStrmOut->pDSB, &dwStatus);
+        if (RT_FAILURE(rc))
+        {
+            DSLOG(("DSound: playback start GetStatus %Rrc\n", rc));
+        }
+        else
+        {
+            if (dwStatus & DSBSTATUS_PLAYING)
+            {
+                DSLOG(("DSound: already playing\n"));
+            }
+            else
+            {
+                dsoundPlayClearSamples(pDSoundStrmOut);
+
+                pDSoundStrmOut->fReinitPlayPos = true;
+
+                DSLOG(("DSound: playback start\n"));
+
+                HRESULT hr = IDirectSoundBuffer_Play(pDSoundStrmOut->pDSB, 0, 0, DSBPLAY_LOOPING);
+                if (FAILED(hr))
+                {
+                    DSLOGREL(("DSound: playback start %Rhrc\n", hr));
+                    rc = VERR_NOT_SUPPORTED;
+                }
+            }
+        }
+    }
+    else
+    {
+        rc = VERR_INVALID_STATE;
+    }
+
+    return rc;
+}
+
+/*
+ * DirectSoundCapture
+ */
+
+static LPCGUID dsoundCaptureSelectDevice(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    LPCGUID pGUID = pThis->cfg.pGuidCapture;
+
+    if (!pGUID)
+    {
+        PDSOUNDDEV pDev = NULL;
+
+        switch (pDSoundStrmIn->enmRecSource)
+        {
+            case PDMAUDIORECSOURCE_MIC:
+            {
+                RTListForEach(&pThis->lstDevInput, pDev, DSOUNDDEV, Node)
+                {
+                    if (RTStrIStr(pDev->pszName, "Mic") == 0) /** @todo what is with non en_us windows versions? */
+                        break;
+                }
+            } break;
+
+            case PDMAUDIORECSOURCE_LINE_IN:
+            default:
+                /* Try opening the default device (NULL). */
+                break;
+        }
+
+        if (pDev)
+        {
+            DSLOG(("DSound: Guest \"%s\" is using host \"%s\"\n",
+                   drvAudioRecSourceToString(pDSoundStrmIn->enmRecSource), pDev->pszName));
+
+            pGUID = &pDev->Guid;
+        }
+    }
+
+    return pGUID;
+}
+
+static void dsoundCaptureInterfaceRelease(PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    if (pDSoundStrmIn->pDSC)
+    {
+        IDirectSoundCapture_Release(pDSoundStrmIn->pDSC);
+        pDSoundStrmIn->pDSC = NULL;
+    }
+}
+
+static int dsoundCaptureInterfaceCreate(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    if (pDSoundStrmIn->pDSC != NULL)
+    {
+        DSLOG(("DSound: DirectSoundCapture instance already exists\n"));
+        return VINF_SUCCESS;
+    }
+
+    HRESULT hr = CoCreateInstance(CLSID_DirectSoundCapture, NULL, CLSCTX_ALL,
+                                  IID_IDirectSoundCapture, (void **)&pDSoundStrmIn->pDSC);
+    if (FAILED(hr))
+    {
+        DSLOGREL(("DSound: DirectSoundCapture create instance %Rhrc\n", hr));
+    }
+    else
+    {
+        LPCGUID pGUID = dsoundCaptureSelectDevice(pThis, pDSoundStrmIn);
+        hr = IDirectSoundCapture_Initialize(pDSoundStrmIn->pDSC, pGUID);
+        if (FAILED(hr))
+        {
+            if (hr == DSERR_NODRIVER)
+            {
+                DSLOGREL(("DSound: DirectSound capture is currently unavailable\n"));
+            }
+            else
+            {
+                DSLOGREL(("DSound: DirectSoundCapture initialize %Rhrc\n", hr));
+            }
+            dsoundCaptureInterfaceRelease(pDSoundStrmIn);
+        }
+    }
+
+    return SUCCEEDED(hr) ? VINF_SUCCESS: VERR_NOT_SUPPORTED;
+}
+
+static void dsoundCaptureClose(PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    DSLOG(("DSound: capture close %p buffer %p\n", pDSoundStrmIn, pDSoundStrmIn->pDSCB));
+
+    if (pDSoundStrmIn->pDSCB)
+    {
+        HRESULT hr = IDirectSoundCaptureBuffer_Stop(pDSoundStrmIn->pDSCB);
+        if (FAILED (hr))
+        {
+            DSLOG(("DSound: close capture buffer stop %Rhrc\n", hr));
+        }
+
+        IDirectSoundCaptureBuffer_Release(pDSoundStrmIn->pDSCB);
+        pDSoundStrmIn->pDSCB = NULL;
+    }
+
+    dsoundCaptureInterfaceRelease(pDSoundStrmIn);
+}
+
+static int dsoundCaptureOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    DSLOG(("DSound: capture open %p size %d bytes freq %d, chan %d, bits %d, sign %d\n",
+           pDSoundStrmIn,
+           pThis->cfg.cbBufferIn,
+           pDSoundStrmIn->hw.Props.uHz,
+           pDSoundStrmIn->hw.Props.cChannels,
+           pDSoundStrmIn->hw.Props.cBits,
+           pDSoundStrmIn->hw.Props.fSigned));
+
+    if (pDSoundStrmIn->pDSCB != NULL)
+    {
+        /* Should not happen but be forgiving. */
+        DSLOGREL(("DSound: DirectSoundCaptureBuffer already exists\n"));
+        dsoundCaptureClose(pDSoundStrmIn);
+    }
+
+    WAVEFORMATEX wfx;
+    int rc = dsoundWaveFmtFromCfg(&pDSoundStrmIn->streamCfg, &wfx);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = dsoundCaptureInterfaceCreate(pThis, pDSoundStrmIn);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    HRESULT hr = S_OK;
+
+    do /* To use breaks. */
+    {
+        DSCBUFFERDESC bd;
+        RT_ZERO(bd);
+        bd.dwSize = sizeof(bd);
+        bd.lpwfxFormat = &wfx;
+        bd.dwBufferBytes = pThis->cfg.cbBufferIn;
+        hr = IDirectSoundCapture_CreateCaptureBuffer(pDSoundStrmIn->pDSC,
+                                                     &bd, &pDSoundStrmIn->pDSCB, NULL);
+
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: create capture buffer %Rhrc\n", hr));
+            pDSoundStrmIn->pDSCB = NULL;
+            break;
+        }
+
+        /* Query the actual parameters. */
+
+        DWORD cbReadPos = 0;
+        hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pDSoundStrmIn->pDSCB, NULL, &cbReadPos);
+        if (FAILED(hr))
+        {
+            cbReadPos = 0;
+            DSLOG(("DSound: open GetCurrentPosition %Rhrc\n", hr));
+        }
+
+        RT_ZERO(wfx);
+        hr = IDirectSoundCaptureBuffer_GetFormat(pDSoundStrmIn->pDSCB, &wfx, sizeof(wfx), NULL);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: capture buffer GetFormat %Rhrc\n", hr));
+            break;
+        }
+
+        DSCBCAPS bc;
+        RT_ZERO(bc);
+        bc.dwSize = sizeof(bc);
+        hr = IDirectSoundCaptureBuffer_GetCaps(pDSoundStrmIn->pDSCB, &bc);
+        if (FAILED (hr))
+        {
+            DSLOGREL(("DSound: capture buffer GetCaps %Rhrc\n", hr));
+            break;
+        }
+
+        DSLOG(("DSound: capture buffer format: size %d bytes\n"
+                "  tag             = %d\n"
+                "  nChannels       = %d\n"
+                "  nSamplesPerSec  = %d\n"
+                "  nAvgBytesPerSec = %d\n"
+                "  nBlockAlign     = %d\n"
+                "  wBitsPerSample  = %d\n"
+                "  cbSize          = %d\n",
+                bc.dwBufferBytes,
+                wfx.wFormatTag,
+                wfx.nChannels,
+                wfx.nSamplesPerSec,
+                wfx.nAvgBytesPerSec,
+                wfx.nBlockAlign,
+                wfx.wBitsPerSample,
+                wfx.cbSize));
+
+        if (bc.dwBufferBytes & pDSoundStrmIn->hw.Props.uAlign)
+        {
+            DSLOGREL(("DSound: GetCaps returned misaligned buffer size %ld, alignment %d\n",
+                      bc.dwBufferBytes, pDSoundStrmIn->hw.Props.uAlign + 1));
+        }
+
+        if (bc.dwBufferBytes != pThis->cfg.cbBufferIn)
+        {
+            DSLOGREL(("DSound: buffer size mismatch dsound %d, requested %d bytes\n",
+                      bc.dwBufferBytes, pThis->cfg.cbBufferIn));
+        }
+
+        /* Initial state: reading at the initial capture position. */
+        pDSoundStrmIn->csCaptureReadPos = cbReadPos >> pDSoundStrmIn->hw.Props.cShift;
+        pDSoundStrmIn->csCaptureBufferSize = bc.dwBufferBytes >> pDSoundStrmIn->hw.Props.cShift;
+        DSLOG(("DSound: capture open csCaptureReadPos %d, csCaptureBufferSize %d samples\n",
+               pDSoundStrmIn->csCaptureReadPos, pDSoundStrmIn->csCaptureBufferSize));
+
+        pDSoundStrmIn->hrLastCaptureIn = S_OK;
+    } while (0);
+
+    if (SUCCEEDED(hr))
+        return VINF_SUCCESS;
+
+    dsoundCaptureClose(pDSoundStrmIn);
+    return VERR_NOT_SUPPORTED;
+}
+
+static void dsoundCaptureStop(PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    if (pDSoundStrmIn->pDSCB)
+    {
+        DSLOG(("DSound: capture stop\n"));
+
+        HRESULT hr = IDirectSoundCaptureBuffer_Stop(pDSoundStrmIn->pDSCB);
+        if (FAILED(hr))
+        {
+            DSLOG(("DSound: stop capture buffer %Rhrc\n", hr));
+        }
+    }
+}
+
+static int dsoundCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    HRESULT hr;
+
+    if (pDSoundStrmIn->pDSCB != NULL)
+    {
+        DWORD dwStatus;
+        hr = IDirectSoundCaptureBuffer_GetStatus(pDSoundStrmIn->pDSCB, &dwStatus);
+        if (FAILED(hr))
+        {
+            DSLOG(("DSound: start GetStatus %Rhrc\n", hr));
+        }
+        else
+        {
+            if (dwStatus & DSCBSTATUS_CAPTURING)
+            {
+                DSLOG(("DSound: already capturing\n"));
+            }
+            else
+            {
+                DSLOG(("DSound: capture start\n"));
+
+                hr = IDirectSoundCaptureBuffer_Start(pDSoundStrmIn->pDSCB, DSCBSTART_LOOPING);
+                if (FAILED (hr))
+                {
+                    DSLOGREL(("DSound: start %Rhrc\n", hr));
+                }
+            }
+        }
+    }
+    else
+    {
+        hr = E_FAIL;
+    }
+
+    return SUCCEEDED(hr) ? VINF_SUCCESS: VERR_NOT_SUPPORTED;
+}
+
+static int dsoundDevAdd(PRTLISTANCHOR pList, LPGUID lpGUID,
+                        LPCWSTR lpwstrDescription, PDSOUNDDEV *ppDev)
+{
+    AssertPtrReturn(pList, VERR_INVALID_POINTER);
+    AssertPtrReturn(lpGUID, VERR_INVALID_POINTER);
+    AssertPtrReturn(lpwstrDescription, VERR_INVALID_POINTER);
+
+    PDSOUNDDEV pDev = (PDSOUNDDEV)RTMemAlloc(sizeof(DSOUNDDEV));
+    if (!pDev)
+        return VERR_NO_MEMORY;
+
+    int rc = RTUtf16ToUtf8(lpwstrDescription, &pDev->pszName);
+    if (RT_SUCCESS(rc))
+        memcpy(&pDev->Guid, lpGUID, sizeof(GUID));
+
+    if (RT_SUCCESS(rc))
+        RTListAppend(pList, &pDev->Node);
+
+    if (ppDev)
+        *ppDev = pDev;
+
+    return rc;
+}
+
+static void dsoundDevRemove(PDSOUNDDEV pDev)
+{
+    if (pDev)
+    {
+        RTStrFree(pDev->pszName);
+        pDev->pszName = NULL;
+
+        RTListNodeRemove(&pDev->Node);
+
+        RTMemFree(pDev);
+    }
+}
+
+static char *dsoundGUIDToUtf8StrA(LPCGUID lpGUID)
+{
+    LPOLESTR lpOLEStr;
+    HRESULT hr = StringFromCLSID(*lpGUID, &lpOLEStr);
+    if (SUCCEEDED(hr))
+    {
+        char *pszGUID;
+        int rc = RTUtf16ToUtf8(lpOLEStr, &pszGUID);
+        CoTaskMemFree(lpOLEStr);
+
+        return RT_SUCCESS(rc) ? pszGUID : NULL;
+    }
+
+    return NULL;
+}
+
+static void dsoundLogDevice(const char *pszType, LPGUID lpGUID, LPCWSTR lpwstrDescription, LPCWSTR lpwstrModule)
+{
+    char *pszGUID = dsoundGUIDToUtf8StrA(lpGUID);
+
+    DSLOG(("DSound: %s: GUID: %s [%ls] (Module: %ls)\n",
+           pszType, pszGUID? pszGUID: "no GUID", lpwstrDescription, lpwstrModule));
+
+    RTStrFree(pszGUID);
+}
+
+static BOOL CALLBACK dsoundEnumCallback(LPGUID lpGUID, LPCWSTR lpwstrDescription,
+                                        LPCWSTR lpwstrModule, LPVOID lpContext)
+{
+    PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
+    AssertPtrReturn(pCtx, FALSE);
+    AssertPtrReturn(pCtx->pDrv, FALSE);
+    AssertPtrReturn(pCtx->pCfg, FALSE);
+
+    if (!lpGUID)
+        return TRUE;
+
+    dsoundLogDevice("Output", lpGUID, lpwstrDescription, lpwstrModule);
+
+    int rc = dsoundDevAdd(&pCtx->pDrv->lstDevOutput,
+                          lpGUID, lpwstrDescription, NULL /* ppDev */);
+    if (RT_FAILURE(rc))
+        return FALSE; /* Abort enumeration. */
+
+    pCtx->pCfg->cMaxHstStrmsOut++;
+
+    return TRUE;
+}
+
+static BOOL CALLBACK dsoundCaptureEnumCallback(LPGUID lpGUID, LPCWSTR lpwstrDescription,
+                                               LPCWSTR lpwstrModule, LPVOID lpContext)
+{
+    PDSOUNDENUMCBCTX pCtx = (PDSOUNDENUMCBCTX)lpContext;
+    AssertPtrReturn(pCtx, FALSE);
+    AssertPtrReturn(pCtx->pDrv, FALSE);
+    AssertPtrReturn(pCtx->pCfg, FALSE);
+
+    if (!lpGUID)
+        return TRUE;
+
+    dsoundLogDevice("Input", lpGUID, lpwstrDescription, lpwstrModule);
+
+    int rc = dsoundDevAdd(&pCtx->pDrv->lstDevInput,
+                                 lpGUID, lpwstrDescription, NULL /* ppDev */);
+    if (RT_FAILURE(rc))
+        return FALSE; /* Abort enumeration. */
+
+    pCtx->pCfg->cMaxHstStrmsIn++;
+
+    return TRUE;
+}
+
+
+/*
+ * PDMIHOSTAUDIO
+ */
+
+static DECLCALLBACK(int) drvHostDSoundInitOut(PPDMIHOSTAUDIO pInterface,
+                                              PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
+                                              uint32_t *pcSamples)
+{
+    LogFlowFunc(("pHstStrmOut=%p, pCfg=%p\n", pHstStrmOut, pCfg));
+
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+    PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
+
+    pDSoundStrmOut->streamCfg = *pCfg;
+    pDSoundStrmOut->streamCfg.enmEndianness = PDMAUDIOHOSTENDIANESS;
+
+    int rc = drvAudioStreamCfgToProps(&pDSoundStrmOut->streamCfg, &pDSoundStrmOut->hw.Props);
+    if (RT_SUCCESS(rc))
+    {
+        pDSoundStrmOut->pDS = NULL;
+        pDSoundStrmOut->pDSB = NULL;
+        pDSoundStrmOut->cbPlayWritePos = 0;
+        pDSoundStrmOut->fReinitPlayPos = true;
+        pDSoundStrmOut->csPlaybackBufferSize = 0;
+
+        if (pcSamples)
+            *pcSamples = pThis->cfg.cbBufferOut >> pHstStrmOut->Props.cShift;
+
+        /* Try to open playback in case the device is already there. */
+        dsoundPlayOpen(pThis, pDSoundStrmOut);
+    }
+    else
+    {
+        RT_ZERO(pDSoundStrmOut->streamCfg);
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostDSoundControlOut(PPDMIHOSTAUDIO pInterface,
+                                                 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+
+    LogFlowFunc(("pHstStrmOut=%p, cmd=%d\n", pHstStrmOut, enmStreamCmd));
+
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+    PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
+
+    int rc = VINF_SUCCESS;
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        {
+            /* Try to start playback. If it fails, then reopen and try again. */
+            rc = dsoundPlayStart(pDSoundStrmOut);
+            if (RT_FAILURE(rc))
+            {
+                dsoundPlayClose(pDSoundStrmOut);
+                dsoundPlayOpen(pThis, pDSoundStrmOut);
+
+                rc = dsoundPlayStart(pDSoundStrmOut);
+            }
+        } break;
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        {
+            dsoundPlayStop(pThis, pDSoundStrmOut);
+        } break;
+
+        default:
+        {
+            AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
+            rc = VERR_INVALID_PARAMETER;
+        } break;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+
+static DECLCALLBACK(int) drvHostDSoundPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                              uint32_t *pcSamplesPlayed)
+{
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+    PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
+    LPDIRECTSOUNDBUFFER pDSB = pDSoundStrmOut->pDSB;
+
+    int rc = VINF_SUCCESS;
+
+    if (!pDSB)
+    {
+        if (pcSamplesPlayed) /** @todo single point of return */
+            *pcSamplesPlayed = 0;
+        return VINF_SUCCESS;
+    }
+
+    int cShift = pHstStrmOut->Props.cShift;
+    int cbBuffer = pDSoundStrmOut->csPlaybackBufferSize << cShift;
+
+    DWORD cbPlayPos, cbWritePos;
+    HRESULT hr = IDirectSoundBuffer_GetCurrentPosition(pDSB, &cbPlayPos, &cbWritePos);
+    if (hr == DSERR_BUFFERLOST)
+    {
+        rc = dsoundPlayRestore(pDSB);
+        if (RT_FAILURE(rc))
+        {
+            if (pcSamplesPlayed)
+                *pcSamplesPlayed = 0;
+            return VINF_SUCCESS;
+        }
+
+        hr = IDirectSoundBuffer_GetCurrentPosition(pDSB, &cbPlayPos, &cbWritePos);
+        if (hr == DSERR_BUFFERLOST)
+        {
+            /* Avoid log flooding if the error is still there. */
+            if (pcSamplesPlayed)
+                *pcSamplesPlayed = 0;
+            return VINF_SUCCESS;
+        }
+    }
+    if (FAILED (hr))
+    {
+        DSLOG(("DSound: get playback buffer position %Rhrc\n", hr));
+        if (pcSamplesPlayed)
+            *pcSamplesPlayed = 0;
+        return VINF_SUCCESS;
+    }
+
+    int cbFree;
+    DWORD cbPlayWritePos;
+    if (pDSoundStrmOut->fReinitPlayPos)
+    {
+        pDSoundStrmOut->fReinitPlayPos = false;
+
+        pDSoundStrmOut->cbPlayWritePos = cbWritePos;
+
+        cbPlayWritePos = pDSoundStrmOut->cbPlayWritePos;
+        cbFree = cbBuffer - (int)dsoundRingDistance(cbWritePos, cbPlayPos, cbBuffer);
+    }
+    else
+    {
+        if (pDSoundStrmOut->cbPlayWritePos == cbPlayPos)
+        {
+            /* Full buffer. */
+            if (pcSamplesPlayed)
+                *pcSamplesPlayed = 0;
+            return VINF_SUCCESS;
+        }
+
+        cbPlayWritePos = pDSoundStrmOut->cbPlayWritePos;
+        cbFree = (int)dsoundRingDistance(cbPlayPos, cbPlayWritePos, cbBuffer);
+    }
+
+    uint32_t csLive = drvAudioHstOutSamplesLive(pHstStrmOut, NULL /* pcStreamsLive */);
+    int cbLive = csLive << cShift;
+
+    /* Do not write more than available space in the DirectSound playback buffer. */
+    cbLive = RT_MIN(cbFree, cbLive);
+
+    cbLive &= ~pHstStrmOut->Props.uAlign;
+    if (cbLive <= 0 || cbLive > cbBuffer)
+    {
+        DSLOG(("DSound: cbLive=%d cbBuffer=%d cbPlayWritePos=%d cbPlayPos=%d\n",
+              cbLive, cbBuffer, cbPlayWritePos, cbPlayPos));
+        if (pcSamplesPlayed)
+            *pcSamplesPlayed = 0;
+        return VINF_SUCCESS;
+    }
+
+    LPVOID pv1, pv2;
+    DWORD cb1, cb2;
+    rc = dsoundLockOutput(pDSB, &pHstStrmOut->Props, cbPlayWritePos, cbLive,
+                          &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */);
+    if (RT_FAILURE(rc))
+    {
+        if (pcSamplesPlayed)
+            *pcSamplesPlayed = 0;
+        return VINF_SUCCESS;
+    }
+
+    DWORD len1 = cb1 >> cShift;
+    DWORD len2 = cb2 >> cShift;
+
+    uint32_t cReadTotal = 0;
+    uint32_t cRead = 0;
+
+    if (pv1 && cb1)
+    {
+        rc = audioMixBufReadCirc(&pHstStrmOut->MixBuf, pv1, cb1, &cRead);
+        if (RT_SUCCESS(rc))
+            cReadTotal += cRead;
+    }
+
+    if (   RT_SUCCESS(rc)
+        && cReadTotal == len1
+        && pv2 && cb2)
+    {
+        rc = audioMixBufReadCirc(&pHstStrmOut->MixBuf, pv2, cb2, &cRead);
+        if (RT_SUCCESS(rc))
+            cReadTotal += cRead;
+    }
+
+    dsoundUnlockOutput(pDSB, pv1, pv2, cb1, cb2);
+
+    pDSoundStrmOut->cbPlayWritePos = (cbPlayWritePos + (cReadTotal << cShift)) % cbBuffer;
+
+    LogFlow(("DSound: PlayOut %RU32 (%RU32 samples) out of %d%s, ds write pos %d -> %d, rc=%Rrc\n",
+             AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal), cReadTotal, cbLive,
+             cbLive != AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal) ? " !!!": "",
+             cbPlayWritePos, pDSoundStrmOut->cbPlayWritePos, rc));
+
+    if (cReadTotal)
+    {
+        audioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
+        rc = VINF_SUCCESS; /* Played something. */
+    }
+
+    if (pcSamplesPlayed)
+        *pcSamplesPlayed = cReadTotal;
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostDSoundFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+    PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
+
+    dsoundPlayClose(pDSoundStrmOut);
+
+    pDSoundStrmOut->cbPlayWritePos = 0;
+    pDSoundStrmOut->fReinitPlayPos = true;
+    pDSoundStrmOut->csPlaybackBufferSize = 0;
+    RT_ZERO(pDSoundStrmOut->streamCfg);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostDSoundInitIn(PPDMIHOSTAUDIO pInterface,
+                                             PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
+                                             PDMAUDIORECSOURCE enmRecSource,
+                                             uint32_t *pcSamples)
+{
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+    PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
+
+    LogFlowFunc(("pHstStrmIn=%p, pAudioSettings=%p, enmRecSource=%ld\n",
+                 pHstStrmIn, pCfg, enmRecSource));
+
+    pDSoundStrmIn->streamCfg = *pCfg;
+    pDSoundStrmIn->streamCfg.enmEndianness = PDMAUDIOHOSTENDIANESS;
+
+    /** @todo caller should already init Props? */
+    int rc = drvAudioStreamCfgToProps(&pDSoundStrmIn->streamCfg, &pHstStrmIn->Props);
+    if (RT_SUCCESS(rc))
+    {
+        /* Init the stream structure and save relevant information to it. */
+        pDSoundStrmIn->csCaptureReadPos = 0;
+        pDSoundStrmIn->csCaptureBufferSize = 0;
+        pDSoundStrmIn->pDSC = NULL;
+        pDSoundStrmIn->pDSCB = NULL;
+        pDSoundStrmIn->enmRecSource = enmRecSource;
+        pDSoundStrmIn->hrLastCaptureIn = S_OK;
+
+        if (pcSamples)
+            *pcSamples = pThis->cfg.cbBufferIn >> pHstStrmIn->Props.cShift;
+
+        /* Try to open capture in case the device is already there. */
+        dsoundCaptureOpen(pThis, pDSoundStrmIn);
+    }
+    else
+    {
+        RT_ZERO(pDSoundStrmIn->streamCfg);
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostDSoundControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+
+    LogFlowFunc(("pHstStrmIn=%p, enmStreamCmd=%ld\n", pHstStrmIn, enmStreamCmd));
+
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+    PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
+
+    int rc = VINF_SUCCESS;
+
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        {
+            /* Try to start capture. If it fails, then reopen and try again. */
+            rc = dsoundCaptureStart(pThis, pDSoundStrmIn);
+            if (RT_FAILURE(rc))
+            {
+                dsoundCaptureClose(pDSoundStrmIn);
+                dsoundCaptureOpen(pThis, pDSoundStrmIn);
+
+                rc = dsoundCaptureStart(pThis, pDSoundStrmIn);
+            }
+        } break;
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        {
+            dsoundCaptureStop(pDSoundStrmIn);
+        } break;
+
+        default:
+        {
+            AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
+            rc = VERR_INVALID_PARAMETER;
+        } break;
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostDSoundCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                uint32_t *pcSamplesCaptured)
+{
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+    PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
+    LPDIRECTSOUNDCAPTUREBUFFER pDSCB = pDSoundStrmIn->pDSCB;
+
+    int rc;
+
+    if (pDSCB == NULL)
+    {
+        if (pcSamplesCaptured) /** @todo single point of return */
+            *pcSamplesCaptured = 0;
+        return VINF_SUCCESS;
+    }
+
+    /* Get DirectSound capture position in bytes. */
+    DWORD cbReadPos;
+    HRESULT hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pDSCB, NULL, &cbReadPos);
+    if (FAILED(hr))
+    {
+        if (hr != pDSoundStrmIn->hrLastCaptureIn)
+        {
+            DSLOGREL(("DSound: CaptureIn GetCurrentPosition %Rhrc\n", hr));
+            pDSoundStrmIn->hrLastCaptureIn = hr;
+        }
+
+        if (pcSamplesCaptured)
+            *pcSamplesCaptured = 0;
+        return VINF_SUCCESS;
+    }
+    pDSoundStrmIn->hrLastCaptureIn = hr;
+
+    if (cbReadPos & pHstStrmIn->Props.uAlign)
+    {
+        DSLOG(("DSound: CaptureIn misaligned read position %d(%d)\n", cbReadPos, pHstStrmIn->Props.uAlign));
+    }
+
+    /* Capture position in samples. */
+    int csReadPos = cbReadPos >> pHstStrmIn->Props.cShift;
+
+    /* Number of samples available in the DirectSound capture buffer. */
+    DWORD csCaptured = dsoundRingDistance(csReadPos, pDSoundStrmIn->csCaptureReadPos, pDSoundStrmIn->csCaptureBufferSize);
+    if (csCaptured == 0)
+    {
+        if (pcSamplesCaptured)
+            *pcSamplesCaptured = 0;
+        return VINF_SUCCESS;
+    }
+
+    /* Using as an intermediate not circular buffer. */
+    audioMixBufReset(&pHstStrmIn->MixBuf);
+
+    /* Get number of free samples in the mix buffer and check that is has free space */
+    size_t csMixFree = audioMixBufFree(&pHstStrmIn->MixBuf);
+    if (csMixFree == 0)
+    {
+        DSLOG(("DSound: capture mix buffer full\n"));
+        if (pcSamplesCaptured)
+            *pcSamplesCaptured = 0;
+        return VINF_SUCCESS;
+    }
+
+    LogFlow(("DSound: CaptureIn csMixFree = %d, csReadPos = %d, csCaptureReadPos = %d, csCaptured = %d\n",
+             csMixFree, csReadPos, pDSoundStrmIn->csCaptureReadPos, csCaptured));
+
+    /* No need to fetch more samples than mix buffer can receive. */
+    csCaptured = RT_MIN(csCaptured, csMixFree);
+
+    /* Lock relevant range in the DirectSound capture buffer. */
+    LPVOID pv1, pv2;
+    DWORD cb1, cb2;
+    rc = dsoundLockInput(pDSCB, &pHstStrmIn->Props,
+                         pDSoundStrmIn->csCaptureReadPos << pHstStrmIn->Props.cShift,
+                         csCaptured << pHstStrmIn->Props.cShift,
+                         &pv1, &pv2, &cb1, &cb2,
+                         0 /* dwFlags */);
+    if (RT_FAILURE(rc))
+    {
+        if (pcSamplesCaptured)
+            *pcSamplesCaptured = 0;
+        return VINF_SUCCESS;
+    }
+
+    DWORD len1 = cb1 >> pHstStrmIn->Props.cShift;
+    DWORD len2 = cb2 >> pHstStrmIn->Props.cShift;
+
+    uint32_t csWrittenTotal = 0;
+    uint32_t csWritten;
+    if (pv1 && len1)
+    {
+        rc = audioMixBufWriteAt(&pHstStrmIn->MixBuf, 0 /* offWrite */,
+                                pv1, cb1, &csWritten);
+        if (RT_SUCCESS(rc))
+            csWrittenTotal += csWritten;
+    }
+
+    if (   RT_SUCCESS(rc)
+        && csWrittenTotal == len1
+        && pv2 && len2)
+    {
+        rc = audioMixBufWriteAt(&pHstStrmIn->MixBuf, csWrittenTotal,
+                                pv2, cb2, &csWritten);
+        if (RT_SUCCESS(rc))
+            csWrittenTotal += csWritten;
+    }
+
+    dsoundUnlockInput(pDSCB, pv1, pv2, cb1, cb2);
+
+    uint32_t csProcessed = 0;
+    if (csWrittenTotal != 0)
+    {
+        /* Captured something. */
+        rc = audioMixBufMixToParent(&pHstStrmIn->MixBuf, csWrittenTotal,
+                                    &csProcessed);
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        pDSoundStrmIn->csCaptureReadPos = (pDSoundStrmIn->csCaptureReadPos + csProcessed) % pDSoundStrmIn->csCaptureBufferSize;
+        LogFlow(("DSound: CaptureIn %d (%d+%d), processed %d/%d\n",
+                 csCaptured, len1, len2, csProcessed, csWrittenTotal));
+    }
+
+    if (pcSamplesCaptured)
+        *pcSamplesCaptured = csProcessed;
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostDSoundFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+    PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
+
+    dsoundCaptureClose(pDSoundStrmIn);
+
+    pDSoundStrmIn->csCaptureReadPos = 0;
+    pDSoundStrmIn->csCaptureBufferSize = 0;
+    RT_ZERO(pDSoundStrmIn->streamCfg);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(bool) drvHostDSoundIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
+{
+    NOREF(pInterface);
+    NOREF(enmDir);
+    return true; /* Always all enabled. */
+}
+
+static DECLCALLBACK(int) drvHostDSoundGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
+{
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    dsoundFreeDeviceLists(pThis);
+
+    pCfg->cbStreamOut = sizeof(DSOUNDSTREAMOUT);
+    pCfg->cbStreamIn  = sizeof(DSOUNDSTREAMIN);
+
+    pCfg->cMaxHstStrmsOut = 0;
+    pCfg->cMaxHstStrmsIn  = 0;
+
+    DSOUNDENUMCBCTX ctx = { pThis, pCfg };
+
+    HRESULT hr = DirectSoundEnumerateW(&dsoundEnumCallback, &ctx);
+    if (FAILED(hr))
+        DSLOG(("DSound: Enumerating host playback devices failed %Rhrc\n", hr));
+
+    DSLOGREL(("DSound: Found %RU32 host playback devices\n", pCfg->cMaxHstStrmsOut));
+    if (pCfg->cMaxHstStrmsOut == 0)
+        pCfg->cMaxHstStrmsOut = 1; /* Support at least one stream. */
+
+    hr = DirectSoundCaptureEnumerateW(&dsoundCaptureEnumCallback, &ctx);
+    if (FAILED(hr))
+        DSLOG(("DSound: Enumerating host capturing devices failed %Rhrc\n", hr));
+
+    DSLOGREL(("DSound: Found %RU32 host capturing devices\n", pCfg->cMaxHstStrmsIn));
+    if (pCfg->cMaxHstStrmsIn == 0)
+        pCfg->cMaxHstStrmsIn = 1; /* Support at least one stream. */
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostDSoundInit(PPDMIHOSTAUDIO pInterface)
+{
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+
+    LogFlowFuncEnter();
+
+    /* Verify that IDirectSound is available. */
+    LPDIRECTSOUND pDirectSound = NULL;
+    HRESULT hr = CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_ALL,
+                                  IID_IDirectSound, (void **)&pDirectSound);
+    if (SUCCEEDED(hr))
+        IDirectSound_Release(pDirectSound);
+    else
+        DSLOGREL(("DSound: not available %Rhrc\n", hr));
+
+    int rc = SUCCEEDED(hr) ? VINF_SUCCESS: VERR_NOT_SUPPORTED;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+
+/*
+ * PDMIBASE
+ */
+
+static DECLCALLBACK(void *) drvHostDSoundQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    PPDMDRVINS     pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTDSOUND pThis   = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
+
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
+    return NULL;
+}
+
+
+/*
+ * PDM driver.
+ */
+
+static int dsoundConfigQueryStringAlloc(PCFGMNODE pNode, const char *pszName, char **ppszString)
+{
+    size_t cbString;
+    int rc = CFGMR3QuerySize(pNode, pszName, &cbString);
+    if (RT_SUCCESS(rc))
+    {
+        char *pszString = RTStrAlloc(cbString);
+        if (pszString)
+        {
+            rc = CFGMR3QueryString(pNode, pszName, pszString, cbString);
+            if (RT_SUCCESS(rc))
+                *ppszString = pszString;
+            else
+                RTStrFree(pszString);
+        }
+        else
+            rc = VERR_NO_MEMORY;
+    }
+    return rc;
+}
+
+static LPCGUID dsoundConfigQueryGUID(PCFGMNODE pCfg, const char *pszName, RTUUID *pUuid)
+{
+    LPCGUID pGuid = NULL;
+
+    char *pszGuid = NULL;
+    dsoundConfigQueryStringAlloc(pCfg, pszName, &pszGuid);
+    if (pszGuid)
+    {
+        int rc = RTUuidFromStr(pUuid, pszGuid);
+        if (RT_SUCCESS(rc))
+            pGuid = (LPCGUID)&pUuid;
+        else
+            DSLOGREL(("DSound: Parse DirectSound %s %Rrc\n", pszName, rc));
+
+        RTStrFree(pszGuid);
+    }
+
+    return pGuid;
+}
+
+static void dSoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg)
+{
+    CFGMR3QuerySIntDef(pCfg, "BufsizeOut",       &pThis->cfg.cbBufferOut,       _16K);
+    CFGMR3QuerySIntDef(pCfg, "BufsizeIn",        &pThis->cfg.cbBufferIn,        _16K);
+
+    pThis->cfg.pGuidPlay    = dsoundConfigQueryGUID(pCfg, "DeviceGuidOut", &pThis->cfg.uuidPlay);
+    pThis->cfg.pGuidCapture = dsoundConfigQueryGUID(pCfg, "DeviceGuidIn",  &pThis->cfg.uuidCapture);
+}
+
+static DECLCALLBACK(void) drvHostDSoundDestruct(PPDMDRVINS pDrvIns)
+{
+    PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
+
+    LogFlowFuncEnter();
+
+    CoUninitialize();
+
+    LogFlowFuncLeave();
+}
+
+/**
+ * Construct a DirectSound Audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvHostDSoundConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+    PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
+    DSLOGREL(("Audio: Initializing DirectSound audio driver\n"));
+
+    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+    if (FAILED(hr))
+    {
+        DSLOGREL(("DSound: COM initialize %Rhrc\n", hr));
+        return VERR_NOT_SUPPORTED;
+    }
+
+    /*
+     * Init the static parts.
+     */
+    pThis->pDrvIns                    = pDrvIns;
+    /* IBase */
+    pDrvIns->IBase.pfnQueryInterface  = drvHostDSoundQueryInterface;
+    /* IHostAudio */
+    PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDSound);
+
+    RTListInit(&pThis->lstDevInput);
+    RTListInit(&pThis->lstDevOutput);
+
+    /*
+     * Initialize configuration values.
+     */
+    dSoundConfigInit(pThis, pCfg);
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * PDM driver registration.
+ */
+const PDMDRVREG g_DrvHostDSound =
+{
+    /* u32Version */
+    PDM_DRVREG_VERSION,
+    /* szName */
+    "DSoundAudio",
+    /* szRCMod */
+    "",
+    /* szR0Mod */
+    "",
+    /* pszDescription */
+    "DirectSound Audio host driver",
+    /* fFlags */
+     PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+    /* fClass. */
+    PDM_DRVREG_CLASS_AUDIO,
+    /* cMaxInstances */
+    ~0U,
+    /* cbInstance */
+    sizeof(DRVHOSTDSOUND),
+    /* pfnConstruct */
+    drvHostDSoundConstruct,
+    /* pfnDestruct */
+    drvHostDSoundDestruct,
+    /* pfnRelocate */
+    NULL,
+    /* pfnIOCtl */
+    NULL,
+    /* pfnPowerOn */
+    NULL,
+    /* pfnReset */
+    NULL,
+    /* pfnSuspend */
+    NULL,
+    /* pfnResume */
+    NULL,
+    /* pfnAttach */
+    NULL,
+    /* pfnDetach */
+    NULL,
+    /* pfnPowerOff */
+    NULL,
+    /* pfnSoftReset */
+    NULL,
+    /* u32EndVersion */
+    PDM_DRVREG_VERSION
+};
Index: /trunk/src/VBox/Devices/Audio/DrvHostNullAudio.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DrvHostNullAudio.cpp	(revision 54433)
+++ /trunk/src/VBox/Devices/Audio/DrvHostNullAudio.cpp	(revision 54433)
@@ -0,0 +1,286 @@
+/* $Id$ */
+/** @file
+ * NULL audio driver -- also acts as a fallback if no
+ * other backend is available.
+ */
+
+/*
+ * Copyright (C) 2006-2015 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ * --------------------------------------------------------------------
+ *
+ * This code is based on: noaudio.c QEMU based code.
+ *
+ * QEMU Timer based audio emulation
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "DrvAudio.h"
+#include "VBoxDD.h"
+#include "vl_vbox.h"
+
+#include <iprt/alloc.h>
+#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
+#include <VBox/vmm/pdmaudioifs.h>
+
+#ifdef LOG_GROUP
+# undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_DEV_AUDIO
+#include <VBox/log.h>
+
+typedef struct NULLAUDIOSTREAMOUT
+{
+    /** Note: Always must come first! */
+    PDMAUDIOHSTSTRMOUT hw;
+} NULLAUDIOSTREAMOUT;
+
+typedef struct NULLAUDIOSTREAMIN
+{
+    /** Note: Always must come first! */
+    PDMAUDIOHSTSTRMIN hw;
+} NULLAUDIOSTREAMIN;
+
+/**
+ * NULL audio driver instance data.
+ * @implements PDMIAUDIOCONNECTOR
+ */
+typedef struct DRVHOSTNULLAUDIO
+{
+    /** Pointer to the driver instance structure. */
+    PPDMDRVINS    pDrvIns;
+    /** Pointer to host audio interface. */
+    PDMIHOSTAUDIO IHostAudio;
+} DRVHOSTNULLAUDIO, *PDRVHOSTNULLAUDIO;
+
+/*******************************************PDM_AUDIO_DRIVER******************************/
+
+
+static DECLCALLBACK(int) drvHostNullAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    pCfg->cbStreamOut = sizeof(NULLAUDIOSTREAMOUT);
+    pCfg->cbStreamIn  = sizeof(NULLAUDIOSTREAMIN);
+
+    pCfg->cMaxHstStrmsOut = 1; /* Output */
+    pCfg->cMaxHstStrmsIn  = 2; /* Line input + microphone input. */
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostNullAudioInit(PPDMIHOSTAUDIO pInterface)
+{
+    NOREF(pInterface);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostNullAudioInitIn(PPDMIHOSTAUDIO pInterface,
+                                                PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
+                                                PDMAUDIORECSOURCE enmRecSource,
+                                                uint32_t *pcSamples)
+{
+    NOREF(pInterface);
+    NOREF(enmRecSource);
+
+    /* Just adopt the wanted stream configuration. */
+    int rc = drvAudioStreamCfgToProps(pCfg, &pHstStrmIn->Props);
+    if (RT_SUCCESS(rc))
+    {
+        if (pcSamples)
+            *pcSamples = 256;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostNullAudioInitOut(PPDMIHOSTAUDIO pInterface,
+                                                 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
+                                                 uint32_t *pcSamples)
+{
+    NOREF(pInterface);
+
+    /* Just adopt the wanted stream configuration. */
+    int rc = drvAudioStreamCfgToProps(pCfg, &pHstStrmOut->Props);
+    if (RT_SUCCESS(rc))
+    {
+        if (pcSamples)
+            *pcSamples = 256;
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(bool) drvHostNullAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
+{
+    NOREF(pInterface);
+    NOREF(enmDir);
+    return true; /* Always all enabled. */
+}
+
+static DECLCALLBACK(int) drvHostNullAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                                 uint32_t *pcSamplesPlayed)
+{
+    if (pcSamplesPlayed)
+        *pcSamplesPlayed = UINT32_MAX;
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostNullAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                   uint32_t *pcSamplesCaptured)
+{
+    if (pcSamplesCaptured)
+        *pcSamplesCaptured = 0;
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostNullAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                   PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    NOREF(pInterface);
+    NOREF(pHstStrmIn);
+    NOREF(enmStreamCmd);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostNullAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                                    PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    NOREF(pInterface);
+    NOREF(pHstStrmOut);
+    NOREF(enmStreamCmd);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostNullAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostNullAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvHostNullAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    PPDMDRVINS        pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTNULLAUDIO pThis   = PDMINS_2_DATA(pDrvIns, PDRVHOSTNULLAUDIO);
+
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
+    return NULL;
+}
+
+static DECLCALLBACK(void) drvHostNullAudioDestruct(PPDMDRVINS pDrvIns)
+{
+    NOREF(pDrvIns);
+}
+
+/**
+ * Constructs a Null audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvHostNullAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+    PDRVHOSTNULLAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTNULLAUDIO);
+    LogRel(("Audio: Initializing NULL driver\n"));
+
+    /*
+     * Init the static parts.
+     */
+    pThis->pDrvIns                   = pDrvIns;
+    /* IBase */
+    pDrvIns->IBase.pfnQueryInterface = drvHostNullAudioQueryInterface;
+    /* IHostAudio */
+    PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostNullAudio);
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Char driver registration record.
+ */
+const PDMDRVREG g_DrvHostNullAudio =
+{
+    /* u32Version */
+    PDM_DRVREG_VERSION,
+    /* szName */
+    "NullAudio",
+    /* szRCMod */
+    "",
+    /* szR0Mod */
+    "",
+    /* pszDescription */
+    "NULL audio host driver",
+    /* fFlags */
+    PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+    /* fClass. */
+    PDM_DRVREG_CLASS_AUDIO,
+    /* cMaxInstances */
+    ~0U,
+    /* cbInstance */
+    sizeof(DRVHOSTNULLAUDIO),
+    /* pfnConstruct */
+    drvHostNullAudioConstruct,
+    /* pfnDestruct */
+    drvHostNullAudioDestruct,
+    /* pfnRelocate */
+    NULL,
+    /* pfnIOCtl */
+    NULL,
+    /* pfnPowerOn */
+    NULL,
+    /* pfnReset */
+    NULL,
+    /* pfnSuspend */
+    NULL,
+    /* pfnResume */
+    NULL,
+    /* pfnAttach */
+    NULL,
+    /* pfnDetach */
+    NULL,
+    /* pfnPowerOff */
+    NULL,
+    /* pfnSoftReset */
+    NULL,
+    /* u32EndVersion */
+    PDM_DRVREG_VERSION
+};
+
Index: /trunk/src/VBox/Devices/Audio/DrvHostPulseAudio.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio/DrvHostPulseAudio.cpp	(revision 54433)
+++ /trunk/src/VBox/Devices/Audio/DrvHostPulseAudio.cpp	(revision 54433)
@@ -0,0 +1,1196 @@
+/* $Id$ */
+/** @file
+ * VBox audio devices: Pulse Audio audio driver.
+ */
+
+/*
+ * Copyright (C) 2006-2015 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+
+#include <stdio.h>
+
+#include <iprt/alloc.h>
+#include <iprt/mem.h>
+#include <iprt/uuid.h>
+
+RT_C_DECLS_BEGIN
+ #include "pulse_mangling.h"
+ #include "pulse_stubs.h"
+RT_C_DECLS_END
+
+#include <pulse/pulseaudio.h>
+#include "vl_vbox.h"
+
+#include "DrvAudio.h"
+#include "AudioMixBuffer.h"
+
+#ifdef LOG_GROUP
+# undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_DEV_AUDIO
+#include <VBox/log.h>
+
+#define VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS 32 /** @todo Make this configurable thru driver options. */
+
+#ifndef PA_STREAM_NOFLAGS
+# define PA_STREAM_NOFLAGS (pa_context_flags_t)0x0000U /* since 0.9.19 */
+#endif
+
+#ifndef PA_CONTEXT_NOFLAGS
+# define PA_CONTEXT_NOFLAGS (pa_context_flags_t)0x0000U /* since 0.9.19 */
+#endif
+
+/*
+ * We use a g_pMainLoop in a separate thread g_pContext. We have to call functions for
+ * manipulating objects either from callback functions or we have to protect
+ * these functions by pa_threaded_mainloop_lock() / pa_threaded_mainloop_unlock().
+ */
+static struct pa_threaded_mainloop *g_pMainLoop;
+static struct pa_context           *g_pContext;
+
+/**
+ * Host Pulse audio driver instance data.
+ * @implements PDMIAUDIOCONNECTOR
+ */
+typedef struct DRVHOSTPULSEAUDIO
+{
+    /** Pointer to the driver instance structure. */
+    PPDMDRVINS         pDrvIns;
+    /** Pointer to host audio interface. */
+    PDMIHOSTAUDIO      IHostAudio;
+    /** Error count for not flooding the release log.
+     *  UINT32_MAX for unlimited logging. */
+    uint32_t           cLogErrors;
+} DRVHOSTPULSEAUDIO, *PDRVHOSTPULSEAUDIO;
+
+typedef struct PULSEAUDIOSTREAM
+{
+    /** Must come first, as this struct might be
+     *  casted to one of these structs. */
+    union
+    {
+        PDMAUDIOHSTSTRMIN  In;
+        PDMAUDIOHSTSTRMOUT Out;
+    } hw;
+    /** Pointer to driver instance. */
+    PDRVHOSTPULSEAUDIO     pDrv;
+    /** DAC/ADC buffer. */
+    void                  *pPCMBuf;
+    /** Pointer to opaque PulseAudio stream. */
+    pa_stream             *pStream;
+    /** Pulse sample format and attribute specification. */
+    pa_sample_spec         SampleSpec;
+    /** Pulse playback and buffer metrics. */
+    pa_buffer_attr         BufAttr;
+    int                    fOpSuccess;
+    /** Pointer to Pulse sample peeking buffer. */
+    const uint8_t         *pu8PeekBuf;
+    /** Current size (in bytes) of peeking data in
+     *  buffer. */
+    size_t                 cbPeekBuf;
+    /** Our offset (in bytes) in peeking buffer. */
+    size_t                 offPeekBuf;
+    pa_operation          *pDrainOp;
+} PULSEAUDIOSTREAM, *PPULSEAUDIOSTREAM;
+
+/* The desired buffer length in milliseconds. Will be the target total stream
+ * latency on newer version of pulse. Apparent latency can be less (or more.)
+ */
+typedef struct PULSEAUDIOCFG
+{
+    RTMSINTERVAL buffer_msecs_out;
+    RTMSINTERVAL buffer_msecs_in;
+} PULSEAUDIOCFG, *PPULSEAUDIOCFG;
+
+static PULSEAUDIOCFG s_pulseCfg =
+{
+    100, /* buffer_msecs_out */
+    100  /* buffer_msecs_in */
+};
+
+/** Makes DRVHOSTPULSEAUDIO out of PDMIHOSTAUDIO. */
+#define PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface) \
+    ( (PDRVHOSTPULSEAUDIO)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTPULSEAUDIO, IHostAudio)) )
+
+static int  drvHostPulseAudioError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg);
+static void drvHostPulseAudioCbSuccess(pa_stream *pStream, int fSuccess, void *pvContext);
+
+static pa_sample_format_t drvHostPulseAudioFmtToPulse(PDMAUDIOFMT fmt)
+{
+    switch (fmt)
+    {
+        case AUD_FMT_U8:
+            return PA_SAMPLE_U8;
+
+        case AUD_FMT_S16:
+            return PA_SAMPLE_S16LE;
+
+#ifdef PA_SAMPLE_S32LE
+        case AUD_FMT_S32:
+            return PA_SAMPLE_S32LE;
+#endif
+        default:
+            break;
+    }
+
+    AssertMsgFailed(("Format %ld not supported\n", fmt));
+    return PA_SAMPLE_U8;
+}
+
+static int drvHostPulseAudioPulseToFmt(pa_sample_format_t pulsefmt,
+                                       PDMAUDIOFMT *pFmt, PDMAUDIOENDIANESS *pEndianess)
+{
+    switch (pulsefmt)
+    {
+        case PA_SAMPLE_U8:
+            *pFmt = AUD_FMT_U8;
+            *pEndianess = PDMAUDIOENDIANESS_LITTLE;
+            break;
+
+        case PA_SAMPLE_S16LE:
+            *pFmt = AUD_FMT_S16;
+            *pEndianess = PDMAUDIOENDIANESS_LITTLE;
+            break;
+
+        case PA_SAMPLE_S16BE:
+            *pFmt = AUD_FMT_S16;
+            *pEndianess = PDMAUDIOENDIANESS_BIG;
+            break;
+
+#ifdef PA_SAMPLE_S32LE
+        case PA_SAMPLE_S32LE:
+            *pFmt = AUD_FMT_S32;
+            *pEndianess = PDMAUDIOENDIANESS_LITTLE;
+            break;
+#endif
+
+#ifdef PA_SAMPLE_S32BE
+        case PA_SAMPLE_S32BE:
+            *pFmt = AUD_FMT_S32;
+            *pEndianess = PDMAUDIOENDIANESS_BIG;
+            break;
+#endif
+
+        default:
+            AssertMsgFailed(("Format %ld not supported\n", pulsefmt));
+            return VERR_NOT_SUPPORTED;
+    }
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Synchronously wait until an operation completed.
+ */
+static int drvHostPulseAudioWaitFor(pa_operation *pOP, RTMSINTERVAL cMsTimeout)
+{
+    AssertPtrReturn(pOP, VERR_INVALID_POINTER);
+
+    int rc = VINF_SUCCESS;
+    if (pOP)
+    {
+        uint64_t u64StartMs = RTTimeMilliTS();
+        uint64_t u64ElapsedMs;
+
+        while (pa_operation_get_state(pOP) == PA_OPERATION_RUNNING)
+        {
+            pa_threaded_mainloop_wait(g_pMainLoop);
+
+            u64ElapsedMs = RTTimeMilliTS() - u64StartMs;
+            if (u64ElapsedMs >= cMsTimeout)
+            {
+                rc = VERR_TIMEOUT;
+                break;
+            }
+        }
+
+        pa_operation_unref(pOP);
+    }
+
+    return rc;
+}
+
+/**
+ * Context status changed.
+ */
+static void drvHostPulseAudioCbCtxState(pa_context *pContext, void *pvContext)
+{
+    AssertPtrReturnVoid(pContext);
+
+    PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
+    NOREF(pStrm);
+
+    switch (pa_context_get_state(pContext))
+    {
+        case PA_CONTEXT_READY:
+        case PA_CONTEXT_TERMINATED:
+            pa_threaded_mainloop_signal(g_pMainLoop, 0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+            LogRel(("PulseAudio: Audio input/output stopped!\n"));
+            pa_threaded_mainloop_signal(g_pMainLoop, 0);
+            break;
+
+        default:
+            break;
+    }
+}
+
+/**
+ * Callback called when our pa_stream_drain operation was completed.
+ */
+static void drvHostPulseAudioCbStreamDrain(pa_stream *pStream, int fSuccess, void *pvContext)
+{
+    AssertPtrReturnVoid(pStream);
+
+    PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
+    AssertPtrReturnVoid(pStrm);
+
+    pStrm->fOpSuccess = fSuccess;
+    if (fSuccess)
+    {
+        pa_operation_unref(pa_stream_cork(pStream, 1,
+                                          drvHostPulseAudioCbSuccess, pvContext));
+    }
+    else
+        drvHostPulseAudioError(pStrm->pDrv, "Failed to drain stream");
+
+    pa_operation_unref(pStrm->pDrainOp);
+    pStrm->pDrainOp = NULL;
+}
+
+/**
+ * Stream status changed.
+ */
+static void drvHostPulseAudioCbStreamState(pa_stream *pStream, void *pvContext)
+{
+    AssertPtrReturnVoid(pStream);
+    NOREF(pvContext);
+
+    switch (pa_stream_get_state(pStream))
+    {
+        case PA_STREAM_READY:
+        case PA_STREAM_FAILED:
+        case PA_STREAM_TERMINATED:
+            pa_threaded_mainloop_signal(g_pMainLoop, 0 /* fWait */);
+            break;
+
+        default:
+            break;
+    }
+}
+
+static void drvHostPulseAudioCbSuccess(pa_stream *pStream, int fSuccess, void *pvContext)
+{
+    AssertPtrReturnVoid(pStream);
+
+    PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
+    AssertPtrReturnVoid(pStrm);
+
+    pStrm->fOpSuccess = fSuccess;
+
+    if (fSuccess)
+    {
+        pa_threaded_mainloop_signal(g_pMainLoop, 0 /* fWait */);
+    }
+    else
+         drvHostPulseAudioError(pStrm->pDrv, "Failed to finish stream operation");
+}
+
+static int drvHostPulseAudioOpen(bool fIn, const char *pszName,
+                                 pa_sample_spec *pSampleSpec, pa_buffer_attr *pBufAttr,
+                                 pa_stream **ppStream)
+{
+    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+    AssertPtrReturn(pSampleSpec, VERR_INVALID_POINTER);
+    AssertPtrReturn(pBufAttr, VERR_INVALID_POINTER);
+    AssertPtrReturn(ppStream, VERR_INVALID_POINTER);
+
+    if (!pa_sample_spec_valid(pSampleSpec))
+    {
+        LogRel(("PulseAudio: Unsupported sample specification for stream \"%s\"\n",
+                pszName));
+        return VERR_NOT_SUPPORTED;
+    }
+
+    int rc = VINF_SUCCESS;
+
+    pa_stream *pStream = NULL;
+    uint32_t   flags = PA_STREAM_NOFLAGS;
+
+    LogFunc(("Opening \"%s\", rate=%dHz, channels=%d, format=%s\n",
+             pszName, pSampleSpec->rate, pSampleSpec->channels,
+             pa_sample_format_to_string(pSampleSpec->format)));
+
+    pa_threaded_mainloop_lock(g_pMainLoop);
+
+    do
+    {
+        if (!(pStream = pa_stream_new(g_pContext, pszName, pSampleSpec,
+                                      NULL /* pa_channel_map */)))
+        {
+            LogRel(("PulseAudio: Could not create stream \"%s\"\n", pszName));
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        pa_stream_set_state_callback(pStream, drvHostPulseAudioCbStreamState, NULL);
+
+#if PA_API_VERSION >= 12
+        /* XXX */
+        flags |= PA_STREAM_ADJUST_LATENCY;
+#endif
+
+#if 0
+        /* Not applicable as we don't use pa_stream_get_latency() and pa_stream_get_time(). */
+        flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
+#endif
+        /* No input/output right away after the stream was started. */
+        flags |= PA_STREAM_START_CORKED;
+
+        if (fIn)
+        {
+            LogFunc(("Input stream attributes: maxlength=%d fragsize=%d\n",
+                     pBufAttr->maxlength, pBufAttr->fragsize));
+
+            if (pa_stream_connect_record(pStream, /*dev=*/NULL, pBufAttr, (pa_stream_flags_t)flags) < 0)
+            {
+                LogRel(("PulseAudio: Could not connect input stream \"%s\": %s\n",
+                        pszName, pa_strerror(pa_context_errno(g_pContext))));
+                rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+                break;
+            }
+        }
+        else
+        {
+            LogFunc(("Output buffer attributes: maxlength=%d tlength=%d prebuf=%d minreq=%d\n",
+                     pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
+
+            if (pa_stream_connect_playback(pStream, /*dev=*/NULL, pBufAttr, (pa_stream_flags_t)flags,
+                                           /*cvolume=*/NULL, /*sync_stream=*/NULL) < 0)
+            {
+                LogRel(("PulseAudio: Could not connect playback stream \"%s\": %s\n",
+                        pszName, pa_strerror(pa_context_errno(g_pContext))));
+                rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+                break;
+            }
+        }
+
+        /* Wait until the stream is ready. */
+        pa_stream_state_t sstate;
+        for (;;)
+        {
+            pa_threaded_mainloop_wait(g_pMainLoop);
+
+            sstate = pa_stream_get_state(pStream);
+            if (sstate == PA_STREAM_READY)
+                break;
+            else if (   sstate == PA_STREAM_FAILED
+                     || sstate == PA_STREAM_TERMINATED)
+            {
+                LogRel(("PulseAudio: Failed to initialize stream \"%s\" (state %ld)\n",
+                        pszName, sstate));
+                rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+                break;
+            }
+        }
+
+        if (RT_FAILURE(rc))
+            break;
+
+        const pa_buffer_attr *pBufAttrObtained = pa_stream_get_buffer_attr(pStream);
+        AssertPtr(pBufAttrObtained);
+        memcpy(pBufAttr, pBufAttrObtained, sizeof(pa_buffer_attr));
+
+        if (fIn)
+            LogFunc(("Obtained record buffer attributes: maxlength=%RU32, fragsize=%RU32\n",
+                     pBufAttr->maxlength, pBufAttr->fragsize));
+        else
+            LogFunc(("Obtained playback buffer attributes: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d\n",
+                     pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
+
+        pa_threaded_mainloop_unlock(g_pMainLoop);
+    }
+    while (0);
+
+    if (RT_FAILURE(rc))
+    {
+        if (pStream)
+            pa_stream_disconnect(pStream);
+
+        pa_threaded_mainloop_unlock(g_pMainLoop);
+
+        if (pStream)
+            pa_stream_unref(pStream);
+    }
+    else
+        *ppStream = pStream;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostPulseAudioInit(PPDMIHOSTAUDIO pInterface)
+{
+    NOREF(pInterface);
+
+    LogFlowFuncEnter();
+
+    int rc = audioLoadPulseLib();
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("PulseAudio: Failed to load the PulseAudio shared library! Error %Rrc\n", rc));
+        return rc;
+    }
+
+    bool fLocked = false;
+
+    do
+    {
+        if (!(g_pMainLoop = pa_threaded_mainloop_new()))
+        {
+            LogRel(("PulseAudio: Failed to allocate main loop: %s\n",
+                     pa_strerror(pa_context_errno(g_pContext))));
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        if (!(g_pContext = pa_context_new(pa_threaded_mainloop_get_api(g_pMainLoop), "VirtualBox")))
+        {
+            LogRel(("PulseAudio: Failed to allocate context: %s\n",
+                     pa_strerror(pa_context_errno(g_pContext))));
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        if (pa_threaded_mainloop_start(g_pMainLoop) < 0)
+        {
+            LogRel(("PulseAudio: Failed to start threaded mainloop: %s\n",
+                     pa_strerror(pa_context_errno(g_pContext))));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        pa_context_set_state_callback(g_pContext, drvHostPulseAudioCbCtxState, NULL);
+        pa_threaded_mainloop_lock(g_pMainLoop);
+        fLocked = true;
+
+        if (pa_context_connect(g_pContext, NULL /* pszServer */,
+                               PA_CONTEXT_NOFLAGS, NULL) < 0)
+        {
+            LogRel(("PulseAudio: Failed to connect to server: %s\n",
+                     pa_strerror(pa_context_errno(g_pContext))));
+            rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+            break;
+        }
+
+        /* Wait until the g_pContext is ready */
+        for (;;)
+        {
+            pa_context_state_t cstate;
+            pa_threaded_mainloop_wait(g_pMainLoop);
+
+            cstate = pa_context_get_state(g_pContext);
+            if (cstate == PA_CONTEXT_READY)
+                break;
+            else if (   cstate == PA_CONTEXT_TERMINATED
+                     || cstate == PA_CONTEXT_FAILED)
+            {
+                LogRel(("PulseAudio: Failed to initialize context (state %d)\n", cstate));
+                rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+                break;
+            }
+        }
+
+        pa_threaded_mainloop_unlock(g_pMainLoop);
+    }
+    while (0);
+
+    if (RT_FAILURE(rc))
+    {
+        if (g_pMainLoop)
+        {
+            if (fLocked)
+                pa_threaded_mainloop_unlock(g_pMainLoop);
+
+            if (g_pMainLoop)
+                pa_threaded_mainloop_stop(g_pMainLoop);
+        }
+
+        if (g_pContext)
+        {
+            pa_context_disconnect(g_pContext);
+            pa_context_unref(g_pContext);
+            g_pContext = NULL;
+        }
+
+        if (g_pMainLoop)
+        {
+            pa_threaded_mainloop_free(g_pMainLoop);
+            g_pMainLoop = NULL;
+        }
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostPulseAudioInitOut(PPDMIHOSTAUDIO pInterface,
+                                                  PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
+                                                  uint32_t *pcSamples)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+    /* pcSamples is optional. */
+
+    PPULSEAUDIOSTREAM pThisStrmOut = (PPULSEAUDIOSTREAM)pHstStrmOut;
+
+    LogFlowFuncEnter();
+
+    pThisStrmOut->pDrainOp            = NULL;
+
+    pThisStrmOut->SampleSpec.format   = drvHostPulseAudioFmtToPulse(pCfg->enmFormat);
+    pThisStrmOut->SampleSpec.rate     = pCfg->uHz;
+    pThisStrmOut->SampleSpec.channels = pCfg->cChannels;
+
+    /* Note that setting maxlength to -1 does not work on PulseAudio servers
+     * older than 0.9.10. So use the suggested value of 3/2 of tlength */
+    pThisStrmOut->BufAttr.tlength     =   (pa_bytes_per_second(&pThisStrmOut->SampleSpec)
+                                        * s_pulseCfg.buffer_msecs_out) / 1000;
+    pThisStrmOut->BufAttr.maxlength   = (pThisStrmOut->BufAttr.tlength * 3) / 2;
+    pThisStrmOut->BufAttr.prebuf      = -1; /* Same as tlength */
+    pThisStrmOut->BufAttr.minreq      = -1; /* Pulse should set something sensible for minreq on it's own */
+
+    /* Note that the struct BufAttr is updated to the obtained values after this call! */
+    int rc = drvHostPulseAudioOpen(false /* fIn */, "pa.out", &pThisStrmOut->SampleSpec, &pThisStrmOut->BufAttr,
+                                   &pThisStrmOut->pStream);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    PDMAUDIOSTREAMCFG streamCfg;
+    rc = drvHostPulseAudioPulseToFmt(pThisStrmOut->SampleSpec.format,
+                                     &streamCfg.enmFormat, &streamCfg.enmEndianness);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("PulseAudio: Cannot find audio output format %ld\n", pThisStrmOut->SampleSpec.format));
+        return rc;
+    }
+
+    streamCfg.uHz       = pThisStrmOut->SampleSpec.rate;
+    streamCfg.cChannels = pThisStrmOut->SampleSpec.channels;
+
+    rc = drvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props);
+    if (RT_SUCCESS(rc))
+    {
+        int cbBuf = RT_MIN(pThisStrmOut->BufAttr.tlength * 2, pThisStrmOut->BufAttr.maxlength);
+
+        pThisStrmOut->pPCMBuf = RTMemAllocZ(cbBuf);
+        if (pThisStrmOut->pPCMBuf)
+        {
+            if (pcSamples)
+                *pcSamples = cbBuf >> pHstStrmOut->Props.cShift;
+        }
+        else
+            rc = VERR_NO_MEMORY;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(bool) drvHostPulseAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
+{
+    NOREF(pInterface);
+    NOREF(enmDir);
+    return true; /* Always all enabled. */
+}
+
+static DECLCALLBACK(int) drvHostPulseAudioInitIn(PPDMIHOSTAUDIO pInterface,
+                                                 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
+                                                 PDMAUDIORECSOURCE enmRecSource,
+                                                 uint32_t *pcSamples)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+    /* pcSamples is optional. */
+
+    PPULSEAUDIOSTREAM pThisStrmIn = (PPULSEAUDIOSTREAM)pHstStrmIn;
+
+    LogFunc(("enmRecSrc=%ld\n", enmRecSource));
+
+    pThisStrmIn->SampleSpec.format   = drvHostPulseAudioFmtToPulse(pCfg->enmFormat);
+    pThisStrmIn->SampleSpec.rate     = pCfg->uHz;
+    pThisStrmIn->SampleSpec.channels = pCfg->cChannels;
+
+    /* XXX check these values */
+    pThisStrmIn->BufAttr.fragsize    = (pa_bytes_per_second(&pThisStrmIn->SampleSpec)
+                                   * s_pulseCfg.buffer_msecs_in) / 1000;
+    pThisStrmIn->BufAttr.maxlength   = (pThisStrmIn->BufAttr.fragsize * 3) / 2;
+    /* Note: Other members of pa_buffer_attr are ignored for record streams. */
+
+    int rc = drvHostPulseAudioOpen(true /* fIn */, "pa.in", &pThisStrmIn->SampleSpec, &pThisStrmIn->BufAttr,
+                                   &pThisStrmIn->pStream);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    PDMAUDIOSTREAMCFG streamCfg;
+    rc = drvHostPulseAudioPulseToFmt(pThisStrmIn->SampleSpec.format, &streamCfg.enmFormat,
+                                     &streamCfg.enmEndianness);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("PulseAudio: Cannot find audio capture format %ld\n", pThisStrmIn->SampleSpec.format));
+        return rc;
+    }
+
+    streamCfg.uHz       = pThisStrmIn->SampleSpec.rate;
+    streamCfg.cChannels = pThisStrmIn->SampleSpec.channels;
+
+    rc = drvAudioStreamCfgToProps(&streamCfg, &pHstStrmIn->Props);
+    if (RT_SUCCESS(rc))
+    {
+        uint32_t cSamples = RT_MIN(pThisStrmIn->BufAttr.fragsize * 10, pThisStrmIn->BufAttr.maxlength)
+                            >> pHstStrmIn->Props.cShift;
+        LogFunc(("cShift=%RU8, cSamples=%RU32\n", pHstStrmIn->Props.cShift, cSamples));
+
+        if (pcSamples)
+            *pcSamples = cSamples;
+
+        pThisStrmIn->pu8PeekBuf = NULL;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostPulseAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                    uint32_t *pcSamplesCaptured)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+    /* pcSamplesPlayed is optional. */
+
+    PPULSEAUDIOSTREAM pThisStrmIn = (PPULSEAUDIOSTREAM)pHstStrmIn;
+
+    /* We should only call pa_stream_readable_size() once and trust the first value. */
+    pa_threaded_mainloop_lock(g_pMainLoop);
+    size_t cbAvail = pa_stream_readable_size(pThisStrmIn->pStream);
+    pa_threaded_mainloop_unlock(g_pMainLoop);
+
+    if (cbAvail == (size_t)-1)
+        return drvHostPulseAudioError(pThisStrmIn->pDrv, "Failed to determine input data size");
+
+    /* If the buffer was not dropped last call, add what remains. */
+    if (pThisStrmIn->pu8PeekBuf)
+    {
+        Assert(pThisStrmIn->cbPeekBuf >= pThisStrmIn->offPeekBuf);
+        cbAvail += (pThisStrmIn->cbPeekBuf - pThisStrmIn->offPeekBuf);
+    }
+
+    if (!cbAvail) /* No data? Bail out. */
+    {
+        if (pcSamplesCaptured)
+            *pcSamplesCaptured = 0;
+        return VINF_SUCCESS;
+    }
+
+    int rc = VINF_SUCCESS;
+    size_t cbToRead = RT_MIN(cbAvail, audioMixBufFreeBytes(&pHstStrmIn->MixBuf));
+
+    LogFlowFunc(("cbToRead=%zu, cbAvail=%zu, offPeekBuf=%zu, cbPeekBuf=%zu\n",
+                 cbToRead, cbAvail, pThisStrmIn->offPeekBuf, pThisStrmIn->cbPeekBuf));
+
+    size_t offWrite = 0;
+    uint32_t cWrittenTotal = 0;
+
+    while (cbToRead)
+    {
+        /* If there is no data, do another peek. */
+        if (!pThisStrmIn->pu8PeekBuf)
+        {
+            pa_threaded_mainloop_lock(g_pMainLoop);
+            pa_stream_peek(pThisStrmIn->pStream,
+                           (const void**)&pThisStrmIn->pu8PeekBuf, &pThisStrmIn->cbPeekBuf);
+            pa_threaded_mainloop_unlock(g_pMainLoop);
+
+            pThisStrmIn->offPeekBuf = 0;
+
+            /* No data anymore?
+             * Note: If there's a data hole (cbPeekBuf then contains the length of the hole)
+             *       we need to drop the stream lateron. */
+            if (   !pThisStrmIn->pu8PeekBuf
+                && !pThisStrmIn->cbPeekBuf)
+            {
+                break;
+            }
+        }
+
+        Assert(pThisStrmIn->cbPeekBuf >= pThisStrmIn->offPeekBuf);
+        size_t cbToWrite = pThisStrmIn->cbPeekBuf - pThisStrmIn->offPeekBuf;
+
+        LogFlowFunc(("cbToRead=%zu, cbToWrite=%zu, offPeekBuf=%zu, cbPeekBuf=%zu, pu8PeekBuf=%p\n",
+                     cbToRead, cbToWrite,
+                     pThisStrmIn->offPeekBuf, pThisStrmIn->cbPeekBuf, pThisStrmIn->pu8PeekBuf));
+
+        if (cbToWrite)
+        {
+            uint32_t cWritten;
+            rc = audioMixBufWriteCirc(&pHstStrmIn->MixBuf,
+                                      pThisStrmIn->pu8PeekBuf + pThisStrmIn->offPeekBuf,
+                                      cbToWrite, &cWritten);
+            if (RT_FAILURE(rc))
+                break;
+
+            uint32_t cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
+
+            Assert(cbToRead >= cbWritten);
+            cbToRead -= cbWritten;
+            cWrittenTotal += cWritten;
+            pThisStrmIn->offPeekBuf += cbWritten;
+        }
+
+        if (/* Nothing to write anymore? Drop the buffer. */
+               !cbToWrite
+            /* Was there a hole in the peeking buffer? Drop it. */
+            || !pThisStrmIn->pu8PeekBuf
+            /* If the buffer is done, drop it. */
+            || pThisStrmIn->offPeekBuf == pThisStrmIn->cbPeekBuf)
+        {
+            pa_threaded_mainloop_lock(g_pMainLoop);
+            pa_stream_drop(pThisStrmIn->pStream);
+            pa_threaded_mainloop_unlock(g_pMainLoop);
+
+            pThisStrmIn->pu8PeekBuf = NULL;
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        uint32_t cProcessed = 0;
+        if (cWrittenTotal)
+            rc = audioMixBufMixToParent(&pHstStrmIn->MixBuf, cWrittenTotal,
+                                        &cProcessed);
+
+        if (pcSamplesCaptured)
+            *pcSamplesCaptured = cWrittenTotal;
+
+        LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
+                     cWrittenTotal, cProcessed, rc));
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostPulseAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                                  uint32_t *pcSamplesPlayed)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+    /* pcSamplesPlayed is optional. */
+
+    PPULSEAUDIOSTREAM pThisStrmOut = (PPULSEAUDIOSTREAM)pHstStrmOut;
+
+    int rc = VINF_SUCCESS;
+    uint32_t cbReadTotal = 0;
+
+    uint32_t cLive = drvAudioHstOutSamplesLive(pHstStrmOut,
+                                               NULL /* pcStreamsLive */);
+    if (!cLive)
+    {
+        LogFlowFunc(("%p: No live samples, skipping\n", pHstStrmOut));
+
+        if (pcSamplesPlayed)
+            *pcSamplesPlayed = 0;
+        return VINF_SUCCESS;
+    }
+
+    pa_threaded_mainloop_lock(g_pMainLoop);
+
+    do
+    {
+        size_t cbWriteable = pa_stream_writable_size(pThisStrmOut->pStream);
+        if (cbWriteable == (size_t)-1)
+        {
+            rc = drvHostPulseAudioError(pThisStrmOut->pDrv, "Failed to determine output data size");
+            break;
+        }
+
+        size_t cbLive   = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cLive);
+        size_t cbToRead = RT_MIN(cbWriteable, cbLive);
+
+        LogFlowFunc(("cbToRead=%zu, cbWriteable=%zu, cbLive=%zu\n",
+                     cbToRead, cbWriteable, cbLive));
+
+        uint32_t cRead, cbRead;
+        while (cbToRead)
+        {
+            rc = audioMixBufReadCirc(&pHstStrmOut->MixBuf, pThisStrmOut->pPCMBuf, cbToRead, &cRead);
+            if (RT_FAILURE(rc))
+                break;
+
+            cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
+            if (pa_stream_write(pThisStrmOut->pStream, pThisStrmOut->pPCMBuf, cbRead,
+                                NULL /* Cleanup callback */, 0, PA_SEEK_RELATIVE) < 0)
+            {
+                rc = drvHostPulseAudioError(pThisStrmOut->pDrv, "Failed to write to output stream");
+                break;
+            }
+
+            Assert(cbToRead >= cRead);
+            cbToRead -= cbRead;
+            cbReadTotal += cbRead;
+        }
+
+    } while (0);
+
+    pa_threaded_mainloop_unlock(g_pMainLoop);
+
+    if (RT_SUCCESS(rc))
+    {
+        uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
+        if (cReadTotal)
+            audioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
+
+        if (pcSamplesPlayed)
+            *pcSamplesPlayed = cReadTotal;
+
+        LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n",
+                     cReadTotal, cbReadTotal, rc));
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+/** @todo Implement va handling. */
+static int drvHostPulseAudioError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(szMsg, VERR_INVALID_POINTER);
+
+    if (pThis->cLogErrors++ < VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS)
+    {
+        int rc2 = pa_context_errno(g_pContext);
+        LogRel(("PulseAudio: %s: %s\n", szMsg, pa_strerror(rc2)));
+    }
+
+    /** @todo Implement some PulseAudio -> IPRT mapping here. */
+    return VERR_GENERAL_FAILURE;
+}
+
+static DECLCALLBACK(int) drvHostPulseAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+
+    LogFlowFuncEnter();
+
+    PPULSEAUDIOSTREAM pThisStrmIn = (PPULSEAUDIOSTREAM)pHstStrmIn;
+    if (pThisStrmIn->pStream)
+    {
+        pa_threaded_mainloop_lock(g_pMainLoop);
+        pa_stream_disconnect(pThisStrmIn->pStream);
+        pa_stream_unref(pThisStrmIn->pStream);
+        pa_threaded_mainloop_unlock(g_pMainLoop);
+
+        pThisStrmIn->pStream = NULL;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostPulseAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+
+    LogFlowFuncEnter();
+
+    PPULSEAUDIOSTREAM pThisStrmOut = (PPULSEAUDIOSTREAM)pHstStrmOut;
+    if (pThisStrmOut->pStream)
+    {
+        pa_threaded_mainloop_lock(g_pMainLoop);
+        pa_stream_disconnect(pThisStrmOut->pStream);
+        pa_stream_unref(pThisStrmOut->pStream);
+        pa_threaded_mainloop_unlock(g_pMainLoop);
+
+        pThisStrmOut->pStream = NULL;
+    }
+
+    if (pThisStrmOut->pPCMBuf)
+    {
+        RTMemFree(pThisStrmOut->pPCMBuf);
+        pThisStrmOut->pPCMBuf = NULL;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostPulseAudioControlOut(PPDMIHOSTAUDIO pInterface,
+                                                     PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+
+    PPULSEAUDIOSTREAM pThisStrmOut = (PPULSEAUDIOSTREAM)pHstStrmOut;
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
+
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        {
+            pa_threaded_mainloop_lock(g_pMainLoop);
+
+            if (   pThisStrmOut->pDrainOp
+                && pa_operation_get_state(pThisStrmOut->pDrainOp) != PA_OPERATION_DONE)
+            {
+                pa_operation_cancel(pThisStrmOut->pDrainOp);
+                pa_operation_unref(pThisStrmOut->pDrainOp);
+
+                pThisStrmOut->pDrainOp = NULL;
+            }
+            else
+            {
+                /* This should return immediately. */
+                rc = drvHostPulseAudioWaitFor(pa_stream_cork(pThisStrmOut->pStream, 0,
+                                                             drvHostPulseAudioCbSuccess, pThisStrmOut),
+                                              15 * 1000 /* 15s timeout */);
+            }
+
+            pa_threaded_mainloop_unlock(g_pMainLoop);
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        {
+            /* Pause audio output (the Pause bit of the AC97 x_CR register is set).
+             * Note that we must return immediately from here! */
+            pa_threaded_mainloop_lock(g_pMainLoop);
+            if (!pThisStrmOut->pDrainOp)
+            {
+                /* This should return immediately. */
+                rc = drvHostPulseAudioWaitFor(pa_stream_trigger(pThisStrmOut->pStream,
+                                                                drvHostPulseAudioCbSuccess, pThisStrmOut),
+                                              15 * 1000 /* 15s timeout */);
+                if (RT_LIKELY(RT_SUCCESS(rc)))
+                    pThisStrmOut->pDrainOp = pa_stream_drain(pThisStrmOut->pStream,
+                                                             drvHostPulseAudioCbStreamDrain, pThisStrmOut);
+            }
+            pa_threaded_mainloop_unlock(g_pMainLoop);
+            break;
+        }
+
+        default:
+            AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
+            rc = VERR_INVALID_PARAMETER;
+            break;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostPulseAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                    PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+
+    PPULSEAUDIOSTREAM pThisStrmIn = (PPULSEAUDIOSTREAM)pHstStrmIn;
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
+
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        {
+            pa_threaded_mainloop_lock(g_pMainLoop);
+            /* This should return immediately. */
+            rc = drvHostPulseAudioWaitFor(pa_stream_cork(pThisStrmIn->pStream, 0 /* Play / resume */,
+                                                         drvHostPulseAudioCbSuccess, pThisStrmIn),
+                                          15 * 1000 /* 15s timeout */);
+            pa_threaded_mainloop_unlock(g_pMainLoop);
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        {
+            pa_threaded_mainloop_lock(g_pMainLoop);
+            if (pThisStrmIn->pu8PeekBuf) /* Do we need to drop the peek buffer?*/
+            {
+                pa_stream_drop(pThisStrmIn->pStream);
+                pThisStrmIn->pu8PeekBuf = NULL;
+            }
+            /* This should return immediately. */
+            rc = drvHostPulseAudioWaitFor(pa_stream_cork(pThisStrmIn->pStream, 1 /* Stop / pause */,
+                                                         drvHostPulseAudioCbSuccess, pThisStrmIn),
+                                          15 * 1000 /* 15s timeout */);
+            pa_threaded_mainloop_unlock(g_pMainLoop);
+            break;
+        }
+
+        default:
+            AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
+            rc = VERR_INVALID_PARAMETER;
+            break;
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostPulseAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    pCfg->cbStreamOut = sizeof(PULSEAUDIOSTREAM);
+    pCfg->cbStreamIn = sizeof(PULSEAUDIOSTREAM);
+    pCfg->cMaxHstStrmsOut = INT_MAX;
+    pCfg->cMaxHstStrmsIn = INT_MAX;
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvHostPulseAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    AssertPtrReturn(pInterface, NULL);
+    AssertPtrReturn(pszIID, NULL);
+
+    PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
+
+    return NULL;
+}
+
+static DECLCALLBACK(void) drvHostPulseAudioDestruct(PPDMDRVINS pDrvIns)
+{
+    NOREF(pDrvIns);
+
+    LogFlowFuncEnter();
+
+    if (g_pMainLoop)
+        pa_threaded_mainloop_stop(g_pMainLoop);
+
+    if (g_pContext)
+    {
+        pa_context_disconnect(g_pContext);
+        pa_context_unref(g_pContext);
+        g_pContext = NULL;
+    }
+
+    if (g_pMainLoop)
+    {
+        pa_threaded_mainloop_free(g_pMainLoop);
+        g_pMainLoop = NULL;
+    }
+
+    LogFlowFuncLeave();
+}
+
+/**
+ * Construct a DirectSound Audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvHostPulseAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+    AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
+    /* pCfg is optional. */
+
+    PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
+    LogRel(("Audio: Initializing PulseAudio driver\n"));
+
+    pThis->pDrvIns                   = pDrvIns;
+    /* IBase */
+    pDrvIns->IBase.pfnQueryInterface = drvHostPulseAudioQueryInterface;
+    /* IHostAudio */
+    PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostPulseAudio);
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Char driver registration record.
+ */
+const PDMDRVREG g_DrvHostPulseAudio =
+{
+    /* u32Version */
+    PDM_DRVREG_VERSION,
+    /* szName */
+    "PulseAudio",
+    /* szRCMod */
+    "",
+    /* szR0Mod */
+    "",
+    /* pszDescription */
+    "Pulse Audio host driver",
+    /* fFlags */
+     PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+    /* fClass. */
+    PDM_DRVREG_CLASS_AUDIO,
+    /* cMaxInstances */
+    ~0U,
+    /* cbInstance */
+    sizeof(DRVHOSTPULSEAUDIO),
+    /* pfnConstruct */
+    drvHostPulseAudioConstruct,
+    /* pfnDestruct */
+    drvHostPulseAudioDestruct,
+    /* pfnRelocate */
+    NULL,
+    /* pfnIOCtl */
+    NULL,
+    /* pfnPowerOn */
+    NULL,
+    /* pfnReset */
+    NULL,
+    /* pfnSuspend */
+    NULL,
+    /* pfnResume */
+    NULL,
+    /* pfnAttach */
+    NULL,
+    /* pfnDetach */
+    NULL,
+    /* pfnPowerOff */
+    NULL,
+    /* pfnSoftReset */
+    NULL,
+    /* u32EndVersion */
+    PDM_DRVREG_VERSION
+};
+
+static struct audio_option pulse_options[] =
+{
+    {"DAC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_out,
+     "DAC period size in milliseconds", NULL, 0},
+    {"ADC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_in,
+     "ADC period size in milliseconds", NULL, 0},
+
+    NULL
+};
+
