Index: /trunk/Config.kmk
===================================================================
--- /trunk/Config.kmk	(revision 61412)
+++ /trunk/Config.kmk	(revision 61413)
@@ -228,4 +228,6 @@
 ifeq ($(int-mod $(VBOX_VERSION_BUILD),2),0)
   VBOX_RELEASE_EXACT_MATCH=1
+  # Decide whether to use the stable (5.0) audio code or not.
+  VBOX_WITH_AUDIO_STABLE:=1
 endif
 ifneq ($(VBOX_RELEASE_EXACT_MATCH),)
@@ -432,4 +434,11 @@
 # Enable new PS/2 mouse emulation.
 VBOX_WITH_NEW_PS2M = 1
+ifdef VBOX_WITH_AUDIO_STABLE
+ VBOX_AUDIO_PATH_SOURCES=Audio_old
+ VBOX_AUDIO_FILE_SUFFIX=_old
+else
+ VBOX_AUDIO_PATH_SOURCES=Audio
+ VBOX_AUDIO_FILE_SUFFIX=
+endif
 # Enable OSS audio support.
 VBOX_WITH_OSS = 1
Index: /trunk/include/VBox/vmm/pdmaudioifs.h
===================================================================
--- /trunk/include/VBox/vmm/pdmaudioifs.h	(revision 61412)
+++ /trunk/include/VBox/vmm/pdmaudioifs.h	(revision 61413)
@@ -32,4 +32,8 @@
 #include <iprt/list.h>
 
+#ifdef VBOX_WITH_AUDIO_STABLE
+# undef ___VBox_vmm_pdmaudioifs_h
+# include "pdmaudioifs_old.h"
+#else
 
 /** @defgroup grp_pdm_ifs_audio     PDM Audio Interfaces
@@ -863,4 +867,6 @@
 /** @} */
 
+#endif /* VBOX_WITH_AUDIO_STABLE */
+
 #endif /* !___VBox_vmm_pdmaudioifs_h */
 
Index: /trunk/include/VBox/vmm/pdmaudioifs_old.h
===================================================================
--- /trunk/include/VBox/vmm/pdmaudioifs_old.h	(revision 61413)
+++ /trunk/include/VBox/vmm/pdmaudioifs_old.h	(revision 61413)
@@ -0,0 +1,786 @@
+/** @file
+ * PDM - Pluggable Device Manager, audio interfaces.
+ */
+
+/*
+ * Copyright (C) 2006-2016 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.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+#ifndef ___VBox_vmm_pdmaudioifs_h
+#define ___VBox_vmm_pdmaudioifs_h
+
+#include <VBox/types.h>
+#include <iprt/critsect.h>
+#include <iprt/list.h>
+
+
+/** @defgroup grp_pdm_ifs_audio     PDM Audio Interfaces
+ * @ingroup grp_pdm_interfaces
+ * @{
+ */
+
+/** @todo r=bird: Don't be lazy with documentation! */
+typedef uint32_t PDMAUDIODRVFLAGS;
+
+/** No flags set. */
+/** @todo r=bird: s/PDMAUDIODRVFLAG/PDMAUDIODRV_FLAGS/g */
+#define PDMAUDIODRVFLAG_NONE        0
+/** Marks a primary audio driver which is critical
+ *  when running the VM. */
+#define PDMAUDIODRVFLAG_PRIMARY     RT_BIT(0)
+
+/**
+ * Audio format in signed or unsigned variants.
+ */
+typedef enum PDMAUDIOFMT
+{
+    AUD_FMT_INVALID,
+    AUD_FMT_U8,
+    AUD_FMT_S8,
+    AUD_FMT_U16,
+    AUD_FMT_S16,
+    AUD_FMT_U32,
+    AUD_FMT_S32,
+    /** Hack to blow the type up to 32-bit. */
+    AUD_FMT_32BIT_HACK = 0x7fffffff
+} PDMAUDIOFMT;
+
+/**
+ * Audio configuration of a certain backend.
+ */
+typedef struct PDMAUDIOBACKENDCFG
+{
+    size_t   cbStreamOut;
+    size_t   cbStreamIn;
+    uint32_t cMaxHstStrmsOut;
+    uint32_t cMaxHstStrmsIn;
+} PDMAUDIOBACKENDCFG, *PPDMAUDIOBACKENDCFG;
+
+/**
+ * An audio sample. At the moment stereo (left + right channels) only.
+ * @todo Replace this with a more generic union
+ *       which then also could handle 2.1 or 5.1 sound.
+ */
+typedef struct PDMAUDIOSAMPLE
+{
+    int64_t i64LSample;
+    int64_t i64RSample;
+} PDMAUDIOSAMPLE, *PPDMAUDIOSAMPLE;
+
+typedef enum PDMAUDIOENDIANNESS
+{
+    /** The usual invalid endian. */
+    PDMAUDIOENDIANNESS_INVALID,
+    /** Little endian. */
+    PDMAUDIOENDIANNESS_LITTLE,
+    /** Bit endian. */
+    PDMAUDIOENDIANNESS_BIG,
+    /** Endianness doesn't have a meaning in the context. */
+    PDMAUDIOENDIANNESS_NA,
+    /** The end of the valid endian values (exclusive). */
+    PDMAUDIOENDIANNESS_END,
+    /** Hack to blow the type up to 32-bit. */
+    PDMAUDIOENDIANNESS_32BIT_HACK = 0x7fffffff
+} PDMAUDIOENDIANNESS;
+
+typedef struct PDMAUDIOSTREAMCFG
+{
+    /** Frequency in Hertz (Hz). */
+    uint32_t uHz;
+    /** Number of channels (2 for stereo). */
+    uint8_t cChannels;
+    /** Audio format. */
+    PDMAUDIOFMT enmFormat;
+    /** @todo Use RT_LE2H_*? */
+    PDMAUDIOENDIANNESS enmEndianness;
+} PDMAUDIOSTREAMCFG, *PPDMAUDIOSTREAMCFG;
+
+#if defined(RT_LITTLE_ENDIAN)
+# define PDMAUDIOHOSTENDIANNESS PDMAUDIOENDIANNESS_LITTLE
+#elif defined(RT_BIG_ENDIAN)
+# define PDMAUDIOHOSTENDIANNESS PDMAUDIOENDIANNESS_BIG
+#else
+# error "Port me!"
+#endif
+
+/**
+ * Audio direction.
+ */
+typedef enum PDMAUDIODIR
+{
+    PDMAUDIODIR_UNKNOWN    = 0,
+    PDMAUDIODIR_IN         = 1,
+    PDMAUDIODIR_OUT        = 2,
+    PDMAUDIODIR_DUPLEX     = 3,
+    /** Hack to blow the type up to 32-bit. */
+    PDMAUDIODIR_32BIT_HACK = 0x7fffffff
+} PDMAUDIODIR;
+
+/**
+ * Audio mixer controls.
+ */
+typedef enum PDMAUDIOMIXERCTL
+{
+    PDMAUDIOMIXERCTL_UNKNOWN = 0,
+    PDMAUDIOMIXERCTL_VOLUME,
+    PDMAUDIOMIXERCTL_PCM,
+    PDMAUDIOMIXERCTL_LINE_IN,
+    PDMAUDIOMIXERCTL_MIC_IN,
+    /** Hack to blow the type up to 32-bit. */
+    PDMAUDIOMIXERCTL_32BIT_HACK = 0x7fffffff
+} PDMAUDIOMIXERCTL;
+
+/**
+ * Audio recording sources.
+ */
+typedef enum PDMAUDIORECSOURCE
+{
+    PDMAUDIORECSOURCE_UNKNOWN = 0,
+    PDMAUDIORECSOURCE_MIC,
+    PDMAUDIORECSOURCE_CD,
+    PDMAUDIORECSOURCE_VIDEO,
+    PDMAUDIORECSOURCE_AUX,
+    PDMAUDIORECSOURCE_LINE_IN,
+    PDMAUDIORECSOURCE_PHONE,
+    /** Hack to blow the type up to 32-bit. */
+    PDMAUDIORECSOURCE_32BIT_HACK = 0x7fffffff
+} PDMAUDIORECSOURCE;
+
+/**
+ * Audio stream commands. Used in the audio connector
+ * as well as in the actual host backends.
+ */
+typedef enum PDMAUDIOSTREAMCMD
+{
+    /** Unknown command, do not use. */
+    PDMAUDIOSTREAMCMD_UNKNOWN = 0,
+    /** Enables the stream. */
+    PDMAUDIOSTREAMCMD_ENABLE,
+    /** Disables the stream. */
+    PDMAUDIOSTREAMCMD_DISABLE,
+    /** Pauses the stream. */
+    PDMAUDIOSTREAMCMD_PAUSE,
+    /** Resumes the stream. */
+    PDMAUDIOSTREAMCMD_RESUME,
+    /** Hack to blow the type up to 32-bit. */
+    PDMAUDIOSTREAMCMD_32BIT_HACK = 0x7fffffff
+} PDMAUDIOSTREAMCMD;
+
+/**
+ * Properties of audio streams for host/guest
+ * for in or out directions.
+ */
+typedef struct PDMPCMPROPS
+{
+    /** Sample width. Bits per sample. */
+    uint8_t     cBits;
+    /** Signed or unsigned sample. */
+    bool        fSigned;
+    /** Shift count used for faster calculation of various
+     *  values, such as the alignment, bytes to samples and so on.
+     *  Depends on number of stream channels and the stream format
+     *  being used.
+     *
+     ** @todo Use some RTAsmXXX functions instead?
+     */
+    uint8_t     cShift;
+    /** Number of audio channels. */
+    uint8_t     cChannels;
+    /** Alignment mask. */
+    uint32_t    uAlign;
+    /** Sample frequency in Hertz (Hz). */
+    uint32_t    uHz;
+    /** Bandwidth (bytes/s). */
+    uint32_t    cbPerSec;
+    /** Whether the endianness is swapped or not. */
+    bool        fSwapEndian;
+} PDMPCMPROPS, *PPDMPCMPROPS;
+
+/**
+ * Structure keeping an audio volume level.
+ */
+typedef struct PDMAUDIOVOLUME
+{
+    /** Set to @c true if this stream is muted, @c false if not. */
+    bool                   fMuted;
+    /** Left channel volume. */
+    uint32_t               uLeft;
+    /** Right channel volume. */
+    uint32_t               uRight;
+} PDMAUDIOVOLUME, *PPDMAUDIOVOLUME;
+
+/**
+ * Structure for holding rate processing information
+ * of a source + destination audio stream. This is needed
+ * because both streams can differ regarding their rates
+ * and therefore need to be treated accordingly.
+ */
+typedef struct PDMAUDIOSTRMRATE
+{
+    /** Current (absolute) offset in the output
+     *  (destination) stream. */
+    uint64_t       dstOffset;
+    /** Increment for moving dstOffset for the
+     *  destination stream. This is needed because the
+     *  source <-> destination rate might be different. */
+    uint64_t       dstInc;
+    /** Current (absolute) offset in the input
+     *  stream. */
+    uint32_t       srcOffset;
+    /** Last processed sample of the input stream.
+     *  Needed for interpolation. */
+    PDMAUDIOSAMPLE srcSampleLast;
+} PDMAUDIOSTRMRATE, *PPDMAUDIOSTRMRATE;
+
+/**
+ * Note: All internal handling is done in samples,
+ *       not in bytes!
+ */
+typedef uint32_t PDMAUDIOMIXBUFFMT;
+typedef PDMAUDIOMIXBUFFMT *PPDMAUDIOMIXBUFFMT;
+
+typedef struct PDMAUDIOMIXBUF *PPDMAUDIOMIXBUF;
+typedef struct PDMAUDIOMIXBUF
+{
+    RTLISTNODE             Node;
+    /** Name of the buffer. */
+    char                  *pszName;
+    /** Sample buffer. */
+    PPDMAUDIOSAMPLE        pSamples;
+    /** Size of the sample buffer (in samples). */
+    uint32_t               cSamples;
+    /** The current read/write position (in samples)
+     *  in the samples buffer. */
+    uint32_t               offReadWrite;
+    /**
+     * Total samples already mixed down to the parent buffer (if any). Always starting at
+     * the parent's offReadWrite position.
+     *
+     * Note: Count always is specified in parent samples, as the sample count can differ between parent
+     *       and child.
+     */
+    uint32_t               cMixed;
+    uint32_t               cProcessed;
+    /** Pointer to parent buffer (if any). */
+    PPDMAUDIOMIXBUF        pParent;
+    /** List of children mix buffers to keep in sync with (if being a parent buffer). */
+    RTLISTANCHOR           lstBuffers;
+    /** Intermediate structure for buffer conversion tasks. */
+    PPDMAUDIOSTRMRATE      pRate;
+    /** Current volume used for mixing. */
+    PDMAUDIOVOLUME         Volume;
+    /** This buffer's audio format. */
+    PDMAUDIOMIXBUFFMT      AudioFmt;
+    /**
+     * Ratio of the associated parent stream's frequency by this stream's
+     * frequency (1<<32), represented as a signed 64 bit integer.
+     *
+     * For example, if the parent stream has a frequency of 44 khZ, and this
+     * stream has a frequency of 11 kHz, the ration then would be
+     * (44/11 * (1 << 32)).
+     *
+     * Currently this does not get changed once assigned.
+     */
+    int64_t                iFreqRatio;
+    /* For quickly converting samples <-> bytes and
+     * vice versa. */
+    uint8_t                cShift;
+} PDMAUDIOMIXBUF;
+
+/** Stream status flag. To be used with PDMAUDIOSTRMSTS_FLAG_ flags. */
+typedef uint32_t PDMAUDIOSTRMSTS;
+
+/** No flags being set. */
+#define PDMAUDIOSTRMSTS_FLAG_NONE            0
+/** Whether this stream is enabled or disabled. */
+#define PDMAUDIOSTRMSTS_FLAG_ENABLED         RT_BIT_32(0)
+/** Whether this stream has been paused or not. This also implies
+ *  that this is an enabled stream! */
+#define PDMAUDIOSTRMSTS_FLAG_PAUSED          RT_BIT_32(1)
+/** Whether this stream was marked as being disabled
+ *  but there are still associated guest output streams
+ *  which rely on its data. */
+#define PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE RT_BIT_32(2)
+/** Validation mask. */
+#define PDMAUDIOSTRMSTS_VALID_MASK           UINT32_C(0x00000007)
+
+/**
+ * Represents an audio input on the host of a certain
+ * backend (e.g. DirectSound, PulseAudio etc).
+ *
+ * One host audio input is assigned to exactly one parent
+ * guest input stream.
+ */
+struct PDMAUDIOGSTSTRMIN;
+typedef PDMAUDIOGSTSTRMIN *PPDMAUDIOGSTSTRMIN;
+
+typedef struct PDMAUDIOHSTSTRMIN
+{
+    /** List node. */
+    RTLISTNODE             Node;
+    /** PCM properties. */
+    PDMPCMPROPS            Props;
+    /** Stream status flag. */
+    PDMAUDIOSTRMSTS        fStatus;
+    /** Critical section for serializing access. */
+    RTCRITSECT             CritSect;
+    /** This stream's mixing buffer. */
+    PDMAUDIOMIXBUF         MixBuf;
+    /** Pointer to (parent) guest stream. */
+    PPDMAUDIOGSTSTRMIN     pGstStrmIn;
+} PDMAUDIOHSTSTRMIN, *PPDMAUDIOHSTSTRMIN;
+
+/*
+ * Represents an audio output on the host through a certain
+ * backend (e.g. DirectSound, PulseAudio etc).
+ *
+ * One host audio output can have multiple (1:N) guest outputs
+ * assigned.
+ */
+typedef struct PDMAUDIOHSTSTRMOUT
+{
+    /** List node. */
+    RTLISTNODE             Node;
+    /** Stream properites. */
+    PDMPCMPROPS            Props;
+    /** Stream status flag. */
+    PDMAUDIOSTRMSTS        fStatus;
+    /** Critical section for serializing access. */
+    RTCRITSECT             CritSect;
+    /** This stream's mixing buffer. */
+    PDMAUDIOMIXBUF         MixBuf;
+    /** Associated guest output streams. */
+    RTLISTANCHOR           lstGstStrmOut;
+} PDMAUDIOHSTSTRMOUT, *PPDMAUDIOHSTSTRMOUT;
+
+/**
+ * Guest audio stream state.
+ */
+typedef struct PDMAUDIOGSTSTRMSTATE
+{
+    /** Guest audio out stream active or not. */
+    bool                   fActive;
+    /** Guest audio output stream has some samples or not. */
+    bool                   fEmpty;
+    /** Name of this stream. */
+    char                  *pszName;
+    /** Number of references to this stream. Only can be
+     *  destroyed if the reference count is reaching 0. */
+    uint8_t                cRefs;
+} PDMAUDIOGSTSTRMSTATE, *PPDMAUDIOGSTSTRMSTATE;
+
+/**
+ * Represents an audio input from the guest (that is, from the
+ * emulated device, e.g. Intel HDA).
+ *
+ * Each guest input can have multiple host input streams.
+ */
+typedef struct PDMAUDIOGSTSTRMIN
+{
+    /** Guest stream properites. */
+    PDMPCMPROPS            Props;
+    /** Current stream state. */
+    PDMAUDIOGSTSTRMSTATE   State;
+    /** This stream's mixing buffer. */
+    PDMAUDIOMIXBUF         MixBuf;
+    /** Pointer to associated host input stream. */
+    PPDMAUDIOHSTSTRMIN     pHstStrmIn;
+} PDMAUDIOGSTSTRMIN, *PPDMAUDIOGSTSTRMIN;
+
+/**
+ * Represents an audio output from the guest (that is, from the
+ * emulated device, e.g. Intel HDA).
+ *
+ * Each guest output is assigned to a single host output.
+ */
+typedef struct PDMAUDIOGSTSTRMOUT
+{
+    /** List node. */
+    RTLISTNODE             Node;
+    /** Guest output stream properites. */
+    PDMPCMPROPS            Props;
+    /** Current stream state. */
+    PDMAUDIOGSTSTRMSTATE   State;
+    /** This stream's mixing buffer. */
+    PDMAUDIOMIXBUF         MixBuf;
+    /** Pointer to the associated host output stream. */
+    PPDMAUDIOHSTSTRMOUT    pHstStrmOut;
+} PDMAUDIOGSTSTRMOUT, *PPDMAUDIOGSTSTRMOUT;
+
+/** Pointer to a audio connector interface. */
+typedef struct PDMIAUDIOCONNECTOR *PPDMIAUDIOCONNECTOR;
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+/**
+ * Audio callback types. These are all kept generic as those
+ * are used by all device emulations across all backends.
+ */
+typedef enum PDMAUDIOCALLBACKTYPE
+{
+    PDMAUDIOCALLBACKTYPE_GENERIC = 0,
+    PDMAUDIOCALLBACKTYPE_INPUT,
+    PDMAUDIOCALLBACKTYPE_OUTPUT
+} PDMAUDIOCALLBACKTYPE;
+
+/**
+ * Callback data for audio input.
+ */
+typedef struct PDMAUDIOCALLBACKDATAIN
+{
+    /** Input: How many bytes are availabe as input for passing
+     *         to the device emulation. */
+    uint32_t cbInAvail;
+    /** Output: How many bytes have been read. */
+    uint32_t cbOutRead;
+} PDMAUDIOCALLBACKDATAIN, *PPDMAUDIOCALLBACKDATAIN;
+
+/**
+ * Callback data for audio output.
+ */
+typedef struct PDMAUDIOCALLBACKDATAOUT
+{
+    /** Input:  How many bytes are free for the device emulation to write. */
+    uint32_t cbInFree;
+    /** Output: How many bytes were written by the device emulation. */
+    uint32_t cbOutWritten;
+} PDMAUDIOCALLBACKDATAOUT, *PPDMAUDIOCALLBACKDATAOUT;
+
+/**
+ * Structure for keeping an audio callback.
+ */
+typedef struct PDMAUDIOCALLBACK
+{
+    RTLISTANCHOR          Node;
+    PDMAUDIOCALLBACKTYPE  enmType;
+    void                 *pvCtx;
+    size_t                cbCtx;
+    DECLR3CALLBACKMEMBER(int, pfnCallback, (PDMAUDIOCALLBACKTYPE enmType, void *pvCtx, size_t cbCtx, void *pvUser, size_t cbUser));
+} PDMAUDIOCALLBACK, *PPDMAUDIOCALLBACK;
+#endif
+
+/**
+ * Audio connector interface (up).
+ */
+typedef struct PDMIAUDIOCONNECTOR
+{
+    DECLR3CALLBACKMEMBER(int, pfnQueryStatus, (PPDMIAUDIOCONNECTOR pInterface, uint32_t *pcbAvailIn, uint32_t *pcbFreeOut, uint32_t *pcSamplesLive));
+
+    /**
+     * Reads PCM audio data from the host (input).
+     *
+     * @returns VBox status code.
+     * @param   pInterface      Pointer to the interface structure containing the called function pointer.
+     * @param   pGstStrmIn      Pointer to guest input stream to write to.
+     * @param   pvBuf           Where to store the read data.
+     * @param   cbBuf           Number of bytes to read.
+     * @param   pcbRead         Bytes of audio data read. Optional.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnRead, (PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead));
+
+    /**
+     * Writes PCM audio data to the host (output).
+     *
+     * @returns VBox status code.
+     * @param   pInterface      Pointer to the interface structure containing the called function pointer.
+     * @param   pGstStrmOut     Pointer to guest output stream to read from.
+     * @param   pvBuf           Audio data to be written.
+     * @param   cbBuf           Number of bytes to be written.
+     * @param   pcbWritten      Bytes of audio data written. Optional.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnWrite, (PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten));
+
+    /**
+     * Retrieves the current configuration of the host audio backend.
+     *
+     * @returns VBox status code.
+     *
+     * @param   pInterface      Pointer to the interface structure containing the called function pointer.
+     * @param   pCfg            Where to store the host audio backend configuration data.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetConfiguration, (PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg));
+
+    /**
+     * Checks whether a specific guest input stream is active or not.
+     *
+     * @returns Whether the specified stream is active or not.
+     * @param   pInterface      Pointer to the interface structure containing the called function pointer.
+     * @param   pGstStrmIn      Pointer to guest input stream.
+     */
+    DECLR3CALLBACKMEMBER(bool, pfnIsActiveIn, (PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn));
+
+    /**
+     * Checks whether a specific guest output stream is active or not.
+     *
+     * @returns Whether the specified stream is active or not.
+     * @param   pInterface      Pointer to the interface structure containing the called function pointer.
+     * @param   pGstStrmOut     Pointer to guest output stream.
+     */
+    DECLR3CALLBACKMEMBER(bool, pfnIsActiveOut, (PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut));
+
+    /**
+     * Checks whether the specified guest input stream is in a valid (working) state.
+     *
+     * @returns True if a host voice in is available, false if not.
+     * @param   pInterface      Pointer to the interface structure containing the called function pointer.
+     * @param   pGstStrmIn      Pointer to guest input stream to check.
+     */
+    DECLR3CALLBACKMEMBER(bool, pfnIsValidIn, (PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn));
+
+    /**
+     * Checks whether the specified guest output stream is in a valid (working) state.
+     *
+     * @returns True if a host voice out is available, false if not.
+     * @param   pInterface      Pointer to the interface structure containing the called function pointer.
+     * @param   pGstStrmOut     Pointer to guest output stream to check.
+     */
+    DECLR3CALLBACKMEMBER(bool, pfnIsValidOut, (PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut));
+
+    /**
+     * Enables a specific guest output stream and starts the audio device.
+     *
+     * @returns VBox status code.
+     * @param   pInterface      Pointer to the interface structure containing the called function pointer.
+     * @param   pGstStrmOut     Pointer to guest output stream.
+     * @param   fEnable         Whether to enable or disable the specified output stream.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnEnableOut, (PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut, bool fEnable));
+
+    /**
+     * Enables a specific guest input stream and starts the audio device.
+     *
+     * @returns VBox status code.
+     * @param   pInterface      Pointer to the interface structure containing the called function pointer.
+     * @param   pGstStrmIn      Pointer to guest input stream.
+     * @param   fEnable         Whether to enable or disable the specified input stream.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnEnableIn, (PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn, bool fEnable));
+
+    /**
+     * Creates a guest input stream.
+     *
+     * @returns VBox status code.
+     * @param   pInterface           Pointer to the interface structure containing the called function pointer.
+     * @param   pszName              Name of the audio channel.
+     * @param   enmRecSource         Specifies the type of recording source to be opened.
+     * @param   pCfg                 Pointer to PDMAUDIOSTREAMCFG to use.
+     * @param   ppGstStrmIn          Pointer where to return the guest guest input stream on success.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnCreateIn, (PPDMIAUDIOCONNECTOR pInterface, const char *pszName,
+                                            PDMAUDIORECSOURCE enmRecSource, PPDMAUDIOSTREAMCFG pCfg,
+                                            PPDMAUDIOGSTSTRMIN *ppGstStrmIn));
+    /**
+     * Creates a guest output stream.
+     *
+     * @returns VBox status code.
+     * @param   pInterface           Pointer to the interface structure containing the called function pointer.
+     * @param   pszName              Name of the audio channel.
+     * @param   pCfg                 Pointer to PDMAUDIOSTREAMCFG to use.
+     * @param   ppGstStrmOut         Pointer where to return the guest guest input stream on success.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnCreateOut, (PPDMIAUDIOCONNECTOR pInterface, const char *pszName,
+                                             PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMOUT *ppGstStrmOut));
+
+    /**
+     * Destroys a guest input stream.
+     *
+     * @param   pInterface      Pointer to the interface structure containing the called function pointer.
+     * @param   pGstStrmIn      Pointer to guest input stream.
+     */
+    DECLR3CALLBACKMEMBER(void, pfnDestroyIn, (PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn));
+
+    /**
+     * Destroys a guest output stream.
+     *
+     * @param   pInterface      Pointer to the interface structure containing the called function pointer.
+     * @param   pGstStrmOut     Pointer to guest output stream.
+     */
+    DECLR3CALLBACKMEMBER(void, pfnDestroyOut, (PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut));
+
+    /**
+     * Plays (transfers) all available samples via the connected host backend.
+     *
+     * @returns VBox status code.
+     * @param   pInterface           Pointer to the interface structure containing the called function pointer.
+     * @param   pcSamplesPlayed      Number of samples played. Optional.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnPlayOut, (PPDMIAUDIOCONNECTOR pInterface, uint32_t *pcSamplesPlayed));
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    DECLR3CALLBACKMEMBER(int, pfnRegisterCallbacks, (PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOCALLBACK paCallbacks, size_t cCallbacks));
+    DECLR3CALLBACKMEMBER(int, pfnCallback, (PPDMIAUDIOCONNECTOR pInterface, PDMAUDIOCALLBACKTYPE enmType, void *pvUser, size_t cbUser));
+#endif
+
+} PDMIAUDIOCONNECTOR;
+
+/** PDMIAUDIOCONNECTOR interface ID. */
+#define PDMIAUDIOCONNECTOR_IID                  "8f8ca10e-9039-423c-9a77-0014aaa98626"
+
+
+/**
+ * Assigns all needed interface callbacks for an audio backend.
+ *
+ * @param   a_NamePrefix        The function name prefix.
+ */
+#define PDMAUDIO_IHOSTAUDIO_CALLBACKS(a_NamePrefix) \
+    do { \
+        pThis->IHostAudio.pfnInit       = RT_CONCAT(a_NamePrefix,Init); \
+        pThis->IHostAudio.pfnShutdown   = RT_CONCAT(a_NamePrefix,Shutdown); \
+        pThis->IHostAudio.pfnInitIn     = RT_CONCAT(a_NamePrefix,InitIn); \
+        pThis->IHostAudio.pfnInitOut    = RT_CONCAT(a_NamePrefix,InitOut); \
+        pThis->IHostAudio.pfnControlOut = RT_CONCAT(a_NamePrefix,ControlOut); \
+        pThis->IHostAudio.pfnControlIn  = RT_CONCAT(a_NamePrefix,ControlIn); \
+        pThis->IHostAudio.pfnFiniIn     = RT_CONCAT(a_NamePrefix,FiniIn); \
+        pThis->IHostAudio.pfnFiniOut    = RT_CONCAT(a_NamePrefix,FiniOut); \
+        pThis->IHostAudio.pfnIsEnabled  = RT_CONCAT(a_NamePrefix,IsEnabled); \
+        pThis->IHostAudio.pfnPlayOut    = RT_CONCAT(a_NamePrefix,PlayOut); \
+        pThis->IHostAudio.pfnCaptureIn  = RT_CONCAT(a_NamePrefix,CaptureIn); \
+        pThis->IHostAudio.pfnGetConf    = RT_CONCAT(a_NamePrefix,GetConf); \
+    } while (0)
+
+/** Pointer to a host audio interface. */
+typedef struct PDMIHOSTAUDIO *PPDMIHOSTAUDIO;
+/**
+ * PDM host audio interface.
+ */
+typedef struct PDMIHOSTAUDIO
+{
+    /**
+     * Initialize the host-specific audio device.
+     *
+     * @returns VBox status code.
+     * @param   pInterface          Pointer to the interface structure containing the called function pointer.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnInit, (PPDMIHOSTAUDIO pInterface));
+
+    /**
+     * Shuts down the host-specific audio device.
+     *
+     * @returns VBox status code.
+     * @param   pInterface          Pointer to the interface structure containing the called function pointer.
+     */
+    DECLR3CALLBACKMEMBER(void, pfnShutdown, (PPDMIHOSTAUDIO pInterface));
+
+    /**
+     * Initialize the host-specific audio device for input stream.
+     *
+     * @returns VBox status code.
+     * @param   pInterface          Pointer to the interface structure containing the called function pointer.
+     * @param   pHstStrmIn          Pointer to host input stream.
+     * @param   pStreamCfg          Pointer to stream configuration.
+     * @param   enmRecSource        Specifies the type of recording source to be initialized.
+     * @param   pcSamples           Returns how many samples the backend can handle. Optional.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnInitIn, (PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pStreamCfg, PDMAUDIORECSOURCE enmRecSource, uint32_t *pcSamples));
+
+    /**
+     * Initialize the host-specific output device for output stream.
+     *
+     * @returns VBox status code.
+     * @param   pInterface          Pointer to the interface structure containing the called function pointer.
+     * @param   pHstStrmOut         Pointer to host output stream.
+     * @param   pStreamCfg          Pointer to stream configuration.
+     * @param   pcSamples           Returns how many samples the backend can handle. Optional.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnInitOut, (PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pStreamCfg, uint32_t *pcSamples));
+
+    /**
+     * Control the host audio device for an input stream.
+     *
+     * @returns VBox status code.
+     * @param   pInterface          Pointer to the interface structure containing the called function pointer.
+     * @param   pHstStrmOut         Pointer to host output stream.
+     * @param   enmStreamCmd        The stream command to issue.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnControlOut, (PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd));
+
+    /**
+     * Control the host audio device for an output stream.
+     *
+     * @returns VBox status code.
+     * @param   pInterface          Pointer to the interface structure containing the called function pointer.
+     * @param   pHstStrmOut         Pointer to host output stream.
+     * @param   enmStreamCmd        The stream command to issue.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnControlIn, (PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn, PDMAUDIOSTREAMCMD enmStreamCmd));
+
+    /**
+     * Ends the host audio input streamm.
+     *
+     * @returns VBox status code.
+     * @param   pInterface          Pointer to the interface structure containing the called function pointer.
+     * @param   pHstStrmIn          Pointer to host input stream.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnFiniIn, (PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn));
+
+    /**
+     * Ends the host output stream.
+     *
+     * @returns VBox status code.
+     * @param   pInterface          Pointer to the interface structure containing the called function pointer.
+     * @param   pHstStrmOut         Pointer to host output stream.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnFiniOut, (PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut));
+
+    /**
+     * Returns whether the specified audio direction in the backend is enabled or not.
+     *
+     * @param   pInterface          Pointer to the interface structure containing the called function pointer.
+     * @param   enmDir              Audio direction to check status for.
+     */
+    DECLR3CALLBACKMEMBER(bool, pfnIsEnabled, (PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir));
+
+    /**
+     * Plays a host audio stream.
+     *
+     * @returns VBox status code.
+     * @param   pInterface          Pointer to the interface structure containing the called function pointer.
+     * @param   pHstStrmOut         Pointer to host output stream.
+     * @param   pcSamplesPlayed     Pointer to number of samples captured.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnPlayOut, (PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut, uint32_t *pcSamplesPlayed));
+
+    /**
+     * Records audio to input stream.
+     *
+     * @returns VBox status code.
+     * @param   pInterface          Pointer to the interface structure containing the called function pointer.
+     * @param   pHstStrmIn          Pointer to host input stream.
+     * @param   pcSamplesCaptured   Pointer to number of samples captured.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnCaptureIn, (PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn, uint32_t *pcSamplesCaptured));
+
+    /**
+     * Gets the configuration from the host audio (backend) driver.
+     *
+     * @returns VBox status code.
+     * @param   pInterface          Pointer to the interface structure containing the called function pointer.
+     * @param   pBackendCfg         Pointer where to store the backend audio configuration to.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetConf, (PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg));
+
+} PDMIHOSTAUDIO;
+
+/** PDMIHOSTAUDIO interface ID. */
+#define PDMIHOSTAUDIO_IID                           "39feea4f-c824-4197-bcff-7d4a6ede7420"
+
+/** @} */
+
+#endif /* !___VBox_vmm_pdmaudioifs_h */
+
Index: /trunk/src/VBox/Devices/Audio_old/AudioMixBuffer.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/AudioMixBuffer.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/AudioMixBuffer.cpp	(revision 61413)
@@ -0,0 +1,1749 @@
+/* $Id$ */
+/** @file
+ * VBox audio: Audio mixing buffer for converting reading/writing audio
+ *             samples.
+ */
+
+/*
+ * Copyright (C) 2014-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.
+ */
+#define LOG_GROUP LOG_GROUP_AUDIO_MIXER_BUFFER
+#include <VBox/log.h>
+
+/*
+ * DEBUG_DUMP_PCM_DATA enables dumping the raw PCM data
+ * to a file on the host. Be sure to adjust DEBUG_DUMP_PCM_DATA_PATH
+ * to your needs before using this!
+ */
+#ifdef DEBUG
+//# define DEBUG_DUMP_PCM_DATA
+# define DEBUG_DUMP_PCM_DATA_PATH "c:\\temp\\"
+#endif
+
+#include <iprt/asm-math.h>
+#include <iprt/assert.h>
+#ifdef DEBUG_DUMP_PCM_DATA
+# include <iprt/file.h>
+#endif
+#include <iprt/mem.h>
+#include <iprt/string.h> /* For RT_BZERO. */
+
+#ifdef TESTCASE
+# define LOG_ENABLED
+# include <iprt/stream.h>
+#endif
+#include <VBox/err.h>
+
+#include "AudioMixBuffer.h"
+
+#if 0
+# define AUDMIXBUF_LOG(x) LogFlowFunc(x)
+#else
+# if defined(TESTCASE)
+#  define AUDMIXBUF_LOG(x) LogFunc(x)
+# else
+#  define AUDMIXBUF_LOG(x) do {} while (0)
+# endif
+#endif
+
+
+/*
+ *   Soft Volume Control
+ *
+ * The external code supplies an 8-bit volume (attenuation) value in the
+ * 0 .. 255 range. This represents 0 to -96dB attenuation where an input
+ * value of 0 corresponds to -96dB and 255 corresponds to 0dB (unchanged).
+ *
+ * Each step thus correspons to 96 / 256 or 0.375dB. Every 6dB (16 steps)
+ * represents doubling the sample value.
+ *
+ * For internal use, the volume control needs to be converted to a 16-bit
+ * (sort of) exponential value between 1 and 65536. This is used with fixed
+ * point arithmetic such that 65536 means 1.0 and 1 means 1/65536.
+ *
+ * For actual volume calculation, 33.31 fixed point is used. Maximum (or
+ * unattenuated) volume is represented as 0x40000000; conveniently, this
+ * value fits into a uint32_t.
+ *
+ * To enable fast processing, the maximum volume must be a power of two
+ * and must not have a sign when converted to int32_t. While 0x80000000
+ * violates these constraints, 0x40000000 does not.
+ */
+
+
+/** Logarithmic/exponential volume conversion table. */
+static uint32_t s_aVolumeConv[256] = {
+        1,     1,     1,     1,     1,     1,     1,     1, /*   7 */
+        1,     2,     2,     2,     2,     2,     2,     2, /*  15 */
+        2,     2,     2,     2,     2,     3,     3,     3, /*  23 */
+        3,     3,     3,     3,     4,     4,     4,     4, /*  31 */
+        4,     4,     5,     5,     5,     5,     5,     6, /*  39 */
+        6,     6,     6,     7,     7,     7,     8,     8, /*  47 */
+        8,     9,     9,    10,    10,    10,    11,    11, /*  55 */
+       12,    12,    13,    13,    14,    15,    15,    16, /*  63 */
+       17,    17,    18,    19,    20,    21,    22,    23, /*  71 */
+       24,    25,    26,    27,    28,    29,    31,    32, /*  79 */
+       33,    35,    36,    38,    40,    41,    43,    45, /*  87 */
+       47,    49,    52,    54,    56,    59,    61,    64, /*  95 */
+       67,    70,    73,    76,    79,    83,    87,    91, /* 103 */
+       95,    99,   103,   108,   112,   117,   123,   128, /* 111 */
+      134,   140,   146,   152,   159,   166,   173,   181, /* 119 */
+      189,   197,   206,   215,   225,   235,   245,   256, /* 127 */
+      267,   279,   292,   304,   318,   332,   347,   362, /* 135 */
+      378,   395,   412,   431,   450,   470,   490,   512, /* 143 */
+      535,   558,   583,   609,   636,   664,   693,   724, /* 151 */
+      756,   790,   825,   861,   899,   939,   981,  1024, /* 159 */
+     1069,  1117,  1166,  1218,  1272,  1328,  1387,  1448, /* 167 */
+     1512,  1579,  1649,  1722,  1798,  1878,  1961,  2048, /* 175 */
+     2139,  2233,  2332,  2435,  2543,  2656,  2774,  2896, /* 183 */
+     3025,  3158,  3298,  3444,  3597,  3756,  3922,  4096, /* 191 */
+     4277,  4467,  4664,  4871,  5087,  5312,  5547,  5793, /* 199 */
+     6049,  6317,  6597,  6889,  7194,  7512,  7845,  8192, /* 207 */
+     8555,  8933,  9329,  9742, 10173, 10624, 11094, 11585, /* 215 */
+    12098, 12634, 13193, 13777, 14387, 15024, 15689, 16384, /* 223 */
+    17109, 17867, 18658, 19484, 20347, 21247, 22188, 23170, /* 231 */
+    24196, 25268, 26386, 27554, 28774, 30048, 31379, 32768, /* 239 */
+    34219, 35734, 37316, 38968, 40693, 42495, 44376, 46341, /* 247 */
+    48393, 50535, 52773, 55109, 57549, 60097, 62757, 65536, /* 255 */
+};
+
+/* Bit shift for fixed point conversion. */
+#define AUDIOMIXBUF_VOL_SHIFT       30
+
+/* Internal representation of 0dB volume (1.0 in fixed point). */
+#define AUDIOMIXBUF_VOL_0DB         (1 << AUDIOMIXBUF_VOL_SHIFT)
+
+AssertCompile(AUDIOMIXBUF_VOL_0DB <= 0x40000000);   /* Must always hold. */
+AssertCompile(AUDIOMIXBUF_VOL_0DB == 0x40000000);   /* For now -- when only attenuation is used. */
+
+/**
+ * Structure for holding sample conversion parameters for
+ * the audioMixBufConvFromXXX / audioMixBufConvToXXX macros.
+ */
+typedef struct AUDMIXBUF_CONVOPTS
+{
+    /** Number of audio samples to convert. */
+    uint32_t       cSamples;
+    /** Volume to apply during conversion. Pass 0
+     *  to convert the original values. May not apply to
+     *  all conversion functions. */
+    PDMAUDIOVOLUME Volume;
+} AUDMIXBUF_CONVOPTS, *PAUDMIXBUF_CONVOPTS;
+
+/*
+ * When running the audio testcases we want to verfiy
+ * the macro-generated routines separately, so unmark them as being
+ * inlined + static.
+ */
+#ifdef TESTCASE
+# define AUDMIXBUF_MACRO_FN
+#else
+# define AUDMIXBUF_MACRO_FN static inline
+#endif
+
+#ifdef DEBUG
+static uint64_t s_cSamplesMixedTotal = 0;
+static inline void audioMixBufPrint(PPDMAUDIOMIXBUF pMixBuf);
+#endif
+
+typedef uint32_t (AUDMIXBUF_FN_CONVFROM) (PPDMAUDIOSAMPLE paDst, const void *pvSrc, uint32_t cbSrc, const PAUDMIXBUF_CONVOPTS pOpts);
+typedef AUDMIXBUF_FN_CONVFROM *PAUDMIXBUF_FN_CONVFROM;
+
+typedef void (AUDMIXBUF_FN_CONVTO) (void *pvDst, const PPDMAUDIOSAMPLE paSrc, const PAUDMIXBUF_CONVOPTS pOpts);
+typedef AUDMIXBUF_FN_CONVTO *PAUDMIXBUF_FN_CONVTO;
+
+/* Can return VINF_TRY_AGAIN for getting next pointer at beginning (circular) */
+
+/**
+ * Acquires (reads) a mutable pointer to the mixing buffer's audio samples without
+ * any conversion done.
+ ** @todo Rename to AudioMixBufPeek(Mutable/Raw)?
+ ** @todo Protect the buffer's data?
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to acquire audio samples from.
+ * @param   cSamplesToRead          Number of audio samples to read.
+ * @param   ppvSamples              Returns a mutable pointer to the buffer's audio sample data.
+ * @param   pcSamplesRead           Number of audio samples read (acquired).
+ *
+ * @remark  This function is not thread safe!
+ */
+int AudioMixBufAcquire(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamplesToRead,
+                       PPDMAUDIOSAMPLE *ppvSamples, uint32_t *pcSamplesRead)
+{
+    AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
+    AssertPtrReturn(ppvSamples, VERR_INVALID_POINTER);
+    AssertPtrReturn(pcSamplesRead, VERR_INVALID_POINTER);
+
+    int rc;
+
+    if (!cSamplesToRead)
+    {
+        *pcSamplesRead = 0;
+        return VINF_SUCCESS;
+    }
+
+    uint32_t cSamplesRead;
+    if (pMixBuf->offReadWrite + cSamplesToRead > pMixBuf->cSamples)
+    {
+        cSamplesRead = pMixBuf->cSamples - pMixBuf->offReadWrite;
+        rc = VINF_TRY_AGAIN;
+    }
+    else
+    {
+        cSamplesRead = cSamplesToRead;
+        rc = VINF_SUCCESS;
+    }
+
+    *ppvSamples = &pMixBuf->pSamples[pMixBuf->offReadWrite];
+    AssertPtr(ppvSamples);
+
+    pMixBuf->offReadWrite = (pMixBuf->offReadWrite + cSamplesRead) % pMixBuf->cSamples;
+    Assert(pMixBuf->offReadWrite <= pMixBuf->cSamples);
+    pMixBuf->cProcessed -= RT_MIN(cSamplesRead, pMixBuf->cProcessed);
+
+    *pcSamplesRead = cSamplesRead;
+
+    return rc;
+}
+
+/**
+ * Returns available number of samples for reading.
+ *
+ * @return  uint32_t                Number of samples available for reading.
+ * @param   pMixBuf                 Mixing buffer to return value for.
+ */
+uint32_t AudioMixBufAvail(PPDMAUDIOMIXBUF pMixBuf)
+{
+    AssertPtrReturn(pMixBuf, true);
+
+    uint32_t cAvail;
+    if (pMixBuf->pParent) /* Child */
+        cAvail = pMixBuf->cMixed;
+    else
+        cAvail = pMixBuf->cProcessed;
+
+    Assert(cAvail <= pMixBuf->cSamples);
+    return cAvail;
+}
+
+/**
+ * Clears the entire sample buffer.
+ *
+ * @param   pMixBuf                 Mixing buffer to clear.
+ *
+ */
+void AudioMixBufClear(PPDMAUDIOMIXBUF pMixBuf)
+{
+    AssertPtrReturnVoid(pMixBuf);
+
+    if (pMixBuf->cSamples)
+        RT_BZERO(pMixBuf->pSamples, pMixBuf->cSamples * sizeof(PDMAUDIOSAMPLE));
+}
+
+/**
+ * Clears (zeroes) the buffer by a certain amount of (processed) samples and
+ * keeps track to eventually assigned children buffers.
+ *
+ * @param   pMixBuf                 Mixing buffer to clear.
+ * @param   cSamplesToClear         Number of audio samples to clear.
+ */
+void AudioMixBufFinish(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamplesToClear)
+{
+    AUDMIXBUF_LOG(("cSamples=%RU32\n", cSamplesToClear));
+    AUDMIXBUF_LOG(("%s: offReadWrite=%RU32, cProcessed=%RU32\n",
+                   pMixBuf->pszName, pMixBuf->offReadWrite, pMixBuf->cProcessed));
+
+    PPDMAUDIOMIXBUF pIter;
+    RTListForEach(&pMixBuf->lstBuffers, pIter, PDMAUDIOMIXBUF, Node)
+    {
+        AUDMIXBUF_LOG(("\t%s: cMixed=%RU32 -> %RU32\n",
+                       pIter->pszName, pIter->cMixed, pIter->cMixed - cSamplesToClear));
+
+        pIter->cMixed -= RT_MIN(pIter->cMixed, cSamplesToClear);
+        pIter->offReadWrite = 0;
+    }
+
+    uint32_t cLeft = RT_MIN(cSamplesToClear, pMixBuf->cSamples);
+    uint32_t offClear;
+
+    if (cLeft > pMixBuf->offReadWrite) /* Zero end of buffer first (wrap-around). */
+    {
+        AUDMIXBUF_LOG(("Clearing1: %RU32 - %RU32\n",
+                       (pMixBuf->cSamples - (cLeft - pMixBuf->offReadWrite)),
+                        pMixBuf->cSamples));
+
+        RT_BZERO(pMixBuf->pSamples + (pMixBuf->cSamples - (cLeft - pMixBuf->offReadWrite)),
+                 (cLeft - pMixBuf->offReadWrite) * sizeof(PDMAUDIOSAMPLE));
+
+        cLeft -= cLeft - pMixBuf->offReadWrite;
+        offClear = 0;
+    }
+    else
+        offClear = pMixBuf->offReadWrite - cLeft;
+
+    if (cLeft)
+    {
+        AUDMIXBUF_LOG(("Clearing2: %RU32 - %RU32\n",
+                       offClear, offClear + cLeft));
+        RT_BZERO(pMixBuf->pSamples + offClear, cLeft * sizeof(PDMAUDIOSAMPLE));
+    }
+}
+
+/**
+ * Destroys (uninitializes) a mixing buffer.
+ *
+ * @param   pMixBuf                 Mixing buffer to destroy.
+ */
+void AudioMixBufDestroy(PPDMAUDIOMIXBUF pMixBuf)
+{
+    if (!pMixBuf)
+        return;
+
+    AudioMixBufUnlink(pMixBuf);
+
+    if (pMixBuf->pszName)
+    {
+        AUDMIXBUF_LOG(("%s\n", pMixBuf->pszName));
+
+        RTStrFree(pMixBuf->pszName);
+        pMixBuf->pszName = NULL;
+    }
+
+    if (pMixBuf->pRate)
+    {
+        RTMemFree(pMixBuf->pRate);
+        pMixBuf->pRate = NULL;
+    }
+
+    if (pMixBuf->pSamples)
+    {
+        Assert(pMixBuf->cSamples);
+
+        RTMemFree(pMixBuf->pSamples);
+        pMixBuf->pSamples = NULL;
+    }
+
+    pMixBuf->cSamples = 0;
+}
+
+/**
+ * Returns the size (in audio samples) of free audio buffer space.
+ *
+ * @return  uint32_t                Size (in audio samples) of free audio buffer space.
+ * @param   pMixBuf                 Mixing buffer to return free size for.
+ */
+uint32_t AudioMixBufFree(PPDMAUDIOMIXBUF pMixBuf)
+{
+    AssertPtrReturn(pMixBuf, 0);
+
+    uint32_t cSamplesFree;
+    if (pMixBuf->pParent)
+    {
+        /*
+         * As a linked child buffer we want to know how many samples
+         * already have been consumed by the parent.
+         */
+        Assert(pMixBuf->cMixed <= pMixBuf->pParent->cSamples);
+        cSamplesFree = pMixBuf->pParent->cSamples - pMixBuf->cMixed;
+    }
+    else /* As a parent. */
+    {
+        Assert(pMixBuf->cSamples >= pMixBuf->cProcessed);
+        cSamplesFree = pMixBuf->cSamples - pMixBuf->cProcessed;
+    }
+
+    AUDMIXBUF_LOG(("%s: cSamplesFree=%RU32\n", pMixBuf->pszName, cSamplesFree));
+    return cSamplesFree;
+}
+
+/**
+ * Returns the size (in bytes) of free audio buffer space.
+ *
+ * @return  uint32_t                Size (in bytes) of free audio buffer space.
+ * @param   pMixBuf                 Mixing buffer to return free size for.
+ */
+uint32_t AudioMixBufFreeBytes(PPDMAUDIOMIXBUF pMixBuf)
+{
+    return AUDIOMIXBUF_S2B(pMixBuf, AudioMixBufFree(pMixBuf));
+}
+
+/**
+ * Allocates the internal audio sample buffer.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to allocate sample buffer for.
+ * @param   cSamples                Number of audio samples to allocate.
+ */
+static int audioMixBufAlloc(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamples)
+{
+    AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
+    AssertReturn(cSamples, VERR_INVALID_PARAMETER);
+
+    AUDMIXBUF_LOG(("%s: cSamples=%RU32\n", pMixBuf->pszName, cSamples));
+
+    size_t cbSamples = cSamples * sizeof(PDMAUDIOSAMPLE);
+    if (!cbSamples)
+        return VERR_INVALID_PARAMETER;
+
+    pMixBuf->pSamples = (PPDMAUDIOSAMPLE)RTMemAllocZ(cbSamples);
+    if (!pMixBuf->pSamples)
+        return VERR_NO_MEMORY;
+
+    pMixBuf->cSamples = cSamples;
+
+    return VINF_SUCCESS;
+}
+
+/** Note: Enabling this will generate huge logs! */
+//#define DEBUG_MACROS
+
+#ifdef DEBUG_MACROS
+# define AUDMIXBUF_MACRO_LOG(x) AUDMIXBUF_LOG(x)
+#elif defined(TESTCASE)
+# define AUDMIXBUF_MACRO_LOG(x) RTPrintf x
+#else
+# define AUDMIXBUF_MACRO_LOG(x) do {} while (0)
+#endif
+
+/**
+ * Macro for generating the conversion routines from/to different formats.
+ * Be careful what to pass in/out, as most of the macros are optimized for speed and
+ * thus don't do any bounds checking!
+ *
+ * Note: Currently does not handle any endianness conversion yet!
+ */
+#define AUDMIXBUF_CONVERT(_aName, _aType, _aMin, _aMax, _aSigned, _aShift) \
+    /* Clips a specific output value to a single sample value. */ \
+    AUDMIXBUF_MACRO_FN int64_t audioMixBufClipFrom##_aName(_aType aVal) \
+    { \
+        if (_aSigned) \
+            return ((int64_t) aVal) << (32 - _aShift); \
+        return ((int64_t) aVal - ((_aMax >> 1) + 1)) << (32 - _aShift); \
+    } \
+    \
+    /* Clips a single sample value to a specific output value. */ \
+    AUDMIXBUF_MACRO_FN _aType audioMixBufClipTo##_aName(int64_t iVal) \
+    { \
+        if (iVal >= 0x7fffffff) \
+            return _aMax; \
+        else if (iVal < -INT64_C(0x80000000)) \
+            return _aMin; \
+        \
+        if (_aSigned) \
+            return (_aType) (iVal >> (32 - _aShift)); \
+        return ((_aType) ((iVal >> (32 - _aShift)) + ((_aMax >> 1) + 1))); \
+    } \
+    \
+    AUDMIXBUF_MACRO_FN uint32_t audioMixBufConvFrom##_aName##Stereo(PPDMAUDIOSAMPLE paDst, const void *pvSrc, uint32_t cbSrc, \
+                                                                    const PAUDMIXBUF_CONVOPTS pOpts) \
+    { \
+        _aType *pSrc = (_aType *)pvSrc; \
+        uint32_t cSamples = (uint32_t)RT_MIN(pOpts->cSamples, cbSrc / sizeof(_aType)); \
+        AUDMIXBUF_MACRO_LOG(("cSamples=%RU32, sizeof(%zu), lVol=%RU32, rVol=%RU32\n", \
+                             pOpts->cSamples, sizeof(_aType), pOpts->Volume.uLeft, pOpts->Volume.uRight)); \
+        for (uint32_t i = 0; i < cSamples; i++) \
+        { \
+            AUDMIXBUF_MACRO_LOG(("%p: l=%RI16, r=%RI16\n", paDst, *pSrc, *(pSrc + 1))); \
+            paDst->i64LSample = ASMMult2xS32RetS64((int32_t)audioMixBufClipFrom##_aName(*pSrc++), pOpts->Volume.uLeft ) >> AUDIOMIXBUF_VOL_SHIFT; \
+            paDst->i64RSample = ASMMult2xS32RetS64((int32_t)audioMixBufClipFrom##_aName(*pSrc++), pOpts->Volume.uRight) >> AUDIOMIXBUF_VOL_SHIFT; \
+            AUDMIXBUF_MACRO_LOG(("\t-> l=%RI64, r=%RI64\n", paDst->i64LSample, paDst->i64RSample)); \
+            paDst++; \
+        } \
+        \
+        return cSamples; \
+    } \
+    \
+    AUDMIXBUF_MACRO_FN uint32_t audioMixBufConvFrom##_aName##Mono(PPDMAUDIOSAMPLE paDst, const void *pvSrc, uint32_t cbSrc, \
+                                                                  const PAUDMIXBUF_CONVOPTS pOpts) \
+    { \
+        _aType *pSrc = (_aType *)pvSrc; \
+        uint32_t cSamples = (uint32_t)RT_MIN(pOpts->cSamples, cbSrc / sizeof(_aType)); \
+        AUDMIXBUF_MACRO_LOG(("cSamples=%RU32, sizeof(%zu), lVol=%RU32, rVol=%RU32\n", \
+                             cSamples, sizeof(_aType), pOpts->Volume.uLeft, pOpts->Volume.uRight)); \
+        for (uint32_t i = 0; i < cSamples; i++) \
+        { \
+            AUDMIXBUF_MACRO_LOG(("%p: s=%RI16\n", paDst, *pSrc)); \
+            paDst->i64LSample = ASMMult2xS32RetS64((int32_t)audioMixBufClipFrom##_aName(*pSrc), pOpts->Volume.uLeft)  >> AUDIOMIXBUF_VOL_SHIFT; \
+            paDst->i64RSample = ASMMult2xS32RetS64((int32_t)audioMixBufClipFrom##_aName(*pSrc), pOpts->Volume.uRight) >> AUDIOMIXBUF_VOL_SHIFT; \
+            ++pSrc; \
+            AUDMIXBUF_MACRO_LOG(("\t-> l=%RI64, r=%RI64\n", paDst->i64LSample, paDst->i64RSample)); \
+            paDst++; \
+        } \
+        \
+        return cSamples; \
+    } \
+    \
+    AUDMIXBUF_MACRO_FN void audioMixBufConvTo##_aName##Stereo(void *pvDst, const PPDMAUDIOSAMPLE paSrc, \
+                                                              const PAUDMIXBUF_CONVOPTS pOpts) \
+    { \
+        PPDMAUDIOSAMPLE pSrc = paSrc; \
+        _aType *pDst = (_aType *)pvDst; \
+        _aType l, r; \
+        uint32_t cSamples = pOpts->cSamples; \
+        while (cSamples--) \
+        { \
+            AUDMIXBUF_MACRO_LOG(("%p: l=%RI64, r=%RI64\n", pSrc, pSrc->i64LSample, pSrc->i64RSample)); \
+            l = audioMixBufClipTo##_aName(pSrc->i64LSample); \
+            r = audioMixBufClipTo##_aName(pSrc->i64RSample); \
+            AUDMIXBUF_MACRO_LOG(("\t-> l=%RI16, r=%RI16\n", l, r)); \
+            *pDst++ = l; \
+            *pDst++ = r; \
+            pSrc++; \
+        } \
+    } \
+    \
+    AUDMIXBUF_MACRO_FN void audioMixBufConvTo##_aName##Mono(void *pvDst, const PPDMAUDIOSAMPLE paSrc, \
+                                                            const PAUDMIXBUF_CONVOPTS pOpts) \
+    { \
+        PPDMAUDIOSAMPLE pSrc = paSrc; \
+        _aType *pDst = (_aType *)pvDst; \
+        uint32_t cSamples = pOpts->cSamples; \
+        while (cSamples--) \
+        { \
+            *pDst++ = audioMixBufClipTo##_aName((pSrc->i64LSample + pSrc->i64RSample) / 2); \
+            pSrc++; \
+        } \
+    }
+
+/* audioMixBufConvXXXS8: 8 bit, signed. */
+AUDMIXBUF_CONVERT(S8 /* Name */,  int8_t,   INT8_MIN  /* Min */, INT8_MAX   /* Max */, true  /* fSigned */, 8  /* cShift */)
+/* audioMixBufConvXXXU8: 8 bit, unsigned. */
+AUDMIXBUF_CONVERT(U8 /* Name */,  uint8_t,  0         /* Min */, UINT8_MAX  /* Max */, false /* fSigned */, 8  /* cShift */)
+/* audioMixBufConvXXXS16: 16 bit, signed. */
+AUDMIXBUF_CONVERT(S16 /* Name */, int16_t,  INT16_MIN /* Min */, INT16_MAX  /* Max */, true  /* fSigned */, 16 /* cShift */)
+/* audioMixBufConvXXXU16: 16 bit, unsigned. */
+AUDMIXBUF_CONVERT(U16 /* Name */, uint16_t, 0         /* Min */, UINT16_MAX /* Max */, false /* fSigned */, 16 /* cShift */)
+/* audioMixBufConvXXXS32: 32 bit, signed. */
+AUDMIXBUF_CONVERT(S32 /* Name */, int32_t,  INT32_MIN /* Min */, INT32_MAX  /* Max */, true  /* fSigned */, 32 /* cShift */)
+/* audioMixBufConvXXXU32: 32 bit, unsigned. */
+AUDMIXBUF_CONVERT(U32 /* Name */, uint32_t, 0         /* Min */, UINT32_MAX /* Max */, false /* fSigned */, 32 /* cShift */)
+
+#undef AUDMIXBUF_CONVERT
+
+#define AUDMIXBUF_MIXOP(_aName, _aOp) \
+    AUDMIXBUF_MACRO_FN void audioMixBufOp##_aName(PPDMAUDIOSAMPLE paDst, uint32_t cDstSamples, \
+                                                  PPDMAUDIOSAMPLE paSrc, uint32_t cSrcSamples, \
+                                                  PPDMAUDIOSTRMRATE pRate, \
+                                                  uint32_t *pcDstWritten, uint32_t *pcSrcRead) \
+    { \
+        AUDMIXBUF_MACRO_LOG(("cSrcSamples=%RU32, cDstSamples=%RU32\n", cSrcSamples, cDstSamples)); \
+        AUDMIXBUF_MACRO_LOG(("pRate=%p: srcOffset=0x%RX32 (%RU32), dstOffset=0x%RX32 (%RU32), dstInc=0x%RX64 (%RU64)\n", \
+                             pRate, pRate->srcOffset, pRate->srcOffset, \
+                             (uint32_t)(pRate->dstOffset >> 32), (uint32_t)(pRate->dstOffset >> 32), \
+                             pRate->dstInc, pRate->dstInc)); \
+        \
+        if (pRate->dstInc == (UINT64_C(1) + UINT32_MAX)) /* No conversion needed? */ \
+        { \
+            uint32_t cSamples = RT_MIN(cSrcSamples, cDstSamples); \
+            AUDMIXBUF_MACRO_LOG(("cSamples=%RU32\n", cSamples)); \
+            for (uint32_t i = 0; i < cSamples; i++) \
+            { \
+                paDst[i].i64LSample _aOp paSrc[i].i64LSample; \
+                paDst[i].i64RSample _aOp paSrc[i].i64RSample; \
+            } \
+            \
+            if (pcDstWritten) \
+                *pcDstWritten = cSamples; \
+            if (pcSrcRead) \
+                *pcSrcRead = cSamples; \
+            return; \
+        } \
+        \
+        PPDMAUDIOSAMPLE paSrcStart = paSrc; \
+        PPDMAUDIOSAMPLE paSrcEnd   = paSrc + cSrcSamples; \
+        PPDMAUDIOSAMPLE paDstStart = paDst; \
+        PPDMAUDIOSAMPLE paDstEnd   = paDst + cDstSamples; \
+        PDMAUDIOSAMPLE  samCur = { 0 }; \
+        PDMAUDIOSAMPLE  samOut; \
+        PDMAUDIOSAMPLE  samLast    = pRate->srcSampleLast; \
+        uint64_t        lDelta = 0; \
+        \
+        AUDMIXBUF_MACRO_LOG(("Start: paDstEnd=%p - paDstStart=%p -> %zu\n", paDstEnd, paDst, paDstEnd - paDstStart)); \
+        AUDMIXBUF_MACRO_LOG(("Start: paSrcEnd=%p - paSrcStart=%p -> %zu\n", paSrcEnd, paSrc, paSrcEnd - paSrcStart)); \
+        \
+        while (paDst < paDstEnd) \
+        { \
+            Assert(paSrc <= paSrcEnd); \
+            Assert(paDst <= paDstEnd); \
+            if (paSrc == paSrcEnd) \
+                break; \
+            \
+            lDelta = 0; \
+            while (pRate->srcOffset <= (pRate->dstOffset >> 32)) \
+            { \
+                Assert(paSrc <= paSrcEnd); \
+                samLast = *paSrc++; \
+                pRate->srcOffset++; \
+                lDelta++; \
+                if (paSrc == paSrcEnd) \
+                    break; \
+            } \
+            \
+            Assert(paSrc <= paSrcEnd); \
+            if (paSrc == paSrcEnd) \
+                break; \
+            \
+            samCur = *paSrc; \
+            \
+            /* Interpolate. */ \
+            int64_t iDstOffInt = pRate->dstOffset & UINT32_MAX; \
+            \
+            samOut.i64LSample = (samLast.i64LSample * ((int64_t) (INT64_C(1) << 32) - iDstOffInt) + samCur.i64LSample * iDstOffInt) >> 32; \
+            samOut.i64RSample = (samLast.i64RSample * ((int64_t) (INT64_C(1) << 32) - iDstOffInt) + samCur.i64RSample * iDstOffInt) >> 32; \
+            \
+            paDst->i64LSample _aOp samOut.i64LSample; \
+            paDst->i64RSample _aOp samOut.i64RSample; \
+            \
+            AUDMIXBUF_MACRO_LOG(("\tlDelta=0x%RX64 (%RU64), iDstOffInt=0x%RX64 (%RI64), l=%RI64, r=%RI64 (cur l=%RI64, r=%RI64)\n", \
+                                 lDelta, lDelta, iDstOffInt, iDstOffInt, \
+                                 paDst->i64LSample, paDst->i64RSample, \
+                                 samCur.i64LSample, samCur.i64RSample)); \
+            \
+            paDst++; \
+            pRate->dstOffset += pRate->dstInc; \
+            \
+            AUDMIXBUF_MACRO_LOG(("\t\tpRate->dstOffset=0x%RX32 (%RU32)\n", pRate->dstOffset, pRate->dstOffset >> 32)); \
+            \
+        } \
+        \
+        AUDMIXBUF_MACRO_LOG(("End: paDst=%p - paDstStart=%p -> %zu\n", paDst, paDstStart, paDst - paDstStart)); \
+        AUDMIXBUF_MACRO_LOG(("End: paSrc=%p - paSrcStart=%p -> %zu\n", paSrc, paSrcStart, paSrc - paSrcStart)); \
+        \
+        pRate->srcSampleLast = samLast; \
+        \
+        AUDMIXBUF_MACRO_LOG(("pRate->srcSampleLast l=%RI64, r=%RI64, lDelta=0x%RX64 (%RU64)\n", \
+                              pRate->srcSampleLast.i64LSample, pRate->srcSampleLast.i64RSample, lDelta, lDelta)); \
+        \
+        if (pcDstWritten) \
+            *pcDstWritten = paDst - paDstStart; \
+        if (pcSrcRead) \
+            *pcSrcRead = paSrc - paSrcStart; \
+    }
+
+/* audioMixBufOpAssign: Assigns values from source buffer to destination bufffer, overwriting the destination. */
+AUDMIXBUF_MIXOP(Assign /* Name */,  = /* Operation */)
+/* audioMixBufOpBlend: Blends together the values from both, the source and the destination buffer. */
+AUDMIXBUF_MIXOP(Blend  /* Name */, += /* Operation */)
+
+#undef AUDMIXBUF_MIXOP
+#undef AUDMIXBUF_MACRO_LOG
+
+/** Dummy conversion used when the source is muted. */
+AUDMIXBUF_MACRO_FN uint32_t audioMixBufConvFromSilence(PPDMAUDIOSAMPLE paDst, const void *pvSrc,
+                                                       uint32_t cbSrc, const PAUDMIXBUF_CONVOPTS pOpts)
+{
+    /* Internally zero always corresponds to silence. */
+    memset(paDst, 0, pOpts->cSamples * sizeof(paDst[0]));
+    return pOpts->cSamples;
+}
+
+/**
+ * Looks up the matching conversion (macro) routine for converting
+ * audio samples from a source format.
+ *
+ ** @todo Speed up the lookup by binding it to the actual stream state.
+ *
+ * @return  PAUDMIXBUF_FN_CONVFROM  Function pointer to conversion macro if found, NULL if not supported.
+ * @param   enmFmt                  Audio format to lookup conversion macro for.
+ * @param   fMuted                  Flag determining whether the source is muted.
+ */
+static inline PAUDMIXBUF_FN_CONVFROM audioMixBufConvFromLookup(PDMAUDIOMIXBUFFMT enmFmt, bool fMuted)
+{
+    if (fMuted)
+        return audioMixBufConvFromSilence;
+
+    if (AUDMIXBUF_FMT_SIGNED(enmFmt))
+    {
+        if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 2)
+        {
+            switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
+            {
+                case 8:  return audioMixBufConvFromS8Stereo;
+                case 16: return audioMixBufConvFromS16Stereo;
+                case 32: return audioMixBufConvFromS32Stereo;
+                default: return NULL;
+            }
+        }
+        else if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 1)
+        {
+            switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
+            {
+                case 8:  return audioMixBufConvFromS8Mono;
+                case 16: return audioMixBufConvFromS16Mono;
+                case 32: return audioMixBufConvFromS32Mono;
+                default: return NULL;
+            }
+        }
+    }
+    else /* Unsigned */
+    {
+        if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 2)
+        {
+            switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
+            {
+                case 8:  return audioMixBufConvFromU8Stereo;
+                case 16: return audioMixBufConvFromU16Stereo;
+                case 32: return audioMixBufConvFromU32Stereo;
+                default: return NULL;
+            }
+        }
+        else if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 1)
+        {
+            switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
+            {
+                case 8:  return audioMixBufConvFromU8Mono;
+                case 16: return audioMixBufConvFromU16Mono;
+                case 32: return audioMixBufConvFromU32Mono;
+                default: return NULL;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * Looks up the matching conversion (macro) routine for converting
+ * audio samples to a destination format.
+ *
+ ** @todo Speed up the lookup by binding it to the actual stream state.
+ *
+ * @return  PAUDMIXBUF_FN_CONVTO    Function pointer to conversion macro if found, NULL if not supported.
+ * @param   enmFmt                  Audio format to lookup conversion macro for.
+ */
+static inline PAUDMIXBUF_FN_CONVTO audioMixBufConvToLookup(PDMAUDIOMIXBUFFMT enmFmt)
+{
+    if (AUDMIXBUF_FMT_SIGNED(enmFmt))
+    {
+        if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 2)
+        {
+            switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
+            {
+                case 8:  return audioMixBufConvToS8Stereo;
+                case 16: return audioMixBufConvToS16Stereo;
+                case 32: return audioMixBufConvToS32Stereo;
+                default: return NULL;
+            }
+        }
+        else if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 1)
+        {
+            switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
+            {
+                case 8:  return audioMixBufConvToS8Mono;
+                case 16: return audioMixBufConvToS16Mono;
+                case 32: return audioMixBufConvToS32Mono;
+                default: return NULL;
+            }
+        }
+    }
+    else /* Unsigned */
+    {
+        if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 2)
+        {
+            switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
+            {
+                case 8:  return audioMixBufConvToU8Stereo;
+                case 16: return audioMixBufConvToU16Stereo;
+                case 32: return audioMixBufConvToU32Stereo;
+                default: return NULL;
+            }
+        }
+        else if (AUDMIXBUF_FMT_CHANNELS(enmFmt) == 1)
+        {
+            switch (AUDMIXBUF_FMT_BITS_PER_SAMPLE(enmFmt))
+            {
+                case 8:  return audioMixBufConvToU8Mono;
+                case 16: return audioMixBufConvToU16Mono;
+                case 32: return audioMixBufConvToU32Mono;
+                default: return NULL;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * Initializes a mixing buffer.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to initialize.
+ * @param   pszName                 Name of mixing buffer for easier identification. Optional.
+ * @param   pProps                  PCM audio properties to use for the mixing buffer.
+ * @param   cSamples                Maximum number of audio samples the mixing buffer can hold.
+ */
+int AudioMixBufInit(PPDMAUDIOMIXBUF pMixBuf, const char *pszName, PPDMPCMPROPS pProps, uint32_t cSamples)
+{
+    AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+    AssertPtrReturn(pProps,  VERR_INVALID_POINTER);
+
+    pMixBuf->pParent = NULL;
+    RTListInit(&pMixBuf->lstBuffers);
+
+    pMixBuf->pSamples = NULL;
+    pMixBuf->cSamples = 0;
+
+    pMixBuf->offReadWrite = 0;
+    pMixBuf->cMixed       = 0;
+    pMixBuf->cProcessed   = 0;
+
+    /* Set initial volume to max. */
+    pMixBuf->Volume.fMuted = false;
+    pMixBuf->Volume.uLeft  = AUDIOMIXBUF_VOL_0DB;
+    pMixBuf->Volume.uRight = AUDIOMIXBUF_VOL_0DB;
+
+    /* Prevent division by zero.
+     * Do a 1:1 conversion according to AUDIOMIXBUF_S2B_RATIO. */
+    pMixBuf->iFreqRatio = 1 << 20;
+
+    pMixBuf->pRate = NULL;
+
+    pMixBuf->AudioFmt = AUDMIXBUF_AUDIO_FMT_MAKE(pProps->uHz,
+                                                 pProps->cChannels,
+                                                 pProps->cBits,
+                                                 pProps->fSigned);
+    pMixBuf->cShift = pProps->cShift;
+    pMixBuf->pszName = RTStrDup(pszName);
+    if (!pMixBuf->pszName)
+        return VERR_NO_MEMORY;
+
+    AUDMIXBUF_LOG(("%s: uHz=%RU32, cChan=%RU8, cBits=%RU8, fSigned=%RTbool\n",
+                   pMixBuf->pszName,
+                   AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt),
+                   AUDMIXBUF_FMT_CHANNELS(pMixBuf->AudioFmt),
+                   AUDMIXBUF_FMT_BITS_PER_SAMPLE(pMixBuf->AudioFmt),
+                   RT_BOOL(AUDMIXBUF_FMT_SIGNED(pMixBuf->AudioFmt))));
+
+    return audioMixBufAlloc(pMixBuf, cSamples);
+}
+
+/**
+ * Returns @true if there are any audio samples available for processing,
+ * @false if not.
+ *
+ * @return  bool                    @true if there are any audio samples available for processing, @false if not.
+ * @param   pMixBuf                 Mixing buffer to return value for.
+ */
+bool AudioMixBufIsEmpty(PPDMAUDIOMIXBUF pMixBuf)
+{
+    AssertPtrReturn(pMixBuf, true);
+
+    if (pMixBuf->pParent)
+        return (pMixBuf->cMixed == 0);
+    return (pMixBuf->cProcessed == 0);
+}
+
+/**
+ * Links an audio mixing buffer to a parent mixing buffer. A parent mixing
+ * buffer can have multiple children mixing buffers [1:N], whereas a child only can
+ * have one parent mixing buffer [N:1].
+ *
+ * The mixing direction always goes from the child/children buffer(s) to the
+ * parent buffer.
+ *
+ * For guest audio output the host backend owns the parent mixing buffer, the
+ * device emulation owns the child/children.
+ *
+ * The audio format of each mixing buffer can vary; the internal mixing code
+ * then will autiomatically do the (needed) conversion.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to link parent to.
+ * @param   pParent                 Parent mixing buffer to use for linking.
+ *
+ * @remark  Circular linking is not allowed.
+ */
+int AudioMixBufLinkTo(PPDMAUDIOMIXBUF pMixBuf, PPDMAUDIOMIXBUF pParent)
+{
+    AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
+    AssertPtrReturn(pParent, VERR_INVALID_POINTER);
+
+    AssertMsgReturn(AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt),
+                    ("Parent sample frequency (Hz) not set\n"), VERR_INVALID_PARAMETER);
+    AssertMsgReturn(AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt),
+                    ("Buffer sample frequency (Hz) not set\n"), VERR_INVALID_PARAMETER);
+    AssertMsgReturn(pMixBuf != pParent,
+                    ("Circular linking not allowed\n"), VERR_INVALID_PARAMETER);
+
+    if (pMixBuf->pParent) /* Already linked? */
+    {
+        AUDMIXBUF_LOG(("%s: Already linked to \"%s\"\n",
+                       pMixBuf->pszName, pMixBuf->pParent->pszName));
+        return VERR_ACCESS_DENIED;
+    }
+
+    RTListAppend(&pParent->lstBuffers, &pMixBuf->Node);
+    pMixBuf->pParent = pParent;
+
+    /* Calculate the frequency ratio. */
+    pMixBuf->iFreqRatio = ((int64_t)AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt) << 32)
+                        /           AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt);
+
+    if (pMixBuf->iFreqRatio == 0) /* Catch division by zero. */
+        pMixBuf->iFreqRatio = 1 << 20; /* Do a 1:1 conversion instead. */
+
+    uint32_t cSamples = (uint32_t)RT_MIN(  ((uint64_t)pParent->cSamples << 32)
+                                         / pMixBuf->iFreqRatio, _64K /* 64K samples max. */);
+    if (!cSamples)
+        cSamples = pParent->cSamples;
+
+    int rc = VINF_SUCCESS;
+
+    if (cSamples != pMixBuf->cSamples)
+    {
+        AUDMIXBUF_LOG(("%s: Reallocating samples %RU32 -> %RU32\n",
+                       pMixBuf->pszName, pMixBuf->cSamples, cSamples));
+
+        uint32_t cbSamples = cSamples * sizeof(PDMAUDIOSAMPLE);
+        Assert(cbSamples);
+        pMixBuf->pSamples = (PPDMAUDIOSAMPLE)RTMemRealloc(pMixBuf->pSamples, cbSamples);
+        if (!pMixBuf->pSamples)
+            rc = VERR_NO_MEMORY;
+
+        if (RT_SUCCESS(rc))
+        {
+            pMixBuf->cSamples = cSamples;
+
+            /* Make sure to zero the reallocated buffer so that it can be
+             * used properly when blending with another buffer later. */
+            RT_BZERO(pMixBuf->pSamples, cbSamples);
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        if (!pMixBuf->pRate)
+        {
+            /* Create rate conversion. */
+            pMixBuf->pRate = (PPDMAUDIOSTRMRATE)RTMemAllocZ(sizeof(PDMAUDIOSTRMRATE));
+            if (!pMixBuf->pRate)
+                return VERR_NO_MEMORY;
+        }
+        else
+            RT_BZERO(pMixBuf->pRate, sizeof(PDMAUDIOSTRMRATE));
+
+        pMixBuf->pRate->dstInc = ((uint64_t)AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt) << 32)
+                               /            AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt);
+
+        AUDMIXBUF_LOG(("uThisHz=%RU32, uParentHz=%RU32, iFreqRatio=0x%RX64 (%RI64), uRateInc=0x%RX64 (%RU64), cSamples=%RU32 (%RU32 parent)\n",
+                       AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt),
+                       AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt),
+                       pMixBuf->iFreqRatio, pMixBuf->iFreqRatio,
+                       pMixBuf->pRate->dstInc, pMixBuf->pRate->dstInc,
+                       pMixBuf->cSamples,
+                       pParent->cSamples));
+        AUDMIXBUF_LOG(("%s (%RU32Hz) -> %s (%RU32Hz)\n",
+                       pMixBuf->pszName, AUDMIXBUF_FMT_SAMPLE_FREQ(pMixBuf->AudioFmt),
+                       pMixBuf->pParent->pszName, AUDMIXBUF_FMT_SAMPLE_FREQ(pParent->AudioFmt)));
+    }
+
+    return rc;
+}
+
+/**
+ * Returns the number of audio samples mixed (processed) by
+ * the parent mixing buffer.
+ *
+ * @return  uint32_t                Number of audio samples mixed (processed).
+ * @param   pMixBuf                 Mixing buffer to return number from.
+ */
+uint32_t AudioMixBufMixed(PPDMAUDIOMIXBUF pMixBuf)
+{
+    AssertPtrReturn(pMixBuf, 0);
+
+    AssertMsgReturn(VALID_PTR(pMixBuf->pParent),
+                              ("Buffer is not linked to a parent buffer\n"),
+                              0);
+
+    AUDMIXBUF_LOG(("%s: cMixed=%RU32\n", pMixBuf->pszName, pMixBuf->cMixed));
+    return pMixBuf->cMixed;
+}
+
+/**
+ * Mixes audio samples from a source mixing buffer to a destination mixing buffer.
+ *
+ * @return  IPRT status code.
+ * @param   pDst                    Destination mixing buffer.
+ * @param   pSrc                    Source mixing buffer.
+ * @param   cSamples                Number of source audio samples to mix.
+ * @param   pcProcessed             Number of audio samples successfully mixed.
+ */
+static int audioMixBufMixTo(PPDMAUDIOMIXBUF pDst, PPDMAUDIOMIXBUF pSrc, uint32_t cSamples, uint32_t *pcProcessed)
+{
+    AssertPtrReturn(pDst, VERR_INVALID_POINTER);
+    AssertPtrReturn(pSrc, VERR_INVALID_POINTER);
+    AssertReturn(cSamples, VERR_INVALID_PARAMETER);
+    /* pcProcessed is optional. */
+
+    /* Live samples indicate how many samples there are in the source buffer
+     * which have not been processed yet by the destination buffer. */
+    uint32_t cLive = pSrc->cMixed;
+    if (cLive >= pDst->cSamples)
+        AUDMIXBUF_LOG(("Destination buffer \"%s\" full (%RU32 samples max), live samples = %RU32\n",
+                       pDst->pszName, pDst->cSamples, cLive));
+
+    /* Dead samples are the number of samples in the destination buffer which
+     * will not be needed, that is, are not needed in order to process the live
+     * samples of the source buffer. */
+    uint32_t cDead = pDst->cSamples - cLive;
+
+    uint32_t cToReadTotal = (uint32_t)RT_MIN(cSamples, AUDIOMIXBUF_S2S_RATIO(pSrc, cDead));
+    uint32_t offRead = 0;
+
+    uint32_t cReadTotal = 0;
+    uint32_t cWrittenTotal = 0;
+    uint32_t offWrite = (pDst->offReadWrite + cLive) % pDst->cSamples;
+
+    AUDMIXBUF_LOG(("pSrc=%s (%RU32 samples), pDst=%s (%RU32 samples), cLive=%RU32, cDead=%RU32, cToReadTotal=%RU32, offWrite=%RU32\n",
+                   pSrc->pszName, pSrc->cSamples, pDst->pszName, pDst->cSamples, cLive, cDead, cToReadTotal, offWrite));
+
+    uint32_t cToRead, cToWrite;
+    uint32_t cWritten, cRead;
+
+    while (cToReadTotal)
+    {
+        cDead = pDst->cSamples - cLive;
+
+        cToRead  = cToReadTotal;
+        cToWrite = RT_MIN(cDead, pDst->cSamples - offWrite);
+        if (!cToWrite)
+        {
+            AUDMIXBUF_LOG(("Warning: Destination buffer \"%s\" full\n", pDst->pszName));
+            break;
+        }
+
+        Assert(offWrite + cToWrite <= pDst->cSamples);
+        Assert(offRead + cToRead <= pSrc->cSamples);
+
+        AUDMIXBUF_LOG(("\t%RU32Hz -> %RU32Hz\n", AUDMIXBUF_FMT_SAMPLE_FREQ(pSrc->AudioFmt), AUDMIXBUF_FMT_SAMPLE_FREQ(pDst->AudioFmt)));
+        AUDMIXBUF_LOG(("\tcDead=%RU32, offWrite=%RU32, cToWrite=%RU32, offRead=%RU32, cToRead=%RU32\n",
+                       cDead, offWrite, cToWrite, offRead, cToRead));
+
+        audioMixBufOpBlend(pDst->pSamples + offWrite, cToWrite,
+                           pSrc->pSamples + offRead, cToRead,
+                           pSrc->pRate, &cWritten, &cRead);
+
+        AUDMIXBUF_LOG(("\t\tcWritten=%RU32, cRead=%RU32\n", cWritten, cRead));
+
+        cReadTotal    += cRead;
+        cWrittenTotal += cWritten;
+
+        offRead += cRead;
+        Assert(cToReadTotal >= cRead);
+        cToReadTotal -= cRead;
+
+        offWrite = (offWrite + cWritten) % pDst->cSamples;
+
+        cLive += cWritten;
+    }
+
+    pSrc->cMixed     += cWrittenTotal;
+    pDst->cProcessed += cWrittenTotal;
+#ifdef DEBUG
+    s_cSamplesMixedTotal += cWrittenTotal;
+    audioMixBufPrint(pDst);
+#endif
+
+    if (pcProcessed)
+        *pcProcessed = cReadTotal;
+
+    AUDMIXBUF_LOG(("cReadTotal=%RU32 (pcProcessed), cWrittenTotal=%RU32, cSrcMixed=%RU32, cDstProc=%RU32\n",
+                   cReadTotal, cWrittenTotal, pSrc->cMixed, pDst->cProcessed));
+    return VINF_SUCCESS;
+}
+
+/**
+ * Mixes (multiplexes) audio samples to all connected mixing buffer children.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to use.
+ * @param   cSamples                Number of audio samples to mix to children.
+ * @param   pcProcessed             Maximum number of audio samples successfully mixed
+ *                                  to all children. Optional.
+ */
+int AudioMixBufMixToChildren(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamples,
+                             uint32_t *pcProcessed)
+{
+    AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
+
+    if (!cSamples)
+    {
+        if (pcProcessed)
+            *pcProcessed = 0;
+        return VINF_SUCCESS;
+    }
+
+    int rc = VINF_SUCCESS;
+
+    uint32_t cProcessed;
+    uint32_t cProcessedMax = 0;
+
+    PPDMAUDIOMIXBUF pIter;
+    RTListForEach(&pMixBuf->lstBuffers, pIter, PDMAUDIOMIXBUF, Node)
+    {
+        rc = audioMixBufMixTo(pIter, pMixBuf, cSamples, &cProcessed);
+        if (RT_FAILURE(rc))
+            break;
+
+        cProcessedMax = RT_MAX(cProcessedMax, cProcessed);
+    }
+
+    if (pcProcessed)
+        *pcProcessed = cProcessedMax;
+
+    return rc;
+}
+
+/**
+ * Mixes audio samples down to the parent mixing buffer.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to mix samples down to parent.
+ * @param   cSamples                Number of audio samples to mix down.
+ * @param   pcProcessed             Number of audio samples successfully processed. Optional.
+ */
+int AudioMixBufMixToParent(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamples,
+                           uint32_t *pcProcessed)
+{
+    AssertMsgReturn(VALID_PTR(pMixBuf->pParent),
+                    ("Buffer is not linked to a parent buffer\n"),
+                    VERR_INVALID_PARAMETER);
+
+    return audioMixBufMixTo(pMixBuf->pParent, pMixBuf, cSamples, pcProcessed);
+}
+
+#ifdef DEBUG
+/**
+ * Prints statistics and status of a mixing buffer to the logger.
+ * For debug versions only.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to print.
+ */
+static inline void audioMixBufPrint(PPDMAUDIOMIXBUF pMixBuf)
+{
+    PPDMAUDIOMIXBUF pParent = pMixBuf;
+    if (pMixBuf->pParent)
+        pParent = pMixBuf->pParent;
+
+    AUDMIXBUF_LOG(("********************************************\n"));
+    AUDMIXBUF_LOG(("%s: offReadWrite=%RU32, cProcessed=%RU32, cMixed=%RU32 (BpS=%RU32)\n",
+                   pParent->pszName,
+                   pParent->offReadWrite, pParent->cProcessed, pParent->cMixed,
+                   AUDIOMIXBUF_S2B(pParent, 1)));
+
+    PPDMAUDIOMIXBUF pIter;
+    RTListForEach(&pParent->lstBuffers, pIter, PDMAUDIOMIXBUF, Node)
+    {
+        AUDMIXBUF_LOG(("\t%s: offReadWrite=%RU32, cProcessed=%RU32, cMixed=%RU32 (BpS=%RU32)\n",
+                       pIter->pszName,
+                       pIter->offReadWrite, pIter->cProcessed, pIter->cMixed,
+                       AUDIOMIXBUF_S2B(pIter, 1)));
+    }
+    AUDMIXBUF_LOG(("Total samples mixed: %RU64\n", s_cSamplesMixedTotal));
+    AUDMIXBUF_LOG(("********************************************\n"));
+}
+#endif
+
+/**
+ * Returns the total number of samples processed.
+ *
+ * @return  uint32_t
+ * @param   pMixBuf
+ */
+uint32_t AudioMixBufProcessed(PPDMAUDIOMIXBUF pMixBuf)
+{
+    AssertPtrReturn(pMixBuf, 0);
+
+    AUDMIXBUF_LOG(("%s: cProcessed=%RU32\n", pMixBuf->pszName, pMixBuf->cProcessed));
+    return pMixBuf->cProcessed;
+}
+
+/**
+ * Reads audio samples at a specific offset.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to read audio samples from.
+ * @param   offSamples              Offset (in audio samples) to start reading from.
+ * @param   pvBuf                   Pointer to buffer to write output to.
+ * @param   cbBuf                   Size (in bytes) of buffer to write to.
+ * @param   pcbRead                 Size (in bytes) of data read. Optional.
+ */
+int AudioMixBufReadAt(PPDMAUDIOMIXBUF pMixBuf,
+                      uint32_t offSamples,
+                      void *pvBuf, uint32_t cbBuf,
+                      uint32_t *pcbRead)
+{
+    return AudioMixBufReadAtEx(pMixBuf, pMixBuf->AudioFmt,
+                               offSamples, pvBuf, cbBuf, pcbRead);
+}
+
+/**
+ * Reads audio samples at a specific offset.
+ * If the audio format of the mixing buffer and the requested audio format do
+ * not match the output will be converted accordingly.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to read audio samples from.
+ * @param   enmFmt                  Audio format to use for output.
+ * @param   offSamples              Offset (in audio samples) to start reading from.
+ * @param   pvBuf                   Pointer to buffer to write output to.
+ * @param   cbBuf                   Size (in bytes) of buffer to write to.
+ * @param   pcbRead                 Size (in bytes) of data read. Optional.
+ */
+int AudioMixBufReadAtEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
+                        uint32_t offSamples,
+                        void *pvBuf, uint32_t cbBuf,
+                        uint32_t *pcbRead)
+{
+    AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+    /* pcbRead is optional. */
+
+    uint32_t cDstSamples = pMixBuf->cSamples;
+    uint32_t cLive = pMixBuf->cProcessed;
+
+    uint32_t cDead = cDstSamples - cLive;
+    uint32_t cToProcess = (uint32_t)AUDIOMIXBUF_S2S_RATIO(pMixBuf, cDead);
+    cToProcess = RT_MIN(cToProcess, AUDIOMIXBUF_B2S(pMixBuf, cbBuf));
+
+    AUDMIXBUF_LOG(("%s: offSamples=%RU32, cLive=%RU32, cDead=%RU32, cToProcess=%RU32\n",
+                   pMixBuf->pszName, offSamples, cLive, cDead, cToProcess));
+
+    int rc;
+    if (cToProcess)
+    {
+        PAUDMIXBUF_FN_CONVTO pConv = audioMixBufConvToLookup(enmFmt);
+        if (pConv)
+        {
+            AUDMIXBUF_CONVOPTS convOpts = { cToProcess, pMixBuf->Volume };
+            pConv(pvBuf, pMixBuf->pSamples + offSamples, &convOpts);
+
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_INVALID_PARAMETER;
+
+#ifdef DEBUG
+        audioMixBufPrint(pMixBuf);
+#endif
+    }
+    else
+        rc = VINF_SUCCESS;
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pcbRead)
+            *pcbRead = AUDIOMIXBUF_S2B(pMixBuf, cToProcess);
+    }
+
+    AUDMIXBUF_LOG(("cbRead=%RU32, rc=%Rrc\n", AUDIOMIXBUF_S2B(pMixBuf, cToProcess), rc));
+    return rc;
+}
+
+/**
+ * Reads audio samples. The audio format of the mixing buffer will be used.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to read audio samples from.
+ * @param   pvBuf                   Pointer to buffer to write output to.
+ * @param   cbBuf                   Size (in bytes) of buffer to write to.
+ * @param   pcRead                  Number of audio samples read. Optional.
+ */
+int AudioMixBufReadCirc(PPDMAUDIOMIXBUF pMixBuf,
+                        void *pvBuf, uint32_t cbBuf, uint32_t *pcRead)
+{
+    return AudioMixBufReadCircEx(pMixBuf, pMixBuf->AudioFmt,
+                                 pvBuf, cbBuf, pcRead);
+}
+
+/**
+ * Reads audio samples in a specific audio format.
+ * If the audio format of the mixing buffer and the requested audio format do
+ * not match the output will be converted accordingly.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to read audio samples from.
+ * @param   enmFmt                  Audio format to use for output.
+ * @param   pvBuf                   Pointer to buffer to write output to.
+ * @param   cbBuf                   Size (in bytes) of buffer to write to.
+ * @param   pcRead                  Number of audio samples read. Optional.
+ */
+int AudioMixBufReadCircEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
+                          void *pvBuf, uint32_t cbBuf, uint32_t *pcRead)
+{
+    AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+    /* pcbRead is optional. */
+
+    if (!cbBuf)
+        return VINF_SUCCESS;
+
+    uint32_t cToRead = RT_MIN(AUDIOMIXBUF_B2S(pMixBuf, cbBuf), pMixBuf->cProcessed);
+
+    AUDMIXBUF_LOG(("%s: pvBuf=%p, cbBuf=%zu (%RU32 samples), cToRead=%RU32\n",
+                   pMixBuf->pszName, pvBuf, cbBuf, AUDIOMIXBUF_B2S(pMixBuf, cbBuf), cToRead));
+
+    if (!cToRead)
+    {
+#ifdef DEBUG
+        audioMixBufPrint(pMixBuf);
+#endif
+        if (pcRead)
+            *pcRead = 0;
+        return VINF_SUCCESS;
+    }
+
+    PAUDMIXBUF_FN_CONVTO pConv = audioMixBufConvToLookup(enmFmt);
+    if (!pConv) /* Audio format not supported. */
+        return VERR_NOT_SUPPORTED;
+
+    PPDMAUDIOSAMPLE pSamplesSrc1 = pMixBuf->pSamples + pMixBuf->offReadWrite;
+    uint32_t cLenSrc1 = cToRead;
+
+    PPDMAUDIOSAMPLE pSamplesSrc2 = NULL;
+    uint32_t cLenSrc2 = 0;
+
+    uint32_t offRead = pMixBuf->offReadWrite + cToRead;
+
+    /*
+     * Do we need to wrap around to read all requested data, that is,
+     * starting at the beginning of our circular buffer? This then will
+     * be the optional second part to do.
+     */
+    if (offRead >= pMixBuf->cSamples)
+    {
+        Assert(pMixBuf->offReadWrite <= pMixBuf->cSamples);
+        cLenSrc1 = pMixBuf->cSamples - pMixBuf->offReadWrite;
+
+        pSamplesSrc2 = pMixBuf->pSamples;
+        Assert(cToRead >= cLenSrc1);
+        cLenSrc2 = RT_MIN(cToRead - cLenSrc1, pMixBuf->cSamples);
+
+        /* Save new read offset. */
+        offRead = cLenSrc2;
+    }
+
+    AUDMIXBUF_CONVOPTS convOpts;
+    convOpts.Volume = pMixBuf->Volume;
+
+    /* Anything to do at all? */
+    int rc = VINF_SUCCESS;
+    if (cLenSrc1)
+    {
+        convOpts.cSamples = cLenSrc1;
+
+        AUDMIXBUF_LOG(("P1: offRead=%RU32, cToRead=%RU32\n", pMixBuf->offReadWrite, cLenSrc1));
+        pConv(pvBuf, pSamplesSrc1, &convOpts);
+    }
+
+    /* Second part present? */
+    if (   RT_LIKELY(RT_SUCCESS(rc))
+        && cLenSrc2)
+    {
+        AssertPtr(pSamplesSrc2);
+
+        convOpts.cSamples = cLenSrc2;
+
+        AUDMIXBUF_LOG(("P2: cToRead=%RU32, offWrite=%RU32 (%zu bytes)\n", cLenSrc2, cLenSrc1,
+                       AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1)));
+        pConv((uint8_t *)pvBuf + AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1), pSamplesSrc2, &convOpts);
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+#ifdef DEBUG_DUMP_PCM_DATA
+        RTFILE fh;
+        rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "mixbuf_readcirc.pcm",
+                        RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
+        if (RT_SUCCESS(rc))
+        {
+            RTFileWrite(fh, pvBuf, AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1 + cLenSrc2), NULL);
+            RTFileClose(fh);
+        }
+#endif
+        pMixBuf->offReadWrite  = offRead % pMixBuf->cSamples;
+        pMixBuf->cProcessed   -= RT_MIN(cLenSrc1 + cLenSrc2, pMixBuf->cProcessed);
+
+        if (pcRead)
+            *pcRead = cLenSrc1 + cLenSrc2;
+    }
+
+#ifdef DEBUG
+    audioMixBufPrint(pMixBuf);
+#endif
+
+    AUDMIXBUF_LOG(("cRead=%RU32 (%zu bytes), rc=%Rrc\n",
+                   cLenSrc1 + cLenSrc2,
+                   AUDIOMIXBUF_S2B(pMixBuf, cLenSrc1 + cLenSrc2), rc));
+    return rc;
+}
+
+/**
+ * Resets a mixing buffer.
+ *
+ * @param   pMixBuf                 Mixing buffer to reset.
+ */
+void AudioMixBufReset(PPDMAUDIOMIXBUF pMixBuf)
+{
+    AssertPtrReturnVoid(pMixBuf);
+
+    AUDMIXBUF_LOG(("%s\n", pMixBuf->pszName));
+
+    pMixBuf->offReadWrite = 0;
+    pMixBuf->cMixed       = 0;
+    pMixBuf->cProcessed   = 0;
+
+    AudioMixBufClear(pMixBuf);
+}
+
+/**
+ * Sets the overall (master) volume.
+ *
+ * @param   pMixBuf                 Mixing buffer to set volume for.
+ * @param   pVol                    Pointer to volume structure to set.
+ */
+void AudioMixBufSetVolume(PPDMAUDIOMIXBUF pMixBuf, PPDMAUDIOVOLUME pVol)
+{
+    AssertPtrReturnVoid(pMixBuf);
+    AssertPtrReturnVoid(pVol);
+
+    LogFlowFunc(("%s: lVol=%RU32, rVol=%RU32\n", pMixBuf->pszName, pVol->uLeft, pVol->uRight));
+
+    pMixBuf->Volume.fMuted = pVol->fMuted;
+    /** @todo Ensure that the input is in the correct range/initialized! */
+    pMixBuf->Volume.uLeft  = s_aVolumeConv[pVol->uLeft  & 0xFF] * (AUDIOMIXBUF_VOL_0DB >> 16);
+    pMixBuf->Volume.uRight = s_aVolumeConv[pVol->uRight & 0xFF] * (AUDIOMIXBUF_VOL_0DB >> 16);
+
+    LogFlowFunc(("\t-> lVol=%#RX32, rVol=%#RX32\n", pMixBuf->Volume.uLeft, pMixBuf->Volume.uRight));
+}
+
+/**
+ * Returns the maximum amount of audio samples this buffer can hold.
+ *
+ * @return  uint32_t                Size (in audio samples) the mixing buffer can hold.
+ * @param   pMixBuf                 Mixing buffer to retrieve maximum for.
+ */
+uint32_t AudioMixBufSize(PPDMAUDIOMIXBUF pMixBuf)
+{
+    AssertPtrReturn(pMixBuf, 0);
+    return pMixBuf->cSamples;
+}
+
+/**
+ * Returns the maximum amount of bytes this buffer can hold.
+ *
+ * @return  uint32_t                Size (in bytes) the mixing buffer can hold.
+ * @param   pMixBuf                 Mixing buffer to retrieve maximum for.
+ */
+uint32_t AudioMixBufSizeBytes(PPDMAUDIOMIXBUF pMixBuf)
+{
+    AssertPtrReturn(pMixBuf, 0);
+    return AUDIOMIXBUF_S2B(pMixBuf, pMixBuf->cSamples);
+}
+
+/**
+ * Unlinks a mixing buffer from its parent, if any.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Mixing buffer to unlink from parent.
+ */
+void AudioMixBufUnlink(PPDMAUDIOMIXBUF pMixBuf)
+{
+    if (!pMixBuf || !pMixBuf->pszName)
+        return;
+
+    AUDMIXBUF_LOG(("%s\n", pMixBuf->pszName));
+
+    if (pMixBuf->pParent)
+    {
+        AUDMIXBUF_LOG(("%s: Unlinking from parent \"%s\"\n",
+                       pMixBuf->pszName, pMixBuf->pParent->pszName));
+
+        RTListNodeRemove(&pMixBuf->Node);
+
+        /* Make sure to reset the parent mixing buffer each time it gets linked
+         * to a new child. */
+        AudioMixBufReset(pMixBuf->pParent);
+        pMixBuf->pParent = NULL;
+    }
+
+    PPDMAUDIOMIXBUF pIter;
+    while (!RTListIsEmpty(&pMixBuf->lstBuffers))
+    {
+        pIter = RTListGetFirst(&pMixBuf->lstBuffers, PDMAUDIOMIXBUF, Node);
+
+        AUDMIXBUF_LOG(("\tUnlinking \"%s\"\n", pIter->pszName));
+
+        AudioMixBufReset(pIter->pParent);
+        pIter->pParent = NULL;
+
+        RTListNodeRemove(&pIter->Node);
+    }
+
+    if (pMixBuf->pRate)
+    {
+        pMixBuf->pRate->dstOffset = pMixBuf->pRate->srcOffset = 0;
+        pMixBuf->pRate->dstInc = 0;
+    }
+
+    pMixBuf->iFreqRatio = 1; /* Prevent division by zero. */
+}
+
+/**
+ * Writes audio samples at a specific offset.
+ * The sample format being written must match the format of the mixing buffer.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Pointer to mixing buffer to write to.
+ * @param   enmFmt                  Audio format supplied in the buffer.
+ * @param   offSamples              Offset (in samples) starting to write at.
+ * @param   pvBuf                   Pointer to audio buffer to be written.
+ * @param   cbBuf                   Size (in bytes) of audio buffer.
+ * @param   pcWritten               Returns number of audio samples written. Optional.
+ */
+int AudioMixBufWriteAt(PPDMAUDIOMIXBUF pMixBuf,
+                       uint32_t offSamples,
+                       const void *pvBuf, uint32_t cbBuf,
+                       uint32_t *pcWritten)
+{
+    return AudioMixBufWriteAtEx(pMixBuf, pMixBuf->AudioFmt,
+                                offSamples, pvBuf, cbBuf, pcWritten);
+}
+
+/**
+ * Writes audio samples at a specific offset. The audio sample format
+ * to be written can be different from the audio format the mixing buffer
+ * operates on.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Pointer to mixing buffer to write to.
+ * @param   enmFmt                  Audio format supplied in the buffer.
+ * @param   offSamples              Offset (in samples) starting to write at.
+ * @param   pvBuf                   Pointer to audio buffer to be written.
+ * @param   cbBuf                   Size (in bytes) of audio buffer.
+ * @param   pcWritten               Returns number of audio samples written. Optional.
+ */
+int AudioMixBufWriteAtEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
+                         uint32_t offSamples,
+                         const void *pvBuf, uint32_t cbBuf,
+                         uint32_t *pcWritten)
+{
+    AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+    /* pcWritten is optional. */
+
+    uint32_t cDstSamples = pMixBuf->pParent
+                         ? pMixBuf->pParent->cSamples : pMixBuf->cSamples;
+    uint32_t cLive = pMixBuf->cProcessed;
+
+    uint32_t cDead = cDstSamples - cLive;
+    uint32_t cToProcess = (uint32_t)AUDIOMIXBUF_S2S_RATIO(pMixBuf, cDead);
+    cToProcess = RT_MIN(cToProcess, AUDIOMIXBUF_B2S(pMixBuf, cbBuf));
+
+    AUDMIXBUF_LOG(("%s: offSamples=%RU32, cLive=%RU32, cDead=%RU32, cToProcess=%RU32\n",
+                   pMixBuf->pszName, offSamples, cLive, cDead, cToProcess));
+
+    if (offSamples + cToProcess > pMixBuf->cSamples)
+        return VERR_BUFFER_OVERFLOW;
+
+    PAUDMIXBUF_FN_CONVFROM pConv = audioMixBufConvFromLookup(enmFmt, pMixBuf->Volume.fMuted);
+    if (!pConv)
+        return VERR_NOT_SUPPORTED;
+
+    int rc;
+    uint32_t cWritten;
+
+#ifdef DEBUG_DUMP_PCM_DATA
+    RTFILE fh;
+    rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "mixbuf_writeat.pcm",
+                    RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
+    if (RT_SUCCESS(rc))
+    {
+        RTFileWrite(fh, pvBuf, cbBuf, NULL);
+        RTFileClose(fh);
+    }
+#endif
+
+    if (cToProcess)
+    {
+        AUDMIXBUF_CONVOPTS convOpts = { cToProcess, pMixBuf->Volume };
+
+        cWritten = pConv(pMixBuf->pSamples + offSamples, pvBuf, cbBuf, &convOpts);
+#ifdef DEBUG
+        audioMixBufPrint(pMixBuf);
+#endif
+        rc = cWritten ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+    else
+    {
+        cWritten = 0;
+        rc = VINF_SUCCESS;
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pcWritten)
+            *pcWritten = cWritten;
+    }
+
+    AUDMIXBUF_LOG(("cWritten=%RU32, rc=%Rrc\n", cWritten, rc));
+    return rc;
+}
+
+/**
+ * Writes audio samples. The sample format being written must match the
+ * format of the mixing buffer.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Pointer to mixing buffer to write to.
+ * @param   pvBuf                   Pointer to audio buffer to be written.
+ * @param   cbBuf                   Size (in bytes) of audio buffer.
+ * @param   pcWritten               Returns number of audio samples written. Optional.
+ */
+int AudioMixBufWriteCirc(PPDMAUDIOMIXBUF pMixBuf,
+                         const void *pvBuf, uint32_t cbBuf,
+                         uint32_t *pcWritten)
+{
+    return AudioMixBufWriteCircEx(pMixBuf, pMixBuf->AudioFmt, pvBuf, cbBuf, pcWritten);
+}
+
+/**
+ * Writes audio samples of a specific format.
+ *
+ * @return  IPRT status code.
+ * @param   pMixBuf                 Pointer to mixing buffer to write to.
+ * @param   enmFmt                  Audio format supplied in the buffer.
+ * @param   pvBuf                   Pointer to audio buffer to be written.
+ * @param   cbBuf                   Size (in bytes) of audio buffer.
+ * @param   pcWritten               Returns number of audio samples written. Optional.
+ */
+int AudioMixBufWriteCircEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt,
+                           const void *pvBuf, uint32_t cbBuf,
+                           uint32_t *pcWritten)
+{
+    AssertPtrReturn(pMixBuf, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+    /* pcbWritten is optional. */
+
+    if (!cbBuf)
+    {
+        if (pcWritten)
+            *pcWritten = 0;
+        return VINF_SUCCESS;
+    }
+
+    PPDMAUDIOMIXBUF pParent = pMixBuf->pParent;
+
+    AUDMIXBUF_LOG(("%s: enmFmt=%ld, pBuf=%p, cbBuf=%zu, pParent=%p (%RU32)\n",
+                   pMixBuf->pszName, enmFmt, pvBuf, cbBuf, pParent, pParent ? pParent->cSamples : 0));
+
+    if (   pParent
+        && pParent->cSamples <= pMixBuf->cMixed)
+    {
+        if (pcWritten)
+            *pcWritten = 0;
+
+        AUDMIXBUF_LOG(("%s: Parent buffer %s is full\n",
+                       pMixBuf->pszName, pMixBuf->pParent->pszName));
+
+        return VINF_SUCCESS;
+    }
+
+    PAUDMIXBUF_FN_CONVFROM pConv = audioMixBufConvFromLookup(enmFmt, pMixBuf->Volume.fMuted);
+    if (!pConv)
+        return VERR_NOT_SUPPORTED;
+
+    int rc = VINF_SUCCESS;
+
+    uint32_t cToWrite = AUDIOMIXBUF_B2S(pMixBuf, cbBuf);
+    AssertMsg(cToWrite, ("cToWrite is 0 (cbBuf=%zu)\n", cbBuf));
+
+    PPDMAUDIOSAMPLE pSamplesDst1 = pMixBuf->pSamples + pMixBuf->offReadWrite;
+    uint32_t cLenDst1 = cToWrite;
+
+    PPDMAUDIOSAMPLE pSamplesDst2 = NULL;
+    uint32_t cLenDst2 = 0;
+
+    uint32_t offWrite = pMixBuf->offReadWrite + cToWrite;
+
+    /*
+     * Do we need to wrap around to write all requested data, that is,
+     * starting at the beginning of our circular buffer? This then will
+     * be the optional second part to do.
+     */
+    if (offWrite >= pMixBuf->cSamples)
+    {
+        Assert(pMixBuf->offReadWrite <= pMixBuf->cSamples);
+        cLenDst1 = pMixBuf->cSamples - pMixBuf->offReadWrite;
+
+        pSamplesDst2 = pMixBuf->pSamples;
+        Assert(cToWrite >= cLenDst1);
+        cLenDst2 = RT_MIN(cToWrite - cLenDst1, pMixBuf->cSamples);
+
+        /* Save new read offset. */
+        offWrite = cLenDst2;
+    }
+
+    uint32_t cWrittenTotal = 0;
+
+    AUDMIXBUF_CONVOPTS convOpts;
+    convOpts.Volume = pMixBuf->Volume;
+
+    /* Anything to do at all? */
+    if (cLenDst1)
+    {
+        convOpts.cSamples = cLenDst1;
+        cWrittenTotal = pConv(pSamplesDst1, pvBuf, cbBuf, &convOpts);
+    }
+
+    /* Second part present? */
+    if (   RT_LIKELY(RT_SUCCESS(rc))
+        && cLenDst2)
+    {
+        AssertPtr(pSamplesDst2);
+
+        convOpts.cSamples = cLenDst2;
+        cWrittenTotal += pConv(pSamplesDst2, (uint8_t *)pvBuf + AUDIOMIXBUF_S2B(pMixBuf, cLenDst1), cbBuf, &convOpts);
+    }
+
+#ifdef DEBUG_DUMP_PCM_DATA
+        RTFILE fh;
+        RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "mixbuf_writeex.pcm",
+                   RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
+        RTFileWrite(fh, pSamplesDst1, AUDIOMIXBUF_S2B(pMixBuf, cLenDst1), NULL);
+        RTFileClose(fh);
+#endif
+
+    AUDMIXBUF_LOG(("cLenDst1=%RU32, cLenDst2=%RU32, offWrite=%RU32\n",
+                   cLenDst1, cLenDst2, offWrite));
+
+    if (RT_SUCCESS(rc))
+    {
+        pMixBuf->offReadWrite = offWrite % pMixBuf->cSamples;
+        pMixBuf->cProcessed = RT_MIN(pMixBuf->cProcessed + cLenDst1 + cLenDst2,
+                                     pMixBuf->cSamples /* Max */);
+        if (pcWritten)
+            *pcWritten = cLenDst1 + cLenDst2;
+    }
+
+#ifdef DEBUG
+    audioMixBufPrint(pMixBuf);
+#endif
+
+    AUDMIXBUF_LOG(("cWritten=%RU32 (%zu bytes), rc=%Rrc\n",
+                   cLenDst1 + cLenDst2,
+                   AUDIOMIXBUF_S2B(pMixBuf, cLenDst1 + cLenDst2), rc));
+    return rc;
+}
+
Index: /trunk/src/VBox/Devices/Audio_old/AudioMixBuffer.h
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/AudioMixBuffer.h	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/AudioMixBuffer.h	(revision 61413)
@@ -0,0 +1,82 @@
+/* $Id$ */
+/** @file
+ * VBox audio: Mixing buffer to convert audio samples to/from different
+ *             rates / formats.
+ */
+
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#ifndef AUDIO_MIXBUF_H
+#define AUDIO_MIXBUF_H
+
+#include <iprt/cdefs.h>
+#include <VBox/vmm/pdmaudioifs.h>
+
+/** Constructs 32 bit value for given frequency, number of channels, bits per sample and signed bit.
+ *  Note: This currently matches 1:1 the VRDE encoding -- this might change in the future, so better don't rely on this fact! */
+#define AUDMIXBUF_AUDIO_FMT_MAKE(freq, c, bps, s) ((((s) & 0x1) << 28) + (((bps) & 0xFF) << 20) + (((c) & 0xF) << 16) + ((freq) & 0xFFFF))
+
+/** Decodes frequency (Hz). */
+#define AUDMIXBUF_FMT_SAMPLE_FREQ(a) ((a) & 0xFFFF)
+/** Decodes number of channels. */
+#define AUDMIXBUF_FMT_CHANNELS(a) (((a) >> 16) & 0xF)
+/** Decodes signed bit. */
+#define AUDMIXBUF_FMT_SIGNED(a) (((a) >> 28) & 0x1)
+/** Decodes number of bits per sample. */
+#define AUDMIXBUF_FMT_BITS_PER_SAMPLE(a) (((a) >> 20) & 0xFF)
+/** Decodes number of bytes per sample. */
+#define AUDMIXBUF_FMT_BYTES_PER_SAMPLE(a) ((AUDMIXBUF_AUDIO_FMT_BITS_PER_SAMPLE(a) + 7) / 8)
+
+/** Converts samples to bytes. */
+#define AUDIOMIXBUF_S2B(pBuf, samples) ((samples) << (pBuf)->cShift)
+/** Converts samples to bytes, respecting the conversion ratio to
+ *  a linked buffer. */
+#define AUDIOMIXBUF_S2B_RATIO(pBuf, samples) ((((int64_t) samples << 32) / (pBuf)->iFreqRatio) << (pBuf)->cShift)
+/** Converts bytes to samples, *not* taking the conversion ratio
+ *  into account. */
+#define AUDIOMIXBUF_B2S(pBuf, cb)  (cb >> (pBuf)->cShift)
+/** Converts number of samples according to the buffer's ratio. */
+#define AUDIOMIXBUF_S2S_RATIO(pBuf, samples)  (((int64_t) samples << 32) / (pBuf)->iFreqRatio)
+
+
+int AudioMixBufAcquire(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamplesToRead, PPDMAUDIOSAMPLE *ppvSamples, uint32_t *pcSamplesRead);
+uint32_t AudioMixBufAvail(PPDMAUDIOMIXBUF pMixBuf);
+inline uint32_t AudioMixBufBytesToSamples(PPDMAUDIOMIXBUF pMixBuf);
+void AudioMixBufClear(PPDMAUDIOMIXBUF pMixBuf);
+void AudioMixBufDestroy(PPDMAUDIOMIXBUF pMixBuf);
+void AudioMixBufFinish(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamplesToClear);
+uint32_t AudioMixBufFree(PPDMAUDIOMIXBUF pMixBuf);
+uint32_t AudioMixBufFreeBytes(PPDMAUDIOMIXBUF pMixBuf);
+int AudioMixBufInit(PPDMAUDIOMIXBUF pMixBuf, const char *pszName, PPDMPCMPROPS pProps, uint32_t cSamples);
+bool AudioMixBufIsEmpty(PPDMAUDIOMIXBUF pMixBuf);
+int AudioMixBufLinkTo(PPDMAUDIOMIXBUF pMixBuf, PPDMAUDIOMIXBUF pParent);
+uint32_t AudioMixBufMixed(PPDMAUDIOMIXBUF pMixBuf);
+int AudioMixBufMixToChildren(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamples, uint32_t *pcProcessed);
+int AudioMixBufMixToParent(PPDMAUDIOMIXBUF pMixBuf, uint32_t cSamples, uint32_t *pcProcessed);
+uint32_t AudioMixBufProcessed(PPDMAUDIOMIXBUF pMixBuf);
+int AudioMixBufReadAt(PPDMAUDIOMIXBUF pMixBuf, uint32_t offSamples, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead);
+int AudioMixBufReadAtEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt, uint32_t offSamples, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead);
+int AudioMixBufReadCirc(PPDMAUDIOMIXBUF pMixBuf, void *pvBuf, uint32_t cbBuf, uint32_t *pcRead);
+int AudioMixBufReadCircEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt, void *pvBuf, uint32_t cbBuf, uint32_t *pcRead);
+void AudioMixBufReset(PPDMAUDIOMIXBUF pMixBuf);
+void AudioMixBufSetVolume(PPDMAUDIOMIXBUF pMixBuf, PPDMAUDIOVOLUME pVol);
+uint32_t AudioMixBufSize(PPDMAUDIOMIXBUF pMixBuf);
+uint32_t AudioMixBufSizeBytes(PPDMAUDIOMIXBUF pMixBuf);
+void AudioMixBufUnlink(PPDMAUDIOMIXBUF pMixBuf);
+int AudioMixBufWriteAt(PPDMAUDIOMIXBUF pMixBuf, uint32_t offSamples, const void *pvBuf, uint32_t cbBuf, uint32_t *pcWritten);
+int AudioMixBufWriteAtEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt, uint32_t offSamples, const void *pvBuf, uint32_t cbBuf, uint32_t *pcWritten);
+int AudioMixBufWriteCirc(PPDMAUDIOMIXBUF pMixBuf, const void *pvBuf, uint32_t cbBuf, uint32_t *pcWritten);
+int AudioMixBufWriteCircEx(PPDMAUDIOMIXBUF pMixBuf, PDMAUDIOMIXBUFFMT enmFmt, const void *pvBuf, uint32_t cbBuf, uint32_t *pcWritten);
+
+#endif /* AUDIO_MIXBUF_H */
+
Index: /trunk/src/VBox/Devices/Audio_old/AudioMixer.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/AudioMixer.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/AudioMixer.cpp	(revision 61413)
@@ -0,0 +1,514 @@
+/* $Id$ */
+/** @file
+ * VBox audio: Mixing routines, mainly used by the various audio device
+ *             emulations to achieve proper multiplexing from/to attached
+ *             devices LUNs.
+ */
+
+/*
+ * Copyright (C) 2014-2016 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.
+ */
+#define LOG_GROUP LOG_GROUP_AUDIO_MIXER
+#include <VBox/log.h>
+#include "AudioMixer.h"
+#include "AudioMixBuffer.h"
+
+#include <VBox/vmm/pdm.h>
+#include <VBox/err.h>
+#include <VBox/vmm/mm.h>
+#include <VBox/vmm/pdmaudioifs.h>
+
+#include <iprt/alloc.h>
+#include <iprt/asm-math.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+static int audioMixerUpdateSinkVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster);
+
+
+int AudioMixerAddSink(PAUDIOMIXER pMixer, const char *pszName, AUDMIXSINKDIR enmDir, PAUDMIXSINK *ppSink)
+{
+    AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+    /** ppSink is optional. */
+
+    int rc = VINF_SUCCESS;
+
+    PAUDMIXSINK pSink = (PAUDMIXSINK)RTMemAllocZ(sizeof(AUDMIXSINK));
+    if (pSink)
+    {
+        pSink->pszName = RTStrDup(pszName);
+        if (!pSink->pszName)
+            rc = VERR_NO_MEMORY;
+
+        if (RT_SUCCESS(rc))
+        {
+            pSink->pParent  = pMixer;
+            pSink->cStreams = 0;
+            pSink->enmDir   = enmDir;
+            RTListInit(&pSink->lstStreams);
+
+            /* Set initial volume to max. */
+            pSink->Volume.fMuted = false;
+            pSink->Volume.uLeft  = 0x7F;
+            pSink->Volume.uRight = 0x7F;
+
+            RTListAppend(&pMixer->lstSinks, &pSink->Node);
+            pMixer->cSinks++;
+
+            LogFlowFunc(("pMixer=%p, pSink=%p, cSinks=%RU8\n",
+                         pMixer, pSink, pMixer->cSinks));
+
+            if (ppSink)
+                *ppSink = pSink;
+        }
+        else
+            RTMemFree(pSink);
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    return rc;
+}
+
+int AudioMixerAddStreamIn(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConnector, PPDMAUDIOGSTSTRMIN pStream,
+                          uint32_t uFlags, PAUDMIXSTREAM *ppStream)
+{
+    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
+    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
+    /** @todo Add flag validation. */
+    /* ppStream is optional. */
+
+    int rc;
+
+    if (pSink->cStreams == UINT8_MAX) /* 255 streams per sink max. */
+        return VERR_TOO_MUCH_DATA;
+
+    PAUDMIXSTREAM pMixStream
+        = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
+    if (pMixStream)
+    {
+        pMixStream->pConn = pConnector;
+        pMixStream->pIn   = pStream;
+        /** @todo Process flags. */
+
+        RTListAppend(&pSink->lstStreams, &pMixStream->Node);
+        pSink->cStreams++;
+
+        LogFlowFunc(("%s: pStream=%p, cStreams=%RU8\n",
+                     pSink->pszName, pMixStream, pSink->cStreams));
+
+        /* Increase the stream's reference count to let others know
+         * we're reyling on it to be around now. */
+        pStream->State.cRefs++;
+
+        if (ppStream)
+            *ppStream = pMixStream;
+
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    return rc;
+}
+
+int AudioMixerAddStreamOut(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConnector, PPDMAUDIOGSTSTRMOUT pStream,
+                           uint32_t uFlags, PAUDMIXSTREAM *ppStream)
+{
+    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
+    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
+    /** @todo Add flag validation. */
+    /* ppStream is optional. */
+
+    int rc;
+
+    if (pSink->cStreams == UINT8_MAX) /* 255 streams per sink max. */
+        return VERR_TOO_MUCH_DATA;
+
+    PAUDMIXSTREAM pMixStream
+        = (PAUDMIXSTREAM)RTMemAllocZ(sizeof(AUDMIXSTREAM));
+    if (pMixStream)
+    {
+        pMixStream->pConn = pConnector;
+        pMixStream->pOut  = pStream;
+        /** @todo Process flags. */
+
+        RTListAppend(&pSink->lstStreams, &pMixStream->Node);
+        pSink->cStreams++;
+
+        LogFlowFunc(("%s: pStream=%p, cStreams=%RU8\n",
+                     pSink->pszName, pMixStream, pSink->cStreams));
+
+        /* Increase the stream's reference count to let others know
+         * we're reyling on it to be around now. */
+        pStream->State.cRefs++;
+
+        if (ppStream)
+            *ppStream = pMixStream;
+
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    return rc;
+}
+
+int AudioMixerControlStream(PAUDIOMIXER pMixer, PAUDMIXSTREAM pHandle)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+int AudioMixerCreate(const char *pszName, uint32_t uFlags, PAUDIOMIXER *ppMixer)
+{
+    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+    /** @todo Add flag validation. */
+    AssertPtrReturn(ppMixer, VERR_INVALID_POINTER);
+
+    int rc = VINF_SUCCESS;
+
+    PAUDIOMIXER pMixer = (PAUDIOMIXER)RTMemAllocZ(sizeof(AUDIOMIXER));
+    if (pMixer)
+    {
+        pMixer->pszName = RTStrDup(pszName);
+        if (!pMixer->pszName)
+            rc = VERR_NO_MEMORY;
+
+        if (RT_SUCCESS(rc))
+        {
+            pMixer->cSinks = 0;
+            RTListInit(&pMixer->lstSinks);
+
+            pMixer->VolMaster.fMuted = false;
+            pMixer->VolMaster.uLeft  = UINT32_MAX;
+            pMixer->VolMaster.uRight = UINT32_MAX;
+
+            LogFlowFunc(("Created %p ...\n", pMixer));
+
+            *ppMixer = pMixer;
+        }
+        else
+            RTMemFree(pMixer);
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+void AudioMixerDestroy(PAUDIOMIXER pMixer)
+{
+    if (!pMixer)
+        return;
+
+    LogFlowFunc(("Destroying %s ...\n", pMixer->pszName));
+
+    PAUDMIXSINK pSink, pSinkNext;
+    RTListForEachSafe(&pMixer->lstSinks, pSink, pSinkNext, AUDMIXSINK, Node)
+        AudioMixerRemoveSink(pMixer, pSink);
+
+    Assert(pMixer->cSinks == 0);
+
+    if (pMixer->pszName)
+    {
+        RTStrFree(pMixer->pszName);
+        pMixer->pszName = NULL;
+    }
+
+    RTMemFree(pMixer);
+}
+
+static void audioMixerDestroySink(PAUDMIXSINK pSink)
+{
+    AssertPtrReturnVoid(pSink);
+    if (!pSink)
+        return;
+
+    if (pSink->pszName)
+        RTStrFree(pSink->pszName);
+
+    RTMemFree(pSink);
+}
+
+static void audioMixerDestroyStream(PAUDMIXSTREAM pStream)
+{
+    AssertPtrReturnVoid(pStream);
+    if (!pStream)
+        return;
+
+    RTMemFree(pStream);
+}
+
+int AudioMixerGetDeviceFormat(PAUDIOMIXER pMixer, PPDMAUDIOSTREAMCFG pCfg)
+{
+    AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    /** @todo Perform a deep copy, if needed. */
+    *pCfg = pMixer->devFmt;
+
+    return VINF_SUCCESS;
+}
+
+uint32_t AudioMixerGetStreamCount(PAUDIOMIXER pMixer)
+{
+    AssertPtrReturn(pMixer, 0);
+
+    uint32_t cStreams = 0;
+
+    PAUDMIXSINK pSink;
+    RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
+        cStreams += pSink->cStreams;
+
+    return cStreams;
+}
+
+void AudioMixerInvalidate(PAUDIOMIXER pMixer)
+{
+    AssertPtrReturnVoid(pMixer);
+
+    LogFlowFunc(("%s: Invalidating ...\n", pMixer->pszName));
+
+    /* Propagate new master volume to all connected sinks. */
+    PAUDMIXSINK pSink;
+    RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
+    {
+        int rc2 = audioMixerUpdateSinkVolume(pSink, &pMixer->VolMaster);
+        AssertRC(rc2);
+    }
+}
+
+int AudioMixerProcessSinkIn(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbProcessed)
+{
+    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
+    /* pcbProcessed is optional. */
+
+    /** @todo Handle mixing operation enmOp! */
+
+    uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbBuf);
+    if (!pvMixBuf)
+        return VERR_NO_MEMORY;
+
+    int rc = VERR_NOT_FOUND;
+    uint32_t cbProcessed = 0;
+
+    LogFlowFunc(("%s: pvBuf=%p, cbBuf=%zu\n", pSink->pszName, pvBuf, cbBuf));
+
+    PAUDMIXSTREAM pStream;
+    RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
+    {
+        /** @todo Support output sinks as well! */
+        if (!pStream->pConn->pfnIsActiveIn(pStream->pConn, pStream->pIn))
+            continue;
+
+        uint32_t cbTotalRead = 0;
+        uint32_t cbToRead = cbBuf;
+
+        while (cbToRead)
+        {
+            uint32_t cbRead;
+            AssertPtr(pStream->pConn);
+            rc = pStream->pConn->pfnRead(pStream->pConn, pStream->pIn,
+                                         (uint8_t *)pvMixBuf + cbTotalRead, cbToRead, &cbRead);
+            if (   RT_FAILURE(rc)
+                || !cbRead)
+                break;
+
+            AssertBreakStmt(cbRead <= cbToRead, rc = VERR_BUFFER_OVERFLOW);
+            cbToRead -= cbRead;
+            cbTotalRead += cbRead;
+        }
+
+        if (RT_FAILURE(rc))
+            continue;
+
+        cbProcessed = RT_MAX(cbProcessed, cbTotalRead);
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        memcpy(pvBuf, pvMixBuf, cbProcessed); /* @todo Use an intermediate mixing buffer per sink! */
+
+        if (pcbProcessed)
+            *pcbProcessed = cbProcessed;
+    }
+
+    RTMemFree(pvMixBuf);
+
+    LogFlowFunc(("cbProcessed=%RU32, rc=%Rrc\n", cbProcessed, rc));
+    return rc;
+}
+
+int AudioMixerProcessSinkOut(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbProcessed)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+void AudioMixerRemoveSink(PAUDIOMIXER pMixer, PAUDMIXSINK pSink)
+{
+    AssertPtrReturnVoid(pMixer);
+    if (!pSink)
+        return;
+
+    PAUDMIXSTREAM pStream, pStreamNext;
+    RTListForEachSafe(&pSink->lstStreams, pStream, pStreamNext, AUDMIXSTREAM, Node)
+        AudioMixerRemoveStream(pSink, pStream);
+
+    Assert(pSink->cStreams == 0);
+
+    RTListNodeRemove(&pSink->Node);
+    Assert(pMixer->cSinks);
+    pMixer->cSinks--;
+
+    LogFlowFunc(("%s: pSink=%s, cSinks=%RU8\n",
+                 pMixer->pszName, pSink->pszName, pMixer->cSinks));
+
+    audioMixerDestroySink(pSink);
+}
+
+void AudioMixerRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream)
+{
+    AssertPtrReturnVoid(pSink);
+    if (!pStream)
+        return;
+
+    Assert(pSink->cStreams);
+    RTListNodeRemove(&pStream->Node);
+    pSink->cStreams--;
+
+#ifdef DEBUG
+    const char *pszStream = pSink->enmDir == AUDMIXSINKDIR_INPUT
+                          ? pStream->pIn->MixBuf.pszName : pStream->pOut->MixBuf.pszName;
+
+    LogFlowFunc(("%s: pStream=%s, cStreams=%RU8\n",
+                 pSink->pszName, pszStream ? pszStream : "<Unnamed>", pSink->cStreams));
+#endif
+
+    /* Decrease the reference count again. */
+    switch (pSink->enmDir)
+    {
+        case AUDMIXSINKDIR_INPUT:
+        {
+            Assert(pStream->pIn->State.cRefs);
+            pStream->pIn->State.cRefs--;
+            break;
+        }
+
+        case AUDMIXSINKDIR_OUTPUT:
+        {
+            Assert(pStream->pOut->State.cRefs);
+            pStream->pOut->State.cRefs--;
+            break;
+        }
+
+        default:
+            AssertMsgFailed(("Not implemented\n"));
+            break;
+    }
+
+    audioMixerDestroyStream(pStream);
+}
+
+int AudioMixerSetDeviceFormat(PAUDIOMIXER pMixer, PPDMAUDIOSTREAMCFG pCfg)
+{
+    AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    /** @todo Perform a deep copy, if needed. */
+    pMixer->devFmt = *pCfg;
+
+    return VINF_SUCCESS;
+}
+
+static int audioMixerUpdateSinkVolume(PAUDMIXSINK pSink, const PPDMAUDIOVOLUME pVolMaster)
+{
+    AssertPtrReturn(pSink,      VERR_INVALID_POINTER);
+    AssertPtrReturn(pVolMaster, VERR_INVALID_POINTER);
+
+    LogFlowFunc(("Master fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
+                  pVolMaster->fMuted, pVolMaster->uLeft, pVolMaster->uRight));
+    LogFlowFunc(("%s: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
+                  pSink->pszName, pSink->Volume.fMuted, pSink->Volume.uLeft, pSink->Volume.uRight));
+
+    /** @todo Very crude implementation for now -- needs more work! */
+
+    PDMAUDIOVOLUME volSink;
+    volSink.fMuted  = pVolMaster->fMuted || pSink->Volume.fMuted;
+    volSink.uLeft   = (pSink->Volume.uLeft  * pVolMaster->uLeft)  / UINT8_MAX;
+    volSink.uRight  = (pSink->Volume.uRight * pVolMaster->uRight) / UINT8_MAX;
+
+    LogFlowFunc(("\t-> fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
+                  volSink.fMuted, volSink.uLeft, volSink.uRight));
+
+    bool fOut = pSink->enmDir == AUDMIXSINKDIR_OUTPUT;
+
+    /* Propagate new sink volume to all streams in the sink. */
+    PAUDMIXSTREAM pStream;
+    RTListForEach(&pSink->lstStreams, pStream, AUDMIXSTREAM, Node)
+    {
+        if (fOut)
+            AudioMixBufSetVolume(&pStream->pOut->MixBuf, &volSink);
+        else
+            AudioMixBufSetVolume(&pStream->pIn->MixBuf,  &volSink);
+    }
+
+    return VINF_SUCCESS;
+}
+
+/** Set the master volume of the mixer. */
+int AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol)
+{
+    AssertPtrReturn(pMixer, VERR_INVALID_POINTER);
+    AssertPtrReturn(pVol,   VERR_INVALID_POINTER);
+
+    pMixer->VolMaster = *pVol;
+
+    LogFlowFunc(("%s: lVol=%RU32, rVol=%RU32 => fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n",
+                 pMixer->pszName, pVol->uLeft, pVol->uRight,
+                 pMixer->VolMaster.fMuted, pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight));
+
+    AudioMixerInvalidate(pMixer);
+    return VINF_SUCCESS;
+}
+
+/** Set the volume of an individual sink. */
+int AudioMixerSetSinkVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol)
+{
+    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
+    AssertPtrReturn(pVol,  VERR_INVALID_POINTER);
+    AssertPtr(pSink->pParent);
+
+    LogFlowFunc(("%s: fMuted=%RTbool, lVol=%RU32, rVol=%RU32\n", pSink->pszName, pVol->fMuted, pVol->uLeft, pVol->uRight));
+
+    pSink->Volume = *pVol;
+
+    return audioMixerUpdateSinkVolume(pSink, &pSink->pParent->VolMaster);
+}
+
+void AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    PAUDMIXSINK pSink;
+    unsigned    iSink = 0;
+
+    pHlp->pfnPrintf(pHlp, "[Master] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", pMixer->pszName,
+                    pMixer->VolMaster.uLeft, pMixer->VolMaster.uRight, pMixer->VolMaster.fMuted);
+
+    RTListForEach(&pMixer->lstSinks, pSink, AUDMIXSINK, Node)
+    {
+        pHlp->pfnPrintf(pHlp, "[Sink %u] %s: lVol=%u, rVol=%u, fMuted=%RTbool\n", iSink, pSink->pszName,
+                        pSink->Volume.uLeft, pSink->Volume.uRight, pSink->Volume.fMuted);
+        ++iSink;
+    }
+}
Index: /trunk/src/VBox/Devices/Audio_old/AudioMixer.h
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/AudioMixer.h	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/AudioMixer.h	(revision 61413)
@@ -0,0 +1,113 @@
+/* $Id$ */
+/** @file
+ * VBox audio: Mixing routines, mainly used by the various audio device
+ *             emulations to achieve proper multiplexing from/to attached
+ *             devices LUNs.
+ */
+
+/*
+ * Copyright (C) 2014-2016 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.
+ */
+
+#ifndef AUDIO_MIXER_H
+#define AUDIO_MIXER_H
+
+#include <iprt/cdefs.h>
+#include <VBox/vmm/pdmaudioifs.h>
+
+typedef struct AUDIOMIXER
+{
+    /** Mixer name. */
+    char                   *pszName;
+    /** Format the mixer should convert/output
+     *  data to so that the underlying device emulation
+     *  can work with it. */
+    PDMAUDIOSTREAMCFG       devFmt;
+    /** The master volume of this mixer. */
+    PDMAUDIOVOLUME          VolMaster;
+    /* List of audio mixer sinks. */
+    RTLISTANCHOR            lstSinks;
+    /** Number of used audio sinks. */
+    uint8_t                 cSinks;
+} AUDIOMIXER, *PAUDIOMIXER;
+
+typedef struct AUDMIXSTREAM
+{
+    RTLISTNODE              Node;
+    PPDMIAUDIOCONNECTOR     pConn;
+    union
+    {
+        PPDMAUDIOGSTSTRMIN  pIn;
+        PPDMAUDIOGSTSTRMOUT pOut;
+    };
+} AUDMIXSTREAM, *PAUDMIXSTREAM;
+
+typedef enum AUDMIXSINKDIR
+{
+    AUDMIXSINKDIR_UNKNOWN = 0,
+    AUDMIXSINKDIR_INPUT,
+    AUDMIXSINKDIR_OUTPUT,
+    /** The usual 32-bit hack. */
+    AUDMIXSINKDIR_32BIT_HACK = 0x7fffffff
+} AUDMIXSINKDIR;
+
+typedef struct AUDMIXSINK
+{
+    RTLISTNODE              Node;
+    /** Name of this sink. */
+    char                   *pszName;
+    /** The sink direction, that is,
+     *  if this sink handles input or output. */
+    AUDMIXSINKDIR           enmDir;
+    /** Pointer to mixer object this sink is bound
+     *  to. */
+    PAUDIOMIXER             pParent;
+    /** Number of streams assigned. */
+    uint8_t                 cStreams;
+    /** List of assigned streams. */
+    RTLISTANCHOR            lstStreams;
+    /** This sink's mixing buffer. */
+    PDMAUDIOMIXBUF          MixBuf;
+    /** The volume of this sink. The volume always will
+     *  be combined with the mixer's master volume. */
+    PDMAUDIOVOLUME          Volume;
+} AUDMIXSINK, *PAUDMIXSINK;
+
+typedef enum AUDMIXOP
+{
+    AUDMIXOP_NONE = 0,
+    AUDMIXOP_COPY,
+    AUDMIXOP_BLEND,
+    /** The usual 32-bit hack. */
+    AUDMIXOP_32BIT_HACK = 0x7fffffff
+} AUDMIXOP;
+
+
+int AudioMixerAddSink(PAUDIOMIXER pMixer, const char *pszName, AUDMIXSINKDIR enmDir, PAUDMIXSINK *ppSink);
+int AudioMixerAddStreamIn(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConnector, PPDMAUDIOGSTSTRMIN pStream, uint32_t uFlags, PAUDMIXSTREAM *ppStream);
+int AudioMixerAddStreamOut(PAUDMIXSINK pSink, PPDMIAUDIOCONNECTOR pConnector, PPDMAUDIOGSTSTRMOUT pStream, uint32_t uFlags, PAUDMIXSTREAM *ppStream);
+int AudioMixerControlStream(AUDMIXSTREAM pHandle); /** @todo Implement me. */
+int AudioMixerCreate(const char *pszName, uint32_t uFlags, PAUDIOMIXER *ppMixer);
+void AudioMixerDestroy(PAUDIOMIXER pMixer);
+int AudioMixerGetDeviceFormat(PAUDIOMIXER pMixer, PPDMAUDIOSTREAMCFG pCfg);
+uint32_t AudioMixerGetStreamCount(PAUDIOMIXER pMixer);
+void AudioMixerInvalidate(PAUDIOMIXER pMixer);
+int AudioMixerProcessSinkIn(PAUDMIXSINK pSink, AUDMIXOP enmOp, void *pvBuf, uint32_t cbBuf, uint32_t *pcbProcessed);
+int AudioMixerProcessSinkOut(PAUDMIXSINK pSink, AUDMIXOP enmOp, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbProcessed);
+void AudioMixerRemoveSink(PAUDIOMIXER pMixer, PAUDMIXSINK pSink);
+void AudioMixerRemoveStream(PAUDMIXSINK pSink, PAUDMIXSTREAM pStream);
+int AudioMixerSetDeviceFormat(PAUDIOMIXER pMixer, PPDMAUDIOSTREAMCFG pCfg);
+int AudioMixerSetMasterVolume(PAUDIOMIXER pMixer, PPDMAUDIOVOLUME pVol);
+int AudioMixerSetSinkVolume(PAUDMIXSINK pSink, PPDMAUDIOVOLUME pVol);
+void AudioMixerDebug(PAUDIOMIXER pMixer, PCDBGFINFOHLP pHlp, const char *pszArgs);
+
+#endif /* AUDIO_MIXER_H */
+
Index: /trunk/src/VBox/Devices/Audio_old/DevIchAc97.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DevIchAc97.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DevIchAc97.cpp	(revision 61413)
@@ -0,0 +1,2747 @@
+/* $Id$ */
+/** @file
+ * DevIchAc97 - VBox ICH AC97 Audio Controller.
+ */
+
+/*
+ * Copyright (C) 2006-2016 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                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_AC97
+#include <VBox/log.h>
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdmaudioifs.h>
+
+#include <iprt/assert.h>
+#ifdef IN_RING3
+# include <iprt/mem.h>
+# include <iprt/string.h>
+# include <iprt/uuid.h>
+#endif
+
+#include "VBoxDD.h"
+
+#include "AudioMixBuffer.h"
+#include "AudioMixer.h"
+#include "DrvAudio.h"
+
+
+/*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+
+#ifdef DEBUG
+//#define DEBUG_LUN
+# ifdef DEBUG_LUN
+#  define DEBUG_LUN_NUM 1
+# endif
+#endif /* DEBUG */
+
+#define AC97_SSM_VERSION 1
+
+#ifdef VBOX
+# define SOFT_VOLUME /** @todo Get rid of this crap. */
+#else
+# define SOFT_VOLUME
+#endif
+
+#define SR_FIFOE RT_BIT(4)          /* rwc, FIFO error. */
+#define SR_BCIS  RT_BIT(3)          /* rwc, Buffer completion interrupt status. */
+#define SR_LVBCI RT_BIT(2)          /* rwc, Last valid buffer completion interrupt. */
+#define SR_CELV  RT_BIT(1)          /* ro,  Current equals last valid. */
+#define SR_DCH   RT_BIT(0)          /* ro,  Controller halted. */
+#define SR_VALID_MASK (RT_BIT(5) - 1)
+#define SR_WCLEAR_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
+#define SR_RO_MASK (SR_DCH | SR_CELV)
+#define SR_INT_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
+
+#define CR_IOCE  RT_BIT(4)         /* rw,   Interrupt On Completion Enable. */
+#define CR_FEIE  RT_BIT(3)         /* rw    FIFO Error Interrupt Enable. */
+#define CR_LVBIE RT_BIT(2)         /* rw    */
+#define CR_RR    RT_BIT(1)         /* rw */
+#define CR_RPBM  RT_BIT(0)         /* rw */
+#define CR_VALID_MASK (RT_BIT(5) - 1)
+#define CR_DONT_CLEAR_MASK (CR_IOCE | CR_FEIE | CR_LVBIE)
+
+#define GC_WR    4              /* rw */
+#define GC_CR    2              /* rw */
+#define GC_VALID_MASK (RT_BIT(6) - 1)
+
+#define GS_MD3   RT_BIT(17)        /* rw */
+#define GS_AD3   RT_BIT(16)        /* rw */
+#define GS_RCS   RT_BIT(15)        /* rwc */
+#define GS_B3S12 RT_BIT(14)        /* ro */
+#define GS_B2S12 RT_BIT(13)        /* ro */
+#define GS_B1S12 RT_BIT(12)        /* ro */
+#define GS_S1R1  RT_BIT(11)        /* rwc */
+#define GS_S0R1  RT_BIT(10)        /* rwc */
+#define GS_S1CR  RT_BIT(9)         /* ro */
+#define GS_S0CR  RT_BIT(8)         /* ro */
+#define GS_MINT  RT_BIT(7)         /* ro */
+#define GS_POINT RT_BIT(6)         /* ro */
+#define GS_PIINT RT_BIT(5)         /* ro */
+#define GS_RSRVD (RT_BIT(4)|RT_BIT(3))
+#define GS_MOINT RT_BIT(2)         /* ro */
+#define GS_MIINT RT_BIT(1)         /* ro */
+#define GS_GSCI  RT_BIT(0)         /* rwc */
+#define GS_RO_MASK (GS_B3S12 |                   \
+                    GS_B2S12 |                   \
+                    GS_B1S12 |                   \
+                    GS_S1CR |                    \
+                    GS_S0CR |                    \
+                    GS_MINT |                    \
+                    GS_POINT |                   \
+                    GS_PIINT |                   \
+                    GS_RSRVD |                   \
+                    GS_MOINT |                   \
+                    GS_MIINT)
+#define GS_VALID_MASK (RT_BIT(18) - 1)
+#define GS_WCLEAR_MASK (GS_RCS|GS_S1R1|GS_S0R1|GS_GSCI)
+
+/** @name Buffer Descriptor
+ * @{ */
+#define BD_IOC RT_BIT(31)          /**< Interrupt on Completion */
+#define BD_BUP RT_BIT(30)          /**< Buffer Underrun Policy */
+/** @} */
+
+#define EACS_VRA 1
+#define EACS_VRM 8
+
+#define VOL_MASK 0x1f
+#define MUTE_SHIFT 15
+
+#define REC_MASK 7
+enum
+{
+    REC_MIC = 0,
+    REC_CD,
+    REC_VIDEO,
+    REC_AUX,
+    REC_LINE_IN,
+    REC_STEREO_MIX,
+    REC_MONO_MIX,
+    REC_PHONE
+};
+
+enum
+{
+    AC97_Reset                     = 0x00,
+    AC97_Master_Volume_Mute        = 0x02,
+    AC97_Headphone_Volume_Mute     = 0x04, /** Also known as AUX, see table 16, section 5.7. */
+    AC97_Master_Volume_Mono_Mute   = 0x06,
+    AC97_Master_Tone_RL            = 0x08,
+    AC97_PC_BEEP_Volume_Mute       = 0x0A,
+    AC97_Phone_Volume_Mute         = 0x0C,
+    AC97_Mic_Volume_Mute           = 0x0E,
+    AC97_Line_In_Volume_Mute       = 0x10,
+    AC97_CD_Volume_Mute            = 0x12,
+    AC97_Video_Volume_Mute         = 0x14,
+    AC97_Aux_Volume_Mute           = 0x16,
+    AC97_PCM_Out_Volume_Mute       = 0x18,
+    AC97_Record_Select             = 0x1A,
+    AC97_Record_Gain_Mute          = 0x1C,
+    AC97_Record_Gain_Mic_Mute      = 0x1E,
+    AC97_General_Purpose           = 0x20,
+    AC97_3D_Control                = 0x22,
+    AC97_AC_97_RESERVED            = 0x24,
+    AC97_Powerdown_Ctrl_Stat       = 0x26,
+    AC97_Extended_Audio_ID         = 0x28,
+    AC97_Extended_Audio_Ctrl_Stat  = 0x2A,
+    AC97_PCM_Front_DAC_Rate        = 0x2C,
+    AC97_PCM_Surround_DAC_Rate     = 0x2E,
+    AC97_PCM_LFE_DAC_Rate          = 0x30,
+    AC97_PCM_LR_ADC_Rate           = 0x32,
+    AC97_MIC_ADC_Rate              = 0x34,
+    AC97_6Ch_Vol_C_LFE_Mute        = 0x36,
+    AC97_6Ch_Vol_L_R_Surround_Mute = 0x38,
+    AC97_Vendor_Reserved           = 0x58,
+    AC97_AD_Misc                   = 0x76,
+    AC97_Vendor_ID1                = 0x7c,
+    AC97_Vendor_ID2                = 0x7e
+};
+
+/* Codec models. */
+enum {
+    Codec_STAC9700 = 0,     /* SigmaTel STAC9700 */
+    Codec_AD1980,           /* Analog Devices AD1980 */
+    Codec_AD1981B           /* Analog Devices AD1981B */
+};
+
+/* Analog Devices miscellaneous regiter bits used in AD1980. */
+#define AD_MISC_LOSEL       RT_BIT(5)   /* Surround (rear) goes to line out outputs. */
+#define AD_MISC_HPSEL       RT_BIT(10)  /* PCM (front) goes to headphone outputs. */
+
+#define ICHAC97STATE_2_DEVINS(a_pAC97)   ((a_pAC97)->pDevInsR3)
+
+enum
+{
+    BUP_SET  = RT_BIT(0),
+    BUP_LAST = RT_BIT(1)
+};
+
+/** Emits registers for a specific (Native Audio Bus Master BAR) NABMBAR. */
+#define AC97_NABMBAR_REGS(prefix, off) \
+    enum {                             \
+        prefix ## _BDBAR = off,        \
+        prefix ## _CIV   = off + 4,    \
+        prefix ## _LVI   = off + 5,    \
+        prefix ## _SR    = off + 6,    \
+        prefix ## _PICB  = off + 8,    \
+        prefix ## _PIV   = off + 10,   \
+        prefix ## _CR    = off + 11    \
+    }
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+typedef enum
+{
+    PI_INDEX = 0, /** PCM in */
+    PO_INDEX,     /** PCM out */
+    MC_INDEX,     /** Mic in */
+    LAST_INDEX
+} AC97SOUNDSOURCE;
+
+AC97_NABMBAR_REGS(PI, PI_INDEX * 16);
+AC97_NABMBAR_REGS(PO, PO_INDEX * 16);
+AC97_NABMBAR_REGS(MC, MC_INDEX * 16);
+#endif
+
+enum
+{
+    /** NABMBAR: Global Control Register. */
+    GLOB_CNT = 0x2c,
+    /** NABMBAR Global Status. */
+    GLOB_STA = 0x30,
+    /** Codec Access Semaphore Register. */
+    CAS      = 0x34
+};
+
+#define AC97_PORT2IDX(a_idx)   ( ((a_idx) >> 4) & 3 )
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+
+/**
+ * Buffer Descriptor List Entry (BDLE).
+ */
+typedef struct AC97BDLE
+{
+    uint32_t addr;
+    uint32_t ctl_len;
+} AC97BDLE, *PAC97BDLE;
+
+/**
+ * Bus master register set for an audio stream.
+ */
+typedef struct AC97BMREGS
+{
+    uint32_t bdbar;             /** rw 0, Buffer Descriptor List: BAR (Base Address Register). */
+    uint8_t  civ;               /** ro 0, Current index value. */
+    uint8_t  lvi;               /** rw 0, Last valid index. */
+    uint16_t sr;                /** rw 1, Status register. */
+    uint16_t picb;              /** ro 0, Position in current buffer. */
+    uint8_t  piv;               /** ro 0, Prefetched index value. */
+    uint8_t  cr;                /** rw 0, Control register. */
+    int      bd_valid;          /** Whether current BDLE is initialized or not. */
+    AC97BDLE bd;                /** Current Buffer Descriptor List Entry (BDLE). */
+} AC97BMREGS, *PAC97BMREGS;
+
+/**
+ * Internal state of an AC97 stream.
+ */
+typedef struct AC97STREAMSTATE
+{
+    /* Nothing yet. */
+} AC97STREAMSTATE, *PAC97STREAMSTATE;
+
+/**
+ * Structure for keeping an AC97 stream state.
+ *
+ * Contains only register values which do *not* change until a
+ * stream reset occurs.
+ */
+typedef struct AC97STREAM
+{
+    /** Stream number (SDn). */
+    uint8_t         u8Strm;
+    /** Bus master registers of this stream. */
+    AC97BMREGS      Regs;
+    /** Internal state of this stream. */
+    AC97STREAMSTATE State;
+} AC97STREAM, *PAC97STREAM;
+
+typedef struct AC97INPUTSTREAM
+{
+    /** PCM line input stream. */
+    R3PTRTYPE(PPDMAUDIOGSTSTRMIN)      pStrmIn;
+    /** Mixer handle for line input stream. */
+    R3PTRTYPE(PAUDMIXSTREAM)           phStrmIn;
+} AC97INPUTSTREAM, *PAC97INPUTSTREAM;
+
+typedef struct AC97OUTPUTSTREAM
+{
+    /** PCM output stream. */
+    R3PTRTYPE(PPDMAUDIOGSTSTRMOUT)     pStrmOut;
+    /** Mixer handle for output stream. */
+    R3PTRTYPE(PAUDMIXSTREAM)           phStrmOut;
+} AC97OUTPUTSTREAM, *PAC97OUTPUTSTREAM;
+
+/**
+ * Struct for maintaining a host backend driver.
+ */
+typedef struct AC97STATE *PAC97STATE;
+typedef struct AC97DRIVER
+{
+    /** Node for storing this driver in our device driver list of AC97STATE. */
+    RTLISTNODER3                       Node;
+    /** Pointer to AC97 controller (state). */
+    R3PTRTYPE(PAC97STATE)              pAC97State;
+    /** Driver flags. */
+    PDMAUDIODRVFLAGS                   Flags;
+    uint32_t                           PaddingFlags;
+    /** LUN # to which this driver has been assigned. */
+    uint8_t                            uLUN;
+    /** Whether this driver is in an attached state or not. */
+    bool                               fAttached;
+    uint8_t                            Padding[4];
+    /** Pointer to attached driver base interface. */
+    R3PTRTYPE(PPDMIBASE)               pDrvBase;
+    /** Audio connector interface to the underlying host backend. */
+    R3PTRTYPE(PPDMIAUDIOCONNECTOR)     pConnector;
+    /** Stream for line input. */
+    AC97INPUTSTREAM                    LineIn;
+    /** Stream for mic input. */
+    AC97INPUTSTREAM                    MicIn;
+    /** Stream for output. */
+    AC97OUTPUTSTREAM                   Out;
+} AC97DRIVER, *PAC97DRIVER;
+
+typedef struct AC97STATE
+{
+    /** The PCI device state. */
+    PCIDevice               PciDev;
+    /** R3 Pointer to the device instance. */
+    PPDMDEVINSR3            pDevInsR3;
+    /** Global Control (Bus Master Control Register) */
+    uint32_t                glob_cnt;
+    /** Global Status (Bus Master Control Register) */
+    uint32_t                glob_sta;
+    /** Codec Access Semaphore Register (Bus Master Control Register) */
+    uint32_t                cas;
+    uint32_t                last_samp;
+    uint8_t                 mixer_data[256];
+    /** Stream state for line-in. */
+    AC97STREAM              StrmStLineIn;
+    /** Stream state for microphone-in. */
+    AC97STREAM              StrmStMicIn;
+    /** Stream state for output. */
+    AC97STREAM              StrmStOut;
+#ifndef VBOX_WITH_AUDIO_CALLBACKS
+    /** The timer for pumping data thru the attached LUN drivers. */
+    PTMTIMERR3              pTimer;
+    /** The timer interval for pumping data thru the LUN drivers in timer ticks. */
+    uint64_t                cTimerTicks;
+    /** Timestamp of the last timer callback (ac97Timer).
+     * Used to calculate the time actually elapsed between two timer callbacks. */
+    uint64_t                uTimerTS;
+#endif
+#ifdef VBOX_WITH_STATISTICS
+    STAMPROFILE             StatTimer;
+    STAMCOUNTER             StatBytesRead;
+    STAMCOUNTER             StatBytesWritten;
+#endif
+    /** List of associated LUN drivers (AC97DRIVER). */
+    RTLISTANCHOR            lstDrv;
+    /** The device' software mixer. */
+    R3PTRTYPE(PAUDIOMIXER)  pMixer;
+    /** Audio sink for PCM output. */
+    R3PTRTYPE(PAUDMIXSINK)  pSinkOutput;
+    /** Audio sink for line input. */
+    R3PTRTYPE(PAUDMIXSINK)  pSinkLineIn;
+    /** Audio sink for microphone input. */
+    R3PTRTYPE(PAUDMIXSINK)  pSinkMicIn;
+    uint8_t                 silence[128];
+    int                     bup_flag;
+    /** The base interface for LUN\#0. */
+    PDMIBASE                IBase;
+    /** Base port of the I/O space region. */
+    RTIOPORT                IOPortBase[2];
+    /** Pointer to temporary scratch read/write buffer. */
+    R3PTRTYPE(uint8_t *)    pvReadWriteBuf;
+    /** Size of the temporary scratch read/write buffer. */
+    uint32_t                cbReadWriteBuf;
+    /** Codec model. */
+    uint32_t                uCodecModel;
+} AC97STATE, *PAC97STATE;
+
+#ifdef VBOX_WITH_STATISTICS
+AssertCompileMemberAlignment(AC97STATE, StatTimer, 8);
+#endif
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+DECLINLINE(PAC97STREAM) ichac97GetStreamFromID(PAC97STATE pThis, uint32_t uID);
+static DECLCALLBACK(void) ichac97Reset(PPDMDEVINS pDevIns);
+#ifndef VBOX_WITH_AUDIO_CALLBACKS
+static DECLCALLBACK(void) ichac97Timer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
+#endif
+static int ichac97TransferAudio(PAC97STATE pThis, AC97SOUNDSOURCE enmSrc, uint32_t cbElapsed);
+
+static void ichac97WarmReset(PAC97STATE pThis)
+{
+    NOREF(pThis);
+}
+
+static void ichac97ColdReset(PAC97STATE pThis)
+{
+    NOREF(pThis);
+}
+
+/** Fetches the buffer descriptor at _CIV. */
+static void ichac97StreamFetchBDLE(PAC97STATE pThis, PAC97STREAM pStrmSt)
+{
+    PPDMDEVINS  pDevIns = ICHAC97STATE_2_DEVINS(pThis);
+    PAC97BMREGS pRegs   = &pStrmSt->Regs;
+
+    uint32_t u32[2];
+
+    PDMDevHlpPhysRead(pDevIns, pRegs->bdbar + pRegs->civ * 8, &u32[0], sizeof(u32));
+    pRegs->bd_valid   = 1;
+#if !defined(RT_ARCH_X86) && !defined(RT_ARCH_AMD64)
+# error Please adapt the code (audio buffers are little endian)!
+#else
+    pRegs->bd.addr    = RT_H2LE_U32(u32[0] & ~3);
+    pRegs->bd.ctl_len = RT_H2LE_U32(u32[1]);
+#endif
+    pRegs->picb       = pRegs->bd.ctl_len & 0xffff;
+    LogFlowFunc(("bd %2d addr=%#x ctl=%#06x len=%#x(%d bytes)\n",
+                  pRegs->civ, pRegs->bd.addr, pRegs->bd.ctl_len >> 16,
+                  pRegs->bd.ctl_len & 0xffff, (pRegs->bd.ctl_len & 0xffff) << 1));
+}
+
+/**
+ * Update the BM status register
+ */
+static void ichac97StreamUpdateStatus(PAC97STATE pThis, PAC97STREAM pStrmSt, uint32_t new_sr)
+{
+    PPDMDEVINS  pDevIns = ICHAC97STATE_2_DEVINS(pThis);
+    PAC97BMREGS pRegs   = &pStrmSt->Regs;
+
+    bool fSignal   = false;
+    bool iIrqLevel;
+
+    uint32_t new_mask = new_sr & SR_INT_MASK;
+    uint32_t old_mask = pRegs->sr  & SR_INT_MASK;
+
+    static uint32_t const masks[] = { GS_PIINT, GS_POINT, GS_MINT };
+
+    if (new_mask ^ old_mask)
+    {
+        /** @todo Is IRQ deasserted when only one of status bits is cleared? */
+        if (!new_mask)
+        {
+            fSignal   = true;
+            iIrqLevel = 0;
+        }
+        else if ((new_mask & SR_LVBCI) && (pRegs->cr & CR_LVBIE))
+        {
+            fSignal   = true;
+            iIrqLevel = 1;
+        }
+        else if ((new_mask & SR_BCIS) && (pRegs->cr & CR_IOCE))
+        {
+            fSignal   = true;
+            iIrqLevel = 1;
+        }
+    }
+
+    pRegs->sr = new_sr;
+
+    LogFlowFunc(("IOC%d, LVB%d, sr=%#x, fSignal=%RTbool, iIrqLevel=%d\n",
+                 pRegs->sr & SR_BCIS, pRegs->sr & SR_LVBCI, pRegs->sr, fSignal, iIrqLevel));
+
+    if (fSignal)
+    {
+        if (iIrqLevel)
+            pThis->glob_sta |=  masks[pStrmSt->u8Strm];
+        else
+            pThis->glob_sta &= ~masks[pStrmSt->u8Strm];
+
+        LogFlowFunc(("set irq level=%d\n", !!iIrqLevel));
+        PDMDevHlpPCISetIrq(pDevIns, 0, !!iIrqLevel);
+    }
+}
+
+static int ichac97StreamSetActive(PAC97STATE pThis, PAC97STREAM pStrmSt, bool fActive)
+{
+    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
+    AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
+
+    LogFlowFunc(("u8Strm=%RU8, fActive=%RTbool\n", pStrmSt->u8Strm, fActive));
+
+    int rc = VINF_SUCCESS;
+
+    PAC97DRIVER pDrv;
+    switch (pStrmSt->u8Strm)
+    {
+        case PI_INDEX:
+            RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
+            {
+                int rc2 = pDrv->pConnector->pfnEnableIn(pDrv->pConnector,
+                                                        pDrv->LineIn.pStrmIn, fActive);
+                if (RT_SUCCESS(rc))
+                    rc = rc2;
+            }
+            break;
+
+        case PO_INDEX:
+            RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
+            {
+                int rc2 = pDrv->pConnector->pfnEnableOut(pDrv->pConnector,
+                                                         pDrv->Out.pStrmOut, fActive);
+                if (RT_SUCCESS(rc))
+                    rc = rc2;
+            }
+            break;
+
+        case MC_INDEX:
+            RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
+            {
+                int rc2 = pDrv->pConnector->pfnEnableIn(pDrv->pConnector,
+                                                        pDrv->MicIn.pStrmIn, fActive);
+                if (RT_SUCCESS(rc))
+                    rc = rc2;
+            }
+            break;
+
+        default:
+            AssertMsgFailed(("Wrong index %RU32\n", pStrmSt->u8Strm));
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+
+    return rc;
+}
+
+static void ichac97StreamResetBMRegs(PAC97STATE pThis, PAC97STREAM pStrmSt)
+{
+    AssertPtrReturnVoid(pThis);
+    AssertPtrReturnVoid(pStrmSt);
+
+    LogFlowFuncEnter();
+
+    PAC97BMREGS pRegs = &pStrmSt->Regs;
+
+    pRegs->bdbar    = 0;
+    pRegs->civ      = 0;
+    pRegs->lvi      = 0;
+
+    ichac97StreamUpdateStatus(pThis, pStrmSt, SR_DCH); /** @todo Do we need to do that? */
+
+    pRegs->picb     = 0;
+    pRegs->piv      = 0;
+    pRegs->cr       = pRegs->cr & CR_DONT_CLEAR_MASK;
+    pRegs->bd_valid = 0;
+
+    int rc = ichac97StreamSetActive(pThis, pStrmSt, false /* fActive */);
+    AssertRC(rc);
+
+    RT_ZERO(pThis->silence);
+}
+
+static void ichac97MixerSet(PAC97STATE pThis, uint32_t u8Idx, uint16_t v)
+{
+    if (u8Idx + 2 > sizeof(pThis->mixer_data))
+    {
+        AssertMsgFailed(("Index %RU8 out of bounds(%zu)\n", u8Idx, sizeof(pThis->mixer_data)));
+        return;
+    }
+
+    pThis->mixer_data[u8Idx + 0] = RT_LO_U8(v);
+    pThis->mixer_data[u8Idx + 1] = RT_HI_U8(v);
+}
+
+static uint16_t ichac97MixerGet(PAC97STATE pThis, uint32_t u8Idx)
+{
+    uint16_t uVal;
+
+    if (u8Idx + 2 > sizeof(pThis->mixer_data))
+    {
+        AssertMsgFailed(("Index %RU8 out of bounds (%zu)\n", u8Idx, sizeof(pThis->mixer_data)));
+        uVal = UINT16_MAX;
+    }
+    else
+        uVal = RT_MAKE_U16(pThis->mixer_data[u8Idx + 0], pThis->mixer_data[u8Idx + 1]);
+
+    return uVal;
+}
+
+static DECLCALLBACK(void) ichac97CloseIn(PAC97STATE pThis, PDMAUDIORECSOURCE enmRecSource)
+{
+    NOREF(pThis);
+    NOREF(enmRecSource);
+    LogFlowFuncEnter();
+}
+
+static DECLCALLBACK(void) ichac97CloseOut(PAC97STATE pThis)
+{
+    NOREF(pThis);
+    LogFlowFuncEnter();
+}
+
+static int ichac97OpenIn(PAC97STATE pThis,
+                         const char *pszName, PDMAUDIORECSOURCE enmRecSource,
+                         PPDMAUDIOSTREAMCFG pCfg)
+{
+    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg,    VERR_INVALID_POINTER);
+
+    PAUDMIXSINK pSink;
+    switch (enmRecSource)
+    {
+        case PDMAUDIORECSOURCE_MIC:
+            pSink = pThis->pSinkMicIn;
+            break;
+        case PDMAUDIORECSOURCE_LINE_IN:
+            pSink = pThis->pSinkLineIn;
+            break;
+        default:
+            AssertMsgFailed(("Audio source %ld not supported\n", enmRecSource));
+            return VERR_NOT_SUPPORTED;
+    }
+
+    int rc = VINF_SUCCESS;
+
+    PAC97DRIVER pDrv;
+    RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
+    {
+        char *pszDesc;
+        if (RTStrAPrintf(&pszDesc, "[LUN#%RU8] %s", pDrv->uLUN, pszName) <= 0)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        PAC97INPUTSTREAM pStrmIn;
+        if (enmRecSource == PDMAUDIORECSOURCE_MIC) /** @todo Refine this once we have more streams. */
+            pStrmIn = &pDrv->MicIn;
+        else
+            pStrmIn = &pDrv->LineIn;
+
+        rc = pDrv->pConnector->pfnCreateIn(pDrv->pConnector, pszDesc, enmRecSource, pCfg, &pStrmIn->pStrmIn);
+
+        LogFlowFunc(("LUN#%RU8: Created input \"%s\", with rc=%Rrc\n", pDrv->uLUN, pszDesc, rc));
+        if (rc == VINF_SUCCESS) /* Note: Could return VWRN_ALREADY_EXISTS. */
+        {
+            AudioMixerRemoveStream(pSink, pStrmIn->phStrmIn);
+            rc = AudioMixerAddStreamIn(pSink,
+                                       pDrv->pConnector, pStrmIn->pStrmIn,
+                                       0 /* uFlags */, &pStrmIn->phStrmIn);
+        }
+
+        RTStrFree(pszDesc);
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static int ichac97OpenOut(PAC97STATE pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg)
+{
+    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg,    VERR_INVALID_POINTER);
+
+    int rc = VINF_SUCCESS;
+    char *pszDesc;
+
+    PAC97DRIVER pDrv;
+    RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
+    {
+        if (RTStrAPrintf(&pszDesc, "[LUN#%RU8] %s (%RU32Hz, %RU8 %s)",
+                         pDrv->uLUN, pszName, pCfg->uHz, pCfg->cChannels, pCfg->cChannels > 1 ? "Channels" : "Channel") <= 0)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        rc = pDrv->pConnector->pfnCreateOut(pDrv->pConnector, pszDesc, pCfg, &pDrv->Out.pStrmOut);
+        LogFlowFunc(("LUN#%RU8: Created output \"%s\", with rc=%Rrc\n", pDrv->uLUN, pszDesc, rc));
+        if (rc == VINF_SUCCESS) /* Note: Could return VWRN_ALREADY_EXISTS. */
+        {
+            AudioMixerRemoveStream(pThis->pSinkOutput, pDrv->Out.phStrmOut);
+            rc = AudioMixerAddStreamOut(pThis->pSinkOutput,
+                                        pDrv->pConnector, pDrv->Out.pStrmOut,
+                                        0 /* uFlags */, &pDrv->Out.phStrmOut);
+        }
+
+        RTStrFree(pszDesc);
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static int ichac97StreamInitEx(PAC97STATE pThis, PAC97STREAM pStrmSt, uint8_t u8Strm, PPDMAUDIOSTREAMCFG pCfg)
+{
+    AssertPtrReturn(pThis,             VERR_INVALID_POINTER);
+    AssertPtrReturn(pStrmSt,           VERR_INVALID_POINTER);
+    AssertReturn(u8Strm <= LAST_INDEX, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pCfg,              VERR_INVALID_POINTER);
+
+    pStrmSt->u8Strm = u8Strm;
+
+    LogFlowFunc(("u8Strm=%RU8, %RU32Hz, %RU8 %s\n",
+                 pStrmSt->u8Strm, pCfg->uHz, pCfg->cChannels, pCfg->cChannels > 1 ? "Channels" : "Channel"));
+
+    int rc;
+    switch (pStrmSt->u8Strm)
+    {
+        case PI_INDEX:
+            rc = ichac97OpenIn(pThis, "ac97.pi", PDMAUDIORECSOURCE_LINE_IN, pCfg);
+            break;
+
+        case MC_INDEX:
+            rc = ichac97OpenIn(pThis, "ac97.mc", PDMAUDIORECSOURCE_MIC, pCfg);
+            break;
+
+        case PO_INDEX:
+            rc = ichac97OpenOut(pThis, "ac97.po", pCfg);
+            break;
+
+        default:
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static int ichac97StreamInit(PAC97STATE pThis, PAC97STREAM pStrmSt, uint8_t u8Strm)
+{
+    int rc = VINF_SUCCESS;
+
+    PDMAUDIOSTREAMCFG streamCfg;
+    RT_ZERO(streamCfg);
+
+    switch (u8Strm)
+    {
+        case PI_INDEX:
+            streamCfg.uHz = ichac97MixerGet(pThis, AC97_PCM_LR_ADC_Rate);
+            break;
+
+        case MC_INDEX:
+            streamCfg.uHz = ichac97MixerGet(pThis, AC97_MIC_ADC_Rate);
+            break;
+
+        case PO_INDEX:
+            streamCfg.uHz = ichac97MixerGet(pThis, AC97_PCM_Front_DAC_Rate);
+            break;
+
+        default:
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+
+    if (RT_FAILURE(rc))
+        return rc;
+
+    if (streamCfg.uHz)
+    {
+        streamCfg.cChannels     = 2;
+        streamCfg.enmFormat     = AUD_FMT_S16;
+        streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
+
+        return ichac97StreamInitEx(pThis, pStrmSt, u8Strm, &streamCfg);
+    }
+
+    /* If no frequency is given, disable the stream. */
+    return ichac97StreamSetActive(pThis, pStrmSt, false /* fActive */);
+}
+
+static int ichac97StreamReInit(PAC97STATE pThis, PAC97STREAM pStrmSt)
+{
+    return ichac97StreamInit(pThis, pStrmSt, pStrmSt->u8Strm);
+}
+
+static void ichac97StreamReset(PAC97STATE pThis, PAC97STREAM pStrmSt)
+{
+    AssertPtrReturnVoid(pThis);
+    AssertPtrReturnVoid(pStrmSt);
+
+    LogFlowFunc(("uStrm=%RU8\n", pStrmSt->u8Strm));
+}
+
+static int ichac97MixerSetVolume(PAC97STATE pThis, int index, PDMAUDIOMIXERCTL mt, uint32_t val)
+{
+    int mute = (val >> MUTE_SHIFT) & 1;
+    uint8_t rvol = val & VOL_MASK;
+    uint8_t lvol = (val >> 8) & VOL_MASK;
+
+    /* For the master volume, 0 corresponds to 0dB gain. But for the other
+     * volume controls, 0 corresponds to +12dB and 8 to 0dB. */
+    if (mt != PDMAUDIOMIXERCTL_VOLUME)
+    {
+        /* NB: Currently there is no gain support, only attenuation. */
+        lvol = lvol < 8 ? 0 : lvol - 8;
+        rvol = rvol < 8 ? 0 : rvol - 8;
+    }
+
+    /* AC'97 has 1.5dB steps; we use 0.375dB steps. */
+    rvol = 255 - rvol * 4;
+    lvol = 255 - lvol * 4;
+
+    LogFunc(("mt=%ld, val=%RX32, mute=%RTbool\n", mt, val, RT_BOOL(mute)));
+
+    int rc;
+
+#ifdef SOFT_VOLUME
+    if (pThis->pMixer) /* Device can be in reset state, so no mixer available. */
+    {
+        PDMAUDIOVOLUME vol = { RT_BOOL(mute), lvol, rvol };
+        switch (mt)
+        {
+            case PDMAUDIOMIXERCTL_VOLUME:
+                rc = AudioMixerSetMasterVolume(pThis->pMixer, &vol);
+                break;
+
+            case PDMAUDIOMIXERCTL_PCM:
+                rc = AudioMixerSetSinkVolume(pThis->pSinkOutput, &vol);
+                break;
+
+            case PDMAUDIOMIXERCTL_MIC_IN:
+                rc = AudioMixerSetSinkVolume(pThis->pSinkMicIn, &vol);
+                break;
+
+            case PDMAUDIOMIXERCTL_LINE_IN:
+                rc = AudioMixerSetSinkVolume(pThis->pSinkLineIn, &vol);
+                break;
+
+            default:
+                rc = VERR_NOT_SUPPORTED;
+                break;
+        }
+    }
+    else
+        rc = VERR_NOT_SUPPORTED;
+
+    if (RT_FAILURE(rc))
+        return rc;
+#else
+    rc = VINF_SUCCESS;
+#endif /* SOFT_VOLUME */
+
+    rvol = VOL_MASK - ((VOL_MASK * rvol) / 255);
+    lvol = VOL_MASK - ((VOL_MASK * lvol) / 255);
+
+    /*
+     * From AC'97 SoundMax Codec AD1981A: "Because AC '97 defines 6-bit volume registers, to
+     * maintain compatibility whenever the D5 or D13 bits are set to `1,' their respective
+     * lower five volume bits are automatically set to `1' by the Codec logic. On readback,
+     * all lower 5 bits will read ones whenever these bits are set to `1.'"
+     *
+     *  Linux ALSA depends on this behavior.
+     */
+    if (val & RT_BIT(5))
+        val |= RT_BIT(4) | RT_BIT(3) | RT_BIT(2) | RT_BIT(1) | RT_BIT(0);
+    if (val & RT_BIT(13))
+        val |= RT_BIT(12) | RT_BIT(11) | RT_BIT(10) | RT_BIT(9) | RT_BIT(8);
+
+    ichac97MixerSet(pThis, index, val);
+
+    return rc;
+}
+
+static PDMAUDIORECSOURCE ichac97IndextoRecSource(uint8_t i)
+{
+    switch (i)
+    {
+        case REC_MIC:     return PDMAUDIORECSOURCE_MIC;
+        case REC_CD:      return PDMAUDIORECSOURCE_CD;
+        case REC_VIDEO:   return PDMAUDIORECSOURCE_VIDEO;
+        case REC_AUX:     return PDMAUDIORECSOURCE_AUX;
+        case REC_LINE_IN: return PDMAUDIORECSOURCE_LINE_IN;
+        case REC_PHONE:   return PDMAUDIORECSOURCE_PHONE;
+        default:
+            break;
+    }
+
+    LogFlowFunc(("Unknown record source %d, using MIC\n", i));
+    return PDMAUDIORECSOURCE_MIC;
+}
+
+static uint8_t ichac97RecSourceToIndex(PDMAUDIORECSOURCE rs)
+{
+    switch (rs)
+    {
+        case PDMAUDIORECSOURCE_MIC:     return REC_MIC;
+        case PDMAUDIORECSOURCE_CD:      return REC_CD;
+        case PDMAUDIORECSOURCE_VIDEO:   return REC_VIDEO;
+        case PDMAUDIORECSOURCE_AUX:     return REC_AUX;
+        case PDMAUDIORECSOURCE_LINE_IN: return REC_LINE_IN;
+        case PDMAUDIORECSOURCE_PHONE:   return REC_PHONE;
+        default:
+            break;
+    }
+
+    LogFlowFunc(("Unknown audio recording source %d using MIC\n", rs));
+    return REC_MIC;
+}
+
+static void ichac97RecordSelect(PAC97STATE pThis, uint32_t val)
+{
+    uint8_t rs = val & REC_MASK;
+    uint8_t ls = (val >> 8) & REC_MASK;
+    PDMAUDIORECSOURCE ars = ichac97IndextoRecSource(rs);
+    PDMAUDIORECSOURCE als = ichac97IndextoRecSource(ls);
+    //AUD_set_record_source(&als, &ars);
+    rs = ichac97RecSourceToIndex(ars);
+    ls = ichac97RecSourceToIndex(als);
+    ichac97MixerSet(pThis, AC97_Record_Select, rs | (ls << 8));
+}
+
+static int ichac97MixerReset(PAC97STATE pThis)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+
+    LogFlowFuncEnter();
+
+    RT_ZERO(pThis->mixer_data);
+
+    /* Note: Make sure to reset all registers first before bailing out on error. */
+
+    ichac97MixerSet(pThis, AC97_Reset                   , 0x0000); /* 6940 */
+    ichac97MixerSet(pThis, AC97_Master_Volume_Mono_Mute , 0x8000);
+    ichac97MixerSet(pThis, AC97_PC_BEEP_Volume_Mute     , 0x0000);
+
+    ichac97MixerSet(pThis, AC97_Phone_Volume_Mute       , 0x8008);
+    ichac97MixerSet(pThis, AC97_Mic_Volume_Mute         , 0x8008);
+    ichac97MixerSet(pThis, AC97_CD_Volume_Mute          , 0x8808);
+    ichac97MixerSet(pThis, AC97_Aux_Volume_Mute         , 0x8808);
+    ichac97MixerSet(pThis, AC97_Record_Gain_Mic_Mute    , 0x8000);
+    ichac97MixerSet(pThis, AC97_General_Purpose         , 0x0000);
+    ichac97MixerSet(pThis, AC97_3D_Control              , 0x0000);
+    ichac97MixerSet(pThis, AC97_Powerdown_Ctrl_Stat     , 0x000f);
+
+    ichac97MixerSet(pThis, AC97_Extended_Audio_ID       , 0x0809);
+    ichac97MixerSet(pThis, AC97_Extended_Audio_Ctrl_Stat, 0x0009);
+    ichac97MixerSet(pThis, AC97_PCM_Front_DAC_Rate      , 0xbb80);
+    ichac97MixerSet(pThis, AC97_PCM_Surround_DAC_Rate   , 0xbb80);
+    ichac97MixerSet(pThis, AC97_PCM_LFE_DAC_Rate        , 0xbb80);
+    ichac97MixerSet(pThis, AC97_PCM_LR_ADC_Rate         , 0xbb80);
+    ichac97MixerSet(pThis, AC97_MIC_ADC_Rate            , 0xbb80);
+
+    if (pThis->uCodecModel == Codec_AD1980)
+    {
+        /* Analog Devices 1980 (AD1980) */
+        ichac97MixerSet(pThis, AC97_Reset                   , 0x0010);    /* Headphones. */
+        ichac97MixerSet(pThis, AC97_Vendor_ID1              , 0x4144);
+        ichac97MixerSet(pThis, AC97_Vendor_ID2              , 0x5370);
+        ichac97MixerSet(pThis, AC97_Headphone_Volume_Mute   , 0x8000);
+    }
+    else if (pThis->uCodecModel == Codec_AD1981B)
+    {
+        /* Analog Devices 1981B (AD1981B) */
+        ichac97MixerSet(pThis, AC97_Vendor_ID1              , 0x4144);
+        ichac97MixerSet(pThis, AC97_Vendor_ID2              , 0x5374);
+    }
+    else
+    {
+        /* Sigmatel 9700 (STAC9700) */
+        ichac97MixerSet(pThis, AC97_Vendor_ID1              , 0x8384);
+        ichac97MixerSet(pThis, AC97_Vendor_ID2              , 0x7600); /* 7608 */
+    }
+    ichac97RecordSelect(pThis, 0);
+
+    ichac97MixerSetVolume(pThis, AC97_Master_Volume_Mute,  PDMAUDIOMIXERCTL_VOLUME,  0x8000);
+    ichac97MixerSetVolume(pThis, AC97_PCM_Out_Volume_Mute, PDMAUDIOMIXERCTL_PCM,     0x8808);
+    ichac97MixerSetVolume(pThis, AC97_Line_In_Volume_Mute, PDMAUDIOMIXERCTL_LINE_IN, 0x8808);
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Writes data from the device to the host backends.
+ *
+ * @return  IPRT status code.
+ * @param   pThis
+ * @param   pStrmSt
+ * @param   cbMax
+ * @param   pcbWritten
+ */
+static int ichac97WriteAudio(PAC97STATE pThis, PAC97STREAM pStrmSt, uint32_t cbMax, uint32_t *pcbWritten)
+{
+    AssertPtrReturn(pThis,      VERR_INVALID_POINTER);
+    AssertPtrReturn(pStrmSt,    VERR_INVALID_POINTER);
+    AssertReturn(cbMax,         VERR_INVALID_PARAMETER);
+    /* pcbWritten is optional. */
+
+    PPDMDEVINS  pDevIns = ICHAC97STATE_2_DEVINS(pThis);
+    PAC97BMREGS pRegs   = &pStrmSt->Regs;
+
+    uint32_t    addr           = pRegs->bd.addr;
+    uint32_t    cbWrittenTotal = 0;
+    uint32_t    cbToRead;
+
+    uint32_t cbToWrite = RT_MIN((uint32_t)(pRegs->picb << 1), cbMax);
+    if (!cbToWrite)
+    {
+        if (pcbWritten)
+            *pcbWritten = 0;
+        return VINF_EOF;
+    }
+
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pReg=%p, cbMax=%RU32, cbToWrite=%RU32\n", pRegs, cbMax, cbToWrite));
+
+    while (cbToWrite)
+    {
+        cbToRead = RT_MIN(cbToWrite, pThis->cbReadWriteBuf);
+        PDMDevHlpPhysRead(pDevIns, addr, pThis->pvReadWriteBuf, cbToRead); /** @todo Check rc? */
+
+        uint32_t cbWritten;
+
+        /* Just multiplex the output to the connected backends.
+         * No need to utilize the virtual mixer here (yet). */
+        PAC97DRIVER pDrv;
+        RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
+        {
+            int rc2 = pDrv->pConnector->pfnWrite(pDrv->pConnector, pDrv->Out.pStrmOut,
+                                                 pThis->pvReadWriteBuf, cbToRead, &cbWritten);
+            LogFlowFunc(("\tLUN#%RU8: rc=%Rrc, cbWritten=%RU32\n", pDrv->uLUN, rc2, cbWritten));
+        }
+
+        LogFlowFunc(("\tcbToRead=%RU32, cbToWrite=%RU32, cbLeft=%RU32\n",
+                     cbToRead, cbToWrite, cbToWrite - cbWrittenTotal));
+
+        Assert(cbToWrite >= cbToRead);
+        cbToWrite      -= cbToRead;
+        addr           += cbToRead;
+        cbWrittenTotal += cbToRead;
+    }
+
+    pRegs->bd.addr = addr;
+
+    if (RT_SUCCESS(rc))
+    {
+        if (!cbToWrite) /* All data written? */
+        {
+            if (cbToRead < 4)
+            {
+                AssertMsgFailed(("Unable to save last written sample, cbToRead < 4 (is %RU32)\n", cbToRead));
+                pThis->last_samp = 0;
+            }
+            else
+                pThis->last_samp = *(uint32_t *)&pThis->pvReadWriteBuf[cbToRead - 4];
+        }
+
+        if (pcbWritten)
+            *pcbWritten = cbWrittenTotal;
+    }
+
+    LogFlowFunc(("cbWrittenTotal=%RU32, rc=%Rrc\n", cbWrittenTotal, rc));
+    return rc;
+}
+
+static void ichac97WriteBUP(PAC97STATE pThis, uint32_t cbElapsed)
+{
+    if (!(pThis->bup_flag & BUP_SET))
+    {
+        if (pThis->bup_flag & BUP_LAST)
+        {
+            unsigned int i;
+            uint32_t *p = (uint32_t*)pThis->silence;
+            for (i = 0; i < sizeof(pThis->silence) / 4; i++)
+                *p++ = pThis->last_samp;
+        }
+        else
+            RT_ZERO(pThis->silence);
+
+        pThis->bup_flag |= BUP_SET;
+    }
+
+    while (cbElapsed)
+    {
+        uint32_t cbToWrite = RT_MIN(cbElapsed, (uint32_t)sizeof(pThis->silence));
+        uint32_t cbWrittenToStream;
+        int rc2;
+
+        PAC97DRIVER pDrv;
+        RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
+        {
+            if (pDrv->pConnector->pfnIsActiveOut(pDrv->pConnector, pDrv->Out.pStrmOut))
+            {
+                rc2 = pDrv->pConnector->pfnWrite(pDrv->pConnector, pDrv->Out.pStrmOut,
+                                                 pThis->silence, cbToWrite, &cbWrittenToStream);
+                if (RT_SUCCESS(rc2))
+                {
+                    if (cbWrittenToStream < cbToWrite) /* Lagging behind? */
+                        LogFlowFunc(("\tLUN#%RU8: Warning: Only written %RU32 / %RU32 bytes, expect lags\n",
+                                     pDrv->uLUN, cbWrittenToStream, cbToWrite));
+                }
+            }
+            else /* Stream disabled, not fatal. */
+            {
+                cbWrittenToStream = 0;
+                rc2 = VERR_NOT_AVAILABLE;
+                /* Keep going. */
+            }
+        }
+
+        /* Always report all data as being written;
+         * backends who were not able to catch up have to deal with it themselves. */
+        Assert(cbElapsed >= cbToWrite);
+        cbElapsed -= cbToWrite;
+    }
+}
+
+static int ichac97ReadAudio(PAC97STATE pThis, PAC97STREAM pStrmSt, uint32_t cbMax, uint32_t *pcbRead)
+{
+    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
+    AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
+    AssertReturn(cbMax,      VERR_INVALID_PARAMETER);
+    /* pcbRead is optional. */
+
+    PPDMDEVINS pDevIns = ICHAC97STATE_2_DEVINS(pThis);
+    PAC97BMREGS pRegs  = &pStrmSt->Regs;
+
+    /* Select audio sink to process. */
+    AssertMsg(pStrmSt->u8Strm != PO_INDEX, ("Can't read from output\n"));
+    PAUDMIXSINK pSink = pStrmSt->u8Strm == MC_INDEX ? pThis->pSinkMicIn : pThis->pSinkLineIn;
+    AssertPtr(pSink);
+
+    uint32_t cbRead   = 0;
+
+    uint32_t cbMixBuf = cbMax;
+    uint32_t cbToRead = RT_MIN((uint32_t)(pRegs->picb << 1), cbMixBuf);
+
+    if (!cbToRead)
+    {
+        if (pcbRead)
+            *pcbRead = 0;
+        return VINF_EOF;
+    }
+
+    int rc;
+
+    uint8_t *pvMixBuf = (uint8_t *)RTMemAlloc(cbMixBuf);
+    if (pvMixBuf)
+    {
+        rc = AudioMixerProcessSinkIn(pSink, AUDMIXOP_BLEND, pvMixBuf, cbToRead, &cbRead);
+        if (   RT_SUCCESS(rc)
+            && cbRead)
+        {
+            PDMDevHlpPCIPhysWrite(pDevIns, pRegs->bd.addr, pvMixBuf, cbRead);
+            pRegs->bd.addr += cbRead;
+        }
+
+        RTMemFree(pvMixBuf);
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pcbRead)
+            *pcbRead = cbRead;
+    }
+
+    return rc;
+}
+
+#ifndef VBOX_WITH_AUDIO_CALLBACKS
+
+static DECLCALLBACK(void) ichac97Timer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+    PAC97STATE pThis = (PAC97STATE)pvUser;
+    Assert(pThis == PDMINS_2_DATA(pDevIns, PAC97STATE));
+    AssertPtr(pThis);
+
+    STAM_PROFILE_START(&pThis->StatTimer, a);
+
+    uint32_t cbInMax  = 0;
+    uint32_t cbOutMin = UINT32_MAX;
+
+    PAC97DRIVER pDrv;
+
+    uint64_t cTicksNow     = TMTimerGet(pTimer);
+    uint64_t cTicksElapsed = cTicksNow  - pThis->uTimerTS;
+    uint64_t cTicksPerSec  = TMTimerGetFreq(pTimer);
+
+    pThis->uTimerTS = cTicksNow;
+
+    /*
+     * Calculate the mixer's (fixed) sampling rate.
+     */
+    AssertPtr(pThis->pMixer);
+
+    PDMAUDIOSTREAMCFG mixerStrmCfg;
+    int rc = AudioMixerGetDeviceFormat(pThis->pMixer, &mixerStrmCfg);
+    AssertRC(rc);
+
+    PDMPCMPROPS mixerStrmProps;
+    rc = DrvAudioStreamCfgToProps(&mixerStrmCfg, &mixerStrmProps);
+    AssertRC(rc);
+
+    uint32_t cMixerSamplesMin  = (int)((2 * cTicksElapsed * mixerStrmCfg.uHz + cTicksPerSec) / cTicksPerSec / 2);
+    uint32_t cbMixerSamplesMin = cMixerSamplesMin << mixerStrmProps.cShift;
+
+    RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
+    {
+        uint32_t cbIn = 0;
+        uint32_t cbOut = 0;
+
+        rc = pDrv->pConnector->pfnQueryStatus(pDrv->pConnector,
+                                              &cbIn, &cbOut, NULL /* cSamplesLive */);
+        if (RT_SUCCESS(rc))
+            rc = pDrv->pConnector->pfnPlayOut(pDrv->pConnector, NULL /* cSamplesPlayed */);
+
+#ifdef DEBUG_TIMER
+        LogFlowFunc(("LUN#%RU8: rc=%Rrc, cbIn=%RU32, cbOut=%RU32\n", pDrv->uLUN, rc, cbIn, cbOut));
+#endif
+        /* If we there was an error handling (available) output or there simply is no output available,
+         * then calculate the minimum data rate which must be processed by the device emulation in order
+         * to function correctly.
+         *
+         * This is not the optimal solution, but as we have to deal with this on a timer-based approach
+         * (until we have the audio callbacks) we need to have device' DMA engines running. */
+        if (!pDrv->pConnector->pfnIsValidOut(pDrv->pConnector, pDrv->Out.pStrmOut))
+        {
+            /* Use the mixer's (fixed) sampling rate. */
+            cbOut = RT_MAX(cbOut, cbMixerSamplesMin);
+            continue;
+        }
+
+        const bool fIsActiveOut = pDrv->pConnector->pfnIsActiveOut(pDrv->pConnector, pDrv->Out.pStrmOut);
+        if (   RT_FAILURE(rc)
+            || !fIsActiveOut)
+        {
+            uint32_t cSamplesMin  = (int)((2 * cTicksElapsed * pDrv->Out.pStrmOut->Props.uHz + cTicksPerSec) / cTicksPerSec / 2);
+            uint32_t cbSamplesMin = AUDIOMIXBUF_S2B(&pDrv->Out.pStrmOut->MixBuf, cSamplesMin);
+
+#ifdef DEBUG_TIMER
+            LogFlowFunc(("\trc=%Rrc, cSamplesMin=%RU32, cbSamplesMin=%RU32\n", rc, cSamplesMin, cbSamplesMin));
+#endif
+            cbOut = RT_MAX(cbOut, cbSamplesMin);
+        }
+
+        cbOutMin = RT_MIN(cbOutMin, cbOut);
+        cbInMax  = RT_MAX(cbInMax, cbIn);
+    }
+
+#ifdef DEBUG_TIMER
+    LogFlowFunc(("cbInMax=%RU32, cbOutMin=%RU32\n", cbInMax, cbOutMin));
+#endif
+
+    if (cbOutMin == UINT32_MAX)
+        cbOutMin = 0;
+
+    /*
+     * Playback.
+     */
+    if (cbOutMin)
+    {
+        Assert(cbOutMin != UINT32_MAX);
+        ichac97TransferAudio(pThis, PO_INDEX, cbOutMin); /** @todo Add rc! */
+    }
+
+    /*
+     * Recording.
+     */
+    if (cbInMax)
+        ichac97TransferAudio(pThis, PI_INDEX, cbInMax); /** @todo Add rc! */
+
+    /* Kick the timer again. */
+    uint64_t cTicks = pThis->cTimerTicks;
+    /** @todo adjust cTicks down by now much cbOutMin represents. */
+    TMTimerSet(pThis->pTimer, cTicksNow + cTicks);
+
+    STAM_PROFILE_STOP(&pThis->StatTimer, a);
+}
+
+#endif
+
+static int ichac97TransferAudio(PAC97STATE pThis, AC97SOUNDSOURCE enmSrc, uint32_t cbElapsed)
+{
+    LogFlowFunc(("pThis=%p, enmSrc=%RU32, cbElapsed=%RU32\n", pThis, enmSrc, cbElapsed));
+
+    PAC97STREAM pStrmSt;
+    switch (enmSrc)
+    {
+        case PI_INDEX: pStrmSt = &pThis->StrmStLineIn; break;
+        case MC_INDEX: pStrmSt = &pThis->StrmStMicIn;  break;
+        case PO_INDEX: pStrmSt = &pThis->StrmStOut;    break;
+        default:
+        {
+            AssertMsgFailed(("Unknown source index %ld\n", enmSrc));
+            return VERR_NOT_SUPPORTED;
+        }
+    }
+
+    PAC97BMREGS pRegs = &pStrmSt->Regs;
+
+    if (pRegs->sr & SR_DCH) /* Controller halted? */
+    {
+        if (pRegs->cr & CR_RPBM)
+        {
+            switch (enmSrc)
+            {
+                case PO_INDEX:
+                    ichac97WriteBUP(pThis, cbElapsed);
+                    break;
+
+                default:
+                    break;
+            }
+        }
+
+        return VINF_SUCCESS;
+    }
+
+    int rc = VINF_SUCCESS;
+    uint32_t cbWrittenTotal = 0;
+
+    while (cbElapsed >> 1)
+    {
+        if (!pRegs->bd_valid)
+        {
+            LogFlowFunc(("Invalid buffer descriptor, fetching next one ...\n"));
+            ichac97StreamFetchBDLE(pThis, pStrmSt);
+        }
+
+        if (!pRegs->picb) /* Got a new buffer descriptor, that is, the position is 0? */
+        {
+            LogFlowFunc(("Fresh buffer descriptor %RU8 is empty, addr=%#x, len=%#x, skipping\n",
+                         pRegs->civ, pRegs->bd.addr, pRegs->bd.ctl_len));
+            if (pRegs->civ == pRegs->lvi)
+            {
+                pRegs->sr |= SR_DCH; /* CELV? */
+                pThis->bup_flag = 0;
+
+                rc = VINF_EOF;
+                break;
+            }
+
+            pRegs->sr &= ~SR_CELV;
+            pRegs->civ = pRegs->piv;
+            pRegs->piv = (pRegs->piv + 1) % 32;
+
+            ichac97StreamFetchBDLE(pThis, pStrmSt);
+            continue;
+        }
+
+        uint32_t cbTransferred;
+        switch (enmSrc)
+        {
+            case PO_INDEX:
+            {
+                rc = ichac97WriteAudio(pThis, pStrmSt, cbElapsed, &cbTransferred);
+                if (   RT_SUCCESS(rc)
+                    && cbTransferred)
+                {
+                    cbWrittenTotal += cbTransferred;
+                    Assert(cbElapsed >= cbTransferred);
+                    cbElapsed      -= cbTransferred;
+                    Assert((cbTransferred & 1) == 0);    /* Else the following shift won't work */
+                    pRegs->picb    -= (cbTransferred >> 1);
+                }
+                break;
+            }
+
+            case PI_INDEX:
+            case MC_INDEX:
+            {
+                rc = ichac97ReadAudio(pThis, pStrmSt, cbElapsed, &cbTransferred);
+                if (   RT_SUCCESS(rc)
+                    && cbTransferred)
+                {
+                    Assert(cbElapsed >= cbTransferred);
+                    cbElapsed  -= cbTransferred;
+                    Assert((cbTransferred & 1) == 0);    /* Else the following shift won't work */
+                    pRegs->picb -= (cbTransferred >> 1);
+                }
+                break;
+            }
+
+            default:
+                AssertMsgFailed(("Source %RU32 not supported\n", enmSrc));
+                rc = VERR_NOT_SUPPORTED;
+                break;
+        }
+
+        LogFlowFunc(("pReg->picb=%#x, cbWrittenTotal=%RU32\n", pRegs->picb, cbWrittenTotal));
+
+        if (!pRegs->picb)
+        {
+            uint32_t new_sr = pRegs->sr & ~SR_CELV;
+
+            if (pRegs->bd.ctl_len & BD_IOC)
+            {
+                new_sr |= SR_BCIS;
+            }
+
+            if (pRegs->civ == pRegs->lvi)
+            {
+                LogFlowFunc(("Underrun civ (%RU8) == lvi (%RU8)\n", pRegs->civ, pRegs->lvi));
+                new_sr |= SR_LVBCI | SR_DCH | SR_CELV;
+                pThis->bup_flag = (pRegs->bd.ctl_len & BD_BUP) ? BUP_LAST : 0;
+
+                rc = VINF_EOF;
+            }
+            else
+            {
+                pRegs->civ = pRegs->piv;
+                pRegs->piv = (pRegs->piv + 1) % 32;
+                ichac97StreamFetchBDLE(pThis, pStrmSt);
+            }
+
+            ichac97StreamUpdateStatus(pThis, pStrmSt, new_sr);
+        }
+
+        if (   RT_FAILURE(rc)
+            || rc == VINF_EOF) /* All data processed? */
+        {
+            break;
+        }
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN}
+ */
+static DECLCALLBACK(int) ichac97IOPortNABMRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port,
+                                               uint32_t *pu32Val, unsigned cbVal)
+{
+    PAC97STATE pThis    = (PAC97STATE)pvUser;
+
+    /* Get the index of the NABMBAR port. */
+    const uint32_t uPortIdx = Port - pThis->IOPortBase[1];
+
+    PAC97STREAM pStrmSt     = ichac97GetStreamFromID(pThis, AC97_PORT2IDX(uPortIdx));
+    PAC97BMREGS pRegs       = pStrmSt ? &pStrmSt->Regs : NULL;
+
+    switch (cbVal)
+    {
+        case 1:
+        {
+            switch (uPortIdx)
+            {
+                case CAS:
+                    /* Codec Access Semaphore Register */
+                    LogFlowFunc(("CAS %d\n", pThis->cas));
+                    *pu32Val = pThis->cas;
+                    pThis->cas = 1;
+                    break;
+                case PI_CIV:
+                case PO_CIV:
+                case MC_CIV:
+                    /* Current Index Value Register */
+                    *pu32Val = pRegs->civ;
+                    LogFlowFunc(("CIV[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
+                    break;
+                case PI_LVI:
+                case PO_LVI:
+                case MC_LVI:
+                    /* Last Valid Index Register */
+                    *pu32Val = pRegs->lvi;
+                    LogFlowFunc(("LVI[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
+                    break;
+                case PI_PIV:
+                case PO_PIV:
+                case MC_PIV:
+                    /* Prefetched Index Value Register */
+                    *pu32Val = pRegs->piv;
+                    LogFlowFunc(("PIV[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
+                    break;
+                case PI_CR:
+                case PO_CR:
+                case MC_CR:
+                    /* Control Register */
+                    *pu32Val = pRegs->cr;
+                    LogFlowFunc(("CR[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
+                    break;
+                case PI_SR:
+                case PO_SR:
+                case MC_SR:
+                    /* Status Register (lower part) */
+                    *pu32Val = pRegs->sr & 0xff; /** @todo r=andy Use RT_LO_U8. */
+                    LogFlowFunc(("SRb[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
+                    break;
+                default:
+                    *pu32Val = UINT32_MAX;
+                    LogFlowFunc(("U nabm readb %#x -> %#x\n", Port, *pu32Val));
+                    break;
+            }
+            break;
+        }
+
+        case 2:
+        {
+            switch (uPortIdx)
+            {
+                case PI_SR:
+                case PO_SR:
+                case MC_SR:
+                    /* Status Register */
+                    *pu32Val = pRegs->sr;
+                    LogFlowFunc(("SR[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
+                    break;
+                case PI_PICB:
+                case PO_PICB:
+                case MC_PICB:
+                    /* Position in Current Buffer Register */
+                    *pu32Val = pRegs->picb;
+                    LogFlowFunc(("PICB[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
+                    break;
+                default:
+                    *pu32Val = UINT32_MAX;
+                    LogFlowFunc(("U nabm readw %#x -> %#x\n", Port, *pu32Val));
+                    break;
+            }
+            break;
+        }
+
+        case 4:
+        {
+            switch (uPortIdx)
+            {
+                case PI_BDBAR:
+                case PO_BDBAR:
+                case MC_BDBAR:
+                    /* Buffer Descriptor Base Address Register */
+                    *pu32Val = pRegs->bdbar;
+                    LogFlowFunc(("BMADDR[%d] -> %#x\n", AC97_PORT2IDX(uPortIdx), *pu32Val));
+                    break;
+                case PI_CIV:
+                case PO_CIV:
+                case MC_CIV:
+                    /* 32-bit access: Current Index Value Register +
+                     *                Last Valid Index Register +
+                     *                Status Register */
+                    *pu32Val = pRegs->civ | (pRegs->lvi << 8) | (pRegs->sr << 16); /** @todo r=andy Use RT_MAKE_U32_FROM_U8. */
+                    LogFlowFunc(("CIV LVI SR[%d] -> %#x, %#x, %#x\n",
+                                 AC97_PORT2IDX(uPortIdx), pRegs->civ, pRegs->lvi, pRegs->sr));
+                    break;
+                case PI_PICB:
+                case PO_PICB:
+                case MC_PICB:
+                    /* 32-bit access: Position in Current Buffer Register +
+                     *                Prefetched Index Value Register +
+                     *                Control Register */
+                    *pu32Val = pRegs->picb | (pRegs->piv << 16) | (pRegs->cr << 24); /** @todo r=andy Use RT_MAKE_U32_FROM_U8. */
+                    LogFlowFunc(("PICB PIV CR[%d] -> %#x %#x %#x %#x\n",
+                                 AC97_PORT2IDX(uPortIdx), *pu32Val, pRegs->picb, pRegs->piv, pRegs->cr));
+                    break;
+                case GLOB_CNT:
+                    /* Global Control */
+                    *pu32Val = pThis->glob_cnt;
+                    LogFlowFunc(("glob_cnt -> %#x\n", *pu32Val));
+                    break;
+                case GLOB_STA:
+                    /* Global Status */
+                    *pu32Val = pThis->glob_sta | GS_S0CR;
+                    LogFlowFunc(("glob_sta -> %#x\n", *pu32Val));
+                    break;
+                default:
+                    *pu32Val = UINT32_MAX;
+                    LogFlowFunc(("U nabm readl %#x -> %#x\n", Port, *pu32Val));
+                    break;
+            }
+            break;
+        }
+
+        default:
+            return VERR_IOM_IOPORT_UNUSED;
+    }
+    return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+static DECLCALLBACK(int) ichac97IOPortNABMWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port,
+                                                uint32_t u32Val, unsigned cbVal)
+{
+    PAC97STATE  pThis   = (PAC97STATE)pvUser;
+
+    /* Get the index of the NABMBAR register. */
+    const uint32_t uPortIdx = Port - pThis->IOPortBase[1];
+
+    PAC97STREAM pStrmSt     = ichac97GetStreamFromID(pThis, AC97_PORT2IDX(uPortIdx));
+    PAC97BMREGS pRegs       = pStrmSt ? &pStrmSt->Regs : NULL;
+
+    switch (cbVal)
+    {
+        case 1:
+        {
+            switch (uPortIdx)
+            {
+                case PI_LVI:
+                case PO_LVI:
+                case MC_LVI:
+                    /* Last Valid Index */
+                    if ((pRegs->cr & CR_RPBM) && (pRegs->sr & SR_DCH))
+                    {
+                        pRegs->sr &= ~(SR_DCH | SR_CELV);
+                        pRegs->civ = pRegs->piv;
+                        pRegs->piv = (pRegs->piv + 1) % 32;
+
+                        ichac97StreamFetchBDLE(pThis, pStrmSt);
+                    }
+                    pRegs->lvi = u32Val % 32;
+                    LogFlowFunc(("LVI[%d] <- %#x\n", AC97_PORT2IDX(uPortIdx), u32Val));
+                    break;
+                case PI_CR:
+                case PO_CR:
+                case MC_CR:
+                {
+                    /* Control Register */
+                    if (u32Val & CR_RR) /* Busmaster reset */
+                    {
+                        ichac97StreamResetBMRegs(pThis, pStrmSt);
+                    }
+                    else
+                    {
+                        pRegs->cr = u32Val & CR_VALID_MASK;
+                        if (!(pRegs->cr & CR_RPBM))
+                        {
+                            ichac97StreamSetActive(pThis, pStrmSt, false /* fActive */);
+                            pRegs->sr |= SR_DCH;
+                        }
+                        else
+                        {
+                            pRegs->civ = pRegs->piv;
+                            pRegs->piv = (pRegs->piv + 1) % 32;
+
+                            ichac97StreamFetchBDLE(pThis, pStrmSt);
+
+                            pRegs->sr &= ~SR_DCH;
+                            ichac97StreamSetActive(pThis, pStrmSt, true /* fActive */);
+                        }
+                    }
+                    LogFlowFunc(("CR[%d] <- %#x (cr %#x)\n", AC97_PORT2IDX(uPortIdx), u32Val, pRegs->cr));
+                    break;
+                }
+                case PI_SR:
+                case PO_SR:
+                case MC_SR:
+                    /* Status Register */
+                    pRegs->sr |= u32Val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
+                    ichac97StreamUpdateStatus(pThis, pStrmSt, pRegs->sr & ~(u32Val & SR_WCLEAR_MASK));
+                    LogFlowFunc(("SR[%d] <- %#x (sr %#x)\n", AC97_PORT2IDX(uPortIdx), u32Val, pRegs->sr));
+                    break;
+                default:
+                    LogFlowFunc(("U nabm writeb %#x <- %#x\n", Port, u32Val));
+                    break;
+            }
+            break;
+        }
+
+        case 2:
+        {
+            switch (uPortIdx)
+            {
+                case PI_SR:
+                case PO_SR:
+                case MC_SR:
+                    /* Status Register */
+                    pRegs->sr |= u32Val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
+                    ichac97StreamUpdateStatus(pThis, pStrmSt, pRegs->sr & ~(u32Val & SR_WCLEAR_MASK));
+                    LogFlowFunc(("SR[%d] <- %#x (sr %#x)\n", AC97_PORT2IDX(uPortIdx), u32Val, pRegs->sr));
+                    break;
+                default:
+                    LogFlowFunc(("U nabm writew %#x <- %#x\n", Port, u32Val));
+                    break;
+            }
+            break;
+        }
+
+        case 4:
+        {
+            switch (uPortIdx)
+            {
+                case PI_BDBAR:
+                case PO_BDBAR:
+                case MC_BDBAR:
+                    /* Buffer Descriptor list Base Address Register */
+                    pRegs->bdbar = u32Val & ~3;
+                    LogFlowFunc(("BDBAR[%d] <- %#x (bdbar %#x)\n", AC97_PORT2IDX(uPortIdx), u32Val, pRegs->bdbar));
+                    break;
+                case GLOB_CNT:
+                    /* Global Control */
+                    if (u32Val & GC_WR)
+                        ichac97WarmReset(pThis);
+                    if (u32Val & GC_CR)
+                        ichac97ColdReset(pThis);
+                    if (!(u32Val & (GC_WR | GC_CR)))
+                        pThis->glob_cnt = u32Val & GC_VALID_MASK;
+                    LogFlowFunc(("glob_cnt <- %#x (glob_cnt %#x)\n", u32Val, pThis->glob_cnt));
+                    break;
+                case GLOB_STA:
+                    /* Global Status */
+                    pThis->glob_sta &= ~(u32Val & GS_WCLEAR_MASK);
+                    pThis->glob_sta |= (u32Val & ~(GS_WCLEAR_MASK | GS_RO_MASK)) & GS_VALID_MASK;
+                    LogFlowFunc(("glob_sta <- %#x (glob_sta %#x)\n", u32Val, pThis->glob_sta));
+                    break;
+                default:
+                    LogFlowFunc(("U nabm writel %#x <- %#x\n", Port, u32Val));
+                    break;
+            }
+            break;
+        }
+
+        default:
+            AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cbVal, u32Val));
+            break;
+    }
+    return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN}
+ */
+static DECLCALLBACK(int) ichac97IOPortNAMRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32Val, unsigned cbVal)
+{
+    PAC97STATE pThis = (PAC97STATE)pvUser;
+
+    switch (cbVal)
+    {
+        case 1:
+        {
+            LogFlowFunc(("U nam readb %#x\n", Port));
+            pThis->cas = 0;
+            *pu32Val = UINT32_MAX;
+            break;
+        }
+
+        case 2:
+        {
+            uint32_t index = Port - pThis->IOPortBase[0];
+            *pu32Val = UINT32_MAX;
+            pThis->cas = 0;
+            switch (index)
+            {
+                default:
+                    *pu32Val = ichac97MixerGet(pThis, index);
+                    LogFlowFunc(("nam readw %#x -> %#x\n", Port, *pu32Val));
+                    break;
+            }
+            break;
+        }
+
+        case 4:
+        {
+            LogFlowFunc(("U nam readl %#x\n", Port));
+            pThis->cas = 0;
+            *pu32Val = UINT32_MAX;
+            break;
+        }
+
+        default:
+            return VERR_IOM_IOPORT_UNUSED;
+    }
+    return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+static DECLCALLBACK(int) ichac97IOPortNAMWrite(PPDMDEVINS pDevIns,
+                                               void *pvUser, RTIOPORT Port, uint32_t u32Val, unsigned cbVal)
+{
+    PAC97STATE pThis = (PAC97STATE)pvUser;
+
+    switch (cbVal)
+    {
+        case 1:
+        {
+            LogFlowFunc(("U nam writeb %#x <- %#x\n", Port, u32Val));
+            pThis->cas = 0;
+            break;
+        }
+
+        case 2:
+        {
+            uint32_t index = Port - pThis->IOPortBase[0];
+            pThis->cas = 0;
+            switch (index)
+            {
+                case AC97_Reset:
+                    ichac97Reset(pThis->CTX_SUFF(pDevIns));
+                    break;
+                case AC97_Powerdown_Ctrl_Stat:
+                    u32Val &= ~0xf;
+                    u32Val |= ichac97MixerGet(pThis, index) & 0xf;
+                    ichac97MixerSet(pThis, index, u32Val);
+                    break;
+                case AC97_Master_Volume_Mute:
+                    if (pThis->uCodecModel == Codec_AD1980)
+                        if (ichac97MixerGet(pThis, AC97_AD_Misc) & AD_MISC_LOSEL)
+                            break;  /* Register controls surround (rear), do nothing. */
+                    ichac97MixerSetVolume(pThis, index, PDMAUDIOMIXERCTL_VOLUME, u32Val);
+                    break;
+                case AC97_Headphone_Volume_Mute:
+                    if (pThis->uCodecModel == Codec_AD1980)
+                        if (ichac97MixerGet(pThis, AC97_AD_Misc) & AD_MISC_HPSEL)
+                            /* Register controls PCM (front) outputs. */
+                            ichac97MixerSetVolume(pThis, index, PDMAUDIOMIXERCTL_VOLUME, u32Val);
+                    break;
+                case AC97_PCM_Out_Volume_Mute:
+                    ichac97MixerSetVolume(pThis, index, PDMAUDIOMIXERCTL_PCM, u32Val);
+                    break;
+                case AC97_Line_In_Volume_Mute:
+                    ichac97MixerSetVolume(pThis, index, PDMAUDIOMIXERCTL_LINE_IN, u32Val);
+                    break;
+                case AC97_Record_Select:
+                    ichac97RecordSelect(pThis, u32Val);
+                    break;
+                case AC97_Vendor_ID1:
+                case AC97_Vendor_ID2:
+                    LogFlowFunc(("Attempt to write vendor ID to %#x\n", u32Val));
+                    break;
+                case AC97_Extended_Audio_ID:
+                    LogFlowFunc(("Attempt to write extended audio ID to %#x\n", u32Val));
+                    break;
+                case AC97_Extended_Audio_Ctrl_Stat:
+                    if (!(u32Val & EACS_VRA))
+                    {
+                        ichac97MixerSet(pThis, AC97_PCM_Front_DAC_Rate, 48000);
+                        ichac97StreamReInit(pThis, &pThis->StrmStOut);
+
+                        ichac97MixerSet(pThis, AC97_PCM_LR_ADC_Rate,    48000);
+                        ichac97StreamReInit(pThis, &pThis->StrmStLineIn);
+                    }
+                    if (!(u32Val & EACS_VRM))
+                    {
+                        ichac97MixerSet(pThis, AC97_MIC_ADC_Rate,       48000);
+                        ichac97StreamReInit(pThis, &pThis->StrmStMicIn);
+                    }
+                    LogFlowFunc(("Setting extended audio control to %#x\n", u32Val));
+                    ichac97MixerSet(pThis, AC97_Extended_Audio_Ctrl_Stat, u32Val);
+                    break;
+                case AC97_PCM_Front_DAC_Rate:
+                    if (ichac97MixerGet(pThis, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA)
+                    {
+                        ichac97MixerSet(pThis, index, u32Val);
+                        LogFlowFunc(("Set front DAC rate to %RU32\n", u32Val));
+                        ichac97StreamReInit(pThis, &pThis->StrmStOut);
+                    }
+                    else
+                        LogFlowFunc(("Attempt to set front DAC rate to %RU32, but VRA is not set\n", u32Val));
+                    break;
+                case AC97_MIC_ADC_Rate:
+                    if (ichac97MixerGet(pThis, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRM)
+                    {
+                        ichac97MixerSet(pThis, index, u32Val);
+                        LogFlowFunc(("Set MIC ADC rate to %RU32\n", u32Val));
+                        ichac97StreamReInit(pThis, &pThis->StrmStMicIn);
+                    }
+                    else
+                        LogFlowFunc(("Attempt to set MIC ADC rate to %RU32, but VRM is not set\n", u32Val));
+                    break;
+                case AC97_PCM_LR_ADC_Rate:
+                    if (ichac97MixerGet(pThis, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA)
+                    {
+                        ichac97MixerSet(pThis, index, u32Val);
+                        LogFlowFunc(("Set front LR ADC rate to %RU32\n", u32Val));
+                        ichac97StreamReInit(pThis, &pThis->StrmStLineIn);
+                    }
+                    else
+                        LogFlowFunc(("Attempt to set LR ADC rate to %RU32, but VRA is not set\n", u32Val));
+                    break;
+                default:
+                    LogFlowFunc(("U nam writew %#x <- %#x\n", Port, u32Val));
+                    ichac97MixerSet(pThis, index, u32Val);
+                    break;
+            }
+            break;
+        }
+
+        case 4:
+        {
+            LogFlowFunc(("U nam writel %#x <- %#x\n", Port, u32Val));
+            pThis->cas = 0;
+            break;
+        }
+
+        default:
+            AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cbVal, u32Val));
+            break;
+    }
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNPCIIOREGIONMAP}
+ */
+static DECLCALLBACK(int) ichac97IOPortMap(PPCIDEVICE pPciDev, int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb,
+                                          PCIADDRESSSPACE enmType)
+{
+    PPDMDEVINS  pDevIns = pPciDev->pDevIns;
+    PAC97STATE  pThis   = RT_FROM_MEMBER(pPciDev, AC97STATE, PciDev);
+    RTIOPORT    Port    = (RTIOPORT)GCPhysAddress;
+
+    Assert(enmType == PCI_ADDRESS_SPACE_IO);
+    Assert(cb >= 0x20);
+
+    if (iRegion < 0 || iRegion > 1) /* We support 2 regions max. at the moment. */
+        return VERR_INVALID_PARAMETER;
+
+    int rc;
+    if (iRegion == 0)
+        rc = PDMDevHlpIOPortRegister(pDevIns, Port, 256, pThis,
+                                     ichac97IOPortNAMWrite, ichac97IOPortNAMRead,
+                                     NULL, NULL, "ICHAC97 NAM");
+    else
+        rc = PDMDevHlpIOPortRegister(pDevIns, Port, 64, pThis,
+                                     ichac97IOPortNABMWrite, ichac97IOPortNABMRead,
+                                     NULL, NULL, "ICHAC97 NABM");
+    if (RT_FAILURE(rc))
+        return rc;
+
+    pThis->IOPortBase[iRegion] = Port;
+    return VINF_SUCCESS;
+}
+
+DECLINLINE(PAC97STREAM) ichac97GetStreamFromID(PAC97STATE pThis, uint32_t uID)
+{
+    switch (uID)
+    {
+        case PI_INDEX: return &pThis->StrmStLineIn;
+        case MC_INDEX: return &pThis->StrmStMicIn;
+        case PO_INDEX: return &pThis->StrmStOut;
+        default:       break;
+    }
+
+    return NULL;
+}
+
+#ifdef IN_RING3
+static int ichac97SaveStream(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PAC97STREAM pStrmSt)
+{
+    PAC97BMREGS pRegs = &pStrmSt->Regs;
+
+    SSMR3PutU32(pSSM, pRegs->bdbar);
+    SSMR3PutU8( pSSM, pRegs->civ);
+    SSMR3PutU8( pSSM, pRegs->lvi);
+    SSMR3PutU16(pSSM, pRegs->sr);
+    SSMR3PutU16(pSSM, pRegs->picb);
+    SSMR3PutU8( pSSM, pRegs->piv);
+    SSMR3PutU8( pSSM, pRegs->cr);
+    SSMR3PutS32(pSSM, pRegs->bd_valid);
+    SSMR3PutU32(pSSM, pRegs->bd.addr);
+    SSMR3PutU32(pSSM, pRegs->bd.ctl_len);
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) ichac97SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+    PAC97STATE pThis = PDMINS_2_DATA(pDevIns, PAC97STATE);
+
+    SSMR3PutU32(pSSM, pThis->glob_cnt);
+    SSMR3PutU32(pSSM, pThis->glob_sta);
+    SSMR3PutU32(pSSM, pThis->cas);
+
+    /** @todo r=andy For the next saved state version, add unique stream identifiers and a stream count. */
+    /* Note: The order the streams are saved here is critical, so don't touch. */
+    int rc2 = ichac97SaveStream(pDevIns, pSSM, &pThis->StrmStLineIn);
+    AssertRC(rc2);
+    rc2 = ichac97SaveStream(pDevIns, pSSM, &pThis->StrmStOut);
+    AssertRC(rc2);
+    rc2 = ichac97SaveStream(pDevIns, pSSM, &pThis->StrmStMicIn);
+    AssertRC(rc2);
+
+    SSMR3PutMem(pSSM, pThis->mixer_data, sizeof(pThis->mixer_data));
+
+    uint8_t active[LAST_INDEX];
+
+    PAC97DRIVER pDrv;
+    RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
+    {
+        PPDMIAUDIOCONNECTOR pCon = pDrv->pConnector;
+        AssertPtr(pCon);
+        active[PI_INDEX] = pCon->pfnIsActiveIn (pCon, pDrv->LineIn.pStrmIn) ? 1 : 0;
+        active[PO_INDEX] = pCon->pfnIsActiveOut(pCon, pDrv->Out.pStrmOut)   ? 1 : 0;
+        active[MC_INDEX] = pCon->pfnIsActiveIn (pCon, pDrv->MicIn.pStrmIn)  ? 1 : 0;
+    }
+
+    SSMR3PutMem(pSSM, active, sizeof(active));
+
+    return VINF_SUCCESS;
+}
+
+static int ichac97LoadStream(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PAC97STREAM pStrmSt)
+{
+    PAC97BMREGS pRegs = &pStrmSt->Regs;
+
+    SSMR3GetU32(pSSM, &pRegs->bdbar);
+    SSMR3GetU8( pSSM, &pRegs->civ);
+    SSMR3GetU8( pSSM, &pRegs->lvi);
+    SSMR3GetU16(pSSM, &pRegs->sr);
+    SSMR3GetU16(pSSM, &pRegs->picb);
+    SSMR3GetU8( pSSM, &pRegs->piv);
+    SSMR3GetU8( pSSM, &pRegs->cr);
+    SSMR3GetS32(pSSM, &pRegs->bd_valid);
+    SSMR3GetU32(pSSM, &pRegs->bd.addr);
+    SSMR3GetU32(pSSM, &pRegs->bd.ctl_len);
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) ichac97LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+    PAC97STATE pThis = PDMINS_2_DATA(pDevIns, PAC97STATE);
+
+    AssertMsgReturn (uVersion == AC97_SSM_VERSION, ("%RU32\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
+    Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
+
+    SSMR3GetU32(pSSM, &pThis->glob_cnt);
+    SSMR3GetU32(pSSM, &pThis->glob_sta);
+    SSMR3GetU32(pSSM, &pThis->cas);
+
+    /** @todo r=andy For the next saved state version, add unique stream identifiers and a stream count. */
+    /* Note: The order the streams are loaded here is critical, so don't touch. */
+    int rc2 = ichac97LoadStream(pDevIns, pSSM, &pThis->StrmStLineIn);
+    AssertRC(rc2);
+    rc2 = ichac97LoadStream(pDevIns, pSSM, &pThis->StrmStOut);
+    AssertRC(rc2);
+    rc2 = ichac97LoadStream(pDevIns, pSSM, &pThis->StrmStMicIn);
+    AssertRC(rc2);
+
+    SSMR3GetMem(pSSM, pThis->mixer_data, sizeof(pThis->mixer_data));
+
+    /** @todo r=andy Stream IDs are hardcoded to certain streams. */
+    uint8_t uaStrmsActive[LAST_INDEX];
+    SSMR3GetMem(pSSM, uaStrmsActive, sizeof(uaStrmsActive));
+
+    ichac97RecordSelect(pThis, ichac97MixerGet(pThis, AC97_Record_Select));
+# define V_(a, b) ichac97MixerSetVolume(pThis, a, b, ichac97MixerGet(pThis, a))
+    V_(AC97_Master_Volume_Mute,  PDMAUDIOMIXERCTL_VOLUME);
+    V_(AC97_PCM_Out_Volume_Mute, PDMAUDIOMIXERCTL_PCM);
+    V_(AC97_Line_In_Volume_Mute, PDMAUDIOMIXERCTL_LINE_IN);
+# undef V_
+    if (pThis->uCodecModel == Codec_AD1980)
+        if (ichac97MixerGet(pThis, AC97_AD_Misc) & AD_MISC_HPSEL)
+            ichac97MixerSetVolume(pThis, AC97_Headphone_Volume_Mute, PDMAUDIOMIXERCTL_VOLUME,
+                             ichac97MixerGet(pThis, AC97_Headphone_Volume_Mute));
+
+    int rc;
+    rc = ichac97StreamInit(pThis, &pThis->StrmStLineIn, PI_INDEX);
+    AssertRC(rc);
+    rc = ichac97StreamInit(pThis, &pThis->StrmStMicIn,  MC_INDEX);
+    AssertRC(rc);
+    rc = ichac97StreamInit(pThis, &pThis->StrmStOut,    PO_INDEX);
+    AssertRC(rc);
+
+    /** @todo r=andy Stream IDs are hardcoded to certain streams. */
+    rc = ichac97StreamSetActive(pThis, &pThis->StrmStLineIn, RT_BOOL(uaStrmsActive[PI_INDEX]));
+    AssertRC(rc);
+    rc = ichac97StreamSetActive(pThis, &pThis->StrmStMicIn,  RT_BOOL(uaStrmsActive[MC_INDEX]));
+    AssertRC(rc);
+    rc = ichac97StreamSetActive(pThis, &pThis->StrmStOut,    RT_BOOL(uaStrmsActive[PO_INDEX]));
+    AssertRC(rc);
+
+    pThis->bup_flag = 0;
+    pThis->last_samp = 0;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) ichac97QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
+{
+    PAC97STATE pThis = RT_FROM_MEMBER(pInterface, AC97STATE, IBase);
+    Assert(&pThis->IBase == pInterface);
+
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
+    return NULL;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ *
+ * @remarks The original sources didn't install a reset handler, but it seems to
+ *          make sense to me so we'll do it.
+ */
+static DECLCALLBACK(void) ichac97Reset(PPDMDEVINS pDevIns)
+{
+    PAC97STATE pThis = PDMINS_2_DATA(pDevIns, PAC97STATE);
+
+    LogFlowFuncEnter();
+
+    /*
+     * Reset the device state (will need pDrv later).
+     */
+    ichac97StreamResetBMRegs(pThis, &pThis->StrmStLineIn);
+    ichac97StreamResetBMRegs(pThis, &pThis->StrmStMicIn);
+    ichac97StreamResetBMRegs(pThis, &pThis->StrmStOut);
+
+    /*
+     * Reset the mixer too. The Windows XP driver seems to rely on
+     * this. At least it wants to read the vendor id before it resets
+     * the codec manually.
+     */
+    ichac97MixerReset(pThis);
+
+    /*
+     * Stop any audio currently playing.
+     */
+    PAC97DRIVER pDrv;
+    RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
+    {
+        pDrv->pConnector->pfnEnableIn(pDrv->pConnector,  pDrv->LineIn.pStrmIn, false /* Disable */);
+        /* Ignore rc. */
+        pDrv->pConnector->pfnEnableIn(pDrv->pConnector,  pDrv->MicIn.pStrmIn,  false /* Disable */);
+        /* Ditto. */
+        pDrv->pConnector->pfnEnableOut(pDrv->pConnector, pDrv->Out.pStrmOut,   false /* Disable */);
+        /* Ditto. */
+    }
+
+    /*
+     * Reset all streams.
+     */
+    ichac97StreamReset(pThis, &pThis->StrmStLineIn);
+    ichac97StreamReset(pThis, &pThis->StrmStMicIn);
+    ichac97StreamReset(pThis, &pThis->StrmStOut);
+
+    LogRel(("AC97: Reset\n"));
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) ichac97Destruct(PPDMDEVINS pDevIns)
+{
+    PAC97STATE pThis = PDMINS_2_DATA(pDevIns, PAC97STATE);
+
+    LogFlowFuncEnter();
+
+    PAC97DRIVER pDrv;
+    while (!RTListIsEmpty(&pThis->lstDrv))
+    {
+        pDrv = RTListGetFirst(&pThis->lstDrv, AC97DRIVER, Node);
+
+        RTListNodeRemove(&pDrv->Node);
+        RTMemFree(pDrv);
+    }
+
+    if (pThis->pMixer)
+    {
+        AudioMixerDestroy(pThis->pMixer);
+        pThis->pMixer = NULL;
+    }
+
+    if (pThis->pvReadWriteBuf)
+    {
+        RTMemFree(pThis->pvReadWriteBuf);
+        pThis->pvReadWriteBuf = NULL;
+        pThis->cbReadWriteBuf = 0;
+    }
+
+    LogFlowFuncLeave();
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Attach command, internal version.
+ *
+ * This is called to let the device attach to a driver for a specified LUN
+ * during runtime. This is not called during VM construction, the device
+ * constructor has to attach to all the available drivers.
+ *
+ * @returns VBox status code.
+ * @param   pDevIns     The device instance.
+ * @param   pDrv        Driver to (re-)use for (re-)attaching to.
+ *                      If NULL is specified, a new driver will be created and appended
+ *                      to the driver list.
+ * @param   uLUN        The logical unit which is being detached.
+ * @param   fFlags      Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ */
+static DECLCALLBACK(int) ichac97AttachInternal(PPDMDEVINS pDevIns, PAC97DRIVER pDrv, unsigned uLUN, uint32_t fFlags)
+{
+    PAC97STATE pThis = PDMINS_2_DATA(pDevIns, PAC97STATE);
+
+    /*
+     * Attach driver.
+     */
+    char *pszDesc = NULL;
+    if (RTStrAPrintf(&pszDesc, "Audio driver port (AC'97) for LUN #%u", uLUN) <= 0)
+        AssertReleaseMsgReturn(pszDesc,
+                               ("Not enough memory for AC'97 driver port description of LUN #%u\n", uLUN),
+                               VERR_NO_MEMORY);
+
+    PPDMIBASE pDrvBase;
+    int rc = PDMDevHlpDriverAttach(pDevIns, uLUN,
+                                   &pThis->IBase, &pDrvBase, pszDesc);
+    if (RT_SUCCESS(rc))
+    {
+        if (pDrv == NULL)
+            pDrv = (PAC97DRIVER)RTMemAllocZ(sizeof(AC97DRIVER));
+        if (pDrv)
+        {
+            pDrv->pDrvBase   = pDrvBase;
+            pDrv->pConnector = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIAUDIOCONNECTOR);
+            AssertMsg(pDrv->pConnector != NULL, ("Configuration error: LUN #%u has no host audio interface, rc=%Rrc\n", uLUN, rc));
+            pDrv->pAC97State = pThis;
+            pDrv->uLUN       = uLUN;
+
+            /*
+             * For now we always set the driver at LUN 0 as our primary
+             * host backend. This might change in the future.
+             */
+            if (pDrv->uLUN == 0)
+                pDrv->Flags |= PDMAUDIODRVFLAG_PRIMARY;
+
+            LogFunc(("LUN#%RU8: pCon=%p, drvFlags=0x%x\n", uLUN, pDrv->pConnector, pDrv->Flags));
+
+            /* Attach to driver list if not attached yet. */
+            if (!pDrv->fAttached)
+            {
+                RTListAppend(&pThis->lstDrv, &pDrv->Node);
+                pDrv->fAttached = true;
+            }
+        }
+        else
+            rc = VERR_NO_MEMORY;
+    }
+    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+        LogFunc(("No attached driver for LUN #%u\n", uLUN));
+
+    if (RT_FAILURE(rc))
+    {
+        /* Only free this string on failure;
+         * must remain valid for the live of the driver instance. */
+        RTStrFree(pszDesc);
+    }
+
+    LogFunc(("iLUN=%u, fFlags=0x%x, rc=%Rrc\n", uLUN, fFlags, rc));
+    return rc;
+}
+
+
+/**
+ * Attach command.
+ *
+ * This is called to let the device attach to a driver for a specified LUN
+ * during runtime. This is not called during VM construction, the device
+ * constructor has to attach to all the available drivers.
+ *
+ * @returns VBox status code.
+ * @param   pDevIns     The device instance.
+ * @param   uLUN        The logical unit which is being detached.
+ * @param   fFlags      Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ */
+static DECLCALLBACK(int) ichac97Attach(PPDMDEVINS pDevIns, unsigned uLUN, uint32_t fFlags)
+{
+    return ichac97AttachInternal(pDevIns, NULL /* pDrv */, uLUN, fFlags);
+}
+
+static DECLCALLBACK(void) ichac97Detach(PPDMDEVINS pDevIns, unsigned uLUN, uint32_t fFlags)
+{
+    LogFunc(("iLUN=%u, fFlags=0x%x\n", uLUN, fFlags));
+}
+
+/**
+ * Re-attach.
+ *
+ * @returns VBox status code.
+ * @param   pThis       Device instance.
+ * @param   pDrv        Driver instance used for attaching to.
+ *                      If NULL is specified, a new driver will be created and appended
+ *                      to the driver list.
+ * @param   uLUN        The logical unit which is being re-detached.
+ * @param   pszDriver   Driver name.
+ */
+static int ichac97Reattach(PAC97STATE pThis, PAC97DRIVER pDrv, uint8_t uLUN, const char *pszDriver)
+{
+    AssertPtrReturn(pThis,     VERR_INVALID_POINTER);
+    AssertPtrReturn(pszDriver, VERR_INVALID_POINTER);
+
+    PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
+    PCFGMNODE pRoot = CFGMR3GetRoot(pVM);
+    PCFGMNODE pDev0 = CFGMR3GetChild(pRoot, "Devices/ichac97/0/");
+
+    /* Remove LUN branch. */
+    CFGMR3RemoveNode(CFGMR3GetChildF(pDev0, "LUN#%u/", uLUN));
+
+    if (pDrv)
+    {
+        /* Re-use a driver instance => detach the driver before. */
+        int rc = PDMDevHlpDriverDetach(pThis->pDevInsR3, PDMIBASE_2_PDMDRV(pDrv->pDrvBase), 0 /* fFlags */);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+#define RC_CHECK() if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; }
+
+    int rc = VINF_SUCCESS;
+    do
+    {
+        PCFGMNODE pLunL0;
+        rc = CFGMR3InsertNodeF(pDev0, &pLunL0, "LUN#%u/", uLUN);        RC_CHECK();
+        rc = CFGMR3InsertString(pLunL0, "Driver",       "AUDIO");       RC_CHECK();
+        rc = CFGMR3InsertNode(pLunL0,   "Config/",       NULL);         RC_CHECK();
+
+        PCFGMNODE pLunL1, pLunL2;
+        rc = CFGMR3InsertNode  (pLunL0, "AttachedDriver/", &pLunL1);    RC_CHECK();
+        rc = CFGMR3InsertNode  (pLunL1,  "Config/",        &pLunL2);    RC_CHECK();
+        rc = CFGMR3InsertString(pLunL1,  "Driver",          pszDriver); RC_CHECK();
+
+        rc = CFGMR3InsertString(pLunL2, "AudioDriver", pszDriver);      RC_CHECK();
+
+    } while (0);
+
+    if (RT_SUCCESS(rc))
+        rc = ichac97AttachInternal(pThis->pDevInsR3, pDrv, uLUN, 0 /* fFlags */);
+
+    LogFunc(("pThis=%p, uLUN=%u, pszDriver=%s, rc=%Rrc\n", pThis, uLUN, pszDriver, rc));
+
+#undef RC_CHECK
+
+    return rc;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) ichac97Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+    PAC97STATE pThis = PDMINS_2_DATA(pDevIns, PAC97STATE);
+
+    /* NB: This must be done *before* any possible failure (and running the destructor). */
+    RTListInit(&pThis->lstDrv);
+
+    Assert(iInstance == 0);
+    PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+
+    /*
+     * Validations.
+     */
+    if (!CFGMR3AreValuesValid(pCfg,
+                              "Codec\0"
+                              "TimerHz\0"))
+        return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
+                                N_("Invalid configuration for the AC'97 device"));
+
+    /*
+     * Read config data.
+     */
+    char szCodec[20];
+    int rc = CFGMR3QueryStringDef(pCfg, "Codec", &szCodec[0], sizeof(szCodec), "STAC9700");
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
+                                N_("AC'97 configuration error: Querying \"Codec\" as string failed"));
+
+#ifndef VBOX_WITH_AUDIO_CALLBACKS
+    uint16_t uTimerHz;
+    rc = CFGMR3QueryU16Def(pCfg, "TimerHz", &uTimerHz, 200 /* Hz */);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("AC'97 configuration error: failed to read Hertz (Hz) rate as unsigned integer"));
+#endif
+
+    /*
+     * The AD1980 codec (with corresponding PCI subsystem vendor ID) is whitelisted
+     * in the Linux kernel; Linux makes no attempt to measure the data rate and assumes
+     * 48 kHz rate, which is exactly what we need. Same goes for AD1981B.
+     */
+    bool fChipAD1980 = false;
+    if (!strcmp(szCodec, "STAC9700"))
+        pThis->uCodecModel = Codec_STAC9700;
+    else if (!strcmp(szCodec, "AD1980"))
+        pThis->uCodecModel = Codec_AD1980;
+    else if (!strcmp(szCodec, "AD1981B"))
+        pThis->uCodecModel = Codec_AD1981B;
+    else
+    {
+        return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
+                                   N_("AC'97 configuration error: The \"Codec\" value \"%s\" is unsupported"),
+                                   szCodec);
+    }
+
+    /*
+     * Initialize data (most of it anyway).
+     */
+    pThis->pDevInsR3                = pDevIns;
+    /* IBase */
+    pThis->IBase.pfnQueryInterface  = ichac97QueryInterface;
+
+    /* PCI Device (the assertions will be removed later) */
+    PCIDevSetVendorId         (&pThis->PciDev, 0x8086); /* 00 ro - intel. */               Assert(pThis->PciDev.config[0x00] == 0x86); Assert(pThis->PciDev.config[0x01] == 0x80);
+    PCIDevSetDeviceId         (&pThis->PciDev, 0x2415); /* 02 ro - 82801 / 82801aa(?). */  Assert(pThis->PciDev.config[0x02] == 0x15); Assert(pThis->PciDev.config[0x03] == 0x24);
+    PCIDevSetCommand          (&pThis->PciDev, 0x0000); /* 04 rw,ro - pcicmd. */           Assert(pThis->PciDev.config[0x04] == 0x00); Assert(pThis->PciDev.config[0x05] == 0x00);
+    PCIDevSetStatus           (&pThis->PciDev, VBOX_PCI_STATUS_DEVSEL_MEDIUM |  VBOX_PCI_STATUS_FAST_BACK); /* 06 rwc?,ro? - pcists. */      Assert(pThis->PciDev.config[0x06] == 0x80); Assert(pThis->PciDev.config[0x07] == 0x02);
+    PCIDevSetRevisionId       (&pThis->PciDev, 0x01);   /* 08 ro - rid. */                 Assert(pThis->PciDev.config[0x08] == 0x01);
+    PCIDevSetClassProg        (&pThis->PciDev, 0x00);   /* 09 ro - pi. */                  Assert(pThis->PciDev.config[0x09] == 0x00);
+    PCIDevSetClassSub         (&pThis->PciDev, 0x01);   /* 0a ro - scc; 01 == Audio. */    Assert(pThis->PciDev.config[0x0a] == 0x01);
+    PCIDevSetClassBase        (&pThis->PciDev, 0x04);   /* 0b ro - bcc; 04 == multimedia. */ Assert(pThis->PciDev.config[0x0b] == 0x04);
+    PCIDevSetHeaderType       (&pThis->PciDev, 0x00);   /* 0e ro - headtyp. */             Assert(pThis->PciDev.config[0x0e] == 0x00);
+    PCIDevSetBaseAddress      (&pThis->PciDev, 0,       /* 10 rw - nambar - native audio mixer base. */
+                               true /* fIoSpace */, false /* fPrefetchable */, false /* f64Bit */, 0x00000000); Assert(pThis->PciDev.config[0x10] == 0x01); Assert(pThis->PciDev.config[0x11] == 0x00); Assert(pThis->PciDev.config[0x12] == 0x00); Assert(pThis->PciDev.config[0x13] == 0x00);
+    PCIDevSetBaseAddress      (&pThis->PciDev, 1,       /* 14 rw - nabmbar - native audio bus mastering. */
+                               true /* fIoSpace */, false /* fPrefetchable */, false /* f64Bit */, 0x00000000); Assert(pThis->PciDev.config[0x14] == 0x01); Assert(pThis->PciDev.config[0x15] == 0x00); Assert(pThis->PciDev.config[0x16] == 0x00); Assert(pThis->PciDev.config[0x17] == 0x00);
+    PCIDevSetInterruptLine    (&pThis->PciDev, 0x00);   /* 3c rw. */                       Assert(pThis->PciDev.config[0x3c] == 0x00);
+    PCIDevSetInterruptPin     (&pThis->PciDev, 0x01);   /* 3d ro - INTA#. */               Assert(pThis->PciDev.config[0x3d] == 0x01);
+
+    if (pThis->uCodecModel == Codec_AD1980)
+    {
+        PCIDevSetSubSystemVendorId(&pThis->PciDev, 0x1028); /* 2c ro - Dell.) */
+        PCIDevSetSubSystemId      (&pThis->PciDev, 0x0177); /* 2e ro. */
+    }
+    else if (pThis->uCodecModel == Codec_AD1981B)
+    {
+        PCIDevSetSubSystemVendorId(&pThis->PciDev, 0x1028); /* 2c ro - Dell.) */
+        PCIDevSetSubSystemId      (&pThis->PciDev, 0x01ad); /* 2e ro. */
+    }
+    else
+    {
+        PCIDevSetSubSystemVendorId(&pThis->PciDev, 0x8086); /* 2c ro - Intel.) */
+        PCIDevSetSubSystemId      (&pThis->PciDev, 0x0000); /* 2e ro. */
+    }
+
+    /*
+     * Register the PCI device, it's I/O regions, the timer and the
+     * saved state item.
+     */
+    rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 256, PCI_ADDRESS_SPACE_IO, ichac97IOPortMap);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, 64, PCI_ADDRESS_SPACE_IO, ichac97IOPortMap);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = PDMDevHlpSSMRegister(pDevIns, AC97_SSM_VERSION, sizeof(*pThis), ichac97SaveExec, ichac97LoadExec);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /*
+     * Attach driver.
+     */
+    uint8_t uLUN;
+    for (uLUN = 0; uLUN < UINT8_MAX; ++uLUN)
+    {
+        LogFunc(("Trying to attach driver for LUN #%RU8 ...\n", uLUN));
+        rc = ichac97AttachInternal(pDevIns, NULL /* pDrv */, uLUN, 0 /* fFlags */);
+        if (RT_FAILURE(rc))
+        {
+            if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+                rc = VINF_SUCCESS;
+            else if (rc == VERR_AUDIO_BACKEND_INIT_FAILED)
+            {
+                ichac97Reattach(pThis, NULL /* pDrv */, uLUN, "NullAudio");
+                PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
+                        N_("No audio devices could be opened. Selecting the NULL audio backend "
+                            "with the consequence that no sound is audible"));
+                /* attaching to the NULL audio backend will never fail */
+                rc = VINF_SUCCESS;
+            }
+            break;
+        }
+    }
+
+    LogFunc(("cLUNs=%RU8, rc=%Rrc\n", uLUN, rc));
+
+    if (RT_SUCCESS(rc))
+    {
+        rc = AudioMixerCreate("AC'97 Mixer", 0 /* uFlags */, &pThis->pMixer);
+        if (RT_SUCCESS(rc))
+        {
+            /* Set a default audio format for our mixer. */
+            PDMAUDIOSTREAMCFG streamCfg;
+            streamCfg.uHz           = 44100;
+            streamCfg.cChannels     = 2;
+            streamCfg.enmFormat     = AUD_FMT_S16;
+            streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
+
+            rc = AudioMixerSetDeviceFormat(pThis->pMixer, &streamCfg);
+            AssertRC(rc);
+
+            /* Add all required audio sinks. */
+            rc = AudioMixerAddSink(pThis->pMixer, "[Playback] PCM Output", AUDMIXSINKDIR_OUTPUT, &pThis->pSinkOutput);
+            AssertRC(rc);
+
+            rc = AudioMixerAddSink(pThis->pMixer, "[Recording] Line In", AUDMIXSINKDIR_INPUT, &pThis->pSinkLineIn);
+            AssertRC(rc);
+
+            rc = AudioMixerAddSink(pThis->pMixer, "[Recording] Microphone In", AUDMIXSINKDIR_INPUT, &pThis->pSinkMicIn);
+            AssertRC(rc);
+        }
+    }
+
+    ichac97Reset(pDevIns);
+
+    if (RT_SUCCESS(rc))
+    {
+        rc = ichac97StreamInit(pThis, &pThis->StrmStLineIn, PI_INDEX);
+        if (RT_FAILURE(rc))
+            return rc;
+        rc = ichac97StreamInit(pThis, &pThis->StrmStMicIn,  MC_INDEX);
+        if (RT_FAILURE(rc))
+            return rc;
+        rc = ichac97StreamInit(pThis, &pThis->StrmStOut,    PO_INDEX);
+        if (RT_FAILURE(rc))
+            return rc;
+
+        PAC97DRIVER pDrv;
+        RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
+        {
+            /*
+             * Only primary drivers are critical for the VM to run. Everything else
+             * might not worth showing an own error message box in the GUI.
+             */
+            if (!(pDrv->Flags & PDMAUDIODRVFLAG_PRIMARY))
+                continue;
+
+            PPDMIAUDIOCONNECTOR pCon = pDrv->pConnector;
+            AssertPtr(pCon);
+
+            bool fValidLineIn = pCon->pfnIsValidIn(pCon, pDrv->LineIn.pStrmIn);
+            bool fValidMicIn  = pCon->pfnIsValidIn (pCon, pDrv->MicIn.pStrmIn);
+            bool fValidOut    = pCon->pfnIsValidOut(pCon, pDrv->Out.pStrmOut);
+
+            if (    !fValidLineIn
+                 && !fValidMicIn
+                 && !fValidOut)
+            {
+                LogRel(("AC97: Falling back to NULL backend (no sound audible)\n"));
+
+                ichac97Reset(pDevIns);
+                ichac97Reattach(pThis, pDrv, pDrv->uLUN, "NullAudio");
+
+                PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
+                    N_("No audio devices could be opened. Selecting the NULL audio backend "
+                       "with the consequence that no sound is audible"));
+            }
+            else
+            {
+                bool fWarn = false;
+
+                PDMAUDIOBACKENDCFG backendCfg;
+                int rc2 = pCon->pfnGetConfiguration(pCon, &backendCfg);
+                if (RT_SUCCESS(rc2))
+                {
+                    if (backendCfg.cMaxHstStrmsIn)
+                    {
+                        /* If the audio backend supports two or more input streams at once,
+                         * warn if one of our two inputs (microphone-in and line-in) failed to initialize. */
+                        if (backendCfg.cMaxHstStrmsIn >= 2)
+                            fWarn = !fValidLineIn || !fValidMicIn;
+                        /* If the audio backend only supports one input stream at once (e.g. pure ALSA, and
+                         * *not* ALSA via PulseAudio plugin!), only warn if both of our inputs failed to initialize.
+                         * One of the two simply is not in use then. */
+                        else if (backendCfg.cMaxHstStrmsIn == 1)
+                            fWarn = !fValidLineIn && !fValidMicIn;
+                        /* Don't warn if our backend is not able of supporting any input streams at all. */
+                    }
+
+                    if (   !fWarn
+                        && backendCfg.cMaxHstStrmsOut)
+                    {
+                        fWarn = !fValidOut;
+                    }
+                }
+                else
+                    AssertReleaseMsgFailed(("Unable to retrieve audio backend configuration for LUN #%RU8, rc=%Rrc\n",
+                                            pDrv->uLUN, rc2));
+
+                if (fWarn)
+                {
+                    char   szMissingStreams[255];
+                    size_t len = 0;
+                    if (!fValidLineIn)
+                    {
+                        LogRel(("AC97: WARNING: Unable to open PCM line input for LUN #%RU8!\n", pDrv->uLUN));
+                        len = RTStrPrintf(szMissingStreams, sizeof(szMissingStreams), "PCM Input");
+                    }
+                    if (!fValidMicIn)
+                    {
+                        LogRel(("AC97: WARNING: Unable to open PCM microphone input for LUN #%RU8!\n", pDrv->uLUN));
+                        len += RTStrPrintf(szMissingStreams + len,
+                                           sizeof(szMissingStreams) - len, len ? ", PCM Microphone" : "PCM Microphone");
+                    }
+                    if (!fValidOut)
+                    {
+                        LogRel(("AC97: WARNING: Unable to open PCM output for LUN #%RU8!\n", pDrv->uLUN));
+                        len += RTStrPrintf(szMissingStreams + len,
+                                           sizeof(szMissingStreams) - len, len ? ", PCM Output" : "PCM Output");
+                    }
+
+                    PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
+                                               N_("Some AC'97 audio streams (%s) could not be opened. Guest applications generating audio "
+                                                  "output or depending on audio input may hang. Make sure your host audio device "
+                                                  "is working properly. Check the logfile for error messages of the audio "
+                                                  "subsystem"), szMissingStreams);
+                }
+            }
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        pThis->cbReadWriteBuf = _4K; /** @todo Make this configurable. */
+        pThis->pvReadWriteBuf = (uint8_t *)RTMemAllocZ(pThis->cbReadWriteBuf);
+        if (!pThis->pvReadWriteBuf)
+            rc = VERR_NO_MEMORY;
+    }
+
+# ifndef VBOX_WITH_AUDIO_CALLBACKS
+    if (RT_SUCCESS(rc))
+    {
+        /* Start the emulation timer. */
+        rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ichac97Timer, pThis,
+                                    TMTIMER_FLAGS_NO_CRIT_SECT, "DevIchAc97", &pThis->pTimer);
+        AssertRCReturn(rc, rc);
+
+        if (RT_SUCCESS(rc))
+        {
+            pThis->cTimerTicks = TMTimerGetFreq(pThis->pTimer) / uTimerHz;
+            pThis->uTimerTS    = TMTimerGet(pThis->pTimer);
+            LogFunc(("Timer ticks=%RU64 (%RU16 Hz)\n", pThis->cTimerTicks, uTimerHz));
+
+            /* Fire off timer. */
+            TMTimerSet(pThis->pTimer, TMTimerGet(pThis->pTimer) + pThis->cTimerTicks);
+        }
+    }
+# else
+    if (RT_SUCCESS(rc))
+    {
+        PAC97DRIVER pDrv;
+        RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node)
+        {
+            /* Only register primary driver.
+             * The device emulation does the output multiplexing then. */
+            if (!(pDrv->Flags & PDMAUDIODRVFLAG_PRIMARY))
+                continue;
+
+            PDMAUDIOCALLBACK AudioCallbacks[2];
+
+            AC97CALLBACKCTX Ctx = { pThis, pDrv };
+
+            AudioCallbacks[0].enmType     = PDMAUDIOCALLBACKTYPE_INPUT;
+            AudioCallbacks[0].pfnCallback = ac97CallbackInput;
+            AudioCallbacks[0].pvCtx       = &Ctx;
+            AudioCallbacks[0].cbCtx       = sizeof(AC97CALLBACKCTX);
+
+            AudioCallbacks[1].enmType     = PDMAUDIOCALLBACKTYPE_OUTPUT;
+            AudioCallbacks[1].pfnCallback = ac97CallbackOutput;
+            AudioCallbacks[1].pvCtx       = &Ctx;
+            AudioCallbacks[1].cbCtx       = sizeof(AC97CALLBACKCTX);
+
+            rc = pDrv->pConnector->pfnRegisterCallbacks(pDrv->pConnector, AudioCallbacks, RT_ELEMENTS(AudioCallbacks));
+            if (RT_FAILURE(rc))
+                break;
+        }
+    }
+# endif
+
+# ifdef VBOX_WITH_STATISTICS
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Register statistics.
+         */
+        PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer,            STAMTYPE_PROFILE, "/Devices/AC97/Timer",             STAMUNIT_TICKS_PER_CALL, "Profiling ichac97Timer.");
+        PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesRead,        STAMTYPE_COUNTER, "/Devices/AC97/BytesRead"   ,      STAMUNIT_BYTES,          "Bytes read from AC97 emulation.");
+        PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesWritten,     STAMTYPE_COUNTER, "/Devices/AC97/BytesWritten",      STAMUNIT_BYTES,          "Bytes written to AC97 emulation.");
+    }
+# endif
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DeviceICHAC97 =
+{
+    /* u32Version */
+    PDM_DEVREG_VERSION,
+    /* szName */
+    "ichac97",
+    /* szRCMod */
+    "",
+    /* szR0Mod */
+    "",
+    /* pszDescription */
+    "ICH AC'97 Audio Controller",
+    /* fFlags */
+    PDM_DEVREG_FLAGS_DEFAULT_BITS,
+    /* fClass */
+    PDM_DEVREG_CLASS_AUDIO,
+    /* cMaxInstances */
+    1,
+    /* cbInstance */
+    sizeof(AC97STATE),
+    /* pfnConstruct */
+    ichac97Construct,
+    /* pfnDestruct */
+    ichac97Destruct,
+    /* pfnRelocate */
+    NULL,
+    /* pfnMemSetup */
+    NULL,
+    /* pfnPowerOn */
+    NULL,
+    /* pfnReset */
+    ichac97Reset,
+    /* pfnSuspend */
+    NULL,
+    /* pfnResume */
+    NULL,
+    /* pfnAttach */
+    ichac97Attach,
+    /* pfnDetach */
+    ichac97Detach,
+    /* pfnQueryInterface. */
+    NULL,
+    /* pfnInitComplete */
+    NULL,
+    /* pfnPowerOff */
+    NULL,
+    /* pfnSoftReset */
+    NULL,
+    /* u32VersionEnd */
+    PDM_DEVREG_VERSION
+};
+
+#endif /* !IN_RING3 */
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Index: /trunk/src/VBox/Devices/Audio_old/DevIchHda.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DevIchHda.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DevIchHda.cpp	(revision 61413)
@@ -0,0 +1,5076 @@
+/* $Id$ */
+/** @file
+ * DevIchHda - VBox ICH Intel HD Audio Controller.
+ *
+ * Implemented against the specifications found in "High Definition Audio
+ * Specification", Revision 1.0a June 17, 2010, and  "Intel I/O Controller
+ * HUB 6 (ICH6) Family, Datasheet", document number 301473-002.
+ */
+
+/*
+ * Copyright (C) 2006-2016 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                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_HDA
+#include <VBox/log.h>
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdmaudioifs.h>
+#include <VBox/version.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/asm-math.h>
+#include <iprt/list.h>
+#ifdef IN_RING3
+# include <iprt/mem.h>
+# include <iprt/semaphore.h>
+# include <iprt/string.h>
+# include <iprt/uuid.h>
+#endif
+
+#include "VBoxDD.h"
+
+#include "AudioMixBuffer.h"
+#include "AudioMixer.h"
+#include "DevIchHdaCodec.h"
+#include "DrvAudio.h"
+
+
+/*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+//#define HDA_AS_PCI_EXPRESS
+#define VBOX_WITH_INTEL_HDA
+
+#ifdef DEBUG_andy
+/* Enables experimental support for separate mic-in handling.
+   Do not enable this yet for regular builds, as this needs more testing first! */
+//# define VBOX_WITH_HDA_MIC_IN
+#endif
+
+#if defined(VBOX_WITH_HP_HDA)
+/* HP Pavilion dv4t-1300 */
+# define HDA_PCI_VENDOR_ID 0x103c
+# define HDA_PCI_DEVICE_ID 0x30f7
+#elif defined(VBOX_WITH_INTEL_HDA)
+/* Intel HDA controller */
+# define HDA_PCI_VENDOR_ID 0x8086
+# define HDA_PCI_DEVICE_ID 0x2668
+#elif defined(VBOX_WITH_NVIDIA_HDA)
+/* nVidia HDA controller */
+# define HDA_PCI_VENDOR_ID 0x10de
+# define HDA_PCI_DEVICE_ID 0x0ac0
+#else
+# error "Please specify your HDA device vendor/device IDs"
+#endif
+
+/** @todo r=bird: Looking at what the linux driver (accidentally?) does when
+ * updating CORBWP, I belive that the ICH6 datahsheet is wrong and that CORBRP
+ * is read only except for bit 15 like the HDA spec states.
+ *
+ * Btw. the CORBRPRST implementation is incomplete according to both docs (sw
+ * writes 1, hw sets it to 1 (after completion), sw reads 1, sw writes 0). */
+#define BIRD_THINKS_CORBRP_IS_MOSTLY_RO
+
+#define HDA_NREGS           114
+#define HDA_NREGS_SAVED     112
+
+/**
+ *  NB: Register values stored in memory (au32Regs[]) are indexed through
+ *  the HDA_RMX_xxx macros (also HDA_MEM_IND_NAME()). On the other hand, the
+ *  register descriptors in g_aHdaRegMap[] are indexed through the
+ *  HDA_REG_xxx macros (also HDA_REG_IND_NAME()).
+ *
+ *  The au32Regs[] layout is kept unchanged for saved state
+ *  compatibility. */
+
+/* Registers */
+#define HDA_REG_IND_NAME(x)                 HDA_REG_##x
+#define HDA_MEM_IND_NAME(x)                 HDA_RMX_##x
+#define HDA_REG_FIELD_MASK(reg, x)          HDA_##reg##_##x##_MASK
+#define HDA_REG_FIELD_FLAG_MASK(reg, x)     RT_BIT(HDA_##reg##_##x##_SHIFT)
+#define HDA_REG_FIELD_SHIFT(reg, x)         HDA_##reg##_##x##_SHIFT
+#define HDA_REG_IND(pThis, x)               ((pThis)->au32Regs[g_aHdaRegMap[x].mem_idx])
+#define HDA_REG(pThis, x)                   (HDA_REG_IND((pThis), HDA_REG_IND_NAME(x)))
+#define HDA_REG_FLAG_VALUE(pThis, reg, val) (HDA_REG((pThis),reg) & (((HDA_REG_FIELD_FLAG_MASK(reg, val)))))
+
+
+#define HDA_REG_GCAP                0 /* range 0x00-0x01*/
+#define HDA_RMX_GCAP                0
+/* GCAP HDASpec 3.3.2 This macro encodes the following information about HDA in a compact manner:
+ * oss (15:12) - number of output streams supported
+ * iss (11:8)  - number of input streams supported
+ * bss (7:3)   - number of bidirectional streams supported
+ * bds (2:1)   - number of serial data out signals supported
+ * b64sup (0)  - 64 bit addressing supported.
+ */
+#define HDA_MAKE_GCAP(oss, iss, bss, bds, b64sup) \
+    (  (((oss) & 0xF)  << 12)   \
+     | (((iss) & 0xF)  << 8)    \
+     | (((bss) & 0x1F) << 3)    \
+     | (((bds) & 0x3)  << 2)    \
+     | ((b64sup) & 1))
+
+#define HDA_REG_VMIN                1 /* 0x02 */
+#define HDA_RMX_VMIN                1
+
+#define HDA_REG_VMAJ                2 /* 0x03 */
+#define HDA_RMX_VMAJ                2
+
+#define HDA_REG_OUTPAY              3 /* 0x04-0x05 */
+#define HDA_RMX_OUTPAY              3
+
+#define HDA_REG_INPAY               4 /* 0x06-0x07 */
+#define HDA_RMX_INPAY               4
+
+#define HDA_REG_GCTL                5 /* 0x08-0x0B */
+#define HDA_RMX_GCTL                5
+#define HDA_GCTL_RST_SHIFT          0
+#define HDA_GCTL_FSH_SHIFT          1
+#define HDA_GCTL_UR_SHIFT           8
+
+#define HDA_REG_WAKEEN              6 /* 0x0C */
+#define HDA_RMX_WAKEEN              6
+
+#define HDA_REG_STATESTS            7 /* 0x0E */
+#define HDA_RMX_STATESTS            7
+#define HDA_STATES_SCSF             0x7
+
+#define HDA_REG_GSTS                8 /* 0x10-0x11*/
+#define HDA_RMX_GSTS                8
+#define HDA_GSTS_FSH_SHIFT          1
+
+#define HDA_REG_OUTSTRMPAY          9  /* 0x18 */
+#define HDA_RMX_OUTSTRMPAY          112
+
+#define HDA_REG_INSTRMPAY           10 /* 0x1a */
+#define HDA_RMX_INSTRMPAY           113
+
+#define HDA_REG_INTCTL              11 /* 0x20 */
+#define HDA_RMX_INTCTL              9
+#define HDA_INTCTL_GIE_SHIFT        31
+#define HDA_INTCTL_CIE_SHIFT        30
+#define HDA_INTCTL_S0_SHIFT         0
+#define HDA_INTCTL_S1_SHIFT         1
+#define HDA_INTCTL_S2_SHIFT         2
+#define HDA_INTCTL_S3_SHIFT         3
+#define HDA_INTCTL_S4_SHIFT         4
+#define HDA_INTCTL_S5_SHIFT         5
+#define HDA_INTCTL_S6_SHIFT         6
+#define HDA_INTCTL_S7_SHIFT         7
+#define INTCTL_SX(pThis, X)         (HDA_REG_FLAG_VALUE((pThis), INTCTL, S##X))
+
+#define HDA_REG_INTSTS              12 /* 0x24 */
+#define HDA_RMX_INTSTS              10
+#define HDA_INTSTS_GIS_SHIFT        31
+#define HDA_INTSTS_CIS_SHIFT        30
+#define HDA_INTSTS_S0_SHIFT         0
+#define HDA_INTSTS_S1_SHIFT         1
+#define HDA_INTSTS_S2_SHIFT         2
+#define HDA_INTSTS_S3_SHIFT         3
+#define HDA_INTSTS_S4_SHIFT         4
+#define HDA_INTSTS_S5_SHIFT         5
+#define HDA_INTSTS_S6_SHIFT         6
+#define HDA_INTSTS_S7_SHIFT         7
+#define HDA_INTSTS_S_MASK(num)      RT_BIT(HDA_REG_FIELD_SHIFT(S##num))
+
+#define HDA_REG_WALCLK              13 /* 0x24 */
+#define HDA_RMX_WALCLK              /* Not defined! */
+
+/* Note: The HDA specification defines a SSYNC register at offset 0x38. The
+ * ICH6/ICH9 datahseet defines SSYNC at offset 0x34. The Linux HDA driver matches
+ * the datasheet.
+ */
+#define HDA_REG_SSYNC               14 /* 0x34 */
+#define HDA_RMX_SSYNC               12
+
+#define HDA_REG_CORBLBASE           15 /* 0x40 */
+#define HDA_RMX_CORBLBASE           13
+
+#define HDA_REG_CORBUBASE           16 /* 0x44 */
+#define HDA_RMX_CORBUBASE           14
+
+#define HDA_REG_CORBWP              17 /* 0x48 */
+#define HDA_RMX_CORBWP              15
+
+#define HDA_REG_CORBRP              18 /* 0x4A */
+#define HDA_RMX_CORBRP              16
+#define HDA_CORBRP_RST_SHIFT        15
+#define HDA_CORBRP_WP_SHIFT         0
+#define HDA_CORBRP_WP_MASK          0xFF
+
+#define HDA_REG_CORBCTL             19 /* 0x4C */
+#define HDA_RMX_CORBCTL             17
+#define HDA_CORBCTL_DMA_SHIFT       1
+#define HDA_CORBCTL_CMEIE_SHIFT     0
+
+#define HDA_REG_CORBSTS             20 /* 0x4D */
+#define HDA_RMX_CORBSTS             18
+#define HDA_CORBSTS_CMEI_SHIFT      0
+
+#define HDA_REG_CORBSIZE            21 /* 0x4E */
+#define HDA_RMX_CORBSIZE            19
+#define HDA_CORBSIZE_SZ_CAP         0xF0
+#define HDA_CORBSIZE_SZ             0x3
+/* till ich 10 sizes of CORB and RIRB are hardcoded to 256 in real hw */
+
+#define HDA_REG_RIRBLBASE           22 /* 0x50 */
+#define HDA_RMX_RIRBLBASE           20
+
+#define HDA_REG_RIRBUBASE           23 /* 0x54 */
+#define HDA_RMX_RIRBUBASE           21
+
+#define HDA_REG_RIRBWP              24 /* 0x58 */
+#define HDA_RMX_RIRBWP              22
+#define HDA_RIRBWP_RST_SHIFT        15
+#define HDA_RIRBWP_WP_MASK          0xFF
+
+#define HDA_REG_RINTCNT             25 /* 0x5A */
+#define HDA_RMX_RINTCNT             23
+#define RINTCNT_N(pThis)            (HDA_REG(pThis, RINTCNT) & 0xff)
+
+#define HDA_REG_RIRBCTL             26 /* 0x5C */
+#define HDA_RMX_RIRBCTL             24
+#define HDA_RIRBCTL_RIC_SHIFT       0
+#define HDA_RIRBCTL_DMA_SHIFT       1
+#define HDA_ROI_DMA_SHIFT           2
+
+#define HDA_REG_RIRBSTS             27 /* 0x5D */
+#define HDA_RMX_RIRBSTS             25
+#define HDA_RIRBSTS_RINTFL_SHIFT    0
+#define HDA_RIRBSTS_RIRBOIS_SHIFT   2
+
+#define HDA_REG_RIRBSIZE            28 /* 0x5E */
+#define HDA_RMX_RIRBSIZE            26
+#define HDA_RIRBSIZE_SZ_CAP         0xF0
+#define HDA_RIRBSIZE_SZ             0x3
+
+#define RIRBSIZE_SZ(pThis)          (HDA_REG(pThis, HDA_REG_RIRBSIZE) & HDA_RIRBSIZE_SZ)
+#define RIRBSIZE_SZ_CAP(pThis)      (HDA_REG(pThis, HDA_REG_RIRBSIZE) & HDA_RIRBSIZE_SZ_CAP)
+
+
+#define HDA_REG_IC                  29 /* 0x60 */
+#define HDA_RMX_IC                  27
+
+#define HDA_REG_IR                  30 /* 0x64 */
+#define HDA_RMX_IR                  28
+
+#define HDA_REG_IRS                 31 /* 0x68 */
+#define HDA_RMX_IRS                 29
+#define HDA_IRS_ICB_SHIFT           0
+#define HDA_IRS_IRV_SHIFT           1
+
+#define HDA_REG_DPLBASE             32 /* 0x70 */
+#define HDA_RMX_DPLBASE             30
+#define DPLBASE(pThis)              (HDA_REG((pThis), DPLBASE))
+
+#define HDA_REG_DPUBASE             33 /* 0x74 */
+#define HDA_RMX_DPUBASE             31
+#define DPUBASE(pThis)              (HDA_REG((pThis), DPUBASE))
+
+#define DPBASE_ADDR_MASK            (~(uint64_t)0x7f)
+
+#define HDA_STREAM_REG_DEF(name, num)           (HDA_REG_SD##num##name)
+#define HDA_STREAM_RMX_DEF(name, num)           (HDA_RMX_SD##num##name)
+/* Note: sdnum here _MUST_ be stream reg number [0,7]. */
+#define HDA_STREAM_REG(pThis, name, sdnum)      (HDA_REG_IND((pThis), HDA_REG_SD0##name + (sdnum) * 10))
+
+#define HDA_SD_NUM_FROM_REG(pThis, func, reg)   ((reg - HDA_STREAM_REG_DEF(func, 0)) / 10)
+
+#define HDA_REG_SD0CTL              34 /* 0x80 */
+#define HDA_REG_SD1CTL              (HDA_STREAM_REG_DEF(CTL, 0) + 10) /* 0xA0 */
+#define HDA_REG_SD2CTL              (HDA_STREAM_REG_DEF(CTL, 0) + 20) /* 0xC0 */
+#define HDA_REG_SD3CTL              (HDA_STREAM_REG_DEF(CTL, 0) + 30) /* 0xE0 */
+#define HDA_REG_SD4CTL              (HDA_STREAM_REG_DEF(CTL, 0) + 40) /* 0x100 */
+#define HDA_REG_SD5CTL              (HDA_STREAM_REG_DEF(CTL, 0) + 50) /* 0x120 */
+#define HDA_REG_SD6CTL              (HDA_STREAM_REG_DEF(CTL, 0) + 60) /* 0x140 */
+#define HDA_REG_SD7CTL              (HDA_STREAM_REG_DEF(CTL, 0) + 70) /* 0x160 */
+#define HDA_RMX_SD0CTL              32
+#define HDA_RMX_SD1CTL              (HDA_STREAM_RMX_DEF(CTL, 0) + 10)
+#define HDA_RMX_SD2CTL              (HDA_STREAM_RMX_DEF(CTL, 0) + 20)
+#define HDA_RMX_SD3CTL              (HDA_STREAM_RMX_DEF(CTL, 0) + 30)
+#define HDA_RMX_SD4CTL              (HDA_STREAM_RMX_DEF(CTL, 0) + 40)
+#define HDA_RMX_SD5CTL              (HDA_STREAM_RMX_DEF(CTL, 0) + 50)
+#define HDA_RMX_SD6CTL              (HDA_STREAM_RMX_DEF(CTL, 0) + 60)
+#define HDA_RMX_SD7CTL              (HDA_STREAM_RMX_DEF(CTL, 0) + 70)
+
+#define SD(func, num)               SD##num##func
+
+#define HDA_SDCTL(pThis, num)       HDA_REG((pThis), SD(CTL, num))
+#define HDA_SDCTL_NUM(pThis, num)   ((HDA_SDCTL((pThis), num) & HDA_REG_FIELD_MASK(SDCTL,NUM)) >> HDA_REG_FIELD_SHIFT(SDCTL, NUM))
+#define HDA_SDCTL_NUM_MASK          0xF
+#define HDA_SDCTL_NUM_SHIFT         20
+#define HDA_SDCTL_DIR_SHIFT         19
+#define HDA_SDCTL_TP_SHIFT          18
+#define HDA_SDCTL_STRIPE_MASK       0x3
+#define HDA_SDCTL_STRIPE_SHIFT      16
+#define HDA_SDCTL_DEIE_SHIFT        4
+#define HDA_SDCTL_FEIE_SHIFT        3
+#define HDA_SDCTL_ICE_SHIFT         2
+#define HDA_SDCTL_RUN_SHIFT         1
+#define HDA_SDCTL_SRST_SHIFT        0
+
+#define HDA_REG_SD0STS              35 /* 0x83 */
+#define HDA_REG_SD1STS              (HDA_STREAM_REG_DEF(STS, 0) + 10) /* 0xA3 */
+#define HDA_REG_SD2STS              (HDA_STREAM_REG_DEF(STS, 0) + 20) /* 0xC3 */
+#define HDA_REG_SD3STS              (HDA_STREAM_REG_DEF(STS, 0) + 30) /* 0xE3 */
+#define HDA_REG_SD4STS              (HDA_STREAM_REG_DEF(STS, 0) + 40) /* 0x103 */
+#define HDA_REG_SD5STS              (HDA_STREAM_REG_DEF(STS, 0) + 50) /* 0x123 */
+#define HDA_REG_SD6STS              (HDA_STREAM_REG_DEF(STS, 0) + 60) /* 0x143 */
+#define HDA_REG_SD7STS              (HDA_STREAM_REG_DEF(STS, 0) + 70) /* 0x163 */
+#define HDA_RMX_SD0STS              33
+#define HDA_RMX_SD1STS              (HDA_STREAM_RMX_DEF(STS, 0) + 10)
+#define HDA_RMX_SD2STS              (HDA_STREAM_RMX_DEF(STS, 0) + 20)
+#define HDA_RMX_SD3STS              (HDA_STREAM_RMX_DEF(STS, 0) + 30)
+#define HDA_RMX_SD4STS              (HDA_STREAM_RMX_DEF(STS, 0) + 40)
+#define HDA_RMX_SD5STS              (HDA_STREAM_RMX_DEF(STS, 0) + 50)
+#define HDA_RMX_SD6STS              (HDA_STREAM_RMX_DEF(STS, 0) + 60)
+#define HDA_RMX_SD7STS              (HDA_STREAM_RMX_DEF(STS, 0) + 70)
+
+#define SDSTS(pThis, num)           HDA_REG((pThis), SD(STS, num))
+#define HDA_SDSTS_FIFORDY_SHIFT     5
+#define HDA_SDSTS_DE_SHIFT          4
+#define HDA_SDSTS_FE_SHIFT          3
+#define HDA_SDSTS_BCIS_SHIFT        2
+
+#define HDA_REG_SD0LPIB             36 /* 0x84 */
+#define HDA_REG_SD1LPIB             (HDA_STREAM_REG_DEF(LPIB, 0) + 10) /* 0xA4 */
+#define HDA_REG_SD2LPIB             (HDA_STREAM_REG_DEF(LPIB, 0) + 20) /* 0xC4 */
+#define HDA_REG_SD3LPIB             (HDA_STREAM_REG_DEF(LPIB, 0) + 30) /* 0xE4 */
+#define HDA_REG_SD4LPIB             (HDA_STREAM_REG_DEF(LPIB, 0) + 40) /* 0x104 */
+#define HDA_REG_SD5LPIB             (HDA_STREAM_REG_DEF(LPIB, 0) + 50) /* 0x124 */
+#define HDA_REG_SD6LPIB             (HDA_STREAM_REG_DEF(LPIB, 0) + 60) /* 0x144 */
+#define HDA_REG_SD7LPIB             (HDA_STREAM_REG_DEF(LPIB, 0) + 70) /* 0x164 */
+#define HDA_RMX_SD0LPIB             34
+#define HDA_RMX_SD1LPIB             (HDA_STREAM_RMX_DEF(LPIB, 0) + 10)
+#define HDA_RMX_SD2LPIB             (HDA_STREAM_RMX_DEF(LPIB, 0) + 20)
+#define HDA_RMX_SD3LPIB             (HDA_STREAM_RMX_DEF(LPIB, 0) + 30)
+#define HDA_RMX_SD4LPIB             (HDA_STREAM_RMX_DEF(LPIB, 0) + 40)
+#define HDA_RMX_SD5LPIB             (HDA_STREAM_RMX_DEF(LPIB, 0) + 50)
+#define HDA_RMX_SD6LPIB             (HDA_STREAM_RMX_DEF(LPIB, 0) + 60)
+#define HDA_RMX_SD7LPIB             (HDA_STREAM_RMX_DEF(LPIB, 0) + 70)
+
+#define HDA_REG_SD0CBL              37 /* 0x88 */
+#define HDA_REG_SD1CBL              (HDA_STREAM_REG_DEF(CBL, 0) + 10) /* 0xA8 */
+#define HDA_REG_SD2CBL              (HDA_STREAM_REG_DEF(CBL, 0) + 20) /* 0xC8 */
+#define HDA_REG_SD3CBL              (HDA_STREAM_REG_DEF(CBL, 0) + 30) /* 0xE8 */
+#define HDA_REG_SD4CBL              (HDA_STREAM_REG_DEF(CBL, 0) + 40) /* 0x108 */
+#define HDA_REG_SD5CBL              (HDA_STREAM_REG_DEF(CBL, 0) + 50) /* 0x128 */
+#define HDA_REG_SD6CBL              (HDA_STREAM_REG_DEF(CBL, 0) + 60) /* 0x148 */
+#define HDA_REG_SD7CBL              (HDA_STREAM_REG_DEF(CBL, 0) + 70) /* 0x168 */
+#define HDA_RMX_SD0CBL              35
+#define HDA_RMX_SD1CBL              (HDA_STREAM_RMX_DEF(CBL, 0) + 10)
+#define HDA_RMX_SD2CBL              (HDA_STREAM_RMX_DEF(CBL, 0) + 20)
+#define HDA_RMX_SD3CBL              (HDA_STREAM_RMX_DEF(CBL, 0) + 30)
+#define HDA_RMX_SD4CBL              (HDA_STREAM_RMX_DEF(CBL, 0) + 40)
+#define HDA_RMX_SD5CBL              (HDA_STREAM_RMX_DEF(CBL, 0) + 50)
+#define HDA_RMX_SD6CBL              (HDA_STREAM_RMX_DEF(CBL, 0) + 60)
+#define HDA_RMX_SD7CBL              (HDA_STREAM_RMX_DEF(CBL, 0) + 70)
+
+#define HDA_REG_SD0LVI              38 /* 0x8C */
+#define HDA_REG_SD1LVI              (HDA_STREAM_REG_DEF(LVI, 0) + 10) /* 0xAC */
+#define HDA_REG_SD2LVI              (HDA_STREAM_REG_DEF(LVI, 0) + 20) /* 0xCC */
+#define HDA_REG_SD3LVI              (HDA_STREAM_REG_DEF(LVI, 0) + 30) /* 0xEC */
+#define HDA_REG_SD4LVI              (HDA_STREAM_REG_DEF(LVI, 0) + 40) /* 0x10C */
+#define HDA_REG_SD5LVI              (HDA_STREAM_REG_DEF(LVI, 0) + 50) /* 0x12C */
+#define HDA_REG_SD6LVI              (HDA_STREAM_REG_DEF(LVI, 0) + 60) /* 0x14C */
+#define HDA_REG_SD7LVI              (HDA_STREAM_REG_DEF(LVI, 0) + 70) /* 0x16C */
+#define HDA_RMX_SD0LVI              36
+#define HDA_RMX_SD1LVI              (HDA_STREAM_RMX_DEF(LVI, 0) + 10)
+#define HDA_RMX_SD2LVI              (HDA_STREAM_RMX_DEF(LVI, 0) + 20)
+#define HDA_RMX_SD3LVI              (HDA_STREAM_RMX_DEF(LVI, 0) + 30)
+#define HDA_RMX_SD4LVI              (HDA_STREAM_RMX_DEF(LVI, 0) + 40)
+#define HDA_RMX_SD5LVI              (HDA_STREAM_RMX_DEF(LVI, 0) + 50)
+#define HDA_RMX_SD6LVI              (HDA_STREAM_RMX_DEF(LVI, 0) + 60)
+#define HDA_RMX_SD7LVI              (HDA_STREAM_RMX_DEF(LVI, 0) + 70)
+
+#define HDA_REG_SD0FIFOW            39 /* 0x8E */
+#define HDA_REG_SD1FIFOW            (HDA_STREAM_REG_DEF(FIFOW, 0) + 10) /* 0xAE */
+#define HDA_REG_SD2FIFOW            (HDA_STREAM_REG_DEF(FIFOW, 0) + 20) /* 0xCE */
+#define HDA_REG_SD3FIFOW            (HDA_STREAM_REG_DEF(FIFOW, 0) + 30) /* 0xEE */
+#define HDA_REG_SD4FIFOW            (HDA_STREAM_REG_DEF(FIFOW, 0) + 40) /* 0x10E */
+#define HDA_REG_SD5FIFOW            (HDA_STREAM_REG_DEF(FIFOW, 0) + 50) /* 0x12E */
+#define HDA_REG_SD6FIFOW            (HDA_STREAM_REG_DEF(FIFOW, 0) + 60) /* 0x14E */
+#define HDA_REG_SD7FIFOW            (HDA_STREAM_REG_DEF(FIFOW, 0) + 70) /* 0x16E */
+#define HDA_RMX_SD0FIFOW            37
+#define HDA_RMX_SD1FIFOW            (HDA_STREAM_RMX_DEF(FIFOW, 0) + 10)
+#define HDA_RMX_SD2FIFOW            (HDA_STREAM_RMX_DEF(FIFOW, 0) + 20)
+#define HDA_RMX_SD3FIFOW            (HDA_STREAM_RMX_DEF(FIFOW, 0) + 30)
+#define HDA_RMX_SD4FIFOW            (HDA_STREAM_RMX_DEF(FIFOW, 0) + 40)
+#define HDA_RMX_SD5FIFOW            (HDA_STREAM_RMX_DEF(FIFOW, 0) + 50)
+#define HDA_RMX_SD6FIFOW            (HDA_STREAM_RMX_DEF(FIFOW, 0) + 60)
+#define HDA_RMX_SD7FIFOW            (HDA_STREAM_RMX_DEF(FIFOW, 0) + 70)
+
+/*
+ * ICH6 datasheet defined limits for FIFOW values (18.2.38).
+ */
+#define HDA_SDFIFOW_8B              0x2
+#define HDA_SDFIFOW_16B             0x3
+#define HDA_SDFIFOW_32B             0x4
+
+#define HDA_REG_SD0FIFOS            40 /* 0x90 */
+#define HDA_REG_SD1FIFOS            (HDA_STREAM_REG_DEF(FIFOS, 0) + 10) /* 0xB0 */
+#define HDA_REG_SD2FIFOS            (HDA_STREAM_REG_DEF(FIFOS, 0) + 20) /* 0xD0 */
+#define HDA_REG_SD3FIFOS            (HDA_STREAM_REG_DEF(FIFOS, 0) + 30) /* 0xF0 */
+#define HDA_REG_SD4FIFOS            (HDA_STREAM_REG_DEF(FIFOS, 0) + 40) /* 0x110 */
+#define HDA_REG_SD5FIFOS            (HDA_STREAM_REG_DEF(FIFOS, 0) + 50) /* 0x130 */
+#define HDA_REG_SD6FIFOS            (HDA_STREAM_REG_DEF(FIFOS, 0) + 60) /* 0x150 */
+#define HDA_REG_SD7FIFOS            (HDA_STREAM_REG_DEF(FIFOS, 0) + 70) /* 0x170 */
+#define HDA_RMX_SD0FIFOS            38
+#define HDA_RMX_SD1FIFOS            (HDA_STREAM_RMX_DEF(FIFOS, 0) + 10)
+#define HDA_RMX_SD2FIFOS            (HDA_STREAM_RMX_DEF(FIFOS, 0) + 20)
+#define HDA_RMX_SD3FIFOS            (HDA_STREAM_RMX_DEF(FIFOS, 0) + 30)
+#define HDA_RMX_SD4FIFOS            (HDA_STREAM_RMX_DEF(FIFOS, 0) + 40)
+#define HDA_RMX_SD5FIFOS            (HDA_STREAM_RMX_DEF(FIFOS, 0) + 50)
+#define HDA_RMX_SD6FIFOS            (HDA_STREAM_RMX_DEF(FIFOS, 0) + 60)
+#define HDA_RMX_SD7FIFOS            (HDA_STREAM_RMX_DEF(FIFOS, 0) + 70)
+
+/*
+ * ICH6 datasheet defines limits for FIFOS registers (18.2.39)
+ * formula: size - 1
+ * Other values not listed are not supported.
+ */
+#define HDA_SDINFIFO_120B           0x77 /* 8-, 16-, 20-, 24-, 32-bit Input Streams */
+#define HDA_SDINFIFO_160B           0x9F /* 20-, 24-bit Input Streams Streams */
+
+#define HDA_SDONFIFO_16B            0x0F /* 8-, 16-, 20-, 24-, 32-bit Output Streams */
+#define HDA_SDONFIFO_32B            0x1F /* 8-, 16-, 20-, 24-, 32-bit Output Streams */
+#define HDA_SDONFIFO_64B            0x3F /* 8-, 16-, 20-, 24-, 32-bit Output Streams */
+#define HDA_SDONFIFO_128B           0x7F /* 8-, 16-, 20-, 24-, 32-bit Output Streams */
+#define HDA_SDONFIFO_192B           0xBF /* 8-, 16-, 20-, 24-, 32-bit Output Streams */
+#define HDA_SDONFIFO_256B           0xFF /* 20-, 24-bit Output Streams */
+#define SDFIFOS(pThis, num)         HDA_REG((pThis), SD(FIFOS, num))
+
+#define HDA_REG_SD0FMT              41 /* 0x92 */
+#define HDA_REG_SD1FMT              (HDA_STREAM_REG_DEF(FMT, 0) + 10) /* 0xB2 */
+#define HDA_REG_SD2FMT              (HDA_STREAM_REG_DEF(FMT, 0) + 20) /* 0xD2 */
+#define HDA_REG_SD3FMT              (HDA_STREAM_REG_DEF(FMT, 0) + 30) /* 0xF2 */
+#define HDA_REG_SD4FMT              (HDA_STREAM_REG_DEF(FMT, 0) + 40) /* 0x112 */
+#define HDA_REG_SD5FMT              (HDA_STREAM_REG_DEF(FMT, 0) + 50) /* 0x132 */
+#define HDA_REG_SD6FMT              (HDA_STREAM_REG_DEF(FMT, 0) + 60) /* 0x152 */
+#define HDA_REG_SD7FMT              (HDA_STREAM_REG_DEF(FMT, 0) + 70) /* 0x172 */
+#define HDA_RMX_SD0FMT              39
+#define HDA_RMX_SD1FMT              (HDA_STREAM_RMX_DEF(FMT, 0) + 10)
+#define HDA_RMX_SD2FMT              (HDA_STREAM_RMX_DEF(FMT, 0) + 20)
+#define HDA_RMX_SD3FMT              (HDA_STREAM_RMX_DEF(FMT, 0) + 30)
+#define HDA_RMX_SD4FMT              (HDA_STREAM_RMX_DEF(FMT, 0) + 40)
+#define HDA_RMX_SD5FMT              (HDA_STREAM_RMX_DEF(FMT, 0) + 50)
+#define HDA_RMX_SD6FMT              (HDA_STREAM_RMX_DEF(FMT, 0) + 60)
+#define HDA_RMX_SD7FMT              (HDA_STREAM_RMX_DEF(FMT, 0) + 70)
+
+#define SDFMT(pThis, num)           (HDA_REG((pThis), SD(FMT, num)))
+#define HDA_SDFMT_BASE_RATE_SHIFT   14
+#define HDA_SDFMT_MULT_SHIFT        11
+#define HDA_SDFMT_MULT_MASK         0x7
+#define HDA_SDFMT_DIV_SHIFT         8
+#define HDA_SDFMT_DIV_MASK          0x7
+#define HDA_SDFMT_BITS_SHIFT        4
+#define HDA_SDFMT_BITS_MASK         0x7
+#define SDFMT_BASE_RATE(pThis, num) ((SDFMT(pThis, num) & HDA_REG_FIELD_FLAG_MASK(SDFMT, BASE_RATE)) >> HDA_REG_FIELD_SHIFT(SDFMT, BASE_RATE))
+#define SDFMT_MULT(pThis, num)      ((SDFMT((pThis), num) & HDA_REG_FIELD_MASK(SDFMT,MULT)) >> HDA_REG_FIELD_SHIFT(SDFMT, MULT))
+#define SDFMT_DIV(pThis, num)       ((SDFMT((pThis), num) & HDA_REG_FIELD_MASK(SDFMT,DIV)) >> HDA_REG_FIELD_SHIFT(SDFMT, DIV))
+
+#define HDA_REG_SD0BDPL             42 /* 0x98 */
+#define HDA_REG_SD1BDPL             (HDA_STREAM_REG_DEF(BDPL, 0) + 10) /* 0xB8 */
+#define HDA_REG_SD2BDPL             (HDA_STREAM_REG_DEF(BDPL, 0) + 20) /* 0xD8 */
+#define HDA_REG_SD3BDPL             (HDA_STREAM_REG_DEF(BDPL, 0) + 30) /* 0xF8 */
+#define HDA_REG_SD4BDPL             (HDA_STREAM_REG_DEF(BDPL, 0) + 40) /* 0x118 */
+#define HDA_REG_SD5BDPL             (HDA_STREAM_REG_DEF(BDPL, 0) + 50) /* 0x138 */
+#define HDA_REG_SD6BDPL             (HDA_STREAM_REG_DEF(BDPL, 0) + 60) /* 0x158 */
+#define HDA_REG_SD7BDPL             (HDA_STREAM_REG_DEF(BDPL, 0) + 70) /* 0x178 */
+#define HDA_RMX_SD0BDPL             40
+#define HDA_RMX_SD1BDPL             (HDA_STREAM_RMX_DEF(BDPL, 0) + 10)
+#define HDA_RMX_SD2BDPL             (HDA_STREAM_RMX_DEF(BDPL, 0) + 20)
+#define HDA_RMX_SD3BDPL             (HDA_STREAM_RMX_DEF(BDPL, 0) + 30)
+#define HDA_RMX_SD4BDPL             (HDA_STREAM_RMX_DEF(BDPL, 0) + 40)
+#define HDA_RMX_SD5BDPL             (HDA_STREAM_RMX_DEF(BDPL, 0) + 50)
+#define HDA_RMX_SD6BDPL             (HDA_STREAM_RMX_DEF(BDPL, 0) + 60)
+#define HDA_RMX_SD7BDPL             (HDA_STREAM_RMX_DEF(BDPL, 0) + 70)
+
+#define HDA_REG_SD0BDPU             43 /* 0x9C */
+#define HDA_REG_SD1BDPU             (HDA_STREAM_REG_DEF(BDPU, 0) + 10) /* 0xBC */
+#define HDA_REG_SD2BDPU             (HDA_STREAM_REG_DEF(BDPU, 0) + 20) /* 0xDC */
+#define HDA_REG_SD3BDPU             (HDA_STREAM_REG_DEF(BDPU, 0) + 30) /* 0xFC */
+#define HDA_REG_SD4BDPU             (HDA_STREAM_REG_DEF(BDPU, 0) + 40) /* 0x11C */
+#define HDA_REG_SD5BDPU             (HDA_STREAM_REG_DEF(BDPU, 0) + 50) /* 0x13C */
+#define HDA_REG_SD6BDPU             (HDA_STREAM_REG_DEF(BDPU, 0) + 60) /* 0x15C */
+#define HDA_REG_SD7BDPU             (HDA_STREAM_REG_DEF(BDPU, 0) + 70) /* 0x17C */
+#define HDA_RMX_SD0BDPU             41
+#define HDA_RMX_SD1BDPU             (HDA_STREAM_RMX_DEF(BDPU, 0) + 10)
+#define HDA_RMX_SD2BDPU             (HDA_STREAM_RMX_DEF(BDPU, 0) + 20)
+#define HDA_RMX_SD3BDPU             (HDA_STREAM_RMX_DEF(BDPU, 0) + 30)
+#define HDA_RMX_SD4BDPU             (HDA_STREAM_RMX_DEF(BDPU, 0) + 40)
+#define HDA_RMX_SD5BDPU             (HDA_STREAM_RMX_DEF(BDPU, 0) + 50)
+#define HDA_RMX_SD6BDPU             (HDA_STREAM_RMX_DEF(BDPU, 0) + 60)
+#define HDA_RMX_SD7BDPU             (HDA_STREAM_RMX_DEF(BDPU, 0) + 70)
+
+#define HDA_CODEC_CAD_SHIFT         28
+/* Encodes the (required) LUN into a codec command. */
+#define HDA_CODEC_CMD(cmd, lun)     ((cmd) | (lun << HDA_CODEC_CAD_SHIFT))
+
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+
+/**
+ * Internal state of a Buffer Descriptor List Entry (BDLE),
+ * needed to keep track of the data needed for the actual device
+ * emulation.
+ */
+typedef struct HDABDLESTATE
+{
+    /** Own index within the BDL (Buffer Descriptor List). */
+    uint32_t     u32BDLIndex;
+    /** Number of bytes below the stream's FIFO watermark (SDFIFOW).
+     *  Used to check if we need fill up the FIFO again. */
+    uint32_t     cbBelowFIFOW;
+    /** The buffer descriptor's internal DMA buffer. */
+    uint8_t      au8FIFO[HDA_SDONFIFO_256B + 1];
+    /** Current offset in DMA buffer (in bytes).*/
+    uint32_t     u32BufOff;
+    uint32_t     Padding;
+} HDABDLESTATE, *PHDABDLESTATE;
+
+/**
+ * Buffer Descriptor List Entry (BDLE) (3.6.3).
+ *
+ * Contains only register values which do *not* change until a
+ * stream reset occurs.
+ */
+typedef struct HDABDLE
+{
+    /** Starting address of the actual buffer. Must be 128-bit aligned. */
+    uint64_t     u64BufAdr;
+    /** Size of the actual buffer (in bytes). */
+    uint32_t     u32BufSize;
+    /** Interrupt on completion; the controller will generate
+     *  an interrupt when the last byte of the buffer has been
+     *  fetched by the DMA engine. */
+    bool         fIntOnCompletion;
+    /** Internal state of this BDLE.
+     *  Not part of the actual BDLE registers. */
+    HDABDLESTATE State;
+} HDABDLE, *PHDABDLE;
+
+/**
+ * Internal state of a HDA stream.
+ */
+typedef struct HDASTREAMSTATE
+{
+    /** Current BDLE to use. Wraps around to 0 if
+     *  maximum (cBDLE) is reached. */
+    uint16_t            uCurBDLE;
+    /** Stop indicator. */
+    volatile bool       fDoStop;
+    /** Flag indicating whether this stream is in an
+     *  active (operative) state or not. */
+    volatile bool       fActive;
+    /** Flag indicating whether this stream currently is
+     *  in reset mode and therefore not acccessible by the guest. */
+    volatile bool       fInReset;
+    /** Unused, padding. */
+    bool                fPadding;
+    /** Event signalling that the stream's state has been changed. */
+    RTSEMEVENT          hStateChangedEvent;
+    /** Current BDLE (Buffer Descriptor List Entry). */
+    HDABDLE             BDLE;
+} HDASTREAMSTATE, *PHDASTREAMSTATE;
+
+/**
+ * Structure for keeping a HDA stream state.
+ *
+ * Contains only register values which do *not* change until a
+ * stream reset occurs.
+ */
+typedef struct HDASTREAM
+{
+    /** Stream number (SDn). */
+    uint8_t        u8Strm;
+    uint8_t        Padding0[7];
+    /** DMA base address (SDnBDPU - SDnBDPL). */
+    uint64_t       u64BDLBase;
+    /** Cyclic Buffer Length (SDnCBL).
+     *  Represents the size of the ring buffer. */
+    uint32_t       u32CBL;
+    /** Format (SDnFMT). */
+    uint16_t       u16FMT;
+    /** FIFO Size (FIFOS).
+     *  Maximum number of bytes that may have been DMA'd into
+     *  memory but not yet transmitted on the link.
+     *
+     *  Must be a power of two. */
+    uint16_t       u16FIFOS;
+    /** Last Valid Index (SDnLVI). */
+    uint16_t       u16LVI;
+    uint16_t       Padding1[3];
+    /** Internal state of this stream. */
+    HDASTREAMSTATE State;
+} HDASTREAM, *PHDASTREAM;
+
+typedef struct HDAINPUTSTREAM
+{
+    /** PCM line input stream. */
+    R3PTRTYPE(PPDMAUDIOGSTSTRMIN)      pStrmIn;
+    /** Mixer handle for line input stream. */
+    R3PTRTYPE(PAUDMIXSTREAM)           phStrmIn;
+} HDAINPUTSTREAM, *PHDAINPUTSTREAM;
+
+typedef struct HDAOUTPUTSTREAM
+{
+    /** PCM output stream. */
+    R3PTRTYPE(PPDMAUDIOGSTSTRMOUT)     pStrmOut;
+    /** Mixer handle for line output stream. */
+    R3PTRTYPE(PAUDMIXSTREAM)           phStrmOut;
+} HDAOUTPUTSTREAM, *PHDAOUTPUTSTREAM;
+
+/**
+ * Struct for maintaining a host backend driver.
+ * This driver must be associated to one, and only one,
+ * HDA codec. The HDA controller does the actual multiplexing
+ * of HDA codec data to various host backend drivers then.
+ *
+ * This HDA device uses a timer in order to synchronize all
+ * read/write accesses across all attached LUNs / backends.
+ */
+typedef struct HDADRIVER
+{
+    /** Node for storing this driver in our device driver list of HDASTATE. */
+    RTLISTNODER3                       Node;
+    /** Pointer to HDA controller (state). */
+    R3PTRTYPE(PHDASTATE)               pHDAState;
+    /** Driver flags. */
+    PDMAUDIODRVFLAGS                   Flags;
+    uint8_t                            u32Padding0[2];
+    /** LUN to which this driver has been assigned. */
+    uint8_t                            uLUN;
+    /** Whether this driver is in an attached state or not. */
+    bool                               fAttached;
+    /** Pointer to attached driver base interface. */
+    R3PTRTYPE(PPDMIBASE)               pDrvBase;
+    /** Audio connector interface to the underlying host backend. */
+    R3PTRTYPE(PPDMIAUDIOCONNECTOR)     pConnector;
+    /** Stream for line input. */
+    HDAINPUTSTREAM                     LineIn;
+    /** Stream for mic input. */
+    HDAINPUTSTREAM                     MicIn;
+    /** Stream for output. */
+    HDAOUTPUTSTREAM                    Out;
+} HDADRIVER;
+
+/**
+ * ICH Intel HD Audio Controller state.
+ */
+typedef struct HDASTATE
+{
+    /** The PCI device structure. */
+    PCIDevice                          PciDev;
+    /** R3 Pointer to the device instance. */
+    PPDMDEVINSR3                       pDevInsR3;
+    /** R0 Pointer to the device instance. */
+    PPDMDEVINSR0                       pDevInsR0;
+    /** R0 Pointer to the device instance. */
+    PPDMDEVINSRC                       pDevInsRC;
+    /** Padding for alignment. */
+    uint32_t                           u32Padding;
+    /** The base interface for LUN\#0. */
+    PDMIBASE                           IBase;
+    RTGCPHYS                           MMIOBaseAddr;
+    /** The HDA's register set. */
+    uint32_t                           au32Regs[HDA_NREGS];
+    /** Stream state for line-in. */
+    HDASTREAM                          StrmStLineIn;
+    /** Stream state for microphone-in. */
+    HDASTREAM                          StrmStMicIn;
+    /** Stream state for output. */
+    HDASTREAM                          StrmStOut;
+    /** CORB buffer base address. */
+    uint64_t                           u64CORBBase;
+    /** RIRB buffer base address. */
+    uint64_t                           u64RIRBBase;
+    /** DMA base address.
+     *  Made out of DPLBASE + DPUBASE (3.3.32 + 3.3.33). */
+    uint64_t                           u64DPBase;
+    /** DMA position buffer enable bit. */
+    bool                               fDMAPosition;
+    /** Padding for alignment. */
+    uint8_t                            u32Padding0[7];
+    /** Pointer to CORB buffer. */
+    R3PTRTYPE(uint32_t *)              pu32CorbBuf;
+    /** Size in bytes of CORB buffer. */
+    uint32_t                           cbCorbBuf;
+    /** Padding for alignment. */
+    uint32_t                           u32Padding1;
+    /** Pointer to RIRB buffer. */
+    R3PTRTYPE(uint64_t *)              pu64RirbBuf;
+    /** Size in bytes of RIRB buffer. */
+    uint32_t                           cbRirbBuf;
+    /** Indicates if HDA is in reset. */
+    bool                               fInReset;
+    /** Flag whether the R0 part is enabled. */
+    bool                               fR0Enabled;
+    /** Flag whether the RC part is enabled. */
+    bool                               fRCEnabled;
+#ifndef VBOX_WITH_AUDIO_CALLBACKS
+    /** The timer for pumping data thru the attached LUN drivers. */
+    PTMTIMERR3                         pTimer;
+    /** The timer interval for pumping data thru the LUN drivers in timer ticks. */
+    uint64_t                           cTimerTicks;
+    /** Timestamp of the last timer callback (hdaTimer).
+     * Used to calculate the time actually elapsed between two timer callbacks. */
+    uint64_t                           uTimerTS;
+#endif
+#ifdef VBOX_WITH_STATISTICS
+# ifndef VBOX_WITH_AUDIO_CALLBACKS
+    STAMPROFILE                        StatTimer;
+# endif
+    STAMCOUNTER                        StatBytesRead;
+    STAMCOUNTER                        StatBytesWritten;
+#endif
+    /** Pointer to HDA codec to use. */
+    R3PTRTYPE(PHDACODEC)               pCodec;
+    /** List of associated LUN drivers (HDADRIVER). */
+    RTLISTANCHORR3                     lstDrv;
+    /** The device' software mixer. */
+    R3PTRTYPE(PAUDIOMIXER)             pMixer;
+    /** Audio sink for PCM output. */
+    R3PTRTYPE(PAUDMIXSINK)             pSinkOutput;
+    /** Audio mixer sink for line input. */
+    R3PTRTYPE(PAUDMIXSINK)             pSinkLineIn;
+    /** Audio mixer sink for microphone input. */
+    R3PTRTYPE(PAUDMIXSINK)             pSinkMicIn;
+    uint64_t                           u64BaseTS;
+    /** Response Interrupt Count (RINTCNT). */
+    uint8_t                            u8RespIntCnt;
+    /** Padding for alignment. */
+    uint8_t                            au8Padding2[7];
+} HDASTATE;
+/** Pointer to the ICH Intel HD Audio Controller state. */
+typedef HDASTATE *PHDASTATE;
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+typedef struct HDACALLBACKCTX
+{
+    PHDASTATE  pThis;
+    PHDADRIVER pDriver;
+} HDACALLBACKCTX, *PHDACALLBACKCTX;
+#endif
+
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+static FNPDMDEVRESET hdaReset;
+
+/*
+ * Stubs.
+ */
+static int hdaRegReadUnimpl(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value);
+static int hdaRegWriteUnimpl(PHDASTATE pThis, uint32_t iReg, uint32_t pu32Value);
+
+/*
+ * Global register set read/write functions.
+ */
+static int hdaRegWriteGCTL(PHDASTATE pThis, uint32_t iReg, uint32_t pu32Value);
+static int hdaRegReadINTSTS(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value);
+static int hdaRegReadLPIB(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value);
+static int hdaRegReadWALCLK(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value);
+static int hdaRegWriteINTSTS(PHDASTATE pThis, uint32_t iReg, uint32_t pu32Value);
+static int hdaRegWriteCORBWP(PHDASTATE pThis, uint32_t iReg, uint32_t pu32Value);
+static int hdaRegWriteCORBRP(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int hdaRegWriteCORBCTL(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int hdaRegWriteCORBSTS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int hdaRegWriteRIRBWP(PHDASTATE pThis, uint32_t iReg, uint32_t pu32Value);
+static int hdaRegWriteRIRBSTS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int hdaRegWriteSTATESTS(PHDASTATE pThis, uint32_t iReg, uint32_t pu32Value);
+static int hdaRegWriteIRS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int hdaRegReadIRS(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value);
+static int hdaRegWriteBase(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+
+/*
+ * {IOB}SDn read/write functions.
+ */
+static int  hdaRegWriteSDCBL(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int  hdaRegWriteSDCTL(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int  hdaRegWriteSDSTS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int  hdaRegWriteSDLVI(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int  hdaRegWriteSDFIFOW(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int  hdaRegWriteSDFIFOS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int  hdaRegWriteSDFMT(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int  hdaRegWriteSDBDPL(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+static int  hdaRegWriteSDBDPU(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+inline bool hdaRegWriteSDIsAllowed(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+
+/*
+ * Generic register read/write functions.
+ */
+static int hdaRegReadU32(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value);
+static int hdaRegWriteU32(PHDASTATE pThis, uint32_t iReg, uint32_t pu32Value);
+static int hdaRegReadU24(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value);
+static int hdaRegWriteU24(PHDASTATE pThis, uint32_t iReg, uint32_t pu32Value);
+static int hdaRegReadU16(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value);
+static int hdaRegWriteU16(PHDASTATE pThis, uint32_t iReg, uint32_t pu32Value);
+static int hdaRegReadU8(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value);
+static int hdaRegWriteU8(PHDASTATE pThis, uint32_t iReg, uint32_t pu32Value);
+
+#ifdef IN_RING3
+static void hdaStreamDestroy(PHDASTREAM pStrmSt);
+static int hdaStreamStart(PHDASTREAM pStrmSt);
+static int hdaStreamStop(PHDASTREAM pStrmSt);
+static int hdaStreamWaitForStateChange(PHDASTREAM pStrmSt, RTMSINTERVAL msTimeout);
+static int hdaTransfer(PHDASTATE pThis, ENMSOUNDSOURCE enmSrc, uint32_t cbToProcess, uint32_t *pcbProcessed);
+#endif
+
+#ifdef IN_RING3
+static int       hdaBDLEFetch(PHDASTATE pThis, PHDABDLE pBDLE, uint64_t u64BaseDMA, uint16_t u16Entry);
+DECLINLINE(void) hdaStreamUpdateLPIB(PHDASTATE pThis, PHDASTREAM pStrmSt, uint32_t u32LPIB);
+# ifdef LOG_ENABLED
+static void             hdaBDLEDumpAll(PHDASTATE pThis, uint64_t u64BaseDMA, uint16_t cBDLE);
+# endif
+#endif
+
+
+/*********************************************************************************************************************************
+*   Global Variables                                                                                                             *
+*********************************************************************************************************************************/
+
+/** Offset of the SD0 register map. */
+#define HDA_REG_DESC_SD0_BASE 0x80
+
+/** Turn a short global register name into an memory index and a stringized name. */
+#define HDA_REG_IDX(abbrev)         HDA_MEM_IND_NAME(abbrev), #abbrev
+
+/** Turns a short stream register name into an memory index and a stringized name. */
+#define HDA_REG_IDX_STRM(reg, suff) HDA_MEM_IND_NAME(reg ## suff), #reg #suff
+
+/** Same as above for a register *not* stored in memory. */
+#define HDA_REG_IDX_LOCAL(abbrev)   0, #abbrev
+
+/** Emits a single audio stream register set (e.g. OSD0) at a specified offset. */
+#define HDA_REG_MAP_STRM(offset, name) \
+    /* offset        size     read mask   write mask  read callback   write callback     index + abbrev                  description */ \
+    /* -------       -------  ----------  ----------  --------------  -----------------  ------------------------------  ----------- */ \
+    /* Offset 0x80 (SD0) */ \
+    { offset,        0x00003, 0x00FF001F, 0x00F0001F, hdaRegReadU24 , hdaRegWriteSDCTL , HDA_REG_IDX_STRM(name, CTL)  , #name " Stream Descriptor Control" }, \
+    /* Offset 0x83 (SD0) */ \
+    { offset + 0x3,  0x00001, 0x0000001C, 0x0000003C, hdaRegReadU8  , hdaRegWriteSDSTS , HDA_REG_IDX_STRM(name, STS)  , #name " Status" }, \
+    /* Offset 0x84 (SD0) */ \
+    { offset + 0x4,  0x00004, 0xFFFFFFFF, 0x00000000, hdaRegReadLPIB, hdaRegWriteU32   , HDA_REG_IDX_STRM(name, LPIB) , #name " Link Position In Buffer" }, \
+    /* Offset 0x88 (SD0) */ \
+    { offset + 0x8,  0x00004, 0xFFFFFFFF, 0xFFFFFFFF, hdaRegReadU32, hdaRegWriteSDCBL  , HDA_REG_IDX_STRM(name, CBL)  , #name " Cyclic Buffer Length" }, \
+    /* Offset 0x8C (SD0) */ \
+    { offset + 0xC,  0x00002, 0x0000FFFF, 0x0000FFFF, hdaRegReadU16, hdaRegWriteSDLVI  , HDA_REG_IDX_STRM(name, LVI)  , #name " Last Valid Index" }, \
+    /* Reserved: FIFO Watermark. ** @todo Document this! */ \
+    { offset + 0xE,  0x00002, 0x00000007, 0x00000007, hdaRegReadU16, hdaRegWriteSDFIFOW, HDA_REG_IDX_STRM(name, FIFOW), #name " FIFO Watermark" }, \
+    /* Offset 0x90 (SD0) */ \
+    { offset + 0x10, 0x00002, 0x000000FF, 0x00000000, hdaRegReadU16, hdaRegWriteSDFIFOS, HDA_REG_IDX_STRM(name, FIFOS), #name " FIFO Size" }, \
+    /* Offset 0x92 (SD0) */ \
+    { offset + 0x12, 0x00002, 0x00007F7F, 0x00007F7F, hdaRegReadU16, hdaRegWriteSDFMT  , HDA_REG_IDX_STRM(name, FMT)  , #name " Stream Format" }, \
+    /* Reserved: 0x94 - 0x98. */ \
+    /* Offset 0x98 (SD0) */ \
+    { offset + 0x18, 0x00004, 0xFFFFFF80, 0xFFFFFF80, hdaRegReadU32, hdaRegWriteSDBDPL , HDA_REG_IDX_STRM(name, BDPL) , #name " Buffer Descriptor List Pointer-Lower Base Address" }, \
+    /* Offset 0x9C (SD0) */ \
+    { offset + 0x1C, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, hdaRegReadU32, hdaRegWriteSDBDPU , HDA_REG_IDX_STRM(name, BDPU) , #name " Buffer Descriptor List Pointer-Upper Base Address" }
+
+/** Defines a single audio stream register set (e.g. OSD0). */
+#define HDA_REG_MAP_DEF_STREAM(index, name) \
+    HDA_REG_MAP_STRM(HDA_REG_DESC_SD0_BASE + (index * 32 /* 0x20 */), name)
+
+/* See 302349 p 6.2. */
+static const struct HDAREGDESC
+{
+    /** Register offset in the register space. */
+    uint32_t    offset;
+    /** Size in bytes. Registers of size > 4 are in fact tables. */
+    uint32_t    size;
+    /** Readable bits. */
+    uint32_t    readable;
+    /** Writable bits. */
+    uint32_t    writable;
+    /** Read callback. */
+    int       (*pfnRead)(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value);
+    /** Write callback. */
+    int       (*pfnWrite)(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value);
+    /** Index into the register storage array. */
+    uint32_t    mem_idx;
+    /** Abbreviated name. */
+    const char *abbrev;
+    /** Descripton. */
+    const char *desc;
+} g_aHdaRegMap[HDA_NREGS] =
+
+{
+    /* offset  size     read mask   write mask  read callback            write callback         index + abbrev   */
+    /*-------  -------  ----------  ----------  -----------------------  ---------------------- ---------------- */
+    { 0x00000, 0x00002, 0x0000FFFB, 0x00000000, hdaRegReadU16          , hdaRegWriteUnimpl     , HDA_REG_IDX(GCAP)         }, /* Global Capabilities */
+    { 0x00002, 0x00001, 0x000000FF, 0x00000000, hdaRegReadU8           , hdaRegWriteUnimpl     , HDA_REG_IDX(VMIN)         }, /* Minor Version */
+    { 0x00003, 0x00001, 0x000000FF, 0x00000000, hdaRegReadU8           , hdaRegWriteUnimpl     , HDA_REG_IDX(VMAJ)         }, /* Major Version */
+    { 0x00004, 0x00002, 0x0000FFFF, 0x00000000, hdaRegReadU16          , hdaRegWriteUnimpl     , HDA_REG_IDX(OUTPAY)       }, /* Output Payload Capabilities */
+    { 0x00006, 0x00002, 0x0000FFFF, 0x00000000, hdaRegReadU16          , hdaRegWriteUnimpl     , HDA_REG_IDX(INPAY)        }, /* Input Payload Capabilities */
+    { 0x00008, 0x00004, 0x00000103, 0x00000103, hdaRegReadU32          , hdaRegWriteGCTL       , HDA_REG_IDX(GCTL)         }, /* Global Control */
+    { 0x0000c, 0x00002, 0x00007FFF, 0x00007FFF, hdaRegReadU16          , hdaRegWriteU16        , HDA_REG_IDX(WAKEEN)       }, /* Wake Enable */
+    { 0x0000e, 0x00002, 0x00000007, 0x00000007, hdaRegReadU8           , hdaRegWriteSTATESTS   , HDA_REG_IDX(STATESTS)     }, /* State Change Status */
+    { 0x00010, 0x00002, 0xFFFFFFFF, 0x00000000, hdaRegReadUnimpl       , hdaRegWriteUnimpl     , HDA_REG_IDX(GSTS)         }, /* Global Status */
+    { 0x00018, 0x00002, 0x0000FFFF, 0x00000000, hdaRegReadU16          , hdaRegWriteUnimpl     , HDA_REG_IDX(OUTSTRMPAY)   }, /* Output Stream Payload Capability */
+    { 0x0001A, 0x00002, 0x0000FFFF, 0x00000000, hdaRegReadU16          , hdaRegWriteUnimpl     , HDA_REG_IDX(INSTRMPAY)    }, /* Input Stream Payload Capability */
+    { 0x00020, 0x00004, 0xC00000FF, 0xC00000FF, hdaRegReadU32          , hdaRegWriteU32        , HDA_REG_IDX(INTCTL)       }, /* Interrupt Control */
+    { 0x00024, 0x00004, 0xC00000FF, 0x00000000, hdaRegReadINTSTS       , hdaRegWriteUnimpl     , HDA_REG_IDX(INTSTS)       }, /* Interrupt Status */
+    { 0x00030, 0x00004, 0xFFFFFFFF, 0x00000000, hdaRegReadWALCLK       , hdaRegWriteUnimpl     , HDA_REG_IDX_LOCAL(WALCLK) }, /* Wall Clock Counter */
+    { 0x00034, 0x00004, 0x000000FF, 0x000000FF, hdaRegReadU32          , hdaRegWriteU32        , HDA_REG_IDX(SSYNC)        }, /* Stream Synchronization */
+    { 0x00040, 0x00004, 0xFFFFFF80, 0xFFFFFF80, hdaRegReadU32          , hdaRegWriteBase       , HDA_REG_IDX(CORBLBASE)    }, /* CORB Lower Base Address */
+    { 0x00044, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, hdaRegReadU32          , hdaRegWriteBase       , HDA_REG_IDX(CORBUBASE)    }, /* CORB Upper Base Address */
+    { 0x00048, 0x00002, 0x000000FF, 0x000000FF, hdaRegReadU16          , hdaRegWriteCORBWP     , HDA_REG_IDX(CORBWP)       }, /* CORB Write Pointer */
+    { 0x0004A, 0x00002, 0x000080FF, 0x000080FF, hdaRegReadU16          , hdaRegWriteCORBRP     , HDA_REG_IDX(CORBRP)       }, /* CORB Read Pointer */
+    { 0x0004C, 0x00001, 0x00000003, 0x00000003, hdaRegReadU8           , hdaRegWriteCORBCTL    , HDA_REG_IDX(CORBCTL)      }, /* CORB Control */
+    { 0x0004D, 0x00001, 0x00000001, 0x00000001, hdaRegReadU8           , hdaRegWriteCORBSTS    , HDA_REG_IDX(CORBSTS)      }, /* CORB Status */
+    { 0x0004E, 0x00001, 0x000000F3, 0x00000000, hdaRegReadU8           , hdaRegWriteUnimpl     , HDA_REG_IDX(CORBSIZE)     }, /* CORB Size */
+    { 0x00050, 0x00004, 0xFFFFFF80, 0xFFFFFF80, hdaRegReadU32          , hdaRegWriteBase       , HDA_REG_IDX(RIRBLBASE)    }, /* RIRB Lower Base Address */
+    { 0x00054, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, hdaRegReadU32          , hdaRegWriteBase       , HDA_REG_IDX(RIRBUBASE)    }, /* RIRB Upper Base Address */
+    { 0x00058, 0x00002, 0x000000FF, 0x00008000, hdaRegReadU8           , hdaRegWriteRIRBWP     , HDA_REG_IDX(RIRBWP)       }, /* RIRB Write Pointer */
+    { 0x0005A, 0x00002, 0x000000FF, 0x000000FF, hdaRegReadU16          , hdaRegWriteU16        , HDA_REG_IDX(RINTCNT)      }, /* Response Interrupt Count */
+    { 0x0005C, 0x00001, 0x00000007, 0x00000007, hdaRegReadU8           , hdaRegWriteU8         , HDA_REG_IDX(RIRBCTL)      }, /* RIRB Control */
+    { 0x0005D, 0x00001, 0x00000005, 0x00000005, hdaRegReadU8           , hdaRegWriteRIRBSTS    , HDA_REG_IDX(RIRBSTS)      }, /* RIRB Status */
+    { 0x0005E, 0x00001, 0x000000F3, 0x00000000, hdaRegReadU8           , hdaRegWriteUnimpl     , HDA_REG_IDX(RIRBSIZE)     }, /* RIRB Size */
+    { 0x00060, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, hdaRegReadU32          , hdaRegWriteU32        , HDA_REG_IDX(IC)           }, /* Immediate Command */
+    { 0x00064, 0x00004, 0x00000000, 0xFFFFFFFF, hdaRegReadU32          , hdaRegWriteUnimpl     , HDA_REG_IDX(IR)           }, /* Immediate Response */
+    { 0x00068, 0x00002, 0x00000002, 0x00000002, hdaRegReadIRS          , hdaRegWriteIRS        , HDA_REG_IDX(IRS)          }, /* Immediate Command Status */
+    { 0x00070, 0x00004, 0xFFFFFFFF, 0xFFFFFF81, hdaRegReadU32          , hdaRegWriteBase       , HDA_REG_IDX(DPLBASE)      }, /* DMA Position Lower Base */
+    { 0x00074, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, hdaRegReadU32          , hdaRegWriteBase       , HDA_REG_IDX(DPUBASE)      }, /* DMA Position Upper Base */
+    /* 4 Input Stream Descriptors (ISD). */
+    HDA_REG_MAP_DEF_STREAM(0, SD0),
+    HDA_REG_MAP_DEF_STREAM(1, SD1),
+    HDA_REG_MAP_DEF_STREAM(2, SD2),
+    HDA_REG_MAP_DEF_STREAM(3, SD3),
+    /* 4 Output Stream Descriptors (OSD). */
+    HDA_REG_MAP_DEF_STREAM(4, SD4),
+    HDA_REG_MAP_DEF_STREAM(5, SD5),
+    HDA_REG_MAP_DEF_STREAM(6, SD6),
+    HDA_REG_MAP_DEF_STREAM(7, SD7)
+};
+
+/**
+ * HDA register aliases (HDA spec 3.3.45).
+ * @remarks Sorted by offReg.
+ */
+static const struct
+{
+    /** The alias register offset. */
+    uint32_t    offReg;
+    /** The register index. */
+    int         idxAlias;
+} g_aHdaRegAliases[] =
+{
+    { 0x2084, HDA_REG_SD0LPIB },
+    { 0x20a4, HDA_REG_SD1LPIB },
+    { 0x20c4, HDA_REG_SD2LPIB },
+    { 0x20e4, HDA_REG_SD3LPIB },
+    { 0x2104, HDA_REG_SD4LPIB },
+    { 0x2124, HDA_REG_SD5LPIB },
+    { 0x2144, HDA_REG_SD6LPIB },
+    { 0x2164, HDA_REG_SD7LPIB },
+};
+
+#ifdef IN_RING3
+/** HDABDLE field descriptors for the v6+ saved state. */
+static SSMFIELD const g_aSSMBDLEFields6[] =
+{
+    SSMFIELD_ENTRY(HDABDLE, u64BufAdr),
+    SSMFIELD_ENTRY(HDABDLE, u32BufSize),
+    SSMFIELD_ENTRY(HDABDLE, fIntOnCompletion),
+    SSMFIELD_ENTRY_TERM()
+};
+
+/** HDABDLESTATE field descriptors for the v6+ saved state. */
+static SSMFIELD const g_aSSMBDLEStateFields6[] =
+{
+    SSMFIELD_ENTRY(HDABDLESTATE, u32BDLIndex),
+    SSMFIELD_ENTRY(HDABDLESTATE, cbBelowFIFOW),
+    SSMFIELD_ENTRY(HDABDLESTATE, au8FIFO),
+    SSMFIELD_ENTRY(HDABDLESTATE, u32BufOff),
+    SSMFIELD_ENTRY_TERM()
+};
+
+/** HDASTREAMSTATE field descriptors for the v6+ saved state. */
+static SSMFIELD const g_aSSMStreamStateFields6[] =
+{
+    SSMFIELD_ENTRY_OLD(cBDLE, 2),
+    SSMFIELD_ENTRY(HDASTREAMSTATE, uCurBDLE),
+    SSMFIELD_ENTRY(HDASTREAMSTATE, fDoStop),
+    SSMFIELD_ENTRY(HDASTREAMSTATE, fActive),
+    SSMFIELD_ENTRY(HDASTREAMSTATE, fInReset),
+    SSMFIELD_ENTRY_TERM()
+};
+#endif
+
+/**
+ * 32-bit size indexed masks, i.e. g_afMasks[2 bytes] = 0xffff.
+ */
+static uint32_t const g_afMasks[5] =
+{
+    UINT32_C(0), UINT32_C(0x000000ff), UINT32_C(0x0000ffff), UINT32_C(0x00ffffff), UINT32_C(0xffffffff)
+};
+
+#ifdef IN_RING3
+DECLINLINE(void) hdaStreamUpdateLPIB(PHDASTATE pThis, PHDASTREAM pStrmSt, uint32_t u32LPIB)
+{
+    AssertPtrReturnVoid(pThis);
+    AssertPtrReturnVoid(pStrmSt);
+
+    Assert(u32LPIB <= pStrmSt->u32CBL);
+
+    LogFlowFunc(("[SD%RU8]: LPIB=%RU32 (DMA Position Buffer Enabled: %RTbool)\n",
+                 pStrmSt->u8Strm, u32LPIB, pThis->fDMAPosition));
+
+    /* Update LPIB in any case. */
+    HDA_STREAM_REG(pThis, LPIB, pStrmSt->u8Strm) = u32LPIB;
+
+    /* Do we need to tell the current DMA position? */
+    if (pThis->fDMAPosition)
+    {
+        int rc2 = PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns),
+                                        (pThis->u64DPBase & DPBASE_ADDR_MASK) + (pStrmSt->u8Strm * 2 * sizeof(uint32_t)),
+                                        (void *)&u32LPIB, sizeof(uint32_t));
+        AssertRC(rc2);
+#ifdef DEBUG
+        hdaBDLEDumpAll(pThis, pStrmSt->u64BDLBase, pStrmSt->State.uCurBDLE);
+#endif
+    }
+}
+#endif
+
+/**
+ * Retrieves the number of bytes of a FIFOS register.
+ *
+ * @return Number of bytes of a given FIFOS register.
+ */
+DECLINLINE(uint16_t) hdaSDFIFOSToBytes(uint32_t u32RegFIFOS)
+{
+    uint16_t cb;
+    switch (u32RegFIFOS)
+    {
+        /* Input */
+        case HDA_SDINFIFO_120B: cb = 120; break;
+        case HDA_SDINFIFO_160B: cb = 160; break;
+
+        /* Output */
+        case HDA_SDONFIFO_16B:  cb = 16;  break;
+        case HDA_SDONFIFO_32B:  cb = 32;  break;
+        case HDA_SDONFIFO_64B:  cb = 64;  break;
+        case HDA_SDONFIFO_128B: cb = 128; break;
+        case HDA_SDONFIFO_192B: cb = 192; break;
+        case HDA_SDONFIFO_256B: cb = 256; break;
+        default:
+        {
+            cb = 0; /* Can happen on stream reset. */
+            break;
+        }
+    }
+
+    return cb;
+}
+
+/**
+ * Retrieves the number of bytes of a FIFOW register.
+ *
+ * @return Number of bytes of a given FIFOW register.
+ */
+DECLINLINE(uint8_t) hdaSDFIFOWToBytes(uint32_t u32RegFIFOW)
+{
+    uint32_t cb;
+    switch (u32RegFIFOW)
+    {
+        case HDA_SDFIFOW_8B:  cb = 8;  break;
+        case HDA_SDFIFOW_16B: cb = 16; break;
+        case HDA_SDFIFOW_32B: cb = 32; break;
+        default:              cb = 0;  break;
+    }
+
+#ifdef RT_STRICT
+    Assert(RT_IS_POWER_OF_TWO(cb));
+#endif
+    return cb;
+}
+
+#ifdef IN_RING3
+/**
+ * Fetches the next BDLE to use for a stream.
+ *
+ * @return  IPRT status code.
+ */
+DECLINLINE(int) hdaStreamGetNextBDLE(PHDASTATE pThis, PHDASTREAM pStrmSt)
+{
+    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
+    AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
+
+    NOREF(pThis);
+
+    Assert(pStrmSt->State.uCurBDLE < pStrmSt->u16LVI + 1);
+
+#ifdef DEBUG
+    uint32_t uOldBDLE = pStrmSt->State.uCurBDLE;
+#endif
+
+    /*
+     * Switch to the next BDLE entry and do a wrap around
+     * if we reached the end of the Buffer Descriptor List (BDL).
+     */
+    pStrmSt->State.uCurBDLE++;
+    if (pStrmSt->State.uCurBDLE == pStrmSt->u16LVI + 1)
+    {
+        pStrmSt->State.uCurBDLE = 0;
+
+        hdaStreamUpdateLPIB(pThis, pStrmSt, 0);
+    }
+
+    Assert(pStrmSt->State.uCurBDLE < pStrmSt->u16LVI + 1);
+
+    int rc = hdaBDLEFetch(pThis, &pStrmSt->State.BDLE, pStrmSt->u64BDLBase, pStrmSt->State.uCurBDLE);
+
+#ifdef DEBUG
+    LogFlowFunc(("[SD%RU8]: uOldBDLE=%RU16, uCurBDLE=%RU16, LVI=%RU32, %R[bdle]\n",
+                 pStrmSt->u8Strm, uOldBDLE, pStrmSt->State.uCurBDLE, pStrmSt->u16LVI, &pStrmSt->State.BDLE));
+#endif
+    return rc;
+}
+#endif
+
+DECLINLINE(PHDASTREAM) hdaStreamFromID(PHDASTATE pThis, uint8_t uStreamID)
+{
+    PHDASTREAM pStrmSt;
+
+    switch (uStreamID)
+    {
+        case 0: /** @todo Use dynamic indices, based on stream assignment. */
+        {
+            pStrmSt = &pThis->StrmStLineIn;
+            break;
+        }
+# ifdef VBOX_WITH_HDA_MIC_IN
+        case 2: /** @todo Use dynamic indices, based on stream assignment. */
+        {
+            pStrmSt = &pThis->StrmStMicIn;
+            break;
+        }
+# endif
+        case 4: /** @todo Use dynamic indices, based on stream assignment. */
+        {
+            pStrmSt = &pThis->StrmStOut;
+            break;
+        }
+
+        default:
+        {
+            pStrmSt = NULL;
+            LogFunc(("Warning: Stream with ID=%RU8 not handled\n", uStreamID));
+            break;
+        }
+    }
+
+    return pStrmSt;
+}
+
+/**
+ * Retrieves the minimum number of bytes accumulated/free in the
+ * FIFO before the controller will start a fetch/eviction of data.
+ *
+ * Uses SDFIFOW (FIFO Watermark Register).
+ *
+ * @return Number of bytes accumulated/free in the FIFO.
+ */
+DECLINLINE(uint8_t) hdaStreamGetFIFOW(PHDASTATE pThis, PHDASTREAM pStrmSt)
+{
+    AssertPtrReturn(pThis, 0);
+    AssertPtrReturn(pStrmSt, 0);
+
+#ifdef VBOX_HDA_WITH_FIFO
+    return hdaSDFIFOWToBytes(HDA_STREAM_REG(pThis, FIFOW, pStrmSt->u8Strm));
+#else
+    return 0;
+#endif
+}
+
+static int hdaProcessInterrupt(PHDASTATE pThis)
+{
+#define IS_INTERRUPT_OCCURED_AND_ENABLED(pThis, num) \
+        (   INTCTL_SX((pThis), num) \
+         && (SDSTS(pThis, num) & HDA_REG_FIELD_FLAG_MASK(SDSTS, BCIS)))
+
+    bool fIrq = false;
+
+    if (/* Controller Interrupt Enable (CIE). */
+          HDA_REG_FLAG_VALUE(pThis, INTCTL, CIE)
+       && (   HDA_REG_FLAG_VALUE(pThis, RIRBSTS, RINTFL)
+           || HDA_REG_FLAG_VALUE(pThis, RIRBSTS, RIRBOIS)
+           || (HDA_REG(pThis, STATESTS) & HDA_REG(pThis, WAKEEN))))
+        fIrq = true;
+
+    /** @todo Don't hardcode stream numbers here. */
+    if (   IS_INTERRUPT_OCCURED_AND_ENABLED(pThis, 0)
+        || IS_INTERRUPT_OCCURED_AND_ENABLED(pThis, 4))
+    {
+#ifdef IN_RING3
+        LogFunc(("BCIS\n"));
+#endif
+        fIrq = true;
+    }
+
+    if (HDA_REG_FLAG_VALUE(pThis, INTCTL, GIE))
+    {
+        LogFunc(("%s\n", fIrq ? "Asserted" : "Deasserted"));
+        PDMDevHlpPCISetIrq(pThis->CTX_SUFF(pDevIns), 0 , fIrq);
+    }
+
+#undef IS_INTERRUPT_OCCURED_AND_ENABLED
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Looks up a register at the exact offset given by @a offReg.
+ *
+ * @returns Register index on success, -1 if not found.
+ * @param   pThis               The HDA device state.
+ * @param   offReg              The register offset.
+ */
+static int hdaRegLookup(PHDASTATE pThis, uint32_t offReg)
+{
+    /*
+     * Aliases.
+     */
+    if (offReg >= g_aHdaRegAliases[0].offReg)
+    {
+        for (unsigned i = 0; i < RT_ELEMENTS(g_aHdaRegAliases); i++)
+            if (offReg == g_aHdaRegAliases[i].offReg)
+                return g_aHdaRegAliases[i].idxAlias;
+        Assert(g_aHdaRegMap[RT_ELEMENTS(g_aHdaRegMap) - 1].offset < offReg);
+        return -1;
+    }
+
+    /*
+     * Binary search the
+     */
+    int idxEnd  = RT_ELEMENTS(g_aHdaRegMap);
+    int idxLow  = 0;
+    for (;;)
+    {
+        int idxMiddle = idxLow + (idxEnd - idxLow) / 2;
+        if (offReg < g_aHdaRegMap[idxMiddle].offset)
+        {
+            if (idxLow == idxMiddle)
+                break;
+            idxEnd = idxMiddle;
+        }
+        else if (offReg > g_aHdaRegMap[idxMiddle].offset)
+        {
+            idxLow = idxMiddle + 1;
+            if (idxLow >= idxEnd)
+                break;
+        }
+        else
+            return idxMiddle;
+    }
+
+#ifdef RT_STRICT
+    for (unsigned i = 0; i < RT_ELEMENTS(g_aHdaRegMap); i++)
+        Assert(g_aHdaRegMap[i].offset != offReg);
+#endif
+    return -1;
+}
+
+/**
+ * Looks up a register covering the offset given by @a offReg.
+ *
+ * @returns Register index on success, -1 if not found.
+ * @param   pThis               The HDA device state.
+ * @param   offReg              The register offset.
+ */
+static int hdaRegLookupWithin(PHDASTATE pThis, uint32_t offReg)
+{
+    /*
+     * Aliases.
+     */
+    if (offReg >= g_aHdaRegAliases[0].offReg)
+    {
+        for (unsigned i = 0; i < RT_ELEMENTS(g_aHdaRegAliases); i++)
+        {
+            uint32_t off = offReg - g_aHdaRegAliases[i].offReg;
+            if (off < 4 && off < g_aHdaRegMap[g_aHdaRegAliases[i].idxAlias].size)
+                return g_aHdaRegAliases[i].idxAlias;
+        }
+        Assert(g_aHdaRegMap[RT_ELEMENTS(g_aHdaRegMap) - 1].offset < offReg);
+        return -1;
+    }
+
+    /*
+     * Binary search the register map.
+     */
+    int idxEnd  = RT_ELEMENTS(g_aHdaRegMap);
+    int idxLow  = 0;
+    for (;;)
+    {
+        int idxMiddle = idxLow + (idxEnd - idxLow) / 2;
+        if (offReg < g_aHdaRegMap[idxMiddle].offset)
+        {
+            if (idxLow == idxMiddle)
+                break;
+            idxEnd = idxMiddle;
+        }
+        else if (offReg >= g_aHdaRegMap[idxMiddle].offset + g_aHdaRegMap[idxMiddle].size)
+        {
+            idxLow = idxMiddle + 1;
+            if (idxLow >= idxEnd)
+                break;
+        }
+        else
+            return idxMiddle;
+    }
+
+#ifdef RT_STRICT
+    for (unsigned i = 0; i < RT_ELEMENTS(g_aHdaRegMap); i++)
+        Assert(offReg - g_aHdaRegMap[i].offset >= g_aHdaRegMap[i].size);
+#endif
+    return -1;
+}
+
+#ifdef IN_RING3
+static int hdaCmdSync(PHDASTATE pThis, bool fLocal)
+{
+    int rc = VINF_SUCCESS;
+    if (fLocal)
+    {
+        Assert((HDA_REG_FLAG_VALUE(pThis, CORBCTL, DMA)));
+        rc = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), pThis->u64CORBBase, pThis->pu32CorbBuf, pThis->cbCorbBuf);
+        if (RT_FAILURE(rc))
+            AssertRCReturn(rc, rc);
+#ifdef DEBUG_CMD_BUFFER
+        uint8_t i = 0;
+        do
+        {
+            LogFunc(("CORB%02x: ", i));
+            uint8_t j = 0;
+            do
+            {
+                const char *pszPrefix;
+                if ((i + j) == HDA_REG(pThis, CORBRP));
+                    pszPrefix = "[R]";
+                else if ((i + j) == HDA_REG(pThis, CORBWP));
+                    pszPrefix = "[W]";
+                else
+                    pszPrefix = "   "; /* three spaces */
+                LogFunc(("%s%08x", pszPrefix, pThis->pu32CorbBuf[i + j]));
+                j++;
+            } while (j < 8);
+            LogFunc(("\n"));
+            i += 8;
+        } while(i != 0);
+#endif
+    }
+    else
+    {
+        Assert((HDA_REG_FLAG_VALUE(pThis, RIRBCTL, DMA)));
+        rc = PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), pThis->u64RIRBBase, pThis->pu64RirbBuf, pThis->cbRirbBuf);
+        if (RT_FAILURE(rc))
+            AssertRCReturn(rc, rc);
+#ifdef DEBUG_CMD_BUFFER
+        uint8_t i = 0;
+        do {
+            LogFunc(("RIRB%02x: ", i));
+            uint8_t j = 0;
+            do {
+                const char *prefix;
+                if ((i + j) == HDA_REG(pThis, RIRBWP))
+                    prefix = "[W]";
+                else
+                    prefix = "   ";
+                LogFunc((" %s%016lx", prefix, pThis->pu64RirbBuf[i + j]));
+            } while (++j < 8);
+            LogFunc(("\n"));
+            i += 8;
+        } while (i != 0);
+#endif
+    }
+    return rc;
+}
+
+static int hdaCORBCmdProcess(PHDASTATE pThis)
+{
+    PFNHDACODECVERBPROCESSOR pfn = (PFNHDACODECVERBPROCESSOR)NULL;
+
+    int rc = hdaCmdSync(pThis, true);
+    if (RT_FAILURE(rc))
+        AssertRCReturn(rc, rc);
+
+    uint8_t corbRp = HDA_REG(pThis, CORBRP);
+    uint8_t corbWp = HDA_REG(pThis, CORBWP);
+    uint8_t rirbWp = HDA_REG(pThis, RIRBWP);
+
+    Assert((corbWp != corbRp));
+    LogFlowFunc(("CORB(RP:%x, WP:%x) RIRBWP:%x\n", HDA_REG(pThis, CORBRP), HDA_REG(pThis, CORBWP), HDA_REG(pThis, RIRBWP)));
+
+    while (corbRp != corbWp)
+    {
+        uint32_t cmd;
+        uint64_t resp;
+        pfn = NULL;
+        corbRp++;
+        cmd = pThis->pu32CorbBuf[corbRp];
+
+        rc = pThis->pCodec->pfnLookup(pThis->pCodec, HDA_CODEC_CMD(cmd, 0 /* Codec index */), &pfn);
+        if (RT_SUCCESS(rc))
+        {
+            AssertPtr(pfn);
+            rc = pfn(pThis->pCodec, HDA_CODEC_CMD(cmd, 0 /* LUN */), &resp);
+        }
+
+        if (RT_FAILURE(rc))
+            AssertRCReturn(rc, rc);
+        (rirbWp)++;
+
+        LogFunc(("verb:%08x->%016lx\n", cmd, resp));
+        if (   (resp & CODEC_RESPONSE_UNSOLICITED)
+            && !HDA_REG_FLAG_VALUE(pThis, GCTL, UR))
+        {
+            LogFunc(("unexpected unsolicited response.\n"));
+            HDA_REG(pThis, CORBRP) = corbRp;
+            return rc;
+        }
+
+        pThis->pu64RirbBuf[rirbWp] = resp;
+
+        pThis->u8RespIntCnt++;
+        if (pThis->u8RespIntCnt == RINTCNT_N(pThis))
+            break;
+    }
+    HDA_REG(pThis, CORBRP) = corbRp;
+    HDA_REG(pThis, RIRBWP) = rirbWp;
+    rc = hdaCmdSync(pThis, false);
+    LogFunc(("CORB(RP:%x, WP:%x) RIRBWP:%x\n", HDA_REG(pThis, CORBRP),
+         HDA_REG(pThis, CORBWP), HDA_REG(pThis, RIRBWP)));
+    if (HDA_REG_FLAG_VALUE(pThis, RIRBCTL, RIC))
+    {
+        HDA_REG(pThis, RIRBSTS) |= HDA_REG_FIELD_FLAG_MASK(RIRBSTS,RINTFL);
+
+        pThis->u8RespIntCnt = 0;
+        rc = hdaProcessInterrupt(pThis);
+    }
+    if (RT_FAILURE(rc))
+        AssertRCReturn(rc, rc);
+    return rc;
+}
+
+static int hdaStreamCreate(PHDASTREAM pStrmSt)
+{
+    AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
+
+    int rc = RTSemEventCreate(&pStrmSt->State.hStateChangedEvent);
+    AssertRC(rc);
+
+    pStrmSt->u8Strm         = UINT8_MAX;
+
+    pStrmSt->State.fActive  = false;
+    pStrmSt->State.fInReset = false;
+    pStrmSt->State.fDoStop  = false;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static void hdaStreamDestroy(PHDASTREAM pStrmSt)
+{
+    AssertPtrReturnVoid(pStrmSt);
+
+    LogFlowFunc(("[SD%RU8]: Destroy\n", pStrmSt->u8Strm));
+
+    int rc2 = hdaStreamStop(pStrmSt);
+    AssertRC(rc2);
+
+    if (pStrmSt->State.hStateChangedEvent != NIL_RTSEMEVENT)
+    {
+        rc2 = RTSemEventDestroy(pStrmSt->State.hStateChangedEvent);
+        AssertRC(rc2);
+    }
+
+    LogFlowFuncLeave();
+}
+
+static int hdaStreamInit(PHDASTATE pThis, PHDASTREAM pStrmSt, uint8_t u8Strm)
+{
+    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
+    AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
+
+    pStrmSt->u8Strm     = u8Strm;
+    pStrmSt->u64BDLBase = RT_MAKE_U64(HDA_STREAM_REG(pThis, BDPL, pStrmSt->u8Strm),
+                                      HDA_STREAM_REG(pThis, BDPU, pStrmSt->u8Strm));
+    pStrmSt->u16LVI     = HDA_STREAM_REG(pThis, LVI, pStrmSt->u8Strm);
+    pStrmSt->u32CBL     = HDA_STREAM_REG(pThis, CBL, pStrmSt->u8Strm);
+    pStrmSt->u16FIFOS   = hdaSDFIFOSToBytes(HDA_STREAM_REG(pThis, FIFOS, pStrmSt->u8Strm));
+
+    RT_ZERO(pStrmSt->State.BDLE);
+    pStrmSt->State.uCurBDLE = 0;
+
+    LogFlowFunc(("[SD%RU8]: DMA @ 0x%x (%RU32 bytes), LVI=%RU16, FIFOS=%RU16\n",
+                 pStrmSt->u8Strm, pStrmSt->u64BDLBase, pStrmSt->u32CBL, pStrmSt->u16LVI, pStrmSt->u16FIFOS));
+
+#ifdef DEBUG
+    uint64_t u64BaseDMA = RT_MAKE_U64(HDA_STREAM_REG(pThis, BDPL, u8Strm),
+                                      HDA_STREAM_REG(pThis, BDPU, u8Strm));
+    uint16_t u16LVI     = HDA_STREAM_REG(pThis, LVI, u8Strm);
+    uint32_t u32CBL     = HDA_STREAM_REG(pThis, CBL, u8Strm);
+
+    LogFlowFunc(("\t-> DMA @ 0x%x, LVI=%RU16, CBL=%RU32\n", u64BaseDMA, u16LVI, u32CBL));
+
+    hdaBDLEDumpAll(pThis, u64BaseDMA, u16LVI + 1);
+#endif
+
+    return VINF_SUCCESS;
+}
+
+static void hdaStreamReset(PHDASTATE pThis, PHDASTREAM pStrmSt, uint8_t u8Strm)
+{
+    AssertPtrReturnVoid(pThis);
+    AssertPtrReturnVoid(pStrmSt);
+    AssertReturnVoid(u8Strm <= 7); /** @todo Use a define for MAX_STREAMS! */
+
+#ifdef VBOX_STRICT
+    AssertReleaseMsg(!RT_BOOL(HDA_STREAM_REG(pThis, CTL, u8Strm) & HDA_REG_FIELD_FLAG_MASK(SDCTL, RUN)),
+                     ("Cannot reset stream %RU8 while in running state\n", u8Strm));
+#endif
+
+    /*
+     * Set reset state.
+     */
+    Assert(ASMAtomicReadBool(&pStrmSt->State.fInReset) == false); /* No nested calls. */
+    ASMAtomicXchgBool(&pStrmSt->State.fInReset, true);
+
+    /*
+     * First, reset the internal stream state.
+     */
+    RT_BZERO(pStrmSt, sizeof(HDASTREAM));
+
+    /*
+     * Second, initialize the registers.
+     */
+    HDA_STREAM_REG(pThis, STS,   u8Strm) = 0;
+    /* According to the ICH6 datasheet, 0x40000 is the default value for stream descriptor register 23:20
+     * bits are reserved for stream number 18.2.33, resets SDnCTL except SRST bit. */
+    HDA_STREAM_REG(pThis, CTL,   u8Strm) = 0x40000 | (HDA_STREAM_REG(pThis, CTL, u8Strm) & HDA_REG_FIELD_FLAG_MASK(SDCTL, SRST));
+    /* ICH6 defines default values (0x77 for input and 0xBF for output descriptors) of FIFO size. 18.2.39. */
+    HDA_STREAM_REG(pThis, FIFOS, u8Strm) = u8Strm < 4 ? HDA_SDINFIFO_120B : HDA_SDONFIFO_192B;
+    /* See 18.2.38: Always defaults to 0x4 (32 bytes). */
+    HDA_STREAM_REG(pThis, FIFOW, u8Strm) = HDA_SDFIFOW_32B;
+    HDA_STREAM_REG(pThis, LPIB,  u8Strm) = 0;
+    HDA_STREAM_REG(pThis, CBL,   u8Strm) = 0;
+    HDA_STREAM_REG(pThis, LVI,   u8Strm) = 0;
+    HDA_STREAM_REG(pThis, FMT,   u8Strm) = 0;
+    HDA_STREAM_REG(pThis, BDPU,  u8Strm) = 0;
+    HDA_STREAM_REG(pThis, BDPL,  u8Strm) = 0;
+
+    /*
+     * Third, set the internal state according to the just set registers.
+     */
+    pStrmSt->u8Strm   = u8Strm;
+    pStrmSt->u16FIFOS = HDA_STREAM_REG(pThis, FIFOS, u8Strm);
+
+
+    /* Report that we're done resetting this stream. */
+    HDA_STREAM_REG(pThis, CTL,   u8Strm) = 0;
+
+    LogFunc(("[SD%RU8]: Reset\n", u8Strm));
+
+    /* Exit reset mode. */
+    ASMAtomicXchgBool(&pStrmSt->State.fInReset, false);
+}
+
+static int hdaStreamStart(PHDASTREAM pStrmSt)
+{
+    AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
+
+    ASMAtomicXchgBool(&pStrmSt->State.fDoStop, false);
+    ASMAtomicXchgBool(&pStrmSt->State.fActive, true);
+
+    LogFlowFuncLeave();
+    return VINF_SUCCESS;
+}
+
+static int hdaStreamStop(PHDASTREAM pStrmSt)
+{
+    AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
+
+    /* Already in stopped state? */
+    bool fActive = ASMAtomicReadBool(&pStrmSt->State.fActive);
+    if (!fActive)
+        return VINF_SUCCESS;
+
+#if 0 /** @todo Does not work (yet), as EMT deadlocks then. */
+    /*
+     * Wait for the stream to stop.
+     */
+    ASMAtomicXchgBool(&pStrmSt->State.fDoStop, true);
+
+    int rc = hdaStreamWaitForStateChange(pStrmSt, 60 * 1000 /* ms timeout */);
+    fActive = ASMAtomicReadBool(&pStrmSt->State.fActive);
+    if (   /* Waiting failed? */
+           RT_FAILURE(rc)
+           /* Stream is still active? */
+        || fActive)
+    {
+        AssertRC(rc);
+        LogRel(("HDA: Warning: Unable to stop stream %RU8 (state: %s), rc=%Rrc\n",
+                pStrmSt->u8Strm, fActive ? "active" : "stopped", rc));
+    }
+#else
+    int rc = VINF_SUCCESS;
+#endif
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static int hdaStreamWaitForStateChange(PHDASTREAM pStrmSt, RTMSINTERVAL msTimeout)
+{
+    AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
+
+    LogFlowFunc(("[SD%RU8]: msTimeout=%RU32\n", pStrmSt->u8Strm, msTimeout));
+    return RTSemEventWait(pStrmSt->State.hStateChangedEvent, msTimeout);
+}
+#endif /* IN_RING3 */
+
+/* Register access handlers. */
+
+static int hdaRegReadUnimpl(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value)
+{
+    *pu32Value = 0;
+    return VINF_SUCCESS;
+}
+
+static int hdaRegWriteUnimpl(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    return VINF_SUCCESS;
+}
+
+/* U8 */
+static int hdaRegReadU8(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value)
+{
+    Assert(((pThis->au32Regs[g_aHdaRegMap[iReg].mem_idx] & g_aHdaRegMap[iReg].readable) & 0xffffff00) == 0);
+    return hdaRegReadU32(pThis, iReg, pu32Value);
+}
+
+static int hdaRegWriteU8(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    Assert((u32Value & 0xffffff00) == 0);
+    return hdaRegWriteU32(pThis, iReg, u32Value);
+}
+
+/* U16 */
+static int hdaRegReadU16(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value)
+{
+    Assert(((pThis->au32Regs[g_aHdaRegMap[iReg].mem_idx] & g_aHdaRegMap[iReg].readable) & 0xffff0000) == 0);
+    return hdaRegReadU32(pThis, iReg, pu32Value);
+}
+
+static int hdaRegWriteU16(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    Assert((u32Value & 0xffff0000) == 0);
+    return hdaRegWriteU32(pThis, iReg, u32Value);
+}
+
+/* U24 */
+static int hdaRegReadU24(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value)
+{
+    Assert(((pThis->au32Regs[g_aHdaRegMap[iReg].mem_idx] & g_aHdaRegMap[iReg].readable) & 0xff000000) == 0);
+    return hdaRegReadU32(pThis, iReg, pu32Value);
+}
+
+static int hdaRegWriteU24(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    Assert((u32Value & 0xff000000) == 0);
+    return hdaRegWriteU32(pThis, iReg, u32Value);
+}
+
+/* U32 */
+static int hdaRegReadU32(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value)
+{
+    uint32_t iRegMem = g_aHdaRegMap[iReg].mem_idx;
+
+    *pu32Value = pThis->au32Regs[iRegMem] & g_aHdaRegMap[iReg].readable;
+    return VINF_SUCCESS;
+}
+
+static int hdaRegWriteU32(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    uint32_t iRegMem = g_aHdaRegMap[iReg].mem_idx;
+
+    pThis->au32Regs[iRegMem]  = (u32Value & g_aHdaRegMap[iReg].writable)
+                              | (pThis->au32Regs[iRegMem] & ~g_aHdaRegMap[iReg].writable);
+    return VINF_SUCCESS;
+}
+
+static int hdaRegWriteGCTL(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    if (u32Value & HDA_REG_FIELD_FLAG_MASK(GCTL, RST))
+    {
+        /* Set the CRST bit to indicate that we're leaving reset mode. */
+        HDA_REG(pThis, GCTL) |= HDA_REG_FIELD_FLAG_MASK(GCTL, RST);
+
+        if (pThis->fInReset)
+        {
+            LogFunc(("Leaving reset\n"));
+            pThis->fInReset = false;
+        }
+    }
+    else
+    {
+#ifdef IN_RING3
+        /* Enter reset state. */
+        if (   HDA_REG_FLAG_VALUE(pThis, CORBCTL, DMA)
+            || HDA_REG_FLAG_VALUE(pThis, RIRBCTL, DMA))
+        {
+            LogFunc(("Entering reset with DMA(RIRB:%s, CORB:%s)\n",
+                     HDA_REG_FLAG_VALUE(pThis, CORBCTL, DMA) ? "on" : "off",
+                     HDA_REG_FLAG_VALUE(pThis, RIRBCTL, DMA) ? "on" : "off"));
+        }
+
+        /* Clear the CRST bit to indicate that we're in reset mode. */
+        HDA_REG(pThis, GCTL) &= ~HDA_REG_FIELD_FLAG_MASK(GCTL, RST);
+        pThis->fInReset = true;
+
+        /* As the CRST bit now is set, we now can proceed resetting stuff. */
+        hdaReset(pThis->CTX_SUFF(pDevIns));
+#else
+        return VINF_IOM_R3_MMIO_WRITE;
+#endif
+    }
+    if (u32Value & HDA_REG_FIELD_FLAG_MASK(GCTL, FSH))
+    {
+        /* Flush: GSTS:1 set, see 6.2.6. */
+        HDA_REG(pThis, GSTS) |= HDA_REG_FIELD_FLAG_MASK(GSTS, FSH); /* Set the flush state. */
+        /* DPLBASE and DPUBASE should be initialized with initial value (see 6.2.6). */
+    }
+    return VINF_SUCCESS;
+}
+
+static int hdaRegWriteSTATESTS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    uint32_t iRegMem = g_aHdaRegMap[iReg].mem_idx;
+
+    uint32_t v = pThis->au32Regs[iRegMem];
+    uint32_t nv = u32Value & HDA_STATES_SCSF;
+    pThis->au32Regs[iRegMem] &= ~(v & nv); /* write of 1 clears corresponding bit */
+    return VINF_SUCCESS;
+}
+
+static int hdaRegReadINTSTS(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value)
+{
+    uint32_t v = 0;
+    if (   HDA_REG_FLAG_VALUE(pThis, RIRBSTS, RIRBOIS)
+        || HDA_REG_FLAG_VALUE(pThis, RIRBSTS, RINTFL)
+        || HDA_REG_FLAG_VALUE(pThis, CORBSTS, CMEI)
+        || HDA_REG(pThis, STATESTS))
+    {
+        v |= RT_BIT(30); /* Touch CIS. */
+    }
+
+#define HDA_IS_STREAM_EVENT(pThis, num)                                  \
+       (   (SDSTS((pThis), num) & HDA_REG_FIELD_FLAG_MASK(SDSTS, DE))    \
+        || (SDSTS((pThis), num) & HDA_REG_FIELD_FLAG_MASK(SDSTS, FE))    \
+        || (SDSTS((pThis), num) & HDA_REG_FIELD_FLAG_MASK(SDSTS, BCIS)))
+
+#define HDA_MARK_STREAM(pThis, num, v) \
+        do { (v) |= HDA_IS_STREAM_EVENT((pThis), num) ? RT_BIT((num)) : 0; } while(0)
+
+    HDA_MARK_STREAM(pThis, 0, v);
+    HDA_MARK_STREAM(pThis, 1, v);
+    HDA_MARK_STREAM(pThis, 2, v);
+    HDA_MARK_STREAM(pThis, 3, v);
+    HDA_MARK_STREAM(pThis, 4, v);
+    HDA_MARK_STREAM(pThis, 5, v);
+    HDA_MARK_STREAM(pThis, 6, v);
+    HDA_MARK_STREAM(pThis, 7, v);
+
+#undef HDA_IS_STREAM_EVENT
+#undef HDA_MARK_STREAM
+
+    v |= v ? RT_BIT(31) : 0;
+
+    *pu32Value = v;
+    return VINF_SUCCESS;
+}
+
+static int hdaRegReadLPIB(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value)
+{
+    const uint8_t  u8Strm  = HDA_SD_NUM_FROM_REG(pThis, LPIB, iReg);
+          uint32_t u32LPIB = HDA_STREAM_REG(pThis, LPIB, u8Strm);
+    const uint32_t u32CBL  = HDA_STREAM_REG(pThis, CBL,  u8Strm);
+
+    LogFlowFunc(("[SD%RU8]: LPIB=%RU32, CBL=%RU32\n", u8Strm, u32LPIB, u32CBL));
+
+    *pu32Value = u32LPIB;
+    return VINF_SUCCESS;
+}
+
+static int hdaRegReadWALCLK(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value)
+{
+    /* HDA spec (1a): 3.3.16 WALCLK counter ticks with 24Mhz bitclock rate. */
+    *pu32Value = (uint32_t)ASMMultU64ByU32DivByU32(PDMDevHlpTMTimeVirtGetNano(pThis->CTX_SUFF(pDevIns))
+                                                   - pThis->u64BaseTS, 24, 1000);
+    LogFlowFunc(("%RU32\n", *pu32Value));
+    return VINF_SUCCESS;
+}
+
+static int hdaRegWriteCORBRP(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    if (u32Value & HDA_REG_FIELD_FLAG_MASK(CORBRP, RST))
+    {
+        HDA_REG(pThis, CORBRP) = 0;
+    }
+#ifndef BIRD_THINKS_CORBRP_IS_MOSTLY_RO
+    else
+        return hdaRegWriteU8(pThis, iReg, u32Value);
+#endif
+    return VINF_SUCCESS;
+}
+
+static int hdaRegWriteCORBCTL(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+#ifdef IN_RING3
+    int rc = hdaRegWriteU8(pThis, iReg, u32Value);
+    AssertRC(rc);
+    if (   HDA_REG(pThis, CORBWP)                  != HDA_REG(pThis, CORBRP)
+        && HDA_REG_FLAG_VALUE(pThis, CORBCTL, DMA) != 0)
+    {
+        return hdaCORBCmdProcess(pThis);
+    }
+    return rc;
+#else
+    return VINF_IOM_R3_MMIO_WRITE;
+#endif
+}
+
+static int hdaRegWriteCORBSTS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    uint32_t v = HDA_REG(pThis, CORBSTS);
+    HDA_REG(pThis, CORBSTS) &= ~(v & u32Value);
+    return VINF_SUCCESS;
+}
+
+static int hdaRegWriteCORBWP(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+#ifdef IN_RING3
+    int rc;
+    rc = hdaRegWriteU16(pThis, iReg, u32Value);
+    if (RT_FAILURE(rc))
+        AssertRCReturn(rc, rc);
+    if (HDA_REG(pThis, CORBWP) == HDA_REG(pThis, CORBRP))
+        return VINF_SUCCESS;
+    if (!HDA_REG_FLAG_VALUE(pThis, CORBCTL, DMA))
+        return VINF_SUCCESS;
+    rc = hdaCORBCmdProcess(pThis);
+    return rc;
+#else
+    return VINF_IOM_R3_MMIO_WRITE;
+#endif
+}
+
+static int hdaRegWriteSDCBL(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    int rc = hdaRegWriteU32(pThis, iReg, u32Value);
+    if (RT_SUCCESS(rc))
+    {
+        uint8_t u8Strm  = HDA_SD_NUM_FROM_REG(pThis, CBL, iReg);
+
+        PHDASTREAM pStrmSt = hdaStreamFromID(pThis, u8Strm);
+        if (pStrmSt)
+        {
+            pStrmSt->u32CBL = u32Value;
+
+            /* Reset BDLE state. */
+            RT_ZERO(pStrmSt->State.BDLE);
+            pStrmSt->State.uCurBDLE = 0;
+        }
+
+        LogFlowFunc(("[SD%RU8]: CBL=%RU32\n", u8Strm, u32Value));
+    }
+    else
+        AssertRCReturn(rc, VINF_SUCCESS);
+
+    return rc;
+}
+
+static int hdaRegWriteSDCTL(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    bool fRun      = RT_BOOL(u32Value & HDA_REG_FIELD_FLAG_MASK(SDCTL, RUN));
+    bool fInRun    = RT_BOOL(HDA_REG_IND(pThis, iReg) & HDA_REG_FIELD_FLAG_MASK(SDCTL, RUN));
+    bool fReset    = RT_BOOL(u32Value & HDA_REG_FIELD_FLAG_MASK(SDCTL, SRST));
+    bool fInReset  = RT_BOOL(HDA_REG_IND(pThis, iReg) & HDA_REG_FIELD_FLAG_MASK(SDCTL, SRST));
+
+    uint8_t u8Strm = HDA_SD_NUM_FROM_REG(pThis, CTL, iReg);
+
+    PHDASTREAM pStrmSt = hdaStreamFromID(pThis, u8Strm);
+    if (!pStrmSt)
+    {
+        LogFunc(("Warning: Changing SDCTL on non-attached stream with ID=%RU8 (iReg=0x%x)\n", u8Strm, iReg));
+        return hdaRegWriteU24(pThis, iReg, u32Value); /* Write 3 bytes. */
+    }
+
+    LogFunc(("[SD%RU8]: fRun=%RTbool, fInRun=%RTbool, fReset=%RTbool, fInReset=%RTbool, %R[sdctl]\n",
+             u8Strm, fRun, fInRun, fReset, fInReset, u32Value));
+
+    if (fInReset)
+    {
+        /* Guest is resetting HDA's stream, we're expecting guest will mark stream as exit. */
+        Assert(!fReset);
+        LogFunc(("Guest initiated exit of stream reset\n"));
+    }
+    else if (fReset)
+    {
+#ifdef IN_RING3
+        /* ICH6 datasheet 18.2.33 says that RUN bit should be cleared before initiation of reset. */
+        Assert(!fInRun && !fRun);
+
+        LogFunc(("Guest initiated enter to stream reset\n"));
+        hdaStreamReset(pThis, pStrmSt, u8Strm);
+#else
+        return VINF_IOM_R3_MMIO_WRITE;
+#endif
+    }
+    else
+    {
+#ifdef IN_RING3
+        /*
+         * We enter here to change DMA states only.
+         */
+        if (fInRun != fRun)
+        {
+            Assert(!fReset && !fInReset);
+            LogFunc(("[SD%RU8]: fRun=%RTbool\n", u8Strm, fRun));
+
+            PHDADRIVER pDrv;
+            switch (u8Strm)
+            {
+                case 0: /** @todo Use a variable here. Later. */
+                {
+                    RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+                        pDrv->pConnector->pfnEnableIn(pDrv->pConnector,
+                                                      pDrv->LineIn.pStrmIn, fRun);
+                    break;
+                }
+# ifdef VBOX_WITH_HDA_MIC_IN
+                case 2: /** @todo Use a variable here. Later. */
+                {
+                    RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+                        pDrv->pConnector->pfnEnableIn(pDrv->pConnector,
+                                                      pDrv->MicIn.pStrmIn, fRun);
+                    break;
+                }
+# endif
+                case 4: /** @todo Use a variable here. Later. */
+                {
+                    RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+                        pDrv->pConnector->pfnEnableOut(pDrv->pConnector,
+                                                       pDrv->Out.pStrmOut, fRun);
+                    break;
+                }
+                default:
+                    AssertMsgFailed(("Changing RUN bit on non-attached stream, register %RU32\n", iReg));
+                    break;
+            }
+        }
+
+        if (!fInRun && !fRun)
+            hdaStreamInit(pThis, pStrmSt, u8Strm);
+
+#else /* !IN_RING3 */
+        return VINF_IOM_R3_MMIO_WRITE;
+#endif /* IN_RING3 */
+    }
+
+    return hdaRegWriteU24(pThis, iReg, u32Value);
+}
+
+static int hdaRegWriteSDSTS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    uint32_t v = HDA_REG_IND(pThis, iReg);
+    v &= ~(u32Value & v);
+    HDA_REG_IND(pThis, iReg) = v;
+    hdaProcessInterrupt(pThis);
+    return VINF_SUCCESS;
+}
+
+static int hdaRegWriteSDLVI(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    if (!hdaRegWriteSDIsAllowed(pThis, iReg, u32Value))
+        return VINF_SUCCESS;
+
+    int rc = hdaRegWriteU16(pThis, iReg, u32Value);
+    if (RT_SUCCESS(rc))
+    {
+        uint8_t u8Strm = HDA_SD_NUM_FROM_REG(pThis, LVI, iReg);
+
+        PHDASTREAM pStrmSt = hdaStreamFromID(pThis, u8Strm);
+        if (pStrmSt)
+        {
+            pStrmSt->u16LVI = u32Value;
+
+            /* Reset BDLE state. */
+            RT_ZERO(pStrmSt->State.BDLE);
+            pStrmSt->State.uCurBDLE = 0;
+        }
+
+        LogFlowFunc(("[SD%RU8]: CBL=%RU32\n", u8Strm, u32Value));
+    }
+    else
+        AssertRCReturn(rc, VINF_SUCCESS);
+
+    return rc;
+}
+
+static int hdaRegWriteSDFIFOW(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    switch (u32Value)
+    {
+        case HDA_SDFIFOW_8B:
+        case HDA_SDFIFOW_16B:
+        case HDA_SDFIFOW_32B:
+            return hdaRegWriteU16(pThis, iReg, u32Value);
+        default:
+            LogFunc(("Attempt to store unsupported value(%x) in SDFIFOW\n", u32Value));
+            return hdaRegWriteU16(pThis, iReg, HDA_SDFIFOW_32B);
+    }
+    return VINF_SUCCESS; /* Never reached. */
+}
+
+/**
+ * @note This method could be called for changing value on Output Streams
+ *       only (ICH6 datasheet 18.2.39).
+ */
+static int hdaRegWriteSDFIFOS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    /** @todo Only allow updating FIFOS if RUN bit is 0? */
+    uint32_t u32FIFOS = 0;
+
+    switch (iReg)
+    {
+        /* SDInFIFOS is RO, n=0-3. */
+        case HDA_REG_SD0FIFOS:
+        case HDA_REG_SD1FIFOS:
+        case HDA_REG_SD2FIFOS:
+        case HDA_REG_SD3FIFOS:
+        {
+            LogFunc(("Guest tries to change R/O value of FIFO size of input stream, ignoring\n"));
+            break;
+        }
+        case HDA_REG_SD4FIFOS:
+        case HDA_REG_SD5FIFOS:
+        case HDA_REG_SD6FIFOS:
+        case HDA_REG_SD7FIFOS:
+        {
+            switch(u32Value)
+            {
+                case HDA_SDONFIFO_16B:
+                case HDA_SDONFIFO_32B:
+                case HDA_SDONFIFO_64B:
+                case HDA_SDONFIFO_128B:
+                case HDA_SDONFIFO_192B:
+                    u32FIFOS = u32Value;
+                    break;
+
+                case HDA_SDONFIFO_256B: /** @todo r=andy Investigate this. */
+                    LogFunc(("256-bit is unsupported, HDA is switched into 192-bit mode\n"));
+                    /* Fall through is intentional. */
+                default:
+                    u32FIFOS = HDA_SDONFIFO_192B;
+                    break;
+            }
+
+            break;
+        }
+        default:
+        {
+            AssertMsgFailed(("Something weird happened with register lookup routine\n"));
+            break;
+        }
+    }
+
+    if (u32FIFOS)
+    {
+        LogFunc(("[SD%RU8]: Updating FIFOS to %RU32 bytes\n", 0, hdaSDFIFOSToBytes(u32FIFOS)));
+        /** @todo Update internal stream state with new FIFOS. */
+
+        return hdaRegWriteU16(pThis, iReg, u32FIFOS);
+    }
+
+    return VINF_SUCCESS;
+}
+
+#ifdef IN_RING3
+static int hdaSDFMTToStrmCfg(uint32_t u32SDFMT, PPDMAUDIOSTREAMCFG pCfg)
+{
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+# define EXTRACT_VALUE(v, mask, shift) ((v & ((mask) << (shift))) >> (shift))
+
+    int rc = VINF_SUCCESS;
+
+    uint32_t u32Hz     = (u32SDFMT & HDA_SDFMT_BASE_RATE_SHIFT) ? 44100 : 48000;
+    uint32_t u32HzMult = 1;
+    uint32_t u32HzDiv  = 1;
+
+    switch (EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_MULT_MASK, HDA_SDFMT_MULT_SHIFT))
+    {
+        case 0: u32HzMult = 1; break;
+        case 1: u32HzMult = 2; break;
+        case 2: u32HzMult = 3; break;
+        case 3: u32HzMult = 4; break;
+        default:
+            LogFunc(("Unsupported multiplier %x\n",
+                     EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_MULT_MASK, HDA_SDFMT_MULT_SHIFT)));
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+    switch (EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_DIV_MASK, HDA_SDFMT_DIV_SHIFT))
+    {
+        case 0: u32HzDiv = 1; break;
+        case 1: u32HzDiv = 2; break;
+        case 2: u32HzDiv = 3; break;
+        case 3: u32HzDiv = 4; break;
+        case 4: u32HzDiv = 5; break;
+        case 5: u32HzDiv = 6; break;
+        case 6: u32HzDiv = 7; break;
+        case 7: u32HzDiv = 8; break;
+        default:
+            LogFunc(("Unsupported divisor %x\n",
+                     EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_DIV_MASK, HDA_SDFMT_DIV_SHIFT)));
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+
+    PDMAUDIOFMT enmFmt = AUD_FMT_S16; /* Default to 16-bit signed. */
+    switch (EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_BITS_MASK, HDA_SDFMT_BITS_SHIFT))
+    {
+        case 0:
+            LogFunc(("Requested 8-bit\n"));
+            enmFmt = AUD_FMT_S8;
+            break;
+        case 1:
+            LogFunc(("Requested 16-bit\n"));
+            enmFmt = AUD_FMT_S16;
+            break;
+        case 2:
+            LogFunc(("Requested 20-bit\n"));
+            break;
+        case 3:
+            LogFunc(("Requested 24-bit\n"));
+            break;
+        case 4:
+            LogFunc(("Requested 32-bit\n"));
+            enmFmt = AUD_FMT_S32;
+            break;
+        default:
+            AssertMsgFailed(("Unsupported bits shift %x\n",
+                             EXTRACT_VALUE(u32SDFMT, HDA_SDFMT_BITS_MASK, HDA_SDFMT_BITS_SHIFT)));
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        pCfg->uHz           = u32Hz * u32HzMult / u32HzDiv;
+        pCfg->cChannels     = (u32SDFMT & 0xf) + 1;
+        pCfg->enmFormat     = enmFmt;
+        pCfg->enmEndianness = PDMAUDIOHOSTENDIANNESS;
+    }
+
+# undef EXTRACT_VALUE
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+#endif
+
+static int hdaRegWriteSDFMT(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+#ifdef IN_RING3
+# ifdef VBOX_WITH_HDA_CODEC_EMU
+    /* No reason to re-open stream with same settings. */
+    if (u32Value == HDA_REG_IND(pThis, iReg))
+        return VINF_SUCCESS;
+
+    PDMAUDIOSTREAMCFG strmCfg;
+    int rc = hdaSDFMTToStrmCfg(u32Value, &strmCfg);
+    if (RT_FAILURE(rc))
+        return VINF_SUCCESS; /* Always return success to the MMIO handler. */
+
+    uint8_t u8Strm = HDA_SD_NUM_FROM_REG(pThis, FMT, iReg);
+
+    PHDADRIVER pDrv;
+    switch (iReg)
+    {
+        case HDA_REG_SD0FMT:
+            RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+                rc = hdaCodecOpenStream(pThis->pCodec, PI_INDEX, &strmCfg);
+            break;
+#  ifdef VBOX_WITH_HDA_MIC_IN
+        case HDA_REG_SD2FMT:
+            RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+                rc = hdaCodecOpenStream(pThis->pCodec, MC_INDEX, &strmCfg);
+            break;
+#  endif
+        case HDA_REG_SD4FMT:
+            RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+                rc = hdaCodecOpenStream(pThis->pCodec, PO_INDEX, &strmCfg);
+            break;
+        default:
+            LogFunc(("Warning: Changing SDFMT on non-attached stream with ID=%RU8 (iReg=0x%x)\n", u8Strm, iReg));
+            break;
+    }
+
+    /** @todo r=andy rc gets lost; needs fixing. */
+    return hdaRegWriteU16(pThis, iReg, u32Value);
+# else /* !VBOX_WITH_HDA_CODEC_EMU */
+    return hdaRegWriteU16(pThis, iReg, u32Value);
+# endif
+#else /* !IN_RING3 */
+    return VINF_IOM_R3_MMIO_WRITE;
+#endif
+}
+
+/* Note: Will be called for both, BDPL and BDPU, registers. */
+DECLINLINE(int) hdaRegWriteSDBDPX(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value, uint8_t u8Strm)
+{
+    if (!hdaRegWriteSDIsAllowed(pThis, iReg, u32Value))
+        return VINF_SUCCESS;
+
+    int rc = hdaRegWriteU32(pThis, iReg, u32Value);
+    if (RT_SUCCESS(rc))
+    {
+        PHDASTREAM pStrmSt = hdaStreamFromID(pThis, u8Strm);
+        if (pStrmSt)
+        {
+            pStrmSt->u64BDLBase = RT_MAKE_U64(HDA_STREAM_REG(pThis, BDPL, u8Strm),
+                                              HDA_STREAM_REG(pThis, BDPU, u8Strm));
+            /* Reset BDLE state. */
+            RT_ZERO(pStrmSt->State.BDLE);
+            pStrmSt->State.uCurBDLE = 0;
+        }
+    }
+    else
+        AssertRCReturn(rc, VINF_SUCCESS);
+
+    return rc;
+}
+
+static int hdaRegWriteSDBDPL(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    return hdaRegWriteSDBDPX(pThis, iReg, u32Value, HDA_SD_NUM_FROM_REG(pThis, BDPL, iReg));
+}
+
+static int hdaRegWriteSDBDPU(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    return hdaRegWriteSDBDPX(pThis, iReg, u32Value, HDA_SD_NUM_FROM_REG(pThis, BDPU, iReg));
+}
+
+/**
+ * Checks whether a write to a specific SDnXXX register is allowed or not.
+ *
+ * @return  bool                Returns @true if write is allowed, @false if not.
+ * @param   pThis               Pointer to HDA state.
+ * @param   iReg                Register to write.
+ * @param   u32Value            Value to write.
+ */
+inline bool hdaRegWriteSDIsAllowed(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    /* Check if the SD's RUN bit is set. */
+    bool fIsRunning = RT_BOOL(HDA_REG_IND(pThis, iReg) & HDA_REG_FIELD_FLAG_MASK(SDCTL, RUN));
+    if (fIsRunning)
+    {
+#ifdef VBOX_STRICT
+        AssertMsgFailed(("[SD%RU8]: Cannot write to register 0x%x (0x%x) when RUN bit is set\n",
+                         HDA_SD_NUM_FROM_REG(pThis, CTL, iReg), iReg, u32Value));
+#endif
+        return false;
+    }
+
+    return true;
+}
+
+static int hdaRegReadIRS(PHDASTATE pThis, uint32_t iReg, uint32_t *pu32Value)
+{
+    int rc = VINF_SUCCESS;
+    /* regarding 3.4.3 we should mark IRS as busy in case CORB is active */
+    if (   HDA_REG(pThis, CORBWP) != HDA_REG(pThis, CORBRP)
+        || HDA_REG_FLAG_VALUE(pThis, CORBCTL, DMA))
+        HDA_REG(pThis, IRS) = HDA_REG_FIELD_FLAG_MASK(IRS, ICB);  /* busy */
+
+    rc = hdaRegReadU32(pThis, iReg, pu32Value);
+    return rc;
+}
+
+static int hdaRegWriteIRS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    int rc = VINF_SUCCESS;
+
+    /*
+     * If the guest set the ICB bit of IRS register, HDA should process the verb in IC register,
+     * write the response to IR register, and set the IRV (valid in case of success) bit of IRS register.
+     */
+    if (   u32Value & HDA_REG_FIELD_FLAG_MASK(IRS, ICB)
+        && !HDA_REG_FLAG_VALUE(pThis, IRS, ICB))
+    {
+#ifdef IN_RING3
+        PFNHDACODECVERBPROCESSOR    pfn = NULL;
+        uint64_t                    resp;
+        uint32_t cmd = HDA_REG(pThis, IC);
+        if (HDA_REG(pThis, CORBWP) != HDA_REG(pThis, CORBRP))
+        {
+            /*
+             * 3.4.3 defines behavior of immediate Command status register.
+             */
+            LogRel(("guest attempted process immediate verb (%x) with active CORB\n", cmd));
+            return rc;
+        }
+        HDA_REG(pThis, IRS) = HDA_REG_FIELD_FLAG_MASK(IRS, ICB);  /* busy */
+        LogFunc(("IC:%x\n", cmd));
+
+        rc = pThis->pCodec->pfnLookup(pThis->pCodec,
+                                      HDA_CODEC_CMD(cmd, 0 /* LUN */),
+                                      &pfn);
+        if (RT_FAILURE(rc))
+            AssertRCReturn(rc, rc);
+        rc = pfn(pThis->pCodec,
+                 HDA_CODEC_CMD(cmd, 0 /* LUN */), &resp);
+        if (RT_FAILURE(rc))
+            AssertRCReturn(rc, rc);
+
+        HDA_REG(pThis, IR) = (uint32_t)resp;
+        LogFunc(("IR:%x\n", HDA_REG(pThis, IR)));
+        HDA_REG(pThis, IRS) = HDA_REG_FIELD_FLAG_MASK(IRS, IRV);  /* result is ready  */
+        HDA_REG(pThis, IRS) &= ~HDA_REG_FIELD_FLAG_MASK(IRS, ICB); /* busy is clear */
+#else /* !IN_RING3 */
+        rc = VINF_IOM_R3_MMIO_WRITE;
+#endif
+        return rc;
+    }
+    /*
+     * Once the guest read the response, it should clean the IRV bit of the IRS register.
+     */
+    if (   u32Value & HDA_REG_FIELD_FLAG_MASK(IRS, IRV)
+        && HDA_REG_FLAG_VALUE(pThis, IRS, IRV))
+        HDA_REG(pThis, IRS) &= ~HDA_REG_FIELD_FLAG_MASK(IRS, IRV);
+    return rc;
+}
+
+static int hdaRegWriteRIRBWP(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    if (u32Value & HDA_REG_FIELD_FLAG_MASK(RIRBWP, RST))
+    {
+        HDA_REG(pThis, RIRBWP) = 0;
+    }
+    /* The remaining bits are O, see 6.2.22 */
+    return VINF_SUCCESS;
+}
+
+static int hdaRegWriteBase(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    uint32_t iRegMem = g_aHdaRegMap[iReg].mem_idx;
+    int rc = hdaRegWriteU32(pThis, iReg, u32Value);
+    if (RT_FAILURE(rc))
+        AssertRCReturn(rc, rc);
+
+    switch(iReg)
+    {
+        case HDA_REG_CORBLBASE:
+            pThis->u64CORBBase &= UINT64_C(0xFFFFFFFF00000000);
+            pThis->u64CORBBase |= pThis->au32Regs[iRegMem];
+            break;
+        case HDA_REG_CORBUBASE:
+            pThis->u64CORBBase &= UINT64_C(0x00000000FFFFFFFF);
+            pThis->u64CORBBase |= ((uint64_t)pThis->au32Regs[iRegMem] << 32);
+            break;
+        case HDA_REG_RIRBLBASE:
+            pThis->u64RIRBBase &= UINT64_C(0xFFFFFFFF00000000);
+            pThis->u64RIRBBase |= pThis->au32Regs[iRegMem];
+            break;
+        case HDA_REG_RIRBUBASE:
+            pThis->u64RIRBBase &= UINT64_C(0x00000000FFFFFFFF);
+            pThis->u64RIRBBase |= ((uint64_t)pThis->au32Regs[iRegMem] << 32);
+            break;
+        case HDA_REG_DPLBASE:
+        {
+            pThis->u64DPBase &= UINT64_C(0xFFFFFFFF00000000);
+            pThis->u64DPBase |= pThis->au32Regs[iRegMem];
+
+            /* Also make sure to handle the DMA position enable bit. */
+            pThis->fDMAPosition = pThis->au32Regs[iRegMem] & RT_BIT_32(0);
+            LogRel(("HDA: %s DMA position buffer\n", pThis->fDMAPosition ? "Enabled" : "Disabled"));
+            break;
+        }
+        case HDA_REG_DPUBASE:
+            pThis->u64DPBase &= UINT64_C(0x00000000FFFFFFFF);
+            pThis->u64DPBase |= ((uint64_t)pThis->au32Regs[iRegMem] << 32);
+            break;
+        default:
+            AssertMsgFailed(("Invalid index\n"));
+            break;
+    }
+
+    LogFunc(("CORB base:%llx RIRB base: %llx DP base: %llx\n",
+             pThis->u64CORBBase, pThis->u64RIRBBase, pThis->u64DPBase));
+    return rc;
+}
+
+static int hdaRegWriteRIRBSTS(PHDASTATE pThis, uint32_t iReg, uint32_t u32Value)
+{
+    uint8_t v = HDA_REG(pThis, RIRBSTS);
+    HDA_REG(pThis, RIRBSTS) &= ~(v & u32Value);
+
+    return hdaProcessInterrupt(pThis);
+}
+
+#ifdef IN_RING3
+#ifdef LOG_ENABLED
+static void hdaBDLEDumpAll(PHDASTATE pThis, uint64_t u64BDLBase, uint16_t cBDLE)
+{
+    LogFlowFunc(("BDLEs @ 0x%x (%RU16):\n", u64BDLBase, cBDLE));
+    if (!u64BDLBase)
+        return;
+
+    uint32_t cbBDLE = 0;
+    for (uint16_t i = 0; i < cBDLE; i++)
+    {
+        uint8_t bdle[16]; /** @todo Use a define. */
+        PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), u64BDLBase + i * 16, bdle, 16); /** @todo Use a define. */
+
+        uint64_t addr = *(uint64_t *)bdle;
+        uint32_t len  = *(uint32_t *)&bdle[8];
+        uint32_t ioc  = *(uint32_t *)&bdle[12];
+
+        LogFlowFunc(("\t#%03d BDLE(adr:0x%llx, size:%RU32, ioc:%RTbool)\n",
+                     i, addr, len, RT_BOOL(ioc & 0x1)));
+
+        cbBDLE += len;
+    }
+
+    LogFlowFunc(("Total: %RU32 bytes\n", cbBDLE));
+
+    if (!pThis->u64DPBase) /* No DMA base given? Bail out. */
+        return;
+
+    LogFlowFunc(("DMA counters:\n"));
+
+    for (int i = 0; i < cBDLE; i++)
+    {
+        uint32_t uDMACnt;
+        PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), (pThis->u64DPBase & DPBASE_ADDR_MASK) + (i * 2 * sizeof(uint32_t)),
+                          &uDMACnt, sizeof(uDMACnt));
+
+        LogFlowFunc(("\t#%03d DMA @ 0x%x\n", i , uDMACnt));
+    }
+}
+#endif
+
+/**
+ * Fetches a Bundle Descriptor List Entry (BDLE) from the DMA engine.
+ *
+ * @param   pThis                   Pointer to HDA state.
+ * @param   pBDLE                   Where to store the fetched result.
+ * @param   u64BaseDMA              Address base of DMA engine to use.
+ * @param   u16Entry                BDLE entry to fetch.
+ */
+static int hdaBDLEFetch(PHDASTATE pThis, PHDABDLE pBDLE, uint64_t u64BaseDMA, uint16_t u16Entry)
+{
+    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
+    AssertPtrReturn(pBDLE,   VERR_INVALID_POINTER);
+    AssertReturn(u64BaseDMA, VERR_INVALID_PARAMETER);
+    /** @todo Compare u16Entry with LVI. */
+
+    uint8_t uBundleEntry[16]; /** @todo Define a BDLE length. */
+    int rc = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), u64BaseDMA + u16Entry * 16, /** @todo Define a BDLE length. */
+                               uBundleEntry, RT_ELEMENTS(uBundleEntry));
+    if (RT_FAILURE(rc))
+        return rc;
+
+    RT_BZERO(pBDLE, sizeof(HDABDLE));
+
+    pBDLE->State.u32BDLIndex = u16Entry;
+    pBDLE->u64BufAdr         = *(uint64_t *) uBundleEntry;
+    pBDLE->u32BufSize        = *(uint32_t *)&uBundleEntry[8];
+    if (pBDLE->u32BufSize < sizeof(uint16_t)) /* Must be at least one word. */
+        return VERR_INVALID_STATE;
+
+    pBDLE->fIntOnCompletion  = (*(uint32_t *)&uBundleEntry[12]) & 0x1;
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Returns the number of outstanding stream data bytes which need to be processed
+ * by the DMA engine assigned to this stream.
+ *
+ * @return Number of bytes for the DMA engine to process.
+ */
+DECLINLINE(uint32_t) hdaStreamGetTransferSize(PHDASTATE pThis, PHDASTREAM pStrmSt, uint32_t cbMax)
+{
+    AssertPtrReturn(pThis, 0);
+    AssertPtrReturn(pStrmSt, 0);
+
+    if (!cbMax)
+        return 0;
+
+    PHDABDLE pBDLE = &pStrmSt->State.BDLE;
+
+    uint32_t cbFree = pStrmSt->u32CBL - HDA_STREAM_REG(pThis, LPIB, pStrmSt->u8Strm);
+    if (cbFree)
+    {
+        /* Limit to the available free space of the current BDLE. */
+        cbFree = RT_MIN(cbFree, pBDLE->u32BufSize - pBDLE->State.u32BufOff);
+
+        /* Make sure we only copy as much as the stream's FIFO can hold (SDFIFOS, 18.2.39). */
+        cbFree = RT_MIN(cbFree, pStrmSt->u16FIFOS);
+
+        /* Make sure we only transfer as many bytes as requested. */
+        cbFree = RT_MIN(cbFree, cbMax);
+
+        if (pBDLE->State.cbBelowFIFOW)
+        {
+            /* Are we not going to reach (or exceed) the FIFO watermark yet with the data to copy?
+             * No need to read data from DMA then. */
+            if (cbFree > pBDLE->State.cbBelowFIFOW)
+            {
+                /* Subtract the amount of bytes that still would fit in the stream's FIFO
+                 * and therefore do not need to be processed by DMA. */
+                cbFree -= pBDLE->State.cbBelowFIFOW;
+            }
+        }
+    }
+
+    LogFlowFunc(("[SD%RU8]: CBL=%RU32, LPIB=%RU32, cbFree=%RU32, %R[bdle]\n", pStrmSt->u8Strm,
+                 pStrmSt->u32CBL, HDA_STREAM_REG(pThis, LPIB, pStrmSt->u8Strm), cbFree, pBDLE));
+    return cbFree;
+}
+
+DECLINLINE(void) hdaBDLEUpdate(PHDABDLE pBDLE, uint32_t cbData, uint32_t cbProcessed)
+{
+    AssertPtrReturnVoid(pBDLE);
+
+    if (!cbData || !cbProcessed)
+        return;
+
+    /* Fewer than cbBelowFIFOW bytes were copied.
+     * Probably we need to move the buffer, but it is rather hard to imagine a situation
+     * where it might happen. */
+    AssertMsg((cbProcessed == pBDLE->State.cbBelowFIFOW + cbData), /* we assume that we write the entire buffer including unreported bytes */
+              ("cbProcessed=%RU32 != pBDLE->State.cbBelowFIFOW=%RU32 + cbData=%RU32\n",
+              cbProcessed, pBDLE->State.cbBelowFIFOW, cbData));
+
+#if 0
+    if (   pBDLE->State.cbBelowFIFOW
+        && pBDLE->State.cbBelowFIFOW <= cbWritten)
+    {
+        LogFlowFunc(("BDLE(cbUnderFifoW:%RU32, off:%RU32, size:%RU32)\n",
+                     pBDLE->State.cbBelowFIFOW, pBDLE->State.u32BufOff, pBDLE->u32BufSize));
+    }
+#endif
+
+    pBDLE->State.cbBelowFIFOW -= RT_MIN(pBDLE->State.cbBelowFIFOW, cbProcessed);
+    Assert(pBDLE->State.cbBelowFIFOW == 0);
+
+    /* We always increment the position of DMA buffer counter because we're always reading
+     * into an intermediate buffer. */
+    pBDLE->State.u32BufOff += cbData;
+    Assert(pBDLE->State.u32BufOff <= pBDLE->u32BufSize);
+
+    LogFlowFunc(("cbData=%RU32, cbProcessed=%RU32, %R[bdle]\n", cbData, cbProcessed, pBDLE));
+}
+
+DECLINLINE(bool) hdaStreamNeedsNextBDLE(PHDASTATE pThis, PHDASTREAM pStrmSt)
+{
+    AssertPtrReturn(pThis,   false);
+    AssertPtrReturn(pStrmSt, false);
+
+    PHDABDLE pBDLE   = &pStrmSt->State.BDLE;
+    uint32_t u32LPIB = HDA_STREAM_REG(pThis, LPIB, pStrmSt->u8Strm);
+
+    /* Did we reach the CBL (Cyclic Buffer List) limit? */
+    bool fCBLLimitReached = u32LPIB >= pStrmSt->u32CBL;
+
+    /* Do we need to use the next BDLE entry? Either because we reached
+     * the CBL limit or our internal DMA buffer is full. */
+    bool fNeedsNextBDLE   = (   fCBLLimitReached
+                             || (pBDLE->State.u32BufOff >= pBDLE->u32BufSize));
+
+    Assert(u32LPIB                <= pStrmSt->u32CBL);
+    Assert(pBDLE->State.u32BufOff <= pBDLE->u32BufSize);
+
+    LogFlowFunc(("[SD%RU8]: LPIB=%RU32, CBL=%RU32, fCBLLimitReached=%RTbool, fNeedsNextBDLE=%RTbool, %R[bdle]\n",
+                 pStrmSt->u8Strm, u32LPIB, pStrmSt->u32CBL, fCBLLimitReached, fNeedsNextBDLE, pBDLE));
+
+    if (fCBLLimitReached)
+    {
+        /* Reset LPIB register. */
+        u32LPIB -= RT_MIN(u32LPIB, pStrmSt->u32CBL);
+        hdaStreamUpdateLPIB(pThis, pStrmSt, u32LPIB);
+    }
+
+    return fNeedsNextBDLE;
+}
+
+DECLINLINE(void) hdaStreamTransferUpdate(PHDASTATE pThis, PHDASTREAM pStrmSt, uint32_t cbInc)
+{
+    AssertPtrReturnVoid(pThis);
+    AssertPtrReturnVoid(pStrmSt);
+
+    LogFlowFunc(("[SD%RU8]: cbInc=%RU32\n", pStrmSt->u8Strm, cbInc));
+
+    Assert(cbInc <= pStrmSt->u16FIFOS);
+
+    PHDABDLE pBDLE = &pStrmSt->State.BDLE;
+
+    /*
+     * If we're below the FIFO watermark (SDFIFOW), it's expected that HDA
+     * doesn't fetch anything via DMA, so just update LPIB.
+     * (ICH6 datasheet 18.2.38).
+     */
+    if (pBDLE->State.cbBelowFIFOW == 0) /* Did we hit (or exceed) the watermark? */
+    {
+        const uint32_t u32LPIB = RT_MIN(HDA_STREAM_REG(pThis, LPIB, pStrmSt->u8Strm) + cbInc,
+                                        pStrmSt->u32CBL);
+
+        LogFlowFunc(("[SD%RU8]: LPIB: %RU32 -> %RU32, CBL=%RU32\n",
+                     pStrmSt->u8Strm,
+                     HDA_STREAM_REG(pThis, LPIB, pStrmSt->u8Strm), HDA_STREAM_REG(pThis, LPIB, pStrmSt->u8Strm) + cbInc,
+                     pStrmSt->u32CBL));
+
+        hdaStreamUpdateLPIB(pThis, pStrmSt, u32LPIB);
+    }
+}
+
+static bool hdaStreamTransferIsComplete(PHDASTATE pThis, PHDASTREAM pStrmSt)
+{
+    AssertPtrReturn(pThis,   true);
+    AssertPtrReturn(pStrmSt, true);
+
+    bool fIsComplete = false;
+
+    PHDABDLE       pBDLE   = &pStrmSt->State.BDLE;
+    const uint32_t u32LPIB = HDA_STREAM_REG(pThis, LPIB, pStrmSt->u8Strm);
+
+    if (   pBDLE->State.u32BufOff >= pBDLE->u32BufSize
+        || u32LPIB                >= pStrmSt->u32CBL)
+    {
+        Assert(pBDLE->State.u32BufOff <= pBDLE->u32BufSize);
+        Assert(u32LPIB                <= pStrmSt->u32CBL);
+
+        if (/* IOC (Interrupt On Completion) bit set? */
+               pBDLE->fIntOnCompletion
+            /* All data put into the DMA FIFO? */
+            && pBDLE->State.cbBelowFIFOW == 0
+           )
+        {
+            /**
+             * Set the BCIS (Buffer Completion Interrupt Status) flag as the
+             * last byte of data for the current descriptor has been fetched
+             * from memory and put into the DMA FIFO.
+             *
+             ** @todo More carefully investigate BCIS flag.
+             *
+             * Speech synthesis works fine on Mac Guest if this bit isn't set
+             * but in general sound quality gets worse.
+             */
+            HDA_STREAM_REG(pThis, STS, pStrmSt->u8Strm) |= HDA_REG_FIELD_FLAG_MASK(SDSTS, BCIS);
+
+            /*
+             * If the ICE (IOCE, "Interrupt On Completion Enable") bit of the SDCTL register is set
+             * we need to generate an interrupt.
+             */
+            if (HDA_STREAM_REG(pThis, CTL, pStrmSt->u8Strm) & HDA_REG_FIELD_FLAG_MASK(SDCTL, ICE))
+                hdaProcessInterrupt(pThis);
+        }
+
+        fIsComplete = true;
+    }
+
+    LogFlowFunc(("[SD%RU8]: u32LPIB=%RU32, CBL=%RU32, %R[bdle] => %s\n",
+                 pStrmSt->u8Strm, u32LPIB, pStrmSt->u32CBL, pBDLE, fIsComplete ? "COMPLETE" : "INCOMPLETE"));
+
+    return fIsComplete;
+}
+
+/**
+ * hdaReadAudio - copies samples from audio backend to DMA.
+ * Note: This function writes to the DMA buffer immediately,
+ *       but "reports bytes" when all conditions are met (FIFOW).
+ */
+static int hdaReadAudio(PHDASTATE pThis, PHDASTREAM pStrmSt, PAUDMIXSINK pSink, uint32_t cbMax, uint32_t *pcbRead)
+{
+    AssertPtrReturn(pThis,   VERR_INVALID_POINTER);
+    AssertPtrReturn(pStrmSt, VERR_INVALID_POINTER);
+    AssertPtrReturn(pSink,   VERR_INVALID_POINTER);
+    /* pcbRead is optional. */
+
+    PHDABDLE pBDLE = &pStrmSt->State.BDLE;
+
+    int rc;
+    uint32_t cbRead = 0;
+    uint32_t cbBuf  = hdaStreamGetTransferSize(pThis, pStrmSt, cbMax);
+
+    LogFlowFunc(("cbBuf=%RU32, %R[bdle]\n", cbBuf, pBDLE));
+
+    if (!cbBuf)
+    {
+        /* Nothing to write, bail out. */
+        rc = VINF_EOF;
+    }
+    else
+    {
+        rc = AudioMixerProcessSinkIn(pSink, AUDMIXOP_BLEND, pBDLE->State.au8FIFO, cbBuf, &cbRead);
+        if (RT_SUCCESS(rc))
+        {
+            Assert(cbRead);
+            Assert(cbRead == cbBuf);
+            Assert(cbRead <= pBDLE->u32BufSize - pBDLE->State.u32BufOff);
+
+            /*
+             * Write to the BDLE's DMA buffer.
+             */
+            rc = PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns),
+                                       pBDLE->u64BufAdr + pBDLE->State.u32BufOff,
+                                       pBDLE->State.au8FIFO, cbRead);
+            AssertRC(rc);
+
+            if (pBDLE->State.cbBelowFIFOW + cbRead > hdaStreamGetFIFOW(pThis, pStrmSt))
+            {
+                pBDLE->State.u32BufOff    += cbRead;
+                pBDLE->State.cbBelowFIFOW  = 0;
+                //hdaBackendReadTransferReported(pBDLE, cbDMAData, cbRead, &cbRead, pcbAvail);
+            }
+            else
+            {
+                pBDLE->State.u32BufOff    += cbRead;
+                pBDLE->State.cbBelowFIFOW += cbRead;
+                Assert(pBDLE->State.cbBelowFIFOW <= hdaStreamGetFIFOW(pThis, pStrmSt));
+                //hdaBackendTransferUnreported(pThis, pBDLE, pStreamDesc, cbRead, pcbAvail);
+
+                rc = VERR_NO_DATA;
+            }
+        }
+    }
+
+    Assert(cbRead <= pStrmSt->u16FIFOS);
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pcbRead)
+            *pcbRead = cbRead;
+    }
+
+    LogFunc(("Returning cbRead=%RU32, rc=%Rrc\n", cbRead, rc));
+    return rc;
+}
+
+static int hdaWriteAudio(PHDASTATE pThis, PHDASTREAM pStrmSt, uint32_t cbMax, uint32_t *pcbWritten)
+{
+    AssertPtrReturn(pThis,      VERR_INVALID_POINTER);
+    AssertPtrReturn(pStrmSt,    VERR_INVALID_POINTER);
+    AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
+    /* pcbWritten is optional. */
+
+    PHDABDLE pBDLE = &pStrmSt->State.BDLE;
+
+    uint32_t cbWritten = 0;
+    uint32_t cbData    = hdaStreamGetTransferSize(pThis, pStrmSt, cbMax);
+
+    LogFlowFunc(("cbData=%RU32, %R[bdle]\n", cbData, pBDLE));
+
+    /*
+     * Copy from DMA to the corresponding stream buffer (if there are any bytes from the
+     * previous unreported transfer we write at offset 'pBDLE->State.cbUnderFifoW').
+     */
+    int rc;
+    if (!cbData)
+    {
+        rc = VINF_EOF;
+    }
+    else
+    {
+        /*
+         * Read from the current BDLE's DMA buffer.
+         */
+        rc = PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns),
+                               pBDLE->u64BufAdr + pBDLE->State.u32BufOff,
+                               pBDLE->State.au8FIFO + pBDLE->State.cbBelowFIFOW, cbData);
+        AssertRC(rc);
+
+#ifdef VBOX_WITH_STATISTICS
+        STAM_COUNTER_ADD(&pThis->StatBytesRead, cbData);
+#endif
+        /*
+         * Write to audio backend. We should ensure that we have enough bytes to copy to the backend.
+         */
+        uint32_t cbToWrite = cbData + pBDLE->State.cbBelowFIFOW;
+        if (cbToWrite >= hdaStreamGetFIFOW(pThis, pStrmSt))
+        {
+            uint32_t cbWrittenToStream;
+            int rc2;
+
+            PHDADRIVER pDrv;
+            RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+            {
+                if (pDrv->pConnector->pfnIsActiveOut(pDrv->pConnector, pDrv->Out.pStrmOut))
+                {
+                    rc2 = pDrv->pConnector->pfnWrite(pDrv->pConnector, pDrv->Out.pStrmOut,
+                                                     pBDLE->State.au8FIFO, cbToWrite, &cbWrittenToStream);
+                    if (RT_SUCCESS(rc2))
+                    {
+                        if (cbWrittenToStream < cbToWrite) /* Lagging behind? */
+                            LogFlowFunc(("\tLUN#%RU8: Warning: Only written %RU32 / %RU32 bytes, expect lags\n",
+                                         pDrv->uLUN, cbWrittenToStream, cbToWrite));
+                    }
+                }
+                else /* Stream disabled, not fatal. */
+                {
+                    cbWrittenToStream = 0;
+                    rc2 = VERR_NOT_AVAILABLE;
+                    /* Keep going. */
+                }
+
+                LogFlowFunc(("\tLUN#%RU8: cbToWrite=%RU32, cbWrittenToStream=%RU32, rc=%Rrc\n",
+                             pDrv->uLUN, cbToWrite, cbWrittenToStream, rc2));
+            }
+
+            /* Always report all data as being written;
+             * backends who were not able to catch up have to deal with it themselves. */
+            cbWritten = cbToWrite;
+
+            hdaBDLEUpdate(pBDLE, cbData, cbWritten);
+        }
+        else
+        {
+            pBDLE->State.u32BufOff += cbWritten;
+            pBDLE->State.cbBelowFIFOW += cbWritten;
+            Assert(pBDLE->State.cbBelowFIFOW <= hdaStreamGetFIFOW(pThis, pStrmSt));
+
+            /* Not enough bytes to be processed and reported, we'll try our luck next time around. */
+            //hdaBackendTransferUnreported(pThis, pBDLE, pStreamDesc, cbAvail, NULL);
+            rc = VINF_EOF;
+        }
+    }
+
+    Assert(cbWritten <= pStrmSt->u16FIFOS);
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pcbWritten)
+            *pcbWritten = cbWritten;
+    }
+
+    LogFunc(("Returning cbWritten=%RU32, rc=%Rrc\n", cbWritten, rc));
+    return rc;
+}
+
+/**
+ * @interface_method_impl{HDACODEC,pfnReset}
+ */
+static DECLCALLBACK(int) hdaCodecReset(PHDACODEC pCodec)
+{
+    PHDASTATE pThis = pCodec->pHDAState;
+    NOREF(pThis);
+    return VINF_SUCCESS;
+}
+
+
+static DECLCALLBACK(void) hdaCloseIn(PHDASTATE pThis, PDMAUDIORECSOURCE enmRecSource)
+{
+    NOREF(pThis);
+    NOREF(enmRecSource);
+    LogFlowFuncEnter();
+}
+
+static DECLCALLBACK(void) hdaCloseOut(PHDASTATE pThis)
+{
+    NOREF(pThis);
+    LogFlowFuncEnter();
+}
+
+static DECLCALLBACK(int) hdaOpenIn(PHDASTATE pThis,
+                                   const char *pszName, PDMAUDIORECSOURCE enmRecSource,
+                                   PPDMAUDIOSTREAMCFG pCfg)
+{
+    PAUDMIXSINK pSink;
+
+    switch (enmRecSource)
+    {
+# ifdef VBOX_WITH_HDA_MIC_IN
+        case PDMAUDIORECSOURCE_MIC:
+            pSink = pThis->pSinkMicIn;
+            break;
+# endif
+        case PDMAUDIORECSOURCE_LINE_IN:
+            pSink = pThis->pSinkLineIn;
+            break;
+        default:
+            AssertMsgFailed(("Audio source %ld not supported\n", enmRecSource));
+            return VERR_NOT_SUPPORTED;
+    }
+
+    int rc = VINF_SUCCESS;
+    char *pszDesc;
+
+    PHDADRIVER pDrv;
+    RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+    {
+        if (RTStrAPrintf(&pszDesc, "[LUN#%RU8] %s", pDrv->uLUN, pszName) <= 0)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        rc = pDrv->pConnector->pfnCreateIn(pDrv->pConnector, pszDesc, enmRecSource, pCfg, &pDrv->LineIn.pStrmIn);
+        LogFlowFunc(("LUN#%RU8: Created input \"%s\", with rc=%Rrc\n", pDrv->uLUN, pszDesc, rc));
+        if (rc == VINF_SUCCESS) /* Note: Could return VWRN_ALREADY_EXISTS. */
+        {
+            AudioMixerRemoveStream(pSink, pDrv->LineIn.phStrmIn);
+            rc = AudioMixerAddStreamIn(pSink,
+                                       pDrv->pConnector, pDrv->LineIn.pStrmIn,
+                                       0 /* uFlags */, &pDrv->LineIn.phStrmIn);
+        }
+
+        RTStrFree(pszDesc);
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) hdaOpenOut(PHDASTATE pThis,
+                                    const char *pszName, PPDMAUDIOSTREAMCFG pCfg)
+{
+    int rc = VINF_SUCCESS;
+    char *pszDesc;
+
+    PHDADRIVER pDrv;
+    RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+    {
+        if (RTStrAPrintf(&pszDesc, "[LUN#%RU8] %s (%RU32Hz, %RU8 %s)",
+                         pDrv->uLUN, pszName, pCfg->uHz, pCfg->cChannels, pCfg->cChannels > 1 ? "Channels" : "Channel") <= 0)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        rc = pDrv->pConnector->pfnCreateOut(pDrv->pConnector, pszDesc, pCfg, &pDrv->Out.pStrmOut);
+        LogFlowFunc(("LUN#%RU8: Created output \"%s\", with rc=%Rrc\n", pDrv->uLUN, pszDesc, rc));
+        if (rc == VINF_SUCCESS) /* Note: Could return VWRN_ALREADY_EXISTS. */
+        {
+            AudioMixerRemoveStream(pThis->pSinkOutput, pDrv->Out.phStrmOut);
+            rc = AudioMixerAddStreamOut(pThis->pSinkOutput,
+                                        pDrv->pConnector, pDrv->Out.pStrmOut,
+                                        0 /* uFlags */, &pDrv->Out.phStrmOut);
+        }
+
+        RTStrFree(pszDesc);
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) hdaSetVolume(PHDASTATE pThis, ENMSOUNDSOURCE enmSource,
+                                      bool fMute, uint8_t uVolLeft, uint8_t uVolRight)
+{
+    int             rc = VINF_SUCCESS;
+    PDMAUDIOVOLUME  vol = { fMute, uVolLeft, uVolRight };
+    PAUDMIXSINK     pSink;
+
+    /* Convert the audio source to corresponding sink. */
+    switch (enmSource)
+    {
+        case PO_INDEX:
+            pSink = pThis->pSinkOutput;
+            break;
+        case PI_INDEX:
+            pSink = pThis->pSinkLineIn;
+            break;
+        case MC_INDEX:
+            pSink = pThis->pSinkMicIn;
+            break;
+        default:
+            AssertFailedReturn(VERR_INVALID_PARAMETER);
+            break;
+    }
+
+    /* Set the volume. Codec already converted it to the correct range. */
+    AudioMixerSetSinkVolume(pSink, &vol);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+#ifndef VBOX_WITH_AUDIO_CALLBACKS
+
+static DECLCALLBACK(void) hdaTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+    PHDASTATE pThis = (PHDASTATE)pvUser;
+    Assert(pThis == PDMINS_2_DATA(pDevIns, PHDASTATE));
+    AssertPtr(pThis);
+
+    STAM_PROFILE_START(&pThis->StatTimer, a);
+
+    uint32_t cbInMax  = 0;
+    uint32_t cbOutMin = UINT32_MAX;
+
+    PHDADRIVER pDrv;
+
+    uint64_t cTicksNow     = TMTimerGet(pTimer);
+    uint64_t cTicksElapsed = cTicksNow  - pThis->uTimerTS;
+    uint64_t cTicksPerSec  = TMTimerGetFreq(pTimer);
+
+    pThis->uTimerTS = cTicksNow;
+
+    /*
+     * Calculate the codec's (fixed) sampling rate.
+     */
+    AssertPtr(pThis->pCodec);
+    PDMPCMPROPS codecStrmProps;
+
+    int rc = DrvAudioStreamCfgToProps(&pThis->pCodec->strmCfg, &codecStrmProps);
+    AssertRC(rc);
+
+    uint32_t cCodecSamplesMin  = (int)((2 * cTicksElapsed * pThis->pCodec->strmCfg.uHz + cTicksPerSec) / cTicksPerSec / 2);
+    uint32_t cbCodecSamplesMin = cCodecSamplesMin << codecStrmProps.cShift;
+
+    /*
+     * Process all driver nodes.
+     */
+    RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+    {
+        uint32_t cbIn = 0;
+        uint32_t cbOut = 0;
+
+        rc = pDrv->pConnector->pfnQueryStatus(pDrv->pConnector, &cbIn, &cbOut, NULL /* pcSamplesLive */);
+        if (RT_SUCCESS(rc))
+            rc = pDrv->pConnector->pfnPlayOut(pDrv->pConnector, NULL /* pcSamplesPlayed */);
+
+#ifdef DEBUG_TIMER
+        LogFlowFunc(("LUN#%RU8: rc=%Rrc, cbIn=%RU32, cbOut=%RU32\n", pDrv->uLUN, rc, cbIn, cbOut));
+#endif
+        /* If we there was an error handling (available) output or there simply is no output available,
+         * then calculate the minimum data rate which must be processed by the device emulation in order
+         * to function correctly.
+         *
+         * This is not the optimal solution, but as we have to deal with this on a timer-based approach
+         * (until we have the audio callbacks) we need to have device' DMA engines running. */
+        if (!pDrv->pConnector->pfnIsValidOut(pDrv->pConnector, pDrv->Out.pStrmOut))
+        {
+            /* Use the codec's (fixed) sampling rate. */
+            cbOut = RT_MAX(cbOut, cbCodecSamplesMin);
+            continue;
+        }
+
+        const bool fIsActiveOut = pDrv->pConnector->pfnIsActiveOut(pDrv->pConnector, pDrv->Out.pStrmOut);
+        if (   RT_FAILURE(rc)
+            || !fIsActiveOut)
+        {
+            uint32_t cSamplesMin  = (int)((2 * cTicksElapsed * pDrv->Out.pStrmOut->Props.uHz + cTicksPerSec) / cTicksPerSec / 2);
+            uint32_t cbSamplesMin = AUDIOMIXBUF_S2B(&pDrv->Out.pStrmOut->MixBuf, cSamplesMin);
+
+#ifdef DEBUG_TIMER
+            LogFlowFunc(("\trc=%Rrc, cSamplesMin=%RU32, cbSamplesMin=%RU32\n", rc, cSamplesMin, cbSamplesMin));
+#endif
+            cbOut = RT_MAX(cbOut, cbSamplesMin);
+        }
+
+        cbOutMin = RT_MIN(cbOutMin, cbOut);
+        cbInMax  = RT_MAX(cbInMax, cbIn);
+    }
+
+#ifdef DEBUG_TIMER
+    LogFlowFunc(("cbInMax=%RU32, cbOutMin=%RU32\n", cbInMax, cbOutMin));
+#endif
+
+    if (cbOutMin == UINT32_MAX)
+        cbOutMin = 0;
+
+    /* Do the actual device transfers. */
+    hdaTransfer(pThis, PO_INDEX, cbOutMin /* cbToProcess */, NULL /* pcbProcessed */);
+    hdaTransfer(pThis, PI_INDEX, cbInMax  /* cbToProcess */, NULL /* pcbProcessed */);
+
+    /* Kick the timer again. */
+    uint64_t cTicks = pThis->cTimerTicks;
+    /** @todo adjust cTicks down by now much cbOutMin represents. */
+    TMTimerSet(pThis->pTimer, cTicksNow + cTicks);
+
+    STAM_PROFILE_STOP(&pThis->StatTimer, a);
+}
+
+#else /* VBOX_WITH_AUDIO_CALLBACKS */
+
+static DECLCALLBACK(int) hdaCallbackInput(PDMAUDIOCALLBACKTYPE enmType, void *pvCtx, size_t cbCtx, void *pvUser, size_t cbUser)
+{
+    Assert(enmType == PDMAUDIOCALLBACKTYPE_INPUT);
+    AssertPtrReturn(pvCtx,  VERR_INVALID_POINTER);
+    AssertReturn(cbCtx,     VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertReturn(cbUser,    VERR_INVALID_PARAMETER);
+
+    PHDACALLBACKCTX pCtx = (PHDACALLBACKCTX)pvCtx;
+    AssertReturn(cbCtx == sizeof(HDACALLBACKCTX), VERR_INVALID_PARAMETER);
+
+    PPDMAUDIOCALLBACKDATAIN pData = (PPDMAUDIOCALLBACKDATAIN)pvUser;
+    AssertReturn(cbUser == sizeof(PDMAUDIOCALLBACKDATAIN), VERR_INVALID_PARAMETER);
+
+    return hdaTransfer(pCtx->pThis, PI_INDEX, UINT32_MAX, &pData->cbOutRead);
+}
+
+static DECLCALLBACK(int) hdaCallbackOutput(PDMAUDIOCALLBACKTYPE enmType, void *pvCtx, size_t cbCtx, void *pvUser, size_t cbUser)
+{
+    Assert(enmType == PDMAUDIOCALLBACKTYPE_OUTPUT);
+    AssertPtrReturn(pvCtx,  VERR_INVALID_POINTER);
+    AssertReturn(cbCtx,     VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+    AssertReturn(cbUser,    VERR_INVALID_PARAMETER);
+
+    PHDACALLBACKCTX pCtx = (PHDACALLBACKCTX)pvCtx;
+    AssertReturn(cbCtx == sizeof(HDACALLBACKCTX), VERR_INVALID_PARAMETER);
+
+    PPDMAUDIOCALLBACKDATAOUT pData = (PPDMAUDIOCALLBACKDATAOUT)pvUser;
+    AssertReturn(cbUser == sizeof(PDMAUDIOCALLBACKDATAOUT), VERR_INVALID_PARAMETER);
+
+    PHDASTATE pThis = pCtx->pThis;
+
+    int rc = hdaTransfer(pCtx->pThis, PO_INDEX, UINT32_MAX, &pData->cbOutWritten);
+    if (   RT_SUCCESS(rc)
+        && pData->cbOutWritten)
+    {
+        PHDADRIVER pDrv;
+        RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+        {
+            uint32_t cSamplesPlayed;
+            int rc2 = pDrv->pConnector->pfnPlayOut(pDrv->pConnector, &cSamplesPlayed);
+            LogFlowFunc(("LUN#%RU8: cSamplesPlayed=%RU32, rc=%Rrc\n", pDrv->uLUN, cSamplesPlayed, rc2));
+        }
+    }
+}
+#endif /* VBOX_WITH_AUDIO_CALLBACKS */
+
+static int hdaTransfer(PHDASTATE pThis, ENMSOUNDSOURCE enmSrc, uint32_t cbToProcess, uint32_t *pcbProcessed)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    /* pcbProcessed is optional. */
+
+    if (ASMAtomicReadBool(&pThis->fInReset)) /* HDA controller in reset mode? Bail out. */
+    {
+        LogFlowFunc(("In reset mode, skipping\n"));
+
+        if (pcbProcessed)
+            *pcbProcessed = 0;
+        return VINF_SUCCESS;
+    }
+
+    PHDASTREAM pStrmSt;
+    switch (enmSrc)
+    {
+        case PI_INDEX:
+        {
+            pStrmSt = &pThis->StrmStLineIn;
+            break;
+        }
+
+#ifdef VBOX_WITH_HDA_MIC_IN
+        case MC_INDEX:
+        {
+            pStrmSt = &pThis->StrmStMicIn;
+            break;
+        }
+#endif
+        case PO_INDEX:
+        {
+            pStrmSt = &pThis->StrmStOut;
+            break;
+        }
+
+        default:
+        {
+            AssertMsgFailed(("Unknown source index %ld\n", enmSrc));
+            return VERR_NOT_SUPPORTED;
+        }
+    }
+
+    int  rc       = VINF_SUCCESS;
+    bool fProceed = true;
+
+    /* Stop request received? */
+    if (ASMAtomicReadBool(&pStrmSt->State.fDoStop))
+    {
+        pStrmSt->State.fActive = false;
+
+        rc = RTSemEventSignal(pStrmSt->State.hStateChangedEvent);
+        AssertRC(rc);
+
+        fProceed = false;
+    }
+    /* Is the stream not in a running state currently? */
+    else if (!(HDA_STREAM_REG(pThis, CTL, pStrmSt->u8Strm) & HDA_REG_FIELD_FLAG_MASK(SDCTL, RUN)))
+        fProceed = false;
+    /* Nothing to process? */
+    else if (!cbToProcess)
+        fProceed = false;
+
+    if (!fProceed)
+    {
+        if (pcbProcessed)
+            *pcbProcessed = 0;
+        return VINF_SUCCESS;
+    }
+
+    LogFlowFunc(("enmSrc=%RU32, cbToProcess=%RU32\n", enmSrc, cbToProcess));
+
+    /* Sanity checks. */
+    Assert(pStrmSt->u8Strm <= 7); /** @todo Use a define for MAX_STREAMS! */
+    Assert(pStrmSt->u64BDLBase);
+    Assert(pStrmSt->u32CBL);
+
+    /* State sanity checks. */
+    Assert(ASMAtomicReadBool(&pStrmSt->State.fInReset) == false);
+
+    uint32_t cbProcessedTotal = 0;
+    bool     fIsComplete      = false;
+
+    while (cbToProcess)
+    {
+        /* Do we need to fetch the next Buffer Descriptor Entry (BDLE)? */
+        if (hdaStreamNeedsNextBDLE(pThis, pStrmSt))
+            hdaStreamGetNextBDLE(pThis, pStrmSt);
+
+        /* Set the FIFORDY bit on the stream while doing the transfer. */
+        HDA_STREAM_REG(pThis, STS, pStrmSt->u8Strm) |= HDA_REG_FIELD_FLAG_MASK(SDSTS, FIFORDY);
+
+        uint32_t cbProcessed;
+        switch (enmSrc)
+        {
+            case PI_INDEX:
+                rc = hdaReadAudio(pThis, pStrmSt, pThis->pSinkLineIn, cbToProcess, &cbProcessed);
+                break;
+            case PO_INDEX:
+                rc = hdaWriteAudio(pThis, pStrmSt, cbToProcess, &cbProcessed);
+                break;
+#ifdef VBOX_WITH_HDA_MIC_IN
+            case MC_INDEX:
+                rc = hdaReadAudio(pThis, pStrmSt, pThis->pSinkMicIn, cbToProcess, &cbProcessed);
+                break;
+#endif
+            default:
+                AssertMsgFailed(("Unsupported source index %ld\n", enmSrc));
+                rc = VERR_NOT_SUPPORTED;
+                break;
+        }
+
+        /* Remove the FIFORDY bit again. */
+        HDA_STREAM_REG(pThis, STS, pStrmSt->u8Strm) &= ~HDA_REG_FIELD_FLAG_MASK(SDSTS, FIFORDY);
+
+        if (RT_FAILURE(rc))
+            break;
+
+        hdaStreamTransferUpdate(pThis, pStrmSt, cbProcessed);
+
+        cbToProcess      -= RT_MIN(cbToProcess, cbProcessed);
+        cbProcessedTotal += cbProcessed;
+
+        LogFlowFunc(("cbProcessed=%RU32, cbToProcess=%RU32, cbProcessedTotal=%RU32, rc=%Rrc\n",
+                     cbProcessed, cbToProcess, cbProcessedTotal, rc));
+
+        if (rc == VINF_EOF)
+            fIsComplete = true;
+
+        if (!fIsComplete)
+            fIsComplete = hdaStreamTransferIsComplete(pThis, pStrmSt);
+
+        if (fIsComplete)
+            break;
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pcbProcessed)
+            *pcbProcessed = cbProcessedTotal;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+#endif /* IN_RING3 */
+
+/* MMIO callbacks */
+
+/**
+ * @callback_method_impl{FNIOMMMIOREAD, Looks up and calls the appropriate handler.}
+ *
+ * @note During implementation, we discovered so-called "forgotten" or "hole"
+ *       registers whose description is not listed in the RPM, datasheet, or
+ *       spec.
+ */
+PDMBOTHCBDECL(int) hdaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
+{
+    PHDASTATE   pThis  = PDMINS_2_DATA(pDevIns, PHDASTATE);
+    int         rc;
+
+    /*
+     * Look up and log.
+     */
+    uint32_t        offReg = GCPhysAddr - pThis->MMIOBaseAddr;
+    int             idxRegDsc = hdaRegLookup(pThis, offReg);    /* Register descriptor index. */
+#ifdef LOG_ENABLED
+    unsigned const  cbLog     = cb;
+    uint32_t        offRegLog = offReg;
+#endif
+
+    LogFunc(("offReg=%#x cb=%#x\n", offReg, cb));
+    Assert(cb == 4); Assert((offReg & 3) == 0);
+
+    if (pThis->fInReset && idxRegDsc != HDA_REG_GCTL)
+        LogFunc(("\tAccess to registers except GCTL is blocked while reset\n"));
+
+    if (idxRegDsc == -1)
+        LogRel(("HDA: Invalid read access @0x%x (bytes=%d)\n", offReg, cb));
+
+    if (idxRegDsc != -1)
+    {
+        /* ASSUMES gapless DWORD at end of map. */
+        if (g_aHdaRegMap[idxRegDsc].size == 4)
+        {
+            /*
+             * Straight forward DWORD access.
+             */
+            rc = g_aHdaRegMap[idxRegDsc].pfnRead(pThis, idxRegDsc, (uint32_t *)pv);
+            LogFunc(("\tRead %s => %x (%Rrc)\n", g_aHdaRegMap[idxRegDsc].abbrev, *(uint32_t *)pv, rc));
+        }
+        else
+        {
+            /*
+             * Multi register read (unless there are trailing gaps).
+             * ASSUMES that only DWORD reads have sideeffects.
+             */
+            uint32_t u32Value = 0;
+            unsigned cbLeft   = 4;
+            do
+            {
+                uint32_t const  cbReg        = g_aHdaRegMap[idxRegDsc].size;
+                uint32_t        u32Tmp       = 0;
+
+                rc = g_aHdaRegMap[idxRegDsc].pfnRead(pThis, idxRegDsc, &u32Tmp);
+                LogFunc(("\tRead %s[%db] => %x (%Rrc)*\n", g_aHdaRegMap[idxRegDsc].abbrev, cbReg, u32Tmp, rc));
+                if (rc != VINF_SUCCESS)
+                    break;
+                u32Value |= (u32Tmp & g_afMasks[cbReg]) << ((4 - cbLeft) * 8);
+
+                cbLeft -= cbReg;
+                offReg += cbReg;
+                idxRegDsc++;
+            } while (cbLeft > 0 && g_aHdaRegMap[idxRegDsc].offset == offReg);
+
+            if (rc == VINF_SUCCESS)
+                *(uint32_t *)pv = u32Value;
+            else
+                Assert(!IOM_SUCCESS(rc));
+        }
+    }
+    else
+    {
+        rc = VINF_IOM_MMIO_UNUSED_FF;
+        LogFunc(("\tHole at %x is accessed for read\n", offReg));
+    }
+
+    /*
+     * Log the outcome.
+     */
+#ifdef LOG_ENABLED
+    if (cbLog == 4)
+        LogFunc(("\tReturning @%#05x -> %#010x %Rrc\n", offRegLog, *(uint32_t *)pv, rc));
+    else if (cbLog == 2)
+        LogFunc(("\tReturning @%#05x -> %#06x %Rrc\n", offRegLog, *(uint16_t *)pv, rc));
+    else if (cbLog == 1)
+        LogFunc(("\tReturning @%#05x -> %#04x %Rrc\n", offRegLog, *(uint8_t *)pv, rc));
+#endif
+    return rc;
+}
+
+
+DECLINLINE(int) hdaWriteReg(PHDASTATE pThis, int idxRegDsc, uint32_t u32Value, char const *pszLog)
+{
+    if (pThis->fInReset && idxRegDsc != HDA_REG_GCTL)
+    {
+        LogRel2(("HDA: Access to register 0x%x is blocked while reset\n", idxRegDsc));
+        return VINF_SUCCESS;
+    }
+
+    uint32_t idxRegMem = g_aHdaRegMap[idxRegDsc].mem_idx;
+#ifdef LOG_ENABLED
+    uint32_t const u32CurValue = pThis->au32Regs[idxRegMem];
+#endif
+    int rc = g_aHdaRegMap[idxRegDsc].pfnWrite(pThis, idxRegDsc, u32Value);
+    LogFunc(("write %#x -> %s[%db]; %x => %x%s\n", u32Value, g_aHdaRegMap[idxRegDsc].abbrev,
+             g_aHdaRegMap[idxRegDsc].size, u32CurValue, pThis->au32Regs[idxRegMem], pszLog));
+    return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMMMIOWRITE, Looks up and calls the appropriate handler.}
+ */
+PDMBOTHCBDECL(int) hdaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
+{
+    PHDASTATE pThis  = PDMINS_2_DATA(pDevIns, PHDASTATE);
+    int       rc;
+
+    /*
+     * The behavior of accesses that aren't aligned on natural boundraries is
+     * undefined. Just reject them outright.
+     */
+    /** @todo IOM could check this, it could also split the 8 byte accesses for us. */
+    Assert(cb == 1 || cb == 2 || cb == 4 || cb == 8);
+    if (GCPhysAddr & (cb - 1))
+        return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "misaligned write access: GCPhysAddr=%RGp cb=%u\n", GCPhysAddr, cb);
+
+    /*
+     * Look up and log the access.
+     */
+    uint32_t    offReg = GCPhysAddr - pThis->MMIOBaseAddr;
+    int         idxRegDsc = hdaRegLookup(pThis, offReg);
+    uint32_t    idxRegMem = idxRegDsc != -1 ? g_aHdaRegMap[idxRegDsc].mem_idx : UINT32_MAX;
+    uint64_t    u64Value;
+    if (cb == 4)        u64Value = *(uint32_t const *)pv;
+    else if (cb == 2)   u64Value = *(uint16_t const *)pv;
+    else if (cb == 1)   u64Value = *(uint8_t const *)pv;
+    else if (cb == 8)   u64Value = *(uint64_t const *)pv;
+    else
+    {
+        u64Value = 0;   /* shut up gcc. */
+        AssertReleaseMsgFailed(("%u\n", cb));
+    }
+
+#ifdef LOG_ENABLED
+    uint32_t const u32LogOldValue = idxRegDsc >= 0 ? pThis->au32Regs[idxRegMem] : UINT32_MAX;
+    if (idxRegDsc == -1)
+        LogFunc(("@%#05x u32=%#010x cb=%d\n", offReg, *(uint32_t const *)pv, cb));
+    else if (cb == 4)
+        LogFunc(("@%#05x u32=%#010x %s\n", offReg, *(uint32_t *)pv, g_aHdaRegMap[idxRegDsc].abbrev));
+    else if (cb == 2)
+        LogFunc(("@%#05x u16=%#06x (%#010x) %s\n", offReg, *(uint16_t *)pv, *(uint32_t *)pv, g_aHdaRegMap[idxRegDsc].abbrev));
+    else if (cb == 1)
+        LogFunc(("@%#05x u8=%#04x (%#010x) %s\n", offReg, *(uint8_t *)pv, *(uint32_t *)pv, g_aHdaRegMap[idxRegDsc].abbrev));
+
+    if (idxRegDsc >= 0 && g_aHdaRegMap[idxRegDsc].size != cb)
+        LogFunc(("\tsize=%RU32 != cb=%u!!\n", g_aHdaRegMap[idxRegDsc].size, cb));
+#endif
+
+    /*
+     * Try for a direct hit first.
+     */
+    if (idxRegDsc != -1 && g_aHdaRegMap[idxRegDsc].size == cb)
+    {
+        rc = hdaWriteReg(pThis, idxRegDsc, u64Value, "");
+#ifdef LOG_ENABLED
+        LogFunc(("\t%#x -> %#x\n", u32LogOldValue, idxRegMem != UINT32_MAX ? pThis->au32Regs[idxRegMem] : UINT32_MAX));
+#endif
+    }
+    /*
+     * Partial or multiple register access, loop thru the requested memory.
+     */
+    else
+    {
+        /*
+         * If it's an access beyond the start of the register, shift the input
+         * value and fill in missing bits. Natural alignment rules means we
+         * will only see 1 or 2 byte accesses of this kind, so no risk of
+         * shifting out input values.
+         */
+        if (idxRegDsc == -1 && (idxRegDsc = hdaRegLookupWithin(pThis, offReg)) != -1)
+        {
+            uint32_t const cbBefore = offReg - g_aHdaRegMap[idxRegDsc].offset; Assert(cbBefore > 0 && cbBefore < 4);
+            offReg    -= cbBefore;
+            idxRegMem = g_aHdaRegMap[idxRegDsc].mem_idx;
+            u64Value <<= cbBefore * 8;
+            u64Value  |= pThis->au32Regs[idxRegMem] & g_afMasks[cbBefore];
+            LogFunc(("\tWithin register, supplied %u leading bits: %#llx -> %#llx ...\n",
+                     cbBefore * 8, ~g_afMasks[cbBefore] & u64Value, u64Value));
+        }
+
+        /* Loop thru the write area, it may cover multiple registers. */
+        rc = VINF_SUCCESS;
+        for (;;)
+        {
+            uint32_t cbReg;
+            if (idxRegDsc != -1)
+            {
+                idxRegMem = g_aHdaRegMap[idxRegDsc].mem_idx;
+                cbReg = g_aHdaRegMap[idxRegDsc].size;
+                if (cb < cbReg)
+                {
+                    u64Value |= pThis->au32Regs[idxRegMem] & g_afMasks[cbReg] & ~g_afMasks[cb];
+                    LogFunc(("\tSupplying missing bits (%#x): %#llx -> %#llx ...\n",
+                             g_afMasks[cbReg] & ~g_afMasks[cb], u64Value & g_afMasks[cb], u64Value));
+                }
+                uint32_t u32LogOldVal = pThis->au32Regs[idxRegMem];
+                rc = hdaWriteReg(pThis, idxRegDsc, u64Value, "*");
+                LogFunc(("\t%#x -> %#x\n", u32LogOldVal, pThis->au32Regs[idxRegMem]));
+            }
+            else
+            {
+                LogRel(("HDA: Invalid write access @0x%x\n", offReg));
+                cbReg = 1;
+            }
+            if (rc != VINF_SUCCESS)
+                break;
+            if (cbReg >= cb)
+                break;
+
+            /* Advance. */
+            offReg += cbReg;
+            cb     -= cbReg;
+            u64Value >>= cbReg * 8;
+            if (idxRegDsc == -1)
+                idxRegDsc = hdaRegLookup(pThis, offReg);
+            else
+            {
+                idxRegDsc++;
+                if (   (unsigned)idxRegDsc >= RT_ELEMENTS(g_aHdaRegMap)
+                    || g_aHdaRegMap[idxRegDsc].offset != offReg)
+                {
+                    idxRegDsc = -1;
+                }
+            }
+        }
+    }
+
+    return rc;
+}
+
+
+/* PCI callback. */
+
+#ifdef IN_RING3
+/**
+ * @callback_method_impl{FNPCIIOREGIONMAP}
+ */
+static DECLCALLBACK(int) hdaPciIoRegionMap(PPCIDEVICE pPciDev, int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb,
+                                           PCIADDRESSSPACE enmType)
+{
+    PPDMDEVINS  pDevIns = pPciDev->pDevIns;
+    PHDASTATE   pThis = RT_FROM_MEMBER(pPciDev, HDASTATE, PciDev);
+    RTIOPORT    Port = (RTIOPORT)GCPhysAddress;
+    int         rc;
+
+    /*
+     * 18.2 of the ICH6 datasheet defines the valid access widths as byte, word, and double word.
+     *
+     * Let IOM talk DWORDs when reading, saves a lot of complications. On
+     * writing though, we have to do it all ourselves because of sideeffects.
+     */
+    Assert(enmType == PCI_ADDRESS_SPACE_MEM);
+    rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
+                                 IOMMMIO_FLAGS_READ_DWORD
+                               | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+                               hdaMMIOWrite, hdaMMIORead, "HDA");
+
+    if (RT_FAILURE(rc))
+        return rc;
+
+    if (pThis->fR0Enabled)
+    {
+        rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, NIL_RTR0PTR /*pvUser*/,
+                                     "hdaMMIOWrite", "hdaMMIORead");
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    if (pThis->fRCEnabled)
+    {
+        rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/,
+                                     "hdaMMIOWrite", "hdaMMIORead");
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    pThis->MMIOBaseAddr = GCPhysAddress;
+    return VINF_SUCCESS;
+}
+
+
+/* Saved state callbacks. */
+
+static int hdaSaveStream(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, PHDASTREAM pStrm)
+{
+    PHDASTATE pThis = PDMINS_2_DATA(pDevIns, PHDASTATE);
+
+    LogFlowFunc(("[SD%RU8]\n", pStrm->u8Strm));
+
+    /* Save stream ID. */
+    int rc = SSMR3PutU8(pSSM, pStrm->u8Strm);
+    AssertRCReturn(rc, rc);
+    Assert(pStrm->u8Strm <= 7); /** @todo Use a define. */
+
+    rc = SSMR3PutStructEx(pSSM, &pStrm->State, sizeof(HDASTREAMSTATE), 0 /*fFlags*/, g_aSSMStreamStateFields6, NULL);
+    AssertRCReturn(rc, rc);
+
+#ifdef DEBUG /* Sanity checks. */
+    uint64_t u64BaseDMA = RT_MAKE_U64(HDA_STREAM_REG(pThis, BDPL, pStrm->u8Strm),
+                                      HDA_STREAM_REG(pThis, BDPU, pStrm->u8Strm));
+    uint16_t u16LVI     = HDA_STREAM_REG(pThis, LVI, pStrm->u8Strm);
+    uint32_t u32CBL     = HDA_STREAM_REG(pThis, CBL, pStrm->u8Strm);
+
+    hdaBDLEDumpAll(pThis, u64BaseDMA, u16LVI + 1);
+
+    Assert(u64BaseDMA == pStrm->u64BDLBase);
+    Assert(u16LVI     == pStrm->u16LVI);
+    Assert(u32CBL     == pStrm->u32CBL);
+#endif
+
+    rc = SSMR3PutStructEx(pSSM, &pStrm->State.BDLE, sizeof(HDABDLE),
+                          0 /*fFlags*/, g_aSSMBDLEFields6, NULL);
+    AssertRCReturn(rc, rc);
+
+    rc = SSMR3PutStructEx(pSSM, &pStrm->State.BDLE.State, sizeof(HDABDLESTATE),
+                          0 /*fFlags*/, g_aSSMBDLEStateFields6, NULL);
+    AssertRCReturn(rc, rc);
+
+#ifdef DEBUG /* Sanity checks. */
+    PHDABDLE pBDLE = &pStrm->State.BDLE;
+    if (u64BaseDMA)
+    {
+        Assert(pStrm->State.uCurBDLE <= u16LVI + 1);
+
+        HDABDLE curBDLE;
+        rc = hdaBDLEFetch(pThis, &curBDLE, u64BaseDMA, pStrm->State.uCurBDLE);
+        AssertRC(rc);
+
+        Assert(curBDLE.u32BufSize       == pBDLE->u32BufSize);
+        Assert(curBDLE.u64BufAdr        == pBDLE->u64BufAdr);
+        Assert(curBDLE.fIntOnCompletion == pBDLE->fIntOnCompletion);
+    }
+    else
+    {
+        Assert(pBDLE->u64BufAdr  == 0);
+        Assert(pBDLE->u32BufSize == 0);
+    }
+#endif
+    return rc;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) hdaSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+    PHDASTATE pThis = PDMINS_2_DATA(pDevIns, PHDASTATE);
+
+    /* Save Codec nodes states. */
+    hdaCodecSaveState(pThis->pCodec, pSSM);
+
+    /* Save MMIO registers. */
+    AssertCompile(RT_ELEMENTS(pThis->au32Regs) >= HDA_NREGS_SAVED);
+    SSMR3PutU32(pSSM, RT_ELEMENTS(pThis->au32Regs));
+    SSMR3PutMem(pSSM, pThis->au32Regs, sizeof(pThis->au32Regs));
+
+    /* Save number of streams. */
+#ifdef VBOX_WITH_HDA_MIC_IN
+    SSMR3PutU32(pSSM, 3);
+#else
+    SSMR3PutU32(pSSM, 2);
+#endif
+
+    /* Save stream states. */
+    int rc = hdaSaveStream(pDevIns, pSSM, &pThis->StrmStOut);
+    AssertRCReturn(rc, rc);
+#ifdef VBOX_WITH_HDA_MIC_IN
+    rc = hdaSaveStream(pDevIns, pSSM, &pThis->StrmStMicIn);
+    AssertRCReturn(rc, rc);
+#endif
+    rc = hdaSaveStream(pDevIns, pSSM, &pThis->StrmStLineIn);
+    AssertRCReturn(rc, rc);
+
+    return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) hdaLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+    PHDASTATE pThis = PDMINS_2_DATA(pDevIns, PHDASTATE);
+
+    Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
+
+    LogRel2(("hdaLoadExec: uVersion=%RU32, uPass=0x%x\n", uVersion, uPass));
+
+    /*
+     * Load Codec nodes states.
+     */
+    int rc = hdaCodecLoadState(pThis->pCodec, pSSM, uVersion);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("HDA: Failed loading codec state (version %RU32, pass 0x%x), rc=%Rrc\n", uVersion, uPass, rc));
+        return rc;
+    }
+
+    /*
+     * Load MMIO registers.
+     */
+    uint32_t cRegs;
+    switch (uVersion)
+    {
+        case HDA_SSM_VERSION_1:
+            /* Starting with r71199, we would save 112 instead of 113
+               registers due to some code cleanups.  This only affected trunk
+               builds in the 4.1 development period. */
+            cRegs = 113;
+            if (SSMR3HandleRevision(pSSM) >= 71199)
+            {
+                uint32_t uVer = SSMR3HandleVersion(pSSM);
+                if (   VBOX_FULL_VERSION_GET_MAJOR(uVer) == 4
+                    && VBOX_FULL_VERSION_GET_MINOR(uVer) == 0
+                    && VBOX_FULL_VERSION_GET_BUILD(uVer) >= 51)
+                    cRegs = 112;
+            }
+            break;
+
+        case HDA_SSM_VERSION_2:
+        case HDA_SSM_VERSION_3:
+            cRegs = 112;
+            AssertCompile(RT_ELEMENTS(pThis->au32Regs) >= HDA_NREGS_SAVED);
+            break;
+
+        /* Since version 4 we store the register count to stay flexible. */
+        case HDA_SSM_VERSION_4:
+        case HDA_SSM_VERSION_5:
+        case HDA_SSM_VERSION:
+            rc = SSMR3GetU32(pSSM, &cRegs); AssertRCReturn(rc, rc);
+            if (cRegs != RT_ELEMENTS(pThis->au32Regs))
+                LogRel(("HDA: SSM version cRegs is %RU32, expected %RU32\n", cRegs, RT_ELEMENTS(pThis->au32Regs)));
+            break;
+
+        default:
+            LogRel(("HDA: Unsupported / too new saved state version (%RU32)\n", uVersion));
+            return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+    }
+
+    if (cRegs >= RT_ELEMENTS(pThis->au32Regs))
+    {
+        SSMR3GetMem(pSSM, pThis->au32Regs, sizeof(pThis->au32Regs));
+        SSMR3Skip(pSSM, sizeof(uint32_t) * (cRegs - RT_ELEMENTS(pThis->au32Regs)));
+    }
+    else
+        SSMR3GetMem(pSSM, pThis->au32Regs, sizeof(uint32_t) * cRegs);
+
+    /*
+     * Note: Saved states < v5 store LVI (u32BdleMaxCvi) for
+     *       *every* BDLE state, whereas it only needs to be stored
+     *       *once* for every stream. Most of the BDLE state we can
+     *       get out of the registers anyway, so just ignore those values.
+     *
+     *       Also, only the current BDLE was saved, regardless whether
+     *       there were more than one (and there are at least two entries,
+     *       according to the spec).
+     */
+#define HDA_SSM_LOAD_BDLE_STATE_PRE_V5(v, x)                            \
+    rc = SSMR3Skip(pSSM, sizeof(uint32_t));        /* Begin marker */   \
+    AssertRCReturn(rc, rc);                                             \
+    rc = SSMR3GetU64(pSSM, &x.u64BufAdr);          /* u64BdleCviAddr */ \
+    AssertRCReturn(rc, rc);                                             \
+    rc = SSMR3Skip(pSSM, sizeof(uint32_t));        /* u32BdleMaxCvi */  \
+    AssertRCReturn(rc, rc);                                             \
+    rc = SSMR3GetU32(pSSM, &x.State.u32BDLIndex);  /* u32BdleCvi */     \
+    AssertRCReturn(rc, rc);                                             \
+    rc = SSMR3GetU32(pSSM, &x.u32BufSize);         /* u32BdleCviLen */  \
+    AssertRCReturn(rc, rc);                                             \
+    rc = SSMR3GetU32(pSSM, &x.State.u32BufOff);    /* u32BdleCviPos */  \
+    AssertRCReturn(rc, rc);                                             \
+    rc = SSMR3GetBool(pSSM, &x.fIntOnCompletion);  /* fBdleCviIoc */    \
+    AssertRCReturn(rc, rc);                                             \
+    rc = SSMR3GetU32(pSSM, &x.State.cbBelowFIFOW); /* cbUnderFifoW */   \
+    AssertRCReturn(rc, rc);                                             \
+    rc = SSMR3GetMem(pSSM, &x.State.au8FIFO, sizeof(x.State.au8FIFO));  \
+    AssertRCReturn(rc, rc);                                             \
+    rc = SSMR3Skip(pSSM, sizeof(uint32_t));        /* End marker */     \
+    AssertRCReturn(rc, rc);                                             \
+
+    /*
+     * Load BDLEs (Buffer Descriptor List Entries) and DMA counters.
+     */
+    switch (uVersion)
+    {
+        case HDA_SSM_VERSION_1:
+        case HDA_SSM_VERSION_2:
+        case HDA_SSM_VERSION_3:
+        case HDA_SSM_VERSION_4:
+        {
+            /* Only load the internal states.
+             * The rest will be initialized from the saved registers later. */
+
+            /* Note 1: Only the *current* BDLE for a stream was saved! */
+            /* Note 2: The stream's saving order is/was fixed, so don't touch! */
+
+            /* Output */
+            rc = hdaStreamInit(pThis, &pThis->StrmStOut,    4 /* Stream number, hardcoded */);
+            if (RT_FAILURE(rc))
+                break;
+            HDA_SSM_LOAD_BDLE_STATE_PRE_V5(uVersion, pThis->StrmStOut.State.BDLE);
+            pThis->StrmStOut.State.uCurBDLE = pThis->StrmStOut.State.BDLE.State.u32BDLIndex;
+
+            /* Microphone-In */
+            rc = hdaStreamInit(pThis, &pThis->StrmStMicIn,  2 /* Stream number, hardcoded */);
+            if (RT_FAILURE(rc))
+                break;
+            HDA_SSM_LOAD_BDLE_STATE_PRE_V5(uVersion, pThis->StrmStMicIn.State.BDLE);
+            pThis->StrmStMicIn.State.uCurBDLE = pThis->StrmStMicIn.State.BDLE.State.u32BDLIndex;
+
+            /* Line-In */
+            rc = hdaStreamInit(pThis, &pThis->StrmStLineIn, 0 /* Stream number, hardcoded */);
+            if (RT_FAILURE(rc))
+                break;
+            HDA_SSM_LOAD_BDLE_STATE_PRE_V5(uVersion, pThis->StrmStLineIn.State.BDLE);
+            pThis->StrmStLineIn.State.uCurBDLE = pThis->StrmStLineIn.State.BDLE.State.u32BDLIndex;
+            break;
+        }
+
+        /* Since v5 we support flexible stream and BDLE counts. */
+        case HDA_SSM_VERSION_5:
+        case HDA_SSM_VERSION:
+        {
+            uint32_t cStreams;
+            rc = SSMR3GetU32(pSSM, &cStreams);
+            if (RT_FAILURE(rc))
+                break;
+
+            LogRel2(("hdaLoadExec: cStreams=%RU32\n", cStreams));
+
+            /* Load stream states. */
+            for (uint32_t i = 0; i < cStreams; i++)
+            {
+                uint8_t uStreamID;
+                rc = SSMR3GetU8(pSSM, &uStreamID);
+                if (RT_FAILURE(rc))
+                    break;
+
+                PHDASTREAM pStrm = hdaStreamFromID(pThis, uStreamID);
+                HDASTREAM  StreamDummy;
+
+                if (!pStrm)
+                {
+                    pStrm = &StreamDummy;
+                    LogRel2(("HDA: Warning: Stream ID=%RU32 not supported, skipping to load ...\n", uStreamID));
+                    break;
+                }
+
+                RT_BZERO(pStrm, sizeof(HDASTREAM));
+
+                rc = hdaStreamInit(pThis, pStrm, uStreamID);
+                if (RT_FAILURE(rc))
+                {
+                    LogRel(("HDA: Stream #%RU32: Initialization of stream %RU8 failed, rc=%Rrc\n", i, uStreamID, rc));
+                    break;
+                }
+
+                if (uVersion == HDA_SSM_VERSION_5)
+                {
+                    /* Get the current BDLE entry and skip the rest. */
+                    uint16_t cBDLE;
+
+                    rc = SSMR3Skip(pSSM, sizeof(uint32_t)); /* Begin marker */
+                    AssertRC(rc);
+                    rc = SSMR3GetU16(pSSM, &cBDLE);                 /* cBDLE */
+                    AssertRC(rc);
+                    rc = SSMR3GetU16(pSSM, &pStrm->State.uCurBDLE); /* uCurBDLE */
+                    AssertRC(rc);
+                    rc = SSMR3Skip(pSSM, sizeof(uint32_t));         /* End marker */
+                    AssertRC(rc);
+
+                    uint32_t u32BDLEIndex;
+                    for (uint16_t a = 0; a < cBDLE; a++)
+                    {
+                        rc = SSMR3Skip(pSSM, sizeof(uint32_t)); /* Begin marker */
+                        AssertRC(rc);
+                        rc = SSMR3GetU32(pSSM, &u32BDLEIndex);  /* u32BDLIndex */
+                        AssertRC(rc);
+
+                        /* Does the current BDLE index match the current BDLE to process? */
+                        if (u32BDLEIndex == pStrm->State.uCurBDLE)
+                        {
+                            rc = SSMR3GetU32(pSSM, &pStrm->State.BDLE.State.cbBelowFIFOW); /* cbBelowFIFOW */
+                            AssertRC(rc);
+                            rc = SSMR3GetMem(pSSM,
+                                             &pStrm->State.BDLE.State.au8FIFO,
+                                             sizeof(pStrm->State.BDLE.State.au8FIFO));     /* au8FIFO */
+                            AssertRC(rc);
+                            rc = SSMR3GetU32(pSSM, &pStrm->State.BDLE.State.u32BufOff);    /* u32BufOff */
+                            AssertRC(rc);
+                            rc = SSMR3Skip(pSSM, sizeof(uint32_t));                        /* End marker */
+                            AssertRC(rc);
+                        }
+                        else /* Skip not current BDLEs. */
+                        {
+                            rc = SSMR3Skip(pSSM,   sizeof(uint32_t)      /* cbBelowFIFOW */
+                                                 + sizeof(uint8_t) * 256 /* au8FIFO */
+                                                 + sizeof(uint32_t)      /* u32BufOff */
+                                                 + sizeof(uint32_t));    /* End marker */
+                            AssertRC(rc);
+                        }
+                    }
+                }
+                else
+                {
+                    rc = SSMR3GetStructEx(pSSM, &pStrm->State, sizeof(HDASTREAMSTATE),
+                                          0 /* fFlags */, g_aSSMStreamStateFields6, NULL);
+                    if (RT_FAILURE(rc))
+                        break;
+
+                    rc = SSMR3GetStructEx(pSSM, &pStrm->State.BDLE, sizeof(HDABDLE),
+                                          0 /* fFlags */, g_aSSMBDLEFields6, NULL);
+                    if (RT_FAILURE(rc))
+                        break;
+
+                    rc = SSMR3GetStructEx(pSSM, &pStrm->State.BDLE.State, sizeof(HDABDLESTATE),
+                                          0 /* fFlags */, g_aSSMBDLEStateFields6, NULL);
+                    if (RT_FAILURE(rc))
+                        break;
+                }
+            }
+            break;
+        }
+
+        default:
+            AssertReleaseFailed(); /* Never reached. */
+            return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+    }
+
+#undef HDA_SSM_LOAD_BDLE_STATE_PRE_V5
+
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Update stuff after the state changes.
+         */
+        bool fEnableIn    = RT_BOOL(HDA_SDCTL(pThis, 0 /** @todo Use a define. */) & HDA_REG_FIELD_FLAG_MASK(SDCTL, RUN));
+#ifdef VBOX_WITH_HDA_MIC_IN
+        bool fEnableMicIn = RT_BOOL(HDA_SDCTL(pThis, 2 /** @todo Use a define. */) & HDA_REG_FIELD_FLAG_MASK(SDCTL, RUN));
+#endif
+        bool fEnableOut   = RT_BOOL(HDA_SDCTL(pThis, 4 /** @todo Use a define. */) & HDA_REG_FIELD_FLAG_MASK(SDCTL, RUN));
+
+        PHDADRIVER pDrv;
+        RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+        {
+            rc = pDrv->pConnector->pfnEnableIn(pDrv->pConnector, pDrv->LineIn.pStrmIn, fEnableIn);
+            if (RT_FAILURE(rc))
+                break;
+#ifdef VBOX_WITH_HDA_MIC_IN
+            rc = pDrv->pConnector->pfnEnableIn(pDrv->pConnector, pDrv->MicIn.pStrmIn, fEnableMicIn);
+            if (RT_FAILURE(rc))
+                break;
+#endif
+            rc = pDrv->pConnector->pfnEnableOut(pDrv->pConnector, pDrv->Out.pStrmOut, fEnableOut);
+            if (RT_FAILURE(rc))
+                break;
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        pThis->u64CORBBase  = RT_MAKE_U64(HDA_REG(pThis, CORBLBASE), HDA_REG(pThis, CORBUBASE));
+        pThis->u64RIRBBase  = RT_MAKE_U64(HDA_REG(pThis, RIRBLBASE), HDA_REG(pThis, RIRBUBASE));
+        pThis->u64DPBase    = RT_MAKE_U64(HDA_REG(pThis, DPLBASE),   HDA_REG(pThis, DPUBASE));
+
+        /* Also make sure to update the DMA position bit if this was enabled when saving the state. */
+        pThis->fDMAPosition = RT_BOOL(HDA_REG(pThis, DPLBASE) & RT_BIT_32(0));
+    }
+    else
+        LogRel(("HDA: Failed loading device state (version %RU32, pass 0x%x), rc=%Rrc\n", uVersion, uPass, rc));
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+#ifdef DEBUG
+/* Debug and log type formatters. */
+
+/**
+ * @callback_method_impl{FNRTSTRFORMATTYPE}
+ */
+static DECLCALLBACK(size_t) hdaDbgFmtBDLE(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+                                           const char *pszType, void const *pvValue,
+                                           int cchWidth, int cchPrecision, unsigned fFlags,
+                                           void *pvUser)
+{
+    PHDABDLE pBDLE = (PHDABDLE)pvValue;
+    return RTStrFormat(pfnOutput,  pvArgOutput, NULL, 0,
+                       "BDLE(idx:%RU32, off:%RU32, fifow:%RU32, DMA[%RU32 bytes @ 0x%x])",
+                       pBDLE->State.u32BDLIndex, pBDLE->State.u32BufOff, pBDLE->State.cbBelowFIFOW, pBDLE->u32BufSize, pBDLE->u64BufAdr);
+}
+
+/**
+ * @callback_method_impl{FNRTSTRFORMATTYPE}
+ */
+static DECLCALLBACK(size_t) hdaDbgFmtSDCTL(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+                                           const char *pszType, void const *pvValue,
+                                           int cchWidth, int cchPrecision, unsigned fFlags,
+                                           void *pvUser)
+{
+    uint32_t uSDCTL = (uint32_t)(uintptr_t)pvValue;
+    return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
+                       "SDCTL(raw:%#x, DIR:%s, TP:%RTbool, STRIPE:%x, DEIE:%RTbool, FEIE:%RTbool, IOCE:%RTbool, RUN:%RTbool, RESET:%RTbool)",
+                       uSDCTL,
+                       (uSDCTL & HDA_REG_FIELD_FLAG_MASK(SDCTL, DIR)) ? "OUT" : "IN",
+                       RT_BOOL(uSDCTL & HDA_REG_FIELD_FLAG_MASK(SDCTL, TP)),
+                       (uSDCTL & HDA_REG_FIELD_MASK(SDCTL, STRIPE)) >> HDA_SDCTL_STRIPE_SHIFT,
+                       RT_BOOL(uSDCTL & HDA_REG_FIELD_FLAG_MASK(SDCTL, DEIE)),
+                       RT_BOOL(uSDCTL & HDA_REG_FIELD_FLAG_MASK(SDCTL, FEIE)),
+                       RT_BOOL(uSDCTL & HDA_REG_FIELD_FLAG_MASK(SDCTL, ICE)),
+                       RT_BOOL(uSDCTL & HDA_REG_FIELD_FLAG_MASK(SDCTL, RUN)),
+                       RT_BOOL(uSDCTL & HDA_REG_FIELD_FLAG_MASK(SDCTL, SRST)));
+}
+
+/**
+ * @callback_method_impl{FNRTSTRFORMATTYPE}
+ */
+static DECLCALLBACK(size_t) hdaDbgFmtSDFIFOS(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+                                             const char *pszType, void const *pvValue,
+                                             int cchWidth, int cchPrecision, unsigned fFlags,
+                                             void *pvUser)
+{
+    uint32_t uSDFIFOS = (uint32_t)(uintptr_t)pvValue;
+    return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "SDFIFOS(raw:%#x, sdfifos:%RU8 B)", uSDFIFOS, hdaSDFIFOSToBytes(uSDFIFOS));
+}
+
+/**
+ * @callback_method_impl{FNRTSTRFORMATTYPE}
+ */
+static DECLCALLBACK(size_t) hdaDbgFmtSDFIFOW(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+                                             const char *pszType, void const *pvValue,
+                                             int cchWidth, int cchPrecision, unsigned fFlags,
+                                             void *pvUser)
+{
+    uint32_t uSDFIFOW = (uint32_t)(uintptr_t)pvValue;
+    return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "SDFIFOW(raw: %#0x, sdfifow:%d B)", uSDFIFOW, hdaSDFIFOWToBytes(uSDFIFOW));
+}
+
+/**
+ * @callback_method_impl{FNRTSTRFORMATTYPE}
+ */
+static DECLCALLBACK(size_t) hdaDbgFmtSDSTS(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+                                           const char *pszType, void const *pvValue,
+                                           int cchWidth, int cchPrecision, unsigned fFlags,
+                                           void *pvUser)
+{
+    uint32_t uSdSts = (uint32_t)(uintptr_t)pvValue;
+    return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
+                       "SDSTS(raw:%#0x, fifordy:%RTbool, dese:%RTbool, fifoe:%RTbool, bcis:%RTbool)",
+                       uSdSts,
+                       RT_BOOL(uSdSts & HDA_REG_FIELD_FLAG_MASK(SDSTS, FIFORDY)),
+                       RT_BOOL(uSdSts & HDA_REG_FIELD_FLAG_MASK(SDSTS, DE)),
+                       RT_BOOL(uSdSts & HDA_REG_FIELD_FLAG_MASK(SDSTS, FE)),
+                       RT_BOOL(uSdSts & HDA_REG_FIELD_FLAG_MASK(SDSTS, BCIS)));
+}
+
+static int hdaLookUpRegisterByName(PHDASTATE pThis, const char *pszArgs)
+{
+    int iReg = 0;
+    for (; iReg < HDA_NREGS; ++iReg)
+        if (!RTStrICmp(g_aHdaRegMap[iReg].abbrev, pszArgs))
+            return iReg;
+    return -1;
+}
+
+
+static void hdaDbgPrintRegister(PHDASTATE pThis, PCDBGFINFOHLP pHlp, int iHdaIndex)
+{
+    Assert(   pThis
+           && iHdaIndex >= 0
+           && iHdaIndex < HDA_NREGS);
+    pHlp->pfnPrintf(pHlp, "%s: 0x%x\n", g_aHdaRegMap[iHdaIndex].abbrev, pThis->au32Regs[g_aHdaRegMap[iHdaIndex].mem_idx]);
+}
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV}
+ */
+static DECLCALLBACK(void) hdaInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    PHDASTATE pThis = PDMINS_2_DATA(pDevIns, PHDASTATE);
+    int iHdaRegisterIndex = hdaLookUpRegisterByName(pThis, pszArgs);
+    if (iHdaRegisterIndex != -1)
+        hdaDbgPrintRegister(pThis, pHlp, iHdaRegisterIndex);
+    else
+        for(iHdaRegisterIndex = 0; (unsigned int)iHdaRegisterIndex < HDA_NREGS; ++iHdaRegisterIndex)
+            hdaDbgPrintRegister(pThis, pHlp, iHdaRegisterIndex);
+}
+
+static void hdaDbgPrintStream(PHDASTATE pThis, PCDBGFINFOHLP pHlp, int iHdaStrmIndex)
+{
+    Assert(   pThis
+           && iHdaStrmIndex >= 0
+           && iHdaStrmIndex < 7);
+    pHlp->pfnPrintf(pHlp, "Dump of %d HDA Stream:\n", iHdaStrmIndex);
+    pHlp->pfnPrintf(pHlp, "SD%dCTL: %R[sdctl]\n", iHdaStrmIndex, HDA_STREAM_REG(pThis, CTL, iHdaStrmIndex));
+    pHlp->pfnPrintf(pHlp, "SD%dCTS: %R[sdsts]\n", iHdaStrmIndex, HDA_STREAM_REG(pThis, STS, iHdaStrmIndex));
+    pHlp->pfnPrintf(pHlp, "SD%dFIFOS: %R[sdfifos]\n", iHdaStrmIndex, HDA_STREAM_REG(pThis, FIFOS, iHdaStrmIndex));
+    pHlp->pfnPrintf(pHlp, "SD%dFIFOW: %R[sdfifow]\n", iHdaStrmIndex, HDA_STREAM_REG(pThis, FIFOW, iHdaStrmIndex));
+}
+
+static int hdaLookUpStreamIndex(PHDASTATE pThis, const char *pszArgs)
+{
+    /* todo: add args parsing */
+    return -1;
+}
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV}
+ */
+static DECLCALLBACK(void) hdaInfoStream(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    PHDASTATE   pThis         = PDMINS_2_DATA(pDevIns, PHDASTATE);
+    int         iHdaStrmIndex = hdaLookUpStreamIndex(pThis, pszArgs);
+    if (iHdaStrmIndex != -1)
+        hdaDbgPrintStream(pThis, pHlp, iHdaStrmIndex);
+    else
+        for(iHdaStrmIndex = 0; iHdaStrmIndex < 7; ++iHdaStrmIndex)
+            hdaDbgPrintStream(pThis, pHlp, iHdaStrmIndex);
+}
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV}
+ */
+static DECLCALLBACK(void) hdaInfoCodecNodes(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    PHDASTATE pThis = PDMINS_2_DATA(pDevIns, PHDASTATE);
+
+    if (pThis->pCodec->pfnDbgListNodes)
+        pThis->pCodec->pfnDbgListNodes(pThis->pCodec, pHlp, pszArgs);
+    else
+        pHlp->pfnPrintf(pHlp, "Codec implementation doesn't provide corresponding callback\n");
+}
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV}
+ */
+static DECLCALLBACK(void) hdaInfoCodecSelector(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    PHDASTATE pThis = PDMINS_2_DATA(pDevIns, PHDASTATE);
+
+    if (pThis->pCodec->pfnDbgSelector)
+        pThis->pCodec->pfnDbgSelector(pThis->pCodec, pHlp, pszArgs);
+    else
+        pHlp->pfnPrintf(pHlp, "Codec implementation doesn't provide corresponding callback\n");
+}
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV}
+ */
+static DECLCALLBACK(void) hdaInfoMixer(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    PHDASTATE pThis = PDMINS_2_DATA(pDevIns, PHDASTATE);
+
+    if (pThis->pMixer)
+        AudioMixerDebug(pThis->pMixer, pHlp, pszArgs);
+    else
+        pHlp->pfnPrintf(pHlp, "Mixer not available\n");
+}
+#endif /* DEBUG */
+
+/* PDMIBASE */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) hdaQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
+{
+    PHDASTATE pThis = RT_FROM_MEMBER(pInterface, HDASTATE, IBase);
+    Assert(&pThis->IBase == pInterface);
+
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
+    return NULL;
+}
+
+
+/* PDMDEVREG */
+
+/**
+ * Reset notification.
+ *
+ * @returns VBox status code.
+ * @param   pDevIns     The device instance data.
+ *
+ * @remark  The original sources didn't install a reset handler, but it seems to
+ *          make sense to me so we'll do it.
+ */
+static DECLCALLBACK(void) hdaReset(PPDMDEVINS pDevIns)
+{
+    PHDASTATE pThis = PDMINS_2_DATA(pDevIns, PHDASTATE);
+
+    HDA_REG(pThis, GCAP)     = HDA_MAKE_GCAP(4,4,0,0,1); /* see 6.2.1 */
+    HDA_REG(pThis, VMIN)     = 0x00;                     /* see 6.2.2 */
+    HDA_REG(pThis, VMAJ)     = 0x01;                     /* see 6.2.3 */
+    HDA_REG(pThis, OUTPAY)   = 0x003C;                   /* see 6.2.4 */
+    HDA_REG(pThis, INPAY)    = 0x001D;                   /* see 6.2.5 */
+    HDA_REG(pThis, CORBSIZE) = 0x42;                     /* see 6.2.1 */
+    HDA_REG(pThis, RIRBSIZE) = 0x42;                     /* see 6.2.1 */
+    HDA_REG(pThis, CORBRP)   = 0x0;
+    HDA_REG(pThis, RIRBWP)   = 0x0;
+
+    LogFunc(("Resetting ...\n"));
+
+# ifndef VBOX_WITH_AUDIO_CALLBACKS
+    /*
+     * Stop the timer, if any.
+     */
+    int rc2;
+    if (pThis->pTimer)
+    {
+        rc2 = TMTimerStop(pThis->pTimer);
+        AssertRC(rc2);
+    }
+# endif
+
+    /*
+     * Stop any audio currently playing and/or recording.
+     */
+    PHDADRIVER pDrv;
+    RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+    {
+        pDrv->pConnector->pfnEnableIn(pDrv->pConnector, pDrv->LineIn.pStrmIn, false /* Disable */);
+# ifdef VBOX_WITH_HDA_MIC_IN
+        /* Ignore rc. */
+        pDrv->pConnector->pfnEnableIn(pDrv->pConnector, pDrv->MicIn.pStrmIn, false /* Disable */);
+# endif
+        /* Ditto. */
+        pDrv->pConnector->pfnEnableOut(pDrv->pConnector, pDrv->Out.pStrmOut, false /* Disable */);
+        /* Ditto. */
+    }
+
+    pThis->cbCorbBuf = 256 * sizeof(uint32_t); /** @todo Use a define here. */
+
+    if (pThis->pu32CorbBuf)
+        RT_BZERO(pThis->pu32CorbBuf, pThis->cbCorbBuf);
+    else
+        pThis->pu32CorbBuf = (uint32_t *)RTMemAllocZ(pThis->cbCorbBuf);
+
+    pThis->cbRirbBuf = 256 * sizeof(uint64_t); /** @todo Use a define here. */
+    if (pThis->pu64RirbBuf)
+        RT_BZERO(pThis->pu64RirbBuf, pThis->cbRirbBuf);
+    else
+        pThis->pu64RirbBuf = (uint64_t *)RTMemAllocZ(pThis->cbRirbBuf);
+
+    pThis->u64BaseTS = PDMDevHlpTMTimeVirtGetNano(pDevIns);
+
+    for (uint8_t u8Strm = 0; u8Strm < 8; u8Strm++) /** @todo Use a define here. */
+    {
+        PHDASTREAM pStrmSt = NULL;
+        if (u8Strm == 0)      /** @todo Implement dynamic stream IDs. */
+            pStrmSt = &pThis->StrmStLineIn;
+# ifdef VBOX_WITH_HDA_MIC_IN
+        else if (u8Strm == 2) /** @todo Implement dynamic stream IDs. */
+            pStrmSt = &pThis->StrmStMicIn;
+# endif
+        else if (u8Strm == 4) /** @todo Implement dynamic stream IDs. */
+            pStrmSt = &pThis->StrmStOut;
+
+        if (pStrmSt)
+        {
+            /* Remove the RUN bit from SDnCTL in case the stream was in a running state before. */
+            HDA_STREAM_REG(pThis, CTL, u8Strm) &= ~HDA_REG_FIELD_FLAG_MASK(SDCTL, RUN);
+
+            hdaStreamReset(pThis, pStrmSt, u8Strm);
+        }
+    }
+
+    /* Emulation of codec "wake up" (HDA spec 5.5.1 and 6.5). */
+    HDA_REG(pThis, STATESTS) = 0x1;
+
+# ifndef VBOX_WITH_AUDIO_CALLBACKS
+    /*
+     * Start timer again, if any.
+     */
+    if (pThis->pTimer)
+    {
+        LogFunc(("Restarting timer\n"));
+        rc2 = TMTimerSet(pThis->pTimer, TMTimerGet(pThis->pTimer) + pThis->cTimerTicks);
+        AssertRC(rc2);
+    }
+# endif
+
+    LogRel(("HDA: Reset\n"));
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) hdaDestruct(PPDMDEVINS pDevIns)
+{
+    PHDASTATE pThis = PDMINS_2_DATA(pDevIns, PHDASTATE);
+
+    PHDADRIVER pDrv;
+    while (!RTListIsEmpty(&pThis->lstDrv))
+    {
+        pDrv = RTListGetFirst(&pThis->lstDrv, HDADRIVER, Node);
+
+        RTListNodeRemove(&pDrv->Node);
+        RTMemFree(pDrv);
+    }
+
+    if (pThis->pMixer)
+    {
+        AudioMixerDestroy(pThis->pMixer);
+        pThis->pMixer = NULL;
+    }
+
+    if (pThis->pCodec)
+    {
+        int rc = hdaCodecDestruct(pThis->pCodec);
+        AssertRC(rc);
+
+        RTMemFree(pThis->pCodec);
+        pThis->pCodec = NULL;
+    }
+
+    RTMemFree(pThis->pu32CorbBuf);
+    pThis->pu32CorbBuf = NULL;
+
+    RTMemFree(pThis->pu64RirbBuf);
+    pThis->pu64RirbBuf = NULL;
+
+    hdaStreamDestroy(&pThis->StrmStLineIn);
+    hdaStreamDestroy(&pThis->StrmStMicIn);
+    hdaStreamDestroy(&pThis->StrmStOut);
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Attach command, internal version.
+ *
+ * This is called to let the device attach to a driver for a specified LUN
+ * during runtime. This is not called during VM construction, the device
+ * constructor has to attach to all the available drivers.
+ *
+ * @returns VBox status code.
+ * @param   pDevIns     The device instance.
+ * @param   pDrv        Driver to (re-)use for (re-)attaching to.
+ *                      If NULL is specified, a new driver will be created and appended
+ *                      to the driver list.
+ * @param   uLUN        The logical unit which is being detached.
+ * @param   fFlags      Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ */
+static int hdaAttachInternal(PPDMDEVINS pDevIns, PHDADRIVER pDrv, unsigned uLUN, uint32_t fFlags)
+{
+    PHDASTATE pThis = PDMINS_2_DATA(pDevIns, PHDASTATE);
+
+    /*
+     * Attach driver.
+     */
+    char *pszDesc = NULL;
+    if (RTStrAPrintf(&pszDesc, "Audio driver port (HDA) for LUN#%u", uLUN) <= 0)
+        AssertReleaseMsgReturn(pszDesc,
+                               ("Not enough memory for HDA driver port description of LUN #%u\n", uLUN),
+                               VERR_NO_MEMORY);
+
+    PPDMIBASE pDrvBase;
+    int rc = PDMDevHlpDriverAttach(pDevIns, uLUN,
+                                   &pThis->IBase, &pDrvBase, pszDesc);
+    if (RT_SUCCESS(rc))
+    {
+        if (pDrv == NULL)
+            pDrv = (PHDADRIVER)RTMemAllocZ(sizeof(HDADRIVER));
+        if (pDrv)
+        {
+            pDrv->pDrvBase   = pDrvBase;
+            pDrv->pConnector = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIAUDIOCONNECTOR);
+            AssertMsg(pDrv->pConnector != NULL, ("Configuration error: LUN#%u has no host audio interface, rc=%Rrc\n", uLUN, rc));
+            pDrv->pHDAState  = pThis;
+            pDrv->uLUN       = uLUN;
+
+            /*
+             * For now we always set the driver at LUN 0 as our primary
+             * host backend. This might change in the future.
+             */
+            if (pDrv->uLUN == 0)
+                pDrv->Flags |= PDMAUDIODRVFLAG_PRIMARY;
+
+            LogFunc(("LUN#%u: pCon=%p, drvFlags=0x%x\n", uLUN, pDrv->pConnector, pDrv->Flags));
+
+            /* Attach to driver list if not attached yet. */
+            if (!pDrv->fAttached)
+            {
+                RTListAppend(&pThis->lstDrv, &pDrv->Node);
+                pDrv->fAttached = true;
+            }
+        }
+        else
+            rc = VERR_NO_MEMORY;
+    }
+    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+        LogFunc(("No attached driver for LUN #%u\n", uLUN));
+
+    if (RT_FAILURE(rc))
+    {
+        /* Only free this string on failure;
+         * must remain valid for the live of the driver instance. */
+        RTStrFree(pszDesc);
+    }
+
+    LogFunc(("uLUN=%u, fFlags=0x%x, rc=%Rrc\n", uLUN, fFlags, rc));
+    return rc;
+}
+
+/**
+ * Attach command.
+ *
+ * This is called to let the device attach to a driver for a specified LUN
+ * during runtime. This is not called during VM construction, the device
+ * constructor has to attach to all the available drivers.
+ *
+ * @returns VBox status code.
+ * @param   pDevIns     The device instance.
+ * @param   uLUN        The logical unit which is being detached.
+ * @param   fFlags      Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ */
+static DECLCALLBACK(int) hdaAttach(PPDMDEVINS pDevIns, unsigned uLUN, uint32_t fFlags)
+{
+    return hdaAttachInternal(pDevIns, NULL /* pDrv */, uLUN, fFlags);
+}
+
+static DECLCALLBACK(void) hdaDetach(PPDMDEVINS pDevIns, unsigned uLUN, uint32_t fFlags)
+{
+    LogFunc(("iLUN=%u, fFlags=0x%x\n", uLUN, fFlags));
+}
+
+/**
+ * Re-attach.
+ *
+ * @returns VBox status code.
+ * @param   pThis       Device instance.
+ * @param   pDrv        Driver instance used for attaching to.
+ *                      If NULL is specified, a new driver will be created and appended
+ *                      to the driver list.
+ * @param   uLUN        The logical unit which is being re-detached.
+ * @param   pszDriver   Driver name.
+ */
+static int hdaReattach(PHDASTATE pThis, PHDADRIVER pDrv, uint8_t uLUN, const char *pszDriver)
+{
+    AssertPtrReturn(pThis,     VERR_INVALID_POINTER);
+    AssertPtrReturn(pszDriver, VERR_INVALID_POINTER);
+
+    PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
+    PCFGMNODE pRoot = CFGMR3GetRoot(pVM);
+    PCFGMNODE pDev0 = CFGMR3GetChild(pRoot, "Devices/hda/0/");
+
+    /* Remove LUN branch. */
+    CFGMR3RemoveNode(CFGMR3GetChildF(pDev0, "LUN#%u/", uLUN));
+
+    if (pDrv)
+    {
+        /* Re-use a driver instance => detach the driver before. */
+        int rc = PDMDevHlpDriverDetach(pThis->pDevInsR3, PDMIBASE_2_PDMDRV(pDrv->pDrvBase), 0 /* fFlags */);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+#define RC_CHECK() if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; }
+
+    int rc = VINF_SUCCESS;
+    do
+    {
+        PCFGMNODE pLunL0;
+        rc = CFGMR3InsertNodeF(pDev0, &pLunL0, "LUN#%u/", uLUN);        RC_CHECK();
+        rc = CFGMR3InsertString(pLunL0, "Driver",       "AUDIO");       RC_CHECK();
+        rc = CFGMR3InsertNode(pLunL0,   "Config/",       NULL);         RC_CHECK();
+
+        PCFGMNODE pLunL1, pLunL2;
+        rc = CFGMR3InsertNode  (pLunL0, "AttachedDriver/", &pLunL1);    RC_CHECK();
+        rc = CFGMR3InsertNode  (pLunL1,  "Config/",        &pLunL2);    RC_CHECK();
+        rc = CFGMR3InsertString(pLunL1,  "Driver",          pszDriver); RC_CHECK();
+
+        rc = CFGMR3InsertString(pLunL2, "AudioDriver", pszDriver);      RC_CHECK();
+
+    } while (0);
+
+    if (RT_SUCCESS(rc))
+        rc = hdaAttachInternal(pThis->pDevInsR3, pDrv, uLUN, 0 /* fFlags */);
+
+    LogFunc(("pThis=%p, uLUN=%u, pszDriver=%s, rc=%Rrc\n", pThis, uLUN, pszDriver, rc));
+
+#undef RC_CHECK
+
+    return rc;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) hdaConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+    PHDASTATE pThis = PDMINS_2_DATA(pDevIns, PHDASTATE);
+    Assert(iInstance == 0);
+    PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+
+    /*
+     * Validations.
+     */
+    if (!CFGMR3AreValuesValid(pCfg, "R0Enabled\0"
+                                    "RCEnabled\0"
+                                    "TimerHz\0"))
+        return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
+                                N_ ("Invalid configuration for the Intel HDA device"));
+
+    int rc = CFGMR3QueryBoolDef(pCfg, "RCEnabled", &pThis->fRCEnabled, false);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("HDA configuration error: failed to read RCEnabled as boolean"));
+    rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, false);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("HDA configuration error: failed to read R0Enabled as boolean"));
+#ifndef VBOX_WITH_AUDIO_CALLBACKS
+    uint16_t uTimerHz;
+    rc = CFGMR3QueryU16Def(pCfg, "TimerHz", &uTimerHz, 200 /* Hz */);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("HDA configuration error: failed to read Hertz (Hz) rate as unsigned integer"));
+#endif
+
+    /*
+     * Initialize data (most of it anyway).
+     */
+    pThis->pDevInsR3                = pDevIns;
+    pThis->pDevInsR0                = PDMDEVINS_2_R0PTR(pDevIns);
+    pThis->pDevInsRC                = PDMDEVINS_2_RCPTR(pDevIns);
+    /* IBase */
+    pThis->IBase.pfnQueryInterface  = hdaQueryInterface;
+
+    /* PCI Device */
+    PCIDevSetVendorId           (&pThis->PciDev, HDA_PCI_VENDOR_ID); /* nVidia */
+    PCIDevSetDeviceId           (&pThis->PciDev, HDA_PCI_DEVICE_ID); /* HDA */
+
+    PCIDevSetCommand            (&pThis->PciDev, 0x0000); /* 04 rw,ro - pcicmd. */
+    PCIDevSetStatus             (&pThis->PciDev, VBOX_PCI_STATUS_CAP_LIST); /* 06 rwc?,ro? - pcists. */
+    PCIDevSetRevisionId         (&pThis->PciDev, 0x01);   /* 08 ro - rid. */
+    PCIDevSetClassProg          (&pThis->PciDev, 0x00);   /* 09 ro - pi. */
+    PCIDevSetClassSub           (&pThis->PciDev, 0x03);   /* 0a ro - scc; 03 == HDA. */
+    PCIDevSetClassBase          (&pThis->PciDev, 0x04);   /* 0b ro - bcc; 04 == multimedia. */
+    PCIDevSetHeaderType         (&pThis->PciDev, 0x00);   /* 0e ro - headtyp. */
+    PCIDevSetBaseAddress        (&pThis->PciDev, 0,       /* 10 rw - MMIO */
+                                 false /* fIoSpace */, false /* fPrefetchable */, true /* f64Bit */, 0x00000000);
+    PCIDevSetInterruptLine      (&pThis->PciDev, 0x00);   /* 3c rw. */
+    PCIDevSetInterruptPin       (&pThis->PciDev, 0x01);   /* 3d ro - INTA#. */
+
+#if defined(HDA_AS_PCI_EXPRESS)
+    PCIDevSetCapabilityList     (&pThis->PciDev, 0x80);
+#elif defined(VBOX_WITH_MSI_DEVICES)
+    PCIDevSetCapabilityList     (&pThis->PciDev, 0x60);
+#else
+    PCIDevSetCapabilityList     (&pThis->PciDev, 0x50);   /* ICH6 datasheet 18.1.16 */
+#endif
+
+    /// @todo r=michaln: If there are really no PCIDevSetXx for these, the meaning
+    /// of these values needs to be properly documented!
+    /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */
+    PCIDevSetByte(&pThis->PciDev, 0x40, 0x01);
+
+    /* Power Management */
+    PCIDevSetByte(&pThis->PciDev, 0x50 + 0, VBOX_PCI_CAP_ID_PM);
+    PCIDevSetByte(&pThis->PciDev, 0x50 + 1, 0x0); /* next */
+    PCIDevSetWord(&pThis->PciDev, 0x50 + 2, VBOX_PCI_PM_CAP_DSI | 0x02 /* version, PM1.1 */ );
+
+#ifdef HDA_AS_PCI_EXPRESS
+    /* PCI Express */
+    PCIDevSetByte(&pThis->PciDev, 0x80 + 0, VBOX_PCI_CAP_ID_EXP); /* PCI_Express */
+    PCIDevSetByte(&pThis->PciDev, 0x80 + 1, 0x60); /* next */
+    /* Device flags */
+    PCIDevSetWord(&pThis->PciDev, 0x80 + 2,
+                   /* version */ 0x1     |
+                   /* Root Complex Integrated Endpoint */ (VBOX_PCI_EXP_TYPE_ROOT_INT_EP << 4) |
+                   /* MSI */ (100) << 9 );
+    /* Device capabilities */
+    PCIDevSetDWord(&pThis->PciDev, 0x80 + 4, VBOX_PCI_EXP_DEVCAP_FLRESET);
+    /* Device control */
+    PCIDevSetWord( &pThis->PciDev, 0x80 + 8, 0);
+    /* Device status */
+    PCIDevSetWord( &pThis->PciDev, 0x80 + 10, 0);
+    /* Link caps */
+    PCIDevSetDWord(&pThis->PciDev, 0x80 + 12, 0);
+    /* Link control */
+    PCIDevSetWord( &pThis->PciDev, 0x80 + 16, 0);
+    /* Link status */
+    PCIDevSetWord( &pThis->PciDev, 0x80 + 18, 0);
+    /* Slot capabilities */
+    PCIDevSetDWord(&pThis->PciDev, 0x80 + 20, 0);
+    /* Slot control */
+    PCIDevSetWord( &pThis->PciDev, 0x80 + 24, 0);
+    /* Slot status */
+    PCIDevSetWord( &pThis->PciDev, 0x80 + 26, 0);
+    /* Root control */
+    PCIDevSetWord( &pThis->PciDev, 0x80 + 28, 0);
+    /* Root capabilities */
+    PCIDevSetWord( &pThis->PciDev, 0x80 + 30, 0);
+    /* Root status */
+    PCIDevSetDWord(&pThis->PciDev, 0x80 + 32, 0);
+    /* Device capabilities 2 */
+    PCIDevSetDWord(&pThis->PciDev, 0x80 + 36, 0);
+    /* Device control 2 */
+    PCIDevSetQWord(&pThis->PciDev, 0x80 + 40, 0);
+    /* Link control 2 */
+    PCIDevSetQWord(&pThis->PciDev, 0x80 + 48, 0);
+    /* Slot control 2 */
+    PCIDevSetWord( &pThis->PciDev, 0x80 + 56, 0);
+#endif
+
+    /*
+     * Register the PCI device.
+     */
+    rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 0x4000, PCI_ADDRESS_SPACE_MEM, hdaPciIoRegionMap);
+    if (RT_FAILURE(rc))
+        return rc;
+
+#ifdef VBOX_WITH_MSI_DEVICES
+    PDMMSIREG MsiReg;
+    RT_ZERO(MsiReg);
+    MsiReg.cMsiVectors    = 1;
+    MsiReg.iMsiCapOffset  = 0x60;
+    MsiReg.iMsiNextOffset = 0x50;
+    rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
+    if (RT_FAILURE(rc))
+    {
+        /* That's OK, we can work without MSI */
+        PCIDevSetCapabilityList(&pThis->PciDev, 0x50);
+    }
+#endif
+
+    rc = PDMDevHlpSSMRegister(pDevIns, HDA_SSM_VERSION, sizeof(*pThis), hdaSaveExec, hdaLoadExec);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    RTListInit(&pThis->lstDrv);
+
+    uint8_t uLUN;
+    for (uLUN = 0; uLUN < UINT8_MAX; ++uLUN)
+    {
+        LogFunc(("Trying to attach driver for LUN #%RU32 ...\n", uLUN));
+        rc = hdaAttachInternal(pDevIns, NULL /* pDrv */, uLUN, 0 /* fFlags */);
+        if (RT_FAILURE(rc))
+        {
+            if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+                rc = VINF_SUCCESS;
+            else if (rc == VERR_AUDIO_BACKEND_INIT_FAILED)
+            {
+                hdaReattach(pThis, NULL /* pDrv */, uLUN, "NullAudio");
+                PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
+                    N_("No audio devices could be opened. Selecting the NULL audio backend "
+                       "with the consequence that no sound is audible"));
+                /* attaching to the NULL audio backend will never fail */
+                rc = VINF_SUCCESS;
+            }
+            break;
+        }
+    }
+
+    LogFunc(("cLUNs=%RU8, rc=%Rrc\n", uLUN, rc));
+
+    if (RT_SUCCESS(rc))
+    {
+        rc = AudioMixerCreate("HDA Mixer", 0 /* uFlags */, &pThis->pMixer);
+        if (RT_SUCCESS(rc))
+        {
+            /* Set a default audio format for our mixer. */
+            PDMAUDIOSTREAMCFG streamCfg;
+            streamCfg.uHz           = 44100;
+            streamCfg.cChannels     = 2;
+            streamCfg.enmFormat     = AUD_FMT_S16;
+            streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
+
+            rc = AudioMixerSetDeviceFormat(pThis->pMixer, &streamCfg);
+            AssertRC(rc);
+
+            /* Add all required audio sinks. */
+            rc = AudioMixerAddSink(pThis->pMixer, "[Playback] PCM Output",
+                                   AUDMIXSINKDIR_OUTPUT, &pThis->pSinkOutput);
+            AssertRC(rc);
+
+            rc = AudioMixerAddSink(pThis->pMixer, "[Recording] Line In",
+                                   AUDMIXSINKDIR_INPUT, &pThis->pSinkLineIn);
+            AssertRC(rc);
+
+            rc = AudioMixerAddSink(pThis->pMixer, "[Recording] Microphone In",
+                                   AUDMIXSINKDIR_INPUT, &pThis->pSinkMicIn);
+            AssertRC(rc);
+
+            /* There is no master volume control. Set the master to max. */
+            PDMAUDIOVOLUME vol = { false, 255, 255 };
+            rc = AudioMixerSetMasterVolume(pThis->pMixer, &vol);
+            AssertRC(rc);
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        /* Construct codec. */
+        pThis->pCodec = (PHDACODEC)RTMemAllocZ(sizeof(HDACODEC));
+        if (!pThis->pCodec)
+            return PDMDEV_SET_ERROR(pDevIns, VERR_NO_MEMORY, N_("Out of memory allocating HDA codec state"));
+
+        /* Audio driver callbacks for multiplexing. */
+        pThis->pCodec->pfnCloseIn   = hdaCloseIn;
+        pThis->pCodec->pfnCloseOut  = hdaCloseOut;
+        pThis->pCodec->pfnOpenIn    = hdaOpenIn;
+        pThis->pCodec->pfnOpenOut   = hdaOpenOut;
+        pThis->pCodec->pfnReset     = hdaCodecReset;
+        pThis->pCodec->pfnSetVolume = hdaSetVolume;
+
+        pThis->pCodec->pHDAState = pThis; /* Assign HDA controller state to codec. */
+
+        /* Construct the codec. */
+        rc = hdaCodecConstruct(pDevIns, pThis->pCodec, 0 /* Codec index */, pCfg);
+        if (RT_FAILURE(rc))
+            AssertRCReturn(rc, rc);
+
+        /* ICH6 datasheet defines 0 values for SVID and SID (18.1.14-15), which together with values returned for
+           verb F20 should provide device/codec recognition. */
+        Assert(pThis->pCodec->u16VendorId);
+        Assert(pThis->pCodec->u16DeviceId);
+        PCIDevSetSubSystemVendorId(&pThis->PciDev, pThis->pCodec->u16VendorId); /* 2c ro - intel.) */
+        PCIDevSetSubSystemId(      &pThis->PciDev, pThis->pCodec->u16DeviceId); /* 2e ro. */
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        rc = hdaStreamCreate(&pThis->StrmStLineIn);
+        AssertRC(rc);
+#ifdef VBOX_WITH_HDA_MIC_IN
+        rc = hdaStreamCreate(&pThis->StrmStMicIn);
+        AssertRC(rc);
+#endif
+        rc = hdaStreamCreate(&pThis->StrmStOut);
+        AssertRC(rc);
+
+        PHDADRIVER pDrv;
+        RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+        {
+            /*
+             * Only primary drivers are critical for the VM to run. Everything else
+             * might not worth showing an own error message box in the GUI.
+             */
+            if (!(pDrv->Flags & PDMAUDIODRVFLAG_PRIMARY))
+                continue;
+
+            PPDMIAUDIOCONNECTOR pCon = pDrv->pConnector;
+            AssertPtr(pCon);
+
+            bool fValidLineIn = pCon->pfnIsValidIn(pCon, pDrv->LineIn.pStrmIn);
+#ifdef VBOX_WITH_HDA_MIC_IN
+            bool fValidMicIn  = pCon->pfnIsValidIn (pCon, pDrv->MicIn.pStrmIn);
+#endif
+            bool fValidOut    = pCon->pfnIsValidOut(pCon, pDrv->Out.pStrmOut);
+
+            if (    !fValidLineIn
+#ifdef VBOX_WITH_HDA_MIC_IN
+                 && !fValidMicIn
+#endif
+                 && !fValidOut)
+            {
+                LogRel(("HDA: Falling back to NULL backend (no sound audible)\n"));
+
+                hdaReset(pDevIns);
+                hdaReattach(pThis, pDrv, pDrv->uLUN, "NullAudio");
+
+                PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
+                    N_("No audio devices could be opened. Selecting the NULL audio backend "
+                       "with the consequence that no sound is audible"));
+            }
+            else
+            {
+                bool fWarn = false;
+
+                PDMAUDIOBACKENDCFG backendCfg;
+                int rc2 = pCon->pfnGetConfiguration(pCon, &backendCfg);
+                if (RT_SUCCESS(rc2))
+                {
+                    if (backendCfg.cMaxHstStrmsIn)
+                    {
+#ifdef VBOX_WITH_HDA_MIC_IN
+                        /* If the audio backend supports two or more input streams at once,
+                         * warn if one of our two inputs (microphone-in and line-in) failed to initialize. */
+                        if (backendCfg.cMaxHstStrmsIn >= 2)
+                            fWarn = !fValidLineIn || !fValidMicIn;
+                        /* If the audio backend only supports one input stream at once (e.g. pure ALSA, and
+                         * *not* ALSA via PulseAudio plugin!), only warn if both of our inputs failed to initialize.
+                         * One of the two simply is not in use then. */
+                        else if (backendCfg.cMaxHstStrmsIn == 1)
+                            fWarn = !fValidLineIn && !fValidMicIn;
+                        /* Don't warn if our backend is not able of supporting any input streams at all. */
+#else
+                        /* We only have line-in as input source. */
+                        fWarn = !fValidLineIn;
+#endif
+                    }
+
+                    if (   !fWarn
+                        && backendCfg.cMaxHstStrmsOut)
+                    {
+                        fWarn = !fValidOut;
+                    }
+                }
+                else
+                    AssertReleaseMsgFailed(("Unable to retrieve audio backend configuration for LUN #%RU8, rc=%Rrc\n",
+                                            pDrv->uLUN, rc2));
+
+                if (fWarn)
+                {
+                    char   szMissingStreams[255];
+                    size_t len = 0;
+                    if (!fValidLineIn)
+                    {
+                        LogRel(("HDA: WARNING: Unable to open PCM line input for LUN #%RU8!\n", pDrv->uLUN));
+                        len = RTStrPrintf(szMissingStreams, sizeof(szMissingStreams), "PCM Input");
+                    }
+#ifdef VBOX_WITH_HDA_MIC_IN
+                    if (!fValidMicIn)
+                    {
+                        LogRel(("HDA: WARNING: Unable to open PCM microphone input for LUN #%RU8!\n", pDrv->uLUN));
+                        len += RTStrPrintf(szMissingStreams + len,
+                                           sizeof(szMissingStreams) - len, len ? ", PCM Microphone" : "PCM Microphone");
+                    }
+#endif
+                    if (!fValidOut)
+                    {
+                        LogRel(("HDA: WARNING: Unable to open PCM output for LUN #%RU8!\n", pDrv->uLUN));
+                        len += RTStrPrintf(szMissingStreams + len,
+                                           sizeof(szMissingStreams) - len, len ? ", PCM Output" : "PCM Output");
+                    }
+
+                    PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
+                                               N_("Some HDA audio streams (%s) could not be opened. Guest applications generating audio "
+                                                  "output or depending on audio input may hang. Make sure your host audio device "
+                                                  "is working properly. Check the logfile for error messages of the audio "
+                                                  "subsystem"), szMissingStreams);
+                }
+            }
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        hdaReset(pDevIns);
+
+        /*
+         * 18.2.6,7 defines that values of this registers might be cleared on power on/reset
+         * hdaReset shouldn't affects these registers.
+         */
+        HDA_REG(pThis, WAKEEN)   = 0x0;
+        HDA_REG(pThis, STATESTS) = 0x0;
+
+#ifdef DEBUG
+        /*
+         * Debug and string formatter types.
+         */
+        PDMDevHlpDBGFInfoRegister(pDevIns, "hda",         "HDA info. (hda [register case-insensitive])",    hdaInfo);
+        PDMDevHlpDBGFInfoRegister(pDevIns, "hdastrm",     "HDA stream info. (hdastrm [stream number])",     hdaInfoStream);
+        PDMDevHlpDBGFInfoRegister(pDevIns, "hdcnodes",    "HDA codec nodes.",                               hdaInfoCodecNodes);
+        PDMDevHlpDBGFInfoRegister(pDevIns, "hdcselector", "HDA codec's selector states [node number].",     hdaInfoCodecSelector);
+        PDMDevHlpDBGFInfoRegister(pDevIns, "hdamixer",    "HDA mixer state.",                               hdaInfoMixer);
+
+        rc = RTStrFormatTypeRegister("bdle",    hdaDbgFmtBDLE,    NULL);
+        AssertRC(rc);
+        rc = RTStrFormatTypeRegister("sdctl",   hdaDbgFmtSDCTL,   NULL);
+        AssertRC(rc);
+        rc = RTStrFormatTypeRegister("sdsts",   hdaDbgFmtSDSTS,   NULL);
+        AssertRC(rc);
+        rc = RTStrFormatTypeRegister("sdfifos", hdaDbgFmtSDFIFOS, NULL);
+        AssertRC(rc);
+        rc = RTStrFormatTypeRegister("sdfifow", hdaDbgFmtSDFIFOW, NULL);
+        AssertRC(rc);
+#endif /* DEBUG */
+
+        /*
+         * Some debug assertions.
+         */
+        for (unsigned i = 0; i < RT_ELEMENTS(g_aHdaRegMap); i++)
+        {
+            struct HDAREGDESC const *pReg     = &g_aHdaRegMap[i];
+            struct HDAREGDESC const *pNextReg = i + 1 < RT_ELEMENTS(g_aHdaRegMap) ?  &g_aHdaRegMap[i + 1] : NULL;
+
+            /* binary search order. */
+            AssertReleaseMsg(!pNextReg || pReg->offset + pReg->size <= pNextReg->offset,
+                             ("[%#x] = {%#x LB %#x}  vs. [%#x] = {%#x LB %#x}\n",
+                              i, pReg->offset, pReg->size, i + 1, pNextReg->offset, pNextReg->size));
+
+            /* alignment. */
+            AssertReleaseMsg(   pReg->size == 1
+                             || (pReg->size == 2 && (pReg->offset & 1) == 0)
+                             || (pReg->size == 3 && (pReg->offset & 3) == 0)
+                             || (pReg->size == 4 && (pReg->offset & 3) == 0),
+                             ("[%#x] = {%#x LB %#x}\n", i, pReg->offset, pReg->size));
+
+            /* registers are packed into dwords - with 3 exceptions with gaps at the end of the dword. */
+            AssertRelease(((pReg->offset + pReg->size) & 3) == 0 || pNextReg);
+            if (pReg->offset & 3)
+            {
+                struct HDAREGDESC const *pPrevReg = i > 0 ?  &g_aHdaRegMap[i - 1] : NULL;
+                AssertReleaseMsg(pPrevReg, ("[%#x] = {%#x LB %#x}\n", i, pReg->offset, pReg->size));
+                if (pPrevReg)
+                    AssertReleaseMsg(pPrevReg->offset + pPrevReg->size == pReg->offset,
+                                     ("[%#x] = {%#x LB %#x}  vs. [%#x] = {%#x LB %#x}\n",
+                                      i - 1, pPrevReg->offset, pPrevReg->size, i + 1, pReg->offset, pReg->size));
+            }
+#if 0
+            if ((pReg->offset + pReg->size) & 3)
+            {
+                AssertReleaseMsg(pNextReg, ("[%#x] = {%#x LB %#x}\n", i, pReg->offset, pReg->size));
+                if (pNextReg)
+                    AssertReleaseMsg(pReg->offset + pReg->size == pNextReg->offset,
+                                     ("[%#x] = {%#x LB %#x}  vs. [%#x] = {%#x LB %#x}\n",
+                                      i, pReg->offset, pReg->size, i + 1,  pNextReg->offset, pNextReg->size));
+            }
+#endif
+            /* The final entry is a full DWORD, no gaps! Allows shortcuts. */
+            AssertReleaseMsg(pNextReg || ((pReg->offset + pReg->size) & 3) == 0,
+                             ("[%#x] = {%#x LB %#x}\n", i, pReg->offset, pReg->size));
+        }
+    }
+
+# ifndef VBOX_WITH_AUDIO_CALLBACKS
+    if (RT_SUCCESS(rc))
+    {
+        /* Start the emulation timer. */
+        rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, hdaTimer, pThis,
+                                    TMTIMER_FLAGS_NO_CRIT_SECT, "DevIchHda", &pThis->pTimer);
+        AssertRCReturn(rc, rc);
+
+        if (RT_SUCCESS(rc))
+        {
+            pThis->cTimerTicks = TMTimerGetFreq(pThis->pTimer) / uTimerHz;
+            pThis->uTimerTS    = TMTimerGet(pThis->pTimer);
+            LogFunc(("Timer ticks=%RU64 (%RU16 Hz)\n", pThis->cTimerTicks, uTimerHz));
+
+            /* Fire off timer. */
+            TMTimerSet(pThis->pTimer, TMTimerGet(pThis->pTimer) + pThis->cTimerTicks);
+        }
+    }
+# else
+    if (RT_SUCCESS(rc))
+    {
+        PHDADRIVER pDrv;
+        RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)
+        {
+            /* Only register primary driver.
+             * The device emulation does the output multiplexing then. */
+            if (pDrv->Flags != PDMAUDIODRVFLAG_PRIMARY)
+                continue;
+
+            PDMAUDIOCALLBACK AudioCallbacks[2];
+
+            HDACALLBACKCTX Ctx = { pThis, pDrv };
+
+            AudioCallbacks[0].enmType     = PDMAUDIOCALLBACKTYPE_INPUT;
+            AudioCallbacks[0].pfnCallback = hdaCallbackInput;
+            AudioCallbacks[0].pvCtx       = &Ctx;
+            AudioCallbacks[0].cbCtx       = sizeof(HDACALLBACKCTX);
+
+            AudioCallbacks[1].enmType     = PDMAUDIOCALLBACKTYPE_OUTPUT;
+            AudioCallbacks[1].pfnCallback = hdaCallbackOutput;
+            AudioCallbacks[1].pvCtx       = &Ctx;
+            AudioCallbacks[1].cbCtx       = sizeof(HDACALLBACKCTX);
+
+            rc = pDrv->pConnector->pfnRegisterCallbacks(pDrv->pConnector, AudioCallbacks, RT_ELEMENTS(AudioCallbacks));
+            if (RT_FAILURE(rc))
+                break;
+        }
+    }
+# endif
+
+# ifdef VBOX_WITH_STATISTICS
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Register statistics.
+         */
+#  ifndef VBOX_WITH_AUDIO_CALLBACKS
+        PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer,            STAMTYPE_PROFILE, "/Devices/HDA/Timer",             STAMUNIT_TICKS_PER_CALL, "Profiling hdaTimer.");
+#  endif
+        PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesRead,        STAMTYPE_COUNTER, "/Devices/HDA/BytesRead"   ,      STAMUNIT_BYTES,          "Bytes read from HDA emulation.");
+        PDMDevHlpSTAMRegister(pDevIns, &pThis->StatBytesWritten,     STAMTYPE_COUNTER, "/Devices/HDA/BytesWritten",      STAMUNIT_BYTES,          "Bytes written to HDA emulation.");
+    }
+# endif
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DeviceICH6_HDA =
+{
+    /* u32Version */
+    PDM_DEVREG_VERSION,
+    /* szName */
+    "hda",
+    /* szRCMod */
+    "VBoxDDRC.rc",
+    /* szR0Mod */
+    "VBoxDDR0.r0",
+    /* pszDescription */
+    "Intel HD Audio Controller",
+    /* fFlags */
+    PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
+    /* fClass */
+    PDM_DEVREG_CLASS_AUDIO,
+    /* cMaxInstances */
+    1,
+    /* cbInstance */
+    sizeof(HDASTATE),
+    /* pfnConstruct */
+    hdaConstruct,
+    /* pfnDestruct */
+    hdaDestruct,
+    /* pfnRelocate */
+    NULL,
+    /* pfnMemSetup */
+    NULL,
+    /* pfnPowerOn */
+    NULL,
+    /* pfnReset */
+    hdaReset,
+    /* pfnSuspend */
+    NULL,
+    /* pfnResume */
+    NULL,
+    /* pfnAttach */
+    hdaAttach,
+    /* pfnDetach */
+    hdaDetach,
+    /* pfnQueryInterface. */
+    NULL,
+    /* pfnInitComplete */
+    NULL,
+    /* pfnPowerOff */
+    NULL,
+    /* pfnSoftReset */
+    NULL,
+    /* u32VersionEnd */
+    PDM_DEVREG_VERSION
+};
+
+#endif /* IN_RING3 */
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Index: /trunk/src/VBox/Devices/Audio_old/DevIchHdaCodec.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DevIchHdaCodec.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DevIchHdaCodec.cpp	(revision 61413)
@@ -0,0 +1,2774 @@
+/* $Id$ */
+/** @file
+ * DevIchHdaCodec - VBox ICH Intel HD Audio Codec.
+ *
+ * Implemented against "Intel I/O Controller Hub 6 (ICH6) High Definition
+ * Audio / AC '97 - Programmer's Reference Manual (PRM)", document number
+ * 302349-003.
+ */
+
+/*
+ * Copyright (C) 2006-2016 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                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_HDA_CODEC
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdmaudioifs.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include <iprt/string.h>
+#include <iprt/mem.h>
+#include <iprt/asm.h>
+#include <iprt/cpp/utils.h>
+
+#include "VBoxDD.h"
+#include "DevIchHdaCodec.h"
+
+
+/*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+/* PRM 5.3.1 */
+/** Codec address mask. */
+#define CODEC_CAD_MASK                                     0xF0000000
+/** Codec address shift. */
+#define CODEC_CAD_SHIFT                                    28
+#define CODEC_DIRECT_MASK                                  RT_BIT(27)
+/** Node ID mask. */
+#define CODEC_NID_MASK                                     0x07F00000
+/** Node ID shift. */
+#define CODEC_NID_SHIFT                                    20
+#define CODEC_VERBDATA_MASK                                0x000FFFFF
+#define CODEC_VERB_4BIT_CMD                                0x000FFFF0
+#define CODEC_VERB_4BIT_DATA                               0x0000000F
+#define CODEC_VERB_8BIT_CMD                                0x000FFF00
+#define CODEC_VERB_8BIT_DATA                               0x000000FF
+#define CODEC_VERB_16BIT_CMD                               0x000F0000
+#define CODEC_VERB_16BIT_DATA                              0x0000FFFF
+
+#define CODEC_CAD(cmd)                                     (((cmd) & CODEC_CAD_MASK) >> CODEC_CAD_SHIFT)
+#define CODEC_DIRECT(cmd)                                  ((cmd) & CODEC_DIRECT_MASK)
+#define CODEC_NID(cmd)                                     ((((cmd) & CODEC_NID_MASK)) >> CODEC_NID_SHIFT)
+#define CODEC_VERBDATA(cmd)                                ((cmd) & CODEC_VERBDATA_MASK)
+#define CODEC_VERB_CMD(cmd, mask, x)                       (((cmd) & (mask)) >> (x))
+#define CODEC_VERB_CMD4(cmd)                               (CODEC_VERB_CMD((cmd), CODEC_VERB_4BIT_CMD, 4))
+#define CODEC_VERB_CMD8(cmd)                               (CODEC_VERB_CMD((cmd), CODEC_VERB_8BIT_CMD, 8))
+#define CODEC_VERB_CMD16(cmd)                              (CODEC_VERB_CMD((cmd), CODEC_VERB_16BIT_CMD, 16))
+#define CODEC_VERB_PAYLOAD4(cmd)                           ((cmd) & CODEC_VERB_4BIT_DATA)
+#define CODEC_VERB_PAYLOAD8(cmd)                           ((cmd) & CODEC_VERB_8BIT_DATA)
+#define CODEC_VERB_PAYLOAD16(cmd)                          ((cmd) & CODEC_VERB_16BIT_DATA)
+
+#define CODEC_VERB_GET_AMP_DIRECTION                       RT_BIT(15)
+#define CODEC_VERB_GET_AMP_SIDE                            RT_BIT(13)
+#define CODEC_VERB_GET_AMP_INDEX                           0x7
+
+/* HDA spec 7.3.3.7 NoteA */
+#define CODEC_GET_AMP_DIRECTION(cmd)                       (((cmd) & CODEC_VERB_GET_AMP_DIRECTION) >> 15)
+#define CODEC_GET_AMP_SIDE(cmd)                            (((cmd) & CODEC_VERB_GET_AMP_SIDE) >> 13)
+#define CODEC_GET_AMP_INDEX(cmd)                           (CODEC_GET_AMP_DIRECTION(cmd) ? 0 : ((cmd) & CODEC_VERB_GET_AMP_INDEX))
+
+/* HDA spec 7.3.3.7 NoteC */
+#define CODEC_VERB_SET_AMP_OUT_DIRECTION                   RT_BIT(15)
+#define CODEC_VERB_SET_AMP_IN_DIRECTION                    RT_BIT(14)
+#define CODEC_VERB_SET_AMP_LEFT_SIDE                       RT_BIT(13)
+#define CODEC_VERB_SET_AMP_RIGHT_SIDE                      RT_BIT(12)
+#define CODEC_VERB_SET_AMP_INDEX                           (0x7 << 8)
+#define CODEC_VERB_SET_AMP_MUTE                            RT_BIT(7)
+/** Note: 7-bit value [6:0]. */
+#define CODEC_VERB_SET_AMP_GAIN                            0x7F
+
+#define CODEC_SET_AMP_IS_OUT_DIRECTION(cmd)                (((cmd) & CODEC_VERB_SET_AMP_OUT_DIRECTION) != 0)
+#define CODEC_SET_AMP_IS_IN_DIRECTION(cmd)                 (((cmd) & CODEC_VERB_SET_AMP_IN_DIRECTION) != 0)
+#define CODEC_SET_AMP_IS_LEFT_SIDE(cmd)                    (((cmd) & CODEC_VERB_SET_AMP_LEFT_SIDE) != 0)
+#define CODEC_SET_AMP_IS_RIGHT_SIDE(cmd)                   (((cmd) & CODEC_VERB_SET_AMP_RIGHT_SIDE) != 0)
+#define CODEC_SET_AMP_INDEX(cmd)                           (((cmd) & CODEC_VERB_SET_AMP_INDEX) >> 7)
+#define CODEC_SET_AMP_MUTE(cmd)                            ((cmd) & CODEC_VERB_SET_AMP_MUTE)
+#define CODEC_SET_AMP_GAIN(cmd)                            ((cmd) & CODEC_VERB_SET_AMP_GAIN)
+
+/* HDA spec 7.3.3.1 defines layout of configuration registers/verbs (0xF00) */
+/* VendorID (7.3.4.1) */
+#define CODEC_MAKE_F00_00(vendorID, deviceID)              (((vendorID) << 16) | (deviceID))
+#define CODEC_F00_00_VENDORID(f00_00)                      (((f00_00) >> 16) & 0xFFFF)
+#define CODEC_F00_00_DEVICEID(f00_00)                      ((f00_00) & 0xFFFF)
+/* RevisionID (7.3.4.2)*/
+#define CODEC_MAKE_F00_02(MajRev, MinRev, RevisionID, SteppingID) (((MajRev) << 20)|((MinRev) << 16)|((RevisionID) << 8)|(SteppingID))
+/* Subordinate node count (7.3.4.3)*/
+#define CODEC_MAKE_F00_04(startNodeNumber, totalNodeNumber) ((((startNodeNumber) & 0xFF) << 16)|((totalNodeNumber) & 0xFF))
+#define CODEC_F00_04_TO_START_NODE_NUMBER(f00_04)          (((f00_04) >> 16) & 0xFF)
+#define CODEC_F00_04_TO_NODE_COUNT(f00_04)                 ((f00_04) & 0xFF)
+/*
+ * Function Group Type  (7.3.4.4)
+ * 0 & [0x3-0x7f] are reserved types
+ * [0x80 - 0xff] are vendor defined function groups
+ */
+#define CODEC_MAKE_F00_05(UnSol, NodeType)                 (((UnSol) << 8)|(NodeType))
+#define CODEC_F00_05_UNSOL                                 RT_BIT(8)
+#define CODEC_F00_05_AFG                                   (0x1)
+#define CODEC_F00_05_MFG                                   (0x2)
+#define CODEC_F00_05_IS_UNSOL(f00_05)                      RT_BOOL((f00_05) & RT_BIT(8))
+#define CODEC_F00_05_GROUP(f00_05)                         ((f00_05) & 0xff)
+/*  Audio Function Group capabilities (7.3.4.5) */
+#define CODEC_MAKE_F00_08(BeepGen, InputDelay, OutputDelay) ((((BeepGen) & 0x1) << 16)| (((InputDelay) & 0xF) << 8) | ((OutputDelay) & 0xF))
+#define CODEC_F00_08_BEEP_GEN(f00_08)                      ((f00_08) & RT_BIT(16)
+
+/* Widget Capabilities (7.3.4.6) */
+#define CODEC_MAKE_F00_09(type, delay, chanel_count) \
+    ( (((type) & 0xF) << 20)            \
+    | (((delay) & 0xF) << 16)           \
+    | (((chanel_count) & 0xF) << 13))
+/* note: types 0x8-0xe are reserved */
+#define CODEC_F00_09_TYPE_AUDIO_OUTPUT                     (0x0)
+#define CODEC_F00_09_TYPE_AUDIO_INPUT                      (0x1)
+#define CODEC_F00_09_TYPE_AUDIO_MIXER                      (0x2)
+#define CODEC_F00_09_TYPE_AUDIO_SELECTOR                   (0x3)
+#define CODEC_F00_09_TYPE_PIN_COMPLEX                      (0x4)
+#define CODEC_F00_09_TYPE_POWER_WIDGET                     (0x5)
+#define CODEC_F00_09_TYPE_VOLUME_KNOB                      (0x6)
+#define CODEC_F00_09_TYPE_BEEP_GEN                         (0x7)
+#define CODEC_F00_09_TYPE_VENDOR_DEFINED                   (0xF)
+
+#define CODEC_F00_09_CAP_CP                                RT_BIT(12)
+#define CODEC_F00_09_CAP_L_R_SWAP                          RT_BIT(11)
+#define CODEC_F00_09_CAP_POWER_CTRL                        RT_BIT(10)
+#define CODEC_F00_09_CAP_DIGITAL                           RT_BIT(9)
+#define CODEC_F00_09_CAP_CONNECTION_LIST                   RT_BIT(8)
+#define CODEC_F00_09_CAP_UNSOL                             RT_BIT(7)
+#define CODEC_F00_09_CAP_PROC_WIDGET                       RT_BIT(6)
+#define CODEC_F00_09_CAP_STRIPE                            RT_BIT(5)
+#define CODEC_F00_09_CAP_FMT_OVERRIDE                      RT_BIT(4)
+#define CODEC_F00_09_CAP_AMP_FMT_OVERRIDE                  RT_BIT(3)
+#define CODEC_F00_09_CAP_OUT_AMP_PRESENT                   RT_BIT(2)
+#define CODEC_F00_09_CAP_IN_AMP_PRESENT                    RT_BIT(1)
+#define CODEC_F00_09_CAP_LSB                               RT_BIT(0)
+
+#define CODEC_F00_09_TYPE(f00_09)                          (((f00_09) >> 20) & 0xF)
+
+#define CODEC_F00_09_IS_CAP_CP(f00_09)                     RT_BOOL((f00_09) & RT_BIT(12))
+#define CODEC_F00_09_IS_CAP_L_R_SWAP(f00_09)               RT_BOOL((f00_09) & RT_BIT(11))
+#define CODEC_F00_09_IS_CAP_POWER_CTRL(f00_09)             RT_BOOL((f00_09) & RT_BIT(10))
+#define CODEC_F00_09_IS_CAP_DIGITAL(f00_09)                RT_BOOL((f00_09) & RT_BIT(9))
+#define CODEC_F00_09_IS_CAP_CONNECTION_LIST(f00_09)        RT_BOOL((f00_09) & RT_BIT(8))
+#define CODEC_F00_09_IS_CAP_UNSOL(f00_09)                  RT_BOOL((f00_09) & RT_BIT(7))
+#define CODEC_F00_09_IS_CAP_PROC_WIDGET(f00_09)            RT_BOOL((f00_09) & RT_BIT(6))
+#define CODEC_F00_09_IS_CAP_STRIPE(f00_09)                 RT_BOOL((f00_09) & RT_BIT(5))
+#define CODEC_F00_09_IS_CAP_FMT_OVERRIDE(f00_09)           RT_BOOL((f00_09) & RT_BIT(4))
+#define CODEC_F00_09_IS_CAP_AMP_OVERRIDE(f00_09)           RT_BOOL((f00_09) & RT_BIT(3))
+#define CODEC_F00_09_IS_CAP_OUT_AMP_PRESENT(f00_09)        RT_BOOL((f00_09) & RT_BIT(2))
+#define CODEC_F00_09_IS_CAP_IN_AMP_PRESENT(f00_09)         RT_BOOL((f00_09) & RT_BIT(1))
+#define CODEC_F00_09_IS_CAP_LSB(f00_09)                    RT_BOOL((f00_09) & RT_BIT(0))
+
+/* Supported PCM size, rates (7.3.4.7) */
+#define CODEC_F00_0A_32_BIT                                RT_BIT(19)
+#define CODEC_F00_0A_24_BIT                                RT_BIT(18)
+#define CODEC_F00_0A_16_BIT                                RT_BIT(17)
+#define CODEC_F00_0A_8_BIT                                 RT_BIT(16)
+
+#define CODEC_F00_0A_48KHZ_MULT_8X                         RT_BIT(11)
+#define CODEC_F00_0A_48KHZ_MULT_4X                         RT_BIT(10)
+#define CODEC_F00_0A_44_1KHZ_MULT_4X                       RT_BIT(9)
+#define CODEC_F00_0A_48KHZ_MULT_2X                         RT_BIT(8)
+#define CODEC_F00_0A_44_1KHZ_MULT_2X                       RT_BIT(7)
+#define CODEC_F00_0A_48KHZ                                 RT_BIT(6)
+#define CODEC_F00_0A_44_1KHZ                               RT_BIT(5)
+/* 2/3 * 48kHz */
+#define CODEC_F00_0A_48KHZ_2_3X                            RT_BIT(4)
+/* 1/2 * 44.1kHz */
+#define CODEC_F00_0A_44_1KHZ_1_2X                          RT_BIT(3)
+/* 1/3 * 48kHz */
+#define CODEC_F00_0A_48KHZ_1_3X                            RT_BIT(2)
+/* 1/4 * 44.1kHz */
+#define CODEC_F00_0A_44_1KHZ_1_4X                          RT_BIT(1)
+/* 1/6 * 48kHz */
+#define CODEC_F00_0A_48KHZ_1_6X                            RT_BIT(0)
+
+/* Supported streams formats (7.3.4.8) */
+#define CODEC_F00_0B_AC3                                   RT_BIT(2)
+#define CODEC_F00_0B_FLOAT32                               RT_BIT(1)
+#define CODEC_F00_0B_PCM                                   RT_BIT(0)
+
+/* Pin Capabilities (7.3.4.9)*/
+#define CODEC_MAKE_F00_0C(vref_ctrl) (((vref_ctrl) & 0xFF) << 8)
+#define CODEC_F00_0C_CAP_HBR                               RT_BIT(27)
+#define CODEC_F00_0C_CAP_DP                                RT_BIT(24)
+#define CODEC_F00_0C_CAP_EAPD                              RT_BIT(16)
+#define CODEC_F00_0C_CAP_HDMI                              RT_BIT(7)
+#define CODEC_F00_0C_CAP_BALANCED_IO                       RT_BIT(6)
+#define CODEC_F00_0C_CAP_INPUT                             RT_BIT(5)
+#define CODEC_F00_0C_CAP_OUTPUT                            RT_BIT(4)
+#define CODEC_F00_0C_CAP_HP                                RT_BIT(3)
+#define CODEC_F00_0C_CAP_PRESENSE_DETECT                   RT_BIT(2)
+#define CODEC_F00_0C_CAP_TRIGGER_REQUIRED                  RT_BIT(1)
+#define CODEC_F00_0C_CAP_IMPENDANCE_SENSE                  RT_BIT(0)
+
+#define CODEC_F00_0C_IS_CAP_HBR(f00_0c)                    ((f00_0c) & RT_BIT(27))
+#define CODEC_F00_0C_IS_CAP_DP(f00_0c)                     ((f00_0c) & RT_BIT(24))
+#define CODEC_F00_0C_IS_CAP_EAPD(f00_0c)                   ((f00_0c) & RT_BIT(16))
+#define CODEC_F00_0C_IS_CAP_HDMI(f00_0c)                   ((f00_0c) & RT_BIT(7))
+#define CODEC_F00_0C_IS_CAP_BALANCED_IO(f00_0c)            ((f00_0c) & RT_BIT(6))
+#define CODEC_F00_0C_IS_CAP_INPUT(f00_0c)                  ((f00_0c) & RT_BIT(5))
+#define CODEC_F00_0C_IS_CAP_OUTPUT(f00_0c)                 ((f00_0c) & RT_BIT(4))
+#define CODEC_F00_0C_IS_CAP_HP(f00_0c)                     ((f00_0c) & RT_BIT(3))
+#define CODEC_F00_0C_IS_CAP_PRESENSE_DETECT(f00_0c)        ((f00_0c) & RT_BIT(2))
+#define CODEC_F00_0C_IS_CAP_TRIGGER_REQUIRED(f00_0c)       ((f00_0c) & RT_BIT(1))
+#define CODEC_F00_0C_IS_CAP_IMPENDANCE_SENSE(f00_0c)       ((f00_0c) & RT_BIT(0))
+
+/* Input Amplifier capabilities (7.3.4.10) */
+#define CODEC_MAKE_F00_0D(mute_cap, step_size, num_steps, offset) \
+        (  (((mute_cap) & 0x1) << 31)                             \
+         | (((step_size) & 0xFF) << 16)                           \
+         | (((num_steps) & 0xFF) << 8)                            \
+         | ((offset) & 0xFF))
+
+#define CODEC_F00_0D_CAP_MUTE                              RT_BIT(7)
+
+#define CODEC_F00_0D_IS_CAP_MUTE(f00_0d)                   ( ( f00_0d) & RT_BIT(31))
+#define CODEC_F00_0D_STEP_SIZE(f00_0d)                     ((( f00_0d) & (0x7F << 16)) >> 16)
+#define CODEC_F00_0D_NUM_STEPS(f00_0d)                     ((((f00_0d) & (0x7F << 8)) >> 8) + 1)
+#define CODEC_F00_0D_OFFSET(f00_0d)                        (  (f00_0d) & 0x7F)
+
+/* Output Amplifier capabilities (7.3.4.10) */
+#define CODEC_MAKE_F00_12                                  CODEC_MAKE_F00_0D
+
+#define CODEC_F00_12_IS_CAP_MUTE(f00_12)                   CODEC_F00_0D_IS_CAP_MUTE(f00_12)
+#define CODEC_F00_12_STEP_SIZE(f00_12)                     CODEC_F00_0D_STEP_SIZE(f00_12)
+#define CODEC_F00_12_NUM_STEPS(f00_12)                     CODEC_F00_0D_NUM_STEPS(f00_12)
+#define CODEC_F00_12_OFFSET(f00_12)                        CODEC_F00_0D_OFFSET(f00_12)
+
+/* Connection list lenght (7.3.4.11) */
+#define CODEC_MAKE_F00_0E(long_form, length)    \
+    (  (((long_form) & 0x1) << 7)               \
+     | ((length) & 0x7F))
+/* Indicates short-form NIDs. */
+#define CODEC_F00_0E_LIST_NID_SHORT                        0
+/* Indicates long-form NIDs. */
+#define CODEC_F00_0E_LIST_NID_LONG                         1
+#define CODEC_F00_0E_IS_LONG(f00_0e)                       RT_BOOL((f00_0e) & RT_BIT(7))
+#define CODEC_F00_0E_COUNT(f00_0e)                         ((f00_0e) & 0x7F)
+/* Supported Power States (7.3.4.12) */
+#define CODEC_F00_0F_EPSS                                  RT_BIT(31)
+#define CODEC_F00_0F_CLKSTOP                               RT_BIT(30)
+#define CODEC_F00_0F_S3D3                                  RT_BIT(29)
+#define CODEC_F00_0F_D3COLD                                RT_BIT(4)
+#define CODEC_F00_0F_D3                                    RT_BIT(3)
+#define CODEC_F00_0F_D2                                    RT_BIT(2)
+#define CODEC_F00_0F_D1                                    RT_BIT(1)
+#define CODEC_F00_0F_D0                                    RT_BIT(0)
+
+/* Processing capabilities 7.3.4.13 */
+#define CODEC_MAKE_F00_10(num, benign)                     ((((num) & 0xFF) << 8) | ((benign) & 0x1))
+#define CODEC_F00_10_NUM(f00_10)                           (((f00_10) & (0xFF << 8)) >> 8)
+#define CODEC_F00_10_BENING(f00_10)                        ((f00_10) & 0x1)
+
+/* CP/IO Count (7.3.4.14) */
+#define CODEC_MAKE_F00_11(wake, unsol, numgpi, numgpo, numgpio) \
+    (  (((wake) & 0x1) << 31)                                   \
+     | (((unsol) & 0x1) << 30)                                  \
+     | (((numgpi) & 0xFF) << 16)                                \
+     | (((numgpo) & 0xFF) << 8)                                 \
+     | ((numgpio) & 0xFF))
+
+/* Processing States (7.3.3.4) */
+#define CODEC_F03_OFF                                      (0)
+#define CODEC_F03_ON                                       RT_BIT(0)
+#define CODEC_F03_BENING                                   RT_BIT(1)
+/* Power States (7.3.3.10) */
+#define CODEC_MAKE_F05(reset, stopok, error, act, set)          \
+    (   (((reset) & 0x1) << 10)                                 \
+      | (((stopok) & 0x1) << 9)                                 \
+      | (((error) & 0x1) << 8)                                  \
+      | (((act) & 0x7) << 4)                                    \
+      | ((set) & 0x7))
+#define CODEC_F05_D3COLD                                   (4)
+#define CODEC_F05_D3                                       (3)
+#define CODEC_F05_D2                                       (2)
+#define CODEC_F05_D1                                       (1)
+#define CODEC_F05_D0                                       (0)
+
+#define CODEC_F05_IS_RESET(value)                          (((value) & RT_BIT(10)) != 0)
+#define CODEC_F05_IS_STOPOK(value)                         (((value) & RT_BIT(9)) != 0)
+#define CODEC_F05_IS_ERROR(value)                          (((value) & RT_BIT(8)) != 0)
+#define CODEC_F05_ACT(value)                               (((value) & 0x7) >> 4)
+#define CODEC_F05_SET(value)                               (((value) & 0x7))
+
+#define CODEC_F05_GE(p0, p1)                               ((p0) <= (p1))
+#define CODEC_F05_LE(p0, p1)                               ((p0) >= (p1))
+
+/* Pin Widged Control (7.3.3.13) */
+#define CODEC_F07_VREF_HIZ                                 (0)
+#define CODEC_F07_VREF_50                                  (0x1)
+#define CODEC_F07_VREF_GROUND                              (0x2)
+#define CODEC_F07_VREF_80                                  (0x4)
+#define CODEC_F07_VREF_100                                 (0x5)
+#define CODEC_F07_IN_ENABLE                                RT_BIT(5)
+#define CODEC_F07_OUT_ENABLE                               RT_BIT(6)
+#define CODEC_F07_OUT_H_ENABLE                             RT_BIT(7)
+
+/* Unsolicited enabled (7.3.3.14) */
+#define CODEC_MAKE_F08(enable, tag) ((((enable) & 1) << 7) | ((tag) & 0x3F))
+
+/* Converter formats (7.3.3.8) and (3.7.1) */
+#define CODEC_MAKE_A(fNonPCM, f44_1BaseRate, mult, div, bits, chan) \
+    (  (((fNonPCM) & 0x1) << 15)                                    \
+     | (((f44_1BaseRate) & 0x1) << 14)                              \
+     | (((mult) & 0x7) << 11)                                       \
+     | (((div) & 0x7) << 8)                                         \
+     | (((bits) & 0x7) << 4)                                        \
+     | ((chan) & 0xF))
+
+#define CODEC_A_TYPE                                       RT_BIT(15)
+#define CODEC_A_TYPE_PCM                                   (0)
+#define CODEC_A_TYPE_NON_PCM                               (1)
+
+#define CODEC_A_BASE                                       RT_BIT(14)
+#define CODEC_A_BASE_48KHZ                                 (0)
+#define CODEC_A_BASE_44KHZ                                 (1)
+
+#define CODEC_A_MULT_1X                                    (0)
+#define CODEC_A_MULT_2X                                    (1)
+#define CODEC_A_MULT_3X                                    (2)
+#define CODEC_A_MULT_4X                                    (3)
+
+#define CODEC_A_DIV_1X                                     (0)
+#define CODEC_A_DIV_2X                                     (1)
+#define CODEC_A_DIV_3X                                     (2)
+#define CODEC_A_DIV_4X                                     (3)
+#define CODEC_A_DIV_5X                                     (4)
+#define CODEC_A_DIV_6X                                     (5)
+#define CODEC_A_DIV_7X                                     (6)
+#define CODEC_A_DIV_8X                                     (7)
+
+#define CODEC_A_8_BIT                                      (0)
+#define CODEC_A_16_BIT                                     (1)
+#define CODEC_A_20_BIT                                     (2)
+#define CODEC_A_24_BIT                                     (3)
+#define CODEC_A_32_BIT                                     (4)
+
+#define CODEC_A_CHAN_MONO                                  (0)
+#define CODEC_A_CHAN_STEREO                                (1)
+
+/* Pin Sense (7.3.3.15) */
+#define CODEC_MAKE_F09_ANALOG(fPresent, impedance)  \
+(  (((fPresent) & 0x1) << 31)                       \
+ | (((impedance) & 0x7FFFFFFF)))
+#define CODEC_F09_ANALOG_NA    0x7FFFFFFF
+#define CODEC_MAKE_F09_DIGITAL(fPresent, fELDValid) \
+(   (((fPresent) & 0x1) << 31)                      \
+  | (((fELDValid) & 0x1) << 30))
+
+#define CODEC_MAKE_F0C(lrswap, eapd, btl) ((((lrswap) & 1) << 2) | (((eapd) & 1) << 1) | ((btl) & 1))
+#define CODEC_FOC_IS_LRSWAP(f0c)                           RT_BOOL((f0c) & RT_BIT(2))
+#define CODEC_FOC_IS_EAPD(f0c)                             RT_BOOL((f0c) & RT_BIT(1))
+#define CODEC_FOC_IS_BTL(f0c)                              RT_BOOL((f0c) & RT_BIT(0))
+/* HDA spec 7.3.3.31 defines layout of configuration registers/verbs (0xF1C) */
+/* Configuration's port connection */
+#define CODEC_F1C_PORT_MASK                                (0x3)
+#define CODEC_F1C_PORT_SHIFT                               (30)
+
+#define CODEC_F1C_PORT_COMPLEX                             (0x0)
+#define CODEC_F1C_PORT_NO_PHYS                             (0x1)
+#define CODEC_F1C_PORT_FIXED                               (0x2)
+#define CODEC_F1C_BOTH                                     (0x3)
+
+/* Configuration default: connection */
+#define CODEC_F1C_PORT_MASK                                (0x3)
+#define CODEC_F1C_PORT_SHIFT                               (30)
+
+/* Connected to a jack (1/8", ATAPI, ...). */
+#define CODEC_F1C_PORT_COMPLEX                             (0x0)
+/* No physical connection. */
+#define CODEC_F1C_PORT_NO_PHYS                             (0x1)
+/* Fixed function device (integrated speaker, integrated mic, ...). */
+#define CODEC_F1C_PORT_FIXED                               (0x2)
+/* Both, a jack and an internal device are attached. */
+#define CODEC_F1C_BOTH                                     (0x3)
+
+/* Configuration default: Location */
+#define CODEC_F1C_LOCATION_MASK                            (0x3F)
+#define CODEC_F1C_LOCATION_SHIFT                           (24)
+
+/* [4:5] bits of location region means chassis attachment */
+#define CODEC_F1C_LOCATION_PRIMARY_CHASSIS                 (0)
+#define CODEC_F1C_LOCATION_INTERNAL                        RT_BIT(4)
+#define CODEC_F1C_LOCATION_SECONDRARY_CHASSIS              RT_BIT(5)
+#define CODEC_F1C_LOCATION_OTHER                           RT_BIT(5)
+
+/* [0:3] bits of location region means geometry location attachment */
+#define CODEC_F1C_LOCATION_NA                              (0)
+#define CODEC_F1C_LOCATION_REAR                            (0x1)
+#define CODEC_F1C_LOCATION_FRONT                           (0x2)
+#define CODEC_F1C_LOCATION_LEFT                            (0x3)
+#define CODEC_F1C_LOCATION_RIGTH                           (0x4)
+#define CODEC_F1C_LOCATION_TOP                             (0x5)
+#define CODEC_F1C_LOCATION_BOTTOM                          (0x6)
+#define CODEC_F1C_LOCATION_SPECIAL_0                       (0x7)
+#define CODEC_F1C_LOCATION_SPECIAL_1                       (0x8)
+#define CODEC_F1C_LOCATION_SPECIAL_2                       (0x9)
+
+/* Configuration default: Device type */
+#define CODEC_F1C_DEVICE_MASK                              (0xF)
+#define CODEC_F1C_DEVICE_SHIFT                             (20)
+#define CODEC_F1C_DEVICE_LINE_OUT                          (0)
+#define CODEC_F1C_DEVICE_SPEAKER                           (0x1)
+#define CODEC_F1C_DEVICE_HP                                (0x2)
+#define CODEC_F1C_DEVICE_CD                                (0x3)
+#define CODEC_F1C_DEVICE_SPDIF_OUT                         (0x4)
+#define CODEC_F1C_DEVICE_DIGITAL_OTHER_OUT                 (0x5)
+#define CODEC_F1C_DEVICE_MODEM_LINE_SIDE                   (0x6)
+#define CODEC_F1C_DEVICE_MODEM_HANDSET_SIDE                (0x7)
+#define CODEC_F1C_DEVICE_LINE_IN                           (0x8)
+#define CODEC_F1C_DEVICE_AUX                               (0x9)
+#define CODEC_F1C_DEVICE_MIC                               (0xA)
+#define CODEC_F1C_DEVICE_PHONE                             (0xB)
+#define CODEC_F1C_DEVICE_SPDIF_IN                          (0xC)
+#define CODEC_F1C_DEVICE_RESERVED                          (0xE)
+#define CODEC_F1C_DEVICE_OTHER                             (0xF)
+
+/* Configuration default: Connection type */
+#define CODEC_F1C_CONNECTION_TYPE_MASK                     (0xF)
+#define CODEC_F1C_CONNECTION_TYPE_SHIFT                    (16)
+
+#define CODEC_F1C_CONNECTION_TYPE_UNKNOWN                  (0)
+#define CODEC_F1C_CONNECTION_TYPE_1_8INCHES                (0x1)
+#define CODEC_F1C_CONNECTION_TYPE_1_4INCHES                (0x2)
+#define CODEC_F1C_CONNECTION_TYPE_ATAPI                    (0x3)
+#define CODEC_F1C_CONNECTION_TYPE_RCA                      (0x4)
+#define CODEC_F1C_CONNECTION_TYPE_OPTICAL                  (0x5)
+#define CODEC_F1C_CONNECTION_TYPE_OTHER_DIGITAL            (0x6)
+#define CODEC_F1C_CONNECTION_TYPE_ANALOG                   (0x7)
+#define CODEC_F1C_CONNECTION_TYPE_DIN                      (0x8)
+#define CODEC_F1C_CONNECTION_TYPE_XLR                      (0x9)
+#define CODEC_F1C_CONNECTION_TYPE_RJ_11                    (0xA)
+#define CODEC_F1C_CONNECTION_TYPE_COMBO                    (0xB)
+#define CODEC_F1C_CONNECTION_TYPE_OTHER                    (0xF)
+
+/* Configuration's color */
+#define CODEC_F1C_COLOR_MASK                               (0xF)
+#define CODEC_F1C_COLOR_SHIFT                              (12)
+#define CODEC_F1C_COLOR_UNKNOWN                            (0)
+#define CODEC_F1C_COLOR_BLACK                              (0x1)
+#define CODEC_F1C_COLOR_GREY                               (0x2)
+#define CODEC_F1C_COLOR_BLUE                               (0x3)
+#define CODEC_F1C_COLOR_GREEN                              (0x4)
+#define CODEC_F1C_COLOR_RED                                (0x5)
+#define CODEC_F1C_COLOR_ORANGE                             (0x6)
+#define CODEC_F1C_COLOR_YELLOW                             (0x7)
+#define CODEC_F1C_COLOR_PURPLE                             (0x8)
+#define CODEC_F1C_COLOR_PINK                               (0x9)
+#define CODEC_F1C_COLOR_RESERVED_0                         (0xA)
+#define CODEC_F1C_COLOR_RESERVED_1                         (0xB)
+#define CODEC_F1C_COLOR_RESERVED_2                         (0xC)
+#define CODEC_F1C_COLOR_RESERVED_3                         (0xD)
+#define CODEC_F1C_COLOR_WHITE                              (0xE)
+#define CODEC_F1C_COLOR_OTHER                              (0xF)
+
+/* Configuration's misc */
+#define CODEC_F1C_MISC_MASK                                (0xF)
+#define CODEC_F1C_MISC_SHIFT                               (8)
+#define CODEC_F1C_MISC_JACK_DETECT                         (0)
+#define CODEC_F1C_MISC_RESERVED_0                          (1)
+#define CODEC_F1C_MISC_RESERVED_1                          (2)
+#define CODEC_F1C_MISC_RESERVED_2                          (3)
+
+/* Configuration default: Association */
+#define CODEC_F1C_ASSOCIATION_MASK                         (0xF)
+#define CODEC_F1C_ASSOCIATION_SHIFT                        (4)
+
+/* Reserved; don't use. */
+#define CODEC_F1C_ASSOCIATION_INVALID                      0x0
+#define CODEC_F1C_ASSOCIATION_GROUP_0                      0x1
+#define CODEC_F1C_ASSOCIATION_GROUP_1                      0x2
+#define CODEC_F1C_ASSOCIATION_GROUP_2                      0x3
+#define CODEC_F1C_ASSOCIATION_GROUP_3                      0x4
+#define CODEC_F1C_ASSOCIATION_GROUP_4                      0x5
+#define CODEC_F1C_ASSOCIATION_GROUP_5                      0x6
+#define CODEC_F1C_ASSOCIATION_GROUP_6                      0x7
+#define CODEC_F1C_ASSOCIATION_GROUP_7                      0x8
+#define CODEC_F1C_ASSOCIATION_GROUP_15                     0xF
+
+/* Configuration default: Association Sequence */
+#define CODEC_F1C_SEQ_MASK                                 (0xF)
+#define CODEC_F1C_SEQ_SHIFT                                (0)
+
+/* Implementation identification (7.3.3.30) */
+#define CODEC_MAKE_F20(bmid, bsku, aid)     \
+    (  (((bmid) & 0xFFFF) << 16)            \
+     | (((bsku) & 0xFF) << 8)               \
+     | (((aid) & 0xFF))                     \
+    )
+
+/* macro definition helping in filling the configuration registers. */
+#define CODEC_MAKE_F1C(port_connectivity, location, device, connection_type, color, misc, association, sequence)    \
+    (  ((port_connectivity) << CODEC_F1C_PORT_SHIFT)          \
+     | ((location) << CODEC_F1C_LOCATION_SHIFT)               \
+     | ((device) << CODEC_F1C_DEVICE_SHIFT)                   \
+     | ((connection_type) << CODEC_F1C_CONNECTION_TYPE_SHIFT) \
+     | ((color) << CODEC_F1C_COLOR_SHIFT)                     \
+     | ((misc) << CODEC_F1C_MISC_SHIFT)                       \
+     | ((association) << CODEC_F1C_ASSOCIATION_SHIFT)         \
+     | ((sequence)))
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+/** The F00 parameter length (in dwords). */
+#define CODECNODE_F00_PARAM_LENGTH  20
+/** The F02 parameter length (in dwords). */
+#define CODECNODE_F02_PARAM_LENGTH  16
+
+/**
+ * Common (or core) codec node structure.
+ */
+typedef struct CODECCOMMONNODE
+{
+    /** Node id - 7 bit format */
+    uint8_t         id;
+    /** The node name. */
+    char const     *pszName;
+    /* PRM 5.3.6 */
+    uint32_t au32F00_param[CODECNODE_F00_PARAM_LENGTH];
+    uint32_t au32F02_param[CODECNODE_F02_PARAM_LENGTH];
+} CODECCOMMONNODE;
+typedef CODECCOMMONNODE *PCODECCOMMONNODE;
+AssertCompile(CODECNODE_F00_PARAM_LENGTH == 20);  /* saved state */
+AssertCompile(CODECNODE_F02_PARAM_LENGTH == 16); /* saved state */
+
+/**
+ * Compile time assertion on the expected node size.
+ */
+#define AssertNodeSize(a_Node, a_cParams) \
+    AssertCompile((a_cParams) <= (60 + 6)); /* the max size - saved state */ \
+    AssertCompile(   sizeof(a_Node) - sizeof(CODECCOMMONNODE)  \
+                  == (((a_cParams) * sizeof(uint32_t) + sizeof(void *) - 1) & ~(sizeof(void *) - 1)) )
+
+typedef struct ROOTCODECNODE
+{
+    CODECCOMMONNODE node;
+} ROOTCODECNODE, *PROOTCODECNODE;
+AssertNodeSize(ROOTCODECNODE, 0);
+
+#define AMPLIFIER_SIZE 60
+typedef uint32_t AMPLIFIER[AMPLIFIER_SIZE];
+#define AMPLIFIER_IN    0
+#define AMPLIFIER_OUT   1
+#define AMPLIFIER_LEFT  1
+#define AMPLIFIER_RIGHT 0
+#define AMPLIFIER_REGISTER(amp, inout, side, index) ((amp)[30*(inout) + 15*(side) + (index)])
+typedef struct DACNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t    u32F0d_param;
+    uint32_t    u32F04_param;
+    uint32_t    u32F05_param;
+    uint32_t    u32F06_param;
+    uint32_t    u32F0c_param;
+
+    uint32_t    u32A_param;
+    AMPLIFIER B_params;
+
+} DACNODE, *PDACNODE;
+AssertNodeSize(DACNODE, 6 + 60);
+
+typedef struct ADCNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t    u32F03_param;
+    uint32_t    u32F05_param;
+    uint32_t    u32F06_param;
+    uint32_t    u32F09_param;
+
+    uint32_t    u32A_param;
+    uint32_t    u32F01_param;
+    AMPLIFIER   B_params;
+} ADCNODE, *PADCNODE;
+AssertNodeSize(DACNODE, 6 + 60);
+
+typedef struct SPDIFOUTNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t    u32F05_param;
+    uint32_t    u32F06_param;
+    uint32_t    u32F09_param;
+    uint32_t    u32F0d_param;
+
+    uint32_t    u32A_param;
+    AMPLIFIER   B_params;
+} SPDIFOUTNODE, *PSPDIFOUTNODE;
+AssertNodeSize(SPDIFOUTNODE, 5 + 60);
+
+typedef struct SPDIFINNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t    u32F05_param;
+    uint32_t    u32F06_param;
+    uint32_t    u32F09_param;
+    uint32_t    u32F0d_param;
+
+    uint32_t    u32A_param;
+    AMPLIFIER   B_params;
+} SPDIFINNODE, *PSPDIFINNODE;
+AssertNodeSize(SPDIFINNODE, 5 + 60);
+
+typedef struct AFGCODECNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t  u32F05_param;
+    uint32_t  u32F08_param;
+    uint32_t  u32F20_param;
+    uint32_t  u32F17_param;
+} AFGCODECNODE, *PAFGCODECNODE;
+AssertNodeSize(AFGCODECNODE, 4);
+
+typedef struct PORTNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t u32F07_param;
+    uint32_t u32F08_param;
+    uint32_t u32F09_param;
+    uint32_t u32F01_param;
+    uint32_t u32F1c_param;
+    AMPLIFIER   B_params;
+} PORTNODE, *PPORTNODE;
+AssertNodeSize(PORTNODE, 5 + 60);
+
+typedef struct DIGOUTNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t u32F01_param;
+    uint32_t u32F08_param;
+    uint32_t u32F07_param;
+    uint32_t u32F09_param;
+    uint32_t u32F1c_param;
+} DIGOUTNODE, *PDIGOUTNODE;
+AssertNodeSize(DIGOUTNODE, 5);
+
+typedef struct DIGINNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t u32F05_param;
+    uint32_t u32F07_param;
+    uint32_t u32F08_param;
+    uint32_t u32F09_param;
+    uint32_t u32F0c_param;
+    uint32_t u32F1c_param;
+    uint32_t u32F1e_param;
+} DIGINNODE, *PDIGINNODE;
+AssertNodeSize(DIGINNODE, 7);
+
+typedef struct ADCMUXNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t    u32F01_param;
+
+    uint32_t    u32A_param;
+    AMPLIFIER   B_params;
+} ADCMUXNODE, *PADCMUXNODE;
+AssertNodeSize(ADCMUXNODE, 2 + 60);
+
+typedef struct PCBEEPNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t    u32F07_param;
+    uint32_t    u32F0a_param;
+
+    uint32_t    u32A_param;
+    AMPLIFIER   B_params;
+    uint32_t    u32F1c_param;
+} PCBEEPNODE, *PPCBEEPNODE;
+AssertNodeSize(PCBEEPNODE, 3 + 60 + 1);
+
+typedef struct CDNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t u32F07_param;
+    uint32_t u32F1c_param;
+} CDNODE, *PCDNODE;
+AssertNodeSize(CDNODE, 2);
+
+typedef struct VOLUMEKNOBNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t    u32F08_param;
+    uint32_t    u32F0f_param;
+} VOLUMEKNOBNODE, *PVOLUMEKNOBNODE;
+AssertNodeSize(VOLUMEKNOBNODE, 2);
+
+typedef struct ADCVOLNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t    u32F0c_param;
+    uint32_t    u32F01_param;
+    uint32_t    u32A_params;
+    AMPLIFIER   B_params;
+} ADCVOLNODE, *PADCVOLNODE;
+AssertNodeSize(ADCVOLNODE, 3 + 60);
+
+typedef struct RESNODE
+{
+    CODECCOMMONNODE node;
+    uint32_t    u32F05_param;
+    uint32_t    u32F06_param;
+    uint32_t    u32F07_param;
+    uint32_t    u32F1c_param;
+} RESNODE, *PRESNODE;
+AssertNodeSize(RESNODE, 4);
+
+/**
+ * Used for the saved state.
+ */
+typedef struct CODECSAVEDSTATENODE
+{
+    CODECCOMMONNODE Core;
+    uint32_t        au32Params[60 + 6];
+} CODECSAVEDSTATENODE;
+AssertNodeSize(CODECSAVEDSTATENODE, 60 + 6);
+
+typedef union CODECNODE
+{
+    CODECCOMMONNODE node;
+    ROOTCODECNODE   root;
+    AFGCODECNODE    afg;
+    DACNODE         dac;
+    ADCNODE         adc;
+    SPDIFOUTNODE    spdifout;
+    SPDIFINNODE     spdifin;
+    PORTNODE        port;
+    DIGOUTNODE      digout;
+    DIGINNODE       digin;
+    ADCMUXNODE      adcmux;
+    PCBEEPNODE      pcbeep;
+    CDNODE          cdnode;
+    VOLUMEKNOBNODE  volumeKnob;
+    ADCVOLNODE      adcvol;
+    RESNODE         reserved;
+    CODECSAVEDSTATENODE SavedState;
+} CODECNODE, *PCODECNODE;
+AssertNodeSize(CODECNODE, 60 + 6);
+
+
+/*********************************************************************************************************************************
+*   Global Variables                                                                                                             *
+*********************************************************************************************************************************/
+/* STAC9220 - Nodes IDs / names. */
+#define STAC9220_NID_ROOT                                  0x0  /* Root node */
+#define STAC9220_NID_AFG                                   0x1  /* Audio Configuration Group */
+#define STAC9220_NID_DAC0                                  0x2  /* Out */
+#define STAC9220_NID_DAC1                                  0x3  /* Out */
+#define STAC9220_NID_DAC2                                  0x4  /* Out */
+#define STAC9220_NID_DAC3                                  0x5  /* Out */
+#define STAC9220_NID_ADC0                                  0x6  /* In */
+#define STAC9220_NID_ADC1                                  0x7  /* In */
+#define STAC9220_NID_SPDIF_OUT                             0x8  /* Out */
+#define STAC9220_NID_SPDIF_IN                              0x9  /* In */
+#define STAC9220_NID_PIN_HEADPHONE0                        0xA  /* In, Out */
+#define STAC9220_NID_PIN_B                                 0xB  /* In, Out */
+#define STAC9220_NID_PIN_C                                 0xC  /* In, Out */
+#define STAC9220_NID_PIN_HEADPHONE1                        0xD  /* In, Out */
+#define STAC9220_NID_PIN_E                                 0xE  /* In */
+#define STAC9220_NID_PIN_F                                 0xF  /* In, Out */
+#define STAC9220_NID_PIN_SPDIF_OUT                         0x10 /* Out */
+#define STAC9220_NID_PIN_SPDIF_IN                          0x11 /* In */
+#define STAC9220_NID_ADC0_MUX                              0x12 /* In */
+#define STAC9220_NID_ADC1_MUX                              0x13 /* In */
+#define STAC9220_NID_PCBEEP                                0x14 /* Out */
+#define STAC9220_NID_PIN_CD                                0x15 /* In */
+#define STAC9220_NID_VOL_KNOB                              0x16
+#define STAC9220_NID_AMP_ADC0                              0x17 /* In */
+#define STAC9220_NID_AMP_ADC1                              0x18 /* In */
+/* STAC9221. */
+#define STAC9221_NID_ADAT_OUT                              0x19 /* Out */
+#define STAC9221_NID_I2S_OUT                               0x1A /* Out */
+#define STAC9221_NID_PIN_I2S_OUT                           0x1B /* Out */
+
+#if 1
+/* STAC9220 - Referenced thru STAC9220WIDGET in the constructor below. */
+static uint8_t const g_abStac9220Ports[]      = { 0x0A, 0xB, 0xC, 0xD, 0xE, 0xF, 0};
+static uint8_t const g_abStac9220Dacs[]       = { 0x02, 0x3, 0x4, 0x5, 0};
+static uint8_t const g_abStac9220Adcs[]       = { 0x06, 0x7, 0};
+static uint8_t const g_abStac9220SpdifOuts[]  = { 0x08, 0 };
+static uint8_t const g_abStac9220SpdifIns[]   = { 0x09, 0 };
+static uint8_t const g_abStac9220DigOutPins[] = { 0x10, 0 };
+static uint8_t const g_abStac9220DigInPins[]  = { 0x11, 0 };
+static uint8_t const g_abStac9220AdcVols[]    = { 0x17, 0x18, 0};
+static uint8_t const g_abStac9220AdcMuxs[]    = { 0x12, 0x13, 0};
+static uint8_t const g_abStac9220Pcbeeps[]    = { 0x14, 0 };
+static uint8_t const g_abStac9220Cds[]        = { 0x15, 0 };
+static uint8_t const g_abStac9220VolKnobs[]   = { 0x16, 0 };
+static uint8_t const g_abStac9220Reserveds[]  = { 0x09, 0x19, 0x1a, 0x1b, 0 };
+#else /** @todo Enable this after 5.0 -- needs more testing first. */
+static uint8_t const g_abStac9220Ports[]      = { STAC9220_NID_PIN_HEADPHONE0, STAC9220_NID_PIN_B, STAC9220_NID_PIN_C, STAC9220_NID_PIN_HEADPHONE1, STAC9220_NID_PIN_E, STAC9220_NID_PIN_F, 0};
+static uint8_t const g_abStac9220Dacs[]       = { STAC9220_NID_DAC0, STAC9220_NID_DAC1, STAC9220_NID_DAC2, STAC9220_NID_DAC3, 0};
+static uint8_t const g_abStac9220Adcs[]       = { STAC9220_NID_ADC0, STAC9220_NID_ADC1, 0};
+static uint8_t const g_abStac9220SpdifOuts[]  = { STAC9220_NID_SPDIF_OUT, 0 };
+static uint8_t const g_abStac9220SpdifIns[]   = { STAC9220_NID_SPDIF_IN, 0 };
+static uint8_t const g_abStac9220DigOutPins[] = { STAC9220_NID_PIN_SPDIF_OUT, 0 };
+static uint8_t const g_abStac9220DigInPins[]  = { STAC9220_NID_PIN_SPDIF_IN, 0 };
+static uint8_t const g_abStac9220AdcVols[]    = { STAC9220_NID_AMP_ADC0, STAC9220_NID_AMP_ADC1, 0};
+static uint8_t const g_abStac9220AdcMuxs[]    = { STAC9220_NID_ADC0_MUX, STAC9220_NID_ADC1_MUX, 0};
+static uint8_t const g_abStac9220Pcbeeps[]    = { STAC9220_NID_PCBEEP, 0 };
+static uint8_t const g_abStac9220Cds[]        = { STAC9220_NID_PIN_CD, 0 };
+static uint8_t const g_abStac9220VolKnobs[]   = { STAC9220_NID_VOL_KNOB, 0 };
+/* STAC 9221. */
+/** @todo Is STAC9220_NID_SPDIF_IN really correct for reserved nodes? */
+static uint8_t const g_abStac9220Reserveds[]  = { STAC9220_NID_SPDIF_IN, STAC9221_NID_ADAT_OUT, STAC9221_NID_I2S_OUT, STAC9221_NID_PIN_I2S_OUT, 0 };
+#endif
+
+/** SSM description of a CODECNODE. */
+static SSMFIELD const g_aCodecNodeFields[] =
+{
+    SSMFIELD_ENTRY(     CODECSAVEDSTATENODE, Core.id),
+    SSMFIELD_ENTRY_PAD_HC_AUTO(3, 3),
+    SSMFIELD_ENTRY(     CODECSAVEDSTATENODE, Core.au32F00_param),
+    SSMFIELD_ENTRY(     CODECSAVEDSTATENODE, Core.au32F02_param),
+    SSMFIELD_ENTRY(     CODECSAVEDSTATENODE, au32Params),
+    SSMFIELD_ENTRY_TERM()
+};
+
+/** Backward compatibility with v1 of the CODECNODE. */
+static SSMFIELD const g_aCodecNodeFieldsV1[] =
+{
+    SSMFIELD_ENTRY(     CODECSAVEDSTATENODE, Core.id),
+    SSMFIELD_ENTRY_PAD_HC_AUTO(3, 7),
+    SSMFIELD_ENTRY_OLD_HCPTR(Core.name),
+    SSMFIELD_ENTRY(     CODECSAVEDSTATENODE, Core.au32F00_param),
+    SSMFIELD_ENTRY(     CODECSAVEDSTATENODE, Core.au32F02_param),
+    SSMFIELD_ENTRY(     CODECSAVEDSTATENODE, au32Params),
+    SSMFIELD_ENTRY_TERM()
+};
+
+
+
+
+static DECLCALLBACK(void) stac9220DbgNodes(PHDACODEC pThis, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    for (int i = 1; i < 12; i++)
+    {
+        PCODECNODE pNode = &pThis->paNodes[i];
+        AMPLIFIER *pAmp = &pNode->dac.B_params;
+
+        uint8_t lVol = AMPLIFIER_REGISTER(*pAmp, AMPLIFIER_OUT, AMPLIFIER_LEFT, 0) & 0x7f;
+        uint8_t rVol = AMPLIFIER_REGISTER(*pAmp, AMPLIFIER_OUT, AMPLIFIER_RIGHT, 0) & 0x7f;
+
+        pHlp->pfnPrintf(pHlp, "0x%x: lVol=%RU8, rVol=%RU8\n", i, lVol, rVol);
+    }
+}
+
+
+static DECLCALLBACK(int) stac9220ResetNode(PHDACODEC pThis, uint8_t nodenum, PCODECNODE pNode)
+{
+    pNode->node.id = nodenum;
+    pNode->node.au32F00_param[0xF] = 0; /* Power statest Supported: are the same as AFG reports */
+    switch (nodenum)
+    {
+        /* Root Node*/
+        case 0:
+            pNode->node.au32F00_param[0x02] = CODEC_MAKE_F00_02(0x1, 0x0, 0x34, 0x1); /* rev id */
+            break;
+        case 1:
+            pNode->node.au32F00_param[0x08] = CODEC_MAKE_F00_08(1, 0xd, 0xd);
+            pNode->node.au32F00_param[0x0C] = CODEC_MAKE_F00_0C(0x17)
+                                            | CODEC_F00_0C_CAP_BALANCED_IO
+                                            | CODEC_F00_0C_CAP_INPUT
+                                            | CODEC_F00_0C_CAP_PRESENSE_DETECT
+                                            | CODEC_F00_0C_CAP_TRIGGER_REQUIRED
+                                            | CODEC_F00_0C_CAP_IMPENDANCE_SENSE;//(17 << 8)|RT_BIT(6)|RT_BIT(5)|RT_BIT(2)|RT_BIT(1)|RT_BIT(0);
+            pNode->node.au32F00_param[0x0B] = CODEC_F00_0B_PCM;
+            pNode->node.au32F00_param[0x0D] = CODEC_MAKE_F00_0D(1, 0x5, 0xE, 0);//RT_BIT(31)|(0x5 << 16)|(0xE)<<8;
+            pNode->node.au32F00_param[0x12] = RT_BIT(31)|(0x2 << 16)|(0x7f << 8)|0x7f;
+            pNode->node.au32F00_param[0x11] = CODEC_MAKE_F00_11(1, 1, 0, 0, 4);//0xc0000004;
+            pNode->node.au32F00_param[0x0F] = CODEC_F00_0F_D3|CODEC_F00_0F_D2|CODEC_F00_0F_D1|CODEC_F00_0F_D0;
+            pNode->afg.u32F05_param = CODEC_MAKE_F05(0, 0, 0, CODEC_F05_D2, CODEC_F05_D2);//0x2 << 4| 0x2; /* PS-Act: D3, PS->Set D3  */
+            pNode->afg.u32F08_param = 0;
+            pNode->afg.u32F17_param = 0;
+            break;
+        case 2:
+        case 3:
+        case 4:
+        case 5:
+            memset(pNode->dac.B_params, 0, AMPLIFIER_SIZE);
+            pNode->dac.u32A_param = CODEC_MAKE_A(0, 1, CODEC_A_MULT_1X, CODEC_A_DIV_1X, CODEC_A_16_BIT, 1);//RT_BIT(14)|(0x1 << 4)|0x1; /* 44100Hz/16bit/2ch */
+
+            AMPLIFIER_REGISTER(pNode->dac.B_params, AMPLIFIER_OUT, AMPLIFIER_LEFT, 0) = 0x7F | RT_BIT(7);
+            AMPLIFIER_REGISTER(pNode->dac.B_params, AMPLIFIER_OUT, AMPLIFIER_RIGHT, 0) = 0x7F | RT_BIT(7);
+
+            pNode->dac.node.au32F00_param[9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_OUTPUT, 0xD, 0)
+                                             | CODEC_F00_09_CAP_L_R_SWAP
+                                             | CODEC_F00_09_CAP_POWER_CTRL
+                                             | CODEC_F00_09_CAP_OUT_AMP_PRESENT
+                                             | CODEC_F00_09_CAP_LSB;//(0xD << 16) | RT_BIT(11) |  RT_BIT(10) | RT_BIT(2) | RT_BIT(0);
+            pNode->dac.u32F0c_param = 0;
+            pNode->dac.u32F05_param = CODEC_MAKE_F05(0, 0, 0, CODEC_F05_D3, CODEC_F05_D3);//0x3 << 4 | 0x3; /* PS-Act: D3, Set: D3  */
+            break;
+        case 6:
+            pNode->node.au32F02_param[0] = 0x17;
+            goto adc_init;
+        case 7:
+            pNode->node.au32F02_param[0] = 0x18;
+        adc_init:
+            pNode->adc.u32A_param = CODEC_MAKE_A(0, 1, CODEC_A_MULT_1X, CODEC_A_DIV_1X, CODEC_A_16_BIT, 1);//RT_BIT(14)|(0x1 << 3)|0x1; /* 44100Hz/16bit/2ch */
+            pNode->adc.node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(0, 1);//RT_BIT(0);
+            pNode->adc.u32F03_param = RT_BIT(0);
+            pNode->adc.u32F05_param = CODEC_MAKE_F05(0, 0, 0, CODEC_F05_D3, CODEC_F05_D3);//0x3 << 4 | 0x3; /* PS-Act: D3 Set: D3 */
+            pNode->adc.u32F06_param = 0;
+            pNode->adc.node.au32F00_param[9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_INPUT, 0xD, 0)
+                                             | CODEC_F00_09_CAP_POWER_CTRL
+                                             | CODEC_F00_09_CAP_CONNECTION_LIST
+                                             | CODEC_F00_09_CAP_PROC_WIDGET
+                                             | CODEC_F00_09_CAP_LSB;//RT_BIT(20)| (0xd << 16) |  RT_BIT(10) | RT_BIT(8) | RT_BIT(6)| RT_BIT(0);
+            break;
+        case 8:
+            pNode->spdifout.u32A_param = CODEC_MAKE_A(0, 1, CODEC_A_MULT_1X, CODEC_A_DIV_1X, CODEC_A_16_BIT, 1);//(1<<14)|(0x1<<4) | 0x1;
+            pNode->spdifout.node.au32F00_param[9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_OUTPUT, 0x4, 0)
+                                                  | CODEC_F00_09_CAP_DIGITAL
+                                                  | CODEC_F00_09_CAP_FMT_OVERRIDE
+                                                  | CODEC_F00_09_CAP_LSB;//(4 << 16) | RT_BIT(9)|RT_BIT(4)|0x1;
+            pNode->node.au32F00_param[0xa] = pThis->paNodes[1].node.au32F00_param[0xA];
+            pNode->spdifout.node.au32F00_param[0xB] = CODEC_F00_0B_PCM;
+            pNode->spdifout.u32F06_param = 0;
+            pNode->spdifout.u32F0d_param = 0;
+            break;
+        case 9:
+            pNode->spdifin.u32A_param = CODEC_MAKE_A(0, 1, CODEC_A_MULT_1X, CODEC_A_DIV_1X, CODEC_A_16_BIT, 1);//(0x1<<4) | 0x1;
+            pNode->spdifin.node.au32F00_param[9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_INPUT, 0x4, 0)
+                                                 | CODEC_F00_09_CAP_DIGITAL
+                                                 | CODEC_F00_09_CAP_CONNECTION_LIST
+                                                 | CODEC_F00_09_CAP_FMT_OVERRIDE
+                                                 | CODEC_F00_09_CAP_LSB;//(0x1 << 20)|(4 << 16) | RT_BIT(9)| RT_BIT(8)|RT_BIT(4)|0x1;
+            pNode->node.au32F00_param[0xA] = pThis->paNodes[1].node.au32F00_param[0xA];
+            pNode->node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(0, 1);//RT_BIT(0);
+            pNode->node.au32F02_param[0] = 0x11;
+            pNode->spdifin.node.au32F00_param[0xB] = CODEC_F00_0B_PCM;
+            pNode->spdifin.u32F06_param = 0;
+            pNode->spdifin.u32F0d_param = 0;
+            break;
+        case 0xA:
+            pNode->node.au32F00_param[0xC] = CODEC_MAKE_F00_0C(0x17)
+                                           | CODEC_F00_0C_CAP_INPUT
+                                           | CODEC_F00_0C_CAP_OUTPUT
+                                           | CODEC_F00_0C_CAP_HP
+                                           | CODEC_F00_0C_CAP_PRESENSE_DETECT
+                                           | CODEC_F00_0C_CAP_TRIGGER_REQUIRED
+                                           | CODEC_F00_0C_CAP_IMPENDANCE_SENSE;//0x173f;
+            pNode->node.au32F02_param[0] = 0x2;
+            pNode->port.u32F07_param = CODEC_F07_IN_ENABLE
+                                     | CODEC_F07_OUT_ENABLE;
+            pNode->port.u32F08_param = 0;
+            if (!pThis->fInReset)
+                pNode->port.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
+                                                          CODEC_F1C_LOCATION_FRONT,
+                                                          CODEC_F1C_DEVICE_HP,
+                                                          CODEC_F1C_CONNECTION_TYPE_1_8INCHES,
+                                                          CODEC_F1C_COLOR_GREEN,
+                                                          CODEC_F1C_MISC_JACK_DETECT,
+                                                          0x2, 0);//RT_MAKE_U32_FROM_U8(0x20, 0x40, 0x21, 0x02);
+            pNode->port.u32F09_param = CODEC_MAKE_F09_ANALOG(0, CODEC_F09_ANALOG_NA);//0x7fffffff;
+            goto port_init;
+        case 0xB:
+            pNode->node.au32F00_param[0xC] = CODEC_MAKE_F00_0C(0x17)
+                                           | CODEC_F00_0C_CAP_INPUT
+                                           | CODEC_F00_0C_CAP_OUTPUT
+                                           | CODEC_F00_0C_CAP_PRESENSE_DETECT
+                                           | CODEC_F00_0C_CAP_TRIGGER_REQUIRED
+                                           | CODEC_F00_0C_CAP_IMPENDANCE_SENSE;//0x1737;
+            pNode->node.au32F02_param[0] = 0x4;
+            pNode->port.u32F07_param = CODEC_F07_IN_ENABLE;
+            if (!pThis->fInReset)
+                pNode->port.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
+                                                          CODEC_F1C_LOCATION_INTERNAL|CODEC_F1C_LOCATION_REAR,
+                                                          CODEC_F1C_DEVICE_SPEAKER,
+                                                          CODEC_F1C_CONNECTION_TYPE_1_8INCHES,
+                                                          CODEC_F1C_COLOR_BLACK,
+                                                          CODEC_F1C_MISC_JACK_DETECT,
+                                                          0x1, 0x1);//RT_MAKE_U32_FROM_U8(0x11, 0x60, 0x11, 0x01);
+            pNode->port.u32F09_param = CODEC_MAKE_F09_ANALOG(1, CODEC_F09_ANALOG_NA);//RT_BIT(31)|0x7fffffff;
+            goto port_init;
+        case 0xC:
+            pNode->node.au32F02_param[0] = 0x3;
+            pNode->node.au32F00_param[0xC] = CODEC_MAKE_F00_0C(0x17)
+                                           | CODEC_F00_0C_CAP_INPUT
+                                           | CODEC_F00_0C_CAP_OUTPUT
+                                           | CODEC_F00_0C_CAP_PRESENSE_DETECT
+                                           | CODEC_F00_0C_CAP_TRIGGER_REQUIRED
+                                           | CODEC_F00_0C_CAP_IMPENDANCE_SENSE;//0x1737;
+            pNode->port.u32F07_param = CODEC_F07_IN_ENABLE;
+            if (!pThis->fInReset)
+                pNode->port.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
+                                                          CODEC_F1C_LOCATION_REAR,
+                                                          CODEC_F1C_DEVICE_SPEAKER,
+                                                          CODEC_F1C_CONNECTION_TYPE_1_8INCHES,
+                                                          CODEC_F1C_COLOR_GREEN,
+                                                          0x0, 0x1, 0x0);//RT_MAKE_U32_FROM_U8(0x10, 0x40, 0x11, 0x01);
+            pNode->port.u32F09_param = CODEC_MAKE_F09_ANALOG(1, CODEC_F09_ANALOG_NA);//RT_BIT(31)|0x7fffffff;
+            goto port_init;
+        case 0xD:
+            pNode->node.au32F00_param[0xC] = CODEC_MAKE_F00_0C(0x17)
+                                           | CODEC_F00_0C_CAP_INPUT
+                                           | CODEC_F00_0C_CAP_OUTPUT
+                                           | CODEC_F00_0C_CAP_PRESENSE_DETECT
+                                           | CODEC_F00_0C_CAP_TRIGGER_REQUIRED
+                                           | CODEC_F00_0C_CAP_IMPENDANCE_SENSE;//0x1737;
+            pNode->port.u32F07_param = CODEC_F07_IN_ENABLE;
+            pNode->node.au32F02_param[0] = 0x2;
+            if (!pThis->fInReset)
+                pNode->port.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
+                                                          CODEC_F1C_LOCATION_FRONT,
+                                                          CODEC_F1C_DEVICE_MIC,
+                                                          CODEC_F1C_CONNECTION_TYPE_1_8INCHES,
+                                                          CODEC_F1C_COLOR_PINK,
+                                                          0x0, 0x5, 0x0);//RT_MAKE_U32_FROM_U8(0x50, 0x90, 0xA1, 0x02); /* Microphone */
+            pNode->port.u32F09_param = CODEC_MAKE_F09_ANALOG(1, CODEC_F09_ANALOG_NA);//RT_BIT(31)|0x7fffffff;
+        port_init:
+            pNode->port.u32F08_param = 0;
+            pNode->node.au32F00_param[9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 0x0, 0)
+                                         | CODEC_F00_09_CAP_CONNECTION_LIST
+                                         | CODEC_F00_09_CAP_UNSOL
+                                         | CODEC_F00_09_CAP_LSB;//(4 << 20)|RT_BIT(8)|RT_BIT(7)|RT_BIT(0);
+            pNode->node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(0, 1);//0x1;
+            break;
+        case 0xE:
+            pNode->node.au32F00_param[9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 0x0, 0)
+                                         | CODEC_F00_09_CAP_UNSOL
+                                         | CODEC_F00_09_CAP_LSB;//(4 << 20)|RT_BIT(7)|RT_BIT(0);
+            pNode->port.u32F08_param = 0;
+            pNode->node.au32F00_param[0xC] = CODEC_F00_0C_CAP_INPUT
+                                           | CODEC_F00_0C_CAP_OUTPUT
+                                           | CODEC_F00_0C_CAP_PRESENSE_DETECT;//0x34;
+            pNode->port.u32F07_param = CODEC_F07_IN_ENABLE;
+            pNode->port.u32F09_param = CODEC_MAKE_F09_ANALOG(0, CODEC_F09_ANALOG_NA);//0x7fffffff;
+            if (!pThis->fInReset)
+                pNode->port.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
+                                                          CODEC_F1C_LOCATION_REAR,
+                                                          CODEC_F1C_DEVICE_LINE_OUT,
+                                                          CODEC_F1C_CONNECTION_TYPE_1_8INCHES,
+                                                          CODEC_F1C_COLOR_BLUE,
+                                                          0x0, 0x4, 0x0);//0x01013040;  /* Line Out */
+            break;
+        case 0xF:
+            pNode->node.au32F00_param[9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 0x0, 0x0)
+                                         | CODEC_F00_09_CAP_CONNECTION_LIST
+                                         | CODEC_F00_09_CAP_UNSOL
+                                         | CODEC_F00_09_CAP_OUT_AMP_PRESENT
+                                         | CODEC_F00_09_CAP_LSB;//(4 << 20)|RT_BIT(8)|RT_BIT(7)|RT_BIT(2)|RT_BIT(0);
+            pNode->node.au32F00_param[0xC] = CODEC_F00_0C_CAP_INPUT
+                                           | CODEC_F00_0C_CAP_OUTPUT
+                                           | CODEC_F00_0C_CAP_PRESENSE_DETECT
+                                           /* | CODEC_F00_0C_CAP_TRIGGER_REQUIRED
+                                           | CODEC_F00_0C_CAP_IMPENDANCE_SENSE */;//0x37;
+            pNode->node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(0, 1);//0x1;
+            pNode->port.u32F08_param = 0;
+            pNode->port.u32F07_param = CODEC_F07_OUT_ENABLE
+                                     | CODEC_F07_IN_ENABLE;
+            if (!pThis->fInReset)
+                pNode->port.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
+                                                          CODEC_F1C_LOCATION_INTERNAL,
+                                                          CODEC_F1C_DEVICE_SPEAKER,
+                                                          CODEC_F1C_CONNECTION_TYPE_1_8INCHES,
+                                                          CODEC_F1C_COLOR_ORANGE,
+                                                          0x0, 0x1, 0x2);//RT_MAKE_U32_FROM_U8(0x12, 0x60, 0x11, 0x01);
+            pNode->node.au32F02_param[0] = 0x5;
+            pNode->port.u32F09_param = CODEC_MAKE_F09_ANALOG(0, CODEC_F09_ANALOG_NA);//0x7fffffff;
+            break;
+        case 0x10:
+            pNode->node.au32F00_param[9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 0x0, 0x0)
+                                         | CODEC_F00_09_CAP_DIGITAL
+                                         | CODEC_F00_09_CAP_CONNECTION_LIST
+                                         | CODEC_F00_09_CAP_LSB;//(4<<20)|RT_BIT(9)|RT_BIT(8)|RT_BIT(0);
+            pNode->node.au32F00_param[0xC] = CODEC_F00_0C_CAP_OUTPUT;//RT_BIT(4);
+            pNode->node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(0, 0x3);
+            pNode->node.au32F02_param[0] = RT_MAKE_U32_FROM_U8(0x08, 0x17, 0x19, 0);
+            if (!pThis->fInReset)
+                pNode->digout.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
+                                                            CODEC_F1C_LOCATION_REAR,
+                                                            CODEC_F1C_DEVICE_SPDIF_OUT,
+                                                            CODEC_F1C_CONNECTION_TYPE_DIN,
+                                                            CODEC_F1C_COLOR_BLACK,
+                                                            0x0, 0x3, 0x0);//RT_MAKE_U32_FROM_U8(0x30, 0x10, 0x45, 0x01);
+            break;
+        case 0x11:
+            pNode->node.au32F00_param[9] = (4 << 20) | (3 << 16) | RT_BIT(10) | RT_BIT(9) | RT_BIT(7) | RT_BIT(0);
+            pNode->node.au32F00_param[0xC] = CODEC_F00_0C_CAP_EAPD
+                                           | CODEC_F00_0C_CAP_INPUT
+                                           | CODEC_F00_0C_CAP_PRESENSE_DETECT;//RT_BIT(16)| RT_BIT(5)|RT_BIT(2);
+            pNode->digin.u32F05_param = CODEC_MAKE_F05(0, 0, 0, CODEC_F05_D3, CODEC_F05_D3);//0x3 << 4 | 0x3; /* PS-Act: D3 -> D3 */
+            pNode->digin.u32F07_param = 0;
+            pNode->digin.u32F08_param = 0;
+            pNode->digin.u32F09_param = CODEC_MAKE_F09_DIGITAL(0, 0);
+            pNode->digin.u32F0c_param = 0;
+            if (!pThis->fInReset)
+                pNode->digin.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
+                                                           CODEC_F1C_LOCATION_REAR,
+                                                           CODEC_F1C_DEVICE_SPDIF_IN,
+                                                           CODEC_F1C_CONNECTION_TYPE_OTHER_DIGITAL,
+                                                           CODEC_F1C_COLOR_BLACK,
+                                                           0x0, 0x6, 0x0);//(0x1 << 24) | (0xc5 << 16) | (0x10 << 8) | 0x60;
+            break;
+        case 0x12:
+            pNode->adcmux.u32F01_param = 0;
+            goto adcmux_init;
+        case 0x13:
+            pNode->adcmux.u32F01_param = 1;
+            adcmux_init:
+            pNode->node.au32F00_param[9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_SELECTOR, 0x0, 0)
+                                         | CODEC_F00_09_CAP_CONNECTION_LIST
+                                         | CODEC_F00_09_CAP_AMP_FMT_OVERRIDE
+                                         | CODEC_F00_09_CAP_OUT_AMP_PRESENT
+                                         | CODEC_F00_09_CAP_LSB;//(3<<20)|RT_BIT(8)|RT_BIT(3)|RT_BIT(2)|RT_BIT(0);
+            pNode->node.au32F00_param[0xe] = CODEC_MAKE_F00_0E(0, 0x7);
+            pNode->node.au32F00_param[0x12] = (0x27 << 16)|(0x4 << 8);
+            /* STAC 9220 v10 6.21-22.{4,5} both(left and right) out amplefiers inited with 0*/
+            memset(pNode->adcmux.B_params, 0, AMPLIFIER_SIZE);
+            pNode->node.au32F02_param[0] = RT_MAKE_U32_FROM_U8(0xe, 0x15, 0xf, 0xb);
+            pNode->node.au32F02_param[4] = RT_MAKE_U32_FROM_U8(0xc, 0xd, 0xa, 0x0);
+            break;
+        case 0x14:
+            pNode->node.au32F00_param[9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_BEEP_GEN, 0, 0)
+                                         | CODEC_F00_09_CAP_AMP_FMT_OVERRIDE
+                                         | CODEC_F00_09_CAP_OUT_AMP_PRESENT;//(7 << 20) | RT_BIT(3) | RT_BIT(2);
+            pNode->node.au32F00_param[0x12] = (0x17 << 16)|(0x3 << 8)| 0x3;
+            pNode->pcbeep.u32F0a_param = 0;
+            memset(pNode->pcbeep.B_params, 0, AMPLIFIER_SIZE);
+            break;
+        case 0x15:
+            pNode->node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 0, 0)
+                                           | CODEC_F00_09_CAP_LSB;//(4 << 20)|RT_BIT(0);
+            pNode->node.au32F00_param[0xc] = CODEC_F00_0C_CAP_INPUT;//RT_BIT(5);
+            if (!pThis->fInReset)
+                pNode->cdnode.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_FIXED,
+                                                            CODEC_F1C_LOCATION_INTERNAL,
+                                                            CODEC_F1C_DEVICE_CD,
+                                                            CODEC_F1C_CONNECTION_TYPE_ATAPI,
+                                                            CODEC_F1C_COLOR_UNKNOWN,
+                                                            0x0, 0x7, 0x0);//RT_MAKE_U32_FROM_U8(0x70, 0x0, 0x33, 0x90);
+            break;
+        case 0x16:
+            pNode->node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_VOLUME_KNOB, 0x0, 0x0);//(0x6 << 20);
+            pNode->node.au32F00_param[0x13] = RT_BIT(7)| 0x7F;
+            pNode->node.au32F00_param[0xe] = CODEC_MAKE_F00_0E(0, 0x4);
+            pNode->node.au32F02_param[0] = RT_MAKE_U32_FROM_U8(0x2, 0x3, 0x4, 0x5);
+            pNode->volumeKnob.u32F08_param = 0;
+            pNode->volumeKnob.u32F0f_param = 0x7f;
+            break;
+        case 0x17:
+            pNode->node.au32F02_param[0] = 0x12;
+            goto adcvol_init;
+        case 0x18:
+            pNode->node.au32F02_param[0] = 0x13;
+        adcvol_init:
+            memset(pNode->adcvol.B_params, 0, AMPLIFIER_SIZE);
+
+            pNode->node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_SELECTOR, 0, 0)
+                                           | CODEC_F00_09_CAP_L_R_SWAP
+                                           | CODEC_F00_09_CAP_CONNECTION_LIST
+                                           | CODEC_F00_09_CAP_IN_AMP_PRESENT
+                                           | CODEC_F00_09_CAP_LSB;//(0x3 << 20)|RT_BIT(11)|RT_BIT(8)|RT_BIT(1)|RT_BIT(0);
+            pNode->node.au32F00_param[0xe] = CODEC_MAKE_F00_0E(0, 0x1);
+            AMPLIFIER_REGISTER(pNode->adcvol.B_params, AMPLIFIER_IN, AMPLIFIER_LEFT, 0) = RT_BIT(7);
+            AMPLIFIER_REGISTER(pNode->adcvol.B_params, AMPLIFIER_IN, AMPLIFIER_RIGHT, 0) = RT_BIT(7);
+            pNode->adcvol.u32F0c_param = 0;
+            break;
+        case 0x19:
+            pNode->node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_VENDOR_DEFINED, 0x3, 0)
+                                           | CODEC_F00_09_CAP_DIGITAL
+                                           | CODEC_F00_09_CAP_LSB;//(0xF << 20)|(0x3 << 16)|RT_BIT(9)|RT_BIT(0);
+            break;
+        case 0x1A:
+            pNode->node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_OUTPUT, 0x3, 0)
+                                           | CODEC_F00_09_CAP_DIGITAL
+                                           | CODEC_F00_09_CAP_LSB;//(0x3 << 16)|RT_BIT(9)|RT_BIT(0);
+            break;
+        case 0x1B:
+            pNode->node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 0, 0)
+                                           | CODEC_F00_09_CAP_DIGITAL
+                                           | CODEC_F00_09_CAP_CONNECTION_LIST
+                                           | CODEC_F00_09_CAP_LSB;//(0x4 << 20)|RT_BIT(9)|RT_BIT(8)|RT_BIT(0);
+            pNode->node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(0, 0x1);
+            pNode->node.au32F00_param[0xC] = CODEC_F00_0C_CAP_OUTPUT;//0x10;
+            pNode->node.au32F02_param[0] = 0x1a;
+            pNode->reserved.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_NO_PHYS,
+                                                          CODEC_F1C_LOCATION_NA,
+                                                          CODEC_F1C_DEVICE_LINE_OUT,
+                                                          CODEC_F1C_CONNECTION_TYPE_UNKNOWN,
+                                                          CODEC_F1C_COLOR_UNKNOWN,
+                                                          0x0, 0x0, 0xf);//0x4000000f;
+            break;
+        default:
+        break;
+    }
+    return VINF_SUCCESS;
+}
+
+
+static int stac9220Construct(PHDACODEC pThis)
+{
+    unconst(pThis->cTotalNodes) = 0x1C;
+    pThis->pfnCodecNodeReset = stac9220ResetNode;
+    pThis->pfnDbgListNodes   = stac9220DbgNodes;
+    pThis->u16VendorId = 0x8384;
+    pThis->u16DeviceId = 0x7680;
+    pThis->u8BSKU = 0x76;
+    pThis->u8AssemblyId = 0x80;
+    pThis->paNodes = (PCODECNODE)RTMemAllocZ(sizeof(CODECNODE) * pThis->cTotalNodes);
+    if (!pThis->paNodes)
+        return VERR_NO_MEMORY;
+    pThis->fInReset = false;
+#define STAC9220WIDGET(type) pThis->au8##type##s = g_abStac9220##type##s
+    STAC9220WIDGET(Port);
+    STAC9220WIDGET(Dac);
+    STAC9220WIDGET(Adc);
+    STAC9220WIDGET(AdcVol);
+    STAC9220WIDGET(AdcMux);
+    STAC9220WIDGET(Pcbeep);
+    STAC9220WIDGET(SpdifIn);
+    STAC9220WIDGET(SpdifOut);
+    STAC9220WIDGET(DigInPin);
+    STAC9220WIDGET(DigOutPin);
+    STAC9220WIDGET(Cd);
+    STAC9220WIDGET(VolKnob);
+    STAC9220WIDGET(Reserved);
+#undef STAC9220WIDGET
+    unconst(pThis->u8AdcVolsLineIn) = 0x17;
+    unconst(pThis->u8DacLineOut) = 0x3;
+
+    return VINF_SUCCESS;
+}
+
+
+/*
+ * Some generic predicate functions.
+ */
+
+#define DECLISNODEOFTYPE(type)                                                                  \
+    DECLINLINE(int) hdaCodecIs##type##Node(PHDACODEC pThis, uint8_t cNode)                     \
+    {                                                                                           \
+        Assert(pThis->au8##type##s);                                                           \
+        for (int i = 0; pThis->au8##type##s[i] != 0; ++i)                                      \
+            if (pThis->au8##type##s[i] == cNode)                                               \
+                return 1;                                                                       \
+        return 0;                                                                               \
+    }
+/* hdaCodecIsPortNode */
+DECLISNODEOFTYPE(Port)
+/* hdaCodecIsDacNode */
+DECLISNODEOFTYPE(Dac)
+/* hdaCodecIsAdcVolNode */
+DECLISNODEOFTYPE(AdcVol)
+/* hdaCodecIsAdcNode */
+DECLISNODEOFTYPE(Adc)
+/* hdaCodecIsAdcMuxNode */
+DECLISNODEOFTYPE(AdcMux)
+/* hdaCodecIsPcbeepNode */
+DECLISNODEOFTYPE(Pcbeep)
+/* hdaCodecIsSpdifOutNode */
+DECLISNODEOFTYPE(SpdifOut)
+/* hdaCodecIsSpdifInNode */
+DECLISNODEOFTYPE(SpdifIn)
+/* hdaCodecIsDigInPinNode */
+DECLISNODEOFTYPE(DigInPin)
+/* hdaCodecIsDigOutPinNode */
+DECLISNODEOFTYPE(DigOutPin)
+/* hdaCodecIsCdNode */
+DECLISNODEOFTYPE(Cd)
+/* hdaCodecIsVolKnobNode */
+DECLISNODEOFTYPE(VolKnob)
+/* hdaCodecIsReservedNode */
+DECLISNODEOFTYPE(Reserved)
+
+
+/*
+ * Misc helpers.
+ */
+static int hdaCodecToAudVolume(PHDACODEC pThis, AMPLIFIER *pAmp, PDMAUDIOMIXERCTL mt)
+{
+    uint32_t dir = AMPLIFIER_OUT;
+    ENMSOUNDSOURCE enmSrc;
+    switch (mt)
+    {
+        case PDMAUDIOMIXERCTL_PCM:
+            enmSrc = PO_INDEX;
+            dir = AMPLIFIER_OUT;
+            break;
+        case PDMAUDIOMIXERCTL_LINE_IN:
+            enmSrc = PI_INDEX;
+            dir = AMPLIFIER_IN;
+            break;
+        default:
+            AssertMsgFailedReturn(("Invalid mixer control %ld\n", mt), VERR_INVALID_PARAMETER);
+            break;
+    }
+
+    int mute = AMPLIFIER_REGISTER(*pAmp, dir, AMPLIFIER_LEFT, 0) & RT_BIT(7);
+    mute |= AMPLIFIER_REGISTER(*pAmp, dir, AMPLIFIER_RIGHT, 0) & RT_BIT(7);
+    mute >>=7;
+    mute &= 0x1;
+    uint8_t lVol = AMPLIFIER_REGISTER(*pAmp, dir, AMPLIFIER_LEFT, 0) & 0x7f;
+    uint8_t rVol = AMPLIFIER_REGISTER(*pAmp, dir, AMPLIFIER_RIGHT, 0) & 0x7f;
+
+    /* The STAC9220 volume controls have 0 to -96dB attenuation range in 128 steps.
+     * We have 0 to -96dB range in 256 steps. HDA volume setting of 127 must map
+     * to 255 internally (0dB), while HDA volume setting of 0 (-96dB) should map
+     * to 1 (rather than zero) internally.
+     */
+    lVol = (lVol + 1) * (2 * 255) / 256;
+    rVol = (rVol + 1) * (2 * 255) / 256;
+
+    return pThis->pfnSetVolume(pThis->pHDAState, enmSrc, RT_BOOL(mute), lVol, rVol);
+}
+
+DECLINLINE(void) hdaCodecSetRegister(uint32_t *pu32Reg, uint32_t u32Cmd, uint8_t u8Offset, uint32_t mask)
+{
+    Assert((pu32Reg && u8Offset < 32));
+    *pu32Reg &= ~(mask << u8Offset);
+    *pu32Reg |= (u32Cmd & mask) << u8Offset;
+}
+
+DECLINLINE(void) hdaCodecSetRegisterU8(uint32_t *pu32Reg, uint32_t u32Cmd, uint8_t u8Offset)
+{
+    hdaCodecSetRegister(pu32Reg, u32Cmd, u8Offset, CODEC_VERB_8BIT_DATA);
+}
+
+DECLINLINE(void) hdaCodecSetRegisterU16(uint32_t *pu32Reg, uint32_t u32Cmd, uint8_t u8Offset)
+{
+    hdaCodecSetRegister(pu32Reg, u32Cmd, u8Offset, CODEC_VERB_16BIT_DATA);
+}
+
+
+/*
+ * Verb processor functions.
+ */
+
+static DECLCALLBACK(int) vrbProcUnimplemented(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    LogFlowFunc(("cmd(raw:%x: cad:%x, d:%c, nid:%x, verb:%x)\n", cmd,
+        CODEC_CAD(cmd), CODEC_DIRECT(cmd) ? 'N' : 'Y', CODEC_NID(cmd), CODEC_VERBDATA(cmd)));
+    *pResp = 0;
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) vrbProcBreak(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    int rc;
+    rc = vrbProcUnimplemented(pThis, cmd, pResp);
+    *pResp |= CODEC_RESPONSE_UNSOLICITED;
+    return rc;
+}
+
+/* B-- */
+static DECLCALLBACK(int) vrbProcGetAmplifier(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    /* HDA spec 7.3.3.7 Note A */
+    /** @todo: if index out of range response should be 0 */
+    uint8_t u8Index = CODEC_GET_AMP_DIRECTION(cmd) == AMPLIFIER_OUT? 0 : CODEC_GET_AMP_INDEX(cmd);
+
+    PCODECNODE pNode = &pThis->paNodes[CODEC_NID(cmd)];
+    if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
+        *pResp = AMPLIFIER_REGISTER(pNode->dac.B_params,
+                            CODEC_GET_AMP_DIRECTION(cmd),
+                            CODEC_GET_AMP_SIDE(cmd),
+                            u8Index);
+    else if (hdaCodecIsAdcVolNode(pThis, CODEC_NID(cmd)))
+        *pResp = AMPLIFIER_REGISTER(pNode->adcvol.B_params,
+                            CODEC_GET_AMP_DIRECTION(cmd),
+                            CODEC_GET_AMP_SIDE(cmd),
+                            u8Index);
+    else if (hdaCodecIsAdcMuxNode(pThis, CODEC_NID(cmd)))
+        *pResp = AMPLIFIER_REGISTER(pNode->adcmux.B_params,
+                            CODEC_GET_AMP_DIRECTION(cmd),
+                            CODEC_GET_AMP_SIDE(cmd),
+                            u8Index);
+    else if (hdaCodecIsPcbeepNode(pThis, CODEC_NID(cmd)))
+        *pResp = AMPLIFIER_REGISTER(pNode->pcbeep.B_params,
+                            CODEC_GET_AMP_DIRECTION(cmd),
+                            CODEC_GET_AMP_SIDE(cmd),
+                            u8Index);
+    else if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
+        *pResp = AMPLIFIER_REGISTER(pNode->port.B_params,
+                            CODEC_GET_AMP_DIRECTION(cmd),
+                            CODEC_GET_AMP_SIDE(cmd),
+                            u8Index);
+    else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
+        *pResp = AMPLIFIER_REGISTER(pNode->adc.B_params,
+                            CODEC_GET_AMP_DIRECTION(cmd),
+                            CODEC_GET_AMP_SIDE(cmd),
+                            u8Index);
+    else
+        AssertMsgFailedReturn(("access to fields of %x need to be implemented\n", CODEC_NID(cmd)), VINF_SUCCESS);
+    return VINF_SUCCESS;
+}
+
+/* 3-- */
+static DECLCALLBACK(int) vrbProcSetAmplifier(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    bool fIsLeft = false;
+    bool fIsRight = false;
+    bool fIsOut = false;
+    bool fIsIn = false;
+    uint8_t u8Index = 0;
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    PCODECNODE pNode = &pThis->paNodes[CODEC_NID(cmd)];
+    AMPLIFIER *pAmplifier;
+    if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
+        pAmplifier = &pNode->dac.B_params;
+    else if (hdaCodecIsAdcVolNode(pThis, CODEC_NID(cmd)))
+        pAmplifier = &pNode->adcvol.B_params;
+    else if (hdaCodecIsAdcMuxNode(pThis, CODEC_NID(cmd)))
+        pAmplifier = &pNode->adcmux.B_params;
+    else if (hdaCodecIsPcbeepNode(pThis, CODEC_NID(cmd)))
+        pAmplifier = &pNode->pcbeep.B_params;
+    else if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
+        pAmplifier = &pNode->port.B_params;
+    else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
+        pAmplifier = &pNode->adc.B_params;
+    else
+        AssertFailedReturn(VINF_SUCCESS);
+
+    fIsOut = CODEC_SET_AMP_IS_OUT_DIRECTION(cmd);
+    fIsIn = CODEC_SET_AMP_IS_IN_DIRECTION(cmd);
+    fIsRight = CODEC_SET_AMP_IS_RIGHT_SIDE(cmd);
+    fIsLeft = CODEC_SET_AMP_IS_LEFT_SIDE(cmd);
+    u8Index = CODEC_SET_AMP_INDEX(cmd);
+    if (   (!fIsLeft && !fIsRight)
+        || (!fIsOut && !fIsIn))
+        return VINF_SUCCESS;
+    if (fIsIn)
+    {
+        if (fIsLeft)
+            hdaCodecSetRegisterU8(&AMPLIFIER_REGISTER(*pAmplifier, AMPLIFIER_IN, AMPLIFIER_LEFT, u8Index), cmd, 0);
+        if (fIsRight)
+            hdaCodecSetRegisterU8(&AMPLIFIER_REGISTER(*pAmplifier, AMPLIFIER_IN, AMPLIFIER_RIGHT, u8Index), cmd, 0);
+
+        /** @todo Fix ID of u8AdcVolsLineIn! */
+        hdaCodecToAudVolume(pThis, pAmplifier, PDMAUDIOMIXERCTL_LINE_IN);
+    }
+    if (fIsOut)
+    {
+        if (fIsLeft)
+            hdaCodecSetRegisterU8(&AMPLIFIER_REGISTER(*pAmplifier, AMPLIFIER_OUT, AMPLIFIER_LEFT, u8Index), cmd, 0);
+        if (fIsRight)
+            hdaCodecSetRegisterU8(&AMPLIFIER_REGISTER(*pAmplifier, AMPLIFIER_OUT, AMPLIFIER_RIGHT, u8Index), cmd, 0);
+
+        if (CODEC_NID(cmd) == pThis->u8DacLineOut)
+            hdaCodecToAudVolume(pThis, pAmplifier, PDMAUDIOMIXERCTL_PCM);
+    }
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) vrbProcGetParameter(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    Assert((cmd & CODEC_VERB_8BIT_DATA) < CODECNODE_F00_PARAM_LENGTH);
+    if ((cmd & CODEC_VERB_8BIT_DATA) >= CODECNODE_F00_PARAM_LENGTH)
+    {
+        LogFlowFunc(("invalid F00 parameter %d\n", (cmd & CODEC_VERB_8BIT_DATA)));
+        return VINF_SUCCESS;
+    }
+    *pResp = pThis->paNodes[CODEC_NID(cmd)].node.au32F00_param[cmd & CODEC_VERB_8BIT_DATA];
+    return VINF_SUCCESS;
+}
+
+/* F01 */
+static DECLCALLBACK(int) vrbProcGetConSelectCtrl(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsAdcMuxNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].adcmux.u32F01_param;
+    else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].digout.u32F01_param;
+    else if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].port.u32F01_param;
+    else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].adc.u32F01_param;
+    else if (hdaCodecIsAdcVolNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].adcvol.u32F01_param;
+    return VINF_SUCCESS;
+}
+
+/* 701 */
+static DECLCALLBACK(int) vrbProcSetConSelectCtrl(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    uint32_t *pu32Reg;
+    if (hdaCodecIsAdcMuxNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].adcmux.u32F01_param;
+    else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].digout.u32F01_param;
+    else if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].port.u32F01_param;
+    else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].adc.u32F01_param;
+    else if (hdaCodecIsAdcVolNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].adcvol.u32F01_param;
+    else
+        AssertFailedReturn(VINF_SUCCESS);
+    hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
+    return VINF_SUCCESS;
+}
+
+/* F07 */
+static DECLCALLBACK(int) vrbProcGetPinCtrl(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].port.u32F07_param;
+    else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].digout.u32F07_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].digin.u32F07_param;
+    else if (hdaCodecIsCdNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].cdnode.u32F07_param;
+    else if (hdaCodecIsPcbeepNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].pcbeep.u32F07_param;
+    else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].reserved.u32F07_param;
+    else
+        AssertMsgFailed(("Unsupported"));
+    return VINF_SUCCESS;
+}
+
+/* 707 */
+static DECLCALLBACK(int) vrbProcSetPinCtrl(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    uint32_t *pu32Reg;
+    if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].port.u32F07_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].digin.u32F07_param;
+    else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].digout.u32F07_param;
+    else if (hdaCodecIsCdNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].cdnode.u32F07_param;
+    else if (hdaCodecIsPcbeepNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].pcbeep.u32F07_param;
+    else if (   hdaCodecIsReservedNode(pThis, CODEC_NID(cmd))
+             && CODEC_NID(cmd) == 0x1b)
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].reserved.u32F07_param;
+    else
+        AssertFailedReturn(VINF_SUCCESS);
+    hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
+    return VINF_SUCCESS;
+}
+
+/* F08 */
+static DECLCALLBACK(int) vrbProcGetUnsolicitedEnabled(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].port.u32F08_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].digin.u32F08_param;
+    else if ((cmd) == 1 /* AFG */)
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].afg.u32F08_param;
+    else if (hdaCodecIsVolKnobNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].volumeKnob.u32F08_param;
+    else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].digout.u32F08_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].digin.u32F08_param;
+    else
+        AssertMsgFailed(("unsupported operation %x on node: %x\n", CODEC_VERB_CMD8(cmd), CODEC_NID(cmd)));
+    return VINF_SUCCESS;
+}
+
+/* 708 */
+static DECLCALLBACK(int) vrbProcSetUnsolicitedEnabled(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    uint32_t *pu32Reg;
+    if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].port.u32F08_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].digin.u32F08_param;
+    else if (CODEC_NID(cmd) == 1 /* AFG */)
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].afg.u32F08_param;
+    else if (hdaCodecIsVolKnobNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].volumeKnob.u32F08_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].digin.u32F08_param;
+    else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].digout.u32F08_param;
+    else
+        AssertMsgFailedReturn(("unsupported operation %x on node: %x\n", CODEC_VERB_CMD8(cmd), CODEC_NID(cmd)), VINF_SUCCESS);
+    hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
+    return VINF_SUCCESS;
+}
+
+/* F09 */
+static DECLCALLBACK(int) vrbProcGetPinSense(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].port.u32F09_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].digin.u32F09_param;
+    else
+        AssertMsgFailed(("unsupported operation %x on node: %x\n", CODEC_VERB_CMD8(cmd), CODEC_NID(cmd)));
+    return VINF_SUCCESS;
+}
+
+/* 709 */
+static DECLCALLBACK(int) vrbProcSetPinSense(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    uint32_t *pu32Reg;
+    if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].port.u32F09_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].digin.u32F09_param;
+    else
+        AssertFailedReturn(VINF_SUCCESS);
+    hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) vrbProcGetConnectionListEntry(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    *pResp = 0;
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    Assert((cmd & CODEC_VERB_8BIT_DATA) < CODECNODE_F02_PARAM_LENGTH);
+    if ((cmd & CODEC_VERB_8BIT_DATA) >= CODECNODE_F02_PARAM_LENGTH)
+    {
+        LogFlowFunc(("access to invalid F02 index %d\n", (cmd & CODEC_VERB_8BIT_DATA)));
+        return VINF_SUCCESS;
+    }
+    *pResp = pThis->paNodes[CODEC_NID(cmd)].node.au32F02_param[cmd & CODEC_VERB_8BIT_DATA];
+    return VINF_SUCCESS;
+}
+
+/* F03 */
+static DECLCALLBACK(int) vrbProcGetProcessingState(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].adc.u32F03_param;
+    return VINF_SUCCESS;
+}
+
+/* 703 */
+static DECLCALLBACK(int) vrbProcSetProcessingState(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
+        hdaCodecSetRegisterU8(&pThis->paNodes[CODEC_NID(cmd)].adc.u32F03_param, cmd, 0);
+    return VINF_SUCCESS;
+}
+
+/* F0D */
+static DECLCALLBACK(int) vrbProcGetDigitalConverter(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].spdifout.u32F0d_param;
+    else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].spdifin.u32F0d_param;
+    return VINF_SUCCESS;
+}
+
+static int codecSetDigitalConverter(PHDACODEC pThis, uint32_t cmd, uint8_t u8Offset, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
+        hdaCodecSetRegisterU8(&pThis->paNodes[CODEC_NID(cmd)].spdifout.u32F0d_param, cmd, u8Offset);
+    else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
+        hdaCodecSetRegisterU8(&pThis->paNodes[CODEC_NID(cmd)].spdifin.u32F0d_param, cmd, u8Offset);
+    return VINF_SUCCESS;
+}
+
+/* 70D */
+static DECLCALLBACK(int) vrbProcSetDigitalConverter1(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    return codecSetDigitalConverter(pThis, cmd, 0, pResp);
+}
+
+/* 70E */
+static DECLCALLBACK(int) vrbProcSetDigitalConverter2(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    return codecSetDigitalConverter(pThis, cmd, 8, pResp);
+}
+
+/* F20 */
+static DECLCALLBACK(int) vrbProcGetSubId(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    if (CODEC_NID(cmd) == 1 /* AFG */)
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].afg.u32F20_param;
+    else
+        *pResp = 0;
+    return VINF_SUCCESS;
+}
+
+static int codecSetSubIdX(PHDACODEC pThis, uint32_t cmd, uint8_t u8Offset)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    uint32_t *pu32Reg;
+    if (CODEC_NID(cmd) == 0x1 /* AFG */)
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].afg.u32F20_param;
+    else
+        AssertFailedReturn(VINF_SUCCESS);
+    hdaCodecSetRegisterU8(pu32Reg, cmd, u8Offset);
+    return VINF_SUCCESS;
+}
+
+/* 720 */
+static DECLCALLBACK(int) vrbProcSetSubId0(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    *pResp = 0;
+    return codecSetSubIdX(pThis, cmd, 0);
+}
+
+/* 721 */
+static DECLCALLBACK(int) vrbProcSetSubId1(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    *pResp = 0;
+    return codecSetSubIdX(pThis, cmd, 8);
+}
+
+/* 722 */
+static DECLCALLBACK(int) vrbProcSetSubId2(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    *pResp = 0;
+    return codecSetSubIdX(pThis, cmd, 16);
+}
+
+/* 723 */
+static DECLCALLBACK(int) vrbProcSetSubId3(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    *pResp = 0;
+    return codecSetSubIdX(pThis, cmd, 24);
+}
+
+static DECLCALLBACK(int) vrbProcReset(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) == 1 /* AFG */);
+    if (   CODEC_NID(cmd) == 1 /* AFG */
+        && pThis->pfnCodecNodeReset)
+    {
+        uint8_t i;
+        LogFlowFunc(("enters reset\n"));
+        Assert(pThis->pfnCodecNodeReset);
+        for (i = 0; i < pThis->cTotalNodes; ++i)
+        {
+            pThis->pfnCodecNodeReset(pThis, i, &pThis->paNodes[i]);
+        }
+        pThis->fInReset = false;
+        LogFlowFunc(("exits reset\n"));
+    }
+    *pResp = 0;
+    return VINF_SUCCESS;
+}
+
+/* F05 */
+static DECLCALLBACK(int) vrbProcGetPowerState(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (CODEC_NID(cmd) == 1 /* AFG */)
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].afg.u32F05_param;
+    else if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].dac.u32F05_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].digin.u32F05_param;
+    else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].adc.u32F05_param;
+    else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].spdifout.u32F05_param;
+    else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].spdifin.u32F05_param;
+    else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].reserved.u32F05_param;
+    return VINF_SUCCESS;
+}
+
+/* 705 */
+
+DECLINLINE(void) codecPropogatePowerState(uint32_t *pu32F05_param)
+{
+    Assert(pu32F05_param);
+    if (!pu32F05_param)
+        return;
+    bool fReset = CODEC_F05_IS_RESET(*pu32F05_param);
+    bool fStopOk = CODEC_F05_IS_STOPOK(*pu32F05_param);
+    uint8_t u8SetPowerState = CODEC_F05_SET(*pu32F05_param);
+    *pu32F05_param = CODEC_MAKE_F05(fReset, fStopOk, 0, u8SetPowerState, u8SetPowerState);
+}
+
+static DECLCALLBACK(int) vrbProcSetPowerState(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    uint32_t *pu32Reg;
+    if (CODEC_NID(cmd) == 1 /* AFG */)
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].afg.u32F05_param;
+    else if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].dac.u32F05_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].digin.u32F05_param;
+    else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].adc.u32F05_param;
+    else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].spdifout.u32F05_param;
+    else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].spdifin.u32F05_param;
+    else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].reserved.u32F05_param;
+    else
+        AssertFailedReturn(VINF_SUCCESS);
+
+    bool fReset = CODEC_F05_IS_RESET(*pu32Reg);
+    bool fStopOk = CODEC_F05_IS_STOPOK(*pu32Reg);
+
+    if (CODEC_NID(cmd) != 1 /* AFG */)
+    {
+        /*
+         * We shouldn't propogate actual power state, which actual for AFG
+         */
+        *pu32Reg = CODEC_MAKE_F05(fReset, fStopOk, 0,
+                                  CODEC_F05_ACT(pThis->paNodes[1].afg.u32F05_param),
+                                  CODEC_F05_SET(cmd));
+    }
+
+    /* Propagate next power state only if AFG is on or verb modifies AFG power state */
+    if (   CODEC_NID(cmd) == 1 /* AFG */
+        || !CODEC_F05_ACT(pThis->paNodes[1].afg.u32F05_param))
+    {
+        *pu32Reg = CODEC_MAKE_F05(fReset, fStopOk, 0, CODEC_F05_SET(cmd), CODEC_F05_SET(cmd));
+        if (   CODEC_NID(cmd) == 1 /* AFG */
+            && (CODEC_F05_SET(cmd)) == CODEC_F05_D0)
+        {
+            /* now we're powered on AFG and may propogate power states on nodes */
+            const uint8_t *pu8NodeIndex = &pThis->au8Dacs[0];
+            while (*(++pu8NodeIndex))
+                codecPropogatePowerState(&pThis->paNodes[*pu8NodeIndex].dac.u32F05_param);
+
+            pu8NodeIndex = &pThis->au8Adcs[0];
+            while (*(++pu8NodeIndex))
+                codecPropogatePowerState(&pThis->paNodes[*pu8NodeIndex].adc.u32F05_param);
+
+            pu8NodeIndex = &pThis->au8DigInPins[0];
+            while (*(++pu8NodeIndex))
+                codecPropogatePowerState(&pThis->paNodes[*pu8NodeIndex].digin.u32F05_param);
+        }
+    }
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) vrbProcGetStreamId(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].dac.u32F06_param;
+    else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].adc.u32F06_param;
+    else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].spdifin.u32F06_param;
+    else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].spdifout.u32F06_param;
+    else if (CODEC_NID(cmd) == 0x1A)
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].reserved.u32F06_param;
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) vrbProcSetStreamId(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    uint32_t *pu32addr;
+    if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
+        pu32addr = &pThis->paNodes[CODEC_NID(cmd)].dac.u32F06_param;
+    else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
+        pu32addr = &pThis->paNodes[CODEC_NID(cmd)].adc.u32F06_param;
+    else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
+        pu32addr = &pThis->paNodes[CODEC_NID(cmd)].spdifout.u32F06_param;
+    else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
+        pu32addr = &pThis->paNodes[CODEC_NID(cmd)].spdifin.u32F06_param;
+    else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
+        pu32addr = &pThis->paNodes[CODEC_NID(cmd)].reserved.u32F06_param;
+    else
+        AssertFailedReturn(VINF_SUCCESS);
+    hdaCodecSetRegisterU8(pu32addr, cmd, 0);
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) vrbProcGetConverterFormat(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].dac.u32A_param;
+    else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].adc.u32A_param;
+    else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].spdifout.u32A_param;
+    else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].spdifin.u32A_param;
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) vrbProcSetConverterFormat(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
+        hdaCodecSetRegisterU16(&pThis->paNodes[CODEC_NID(cmd)].dac.u32A_param, cmd, 0);
+    else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
+        hdaCodecSetRegisterU16(&pThis->paNodes[CODEC_NID(cmd)].adc.u32A_param, cmd, 0);
+    else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
+        hdaCodecSetRegisterU16(&pThis->paNodes[CODEC_NID(cmd)].spdifout.u32A_param, cmd, 0);
+    else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
+        hdaCodecSetRegisterU16(&pThis->paNodes[CODEC_NID(cmd)].spdifin.u32A_param, cmd, 0);
+    return VINF_SUCCESS;
+}
+
+/* F0C */
+static DECLCALLBACK(int) vrbProcGetEAPD_BTLEnabled(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsAdcVolNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].adcvol.u32F0c_param;
+    else if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].dac.u32F0c_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].digin.u32F0c_param;
+    return VINF_SUCCESS;
+}
+
+/* 70C */
+static DECLCALLBACK(int) vrbProcSetEAPD_BTLEnabled(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+
+    *pResp = 0;
+    uint32_t *pu32Reg;
+    if (hdaCodecIsAdcVolNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].adcvol.u32F0c_param;
+    else if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].dac.u32F0c_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].digin.u32F0c_param;
+    else
+        AssertFailedReturn(VINF_SUCCESS);
+    hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
+
+    return VINF_SUCCESS;
+}
+
+/* F0F */
+static DECLCALLBACK(int) vrbProcGetVolumeKnobCtrl(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsVolKnobNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].volumeKnob.u32F0f_param;
+    return VINF_SUCCESS;
+}
+
+/* 70F */
+static DECLCALLBACK(int) vrbProcSetVolumeKnobCtrl(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    uint32_t *pu32Reg = NULL;
+    *pResp = 0;
+    if (hdaCodecIsVolKnobNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].volumeKnob.u32F0f_param;
+    Assert(pu32Reg);
+    if (pu32Reg)
+        hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
+    return VINF_SUCCESS;
+}
+
+/* F17 */
+static DECLCALLBACK(int) vrbProcGetGPIOUnsolisted(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    /* note: this is true for ALC885 */
+    if (CODEC_NID(cmd) == 0x1 /* AFG */)
+        *pResp = pThis->paNodes[1].afg.u32F17_param;
+    return VINF_SUCCESS;
+}
+
+/* 717 */
+static DECLCALLBACK(int) vrbProcSetGPIOUnsolisted(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    uint32_t *pu32Reg = NULL;
+    *pResp = 0;
+    if (CODEC_NID(cmd) == 1 /* AFG */)
+        pu32Reg = &pThis->paNodes[1].afg.u32F17_param;
+    Assert(pu32Reg);
+    if (pu32Reg)
+        hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
+    return VINF_SUCCESS;
+}
+
+/* F1C */
+static DECLCALLBACK(int) vrbProcGetConfig(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    *pResp = 0;
+    if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].port.u32F1c_param;
+    else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].digout.u32F1c_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].digin.u32F1c_param;
+    else if (hdaCodecIsPcbeepNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].pcbeep.u32F1c_param;
+    else if (hdaCodecIsCdNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].cdnode.u32F1c_param;
+    else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
+        *pResp = pThis->paNodes[CODEC_NID(cmd)].reserved.u32F1c_param;
+    return VINF_SUCCESS;
+}
+
+static int codecSetConfigX(PHDACODEC pThis, uint32_t cmd, uint8_t u8Offset)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    Assert(CODEC_NID(cmd) < pThis->cTotalNodes);
+    if (CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
+        return VINF_SUCCESS;
+    }
+    uint32_t *pu32Reg = NULL;
+    if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].port.u32F1c_param;
+    else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].digin.u32F1c_param;
+    else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].digout.u32F1c_param;
+    else if (hdaCodecIsCdNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].cdnode.u32F1c_param;
+    else if (hdaCodecIsPcbeepNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].pcbeep.u32F1c_param;
+    else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
+        pu32Reg = &pThis->paNodes[CODEC_NID(cmd)].reserved.u32F1c_param;
+    Assert(pu32Reg);
+    if (pu32Reg)
+        hdaCodecSetRegisterU8(pu32Reg, cmd, u8Offset);
+    return VINF_SUCCESS;
+}
+
+/* 71C */
+static DECLCALLBACK(int) vrbProcSetConfig0(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    *pResp = 0;
+    return codecSetConfigX(pThis, cmd, 0);
+}
+
+/* 71D */
+static DECLCALLBACK(int) vrbProcSetConfig1(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    *pResp = 0;
+    return codecSetConfigX(pThis, cmd, 8);
+}
+
+/* 71E */
+static DECLCALLBACK(int) vrbProcSetConfig2(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    *pResp = 0;
+    return codecSetConfigX(pThis, cmd, 16);
+}
+
+/* 71E */
+static DECLCALLBACK(int) vrbProcSetConfig3(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp)
+{
+    *pResp = 0;
+    return codecSetConfigX(pThis, cmd, 24);
+}
+
+
+/**
+ * HDA codec verb map.
+ * @todo Any reason not to use binary search here?
+ */
+static const CODECVERB g_aCodecVerbs[] =
+{
+/*     verb     | verb mask              | callback               */
+/*   -----------  --------------------   -----------------------  */
+    { 0x000F0000, CODEC_VERB_8BIT_CMD , vrbProcGetParameter           },
+    { 0x000F0100, CODEC_VERB_8BIT_CMD , vrbProcGetConSelectCtrl       },
+    { 0x00070100, CODEC_VERB_8BIT_CMD , vrbProcSetConSelectCtrl       },
+    { 0x000F0600, CODEC_VERB_8BIT_CMD , vrbProcGetStreamId            },
+    { 0x00070600, CODEC_VERB_8BIT_CMD , vrbProcSetStreamId            },
+    { 0x000F0700, CODEC_VERB_8BIT_CMD , vrbProcGetPinCtrl             },
+    { 0x00070700, CODEC_VERB_8BIT_CMD , vrbProcSetPinCtrl             },
+    { 0x000F0800, CODEC_VERB_8BIT_CMD , vrbProcGetUnsolicitedEnabled  },
+    { 0x00070800, CODEC_VERB_8BIT_CMD , vrbProcSetUnsolicitedEnabled  },
+    { 0x000F0900, CODEC_VERB_8BIT_CMD , vrbProcGetPinSense            },
+    { 0x00070900, CODEC_VERB_8BIT_CMD , vrbProcSetPinSense            },
+    { 0x000F0200, CODEC_VERB_8BIT_CMD , vrbProcGetConnectionListEntry },
+    { 0x000F0300, CODEC_VERB_8BIT_CMD , vrbProcGetProcessingState     },
+    { 0x00070300, CODEC_VERB_8BIT_CMD , vrbProcSetProcessingState     },
+    { 0x000F0D00, CODEC_VERB_8BIT_CMD , vrbProcGetDigitalConverter    },
+    { 0x00070D00, CODEC_VERB_8BIT_CMD , vrbProcSetDigitalConverter1   },
+    { 0x00070E00, CODEC_VERB_8BIT_CMD , vrbProcSetDigitalConverter2   },
+    { 0x000F2000, CODEC_VERB_8BIT_CMD , vrbProcGetSubId               },
+    { 0x00072000, CODEC_VERB_8BIT_CMD , vrbProcSetSubId0              },
+    { 0x00072100, CODEC_VERB_8BIT_CMD , vrbProcSetSubId1              },
+    { 0x00072200, CODEC_VERB_8BIT_CMD , vrbProcSetSubId2              },
+    { 0x00072300, CODEC_VERB_8BIT_CMD , vrbProcSetSubId3              },
+    { 0x0007FF00, CODEC_VERB_8BIT_CMD , vrbProcReset                  },
+    { 0x000F0500, CODEC_VERB_8BIT_CMD , vrbProcGetPowerState          },
+    { 0x00070500, CODEC_VERB_8BIT_CMD , vrbProcSetPowerState          },
+    { 0x000F0C00, CODEC_VERB_8BIT_CMD , vrbProcGetEAPD_BTLEnabled     },
+    { 0x00070C00, CODEC_VERB_8BIT_CMD , vrbProcSetEAPD_BTLEnabled     },
+    { 0x000F0F00, CODEC_VERB_8BIT_CMD , vrbProcGetVolumeKnobCtrl      },
+    { 0x00070F00, CODEC_VERB_8BIT_CMD , vrbProcSetVolumeKnobCtrl      },
+    { 0x000F1700, CODEC_VERB_8BIT_CMD , vrbProcGetGPIOUnsolisted      },
+    { 0x00071700, CODEC_VERB_8BIT_CMD , vrbProcSetGPIOUnsolisted      },
+    { 0x000F1C00, CODEC_VERB_8BIT_CMD , vrbProcGetConfig              },
+    { 0x00071C00, CODEC_VERB_8BIT_CMD , vrbProcSetConfig0             },
+    { 0x00071D00, CODEC_VERB_8BIT_CMD , vrbProcSetConfig1             },
+    { 0x00071E00, CODEC_VERB_8BIT_CMD , vrbProcSetConfig2             },
+    { 0x00071F00, CODEC_VERB_8BIT_CMD , vrbProcSetConfig3             },
+    { 0x000A0000, CODEC_VERB_16BIT_CMD, vrbProcGetConverterFormat     },
+    { 0x00020000, CODEC_VERB_16BIT_CMD, vrbProcSetConverterFormat     },
+    { 0x000B0000, CODEC_VERB_16BIT_CMD, vrbProcGetAmplifier           },
+    { 0x00030000, CODEC_VERB_16BIT_CMD, vrbProcSetAmplifier           },
+};
+
+#ifdef DEBUG
+typedef struct CODECDBGINFO
+{
+    /** DBGF info helpers. */
+    PCDBGFINFOHLP pHlp;
+    /** Current recursion level. */
+    uint8_t uLevel;
+    /** Pointer to codec state. */
+    PHDACODEC pThis;
+
+} CODECDBGINFO, *PCODECDBGINFO;
+
+#define CODECDBG_INDENT   pInfo->uLevel++;
+#define CODECDBG_UNINDENT if (pInfo->uLevel) pInfo->uLevel--;
+
+#define CODECDBG_PRINT(...)  pInfo->pHlp->pfnPrintf(pInfo->pHlp, __VA_ARGS__)
+#define CODECDBG_PRINTI(...) codecDbgPrintf(pInfo, __VA_ARGS__)
+
+static void codecDbgPrintfIndentV(PCODECDBGINFO pInfo, uint16_t uIndent, const char *pszFormat, va_list va)
+{
+    char *pszValueFormat;
+    if (RTStrAPrintfV(&pszValueFormat, pszFormat, va))
+    {
+        pInfo->pHlp->pfnPrintf(pInfo->pHlp, "%*s%s", uIndent, "", pszValueFormat);
+        RTStrFree(pszValueFormat);
+    }
+}
+
+static void codecDbgPrintf(PCODECDBGINFO pInfo, const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    codecDbgPrintfIndentV(pInfo, pInfo->uLevel * 4, pszFormat, va);
+    va_end(va);
+}
+
+/* Power state */
+static void codecDbgPrintNodeRegF05(PCODECDBGINFO pInfo, uint32_t u32Reg)
+{
+    codecDbgPrintf(pInfo, "Power (F05): fReset=%RTbool, fStopOk=%RTbool, Set=%RU8, Act=%RU8\n",
+                   CODEC_F05_IS_RESET(u32Reg), CODEC_F05_IS_STOPOK(u32Reg), CODEC_F05_SET(u32Reg), CODEC_F05_ACT(u32Reg));
+}
+
+static void codecDbgPrintNodeRegA(PCODECDBGINFO pInfo, uint32_t u32Reg)
+{
+    codecDbgPrintf(pInfo, "RegA: %x\n", u32Reg);
+}
+
+static void codecDbgPrintNodeRegF00(PCODECDBGINFO pInfo, uint32_t *paReg00)
+{
+    codecDbgPrintf(pInfo, "Parameters (F00):\n");
+
+    CODECDBG_INDENT
+        codecDbgPrintf(pInfo, "Amplifier Caps:\n");
+        uint32_t uReg = paReg00[0xD];
+        CODECDBG_INDENT
+            codecDbgPrintf(pInfo, "Input Steps=%02RU8, StepSize=%02RU8, StepOff=%02RU8, fCanMute=%RTbool\n",
+                           CODEC_F00_0D_NUM_STEPS(uReg),
+                           CODEC_F00_0D_STEP_SIZE(uReg),
+                           CODEC_F00_0D_OFFSET(uReg),
+                           RT_BOOL(CODEC_F00_0D_IS_CAP_MUTE(uReg)));
+
+            uReg = paReg00[0x12];
+            codecDbgPrintf(pInfo, "Output Steps=%02RU8, StepSize=%02RU8, StepOff=%02RU8, fCanMute=%RTbool\n",
+                           CODEC_F00_12_NUM_STEPS(uReg),
+                           CODEC_F00_12_STEP_SIZE(uReg),
+                           CODEC_F00_12_OFFSET(uReg),
+                           RT_BOOL(CODEC_F00_0D_IS_CAP_MUTE(uReg)));
+        CODECDBG_UNINDENT
+    CODECDBG_UNINDENT
+}
+
+static void codecDbgPrintNodeAmp(PCODECDBGINFO pInfo, uint32_t *paReg, uint8_t uIdx, uint8_t uDir)
+{
+#define CODECDBG_AMP(reg, chan) \
+    codecDbgPrintf(pInfo, "Amp %RU8 %s %s: In=%RTbool, Out=%RTbool, Left=%RTbool, Right=%RTbool, Idx=%RU8, fMute=%RTbool, uGain=%RU8\n", \
+                   uIdx, chan, uDir == AMPLIFIER_IN ? "In" : "Out", \
+                   RT_BOOL(CODEC_SET_AMP_IS_IN_DIRECTION(reg)), RT_BOOL(CODEC_SET_AMP_IS_OUT_DIRECTION(reg)), \
+                   RT_BOOL(CODEC_SET_AMP_IS_LEFT_SIDE(reg)), RT_BOOL(CODEC_SET_AMP_IS_RIGHT_SIDE(reg)), \
+                   CODEC_SET_AMP_INDEX(reg), RT_BOOL(CODEC_SET_AMP_MUTE(reg)), CODEC_SET_AMP_GAIN(reg));
+
+    uint32_t regAmp = AMPLIFIER_REGISTER(paReg, uDir, AMPLIFIER_LEFT, uIdx);
+    CODECDBG_AMP(regAmp, "Left");
+    regAmp = AMPLIFIER_REGISTER(paReg, uDir, AMPLIFIER_RIGHT, uIdx);
+    CODECDBG_AMP(regAmp, "Right");
+
+#undef CODECDBG_AMP
+}
+
+static void codecDbgPrintNodeConnections(PCODECDBGINFO pInfo, PCODECNODE pNode)
+{
+    if (pNode->node.au32F00_param[0xE] == 0) /* Directly connected to HDA link. */
+    {
+         codecDbgPrintf(pInfo, "[HDA LINK]\n");
+         return;
+    }
+}
+
+static void codecDbgPrintNode(PCODECDBGINFO pInfo, PCODECNODE pNode)
+{
+    codecDbgPrintf(pInfo, "Node 0x%02x (%02RU8): ", pNode->node.id, pNode->node.id);
+
+    if (pNode->node.id == STAC9220_NID_ROOT)
+    {
+        CODECDBG_PRINT("ROOT\n");
+    }
+    else if (pNode->node.id == STAC9220_NID_AFG)
+    {
+        CODECDBG_PRINT("AFG\n");
+        CODECDBG_INDENT
+            codecDbgPrintNodeRegF00(pInfo, pNode->node.au32F00_param);
+            codecDbgPrintNodeRegF05(pInfo, pNode->afg.u32F05_param);
+        CODECDBG_UNINDENT
+    }
+    else if (hdaCodecIsPortNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("PORT\n");
+    }
+    else if (hdaCodecIsDacNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("DAC\n");
+        CODECDBG_INDENT
+            codecDbgPrintNodeRegF00(pInfo, pNode->node.au32F00_param);
+            codecDbgPrintNodeRegF05(pInfo, pNode->dac.u32F05_param);
+            codecDbgPrintNodeRegA  (pInfo, pNode->dac.u32A_param);
+            codecDbgPrintNodeAmp   (pInfo, pNode->dac.B_params, 0, AMPLIFIER_OUT);
+        CODECDBG_UNINDENT
+    }
+    else if (hdaCodecIsAdcVolNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("ADC VOLUME\n");
+        CODECDBG_INDENT
+            codecDbgPrintNodeRegF00(pInfo, pNode->node.au32F00_param);
+            codecDbgPrintNodeRegA  (pInfo, pNode->adcvol.u32A_params);
+            codecDbgPrintNodeAmp   (pInfo, pNode->adcvol.B_params, 0, AMPLIFIER_IN);
+        CODECDBG_UNINDENT
+    }
+    else if (hdaCodecIsAdcNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("ADC\n");
+        CODECDBG_INDENT
+            codecDbgPrintNodeRegF00(pInfo, pNode->node.au32F00_param);
+            codecDbgPrintNodeRegF05(pInfo, pNode->adc.u32F05_param);
+            codecDbgPrintNodeRegA  (pInfo, pNode->adc.u32A_param);
+            codecDbgPrintNodeAmp   (pInfo, pNode->adc.B_params, 0, AMPLIFIER_IN);
+        CODECDBG_UNINDENT
+    }
+    else if (hdaCodecIsAdcMuxNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("ADC MUX\n");
+        CODECDBG_INDENT
+            codecDbgPrintNodeRegF00(pInfo, pNode->node.au32F00_param);
+            codecDbgPrintNodeRegA  (pInfo, pNode->adcmux.u32A_param);
+            codecDbgPrintNodeAmp   (pInfo, pNode->adcmux.B_params, 0, AMPLIFIER_IN);
+        CODECDBG_UNINDENT
+    }
+    else if (hdaCodecIsPcbeepNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("PC BEEP\n");
+    }
+    else if (hdaCodecIsSpdifOutNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("SPDIF OUT\n");
+    }
+    else if (hdaCodecIsSpdifInNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("SPDIF IN\n");
+    }
+    else if (hdaCodecIsDigInPinNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("DIGITAL IN PIN\n");
+    }
+    else if (hdaCodecIsDigOutPinNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("DIGITAL OUT PIN\n");
+    }
+    else if (hdaCodecIsCdNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("CD\n");
+    }
+    else if (hdaCodecIsVolKnobNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("VOLUME KNOB\n");
+    }
+    else if (hdaCodecIsReservedNode(pInfo->pThis, pNode->node.id))
+    {
+        CODECDBG_PRINT("RESERVED\n");
+    }
+    else
+        CODECDBG_PRINT("UNKNOWN TYPE 0x%x\n", pNode->node.id);
+}
+
+static DECLCALLBACK(void) codecDbgListNodes(PHDACODEC pThis, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    pHlp->pfnPrintf(pHlp, "HDA LINK\n");
+
+    CODECDBGINFO dbgInfo;
+    dbgInfo.pHlp   = pHlp;
+    dbgInfo.pThis  = pThis;
+    dbgInfo.uLevel = 0;
+
+    PCODECDBGINFO pInfo = &dbgInfo;
+
+    CODECDBG_INDENT
+        for (uint8_t i = 0; i < pThis->cTotalNodes; i++)
+        {
+            PCODECNODE pNode = &pThis->paNodes[i];
+            if (pNode->node.au32F00_param[0xE] == 0) /* Start with all nodes connected directly to the HDA (Azalia) link. */
+                codecDbgPrintNode(&dbgInfo, pNode);
+        }
+    CODECDBG_UNINDENT
+}
+
+static DECLCALLBACK(void) codecDbgSelector(PHDACODEC pThis, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+
+}
+#endif
+
+static DECLCALLBACK(int) codecLookup(PHDACODEC pThis, uint32_t cmd, PPFNHDACODECVERBPROCESSOR pfn)
+{
+    Assert(CODEC_CAD(cmd) == pThis->id);
+    if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
+        LogFlowFunc(("cmd %x was addressed to reserved node\n", cmd));
+
+    if (   CODEC_VERBDATA(cmd) == 0
+        || CODEC_NID(cmd) >= pThis->cTotalNodes)
+    {
+        *pfn = vrbProcUnimplemented;
+        /// @todo r=michaln: There needs to be a counter to avoid log flooding (see e.g. DevRTC.cpp)
+        LogFlowFunc(("cmd %x was ignored\n", cmd));
+        return VINF_SUCCESS;
+    }
+
+    for (int i = 0; i < pThis->cVerbs; ++i)
+    {
+        if ((CODEC_VERBDATA(cmd) & pThis->paVerbs[i].mask) == pThis->paVerbs[i].verb)
+        {
+            *pfn = pThis->paVerbs[i].pfn;
+            return VINF_SUCCESS;
+        }
+    }
+
+    *pfn = vrbProcUnimplemented;
+    LogFlowFunc(("callback for %x wasn't found\n", CODEC_VERBDATA(cmd)));
+    return VINF_SUCCESS;
+}
+
+/*
+ * APIs exposed to DevHDA.
+ */
+
+/**
+ *
+ * routines open one of the voices (IN, OUT) with corresponding parameters.
+ * this routine could be called from HDA on setting/resseting sound format.
+ *
+ * @todo Probably passed settings should be verified (if AFG's declared proposed
+ *       format) before enabling.
+ */
+int hdaCodecOpenStream(PHDACODEC pThis, ENMSOUNDSOURCE enmSoundSource, PPDMAUDIOSTREAMCFG pCfg)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+    int rc;
+    switch (enmSoundSource)
+    {
+        case PI_INDEX:
+            rc = pThis->pfnOpenIn(pThis->pHDAState,  "hda.in", PDMAUDIORECSOURCE_LINE_IN, pCfg);
+            break;
+#ifdef VBOX_WITH_HDA_MIC_IN
+        case MC_INDEX:
+            rc = pThis->pfnOpenIn(pThis->pHDAState,  "hda.mc", PDMAUDIORECSOURCE_MIC, pCfg);
+            break;
+#endif
+        case PO_INDEX:
+            rc = pThis->pfnOpenOut(pThis->pHDAState, "hda.out", pCfg);
+            break;
+
+        default:
+            AssertMsgFailed(("Index %ld not implemented\n", enmSoundSource));
+            rc = VERR_NOT_IMPLEMENTED;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+int hdaCodecSaveState(PHDACODEC pThis, PSSMHANDLE pSSM)
+{
+    AssertLogRelMsgReturn(pThis->cTotalNodes == 0x1c, ("cTotalNodes=%#x, should be 0x1c", pThis->cTotalNodes),
+                          VERR_INTERNAL_ERROR);
+    SSMR3PutU32(pSSM, pThis->cTotalNodes);
+    for (unsigned idxNode = 0; idxNode < pThis->cTotalNodes; ++idxNode)
+        SSMR3PutStructEx(pSSM, &pThis->paNodes[idxNode].SavedState, sizeof(pThis->paNodes[idxNode].SavedState),
+                         0 /*fFlags*/, g_aCodecNodeFields, NULL /*pvUser*/);
+    return VINF_SUCCESS;
+}
+
+int hdaCodecLoadState(PHDACODEC pThis, PSSMHANDLE pSSM, uint32_t uVersion)
+{
+    PCSSMFIELD pFields;
+    uint32_t   fFlags;
+    switch (uVersion)
+    {
+        case HDA_SSM_VERSION_1:
+            AssertReturn(pThis->cTotalNodes == 0x1c, VERR_INTERNAL_ERROR);
+            pFields = g_aCodecNodeFieldsV1;
+            fFlags  = SSMSTRUCT_FLAGS_MEM_BAND_AID_RELAXED;
+            break;
+
+        case HDA_SSM_VERSION_2:
+        case HDA_SSM_VERSION_3:
+            AssertReturn(pThis->cTotalNodes == 0x1c, VERR_INTERNAL_ERROR);
+            pFields = g_aCodecNodeFields;
+            fFlags  = SSMSTRUCT_FLAGS_MEM_BAND_AID_RELAXED;
+            break;
+
+        /* Since version 4 a flexible node count is supported. */
+        case HDA_SSM_VERSION_4:
+        case HDA_SSM_VERSION_5:
+        case HDA_SSM_VERSION:
+        {
+            uint32_t cNodes;
+            int rc2 = SSMR3GetU32(pSSM, &cNodes);
+            AssertRCReturn(rc2, rc2);
+            if (cNodes != 0x1c)
+                return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+            AssertReturn(pThis->cTotalNodes == 0x1c, VERR_INTERNAL_ERROR);
+
+            pFields = g_aCodecNodeFields;
+            fFlags  = 0;
+            break;
+        }
+
+        default:
+            return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+    }
+
+    for (unsigned idxNode = 0; idxNode < pThis->cTotalNodes; ++idxNode)
+    {
+        uint8_t idOld = pThis->paNodes[idxNode].SavedState.Core.id;
+        int rc = SSMR3GetStructEx(pSSM, &pThis->paNodes[idxNode].SavedState,
+                                  sizeof(pThis->paNodes[idxNode].SavedState),
+                                  fFlags, pFields, NULL);
+        if (RT_FAILURE(rc))
+            return rc;
+        AssertLogRelMsgReturn(idOld == pThis->paNodes[idxNode].SavedState.Core.id,
+                              ("loaded %#x, expected %#x\n", pThis->paNodes[idxNode].SavedState.Core.id, idOld),
+                              VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+    }
+
+    /*
+     * Update stuff after changing the state.
+     */
+    if (hdaCodecIsDacNode(pThis, pThis->u8DacLineOut))
+        hdaCodecToAudVolume(pThis, &pThis->paNodes[pThis->u8DacLineOut].dac.B_params, PDMAUDIOMIXERCTL_PCM);
+    else if (hdaCodecIsSpdifOutNode(pThis, pThis->u8DacLineOut))
+        hdaCodecToAudVolume(pThis, &pThis->paNodes[pThis->u8DacLineOut].spdifout.B_params, PDMAUDIOMIXERCTL_PCM);
+    hdaCodecToAudVolume(pThis, &pThis->paNodes[pThis->u8AdcVolsLineIn].adcvol.B_params, PDMAUDIOMIXERCTL_LINE_IN);
+
+    return VINF_SUCCESS;
+}
+
+int hdaCodecDestruct(PHDACODEC pThis)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+    if (pThis->paNodes)
+    {
+        RTMemFree(pThis->paNodes);
+        pThis->paNodes = NULL;
+    }
+
+    return VINF_SUCCESS;
+}
+
+int hdaCodecConstruct(PPDMDEVINS pDevIns, PHDACODEC pThis,
+                      uint16_t uLUN, PCFGMNODE pCfg)
+{
+    AssertPtrReturn(pDevIns, VERR_INVALID_POINTER);
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    pThis->id        = uLUN;
+    pThis->paVerbs   = &g_aCodecVerbs[0];
+    pThis->cVerbs    = RT_ELEMENTS(g_aCodecVerbs);
+
+    pThis->pfnLookup       = codecLookup;
+#ifdef DEBUG
+    pThis->pfnDbgSelector  = codecDbgSelector;
+    pThis->pfnDbgListNodes = codecDbgListNodes;
+#endif
+    int rc = stac9220Construct(pThis);
+    AssertRC(rc);
+
+    /* common root node initializers */
+    pThis->paNodes[0].node.au32F00_param[0] = CODEC_MAKE_F00_00(pThis->u16VendorId, pThis->u16DeviceId);
+    pThis->paNodes[0].node.au32F00_param[4] = CODEC_MAKE_F00_04(0x1, 0x1);
+    /* common AFG node initializers */
+    pThis->paNodes[1].node.au32F00_param[4] = CODEC_MAKE_F00_04(0x2, pThis->cTotalNodes - 2);
+    pThis->paNodes[1].node.au32F00_param[5] = CODEC_MAKE_F00_05(1, CODEC_F00_05_AFG);
+    pThis->paNodes[1].afg.u32F20_param = CODEC_MAKE_F20(pThis->u16VendorId, pThis->u8BSKU, pThis->u8AssemblyId);
+
+    /* This codec uses a fixed setting (44.1 kHz, 16-bit signed, 2 channels). */
+    pThis->strmCfg.uHz           = 44100;
+    pThis->strmCfg.cChannels     = 2;
+    pThis->strmCfg.enmFormat     = AUD_FMT_S16;
+    pThis->strmCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
+
+    hdaCodecOpenStream(pThis, PI_INDEX, &pThis->strmCfg);
+#ifdef VBOX_WITH_HDA_MIC_IN
+    hdaCodecOpenStream(pThis, MC_INDEX, &pThis->strmCfg);
+#endif
+    hdaCodecOpenStream(pThis, PO_INDEX, &pThis->strmCfg);
+
+    /* Initialize the AFG node with the fixed setting. */
+    pThis->paNodes[1].node.au32F00_param[0xA] = CODEC_F00_0A_44_1KHZ | CODEC_F00_0A_16_BIT;
+
+    AssertPtr(pThis->paNodes);
+    AssertPtr(pThis->pfnCodecNodeReset);
+
+    for (uint8_t i = 0; i < pThis->cTotalNodes; i++)
+        pThis->pfnCodecNodeReset(pThis, i, &pThis->paNodes[i]);
+
+    hdaCodecToAudVolume(pThis, &pThis->paNodes[pThis->u8DacLineOut].dac.B_params,       PDMAUDIOMIXERCTL_PCM);
+    hdaCodecToAudVolume(pThis, &pThis->paNodes[pThis->u8AdcVolsLineIn].adcvol.B_params, PDMAUDIOMIXERCTL_LINE_IN);
+
+    return VINF_SUCCESS;
+}
+
Index: /trunk/src/VBox/Devices/Audio_old/DevIchHdaCodec.h
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DevIchHdaCodec.h	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DevIchHdaCodec.h	(revision 61413)
@@ -0,0 +1,155 @@
+/* $Id$ */
+/** @file
+ * DevIchHdaCodec - VBox ICH Intel HD Audio Codec.
+ */
+
+/*
+ * Copyright (C) 2006-2016 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.
+ */
+
+#ifndef DEV_CODEC_H
+#define DEV_CODEC_H
+
+/** The ICH HDA (Intel) controller. */
+typedef struct HDASTATE *PHDASTATE;
+/** The ICH HDA (Intel) codec state. */
+typedef struct HDACODEC *PHDACODEC;
+/** The HDA host driver backend. */
+typedef struct HDADRIVER *PHDADRIVER;
+typedef struct PDMIAUDIOCONNECTOR *PPDMIAUDIOCONNECTOR;
+typedef struct PDMAUDIOGSTSTRMOUT *PPDMAUDIOGSTSTRMOUT;
+typedef struct PDMAUDIOGSTSTRMIN  *PPDMAUDIOGSTSTRMIN;
+
+/**
+ * Verb processor method.
+ */
+typedef DECLCALLBACK(int) FNHDACODECVERBPROCESSOR(PHDACODEC pThis, uint32_t cmd, uint64_t *pResp);
+typedef FNHDACODECVERBPROCESSOR *PFNHDACODECVERBPROCESSOR;
+typedef FNHDACODECVERBPROCESSOR **PPFNHDACODECVERBPROCESSOR;
+
+/* PRM 5.3.1 */
+#define CODEC_RESPONSE_UNSOLICITED RT_BIT_64(34)
+
+
+#ifndef VBOX_WITH_HDA_CODEC_EMU
+typedef struct CODECVERB
+{
+    uint32_t verb;
+    /** operation bitness mask */
+    uint32_t mask;
+    PFNHDACODECVERBPROCESSOR pfn;
+} CODECVERB;
+#endif
+
+#ifndef VBOX_WITH_HDA_CODEC_EMU
+# define TYPE union
+#else
+# define TYPE struct
+typedef struct CODECEMU CODECEMU;
+typedef CODECEMU *PCODECEMU;
+#endif
+TYPE CODECNODE;
+typedef TYPE CODECNODE CODECNODE;
+typedef TYPE CODECNODE *PCODECNODE;
+
+typedef enum
+{
+    PI_INDEX = 0,    /**< PCM in */
+    PO_INDEX,        /**< PCM out */
+    MC_INDEX,        /**< Mic in */
+    LAST_INDEX
+} ENMSOUNDSOURCE;
+
+typedef struct HDACODEC
+{
+    uint16_t                id;
+    uint16_t                u16VendorId;
+    uint16_t                u16DeviceId;
+    uint8_t                 u8BSKU;
+    uint8_t                 u8AssemblyId;
+    /** List of assigned HDA drivers to this codec.
+     * A driver only can be assigned to one codec at a time. */
+    RTLISTANCHOR            lstDrv;
+    /** The codec's current audio stream configuration. */
+    PDMAUDIOSTREAMCFG       strmCfg;
+
+#ifndef VBOX_WITH_HDA_CODEC_EMU
+    CODECVERB const        *paVerbs;
+    int                     cVerbs;
+#else
+    PCODECEMU               pCodecBackend;
+#endif
+    PCODECNODE              paNodes;
+    /** Pointer to HDA state (controller) this
+     *  codec is assigned to. */
+    PHDASTATE               pHDAState;
+    bool                    fInReset;
+#ifndef VBOX_WITH_HDA_CODEC_EMU
+    const uint8_t           cTotalNodes;
+    const uint8_t          *au8Ports;
+    const uint8_t          *au8Dacs;
+    const uint8_t          *au8AdcVols;
+    const uint8_t          *au8Adcs;
+    const uint8_t          *au8AdcMuxs;
+    const uint8_t          *au8Pcbeeps;
+    const uint8_t          *au8SpdifIns;
+    const uint8_t          *au8SpdifOuts;
+    const uint8_t          *au8DigInPins;
+    const uint8_t          *au8DigOutPins;
+    const uint8_t          *au8Cds;
+    const uint8_t          *au8VolKnobs;
+    const uint8_t          *au8Reserveds;
+    const uint8_t           u8AdcVolsLineIn;
+    const uint8_t           u8DacLineOut;
+#endif
+    /** Callbacks to the HDA controller, mostly used for multiplexing to the various host backends. */
+    DECLR3CALLBACKMEMBER(void, pfnCloseIn, (PHDASTATE pThis, PDMAUDIORECSOURCE enmRecSource));
+    DECLR3CALLBACKMEMBER(void, pfnCloseOut, (PHDASTATE pThis));
+    DECLR3CALLBACKMEMBER(int, pfnOpenIn, (PHDASTATE pThis, const char *pszName, PDMAUDIORECSOURCE enmRecSource, PPDMAUDIOSTREAMCFG pCfg));
+    DECLR3CALLBACKMEMBER(int, pfnOpenOut, (PHDASTATE pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg));
+    DECLR3CALLBACKMEMBER(int, pfnSetVolume, (PHDASTATE pThis, ENMSOUNDSOURCE enmSource, bool fMute, uint8_t uVolLeft, uint8_t uVolRight));
+    /** Callbacks by codec implementation. */
+    DECLR3CALLBACKMEMBER(int, pfnLookup, (PHDACODEC pThis, uint32_t verb, PPFNHDACODECVERBPROCESSOR));
+    DECLR3CALLBACKMEMBER(int, pfnReset, (PHDACODEC pThis));
+    DECLR3CALLBACKMEMBER(int, pfnCodecNodeReset, (PHDACODEC pThis, uint8_t, PCODECNODE));
+    /** These callbacks are set by codec implementation to answer debugger requests. */
+    DECLR3CALLBACKMEMBER(void, pfnDbgListNodes, (PHDACODEC pThis, PCDBGFINFOHLP pHlp, const char *pszArgs));
+    DECLR3CALLBACKMEMBER(void, pfnDbgSelector, (PHDACODEC pThis, PCDBGFINFOHLP pHlp, const char *pszArgs));
+} HDACODEC;
+
+int hdaCodecConstruct(PPDMDEVINS pDevIns, PHDACODEC pThis, uint16_t uLUN, PCFGMNODE pCfg);
+int hdaCodecDestruct(PHDACODEC pThis);
+int hdaCodecSaveState(PHDACODEC pThis, PSSMHANDLE pSSM);
+int hdaCodecLoadState(PHDACODEC pThis, PSSMHANDLE pSSM, uint32_t uVersion);
+int hdaCodecOpenStream(PHDACODEC pThis, ENMSOUNDSOURCE enmSoundSource, PPDMAUDIOSTREAMCFG pCfg);
+
+#define HDA_SSM_VERSION   6
+/** Introduced dynamic number of streams + stream identifiers for serialization.
+ *  Bug: Did not save the BDLE states correctly.
+ *  Those will be skipped on load then. */
+#define HDA_SSM_VERSION_5 5
+/** Since this version the number of MMIO registers can be flexible. */
+#define HDA_SSM_VERSION_4 4
+#define HDA_SSM_VERSION_3 3
+#define HDA_SSM_VERSION_2 2
+#define HDA_SSM_VERSION_1 1
+
+# ifdef VBOX_WITH_HDA_CODEC_EMU
+/* */
+struct CODECEMU
+{
+    DECLR3CALLBACKMEMBER(int, pfnCodecEmuConstruct,(PHDACODEC pThis));
+    DECLR3CALLBACKMEMBER(int, pfnCodecEmuDestruct,(PHDACODEC pThis));
+    DECLR3CALLBACKMEMBER(int, pfnCodecEmuReset,(PHDACODEC pThis, bool fInit));
+};
+# endif
+#endif
+
Index: /trunk/src/VBox/Devices/Audio_old/DevSB16.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DevSB16.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DevSB16.cpp	(revision 61413)
@@ -0,0 +1,2438 @@
+/* $Id$ */
+/** @file
+ * DevSB16 - VBox SB16 Audio Controller.
+ *
+ * @todo hiccups on NT4 and Win98.
+ */
+
+/*
+ * Copyright (C) 2015-2016 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: sb16.c from QEMU AUDIO subsystem (r3917).
+ * QEMU Soundblaster 16 emulation
+ *
+ * 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.
+ */
+#define LOG_GROUP LOG_GROUP_DEV_SB16
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#ifdef IN_RING3
+# include <iprt/mem.h>
+# include <iprt/string.h>
+# include <iprt/uuid.h>
+#endif
+
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdmaudioifs.h>
+
+#include "VBoxDD.h"
+
+#include "AudioMixBuffer.h"
+#include "AudioMixer.h"
+#include "DrvAudio.h"
+
+/** Current saved state version. */
+#define SB16_SAVE_STATE_VERSION         2
+/** The version used in VirtualBox version 3.0 and earlier. This didn't include the config dump. */
+#define SB16_SAVE_STATE_VERSION_VBOX_30 1
+
+#define IO_READ_PROTO(name)                                             \
+    DECLCALLBACK(int) name (PPDMDEVINS pDevIns, void *opaque,       \
+                            RTIOPORT nport, uint32_t *pu32, unsigned cb)
+
+#define IO_WRITE_PROTO(name)                                            \
+    DECLCALLBACK(int) name (PPDMDEVINS pDevIns, void *opaque,       \
+                            RTIOPORT nport, uint32_t val, unsigned cb)
+
+static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
+
+typedef struct SB16OUTPUTSTREAM
+{
+    /** PCM output stream. */
+    R3PTRTYPE(PPDMAUDIOGSTSTRMOUT)     pStrmOut;
+    /** Mixer handle for output stream. */
+    R3PTRTYPE(PAUDMIXSTREAM)           phStrmOut;
+} SB16OUTPUTSTREAM, *PSB16OUTPUTSTREAM;
+
+/**
+ * Struct for maintaining a host backend driver.
+ */
+typedef struct SB16STATE *PSB16STATE;
+typedef struct SB16DRIVER
+{
+    /** Node for storing this driver in our device driver list of SB16STATE. */
+    RTLISTNODER3                       Node;
+    /** Pointer to SB16 controller (state). */
+    R3PTRTYPE(PSB16STATE)              pSB16State;
+    /** Driver flags. */
+    PDMAUDIODRVFLAGS                   Flags;
+    uint32_t                           PaddingFlags;
+    /** LUN # to which this driver has been assigned. */
+    uint8_t                            uLUN;
+    /** Whether this driver is in an attached state or not. */
+    bool                               fAttached;
+    uint8_t                            Padding[4];
+    /** Pointer to attached driver base interface. */
+    R3PTRTYPE(PPDMIBASE)               pDrvBase;
+    /** Audio connector interface to the underlying host backend. */
+    R3PTRTYPE(PPDMIAUDIOCONNECTOR)     pConnector;
+    /** Stream for output. */
+    SB16OUTPUTSTREAM                   Out;
+} SB16DRIVER, *PSB16DRIVER;
+
+typedef struct SB16STATE
+{
+#ifdef VBOX
+    /** Pointer to the device instance. */
+    PPDMDEVINSR3        pDevInsR3;
+    /** Pointer to the connector of the attached audio driver. */
+    PPDMIAUDIOCONNECTOR pDrv;
+    int irqCfg;
+    int dmaCfg;
+    int hdmaCfg;
+    int portCfg;
+    int verCfg;
+#endif
+    int irq;
+    int dma;
+    int hdma;
+    int port;
+    int ver;
+
+    int in_index;
+    int out_data_len;
+    int fmt_stereo;
+    int fmt_signed;
+    int fmt_bits;
+    PDMAUDIOFMT fmt;
+    int dma_auto;
+    int block_size;
+    int fifo;
+    int freq;
+    int time_const;
+    int speaker;
+    int needed_bytes;
+    int cmd;
+    int use_hdma;
+    int highspeed;
+    int can_write; /** @todo Value never gets 0? */
+
+    int v2x6;
+
+    uint8_t csp_param;
+    uint8_t csp_value;
+    uint8_t csp_mode;
+    uint8_t csp_regs[256];
+    uint8_t csp_index;
+    uint8_t csp_reg83[4];
+    int csp_reg83r;
+    int csp_reg83w;
+
+    uint8_t in2_data[10];
+    uint8_t out_data[50];
+    uint8_t test_reg;
+    uint8_t last_read_byte;
+    int nzero;
+
+    int left_till_irq; /** Note: Can be < 0. */
+
+    int dma_running;
+    int bytes_per_second;
+    int align;
+
+    RTLISTANCHOR                   lstDrv;
+    /** The device' software mixer. */
+    R3PTRTYPE(PAUDIOMIXER)         pMixer;
+    /** Audio sink for PCM output. */
+    R3PTRTYPE(PAUDMIXSINK)         pSinkOutput;
+#ifndef VBOX_WITH_AUDIO_CALLBACKS
+    /** The timer for pumping data thru the attached LUN drivers. */
+    PTMTIMERR3                     pTimerIO;
+    /** The timer interval for pumping data thru the LUN drivers in timer ticks. */
+    uint64_t                       cTimerTicksIO;
+    /** Timestamp of the last timer callback (sb16TimerIO).
+     * Used to calculate the time actually elapsed between two timer callbacks. */
+    uint64_t                       uTimerTSIO;
+#endif
+    PTMTIMER                       pTimerIRQ;
+    /** The base interface for LUN\#0. */
+    PDMIBASE                       IBase;
+
+    /* mixer state */
+    int mixer_nreg;
+    uint8_t mixer_regs[256];
+} SB16STATE, *PSB16STATE;
+
+static int sb16OpenOut(PSB16STATE pThis, PPDMAUDIOSTREAMCFG pCfg);
+
+/**
+ * Attach command, internal version.
+ *
+ * This is called to let the device attach to a driver for a specified LUN
+ * during runtime. This is not called during VM construction, the device
+ * constructor has to attach to all the available drivers.
+ *
+ * @returns VBox status code.
+ * @param   pDevIns     The device instance.
+ * @param   pDrv        Driver to (re-)use for (re-)attaching to.
+ *                      If NULL is specified, a new driver will be created and appended
+ *                      to the driver list.
+ * @param   uLUN        The logical unit which is being detached.
+ * @param   fFlags      Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ */
+static DECLCALLBACK(int) sb16AttachInternal(PPDMDEVINS pDevIns, PSB16DRIVER pDrv, unsigned uLUN, uint32_t fFlags)
+{
+    PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
+
+    /*
+     * Attach driver.
+     */
+    char *pszDesc = NULL;
+    if (RTStrAPrintf(&pszDesc, "Audio driver port (SB16) for LUN #%u", uLUN) <= 0)
+        AssertReleaseMsgReturn(pszDesc,
+                               ("Not enough memory for SB16 driver port description of LUN #%u\n", uLUN),
+                               VERR_NO_MEMORY);
+
+    PPDMIBASE pDrvBase;
+    int rc = PDMDevHlpDriverAttach(pDevIns, uLUN,
+                                   &pThis->IBase, &pDrvBase, pszDesc);
+    if (RT_SUCCESS(rc))
+    {
+        if (pDrv == NULL)
+            pDrv = (PSB16DRIVER)RTMemAllocZ(sizeof(SB16DRIVER));
+        if (pDrv)
+        {
+            pDrv->pDrvBase   = pDrvBase;
+            pDrv->pConnector = PDMIBASE_QUERY_INTERFACE(pDrvBase, PDMIAUDIOCONNECTOR);
+            AssertMsg(pDrv->pConnector != NULL, ("Configuration error: LUN #%u has no host audio interface, rc=%Rrc\n", uLUN, rc));
+            pDrv->pSB16State = pThis;
+            pDrv->uLUN       = uLUN;
+
+            /*
+             * For now we always set the driver at LUN 0 as our primary
+             * host backend. This might change in the future.
+             */
+            if (pDrv->uLUN == 0)
+                pDrv->Flags |= PDMAUDIODRVFLAG_PRIMARY;
+
+            LogFunc(("LUN#%RU8: pCon=%p, drvFlags=0x%x\n", uLUN, pDrv->pConnector, pDrv->Flags));
+
+            /* Attach to driver list if not attached yet. */
+            if (!pDrv->fAttached)
+            {
+                RTListAppend(&pThis->lstDrv, &pDrv->Node);
+                pDrv->fAttached = true;
+            }
+        }
+        else
+            rc = VERR_NO_MEMORY;
+    }
+    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+    {
+        LogFunc(("No attached driver for LUN #%u\n", uLUN));
+    }
+    else if (RT_FAILURE(rc))
+        AssertMsgFailed(("Failed to attach SB16 LUN #%u (\"%s\"), rc=%Rrc\n",
+                        uLUN, pszDesc, rc));
+
+    if (RT_FAILURE(rc))
+    {
+        /* Only free this string on failure;
+         * must remain valid for the live of the driver instance. */
+        RTStrFree(pszDesc);
+    }
+
+    LogFunc(("iLUN=%u, fFlags=0x%x, rc=%Rrc\n", uLUN, fFlags, rc));
+    return rc;
+}
+
+/**
+ * Attach command.
+ *
+ * This is called to let the device attach to a driver for a specified LUN
+ * during runtime. This is not called during VM construction, the device
+ * constructor has to attach to all the available drivers.
+ *
+ * @returns VBox status code.
+ * @param   pDevIns     The device instance.
+ * @param   uLUN        The logical unit which is being detached.
+ * @param   fFlags      Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ */
+static DECLCALLBACK(int) sb16Attach(PPDMDEVINS pDevIns, unsigned uLUN, uint32_t fFlags)
+{
+    return sb16AttachInternal(pDevIns, NULL /* pDrv */, uLUN, fFlags);
+}
+
+static DECLCALLBACK(void) sb16Detach(PPDMDEVINS pDevIns, unsigned uLUN, uint32_t fFlags)
+{
+    LogFunc(("iLUN=%u, fFlags=0x%x\n", uLUN, fFlags));
+}
+
+/**
+ * Re-attach.
+ *
+ * @returns VBox status code.
+ * @param   pThis       Device instance.
+ * @param   pDrv        Driver instance used for attaching to.
+ *                      If NULL is specified, a new driver will be created and appended
+ *                      to the driver list.
+ * @param   uLUN        The logical unit which is being re-detached.
+ * @param   pszDriver   Driver name.
+ */
+static int sb16Reattach(PSB16STATE pThis, PSB16DRIVER pDrv, uint8_t uLUN, const char *pszDriver)
+{
+    AssertPtrReturn(pThis,     VERR_INVALID_POINTER);
+    AssertPtrReturn(pszDriver, VERR_INVALID_POINTER);
+
+    PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
+    PCFGMNODE pRoot = CFGMR3GetRoot(pVM);
+    PCFGMNODE pDev0 = CFGMR3GetChild(pRoot, "Devices/sb16/0/");
+
+    /* Remove LUN branch. */
+    CFGMR3RemoveNode(CFGMR3GetChildF(pDev0, "LUN#%u/", uLUN));
+
+    if (pDrv)
+    {
+        /* Re-use the driver instance so detach it before. */
+        int rc = PDMDevHlpDriverDetach(pThis->pDevInsR3, PDMIBASE_2_PDMDRV(pDrv->pDrvBase), 0 /* fFlags */);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+#define RC_CHECK() if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; }
+
+    int rc = VINF_SUCCESS;
+    do
+    {
+        PCFGMNODE pLunL0;
+        rc = CFGMR3InsertNodeF(pDev0, &pLunL0, "LUN#%u/", uLUN);        RC_CHECK();
+        rc = CFGMR3InsertString(pLunL0, "Driver",       "AUDIO");       RC_CHECK();
+        rc = CFGMR3InsertNode(pLunL0,   "Config/",       NULL);         RC_CHECK();
+
+        PCFGMNODE pLunL1, pLunL2;
+        rc = CFGMR3InsertNode  (pLunL0, "AttachedDriver/", &pLunL1);    RC_CHECK();
+        rc = CFGMR3InsertNode  (pLunL1,  "Config/",        &pLunL2);    RC_CHECK();
+        rc = CFGMR3InsertString(pLunL1,  "Driver",          pszDriver); RC_CHECK();
+
+        rc = CFGMR3InsertString(pLunL2, "AudioDriver", pszDriver);      RC_CHECK();
+
+    } while (0);
+
+    if (RT_SUCCESS(rc))
+        rc = sb16AttachInternal(pThis->pDevInsR3, pDrv, uLUN, 0 /* fFlags */);
+
+    LogFunc(("pThis=%p, uLUN=%u, pszDriver=%s, rc=%Rrc\n", pThis, uLUN, pszDriver, rc));
+
+#undef RC_CHECK
+
+    return rc;
+}
+
+static void sb16AudioCallback(void *pvContext, uint32_t cbFree);
+
+static int magic_of_irq(int irq)
+{
+    switch (irq)
+    {
+        case 5:
+            return 2;
+        case 7:
+            return 4;
+        case 9:
+            return 1;
+        case 10:
+            return 8;
+        default:
+            break;
+    }
+
+    LogFlowFunc(("bad irq %d\n", irq));
+    return 2;
+}
+
+static int irq_of_magic(int magic)
+{
+    switch (magic)
+    {
+        case 1:
+            return 9;
+        case 2:
+            return 5;
+        case 4:
+            return 7;
+        case 8:
+            return 10;
+        default:
+            break;
+    }
+
+    LogFlowFunc(("bad irq magic %d\n", magic));
+    return -1;
+}
+
+#ifdef DEBUG
+static inline void log_dsp(PSB16STATE pThis)
+{
+    LogFlowFunc(("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n",
+                 pThis->fmt_stereo ? "Stereo" : "Mono",
+                 pThis->fmt_signed ? "Signed" : "Unsigned",
+                 pThis->fmt_bits,
+                 pThis->dma_auto ? "Auto" : "Single",
+                 pThis->block_size,
+                 pThis->freq,
+                 pThis->time_const,
+                 pThis->speaker));
+}
+#endif
+
+static void sb16SpeakerControl(PSB16STATE pThis, int on)
+{
+    pThis->speaker = on;
+    /* AUD_enable (pThis->voice, on); */
+}
+
+static void sb16Control(PSB16STATE pThis, int hold)
+{
+    int dma = pThis->use_hdma ? pThis->hdma : pThis->dma;
+    pThis->dma_running = hold;
+
+    LogFlowFunc(("hold %d high %d dma %d\n", hold, pThis->use_hdma, dma));
+
+    PSB16DRIVER pDrv;
+    if (hold)
+    {
+        PDMDevHlpDMASetDREQ (pThis->pDevInsR3, dma, 1);
+        PDMDevHlpDMASchedule (pThis->pDevInsR3);
+        RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
+            pDrv->pConnector->pfnEnableOut(pDrv->pConnector,
+                                           pDrv->Out.pStrmOut, true /* fEnable */);
+    }
+    else
+    {
+        PDMDevHlpDMASetDREQ (pThis->pDevInsR3, dma, 0);
+        RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
+            pDrv->pConnector->pfnEnableOut(pDrv->pConnector,
+                                           pDrv->Out.pStrmOut, false /* fEnable */);
+    }
+}
+
+static DECLCALLBACK(void) sb16TimerIRQ(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvThis)
+{
+    PSB16STATE pThis = (PSB16STATE)pvThis;
+    pThis->can_write = 1;
+    PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
+}
+
+#define DMA8_AUTO 1
+#define DMA8_HIGH 2
+
+static void continue_dma8(PSB16STATE pThis)
+{
+    if (pThis->freq > 0)
+    {
+        PDMAUDIOSTREAMCFG streamCfg;
+        streamCfg.uHz           = pThis->freq;
+        streamCfg.cChannels     = 1 << pThis->fmt_stereo;
+        streamCfg.enmFormat     = pThis->fmt;
+        streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
+
+        int rc = sb16OpenOut(pThis, &streamCfg);
+        AssertRC(rc);
+    }
+
+    sb16Control(pThis, 1);
+}
+
+static void dma_cmd8(PSB16STATE pThis, int mask, int dma_len)
+{
+    pThis->fmt        = AUD_FMT_U8;
+    pThis->use_hdma   = 0;
+    pThis->fmt_bits   = 8;
+    pThis->fmt_signed = 0;
+    pThis->fmt_stereo = (pThis->mixer_regs[0x0e] & 2) != 0;
+
+    if (-1 == pThis->time_const)
+    {
+        if (pThis->freq <= 0)
+            pThis->freq = 11025;
+    }
+    else
+    {
+        int tmp = (256 - pThis->time_const);
+        pThis->freq = (1000000 + (tmp / 2)) / tmp;
+    }
+
+    if (dma_len != -1)
+    {
+        pThis->block_size = dma_len << pThis->fmt_stereo;
+    }
+    else
+    {
+        /* This is apparently the only way to make both Act1/PL
+           and SecondReality/FC work
+
+           r=andy Wow, actually someone who remembers Future Crew :-)
+
+           Act1 sets block size via command 0x48 and it's an odd number
+           SR does the same with even number
+           Both use stereo, and Creatives own documentation states that
+           0x48 sets block size in bytes less one.. go figure */
+        pThis->block_size &= ~pThis->fmt_stereo;
+    }
+
+    pThis->freq >>= pThis->fmt_stereo;
+    pThis->left_till_irq = pThis->block_size;
+    pThis->bytes_per_second = (pThis->freq << pThis->fmt_stereo);
+    /* pThis->highspeed = (mask & DMA8_HIGH) != 0; */
+    pThis->dma_auto = (mask & DMA8_AUTO) != 0;
+    pThis->align = (1 << pThis->fmt_stereo) - 1;
+
+    if (pThis->block_size & pThis->align)
+        LogFlowFunc(("warning: misaligned block size %d, alignment %d\n",
+                     pThis->block_size, pThis->align + 1));
+
+    LogFlowFunc(("freq %d, stereo %d, sign %d, bits %d, dma %d, auto %d, fifo %d, high %d\n",
+                 pThis->freq, pThis->fmt_stereo, pThis->fmt_signed, pThis->fmt_bits,
+                 pThis->block_size, pThis->dma_auto, pThis->fifo, pThis->highspeed));
+
+    continue_dma8(pThis);
+    sb16SpeakerControl(pThis, 1);
+}
+
+static void dma_cmd(PSB16STATE pThis, uint8_t cmd, uint8_t d0, int dma_len)
+{
+    pThis->use_hdma   = cmd < 0xc0;
+    pThis->fifo       = (cmd >> 1) & 1;
+    pThis->dma_auto   = (cmd >> 2) & 1;
+    pThis->fmt_signed = (d0 >> 4) & 1;
+    pThis->fmt_stereo = (d0 >> 5) & 1;
+
+    switch (cmd >> 4)
+    {
+        case 11:
+            pThis->fmt_bits = 16;
+            break;
+
+        case 12:
+            pThis->fmt_bits = 8;
+            break;
+    }
+
+    if (-1 != pThis->time_const)
+    {
+#if 1
+        int tmp = 256 - pThis->time_const;
+        pThis->freq = (1000000 + (tmp / 2)) / tmp;
+#else
+        /* pThis->freq = 1000000 / ((255 - pThis->time_const) << pThis->fmt_stereo); */
+        pThis->freq = 1000000 / ((255 - pThis->time_const));
+#endif
+        pThis->time_const = -1;
+    }
+
+    pThis->block_size = dma_len + 1;
+    pThis->block_size <<= ((pThis->fmt_bits == 16) ? 1 : 0);
+    if (!pThis->dma_auto)
+    {
+        /*
+         * It is clear that for DOOM and auto-init this value
+         * shouldn't take stereo into account, while Miles Sound Systems
+         * setsound.exe with single transfer mode wouldn't work without it
+         * wonders of SB16 yet again.
+         */
+        pThis->block_size <<= pThis->fmt_stereo;
+    }
+
+    LogFlowFunc(("freq %d, stereo %d, sign %d, bits %d, dma %d, auto %d, fifo %d, high %d\n",
+                 pThis->freq, pThis->fmt_stereo, pThis->fmt_signed, pThis->fmt_bits,
+                 pThis->block_size, pThis->dma_auto, pThis->fifo, pThis->highspeed));
+
+    if (16 == pThis->fmt_bits)
+        pThis->fmt = pThis->fmt_signed ? AUD_FMT_S16 : AUD_FMT_U16;
+    else
+        pThis->fmt = pThis->fmt_signed ? AUD_FMT_S8 : AUD_FMT_U8;
+
+    pThis->left_till_irq = pThis->block_size;
+
+    pThis->bytes_per_second = (pThis->freq << pThis->fmt_stereo) << ((pThis->fmt_bits == 16) ? 1 : 0);
+    pThis->highspeed = 0;
+    pThis->align = (1 << (pThis->fmt_stereo + (pThis->fmt_bits == 16))) - 1;
+    if (pThis->block_size & pThis->align)
+    {
+        LogFlowFunc(("warning: misaligned block size %d, alignment %d\n",
+                     pThis->block_size, pThis->align + 1));
+    }
+
+    if (pThis->freq)
+    {
+        PDMAUDIOSTREAMCFG streamCfg;
+        streamCfg.uHz           = pThis->freq;
+        streamCfg.cChannels     = 1 << pThis->fmt_stereo;
+        streamCfg.enmFormat     = pThis->fmt;
+        streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
+
+        int rc = sb16OpenOut(pThis, &streamCfg);
+        AssertRC(rc);
+    }
+
+    sb16Control(pThis, 1);
+    sb16SpeakerControl(pThis, 1);
+}
+
+static inline void dsp_out_data (PSB16STATE pThis, uint8_t val)
+{
+    LogFlowFunc(("outdata %#x\n", val));
+    if ((size_t) pThis->out_data_len < sizeof (pThis->out_data)) {
+        pThis->out_data[pThis->out_data_len++] = val;
+    }
+}
+
+static inline uint8_t dsp_get_data (PSB16STATE pThis)
+{
+    if (pThis->in_index) {
+        return pThis->in2_data[--pThis->in_index];
+    }
+    else {
+        LogFlowFunc(("buffer underflow\n"));
+        return 0;
+    }
+}
+
+static void sb16HandleCommand(PSB16STATE pThis, uint8_t cmd)
+{
+    LogFlowFunc(("command %#x\n", cmd));
+
+    if (cmd > 0xaf && cmd < 0xd0)
+    {
+        if (cmd & 8) /** @todo Handle recording. */
+            LogFlowFunc(("ADC not yet supported (command %#x)\n", cmd));
+
+        switch (cmd >> 4)
+        {
+            case 11:
+            case 12:
+                break;
+            default:
+                LogFlowFunc(("%#x wrong bits\n", cmd));
+        }
+
+        pThis->needed_bytes = 3;
+    }
+    else
+    {
+        pThis->needed_bytes = 0;
+
+        switch (cmd)
+        {
+            case 0x03:
+                dsp_out_data(pThis, 0x10); /* pThis->csp_param); */
+                goto warn;
+
+            case 0x04:
+                pThis->needed_bytes = 1;
+                goto warn;
+
+            case 0x05:
+                pThis->needed_bytes = 2;
+                goto warn;
+
+            case 0x08:
+                /* __asm__ ("int3"); */
+                goto warn;
+
+            case 0x0e:
+                pThis->needed_bytes = 2;
+                goto warn;
+
+            case 0x09:
+                dsp_out_data(pThis, 0xf8);
+                goto warn;
+
+            case 0x0f:
+                pThis->needed_bytes = 1;
+                goto warn;
+
+            case 0x10:
+                pThis->needed_bytes = 1;
+                goto warn;
+
+            case 0x14:
+                pThis->needed_bytes = 2;
+                pThis->block_size = 0;
+                break;
+
+            case 0x1c:              /* Auto-Initialize DMA DAC, 8-bit */
+                dma_cmd8(pThis, DMA8_AUTO, -1);
+                break;
+
+            case 0x20:              /* Direct ADC, Juice/PL */
+                dsp_out_data(pThis, 0xff);
+                goto warn;
+
+            case 0x35:
+                LogFlowFunc(("0x35 - MIDI command not implemented\n"));
+                break;
+
+            case 0x40:
+                pThis->freq = -1;
+                pThis->time_const = -1;
+                pThis->needed_bytes = 1;
+                break;
+
+            case 0x41:
+                pThis->freq = -1;
+                pThis->time_const = -1;
+                pThis->needed_bytes = 2;
+                break;
+
+            case 0x42:
+                pThis->freq = -1;
+                pThis->time_const = -1;
+                pThis->needed_bytes = 2;
+                goto warn;
+
+            case 0x45:
+                dsp_out_data(pThis, 0xaa);
+                goto warn;
+
+            case 0x47:                /* Continue Auto-Initialize DMA 16bit */
+                break;
+
+            case 0x48:
+                pThis->needed_bytes = 2;
+                break;
+
+            case 0x74:
+                pThis->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
+                LogFlowFunc(("0x75 - DMA DAC, 4-bit ADPCM not implemented\n"));
+                break;
+
+            case 0x75:              /* DMA DAC, 4-bit ADPCM Reference */
+                pThis->needed_bytes = 2;
+                LogFlowFunc(("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n"));
+                break;
+
+            case 0x76:              /* DMA DAC, 2.6-bit ADPCM */
+                pThis->needed_bytes = 2;
+                LogFlowFunc(("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n"));
+                break;
+
+            case 0x77:              /* DMA DAC, 2.6-bit ADPCM Reference */
+                pThis->needed_bytes = 2;
+                LogFlowFunc(("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n"));
+                break;
+
+            case 0x7d:
+                LogFlowFunc(("0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n"));
+                LogFlowFunc(("not implemented\n"));
+                break;
+
+            case 0x7f:
+                LogFlowFunc(("0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n"));
+                LogFlowFunc(("not implemented\n"));
+                break;
+
+            case 0x80:
+                pThis->needed_bytes = 2;
+                break;
+
+            case 0x90:
+            case 0x91:
+                dma_cmd8(pThis, (((cmd & 1) == 0) ? 1 : 0) | DMA8_HIGH, -1);
+                break;
+
+            case 0xd0:              /* halt DMA operation. 8bit */
+                sb16Control(pThis, 0);
+                break;
+
+            case 0xd1:              /* speaker on */
+                sb16SpeakerControl(pThis, 1);
+                break;
+
+            case 0xd3:              /* speaker off */
+                sb16SpeakerControl(pThis, 0);
+                break;
+
+            case 0xd4:              /* continue DMA operation. 8bit */
+                /* KQ6 (or maybe Sierras audblst.drv in general) resets
+                   the frequency between halt/continue */
+                continue_dma8(pThis);
+                break;
+
+            case 0xd5:              /* halt DMA operation. 16bit */
+                sb16Control(pThis, 0);
+                break;
+
+            case 0xd6:              /* continue DMA operation. 16bit */
+                sb16Control(pThis, 1);
+                break;
+
+            case 0xd9:              /* exit auto-init DMA after this block. 16bit */
+                pThis->dma_auto = 0;
+                break;
+
+            case 0xda:              /* exit auto-init DMA after this block. 8bit */
+                pThis->dma_auto = 0;
+                break;
+
+            case 0xe0:              /* DSP identification */
+                pThis->needed_bytes = 1;
+                break;
+
+            case 0xe1:
+                dsp_out_data(pThis, pThis->ver & 0xff);
+                dsp_out_data(pThis, pThis->ver >> 8);
+                break;
+
+            case 0xe2:
+                pThis->needed_bytes = 1;
+                goto warn;
+
+            case 0xe3:
+            {
+                for (int i = sizeof (e3) - 1; i >= 0; --i)
+                    dsp_out_data(pThis, e3[i]);
+
+                break;
+            }
+
+            case 0xe4:              /* write test reg */
+                pThis->needed_bytes = 1;
+                break;
+
+            case 0xe7:
+                LogFlowFunc(("Attempt to probe for ESS (0xe7)?\n"));
+                break;
+
+            case 0xe8:              /* read test reg */
+                dsp_out_data(pThis, pThis->test_reg);
+                break;
+
+            case 0xf2:
+            case 0xf3:
+                dsp_out_data(pThis, 0xaa);
+                pThis->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2;
+                PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
+                break;
+
+            case 0xf8:
+                /* Undocumented, used by old Creative diagnostic programs. */
+                dsp_out_data (pThis, 0);
+                goto warn;
+
+            case 0xf9:
+                pThis->needed_bytes = 1;
+                goto warn;
+
+            case 0xfa:
+                dsp_out_data (pThis, 0);
+                goto warn;
+
+            case 0xfc:              /* FIXME */
+                dsp_out_data (pThis, 0);
+                goto warn;
+
+            default:
+                LogFlowFunc(("Unrecognized command %#x\n", cmd));
+                break;
+        }
+    }
+
+    if (!pThis->needed_bytes)
+        LogFlow(("\n"));
+
+exit:
+
+     if (!pThis->needed_bytes)
+        pThis->cmd = -1;
+     else
+        pThis->cmd = cmd;
+
+    return;
+
+warn:
+    LogFlowFunc(("warning: command %#x,%d is not truly understood yet\n",
+                 cmd, pThis->needed_bytes));
+    goto exit;
+}
+
+static uint16_t dsp_get_lohi (PSB16STATE pThis)
+{
+    uint8_t hi = dsp_get_data (pThis);
+    uint8_t lo = dsp_get_data (pThis);
+    return (hi << 8) | lo;
+}
+
+static uint16_t dsp_get_hilo (PSB16STATE pThis)
+{
+    uint8_t lo = dsp_get_data (pThis);
+    uint8_t hi = dsp_get_data (pThis);
+    return (hi << 8) | lo;
+}
+
+static void complete(PSB16STATE pThis)
+{
+    int d0, d1, d2;
+    LogFlowFunc(("complete command %#x, in_index %d, needed_bytes %d\n",
+            pThis->cmd, pThis->in_index, pThis->needed_bytes));
+
+    if (pThis->cmd > 0xaf && pThis->cmd < 0xd0)
+    {
+        d2 = dsp_get_data (pThis);
+        d1 = dsp_get_data (pThis);
+        d0 = dsp_get_data (pThis);
+
+        if (pThis->cmd & 8)
+            LogFlowFunc(("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, d0, d1, d2));
+        else
+        {
+            LogFlowFunc(("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, d0, d1, d2));
+            dma_cmd(pThis, pThis->cmd, d0, d1 + (d2 << 8));
+        }
+    }
+    else
+    {
+        switch (pThis->cmd)
+        {
+        case 0x04:
+            pThis->csp_mode = dsp_get_data (pThis);
+            pThis->csp_reg83r = 0;
+            pThis->csp_reg83w = 0;
+            LogFlowFunc(("CSP command 0x04: mode=%#x\n", pThis->csp_mode));
+            break;
+
+        case 0x05:
+            pThis->csp_param = dsp_get_data (pThis);
+            pThis->csp_value = dsp_get_data (pThis);
+            LogFlowFunc(("CSP command 0x05: param=%#x value=%#x\n",
+                    pThis->csp_param,
+                    pThis->csp_value));
+            break;
+
+        case 0x0e:
+        {
+            d0 = dsp_get_data(pThis);
+            d1 = dsp_get_data(pThis);
+            LogFlowFunc(("write CSP register %d <- %#x\n", d1, d0));
+            if (d1 == 0x83)
+            {
+                LogFlowFunc(("0x83[%d] <- %#x\n", pThis->csp_reg83r, d0));
+                pThis->csp_reg83[pThis->csp_reg83r % 4] = d0;
+                pThis->csp_reg83r += 1;
+            }
+            else
+                pThis->csp_regs[d1] = d0;
+            break;
+        }
+
+        case 0x0f:
+            d0 = dsp_get_data(pThis);
+            LogFlowFunc(("read CSP register %#x -> %#x, mode=%#x\n", d0, pThis->csp_regs[d0], pThis->csp_mode));
+            if (d0 == 0x83)
+            {
+                LogFlowFunc(("0x83[%d] -> %#x\n",
+                        pThis->csp_reg83w,
+                        pThis->csp_reg83[pThis->csp_reg83w % 4]));
+                dsp_out_data (pThis, pThis->csp_reg83[pThis->csp_reg83w % 4]);
+                pThis->csp_reg83w += 1;
+            }
+            else
+                dsp_out_data(pThis, pThis->csp_regs[d0]);
+            break;
+
+        case 0x10:
+            d0 = dsp_get_data(pThis);
+            LogFlowFunc(("cmd 0x10 d0=%#x\n", d0));
+            break;
+
+        case 0x14:
+            dma_cmd8(pThis, 0, dsp_get_lohi (pThis) + 1);
+            break;
+
+        case 0x40:
+            pThis->time_const = dsp_get_data(pThis);
+            LogFlowFunc(("set time const %d\n", pThis->time_const));
+            break;
+
+        case 0x42:              /* FT2 sets output freq with this, go figure */
+#if 0
+            LogFlowFunc(("cmd 0x42 might not do what it think it should\n"));
+#endif
+        case 0x41:
+            pThis->freq = dsp_get_hilo(pThis);
+            LogFlowFunc(("set freq %d\n", pThis->freq));
+            break;
+
+        case 0x48:
+            pThis->block_size = dsp_get_lohi(pThis) + 1;
+            LogFlowFunc(("set dma block len %d\n", pThis->block_size));
+            break;
+
+        case 0x74:
+        case 0x75:
+        case 0x76:
+        case 0x77:
+            /* ADPCM stuff, ignore */
+            break;
+
+        case 0x80:
+        {
+            int freq, samples, bytes;
+            uint64_t ticks;
+
+            freq = pThis->freq > 0 ? pThis->freq : 11025;
+            samples = dsp_get_lohi (pThis) + 1;
+            bytes = samples << pThis->fmt_stereo << ((pThis->fmt_bits == 16) ? 1 : 0);
+            ticks = (bytes * TMTimerGetFreq(pThis->pTimerIRQ)) / freq;
+            if (ticks < TMTimerGetFreq(pThis->pTimerIRQ) / 1024)
+                PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
+            else
+                TMTimerSet(pThis->pTimerIRQ, TMTimerGet(pThis->pTimerIRQ) + ticks);
+            LogFlowFunc(("mix silence: %d samples, %d bytes, %RU64 ticks\n", samples, bytes, ticks));
+            break;
+        }
+
+        case 0xe0:
+            d0 = dsp_get_data(pThis);
+            pThis->out_data_len = 0;
+            LogFlowFunc(("E0 data = %#x\n", d0));
+            dsp_out_data(pThis, ~d0);
+            break;
+
+        case 0xe2:
+            d0 = dsp_get_data(pThis);
+            LogFlow(("SB16:E2 = %#x\n", d0));
+            break;
+
+        case 0xe4:
+            pThis->test_reg = dsp_get_data(pThis);
+            break;
+
+        case 0xf9:
+            d0 = dsp_get_data(pThis);
+            LogFlowFunc(("command 0xf9 with %#x\n", d0));
+            switch (d0) {
+            case 0x0e:
+                dsp_out_data(pThis, 0xff);
+                break;
+
+            case 0x0f:
+                dsp_out_data(pThis, 0x07);
+                break;
+
+            case 0x37:
+                dsp_out_data(pThis, 0x38);
+                break;
+
+            default:
+                dsp_out_data(pThis, 0x00);
+                break;
+            }
+            break;
+
+        default:
+            LogFlowFunc(("complete: unrecognized command %#x\n", pThis->cmd));
+            return;
+        }
+    }
+
+    LogFlow(("\n"));
+    pThis->cmd = -1;
+    return;
+}
+
+static uint8_t sb16MixRegToVol(PSB16STATE pThis, int reg)
+{
+    /* The SB16 mixer has a 0 to -62dB range in 32 levels (2dB each step).
+     * We use a 0 to -96dB range in 256 levels (0.375dB each step).
+     * Only the top 5 bits of a mixer register are used.
+     */
+    uint8_t steps = 31 - (pThis->mixer_regs[reg] >> 3);
+    uint8_t vol   = 255 - steps * 16 / 3;   /* (2dB*8) / (0.375dB*8) */
+    return vol;
+}
+
+static void sb16SetMasterVolume(PSB16STATE pThis)
+{
+    /* There's no mute switch, only volume controls. */
+    uint8_t lvol = sb16MixRegToVol(pThis, 0x30);
+    uint8_t rvol = sb16MixRegToVol(pThis, 0x31);
+    PDMAUDIOVOLUME vol = { false, lvol, rvol };
+    AudioMixerSetMasterVolume(pThis->pMixer, &vol);
+}
+
+static void sb16SetPcmOutVolume(PSB16STATE pThis)
+{
+    /* There's no mute switch, only volume controls. */
+    uint8_t lvol = sb16MixRegToVol(pThis, 0x32);
+    uint8_t rvol = sb16MixRegToVol(pThis, 0x33);
+    PDMAUDIOVOLUME vol = { false, lvol, rvol };
+    AudioMixerSetSinkVolume(pThis->pSinkOutput, &vol);
+}
+
+static void sb16ResetLegacy(PSB16STATE pThis)
+{
+    pThis->freq       = 11025;
+    pThis->fmt_signed = 0;
+    pThis->fmt_bits   = 8;
+    pThis->fmt_stereo = 0;
+
+    PDMAUDIOSTREAMCFG streamCfg;
+    streamCfg.uHz           = pThis->freq;
+    streamCfg.cChannels     = 1; /* Mono */
+    streamCfg.enmFormat     = AUD_FMT_U8;
+    streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
+
+    int rc2 = sb16OpenOut(pThis, &streamCfg);
+    AssertRC(rc2);
+}
+
+static void sb16Reset(PSB16STATE pThis)
+{
+    PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
+    if (pThis->dma_auto)
+    {
+        PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
+        PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
+    }
+
+    pThis->mixer_regs[0x82] = 0;
+    pThis->dma_auto = 0;
+    pThis->in_index = 0;
+    pThis->out_data_len = 0;
+    pThis->left_till_irq = 0;
+    pThis->needed_bytes = 0;
+    pThis->block_size = -1;
+    pThis->nzero = 0;
+    pThis->highspeed = 0;
+    pThis->v2x6 = 0;
+    pThis->cmd = -1;
+
+    dsp_out_data(pThis, 0xaa);
+    sb16SpeakerControl(pThis, 0);
+    sb16Control(pThis, 0);
+    sb16ResetLegacy(pThis);
+}
+
+static IO_WRITE_PROTO(dsp_write)
+{
+    PSB16STATE pThis = (PSB16STATE)opaque;
+    int iport = nport - pThis->port;
+
+    LogFlowFunc(("write %#x <- %#x\n", nport, val));
+    switch (iport)
+    {
+        case 0x06:
+            switch (val)
+            {
+                case 0x00:
+                {
+                    if (pThis->v2x6 == 1)
+                    {
+                        if (0 && pThis->highspeed)
+                        {
+                            pThis->highspeed = 0;
+                            PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
+                            sb16Control(pThis, 0);
+                        }
+                        else
+                            sb16Reset(pThis);
+                    }
+                    pThis->v2x6 = 0;
+                    break;
+                }
+
+                case 0x01:
+                case 0x03:              /* FreeBSD kludge */
+                    pThis->v2x6 = 1;
+                    break;
+
+                case 0xc6:
+                    pThis->v2x6 = 0;    /* Prince of Persia, csp.sys, diagnose.exe */
+                    break;
+
+                case 0xb8:              /* Panic */
+                    sb16Reset(pThis);
+                    break;
+
+                case 0x39:
+                    dsp_out_data(pThis, 0x38);
+                    sb16Reset(pThis);
+                    pThis->v2x6 = 0x39;
+                    break;
+
+                default:
+                    pThis->v2x6 = val;
+                    break;
+            }
+            break;
+
+        case 0x0c:                      /* Write data or command | write status */
+#if 0
+            if (pThis->highspeed)
+                break;
+#endif
+            if (0 == pThis->needed_bytes)
+            {
+                sb16HandleCommand(pThis, val);
+#if 0
+                if (0 == pThis->needed_bytes) {
+                    log_dsp (pThis);
+                }
+#endif
+            }
+            else
+            {
+                if (pThis->in_index == sizeof (pThis->in2_data))
+                {
+                    LogFlowFunc(("in data overrun\n"));
+                }
+                else
+                {
+                    pThis->in2_data[pThis->in_index++] = val;
+                    if (pThis->in_index == pThis->needed_bytes)
+                    {
+                        pThis->needed_bytes = 0;
+                        complete (pThis);
+#if 0
+                        log_dsp (pThis);
+#endif
+                    }
+                }
+            }
+            break;
+
+        default:
+            LogFlowFunc(("nport=%#x, val=%#x)\n", nport, val));
+            break;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static IO_READ_PROTO(dsp_read)
+{
+    PSB16STATE pThis = (PSB16STATE)opaque;
+    int iport, retval, ack = 0;
+
+    iport = nport - pThis->port;
+
+    /** @todo reject non-byte access?
+     *  The spec does not mention a non-byte access so we should check how real hardware behaves. */
+
+    switch (iport)
+    {
+        case 0x06:                  /* reset */
+            retval = 0xff;
+            break;
+
+        case 0x0a:                  /* read data */
+            if (pThis->out_data_len)
+            {
+                retval = pThis->out_data[--pThis->out_data_len];
+                pThis->last_read_byte = retval;
+            }
+            else
+            {
+                if (pThis->cmd != -1)
+                    LogFlowFunc(("empty output buffer for command %#x\n", pThis->cmd));
+                retval = pThis->last_read_byte;
+                /* goto error; */
+            }
+            break;
+
+        case 0x0c:                  /* 0 can write */
+            retval = pThis->can_write ? 0 : 0x80;
+            break;
+
+        case 0x0d:                  /* timer interrupt clear */
+            /* LogFlowFunc(("timer interrupt clear\n")); */
+            retval = 0;
+            break;
+
+        case 0x0e:                  /* data available status | irq 8 ack */
+            retval = (!pThis->out_data_len || pThis->highspeed) ? 0 : 0x80;
+            if (pThis->mixer_regs[0x82] & 1)
+            {
+                ack = 1;
+                pThis->mixer_regs[0x82] &= ~1;
+                PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
+            }
+            break;
+
+        case 0x0f:                  /* irq 16 ack */
+            retval = 0xff;
+            if (pThis->mixer_regs[0x82] & 2)
+            {
+                ack = 1;
+                pThis->mixer_regs[0x82] &= ~2;
+               PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
+            }
+            break;
+
+        default:
+            goto error;
+    }
+
+    if (!ack)
+        LogFlowFunc(("read %#x -> %#x\n", nport, retval));
+
+    *pu32 = retval;
+    return VINF_SUCCESS;
+
+ error:
+    LogFlowFunc(("warning: dsp_read %#x error\n", nport));
+    return VERR_IOM_IOPORT_UNUSED;
+}
+
+static void sb16MixerReset(PSB16STATE pThis)
+{
+    PSB16DRIVER pDrv;
+
+    RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
+        pDrv->Out.phStrmOut = NULL;
+
+    pThis->pSinkOutput = NULL;
+
+    if (pThis->pMixer)
+    {
+        AudioMixerDestroy(pThis->pMixer);
+        pThis->pMixer = NULL;
+    }
+
+    memset(pThis->mixer_regs, 0xff, 0x7f);
+    memset(pThis->mixer_regs + 0x83, 0xff, sizeof (pThis->mixer_regs) - 0x83);
+
+    pThis->mixer_regs[0x02] = 4;    /* master volume 3bits */
+    pThis->mixer_regs[0x06] = 4;    /* MIDI volume 3bits */
+    pThis->mixer_regs[0x08] = 0;    /* CD volume 3bits */
+    pThis->mixer_regs[0x0a] = 0;    /* voice volume 2bits */
+
+    /* d5=input filt, d3=lowpass filt, d1,d2=input source */
+    pThis->mixer_regs[0x0c] = 0;
+
+    /* d5=output filt, d1=stereo switch */
+    pThis->mixer_regs[0x0e] = 0;
+
+    /* voice volume L d5,d7, R d1,d3 */
+    pThis->mixer_regs[0x04] = (12 << 4) | 12;
+    /* master ... */
+    pThis->mixer_regs[0x22] = (12 << 4) | 12;
+    /* MIDI ... */
+    pThis->mixer_regs[0x26] = (12 << 4) | 12;
+
+    /* master/voice/MIDI L/R volume */
+    for (int i = 0x30; i < 0x36; i++)
+        pThis->mixer_regs[i] = 24 << 3; /* -14 dB */
+
+    /* treble/bass */
+    for (int i = 0x44; i < 0x48; i++)
+        pThis->mixer_regs[i] = 0x80;
+
+    int rc2 = AudioMixerCreate("SB16 Mixer", 0 /* uFlags */, &pThis->pMixer);
+    if (RT_SUCCESS(rc2))
+    {
+        /* Set a default audio format for our mixer. */
+        PDMAUDIOSTREAMCFG streamCfg;
+        streamCfg.uHz           = 44100;
+        streamCfg.cChannels     = 2;
+        streamCfg.enmFormat     = AUD_FMT_S16;
+        streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
+
+        rc2 = AudioMixerSetDeviceFormat(pThis->pMixer, &streamCfg);
+        AssertRC(rc2);
+
+        /* Add all required audio sinks. */
+        rc2 = AudioMixerAddSink(pThis->pMixer, "[Playback] PCM Output",
+                                AUDMIXSINKDIR_OUTPUT, &pThis->pSinkOutput);
+        AssertRC(rc2);
+    }
+
+    /* Update the master (mixer) and PCM out volumes. */
+    sb16SetMasterVolume(pThis);
+    sb16SetPcmOutVolume(pThis);
+}
+
+static IO_WRITE_PROTO(mixer_write_indexb)
+{
+    PSB16STATE pThis = (PSB16STATE)opaque;
+    (void) nport;
+    pThis->mixer_nreg = val;
+
+    return VINF_SUCCESS;
+}
+
+uint32_t popcount(uint32_t u) /** @todo r=andy WTF? */
+{
+    u = ((u&0x55555555) + ((u>>1)&0x55555555));
+    u = ((u&0x33333333) + ((u>>2)&0x33333333));
+    u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
+    u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
+    u = ( u&0x0000ffff) + (u>>16);
+    return u;
+}
+
+uint32_t lsbindex(uint32_t u)
+{
+    return popcount((u & -(int32_t)u) - 1);
+}
+
+/* Convert SB16 to SB Pro mixer volume (left). */
+static inline void sb16ConvVolumeL(PSB16STATE pThis, unsigned reg, uint8_t val)
+{
+    /* High nibble in SBP mixer. */
+    pThis->mixer_regs[reg] = (pThis->mixer_regs[reg] & 0x0f) | (val & 0xf0);
+}
+
+/* Convert SB16 to SB Pro mixer volume (right). */
+static inline void sb16ConvVolumeR(PSB16STATE pThis, unsigned reg, uint8_t val)
+{
+    /* Low nibble in SBP mixer. */
+    pThis->mixer_regs[reg] = (pThis->mixer_regs[reg] & 0xf0) | (val >> 4);
+}
+
+/* Convert SB Pro to SB16 mixer volume (left + right). */
+static inline void sb16ConvVolumeOldToNew(PSB16STATE pThis, unsigned reg, uint8_t val)
+{
+    /* Left channel. */
+    pThis->mixer_regs[reg + 0] = (val & 0xf0) | RT_BIT(3);
+    /* Right channel (the register immediately following). */
+    pThis->mixer_regs[reg + 1] = (val << 4)   | RT_BIT(3);
+}
+
+static IO_WRITE_PROTO(mixer_write_datab)
+{
+    PSB16STATE  pThis = (PSB16STATE)opaque;
+    bool        fUpdateMaster = false;
+    bool        fUpdateStream = false;
+
+    (void) nport;
+    LogFlowFunc(("mixer_write [%#x] <- %#x\n", pThis->mixer_nreg, val));
+
+    switch (pThis->mixer_nreg)
+    {
+        case 0x00:
+            sb16MixerReset(pThis);
+            /* And update the actual volume, too. */
+            fUpdateMaster = true;
+            fUpdateStream = true;
+            break;
+
+        case 0x04:  /* Translate from old style voice volume (L/R). */
+            sb16ConvVolumeOldToNew(pThis, 0x32, val);
+            fUpdateStream = true;
+            break;
+
+        case 0x22:  /* Translate from old style master volume (L/R). */
+            sb16ConvVolumeOldToNew(pThis, 0x30, val);
+            fUpdateMaster = true;
+            break;
+
+        case 0x26:  /* Translate from old style MIDI volume (L/R). */
+            sb16ConvVolumeOldToNew(pThis, 0x34, val);
+            break;
+
+        case 0x28:  /* Translate from old style CD volume (L/R). */
+            sb16ConvVolumeOldToNew(pThis, 0x36, val);
+            break;
+
+        case 0x2E:  /* Translate from old style line volume (L/R). */
+            sb16ConvVolumeOldToNew(pThis, 0x38, val);
+            break;
+
+        case 0x30:  /* Translate to old style master volume (L). */
+            sb16ConvVolumeL(pThis, 0x22, val);
+            fUpdateMaster = true;
+            break;
+
+        case 0x31:  /* Translate to old style master volume (R). */
+            sb16ConvVolumeR(pThis, 0x22, val);
+            fUpdateMaster = true;
+            break;
+
+        case 0x32:  /* Translate to old style voice volume (L). */
+            sb16ConvVolumeL(pThis, 0x04, val);
+            fUpdateStream = true;
+            break;
+
+        case 0x33:  /* Translate to old style voice volume (R). */
+            sb16ConvVolumeR(pThis, 0x04, val);
+            fUpdateStream = true;
+            break;
+
+        case 0x34:  /* Translate to old style MIDI volume (L). */
+            sb16ConvVolumeL(pThis, 0x26, val);
+            break;
+
+        case 0x35:  /* Translate to old style MIDI volume (R). */
+            sb16ConvVolumeR(pThis, 0x26, val);
+            break;
+
+        case 0x36:  /* Translate to old style CD volume (L). */
+            sb16ConvVolumeL(pThis, 0x28, val);
+            break;
+
+        case 0x37:  /* Translate to old style CD volume (R). */
+            sb16ConvVolumeR(pThis, 0x28, val);
+            break;
+
+        case 0x38:  /* Translate to old style line volume (L). */
+            sb16ConvVolumeL(pThis, 0x2E, val);
+            break;
+
+        case 0x39:  /* Translate to old style line volume (R). */
+            sb16ConvVolumeR(pThis, 0x2E, val);
+            break;
+
+        case 0x80:
+        {
+            int irq = irq_of_magic(val);
+            LogFlowFunc(("setting irq to %d (val=%#x)\n", irq, val));
+            if (irq > 0)
+                pThis->irq = irq;
+            break;
+        }
+
+        case 0x81:
+        {
+            int dma, hdma;
+
+            dma = lsbindex (val & 0xf);
+            hdma = lsbindex (val & 0xf0);
+            if (dma != pThis->dma || hdma != pThis->hdma)
+                LogFlow(("SB16: attempt to change DMA 8bit %d(%d), 16bit %d(%d) (val=%#x)\n",
+                         dma, pThis->dma, hdma, pThis->hdma, val));
+#if 0
+            pThis->dma = dma;
+            pThis->hdma = hdma;
+#endif
+            break;
+        }
+
+        case 0x82:
+            LogFlowFunc(("attempt to write into IRQ status register (val=%#x)\n", val));
+            return VINF_SUCCESS;
+
+        default:
+            if (pThis->mixer_nreg >= 0x80)
+                LogFlowFunc(("attempt to write mixer[%#x] <- %#x\n", pThis->mixer_nreg, val));
+            break;
+    }
+
+    pThis->mixer_regs[pThis->mixer_nreg] = val;
+
+    /* Update the master (mixer) volume. */
+    if (fUpdateMaster)
+        sb16SetMasterVolume(pThis);
+
+    /* Update the stream (PCM) volume. */
+    if (fUpdateStream)
+        sb16SetPcmOutVolume(pThis);
+
+    return VINF_SUCCESS;
+}
+
+static IO_WRITE_PROTO(mixer_write)
+{
+    PSB16STATE pThis = (PSB16STATE)opaque;
+    int iport = nport - pThis->port;
+    switch (cb)
+    {
+        case 1:
+            switch (iport)
+            {
+                case 4:
+                    mixer_write_indexb (pDevIns, opaque, nport, val, 1);
+                    break;
+                case 5:
+                    mixer_write_datab (pDevIns, opaque, nport, val, 1);
+                    break;
+            }
+            break;
+        case 2:
+            mixer_write_indexb (pDevIns, opaque, nport, val & 0xff, 1);
+            mixer_write_datab (pDevIns, opaque, nport, (val >> 8) & 0xff, 1);
+            break;
+        default:
+            AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", nport, cb, val));
+            break;
+    }
+    return VINF_SUCCESS;
+}
+
+static IO_READ_PROTO(mixer_read)
+{
+    PSB16STATE pThis = (PSB16STATE)opaque;
+
+    (void) nport;
+#ifndef DEBUG_SB16_MOST
+    if (pThis->mixer_nreg != 0x82) {
+        LogFlowFunc(("mixer_read[%#x] -> %#x\n",
+                pThis->mixer_nreg, pThis->mixer_regs[pThis->mixer_nreg]));
+    }
+#else
+    LogFlowFunc(("mixer_read[%#x] -> %#x\n",
+            pThis->mixer_nreg, pThis->mixer_regs[pThis->mixer_nreg]));
+#endif
+    *pu32 = pThis->mixer_regs[pThis->mixer_nreg];
+    return VINF_SUCCESS;
+}
+
+static int sb16WriteAudio(PSB16STATE pThis, int nchan, uint32_t dma_pos,
+                          uint32_t dma_len, int len)
+{
+    uint8_t  tmpbuf[_4K]; /** @todo Have a buffer on the heap. */
+    uint32_t cbToWrite = len;
+    uint32_t cbWrittenTotal = 0;
+
+    while (cbToWrite)
+    {
+        uint32_t cbToRead;
+        uint32_t cbRead;
+
+        cbToRead = RT_MIN(dma_len - dma_pos, cbToWrite);
+        if (cbToRead > sizeof(tmpbuf))
+            cbToRead = sizeof(tmpbuf);
+
+        int rc = PDMDevHlpDMAReadMemory(pThis->pDevInsR3, nchan, tmpbuf, dma_pos, cbToRead, &cbRead);
+        AssertMsgRC(rc, ("DMAReadMemory -> %Rrc\n", rc));
+
+        uint32_t cbWritten;
+
+        /* Just multiplex the output to the connected backends.
+         * No need to utilize the virtual mixer here (yet). */
+        PSB16DRIVER pDrv;
+        RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
+        {
+            int rc2 = pDrv->pConnector->pfnWrite(pDrv->pConnector, pDrv->Out.pStrmOut,
+                                                 tmpbuf, cbToRead, &cbWritten);
+            LogFlowFunc(("\tLUN#%RU8: rc=%Rrc, cbWritten=%RU32\n", pDrv->uLUN, rc2, cbWritten));
+        }
+
+        Assert(cbToWrite >= cbToRead);
+        cbToWrite      -= cbToRead;
+        dma_pos         = (dma_pos + cbToRead) % dma_len;
+        cbWrittenTotal += cbToRead;
+
+        if (!cbRead)
+            break;
+    }
+
+    return cbWrittenTotal;
+}
+
+static DECLCALLBACK(uint32_t) sb16DMARead(PPDMDEVINS pDevIns, void *opaque, unsigned nchan, uint32_t dma_pos, uint32_t dma_len)
+{
+    PSB16STATE pThis = (PSB16STATE)opaque;
+    int till, copy, written, free;
+
+    if (pThis->block_size <= 0)
+    {
+        LogFlowFunc(("invalid block size=%d nchan=%d dma_pos=%d dma_len=%d\n",
+                     pThis->block_size, nchan, dma_pos, dma_len));
+        return dma_pos;
+    }
+
+    if (pThis->left_till_irq < 0)
+        pThis->left_till_irq = pThis->block_size;
+
+    PSB16DRIVER pDrv;
+
+    uint32_t cbOutMin = UINT32_MAX;
+    uint32_t cbOut;
+    RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
+    {
+        int rc2 = pDrv->pConnector->pfnQueryStatus(pDrv->pConnector,
+                                                   NULL /* pcbIn */, &cbOut, NULL /* pcSamplesLive */);
+        if (RT_SUCCESS(rc2))
+            cbOutMin = RT_MIN(cbOutMin, cbOut);
+    }
+
+    LogFlowFunc(("cbOutMin=%RU32\n", cbOutMin));
+    if (cbOutMin == UINT32_MAX)
+    {
+        free = dma_len;
+    }
+    else
+    {
+        free = cbOutMin & ~pThis->align; /** @todo int vs. uint32. */
+        if ((free <= 0) || !dma_len)
+            return dma_pos;
+    }
+
+    copy = free;
+    till = pThis->left_till_irq;
+
+#ifdef DEBUG_SB16_MOST
+    LogFlowFunc(("pos:%06d %d till:%d len:%d\n", dma_pos, free, till, dma_len));
+#endif
+
+    if (copy >= till)
+    {
+        if (0 == pThis->dma_auto)
+        {
+            copy = till;
+        }
+        else
+        {
+            if (copy >= till + pThis->block_size)
+                copy = till; /* Make sure we won't skip IRQs. */
+        }
+    }
+
+    written = sb16WriteAudio(pThis, nchan, dma_pos, dma_len, copy);
+    dma_pos = (dma_pos + written) % dma_len;
+    pThis->left_till_irq -= written;
+
+    if (pThis->left_till_irq <= 0)
+    {
+        pThis->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1;
+        PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
+        if (0 == pThis->dma_auto)
+        {
+            sb16Control(pThis, 0);
+            sb16SpeakerControl(pThis, 0);
+        }
+    }
+
+#ifdef DEBUG_SB16_MOST
+    LogFlowFunc(("pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d\n",
+                 dma_pos, free, dma_len, pThis->left_till_irq, copy, written,
+                 pThis->block_size));
+#endif
+
+    while (pThis->left_till_irq <= 0)
+        pThis->left_till_irq += pThis->block_size;
+
+    return dma_pos;
+}
+
+static DECLCALLBACK(void) sb16TimerIO(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+    PSB16STATE pThis = (PSB16STATE)pvUser;
+    Assert(pThis == PDMINS_2_DATA(pDevIns, PSB16STATE));
+    AssertPtr(pThis);
+
+    uint32_t cbInMax  = 0;
+    uint32_t cbOutMin = UINT32_MAX;
+
+    PSB16DRIVER pDrv;
+
+    uint64_t cTicksNow     = TMTimerGet(pTimer);
+    uint64_t cTicksElapsed = cTicksNow  - pThis->uTimerTSIO;
+    uint64_t cTicksPerSec  = TMTimerGetFreq(pTimer);
+
+    pThis->uTimerTSIO = cTicksNow;
+
+    /*
+     * Calculate the mixer's (fixed) sampling rate.
+     */
+    AssertPtr(pThis->pMixer);
+
+    PDMAUDIOSTREAMCFG mixerStrmCfg;
+    int rc = AudioMixerGetDeviceFormat(pThis->pMixer, &mixerStrmCfg);
+    AssertRC(rc);
+
+    PDMPCMPROPS mixerStrmProps;
+    rc = DrvAudioStreamCfgToProps(&mixerStrmCfg, &mixerStrmProps);
+    AssertRC(rc);
+
+    uint32_t cMixerSamplesMin  = (int)((2 * cTicksElapsed * mixerStrmCfg.uHz + cTicksPerSec) / cTicksPerSec / 2);
+    uint32_t cbMixerSamplesMin = cMixerSamplesMin << mixerStrmProps.cShift;
+
+    RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
+    {
+        uint32_t cbIn = 0;
+        uint32_t cbOut = 0;
+
+        rc = pDrv->pConnector->pfnQueryStatus(pDrv->pConnector,
+                                              &cbIn, &cbOut, NULL /* cSamplesLive */);
+        if (RT_SUCCESS(rc))
+            rc = pDrv->pConnector->pfnPlayOut(pDrv->pConnector, NULL /* cSamplesPlayed */);
+
+#ifdef DEBUG_TIMER
+        LogFlowFunc(("LUN#%RU8: rc=%Rrc, cbIn=%RU32, cbOut=%RU32\n", pDrv->uLUN, rc, cbIn, cbOut));
+#endif
+        /* If we there was an error handling (available) output or there simply is no output available,
+         * then calculate the minimum data rate which must be processed by the device emulation in order
+         * to function correctly.
+         *
+         * This is not the optimal solution, but as we have to deal with this on a timer-based approach
+         * (until we have the audio callbacks) we need to have device' DMA engines running. */
+        if (!pDrv->pConnector->pfnIsValidOut(pDrv->pConnector, pDrv->Out.pStrmOut))
+        {
+            /* Use the mixer's (fixed) sampling rate. */
+            cbOut = RT_MAX(cbOut, cbMixerSamplesMin);
+            continue;
+        }
+
+        const bool fIsActiveOut = pDrv->pConnector->pfnIsActiveOut(pDrv->pConnector, pDrv->Out.pStrmOut);
+        if (   RT_FAILURE(rc)
+            || !fIsActiveOut)
+        {
+            uint32_t cSamplesMin  = (int)((2 * cTicksElapsed * pDrv->Out.pStrmOut->Props.uHz + cTicksPerSec) / cTicksPerSec / 2);
+            uint32_t cbSamplesMin = AUDIOMIXBUF_S2B(&pDrv->Out.pStrmOut->MixBuf, cSamplesMin);
+
+            Log2Func(("\trc=%Rrc, cSamplesMin=%RU32, cbSamplesMin=%RU32\n", rc, cSamplesMin, cbSamplesMin));
+
+            cbOut = RT_MAX(cbOut, cbSamplesMin);
+        }
+
+        cbOutMin = RT_MIN(cbOutMin, cbOut);
+        cbInMax  = RT_MAX(cbInMax, cbIn);
+    }
+
+    Log2Func(("cbInMax=%RU32, cbOutMin=%RU32\n", cbInMax, cbOutMin));
+
+    if (cbOutMin == UINT32_MAX)
+        cbOutMin = 0;
+
+    /*
+     * Playback.
+     */
+    if (cbOutMin)
+    {
+        Assert(cbOutMin != UINT32_MAX);
+
+        /* New space available, see if we can transfer more. */
+        PDMDevHlpDMASchedule(pThis->pDevInsR3);
+    }
+
+    /*
+     * Recording.
+     */
+    /** @todo Implement recording. */
+
+    /* Kick the timer again. */
+    uint64_t cTicks = pThis->cTimerTicksIO;
+    /** @todo adjust cTicks down by now much cbOutMin represents. */
+    TMTimerSet(pThis->pTimerIO, cTicksNow + cTicks);
+}
+
+static void sb16Save(PSSMHANDLE pSSM, PSB16STATE pThis)
+{
+    SSMR3PutS32(pSSM, pThis->irq);
+    SSMR3PutS32(pSSM, pThis->dma);
+    SSMR3PutS32(pSSM, pThis->hdma);
+    SSMR3PutS32(pSSM, pThis->port);
+    SSMR3PutS32(pSSM, pThis->ver);
+    SSMR3PutS32(pSSM, pThis->in_index);
+    SSMR3PutS32(pSSM, pThis->out_data_len);
+    SSMR3PutS32(pSSM, pThis->fmt_stereo);
+    SSMR3PutS32(pSSM, pThis->fmt_signed);
+    SSMR3PutS32(pSSM, pThis->fmt_bits);
+
+    SSMR3PutU32(pSSM, pThis->fmt);
+
+    SSMR3PutS32(pSSM, pThis->dma_auto);
+    SSMR3PutS32(pSSM, pThis->block_size);
+    SSMR3PutS32(pSSM, pThis->fifo);
+    SSMR3PutS32(pSSM, pThis->freq);
+    SSMR3PutS32(pSSM, pThis->time_const);
+    SSMR3PutS32(pSSM, pThis->speaker);
+    SSMR3PutS32(pSSM, pThis->needed_bytes);
+    SSMR3PutS32(pSSM, pThis->cmd);
+    SSMR3PutS32(pSSM, pThis->use_hdma);
+    SSMR3PutS32(pSSM, pThis->highspeed);
+    SSMR3PutS32(pSSM, pThis->can_write);
+    SSMR3PutS32(pSSM, pThis->v2x6);
+
+    SSMR3PutU8 (pSSM, pThis->csp_param);
+    SSMR3PutU8 (pSSM, pThis->csp_value);
+    SSMR3PutU8 (pSSM, pThis->csp_mode);
+    SSMR3PutU8 (pSSM, pThis->csp_param); /* Bug compatible! */
+    SSMR3PutMem(pSSM, pThis->csp_regs, 256);
+    SSMR3PutU8 (pSSM, pThis->csp_index);
+    SSMR3PutMem(pSSM, pThis->csp_reg83, 4);
+    SSMR3PutS32(pSSM, pThis->csp_reg83r);
+    SSMR3PutS32(pSSM, pThis->csp_reg83w);
+
+    SSMR3PutMem(pSSM, pThis->in2_data, sizeof (pThis->in2_data));
+    SSMR3PutMem(pSSM, pThis->out_data, sizeof (pThis->out_data));
+    SSMR3PutU8 (pSSM, pThis->test_reg);
+    SSMR3PutU8 (pSSM, pThis->last_read_byte);
+
+    SSMR3PutS32(pSSM, pThis->nzero);
+    SSMR3PutS32(pSSM, pThis->left_till_irq);
+    SSMR3PutS32(pSSM, pThis->dma_running);
+    SSMR3PutS32(pSSM, pThis->bytes_per_second);
+    SSMR3PutS32(pSSM, pThis->align);
+
+    SSMR3PutS32(pSSM, pThis->mixer_nreg);
+    SSMR3PutMem(pSSM, pThis->mixer_regs, 256);
+
+}
+
+static int sb16Load(PSSMHANDLE pSSM, PSB16STATE pThis, int version_id)
+{
+    SSMR3GetS32(pSSM, &pThis->irq);
+    SSMR3GetS32(pSSM, &pThis->dma);
+    SSMR3GetS32(pSSM, &pThis->hdma);
+    SSMR3GetS32(pSSM, &pThis->port);
+    SSMR3GetS32(pSSM, &pThis->ver);
+    SSMR3GetS32(pSSM, &pThis->in_index);
+    SSMR3GetS32(pSSM, &pThis->out_data_len);
+    SSMR3GetS32(pSSM, &pThis->fmt_stereo);
+    SSMR3GetS32(pSSM, &pThis->fmt_signed);
+    SSMR3GetS32(pSSM, &pThis->fmt_bits);
+
+    SSMR3GetU32(pSSM, (uint32_t *)&pThis->fmt);
+
+    SSMR3GetS32(pSSM, &pThis->dma_auto);
+    SSMR3GetS32(pSSM, &pThis->block_size);
+    SSMR3GetS32(pSSM, &pThis->fifo);
+    SSMR3GetS32(pSSM, &pThis->freq);
+    SSMR3GetS32(pSSM, &pThis->time_const);
+    SSMR3GetS32(pSSM, &pThis->speaker);
+    SSMR3GetS32(pSSM, &pThis->needed_bytes);
+    SSMR3GetS32(pSSM, &pThis->cmd);
+    SSMR3GetS32(pSSM, &pThis->use_hdma);
+    SSMR3GetS32(pSSM, &pThis->highspeed);
+    SSMR3GetS32(pSSM, &pThis->can_write);
+    SSMR3GetS32(pSSM, &pThis->v2x6);
+
+    SSMR3GetU8 (pSSM, &pThis->csp_param);
+    SSMR3GetU8 (pSSM, &pThis->csp_value);
+    SSMR3GetU8 (pSSM, &pThis->csp_mode);
+    SSMR3GetU8 (pSSM, &pThis->csp_param);   /* Bug compatible! */
+    SSMR3GetMem(pSSM, pThis->csp_regs, 256);
+    SSMR3GetU8 (pSSM, &pThis->csp_index);
+    SSMR3GetMem(pSSM, pThis->csp_reg83, 4);
+    SSMR3GetS32(pSSM, &pThis->csp_reg83r);
+    SSMR3GetS32(pSSM, &pThis->csp_reg83w);
+
+    SSMR3GetMem(pSSM, pThis->in2_data, sizeof (pThis->in2_data));
+    SSMR3GetMem(pSSM, pThis->out_data, sizeof (pThis->out_data));
+    SSMR3GetU8 (pSSM, &pThis->test_reg);
+    SSMR3GetU8 (pSSM, &pThis->last_read_byte);
+
+    SSMR3GetS32(pSSM, &pThis->nzero);
+    SSMR3GetS32(pSSM, &pThis->left_till_irq);
+    SSMR3GetS32(pSSM, &pThis->dma_running);
+    SSMR3GetS32(pSSM, &pThis->bytes_per_second);
+    SSMR3GetS32(pSSM, &pThis->align);
+
+    SSMR3GetS32(pSSM, &pThis->mixer_nreg);
+    SSMR3GetMem(pSSM, pThis->mixer_regs, 256);
+
+#if 0
+    PSB16DRIVER pDrv;
+    RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
+    {
+        if (pDrv->Out.pStrmOut)
+        {
+            pDrv->pConnector->pfnCloseOut(pThis->pDrv, pDrv->Out.pStrmOut);
+            pDrv->Out.pStrmOut = NULL;
+        }
+    }
+#endif
+
+    if (pThis->dma_running)
+    {
+        if (pThis->freq)
+        {
+            PDMAUDIOSTREAMCFG streamCfg;
+            streamCfg.uHz           = pThis->freq;
+            streamCfg.cChannels     = 1 << pThis->fmt_stereo;
+            streamCfg.enmFormat     = pThis->fmt;
+            streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
+
+            int rc = sb16OpenOut(pThis, &streamCfg);
+            AssertRC(rc);
+        }
+
+        sb16Control(pThis, 1);
+        sb16SpeakerControl(pThis, pThis->speaker);
+    }
+
+    /* Update the master (mixer) and PCM out volumes. */
+    sb16SetMasterVolume(pThis);
+    sb16SetPcmOutVolume(pThis);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) sb16LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
+{
+    PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
+
+    SSMR3PutS32(pSSM, pThis->irqCfg);
+    SSMR3PutS32(pSSM, pThis->dmaCfg);
+    SSMR3PutS32(pSSM, pThis->hdmaCfg);
+    SSMR3PutS32(pSSM, pThis->portCfg);
+    SSMR3PutS32(pSSM, pThis->verCfg);
+    return VINF_SSM_DONT_CALL_AGAIN;
+}
+
+static DECLCALLBACK(int) sb16SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+    PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
+
+    sb16LiveExec(pDevIns, pSSM, 0);
+    sb16Save(pSSM, pThis);
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) sb16LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+    PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
+
+    AssertMsgReturn(    uVersion == SB16_SAVE_STATE_VERSION
+                    ||  uVersion == SB16_SAVE_STATE_VERSION_VBOX_30,
+                    ("%u\n", uVersion),
+                    VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
+    if (uVersion > SB16_SAVE_STATE_VERSION_VBOX_30)
+    {
+        int32_t irq;
+        SSMR3GetS32 (pSSM, &irq);
+        int32_t dma;
+        SSMR3GetS32 (pSSM, &dma);
+        int32_t hdma;
+        SSMR3GetS32 (pSSM, &hdma);
+        int32_t port;
+        SSMR3GetS32 (pSSM, &port);
+        int32_t ver;
+        int rc = SSMR3GetS32 (pSSM, &ver);
+        AssertRCReturn (rc, rc);
+
+        if (   irq  != pThis->irqCfg
+            || dma  != pThis->dmaCfg
+            || hdma != pThis->hdmaCfg
+            || port != pThis->portCfg
+            || ver  != pThis->verCfg)
+        {
+            return SSMR3SetCfgError(pSSM, RT_SRC_POS,
+                                    N_("config changed: irq=%x/%x dma=%x/%x hdma=%x/%x port=%x/%x ver=%x/%x (saved/config)"),
+                                    irq,  pThis->irqCfg,
+                                    dma,  pThis->dmaCfg,
+                                    hdma, pThis->hdmaCfg,
+                                    port, pThis->portCfg,
+                                    ver,  pThis->verCfg);
+        }
+    }
+
+    if (uPass != SSM_PASS_FINAL)
+        return VINF_SUCCESS;
+
+    sb16Load(pSSM, pThis, uVersion);
+    return VINF_SUCCESS;
+}
+
+static int sb16OpenOut(PSB16STATE pThis, PPDMAUDIOSTREAMCFG pCfg)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    int rc = VINF_SUCCESS;
+
+    PSB16DRIVER pDrv;
+    uint8_t uLUN = 0;
+
+    RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
+    {
+        char *pszDesc;
+        if (RTStrAPrintf(&pszDesc, "[LUN#%RU8] sb16.po", uLUN) <= 0)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        int rc2 = pDrv->pConnector->pfnCreateOut(pDrv->pConnector, pszDesc, pCfg, &pDrv->Out.pStrmOut);
+        LogFlowFunc(("LUN#%RU8: Created output with rc=%Rrc\n", uLUN, rc));
+        if (rc2 == VINF_SUCCESS) /* Note: Could return VWRN_ALREADY_EXISTS. */
+        {
+            AudioMixerRemoveStream(pThis->pSinkOutput, pDrv->Out.phStrmOut);
+            rc = AudioMixerAddStreamOut(pThis->pSinkOutput,
+                                        pDrv->pConnector, pDrv->Out.pStrmOut,
+                                        0 /* uFlags */,
+                                        &pDrv->Out.phStrmOut);
+        }
+
+        RTStrFree(pszDesc);
+
+        if (RT_FAILURE(rc2))
+        {
+            if (RT_SUCCESS(rc))
+                rc = rc2;
+            break;
+        }
+
+        uLUN++;
+    }
+
+    /* Ensure volume gets propagated. */
+    AudioMixerInvalidate(pThis->pMixer);
+
+    return rc;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) sb16DevReset(PPDMDEVINS pDevIns)
+{
+    PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
+
+    /* Bring back the device to initial state, and especially make
+     * sure there's no interrupt or DMA activity.
+     */
+    PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 0);
+
+    pThis->mixer_regs[0x82] = 0;
+    pThis->csp_regs[5]      = 1;
+    pThis->csp_regs[9]      = 0xf8;
+
+    pThis->dma_auto = 0;
+    pThis->in_index = 0;
+    pThis->out_data_len = 0;
+    pThis->left_till_irq = 0;
+    pThis->needed_bytes = 0;
+    pThis->block_size = -1;
+    pThis->nzero = 0;
+    pThis->highspeed = 0;
+    pThis->v2x6 = 0;
+    pThis->cmd = -1;
+
+    sb16MixerReset(pThis);
+    sb16SpeakerControl(pThis, 0);
+    sb16Control(pThis, 0);
+    sb16ResetLegacy(pThis);
+}
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) sb16QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
+{
+    PSB16STATE pThis = RT_FROM_MEMBER(pInterface, SB16STATE, IBase);
+    Assert(&pThis->IBase == pInterface);
+
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
+    return NULL;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) sb16Destruct(PPDMDEVINS pDevIns)
+{
+    PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
+
+    PSB16DRIVER pDrv;
+
+    RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
+        pDrv->Out.phStrmOut = NULL;
+
+    pThis->pSinkOutput = NULL;
+
+    if (pThis->pMixer)
+    {
+        AudioMixerDestroy(pThis->pMixer);
+        pThis->pMixer = NULL;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) sb16Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+    PSB16STATE pThis = PDMINS_2_DATA(pDevIns, PSB16STATE);
+
+    /*
+     * Validations.
+     */
+    Assert(iInstance == 0);
+    PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+    if (!CFGMR3AreValuesValid(pCfg,
+                              "IRQ\0"
+                              "DMA\0"
+                              "DMA16\0"
+                              "Port\0"
+                              "Version\0"
+                              "TimerHz\0"))
+        return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
+                                N_("Invalid configuration for SB16 device"));
+
+    /*
+     * Read config data.
+     */
+    int rc = CFGMR3QuerySIntDef(pCfg, "IRQ", &pThis->irq, 5);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("SB16 configuration error: Failed to get the \"IRQ\" value"));
+    pThis->irqCfg  = pThis->irq;
+
+    rc = CFGMR3QuerySIntDef(pCfg, "DMA", &pThis->dma, 1);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("SB16 configuration error: Failed to get the \"DMA\" value"));
+    pThis->dmaCfg  = pThis->dma;
+
+    rc = CFGMR3QuerySIntDef(pCfg, "DMA16", &pThis->hdma, 5);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("SB16 configuration error: Failed to get the \"DMA16\" value"));
+    pThis->hdmaCfg = pThis->hdma;
+
+    RTIOPORT Port;
+    rc = CFGMR3QueryPortDef(pCfg, "Port", &Port, 0x220);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("SB16 configuration error: Failed to get the \"Port\" value"));
+    pThis->port    = Port;
+    pThis->portCfg = Port;
+
+    uint16_t u16Version;
+    rc = CFGMR3QueryU16Def(pCfg, "Version", &u16Version, 0x0405);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("SB16 configuration error: Failed to get the \"Version\" value"));
+
+#ifndef VBOX_WITH_AUDIO_CALLBACKS
+    uint16_t uTimerHz;
+    rc = CFGMR3QueryU16Def(pCfg, "TimerHz", &uTimerHz, 200 /* Hz */);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("SB16 configuration error: failed to read Hertz (Hz) rate as unsigned integer"));
+#endif
+
+    pThis->ver     = u16Version;
+    pThis->verCfg  = u16Version;
+
+    /*
+     * Init instance data.
+     */
+    pThis->pDevInsR3               = pDevIns;
+    pThis->IBase.pfnQueryInterface = sb16QueryInterface;
+    pThis->cmd                     = -1;
+
+    pThis->mixer_regs[0x80]        = magic_of_irq (pThis->irq);
+    pThis->mixer_regs[0x81]        = (1 << pThis->dma) | (1 << pThis->hdma);
+    pThis->mixer_regs[0x82]        = 2 << 5;
+
+    pThis->csp_regs[5]             = 1;
+    pThis->csp_regs[9]             = 0xf8;
+
+    RTListInit(&pThis->lstDrv);
+
+    sb16MixerReset(pThis);
+
+    /*
+     * Create timer(s), register & attach stuff.
+     */
+    rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIRQ, pThis,
+                                TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "SB16 IRQ timer", &pThis->pTimerIRQ);
+    if (RT_FAILURE(rc))
+        AssertMsgFailedReturn(("Error creating IRQ timer, rc=%Rrc\n", rc), rc);
+
+    rc = PDMDevHlpIOPortRegister(pDevIns, pThis->port + 0x04,  2, pThis,
+                                 mixer_write, mixer_read, NULL, NULL, "SB16");
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = PDMDevHlpIOPortRegister(pDevIns, pThis->port + 0x06, 10, pThis,
+                                 dsp_write, dsp_read, NULL, NULL, "SB16");
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = PDMDevHlpDMARegister(pDevIns, pThis->hdma, sb16DMARead, pThis);
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = PDMDevHlpDMARegister(pDevIns, pThis->dma, sb16DMARead, pThis);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    pThis->can_write = 1;
+
+    rc = PDMDevHlpSSMRegister3(pDevIns, SB16_SAVE_STATE_VERSION, sizeof(SB16STATE), sb16LiveExec, sb16SaveExec, sb16LoadExec);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /*
+     * Attach driver.
+     */
+    uint8_t uLUN;
+    for (uLUN = 0; uLUN < UINT8_MAX; ++uLUN)
+    {
+        LogFunc(("Trying to attach driver for LUN #%RU8 ...\n", uLUN));
+        rc = sb16AttachInternal(pDevIns, NULL /* pDrv */, uLUN, 0 /* fFlags */);
+        if (RT_FAILURE(rc))
+        {
+            if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+                rc = VINF_SUCCESS;
+            else if (rc == VERR_AUDIO_BACKEND_INIT_FAILED)
+            {
+                sb16Reattach(pThis, NULL /* pDrv */, uLUN, "NullAudio");
+                PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
+                        N_("No audio devices could be opened. Selecting the NULL audio backend "
+                           "with the consequence that no sound is audible"));
+                /* attaching to the NULL audio backend will never fail */
+                rc = VINF_SUCCESS;
+            }
+            break;
+        }
+    }
+
+    LogFunc(("cLUNs=%RU8, rc=%Rrc\n", uLUN, rc));
+
+    sb16ResetLegacy(pThis);
+
+    PSB16DRIVER pDrv;
+    RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
+    {
+        /*
+         * Only primary drivers are critical for the VM to run. Everything else
+         * might not worth showing an own error message box in the GUI.
+         */
+        if (!(pDrv->Flags & PDMAUDIODRVFLAG_PRIMARY))
+            continue;
+
+        PPDMIAUDIOCONNECTOR pCon = pDrv->pConnector;
+        AssertPtr(pCon);
+
+        /* Note: No input streams available for SB16 yet. */
+        bool fValidOut = pCon->pfnIsValidOut(pCon, pDrv->Out.pStrmOut);
+        if (!fValidOut)
+        {
+            LogRel(("SB16: Falling back to NULL backend (no sound audible)\n"));
+
+            sb16ResetLegacy(pThis);
+            sb16Reattach(pThis, pDrv, pDrv->uLUN, "NullAudio");
+
+            PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",
+                N_("No audio devices could be opened. Selecting the NULL audio backend "
+                   "with the consequence that no sound is audible"));
+        }
+    }
+
+#ifndef VBOX_WITH_AUDIO_CALLBACKS
+    if (RT_SUCCESS(rc))
+    {
+        rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIO, pThis,
+                                    TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "SB16 IO timer", &pThis->pTimerIO);
+        if (RT_FAILURE(rc))
+            AssertMsgFailedReturn(("Error creating I/O timer, rc=%Rrc\n", rc), rc);
+        else
+        {
+            pThis->cTimerTicksIO = TMTimerGetFreq(pThis->pTimerIO) / uTimerHz;
+            pThis->uTimerTSIO    = TMTimerGet(pThis->pTimerIO);
+            LogFunc(("Timer ticks=%RU64 (%RU16 Hz)\n", pThis->cTimerTicksIO, uTimerHz));
+
+            /* Fire off timer. */
+            TMTimerSet(pThis->pTimerIO, TMTimerGet(pThis->pTimerIO) + pThis->cTimerTicksIO);
+        }
+    }
+#else
+    if (RT_SUCCESS(rc))
+    {
+        /** @todo Merge this callback registration with the validation block above once
+         *  this becomes the standard. */
+        PSB16DRIVER pDrv;
+        RTListForEach(&pThis->lstDrv, pDrv, SB16DRIVER, Node)
+        {
+            /* Only register primary driver.
+             * The device emulation does the output multiplexing then. */
+            if (pDrv->Flags != PDMAUDIODRVFLAG_PRIMARY)
+                continue;
+
+            PDMAUDIOCALLBACK AudioCallbacks[2];
+
+            SB16CALLBACKCTX Ctx = { pThis, pDrv };
+
+            AudioCallbacks[0].enmType     = PDMAUDIOCALLBACKTYPE_INPUT;
+            AudioCallbacks[0].pfnCallback = sb16CallbackInput;
+            AudioCallbacks[0].pvCtx       = &Ctx;
+            AudioCallbacks[0].cbCtx       = sizeof(SB16CALLBACKCTX);
+
+            AudioCallbacks[1].enmType     = PDMAUDIOCALLBACKTYPE_OUTPUT;
+            AudioCallbacks[1].pfnCallback = sb16CallbackOutput;
+            AudioCallbacks[1].pvCtx       = &Ctx;
+            AudioCallbacks[1].cbCtx       = sizeof(SB16CALLBACKCTX);
+
+            rc = pDrv->pConnector->pfnRegisterCallbacks(pDrv->pConnector, AudioCallbacks, RT_ELEMENTS(AudioCallbacks));
+            if (RT_FAILURE(rc))
+                break;
+        }
+    }
+#endif
+
+    return VINF_SUCCESS;
+}
+
+const PDMDEVREG g_DeviceSB16 =
+{
+    /* u32Version */
+    PDM_DEVREG_VERSION,
+    /* szName */
+    "sb16",
+    /* szRCMod */
+    "",
+    /* szR0Mod */
+    "",
+    /* pszDescription */
+    "Sound Blaster 16 Controller",
+    /* fFlags */
+    PDM_DEVREG_FLAGS_DEFAULT_BITS,
+    /* fClass */
+    PDM_DEVREG_CLASS_AUDIO,
+    /* cMaxInstances */
+    1,
+    /* cbInstance */
+    sizeof(SB16STATE),
+    /* pfnConstruct */
+    sb16Construct,
+    /* pfnDestruct */
+    sb16Destruct,
+    /* pfnRelocate */
+    NULL,
+    /* pfnMemSetup */
+    NULL,
+    /* pfnPowerOn */
+    NULL,
+    /* pfnReset */
+    sb16DevReset,
+    /* pfnSuspend */
+    NULL,
+    /* pfnResume */
+    NULL,
+    /* pfnAttach */
+    sb16Attach,
+    /* pfnDetach */
+    sb16Detach,
+    /* pfnQueryInterface */
+    NULL,
+    /* pfnInitComplete */
+    NULL,
+    /* pfnPowerOff */
+    NULL,
+    /* pfnSoftReset */
+    NULL,
+    /* u32VersionEnd */
+    PDM_DEVREG_VERSION
+};
Index: /trunk/src/VBox/Devices/Audio_old/DrvAudio.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DrvAudio.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DrvAudio.cpp	(revision 61413)
@@ -0,0 +1,2521 @@
+/* $Id$ */
+/** @file
+ * Intermediate audio driver header.
+ *
+ * @remarks Intermediate audio driver for connecting the audio device emulation
+ *          with the host backend.
+ */
+
+/*
+ * Copyright (C) 2006-2016 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.c from QEMU AUDIO subsystem.
+ *
+ * QEMU Audio subsystem
+ *
+ * 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.
+ */
+#define LOG_GROUP LOG_GROUP_DRV_AUDIO
+#include <VBox/log.h>
+#include <VBox/vmm/pdm.h>
+#include <VBox/err.h>
+#include <VBox/vmm/mm.h>
+#include <VBox/vmm/pdmaudioifs.h>
+
+#include <iprt/alloc.h>
+#include <iprt/asm-math.h>
+#include <iprt/assert.h>
+#include <iprt/circbuf.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+
+#include "VBoxDD.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "DrvAudio.h"
+#include "AudioMixBuffer.h"
+
+static int drvAudioDestroyGstIn(PDRVAUDIO pThis, PPDMAUDIOGSTSTRMIN pGstStrmIn);
+
+static int drvAudioAllocHstIn(PDRVAUDIO pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg, PDMAUDIORECSOURCE enmRecSource, PPDMAUDIOHSTSTRMIN *ppHstStrmIn);
+static int drvAudioDestroyHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn);
+
+int drvAudioAddHstOut(PDRVAUDIO pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOHSTSTRMOUT *ppHstStrmOut)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    PPDMAUDIOHSTSTRMOUT pHstStrmOut;
+
+    int rc;
+    if (   conf.fixed_out.enabled /** @todo Get rid of these settings! */
+        && conf.fixed_out.greedy)
+    {
+        rc = drvAudioAllocHstOut(pThis, pszName, pCfg, &pHstStrmOut);
+    }
+    else
+        rc = VERR_NOT_FOUND;
+
+    if (RT_FAILURE(rc))
+    {
+        pHstStrmOut = drvAudioFindSpecificOut(pThis, NULL, pCfg);
+        if (!pHstStrmOut)
+        {
+            rc = drvAudioAllocHstOut(pThis, pszName, pCfg, &pHstStrmOut);
+            if (RT_FAILURE(rc))
+                pHstStrmOut = drvAudioFindAnyHstOut(pThis, NULL /* pHstStrmOut */);
+        }
+
+        rc = pHstStrmOut ? VINF_SUCCESS : rc;
+    }
+
+    if (RT_SUCCESS(rc))
+        *ppHstStrmOut = pHstStrmOut;
+
+    return rc;
+}
+
+static PDMAUDIOFMT drvAudioGetConfFormat(PCFGMNODE pCfgHandle, const char *pszKey,
+                                         PDMAUDIOFMT enmDefault, bool *pfDefault)
+{
+    if (   pCfgHandle == NULL
+        || pszKey == NULL)
+    {
+        *pfDefault = true;
+        return enmDefault;
+    }
+
+    char *pszValue = NULL;
+    int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
+    if (RT_FAILURE(rc))
+    {
+        *pfDefault = true;
+        return enmDefault;
+    }
+
+    PDMAUDIOFMT fmt = drvAudioHlpStringToFormat(pszValue);
+    if (fmt == AUD_FMT_INVALID)
+    {
+         *pfDefault = true;
+        return enmDefault;
+    }
+
+    *pfDefault = false;
+    return fmt;
+}
+
+static int drvAudioGetConfInt(PCFGMNODE pCfgHandle, const char *pszKey,
+                              int iDefault, bool *pfDefault)
+{
+
+    if (   pCfgHandle == NULL
+        || pszKey == NULL)
+    {
+        *pfDefault = true;
+        return iDefault;
+    }
+
+    uint64_t u64Data = 0;
+    int rc = CFGMR3QueryInteger(pCfgHandle, pszKey, &u64Data);
+    if (RT_FAILURE(rc))
+    {
+        *pfDefault = true;
+        return iDefault;
+
+    }
+
+    *pfDefault = false;
+    return u64Data;
+}
+
+static const char *drvAudioGetConfStr(PCFGMNODE pCfgHandle, const char *pszKey,
+                                      const char *pszDefault, bool *pfDefault)
+{
+    if (   pCfgHandle == NULL
+        || pszKey == NULL)
+    {
+        *pfDefault = true;
+        return pszDefault;
+    }
+
+    char *pszValue = NULL;
+    int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszKey, &pszValue);
+    if (RT_FAILURE(rc))
+    {
+        *pfDefault = true;
+        return pszDefault;
+    }
+
+    *pfDefault = false;
+    return pszValue;
+}
+
+static int drvAudioProcessOptions(PCFGMNODE pCfgHandle, const char *pszPrefix, struct audio_option *opt)
+{
+    AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszPrefix, VERR_INVALID_POINTER);
+    AssertPtrReturn(opt, VERR_INVALID_POINTER);
+
+    PCFGMNODE pCfgChildHandle = NULL;
+    PCFGMNODE pCfgChildChildHandle = NULL;
+
+   /* If pCfgHandle is NULL, let NULL be passed to get int and get string functions..
+    * The getter function will return default values.
+    */
+    if (pCfgHandle != NULL)
+    {
+       /* If its audio general setting, need to traverse to one child node.
+        * /Devices/ichac97/0/LUN#0/Config/Audio
+        */
+       if(!strncmp(pszPrefix, "AUDIO", 5)) /** @todo Use a \#define */
+       {
+            pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
+            if(pCfgChildHandle)
+                pCfgHandle = pCfgChildHandle;
+        }
+        else
+        {
+            /* If its driver specific configuration , then need to traverse two level deep child
+             * child nodes. for eg. in case of DirectSoundConfiguration item
+             * /Devices/ichac97/0/LUN#0/Config/Audio/DirectSoundConfig
+             */
+            pCfgChildHandle = CFGMR3GetFirstChild(pCfgHandle);
+            if (pCfgChildHandle)
+            {
+                pCfgChildChildHandle = CFGMR3GetFirstChild(pCfgChildHandle);
+                if (pCfgChildChildHandle)
+                    pCfgHandle = pCfgChildChildHandle;
+            }
+        }
+    }
+
+    for (; opt->name; opt++)
+    {
+        LogFlowFunc(("Option value pointer for `%s' is not set\n",
+                     opt->name));
+        if (!opt->valp) {
+            LogFlowFunc(("Option value pointer for `%s' is not set\n",
+                   opt->name));
+            continue;
+        }
+
+        bool fUseDefault;
+
+        switch (opt->tag)
+        {
+            case AUD_OPT_BOOL:
+            case AUD_OPT_INT:
+            {
+                int *intp = (int *)opt->valp;
+                *intp = drvAudioGetConfInt(pCfgHandle, opt->name, *intp, &fUseDefault);
+
+                break;
+            }
+
+            case AUD_OPT_FMT:
+            {
+                PDMAUDIOFMT *fmtp = (PDMAUDIOFMT *)opt->valp;
+                *fmtp = drvAudioGetConfFormat(pCfgHandle, opt->name, *fmtp, &fUseDefault);
+
+                break;
+            }
+
+            case AUD_OPT_STR:
+            {
+                const char **strp = (const char **)opt->valp;
+                *strp = drvAudioGetConfStr(pCfgHandle, opt->name, *strp, &fUseDefault);
+
+                break;
+            }
+
+            default:
+                LogFlowFunc(("Bad value tag for option `%s' - %d\n", opt->name, opt->tag));
+                fUseDefault = false;
+                break;
+        }
+
+        if (!opt->overridenp)
+            opt->overridenp = &opt->overriden;
+
+        *opt->overridenp = !fUseDefault;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static bool drvAudioStreamCfgIsValid(PPDMAUDIOSTREAMCFG pCfg)
+{
+    bool fValid = (   pCfg->cChannels == 1
+                   || pCfg->cChannels == 2); /* Either stereo (2) or mono (1), per stream. */
+
+    fValid |= (   pCfg->enmEndianness == PDMAUDIOENDIANNESS_LITTLE
+               || pCfg->enmEndianness == PDMAUDIOENDIANNESS_BIG);
+
+    if (fValid)
+    {
+        switch (pCfg->enmFormat)
+        {
+            case AUD_FMT_S8:
+            case AUD_FMT_U8:
+            case AUD_FMT_S16:
+            case AUD_FMT_U16:
+            case AUD_FMT_S32:
+            case AUD_FMT_U32:
+                break;
+            default:
+                fValid = false;
+                break;
+        }
+    }
+
+    /** @todo Check for defined frequencies supported. */
+    fValid |= pCfg->uHz > 0;
+
+#ifdef DEBUG
+    drvAudioStreamCfgPrint(pCfg);
+#endif
+
+    LogFlowFunc(("pCfg=%p, fValid=%RTbool\n", pCfg, fValid));
+    return fValid;
+}
+
+/**
+ * Clears a sample buffer by the given amount of audio samples.
+ *
+ * @return  IPRT status code.
+ * @param   pPCMProps               PCM properties to use for the buffer to clear.
+ * @param   pvBuf                   Buffer to clear.
+ * @param   cbBuf                   Size (in bytes) of the buffer.
+ * @param   cSamples                Number of audio samples to clear in the buffer.
+ */
+void DrvAudioClearBuf(PPDMPCMPROPS pPCMProps, void *pvBuf, size_t cbBuf, uint32_t cSamples)
+{
+    AssertPtrReturnVoid(pPCMProps);
+    AssertPtrReturnVoid(pvBuf);
+
+    if (!cbBuf || !cSamples)
+        return;
+
+    Log2Func(("pPCMInfo=%p, pvBuf=%p, cSamples=%RU32, fSigned=%RTbool, cBits=%RU8, cShift=%RU8\n",
+              pPCMProps, pvBuf, cSamples, pPCMProps->fSigned, pPCMProps->cBits, pPCMProps->cShift));
+
+    if (pPCMProps->fSigned)
+    {
+        memset(pvBuf, 0, cSamples << pPCMProps->cShift);
+    }
+    else
+    {
+        switch (pPCMProps->cBits)
+        {
+            case 8:
+            {
+                memset(pvBuf, 0x80, cSamples << pPCMProps->cShift);
+                break;
+            }
+
+            case 16:
+            {
+                uint16_t *p = (uint16_t *)pvBuf;
+                int shift = pPCMProps->cChannels - 1;
+                short s = INT16_MAX;
+
+                if (pPCMProps->fSwapEndian)
+                    s = RT_BSWAP_U16(s);
+
+                for (unsigned i = 0; i < cSamples << shift; i++)
+                    p[i] = s;
+
+                break;
+            }
+
+            case 32:
+            {
+                uint32_t *p = (uint32_t *)pvBuf;
+                int shift = pPCMProps->cChannels - 1;
+                int32_t s = INT32_MAX;
+
+                if (pPCMProps->fSwapEndian)
+                    s = RT_BSWAP_U32(s);
+
+                for (unsigned i = 0; i < cSamples << shift; i++)
+                    p[i] = s;
+
+                break;
+            }
+
+            default:
+            {
+                AssertMsgFailed(("Invalid bits: %RU8\n", pPCMProps->cBits));
+                break;
+            }
+        }
+    }
+}
+
+static int drvAudioControlHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn, PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+
+    int rc = RTCritSectEnter(&pHstStrmIn->CritSect);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        {
+            if (!(pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
+            {
+                rc = pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn, PDMAUDIOSTREAMCMD_ENABLE);
+                if (RT_SUCCESS(rc))
+                {
+                    pHstStrmIn->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
+                }
+                else
+                    LogFlowFunc(("Backend reported an error when opening input stream, rc=%Rrc\n", rc));
+            }
+            else
+                rc = VINF_SUCCESS;
+
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        {
+            if (pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
+            {
+                rc = pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn, PDMAUDIOSTREAMCMD_DISABLE);
+                if (RT_SUCCESS(rc))
+                {
+                    pHstStrmIn->fStatus = PDMAUDIOSTRMSTS_FLAG_NONE; /* Clear all. */
+                    AudioMixBufClear(&pHstStrmIn->MixBuf);
+                }
+                else
+                    LogFlowFunc(("Backend vetoed closing output stream, rc=%Rrc\n", rc));
+            }
+            else
+                rc = VINF_SUCCESS;
+
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_PAUSE:
+        {
+            if (!(pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED))
+            {
+                Assert(pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED);
+                rc = pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn, PDMAUDIOSTREAMCMD_PAUSE);
+                if (RT_SUCCESS(rc))
+                {
+                    LogFunc(("[%s] Pausing stream\n", pHstStrmIn->MixBuf.pszName));
+                    pHstStrmIn->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
+                }
+                else
+                    LogFlowFunc(("Backend vetoed pausing input stream, rc=%Rrc\n", rc));
+            }
+            else
+                rc = VINF_SUCCESS;
+
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_RESUME:
+        {
+            if (pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
+            {
+                Assert(pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED);
+                rc = pThis->pHostDrvAudio->pfnControlIn(pThis->pHostDrvAudio, pHstStrmIn, PDMAUDIOSTREAMCMD_RESUME);
+                if (RT_SUCCESS(rc))
+                {
+                    pHstStrmIn->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
+                    LogFunc(("[%s] Resumed stream\n", pHstStrmIn->MixBuf.pszName));
+                }
+                else
+                    LogFlowFunc(("Backend vetoed resuming input stream, rc=%Rrc\n", rc));
+            }
+            else
+                rc = VINF_SUCCESS;
+
+            break;
+        }
+
+        default:
+            AssertMsgFailed(("Command %ld not implemented\n", enmStreamCmd));
+            rc = VERR_NOT_IMPLEMENTED;
+            break;
+    }
+
+    int rc2 = RTCritSectLeave(&pHstStrmIn->CritSect);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    return rc;
+}
+
+static int drvAudioControlHstOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+
+    int rc = RTCritSectEnter(&pHstStrmOut->CritSect);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        {
+            if (!(pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED))
+            {
+                rc = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_ENABLE);
+                if (RT_SUCCESS(rc))
+                {
+                    Assert(!(pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE));
+                    pHstStrmOut->fStatus |= PDMAUDIOSTRMSTS_FLAG_ENABLED;
+                    LogFunc(("[%s] Enabled stream\n", pHstStrmOut->MixBuf.pszName));
+                }
+                else
+                    LogFlowFunc(("[%s] Backend reported an error when enabling output stream, rc=%Rrc\n",
+                                 pHstStrmOut->MixBuf.pszName, rc));
+            }
+            else
+                rc = VINF_SUCCESS;
+
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        {
+            if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
+            {
+                rc = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
+                if (RT_SUCCESS(rc))
+                {
+                    pHstStrmOut->fStatus = PDMAUDIOSTRMSTS_FLAG_NONE; /* Clear all. */
+                    AudioMixBufClear(&pHstStrmOut->MixBuf);
+
+                    LogFunc(("[%s] Disabled stream\n", pHstStrmOut->MixBuf.pszName));
+                }
+                else
+                    LogFlowFunc(("[%s] Backend vetoed disabling output stream, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, rc));
+            }
+            else
+                rc = VINF_SUCCESS;
+
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_PAUSE:
+        {
+            if (!(pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED))
+            {
+                Assert(pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED);
+                rc = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_PAUSE);
+                if (RT_SUCCESS(rc))
+                {
+                    pHstStrmOut->fStatus |= PDMAUDIOSTRMSTS_FLAG_PAUSED;
+                    LogFunc(("[%s] Pausing stream\n", pHstStrmOut->MixBuf.pszName));
+                }
+                else
+                    LogFlowFunc(("[%s] Backend vetoed pausing output stream, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, rc));
+            }
+            else
+                rc = VINF_SUCCESS;
+
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_RESUME:
+        {
+            if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PAUSED)
+            {
+                Assert(pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED);
+                rc = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_RESUME);
+                if (RT_SUCCESS(rc))
+                {
+                    pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PAUSED;
+                    LogFunc(("[%s] Resumed stream\n", pHstStrmOut->MixBuf.pszName));
+                }
+                else
+                    LogFlowFunc(("[%s] Backend vetoed resuming output stream, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, rc));
+            }
+            else
+                rc = VINF_SUCCESS;
+
+            break;
+        }
+
+        default:
+            AssertMsgFailed(("Command %ld not implemented\n", enmStreamCmd));
+            rc = VERR_NOT_IMPLEMENTED;
+            break;
+    }
+
+    int rc2 = RTCritSectLeave(&pHstStrmOut->CritSect);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    return rc;
+}
+
+int drvAudioDestroyHstOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+
+    LogFlowFunc(("%s\n", pHstStrmOut->MixBuf.pszName));
+
+    int rc;
+    if (RTListIsEmpty(&pHstStrmOut->lstGstStrmOut))
+    {
+        rc = pThis->pHostDrvAudio->pfnFiniOut(pThis->pHostDrvAudio, pHstStrmOut);
+        if (RT_SUCCESS(rc))
+        {
+            drvAudioHstOutFreeRes(pHstStrmOut);
+
+            /* Remove from driver instance list. */
+            RTListNodeRemove(&pHstStrmOut->Node);
+
+            if (RTCritSectIsInitialized(&pHstStrmOut->CritSect))
+            {
+                int rc2 = RTCritSectDelete(&pHstStrmOut->CritSect);
+                AssertRC(rc2);
+            }
+
+            RTMemFree(pHstStrmOut);
+            pThis->cFreeOutputStreams++;
+            return VINF_SUCCESS;
+        }
+    }
+    else
+    {
+        rc = VERR_ACCESS_DENIED;
+        LogFlowFunc(("[%s] Still is being used, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, rc));
+    }
+
+    return rc;
+}
+
+int drvAudioDestroyGstOut(PDRVAUDIO pThis, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+    if (!pGstStrmOut)
+        return VINF_SUCCESS;
+
+    if (pGstStrmOut->State.cRefs > 1) /* Do other objects still have a reference to it? Bail out. */
+        return VERR_WRONG_ORDER;
+
+    drvAudioGstOutFreeRes(pGstStrmOut);
+
+    if (pGstStrmOut->pHstStrmOut)
+    {
+        /* Unregister from parent first. */
+        RTListNodeRemove(&pGstStrmOut->Node);
+
+        /* Try destroying the associated host output stream. This could
+         * be skipped if there are other guest output streams with this
+         * host stream. */
+        drvAudioDestroyHstOut(pThis, pGstStrmOut->pHstStrmOut);
+    }
+
+    RTMemFree(pGstStrmOut);
+
+    return VINF_SUCCESS;
+}
+
+PPDMAUDIOHSTSTRMIN drvAudioFindNextHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    if (pHstStrmIn)
+    {
+        if (RTListNodeIsLast(&pThis->lstHstStrmIn, &pHstStrmIn->Node))
+            return NULL;
+
+        return RTListNodeGetNext(&pHstStrmIn->Node, PDMAUDIOHSTSTRMIN, Node);
+    }
+
+    return RTListGetFirst(&pThis->lstHstStrmIn, PDMAUDIOHSTSTRMIN, Node);
+}
+
+PPDMAUDIOHSTSTRMIN drvAudioFindNextEnabledHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    while ((pHstStrmIn = drvAudioFindNextHstIn(pThis, pHstStrmIn)))
+        if (pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
+            return pHstStrmIn;
+
+    return NULL;
+}
+
+PPDMAUDIOHSTSTRMIN drvAudioFindNextEqHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                           PPDMAUDIOSTREAMCFG pCfg)
+{
+    while ((pHstStrmIn = drvAudioFindNextHstIn(pThis, pHstStrmIn)))
+        if (drvAudioPCMPropsAreEqual(&pHstStrmIn->Props, pCfg))
+            return pHstStrmIn;
+
+    return NULL;
+}
+
+static int drvAudioHstInAdd(PDRVAUDIO pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg, PDMAUDIORECSOURCE enmRecSource,
+                            PPDMAUDIOHSTSTRMIN *ppHstStrmIn)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+    AssertPtrReturn(ppHstStrmIn, VERR_INVALID_POINTER);
+
+    PPDMAUDIOHSTSTRMIN pHstStrmIn;
+    int rc = drvAudioAllocHstIn(pThis, pszName, pCfg, enmRecSource, &pHstStrmIn);
+    if (RT_SUCCESS(rc))
+        *ppHstStrmIn = pHstStrmIn;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+int drvAudioGstOutInit(PPDMAUDIOGSTSTRMOUT pGstStrmOut, PPDMAUDIOHSTSTRMOUT pHostStrmOut,
+                       const char *pszName, PPDMAUDIOSTREAMCFG pCfg)
+{
+    AssertPtrReturn(pGstStrmOut,  VERR_INVALID_POINTER);
+    AssertPtrReturn(pHostStrmOut, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName,      VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg,         VERR_INVALID_POINTER);
+
+    int rc = DrvAudioStreamCfgToProps(pCfg, &pGstStrmOut->Props);
+    if (RT_SUCCESS(rc))
+    {
+        char *pszTemp;
+        if (RTStrAPrintf(&pszTemp, "%s (Guest)", pszName) <= 0)
+            return VERR_NO_MEMORY;
+
+        rc = AudioMixBufInit(&pGstStrmOut->MixBuf, pszTemp, &pGstStrmOut->Props, AudioMixBufSize(&pHostStrmOut->MixBuf));
+        if (RT_SUCCESS(rc))
+            rc = AudioMixBufLinkTo(&pGstStrmOut->MixBuf, &pHostStrmOut->MixBuf);
+
+        RTStrFree(pszTemp);
+
+        if (RT_SUCCESS(rc))
+        {
+            pGstStrmOut->State.cRefs   = 1;
+            pGstStrmOut->State.fActive = false;
+            pGstStrmOut->State.fEmpty  = true;
+
+            pGstStrmOut->State.pszName = RTStrDup(pszName);
+            if (!pGstStrmOut->State.pszName)
+                return VERR_NO_MEMORY;
+
+            pGstStrmOut->pHstStrmOut = pHostStrmOut;
+        }
+    }
+
+    LogFlowFunc(("pszName=%s, rc=%Rrc\n", pszName, rc));
+    return rc;
+}
+
+int drvAudioAllocHstOut(PDRVAUDIO pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOHSTSTRMOUT *ppHstStrmOut)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    if (!pThis->cFreeOutputStreams)
+    {
+        LogFlowFunc(("Maximum number of host output streams reached\n"));
+        return VERR_NO_MORE_HANDLES;
+    }
+
+    /* Validate backend configuration. */
+    if (!pThis->BackendCfg.cbStreamOut)
+    {
+        LogFlowFunc(("Backend output configuration not valid, bailing out\n"));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    PPDMAUDIOHSTSTRMOUT pHstStrmOut = (PPDMAUDIOHSTSTRMOUT)RTMemAllocZ(pThis->BackendCfg.cbStreamOut);
+    if (!pHstStrmOut)
+    {
+        LogFlowFunc(("Error allocating host output stream with %zu bytes\n",
+                     pThis->BackendCfg.cbStreamOut));
+        return VERR_NO_MEMORY;
+    }
+
+    int rc;
+    bool fInitialized = false;
+
+    do
+    {
+        RTListInit(&pHstStrmOut->lstGstStrmOut);
+
+        uint32_t cSamples;
+        rc = pThis->pHostDrvAudio->pfnInitOut(pThis->pHostDrvAudio, pHstStrmOut, pCfg, &cSamples);
+        if (RT_FAILURE(rc))
+        {
+            LogFlowFunc(("Initializing host backend failed with rc=%Rrc\n", rc));
+            break;
+        }
+
+        fInitialized = true;
+
+        char *pszTemp;
+        if (RTStrAPrintf(&pszTemp, "%s (Host)", pszName) <= 0)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        rc = AudioMixBufInit(&pHstStrmOut->MixBuf, pszTemp, &pHstStrmOut->Props, cSamples);
+        if (RT_SUCCESS(rc))
+            rc = RTCritSectInit(&pHstStrmOut->CritSect);
+
+        if (RT_SUCCESS(rc))
+        {
+            RTListPrepend(&pThis->lstHstStrmOut, &pHstStrmOut->Node);
+            pThis->cFreeOutputStreams--;
+        }
+
+        RTStrFree(pszTemp);
+
+    } while (0);
+
+    if (RT_FAILURE(rc))
+    {
+        if (fInitialized)
+        {
+            int rc2 = pThis->pHostDrvAudio->pfnFiniOut(pThis->pHostDrvAudio, pHstStrmOut);
+            AssertRC(rc2);
+        }
+
+        drvAudioHstOutFreeRes(pHstStrmOut);
+        RTMemFree(pHstStrmOut);
+    }
+    else
+        *ppHstStrmOut = pHstStrmOut;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+int drvAudioCreateStreamPairOut(PDRVAUDIO pThis, const char *pszName,
+                                PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMOUT *ppGstStrmOut)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    /*
+     * Try figuring out which audio stream configuration this backend
+     * should use. If fixed output is enabled the backend will be tied
+     * to a fixed rate (in Hz, among other parameters), regardless of
+     * what the backend could do else.
+     */
+    PPDMAUDIOSTREAMCFG pBackendCfg;
+    if (conf.fixed_out.enabled)
+        pBackendCfg = &conf.fixed_out.settings;
+    else
+        pBackendCfg = pCfg;
+
+    AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
+
+    LogFlowFunc(("Using fixed audio output settings: %RTbool\n",
+                 RT_BOOL(conf.fixed_out.enabled)));
+
+    PPDMAUDIOGSTSTRMOUT pGstStrmOut =
+        (PPDMAUDIOGSTSTRMOUT)RTMemAllocZ(sizeof(PDMAUDIOGSTSTRMOUT));
+    if (!pGstStrmOut)
+    {
+        LogFlowFunc(("Failed to allocate memory for guest output stream \"%s\"\n", pszName));
+        return VERR_NO_MEMORY;
+    }
+
+    /*
+     * The host stream always will get the backend audio stream configuration.
+     */
+    PPDMAUDIOHSTSTRMOUT pHstStrmOut;
+    int rc = drvAudioAddHstOut(pThis, pszName, pBackendCfg, &pHstStrmOut);
+    if (RT_FAILURE(rc))
+    {
+        LogFlowFunc(("Error adding host output stream \"%s\", rc=%Rrc\n", pszName, rc));
+
+        RTMemFree(pGstStrmOut);
+        return rc;
+    }
+
+    /*
+     * The guest stream always will get the audio stream configuration told
+     * by the device emulation (which in turn was/could be set by the guest OS).
+     */
+    rc = drvAudioGstOutInit(pGstStrmOut, pHstStrmOut, pszName, pCfg);
+    if (RT_SUCCESS(rc))
+    {
+        RTListPrepend(&pHstStrmOut->lstGstStrmOut, &pGstStrmOut->Node);
+
+        if (ppGstStrmOut)
+            *ppGstStrmOut = pGstStrmOut;
+    }
+
+    if (RT_FAILURE(rc))
+        drvAudioDestroyGstOut(pThis, pGstStrmOut);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static int drvAudioCreateStreamPairIn(PDRVAUDIO pThis, const char *pszName, PDMAUDIORECSOURCE enmRecSource,
+                                      PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMIN *ppGstStrmIn)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+
+/*
+     * Try figuring out which audio stream configuration this backend
+     * should use for the audio input data. If fixed input is enabled
+     * the backend will be tied to a fixed rate (in Hz, among other parameters),
+     * regardless of what the backend initially wanted to use.
+     */
+    PPDMAUDIOSTREAMCFG pBackendCfg;
+    if (conf.fixed_in.enabled)
+        pBackendCfg = &conf.fixed_in.settings;
+    else
+        pBackendCfg = pCfg;
+
+    AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
+
+    LogFlowFunc(("Using fixed audio input settings: %RTbool\n",
+                 RT_BOOL(conf.fixed_in.enabled)));
+
+    PPDMAUDIOGSTSTRMIN pGstStrmIn = (PPDMAUDIOGSTSTRMIN)RTMemAllocZ(sizeof(PDMAUDIOGSTSTRMIN));
+    if (!pGstStrmIn)
+        return VERR_NO_MEMORY;
+
+    /*
+     * The host stream always will get the backend audio stream configuration.
+     */
+    PPDMAUDIOHSTSTRMIN pHstStrmIn;
+    int rc = drvAudioHstInAdd(pThis, pszName, pBackendCfg, enmRecSource, &pHstStrmIn);
+    if (RT_FAILURE(rc))
+    {
+        LogFunc(("Failed to add host audio input stream \"%s\", rc=%Rrc\n", pszName, rc));
+
+        RTMemFree(pGstStrmIn);
+        return rc;
+    }
+
+    /*
+     * The guest stream always will get the audio stream configuration told
+     * by the device emulation (which in turn was/could be set by the guest OS).
+     */
+    rc = drvAudioGstInInit(pGstStrmIn, pHstStrmIn, pszName, pCfg);
+    if (RT_SUCCESS(rc))
+    {
+        pHstStrmIn->pGstStrmIn = pGstStrmIn;
+
+        if (ppGstStrmIn)
+            *ppGstStrmIn = pGstStrmIn;
+    }
+    else
+        drvAudioDestroyGstIn(pThis, pGstStrmIn);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+/**
+ * Initializes a guest input stream.
+ *
+ * @return  IPRT status code.
+ * @param   pGstStrmIn          Pointer to guest stream to initialize.
+ * @param   pHstStrmIn          Pointer to host input stream to associate this guest
+ *                              stream with.
+ * @param   pszName             Pointer to stream name to use for this stream.
+ * @param   pCfg                Pointer to stream configuration to use.
+ */
+int drvAudioGstInInit(PPDMAUDIOGSTSTRMIN pGstStrmIn, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                      const char *pszName, PPDMAUDIOSTREAMCFG pCfg)
+{
+    AssertPtrReturn(pGstStrmIn, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    int rc = DrvAudioStreamCfgToProps(pCfg, &pGstStrmIn->Props);
+    if (RT_SUCCESS(rc))
+    {
+        char *pszTemp;
+        if (RTStrAPrintf(&pszTemp, "%s (Guest)", pszName) <= 0)
+            return VERR_NO_MEMORY;
+
+        rc = AudioMixBufInit(&pGstStrmIn->MixBuf, pszTemp, &pGstStrmIn->Props, AudioMixBufSize(&pHstStrmIn->MixBuf));
+        if (RT_SUCCESS(rc))
+            rc = AudioMixBufLinkTo(&pHstStrmIn->MixBuf, &pGstStrmIn->MixBuf);
+
+        RTStrFree(pszTemp);
+
+        if (RT_SUCCESS(rc))
+        {
+#ifdef DEBUG
+            drvAudioStreamCfgPrint(pCfg);
+#endif
+            pGstStrmIn->State.cRefs   = 1;
+            pGstStrmIn->State.fActive = false;
+            pGstStrmIn->State.fEmpty  = true;
+
+            pGstStrmIn->State.pszName = RTStrDup(pszName);
+            if (!pGstStrmIn->State.pszName)
+                return VERR_NO_MEMORY;
+
+            pGstStrmIn->pHstStrmIn = pHstStrmIn;
+        }
+    }
+
+    LogFlowFunc(("pszName=%s, rc=%Rrc\n", pszName, rc));
+    return rc;
+}
+
+static int drvAudioAllocHstIn(PDRVAUDIO pThis, const char *pszName, PPDMAUDIOSTREAMCFG pCfg,
+                              PDMAUDIORECSOURCE enmRecSource, PPDMAUDIOHSTSTRMIN *ppHstStrmIn)
+{
+    if (!pThis->cFreeInputStreams)
+    {
+        LogFlowFunc(("No more input streams free to use, bailing out\n"));
+        return VERR_NO_MORE_HANDLES;
+    }
+
+    /* Validate backend configuration. */
+    if (!pThis->BackendCfg.cbStreamIn)
+    {
+        LogFlowFunc(("Backend input configuration not valid, bailing out\n"));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    PPDMAUDIOHSTSTRMIN pHstStrmIn =
+        (PPDMAUDIOHSTSTRMIN)RTMemAllocZ(pThis->BackendCfg.cbStreamIn);
+    if (!pHstStrmIn)
+    {
+        LogFlowFunc(("Error allocating host innput stream with %RU32 bytes\n",
+                     pThis->BackendCfg.cbStreamOut));
+        return VERR_NO_MEMORY;
+    }
+
+    int rc;
+    bool fInitialized = false;
+
+    do
+    {
+        uint32_t cSamples;
+        rc = pThis->pHostDrvAudio->pfnInitIn(pThis->pHostDrvAudio, pHstStrmIn,
+                                             pCfg, enmRecSource, &cSamples);
+        if (RT_FAILURE(rc))
+        {
+            LogFlowFunc(("Initializing host backend failed with rc=%Rrc\n", rc));
+            break;
+        }
+
+        fInitialized = true;
+
+        char *pszTemp;
+        if (RTStrAPrintf(&pszTemp, "%s (Host)", pszName) <= 0)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        rc = AudioMixBufInit(&pHstStrmIn->MixBuf, pszTemp, &pHstStrmIn->Props, cSamples);
+        if (RT_SUCCESS(rc))
+            rc = RTCritSectInit(&pHstStrmIn->CritSect);
+
+        if (RT_SUCCESS(rc))
+        {
+            RTListPrepend(&pThis->lstHstStrmIn, &pHstStrmIn->Node);
+            pThis->cFreeInputStreams--;
+        }
+
+        RTStrFree(pszTemp);
+
+    } while (0);
+
+    if (RT_FAILURE(rc))
+    {
+        if (fInitialized)
+        {
+            int rc2 = pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio,
+                                                      pHstStrmIn);
+            AssertRC(rc2);
+        }
+
+        drvAudioHstInFreeRes(pHstStrmIn);
+        RTMemFree(pHstStrmIn);
+    }
+    else
+        *ppHstStrmIn = pHstStrmIn;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+/**
+ * Writes VM audio output data from the guest stream into the host stream.
+ * The attached host driver backend then will play out the audio in a
+ * later step then.
+ *
+ * @return  IPRT status code.
+ * @return  int
+ * @param   pThis
+ * @param   pGstStrmOut
+ * @param   pvBuf
+ * @param   cbBuf
+ * @param   pcbWritten
+ */
+static DECLCALLBACK(int) drvAudioWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut,
+                                       const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
+{
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+    AssertPtrReturn(pGstStrmOut, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvBuf,       VERR_INVALID_POINTER);
+    AssertReturn(cbBuf,          VERR_INVALID_PARAMETER);
+    /* pcbWritten is optional. */
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
+    {
+        rc = RTCritSectLeave(&pThis->CritSect);
+        AssertRC(rc);
+
+        return VERR_NOT_AVAILABLE;
+    }
+
+    PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+
+    AssertMsg(pGstStrmOut->pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
+              ("Writing to disabled host output stream \"%s\" not possible\n",
+              pHstStrmOut->MixBuf.pszName));
+
+    if (!AudioMixBufFreeBytes(&pGstStrmOut->MixBuf))
+    {
+        if (pcbWritten)
+            *pcbWritten = 0;
+
+        return RTCritSectLeave(&pThis->CritSect);
+    }
+
+    /*
+     * First, write data from the device emulation into our
+     * guest mixing buffer.
+     */
+    uint32_t cWritten;
+    rc = AudioMixBufWriteAt(&pGstStrmOut->MixBuf, 0 /* Offset in samples */, pvBuf, cbBuf, &cWritten);
+
+    /*
+     * Second, mix the guest mixing buffer with the host mixing
+     * buffer so that the host backend can play the data lateron.
+     */
+    uint32_t cMixed;
+    if (   RT_SUCCESS(rc)
+        && cWritten)
+    {
+        rc = AudioMixBufMixToParent(&pGstStrmOut->MixBuf, cWritten, &cMixed);
+    }
+    else
+        cMixed = 0;
+
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Return the number of samples which actually have been mixed
+         * down to the parent, regardless how much samples were written
+         * into the children buffer.
+         */
+        if (pcbWritten)
+            *pcbWritten = AUDIOMIXBUF_S2B(&pGstStrmOut->MixBuf, cMixed);
+    }
+
+    LogFlowFunc(("%s -> %s: Written pvBuf=%p, cbBuf=%RU32, cWritten=%RU32 (%RU32 bytes), cMixed=%RU32, rc=%Rrc\n",
+                 pGstStrmOut->MixBuf.pszName, pHstStrmOut->MixBuf.pszName, pvBuf, cbBuf, cWritten,
+                 AUDIOMIXBUF_S2B(&pGstStrmOut->MixBuf, cWritten), cMixed, rc));
+
+    int rc2 = RTCritSectLeave(&pThis->CritSect);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    return rc;
+}
+
+PPDMAUDIOHSTSTRMOUT drvAudioFindAnyHstOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    if (pHstStrmOut)
+    {
+        if (RTListNodeIsLast(&pThis->lstHstStrmOut, &pHstStrmOut->Node))
+            return NULL;
+
+        return RTListNodeGetNext(&pHstStrmOut->Node, PDMAUDIOHSTSTRMOUT, Node);
+    }
+
+    return RTListGetFirst(&pThis->lstHstStrmOut, PDMAUDIOHSTSTRMOUT, Node);
+}
+
+PPDMAUDIOHSTSTRMOUT drvAudioHstFindAnyEnabledOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHostStrmOut)
+{
+    while ((pHostStrmOut = drvAudioFindAnyHstOut(pThis, pHostStrmOut)))
+    {
+        if (pHostStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
+            return pHostStrmOut;
+    }
+
+    return NULL;
+}
+
+PPDMAUDIOHSTSTRMOUT drvAudioFindSpecificOut(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                            PPDMAUDIOSTREAMCFG pCfg)
+{
+    while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
+    {
+        if (drvAudioPCMPropsAreEqual(&pHstStrmOut->Props, pCfg))
+            return pHstStrmOut;
+    }
+
+    return NULL;
+}
+
+int drvAudioDestroyHstIn(PDRVAUDIO pThis, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+
+    LogFlowFunc(("%s\n", pHstStrmIn->MixBuf.pszName));
+
+    int rc;
+    if (!pHstStrmIn->pGstStrmIn) /* No parent anymore? */
+    {
+        rc = pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio, pHstStrmIn);
+        if (RT_SUCCESS(rc))
+        {
+            drvAudioHstInFreeRes(pHstStrmIn);
+
+            if (RTCritSectIsInitialized(&pHstStrmIn->CritSect))
+            {
+                int rc2 = RTCritSectDelete(&pHstStrmIn->CritSect);
+                AssertRC(rc2);
+            }
+
+            /* Remove from driver instance list. */
+            RTListNodeRemove(&pHstStrmIn->Node);
+
+            RTMemFree(pHstStrmIn);
+            pThis->cFreeInputStreams++;
+        }
+    }
+    else
+    {
+        rc = VERR_ACCESS_DENIED;
+        LogFlowFunc(("[%s] Still is being used, rc=%Rrc\n", pHstStrmIn->MixBuf.pszName, rc));
+    }
+
+    return rc;
+}
+
+static int drvAudioDestroyGstIn(PDRVAUDIO pThis, PPDMAUDIOGSTSTRMIN pGstStrmIn)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+    LogFlowFunc(("%s\n", pGstStrmIn->MixBuf.pszName));
+
+    if (!pGstStrmIn)
+        return VINF_SUCCESS;
+
+    if (pGstStrmIn->State.cRefs > 1) /* Do other objects still have a reference to it? Bail out. */
+        return VERR_WRONG_ORDER;
+
+    drvAudioGstInFreeRes(pGstStrmIn);
+
+    if (pGstStrmIn->pHstStrmIn)
+    {
+        /* Unlink child. */
+        pGstStrmIn->pHstStrmIn->pGstStrmIn = NULL;
+
+        /* Try destroying the associated host input stream. This could
+         * be skipped if there are other guest input streams with this
+         * host stream. */
+        drvAudioDestroyHstIn(pThis, pGstStrmIn->pHstStrmIn);
+    }
+
+    RTMemFree(pGstStrmIn);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvAudioQueryStatus(PPDMIAUDIOCONNECTOR pInterface,
+                                             uint32_t *pcbAvailIn, uint32_t *pcbFreeOut,
+                                             uint32_t *pcSamplesLive)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    /* pcbAvailIn is optional. */
+    /* pcbFreeOut is optional. */
+    /* pcSamplesLive is optional. */
+
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /*
+     * Playback.
+     */
+    uint32_t cSamplesLive = 0;
+    uint32_t cbFreeOut    = UINT32_MAX;
+
+    PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
+    while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
+    {
+        cSamplesLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
+
+        /* Has this stream marked as disabled but there still were guest streams relying
+         * on it? Check if this stream now can be closed and do so, if possible. */
+        if (   (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
+            && !cSamplesLive)
+        {
+            /* Stop playing the current (pending) stream. */
+            int rc2 = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
+            if (RT_SUCCESS(rc2))
+            {
+                pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
+
+                LogFunc(("[%s] Disabling stream\n", pHstStrmOut->MixBuf.pszName));
+            }
+            else
+                LogFunc(("[%s] Backend vetoed against closing output stream, rc=%Rrc\n", pHstStrmOut->MixBuf.pszName, rc2));
+
+            continue;
+        }
+
+        LogFlowFunc(("[%s] cSamplesLive=%RU32\n", pHstStrmOut->MixBuf.pszName, cSamplesLive));
+
+        /*
+         * No live samples to play at the moment?
+         *
+         * Tell the device emulation for each connected guest stream how many
+         * bytes are free so that the device emulation can continue writing data to
+         * these streams.
+         */
+        PPDMAUDIOGSTSTRMOUT pGstStrmOut;
+        uint32_t cbFree2 = UINT32_MAX;
+        RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
+        {
+            if (pGstStrmOut->State.fActive)
+            {
+                /* Tell the sound device emulation how many samples are free
+                 * so that it can start writing PCM data to us. */
+                cbFree2 = RT_MIN(cbFree2, AUDIOMIXBUF_S2B_RATIO(&pGstStrmOut->MixBuf,
+                                                                AudioMixBufFree(&pGstStrmOut->MixBuf)));
+#ifdef DEBUG_andy
+                LogFlowFunc(("\t[%s] cbFreeOut=%RU32\n", pGstStrmOut->MixBuf.pszName, cbFree2));
+#endif
+            }
+        }
+
+        cbFreeOut = RT_MIN(cbFreeOut, cbFree2);
+    }
+
+    /*
+     * Recording.
+     */
+    uint32_t cbAvailIn = 0;
+
+    PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
+    while ((pHstStrmIn = drvAudioFindNextEnabledHstIn(pThis, pHstStrmIn)))
+    {
+        /* Call the host backend to capture the audio input data. */
+        uint32_t cSamplesCaptured;
+        int rc2 = pThis->pHostDrvAudio->pfnCaptureIn(pThis->pHostDrvAudio, pHstStrmIn,
+                                                     &cSamplesCaptured);
+        if (RT_FAILURE(rc2))
+            continue;
+
+        PPDMAUDIOGSTSTRMIN pGstStrmIn = pHstStrmIn->pGstStrmIn;
+        AssertPtrBreak(pGstStrmIn);
+
+        if (pGstStrmIn->State.fActive)
+        {
+            cbAvailIn = RT_MAX(cbAvailIn, AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf,
+                                                          AudioMixBufMixed(&pHstStrmIn->MixBuf)));
+#ifdef DEBUG_andy
+            LogFlowFunc(("\t[%s] cbAvailIn=%RU32\n", pHstStrmIn->MixBuf.pszName, cbAvailIn));
+#endif
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        if (cbFreeOut == UINT32_MAX)
+            cbFreeOut = 0;
+
+        if (pcbAvailIn)
+            *pcbAvailIn = cbAvailIn;
+
+        if (pcbFreeOut)
+            *pcbFreeOut = cbFreeOut;
+
+        if (pcSamplesLive)
+            *pcSamplesLive = cSamplesLive;
+    }
+
+    int rc2 = RTCritSectLeave(&pThis->CritSect);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    if (RT_FAILURE(rc))
+        LogFlowFuncLeaveRC(rc);
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvAudioPlayOut(PPDMIAUDIOCONNECTOR pInterface, uint32_t *pcSamplesPlayed)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    /* pcSamplesPlayed is optional. */
+
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /* Backend output (temporarily) disabled / unavailable? */
+    if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_OUT))
+    {
+        rc = pThis->pHostDrvAudio->pfnGetConf(pThis->pHostDrvAudio, &pThis->BackendCfg);
+        AssertRC(rc);
+
+        if (!pThis->BackendCfg.cMaxHstStrmsOut)
+        {
+            int rc2 = RTCritSectLeave(&pThis->CritSect);
+            AssertRC(rc2);
+
+            return VERR_NOT_AVAILABLE;
+        }
+    }
+
+    /*
+     * Process all enabled host output streams.
+     */
+    uint32_t            cSamplesPlayedMax = 0;
+    PPDMAUDIOHSTSTRMOUT pHstStrmOut       = NULL;
+    while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
+    {
+#if 0
+        uint32_t cStreamsLive;
+        uint32_t cSamplesLive = drvAudioHstOutSamplesLive(pHstStrmOut, &cStreamsLive);
+        if (!cStreamsLive)
+            cSamplesLive = 0;
+
+        /* Has this stream marked as disabled but there still were guest streams relying
+         * on it? Check if this stream now can be closed and do so, if possible. */
+        if (   pHstStrmOut->fPendingDisable
+            && !cStreamsLive)
+        {
+            /* Stop playing the current (pending) stream. */
+            int rc2 = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut,
+                                                          PDMAUDIOSTREAMCMD_DISABLE);
+            if (RT_SUCCESS(rc2))
+            {
+                pHstStrmOut->fEnabled        = false;
+                pHstStrmOut->fPendingDisable = false;
+
+                LogFunc(("\t%p: Disabling stream\n", pHstStrmOut));
+            }
+            else
+                LogFunc(("\t%p: Backend vetoed against closing output stream, rc=%Rrc\n",
+                         pHstStrmOut, rc2));
+
+            continue;
+        }
+#endif
+
+        uint32_t cSamplesPlayed = 0;
+        int rc2 = pThis->pHostDrvAudio->pfnPlayOut(pThis->pHostDrvAudio, pHstStrmOut, &cSamplesPlayed);
+        if (RT_FAILURE(rc2))
+        {
+            rc2 = pThis->pHostDrvAudio->pfnControlOut(pThis->pHostDrvAudio, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
+            AssertRC(rc2);
+        }
+        else
+            cSamplesPlayedMax = RT_MAX(cSamplesPlayed, cSamplesPlayedMax);
+
+        LogFlowFunc(("\t[%s] cSamplesPlayed=%RU32, cSamplesPlayedMax=%RU32, rc=%Rrc\n",
+                     pHstStrmOut->MixBuf.pszName, cSamplesPlayed, cSamplesPlayedMax, rc2));
+
+        bool fNeedsCleanup = false;
+
+        PPDMAUDIOGSTSTRMOUT pGstStrmOut;
+        RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
+        {
+            if (   !pGstStrmOut->State.fActive
+                && pGstStrmOut->State.fEmpty)
+                continue;
+
+            if (AudioMixBufIsEmpty(&pGstStrmOut->MixBuf))
+            {
+                pGstStrmOut->State.fEmpty = true;
+                fNeedsCleanup |= !pGstStrmOut->State.fActive;
+            }
+        }
+
+        if (fNeedsCleanup)
+        {
+            RTListForEach(&pHstStrmOut->lstGstStrmOut, pGstStrmOut, PDMAUDIOGSTSTRMOUT, Node)
+            {
+                if (!pGstStrmOut->State.fActive)
+                    drvAudioDestroyGstOut(pThis, pGstStrmOut);
+            }
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pcSamplesPlayed)
+            *pcSamplesPlayed = cSamplesPlayedMax;
+    }
+
+    int rc2 = RTCritSectLeave(&pThis->CritSect);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    if (RT_FAILURE(rc))
+        LogFlowFuncLeaveRC(rc);
+
+    return rc;
+}
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+static PPDMAUDIOCALLBACK drvAudioCallbackDuplicate(PPDMAUDIOCALLBACK pCB)
+{
+    PPDMAUDIOCALLBACK pCBCopy = (PPDMAUDIOCALLBACK)RTMemDup((void *)pCB, sizeof(PDMAUDIOCALLBACK));
+    if (!pCBCopy)
+        return NULL;
+
+    if (pCB->pvCtx)
+    {
+        pCBCopy->pvCtx = RTMemDup(pCB->pvCtx, pCB->cbCtx);
+        if (!pCBCopy->pvCtx)
+        {
+            RTMemFree(pCBCopy);
+            return NULL;
+        }
+
+        pCBCopy->cbCtx = pCB->cbCtx;
+    }
+
+    return pCBCopy;
+}
+
+static void drvAudioCallbackDestroy(PPDMAUDIOCALLBACK pCB)
+{
+    if (!pCB)
+        return;
+
+    RTListNodeRemove(&pCB->Node);
+    if (pCB->pvCtx)
+    {
+        Assert(pCB->cbCtx);
+        RTMemFree(pCB->pvCtx);
+    }
+    RTMemFree(pCB);
+}
+
+static DECLCALLBACK(int) drvAudioRegisterCallbacks(PPDMIAUDIOCONNECTOR pInterface,
+                                                   PPDMAUDIOCALLBACK paCallbacks, size_t cCallbacks)
+{
+    AssertPtrReturn(pInterface,  VERR_INVALID_POINTER);
+    AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER);
+    AssertReturn(cCallbacks,     VERR_INVALID_PARAMETER);
+
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    for (size_t i = 0; i < cCallbacks; i++)
+    {
+        PPDMAUDIOCALLBACK pCB = drvAudioCallbackDuplicate(&paCallbacks[i]);
+        if (!pCB)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        switch (pCB->enmType)
+        {
+            case PDMAUDIOCALLBACKTYPE_INPUT:
+                RTListAppend(&pThis->lstCBIn, &pCB->Node);
+                break;
+
+            case PDMAUDIOCALLBACKTYPE_OUTPUT:
+                RTListAppend(&pThis->lstCBOut, &pCB->Node);
+                break;
+
+            default:
+                AssertMsgFailed(("Not supported\n"));
+                break;
+        }
+    }
+
+    /** @todo Undo allocations on error. */
+
+    int rc2 = RTCritSectLeave(&pThis->CritSect);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvAudioCallback(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIOCALLBACKTYPE enmType,
+                                          void *pvUser, size_t cbUser)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvUser,     VERR_INVALID_POINTER);
+    AssertReturn(cbUser,        VERR_INVALID_PARAMETER);
+
+    PDRVAUDIO     pThis       = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+    PRTLISTANCHOR pListAnchor = NULL;
+
+    switch (enmType)
+    {
+        case PDMAUDIOCALLBACKTYPE_INPUT:
+            pListAnchor = &pThis->lstCBIn;
+            break;
+
+        case PDMAUDIOCALLBACKTYPE_OUTPUT:
+            pListAnchor = &pThis->lstCBOut;
+            break;
+
+        default:
+            AssertMsgFailed(("Not supported\n"));
+            break;
+    }
+
+    if (pListAnchor)
+    {
+        PPDMAUDIOCALLBACK pCB;
+        RTListForEach(pListAnchor, pCB, PDMAUDIOCALLBACK, Node)
+        {
+            Assert(pCB->enmType == enmType);
+            pCB->pfnCallback(enmType, pCB->pvCtx, pCB->cbCtx, pvUser, cbUser);
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+#endif
+
+static int drvAudioHostInit(PCFGMNODE pCfgHandle, PDRVAUDIO pThis)
+{
+    /* pCfgHandle is optional. */
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+    NOREF(pCfgHandle);
+
+    LogFlowFuncEnter();
+
+    AssertPtr(pThis->pHostDrvAudio);
+    int rc = pThis->pHostDrvAudio->pfnInit(pThis->pHostDrvAudio);
+    if (RT_FAILURE(rc))
+    {
+        LogFlowFunc(("Initialization of lower driver failed with rc=%Rrc\n", rc));
+        return rc;
+    }
+
+    /* Get the configuration data from backend. */
+    rc = pThis->pHostDrvAudio->pfnGetConf(pThis->pHostDrvAudio, &pThis->BackendCfg);
+    if (RT_FAILURE(rc))
+    {
+        LogFlowFunc(("Getting backend configuration failed with rc=%Rrc\n", rc));
+        return rc;
+    }
+
+    uint32_t cMaxHstStrmsOut = pThis->BackendCfg.cMaxHstStrmsOut;
+    size_t cbHstStrmsOut     = pThis->BackendCfg.cbStreamOut;
+
+    if (cbHstStrmsOut)
+    {
+        pThis->cFreeOutputStreams = cMaxHstStrmsOut;
+    }
+    else
+        pThis->cFreeOutputStreams = 0;
+
+    uint32_t cMaxHstStrmsIn = pThis->BackendCfg.cMaxHstStrmsIn;
+    size_t cbHstStrmIn      = pThis->BackendCfg.cbStreamIn;
+
+    if (cbHstStrmIn)
+    {
+        /*
+         * Note:
+         *  - Our AC'97 emulation has two inputs, line (ac97.pi) and microphone (ac97.mc).
+         *  - Our HDA emulation currently has only line input (hda.pi).
+         */
+        pThis->cFreeInputStreams = cMaxHstStrmsIn;
+    }
+    else
+        pThis->cFreeInputStreams = 0;
+
+    LogFlowFunc(("cMaxHstStrmsOut=%RU32 (cb=%zu), cMaxHstStrmsIn=%RU32 (cb=%zu)\n",
+                 cMaxHstStrmsOut, cbHstStrmsOut, cMaxHstStrmsIn, cbHstStrmIn));
+
+    LogFlowFunc(("cFreeInputStreams=%RU8, cFreeOutputStreams=%RU8\n",
+                 pThis->cFreeInputStreams, pThis->cFreeOutputStreams));
+
+    LogRel(("Audio: Host audio backend supports %RU32 output streams and %RU32 input streams at once\n",
+            /* Clamp for logging. Unlimited streams are defined by UINT32_MAX. */
+            RT_MIN(64, cMaxHstStrmsOut), RT_MIN(64, cMaxHstStrmsIn)));
+
+    LogFlowFuncLeave();
+    return VINF_SUCCESS;
+}
+
+static void drvAudioStateHandler(PPDMDRVINS pDrvIns, PDMAUDIOSTREAMCMD enmCmd)
+{
+    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+    PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
+
+    LogFlowFunc(("enmCmd=%ld\n", enmCmd));
+
+    if (!pThis->pHostDrvAudio)
+        return;
+
+    PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
+    while ((pHstStrmOut = drvAudioHstFindAnyEnabledOut(pThis, pHstStrmOut)))
+        drvAudioControlHstOut(pThis, pHstStrmOut, enmCmd);
+
+    PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
+    while ((pHstStrmIn = drvAudioFindNextEnabledHstIn(pThis, pHstStrmIn)))
+        drvAudioControlHstIn(pThis, pHstStrmIn, enmCmd);
+}
+
+static struct audio_option audio_options[] =
+{
+    /* DAC */
+    {"DACFixedSettings", AUD_OPT_BOOL, &conf.fixed_out.enabled,
+     "Use fixed settings for host DAC", NULL, 0},
+
+    {"DACFixedFreq", AUD_OPT_INT, &conf.fixed_out.settings.uHz,
+     "Frequency for fixed host DAC", NULL, 0},
+
+    {"DACFixedFmt", AUD_OPT_FMT, &conf.fixed_out.settings.enmFormat,
+     "Format for fixed host DAC", NULL, 0},
+
+    {"DACFixedChannels", AUD_OPT_INT, &conf.fixed_out.settings.cChannels,
+     "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0},
+
+    {"DACVoices", AUD_OPT_INT, &conf.fixed_out.cStreams, /** @todo Rename! */
+     "Number of streams for DAC", NULL, 0},
+
+    /* ADC */
+    {"ADCFixedSettings", AUD_OPT_BOOL, &conf.fixed_in.enabled,
+     "Use fixed settings for host ADC", NULL, 0},
+
+    {"ADCFixedFreq", AUD_OPT_INT, &conf.fixed_in.settings.uHz,
+     "Frequency for fixed host ADC", NULL, 0},
+
+    {"ADCFixedFmt", AUD_OPT_FMT, &conf.fixed_in.settings.enmFormat,
+     "Format for fixed host ADC", NULL, 0},
+
+    {"ADCFixedChannels", AUD_OPT_INT, &conf.fixed_in.settings.cChannels,
+     "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0},
+
+    {"ADCVoices", AUD_OPT_INT, &conf.fixed_in.cStreams, /** @todo Rename! */
+     "Number of streams for ADC", NULL, 0},
+
+    /* Misc */
+    {"TimerFreq", AUD_OPT_INT, &conf.period.hz,
+     "Timer frequency in Hz (0 - use lowest possible)", NULL, 0},
+
+    {"PLIVE", AUD_OPT_BOOL, &conf.plive,
+     "(undocumented)", NULL, 0}, /** @todo What is this? */
+
+    NULL
+};
+
+static DECLCALLBACK(int) drvAudioInit(PCFGMNODE pCfgHandle, PPDMDRVINS pDrvIns)
+{
+    AssertPtrReturn(pCfgHandle, VERR_INVALID_POINTER);
+    AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
+
+    PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
+    LogFlowFunc(("pDrvAudio=%p, pDrvIns=%p\n", pThis, pDrvIns));
+
+    RTListInit(&pThis->lstHstStrmIn);
+    RTListInit(&pThis->lstHstStrmOut);
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    RTListInit(&pThis->lstCBIn);
+    RTListInit(&pThis->lstCBOut);
+#endif
+
+    int rc = RTCritSectInit(&pThis->CritSect);
+    if (RT_SUCCESS(rc))
+    {
+        rc = drvAudioProcessOptions(pCfgHandle, "AUDIO", audio_options);
+        /** @todo Check for invalid options? */
+
+        pThis->cFreeOutputStreams = conf.fixed_out.cStreams;
+        pThis->cFreeInputStreams  = conf.fixed_in.cStreams;
+
+        if (!pThis->cFreeOutputStreams)
+            pThis->cFreeOutputStreams = 1;
+
+        if (!pThis->cFreeInputStreams)
+            pThis->cFreeInputStreams = 1;
+    }
+
+    /*
+     * If everything went well, initialize the lower driver.
+     */
+    if (RT_SUCCESS(rc))
+        rc = drvAudioHostInit(pCfgHandle, pThis);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvAudioRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn,
+                                      void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
+{
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+    AssertPtrReturn(pGstStrmIn, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvBuf,      VERR_INVALID_POINTER);
+    AssertReturn(cbBuf,         VERR_INVALID_PARAMETER);
+    /* pcbWritten is optional. */
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    if (!pThis->pHostDrvAudio->pfnIsEnabled(pThis->pHostDrvAudio, PDMAUDIODIR_IN))
+    {
+        if (pcbRead)
+            *pcbRead = 0;
+
+        return RTCritSectLeave(&pThis->CritSect);
+    }
+
+    PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+
+    AssertMsg(pGstStrmIn->pHstStrmIn->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED,
+              ("Reading from disabled host input stream \"%s\" not possible\n", pGstStrmIn->MixBuf.pszName));
+
+    /*
+     * Read from the parent buffer (that is, the guest buffer) which
+     * should have the audio data in the format the guest needs.
+     */
+    uint32_t cRead;
+    rc = AudioMixBufReadCirc(&pGstStrmIn->MixBuf, pvBuf, cbBuf, &cRead);
+    if (RT_SUCCESS(rc))
+    {
+        AudioMixBufFinish(&pGstStrmIn->MixBuf, cRead);
+
+        if (pcbRead)
+            *pcbRead = AUDIOMIXBUF_S2B(&pGstStrmIn->MixBuf, cRead);
+    }
+
+    LogFlowFunc(("cRead=%RU32 (%RU32 bytes), rc=%Rrc\n",
+                 cRead, AUDIOMIXBUF_S2B(&pGstStrmIn->MixBuf, cRead), rc));
+
+    int rc2 = RTCritSectLeave(&pThis->CritSect);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvAudioEnableOut(PPDMIAUDIOCONNECTOR pInterface,
+                                           PPDMAUDIOGSTSTRMOUT pGstStrmOut, bool fEnable)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    /* pGstStrmOut is optional. */
+
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+
+    int rc = VINF_SUCCESS;
+
+    if (pGstStrmOut)
+    {
+        PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
+        AssertPtr(pHstStrmOut);
+
+        if (fEnable)
+        {
+            /* Is a pending disable outstanding? Then disable first. */
+            if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE)
+            {
+                rc = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
+                if (RT_SUCCESS(rc))
+                    pHstStrmOut->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
+            }
+
+            if (RT_SUCCESS(rc))
+                rc = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_ENABLE);
+        }
+        else /* Disable */
+        {
+            if (pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED)
+            {
+                uint32_t cGstStrmsActive = 0;
+
+                /*
+                 * Check if there are any active guest streams assigned
+                 * to this host stream which still are being marked as active.
+                 *
+                 * In that case we have to defer closing the host stream and
+                 * wait until all guest streams have been finished.
+                 */
+                PPDMAUDIOGSTSTRMOUT pIter;
+                RTListForEach(&pHstStrmOut->lstGstStrmOut, pIter, PDMAUDIOGSTSTRMOUT, Node)
+                {
+                    if (pIter->State.fActive)
+                    {
+                        cGstStrmsActive++;
+                        break; /* At least one assigned & active guest stream is enough. */
+                    }
+                }
+
+                /* Do we need to defer closing the host stream? */
+                if (cGstStrmsActive >= 1)
+                    pHstStrmOut->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE;
+
+                /* Can we close the host stream now instead of deferring it? */
+                if (!(pHstStrmOut->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_DISABLE))
+                    rc = drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
+            }
+        }
+
+        if (RT_SUCCESS(rc))
+            pGstStrmOut->State.fActive = fEnable;
+
+        LogFlowFunc(("%s: fEnable=%RTbool, fStatus=0x%x, rc=%Rrc\n",
+                     pGstStrmOut->MixBuf.pszName, fEnable, pHstStrmOut->fStatus, rc));
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvAudioEnableIn(PPDMIAUDIOCONNECTOR pInterface,
+                                          PPDMAUDIOGSTSTRMIN pGstStrmIn, bool fEnable)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    /* pGstStrmIn is optional. */
+
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+
+    int rc = VINF_SUCCESS;
+
+    if (pGstStrmIn)
+    {
+        PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
+        AssertPtr(pHstStrmIn);
+
+        LogFlowFunc(("%s: fEnable=%RTbool\n", pGstStrmIn->MixBuf.pszName, fEnable));
+
+        rc = drvAudioControlHstIn(pThis, pHstStrmIn,
+                                  fEnable ? PDMAUDIOSTREAMCMD_ENABLE : PDMAUDIOSTREAMCMD_DISABLE);
+        if (RT_SUCCESS(rc))
+            pGstStrmIn->State.fActive = fEnable;
+
+        LogFlowFunc(("%s: fEnable=%RTbool, rc=%Rrc\n", pGstStrmIn->MixBuf.pszName, fEnable, rc));
+    }
+
+    return rc;
+}
+
+static DECLCALLBACK(bool) drvAudioIsValidIn(PPDMIAUDIOCONNECTOR pInterface,
+                                            PPDMAUDIOGSTSTRMIN  pGstStrmIn)
+{
+    return (pGstStrmIn != NULL);
+}
+
+static DECLCALLBACK(bool) drvAudioIsValidOut(PPDMIAUDIOCONNECTOR pInterface,
+                                             PPDMAUDIOGSTSTRMOUT pGstStrmOut)
+{
+    return (pGstStrmOut != NULL);
+}
+
+static DECLCALLBACK(int) drvAudioCreateIn(PPDMIAUDIOCONNECTOR pInterface, const char *pszName,
+                                          PDMAUDIORECSOURCE enmRecSource, PPDMAUDIOSTREAMCFG pCfg,
+                                          PPDMAUDIOGSTSTRMIN *ppGstStrmIn)
+{
+    AssertPtrReturn(pInterface,  VERR_INVALID_POINTER);
+    AssertPtrReturn(ppGstStrmIn, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName,     VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg,        VERR_INVALID_POINTER);
+    AssertPtrReturn(ppGstStrmIn, VERR_INVALID_POINTER);
+
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    LogFlowFunc(("pszName=%s, pCfg=%p\n", pszName, pCfg));
+
+    if (!drvAudioStreamCfgIsValid(pCfg))
+    {
+        LogFunc(("Input stream configuration is not valid, bailing out\n"));
+        rc = VERR_INVALID_PARAMETER;
+    }
+
+    PPDMAUDIOGSTSTRMIN pGstStrmIn = *ppGstStrmIn;
+    if (   RT_SUCCESS(rc)
+        && pGstStrmIn
+        && drvAudioPCMPropsAreEqual(&pGstStrmIn->Props, pCfg))
+    {
+        LogFunc(("[%s] Exists and matches required configuration, skipping creation\n",
+                 pGstStrmIn->MixBuf.pszName));
+        rc = VWRN_ALREADY_EXISTS;
+    }
+
+    if (rc != VINF_SUCCESS) /* Note: Can be VWRN_ALREADY_EXISTS, so don't use VINF_SUCCESS here. */
+    {
+        int rc2 = RTCritSectLeave(&pThis->CritSect);
+        AssertRC(rc2);
+
+        return rc;
+    }
+
+    if (   !conf.fixed_in.enabled
+        && pGstStrmIn)
+    {
+        drvAudioDestroyGstIn(pThis, pGstStrmIn);
+        pGstStrmIn = NULL;
+    }
+
+    if (pGstStrmIn)
+    {
+        PPDMAUDIOHSTSTRMIN pHstStrmIn = pGstStrmIn->pHstStrmIn;
+        AssertPtr(pHstStrmIn);
+
+        drvAudioGstInFreeRes(pGstStrmIn);
+
+        char *pszTemp;
+        if (RTStrAPrintf(&pszTemp, "%s (Guest)", pszName) <= 0)
+        {
+            RTMemFree(pGstStrmIn);
+
+            int rc2 = RTCritSectLeave(&pThis->CritSect);
+            AssertRC(rc2);
+
+            return VERR_NO_MEMORY;
+        }
+
+        rc = drvAudioGstInInit(pGstStrmIn, pHstStrmIn, pszName, pCfg);
+
+        RTStrFree(pszTemp);
+    }
+    else
+        rc = drvAudioCreateStreamPairIn(pThis, pszName, enmRecSource, pCfg, &pGstStrmIn);
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pGstStrmIn)
+            *ppGstStrmIn = pGstStrmIn;
+    }
+    else
+    {
+        switch (rc)
+        {
+            case VERR_NO_MORE_HANDLES: /** @todo Find a better rc. */
+                LogRel(("Audio: Skipping to create input stream \"%s\", " \
+                        "as the host audio backend reached its maximum of concurrent audio input streams\n", pszName));
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    int rc2 = RTCritSectLeave(&pThis->CritSect);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvAudioCreateOut(PPDMIAUDIOCONNECTOR pInterface, const char *pszName,
+                                           PPDMAUDIOSTREAMCFG pCfg, PPDMAUDIOGSTSTRMOUT *ppGstStrmOut)
+{
+    AssertPtrReturn(pInterface,   VERR_INVALID_POINTER);
+    AssertPtrReturn(pszName,      VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg,         VERR_INVALID_POINTER);
+    AssertPtrReturn(ppGstStrmOut, VERR_INVALID_POINTER);
+
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    LogFlowFunc(("pszName=%s, pCfg=%p\n", pszName, pCfg));
+
+    if (!drvAudioStreamCfgIsValid(pCfg))
+    {
+        LogFunc(("Output stream configuration is not valid, bailing out\n"));
+        rc = VERR_INVALID_PARAMETER;
+    }
+
+    PPDMAUDIOGSTSTRMOUT pGstStrmOut = *ppGstStrmOut;
+    if (   RT_SUCCESS(rc)
+        && pGstStrmOut
+        && drvAudioPCMPropsAreEqual(&pGstStrmOut->Props, pCfg))
+    {
+        LogFunc(("[%s] Exists and matches required configuration, skipping creation\n",
+                 pGstStrmOut->MixBuf.pszName));
+
+        rc = VWRN_ALREADY_EXISTS;
+    }
+
+    if (rc != VINF_SUCCESS) /* Note: Can be VWRN_ALREADY_EXISTS, so don't use VINF_SUCCESS here. */
+    {
+        int rc2 = RTCritSectLeave(&pThis->CritSect);
+        AssertRC(rc2);
+
+        return rc;
+    }
+
+#if 0
+    /* Any live samples that need to be updated after
+     * we set the new parameters? */
+    PPDMAUDIOGSTSTRMOUT pOldGstStrmOut = NULL;
+    uint32_t cLiveSamples = 0;
+
+    if (   conf.plive
+        && pGstStrmOut
+        && (   !pGstStrmOut->State.fActive
+            && !pGstStrmOut->State.fEmpty))
+    {
+        cLiveSamples = pGstStrmOut->cTotalSamplesWritten;
+        if (cLiveSamples)
+        {
+            pOldGstStrmOut = pGstStrmOut;
+            pGstStrmOut = NULL;
+        }
+    }
+#endif
+
+    if (   pGstStrmOut
+        && !conf.fixed_out.enabled)
+    {
+        drvAudioDestroyGstOut(pThis, pGstStrmOut);
+        pGstStrmOut = NULL;
+    }
+
+    if (pGstStrmOut)
+    {
+        PPDMAUDIOHSTSTRMOUT pHstStrmOut = pGstStrmOut->pHstStrmOut;
+        AssertPtr(pHstStrmOut);
+
+        drvAudioGstOutFreeRes(pGstStrmOut);
+
+        rc = drvAudioGstOutInit(pGstStrmOut, pHstStrmOut, pszName, pCfg);
+    }
+    else
+    {
+        rc = drvAudioCreateStreamPairOut(pThis, pszName, pCfg, &pGstStrmOut);
+        if (RT_FAILURE(rc))
+            LogFunc(("Failed to create output stream \"%s\", rc=%Rrc\n", pszName, rc));
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pGstStrmOut)
+            *ppGstStrmOut = pGstStrmOut;
+#if 0
+        /* Update remaining live samples with new rate. */
+        if (cLiveSamples)
+        {
+            AssertPtr(pOldGstStrmOut);
+
+            uint32_t cSamplesMixed =
+                (cLiveSamples << pOldGstStrmOut->Props.cShift)
+                * pOldGstStrmOut->Props.cbPerSec
+                / (*ppGstStrmOut)->Props.cbPerSec;
+
+            pGstStrmOut->cTotalSamplesWritten += cSamplesMixed;
+        }
+#endif
+    }
+    else
+    {
+        switch (rc)
+        {
+            case VERR_NO_MORE_HANDLES: /** @todo Find a better rc. */
+                LogRel(("Audio: Skipping to create output stream \"%s\", " \
+                        "as the host audio backend reached its maximum of concurrent audio output streams\n", pszName));
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    int rc2 = RTCritSectLeave(&pThis->CritSect);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvAudioGetConfiguration(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOBACKENDCFG pCfg)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg,       VERR_INVALID_POINTER);
+
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+
+    int rc = RTCritSectEnter(&pThis->CritSect);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = pThis->pHostDrvAudio->pfnGetConf(pThis->pHostDrvAudio, pCfg);
+
+    int rc2 = RTCritSectLeave(&pThis->CritSect);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(bool) drvAudioIsActiveIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn)
+{
+    AssertPtrReturn(pInterface, false);
+    /* pGstStrmIn is optional. */
+
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+
+    int rc2 = RTCritSectEnter(&pThis->CritSect);
+    AssertRC(rc2);
+
+    bool fRet = pGstStrmIn ? pGstStrmIn->State.fActive : false;
+
+    rc2 = RTCritSectLeave(&pThis->CritSect);
+    AssertRC(rc2);
+
+    return fRet;
+}
+
+static DECLCALLBACK(bool) drvAudioIsActiveOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
+{
+    AssertPtrReturn(pInterface,  false);
+    /* pGstStrmOut is optional. */
+
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+
+    int rc2 = RTCritSectEnter(&pThis->CritSect);
+    AssertRC(rc2);
+
+    bool fRet = pGstStrmOut ? pGstStrmOut->State.fActive : false;
+
+    rc2 = RTCritSectLeave(&pThis->CritSect);
+    AssertRC(rc2);
+
+    return fRet;
+}
+
+static DECLCALLBACK(void) drvAudioDestroyIn(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMIN pGstStrmIn)
+{
+    AssertPtrReturnVoid(pInterface);
+    /* pGstStrmIn is optional. */
+
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+
+    int rc2 = RTCritSectEnter(&pThis->CritSect);
+    AssertRC(rc2);
+
+    if (pGstStrmIn)
+        drvAudioDestroyGstIn(pThis, pGstStrmIn);
+
+    rc2 = RTCritSectLeave(&pThis->CritSect);
+    AssertRC(rc2);
+}
+
+static DECLCALLBACK(void) drvAudioDestroyOut(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOGSTSTRMOUT pGstStrmOut)
+{
+    AssertPtrReturnVoid(pInterface);
+    /* pGstStrmOut is optional. */
+
+    PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface);
+
+    int rc2 = RTCritSectEnter(&pThis->CritSect);
+    AssertRC(rc2);
+
+    if (pGstStrmOut)
+        drvAudioDestroyGstOut(pThis, pGstStrmOut);
+
+    rc2 = RTCritSectLeave(&pThis->CritSect);
+    AssertRC(rc2);
+}
+
+/********************************************************************/
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    LogFlowFunc(("pInterface=%p, pszIID=%s\n", pInterface, pszIID));
+
+    PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVAUDIO  pThis   = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
+
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIAUDIOCONNECTOR, &pThis->IAudioConnector);
+
+    return NULL;
+}
+
+/**
+ * Power Off notification.
+ *
+ * @param   pDrvIns     The driver instance data.
+ */
+static DECLCALLBACK(void) drvAudioPowerOff(PPDMDRVINS pDrvIns)
+{
+    LogFlowFuncEnter();
+    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+    PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
+
+    if (!pThis->pHostDrvAudio)
+        return;
+
+    /* Tear down all host output streams. */
+    PPDMAUDIOHSTSTRMOUT pHstStrmOut = NULL;
+    while ((pHstStrmOut = drvAudioFindAnyHstOut(pThis, pHstStrmOut)))
+    {
+        drvAudioControlHstOut(pThis, pHstStrmOut, PDMAUDIOSTREAMCMD_DISABLE);
+        pThis->pHostDrvAudio->pfnFiniOut(pThis->pHostDrvAudio, pHstStrmOut);
+    }
+
+    /* Tear down all host input streams. */
+    PPDMAUDIOHSTSTRMIN pHstStrmIn = NULL;
+    while ((pHstStrmIn = drvAudioFindNextHstIn(pThis, pHstStrmIn)))
+    {
+        drvAudioControlHstIn(pThis, pHstStrmIn, PDMAUDIOSTREAMCMD_DISABLE);
+        pThis->pHostDrvAudio->pfnFiniIn(pThis->pHostDrvAudio, pHstStrmIn);
+    }
+
+    if (pThis->pHostDrvAudio->pfnShutdown)
+        pThis->pHostDrvAudio->pfnShutdown(pThis->pHostDrvAudio);
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    PPDMAUDIOCALLBACK pCB, pCBNext;
+    RTListForEachSafe(&pThis->lstCBIn, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
+        drvAudioCallbackDestroy(pCB);
+
+    RTListForEachSafe(&pThis->lstCBOut, pCB, pCBNext, PDMAUDIOCALLBACK, Node)
+        drvAudioCallbackDestroy(pCB);
+#endif
+
+    LogFlowFuncLeave();
+}
+
+/**
+ * Constructs an audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
+{
+    LogFlowFunc(("pDrvIns=%#p, pCfgHandle=%#p, fFlags=%x\n", pDrvIns, pCfgHandle, fFlags));
+
+    PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
+    PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+
+    /*
+     * Init the static parts.
+     */
+    pThis->pDrvIns                                   = pDrvIns;
+    /* IBase. */
+    pDrvIns->IBase.pfnQueryInterface                 = drvAudioQueryInterface;
+    /* IAudioConnector. */
+    pThis->IAudioConnector.pfnQueryStatus            = drvAudioQueryStatus;
+    pThis->IAudioConnector.pfnRead                   = drvAudioRead;
+    pThis->IAudioConnector.pfnWrite                  = drvAudioWrite;
+    pThis->IAudioConnector.pfnGetConfiguration       = drvAudioGetConfiguration;
+    pThis->IAudioConnector.pfnIsActiveIn             = drvAudioIsActiveIn;
+    pThis->IAudioConnector.pfnIsActiveOut            = drvAudioIsActiveOut;
+    pThis->IAudioConnector.pfnIsValidIn              = drvAudioIsValidIn;
+    pThis->IAudioConnector.pfnIsValidOut             = drvAudioIsValidOut;
+    pThis->IAudioConnector.pfnEnableOut              = drvAudioEnableOut;
+    pThis->IAudioConnector.pfnEnableIn               = drvAudioEnableIn;
+    pThis->IAudioConnector.pfnDestroyIn              = drvAudioDestroyIn;
+    pThis->IAudioConnector.pfnDestroyOut             = drvAudioDestroyOut;
+    pThis->IAudioConnector.pfnCreateIn               = drvAudioCreateIn;
+    pThis->IAudioConnector.pfnCreateOut              = drvAudioCreateOut;
+    pThis->IAudioConnector.pfnPlayOut                = drvAudioPlayOut;
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    pThis->IAudioConnector.pfnRegisterCallbacks      = drvAudioRegisterCallbacks;
+    pThis->IAudioConnector.pfnCallback               = drvAudioCallback;
+#endif
+
+    /*
+     * Attach driver below and query its connector interface.
+     */
+    PPDMIBASE pDownBase;
+    int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pDownBase);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("Audio: Failed to attach to driver %p below (flags=0x%x), rc=%Rrc\n",
+                pDrvIns, fFlags, rc));
+        return rc;
+    }
+
+    pThis->pHostDrvAudio = PDMIBASE_QUERY_INTERFACE(pDownBase, PDMIHOSTAUDIO);
+    if (!pThis->pHostDrvAudio)
+    {
+        LogRel(("Audio: Failed to query interface for underlying host driver\n"));
+        return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
+                                N_("Host audio backend missing or invalid"));
+    }
+
+#ifdef DEBUG_andy
+    CFGMR3Dump(pCfgHandle);
+#endif
+
+    rc = drvAudioInit(pCfgHandle, pDrvIns);
+    if (RT_SUCCESS(rc))
+    {
+        pThis->fTerminate = false;
+        pThis->pDrvIns    = pDrvIns;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+/**
+ * Destructs an audio driver instance.
+ *
+ * @copydoc FNPDMDRVDESTRUCT
+ */
+static DECLCALLBACK(void) drvAudioDestruct(PPDMDRVINS pDrvIns)
+{
+    LogFlowFuncEnter();
+
+    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+    PDRVAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIO);
+
+    if (RTCritSectIsInitialized(&pThis->CritSect))
+    {
+        int rc2 = RTCritSectDelete(&pThis->CritSect);
+        AssertRC(rc2);
+    }
+}
+
+/**
+ * Suspend notification.
+ *
+ * @param   pDrvIns     The driver instance data.
+ */
+static DECLCALLBACK(void) drvAudioSuspend(PPDMDRVINS pDrvIns)
+{
+    drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_PAUSE);
+}
+
+/**
+ * Resume notification.
+ *
+ * @param   pDrvIns     The driver instance data.
+ */
+static DECLCALLBACK(void) drvAudioResume(PPDMDRVINS pDrvIns)
+{
+    drvAudioStateHandler(pDrvIns, PDMAUDIOSTREAMCMD_RESUME);
+}
+
+/**
+ * Audio driver registration record.
+ */
+const PDMDRVREG g_DrvAUDIO =
+{
+    /* u32Version */
+    PDM_DRVREG_VERSION,
+    /* szName */
+    "AUDIO",
+    /* szRCMod */
+    "",
+    /* szR0Mod */
+    "",
+    /* pszDescription */
+    "Audio connector driver",
+    /* fFlags */
+    PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+    /* fClass */
+    PDM_DRVREG_CLASS_AUDIO,
+    /* cMaxInstances */
+    2,
+    /* cbInstance */
+    sizeof(DRVAUDIO),
+    /* pfnConstruct */
+    drvAudioConstruct,
+    /* pfnDestruct */
+    drvAudioDestruct,
+    /* pfnRelocate */
+    NULL,
+    /* pfnIOCtl */
+    NULL,
+    /* pfnPowerOn */
+    NULL,
+    /* pfnReset */
+    NULL,
+    /* pfnSuspend */
+    drvAudioSuspend,
+    /* pfnResume */
+    drvAudioResume,
+    /* pfnAttach */
+    NULL,
+    /* pfnDetach */
+    NULL,
+    /* pfnPowerOff */
+    drvAudioPowerOff,
+    /* pfnSoftReset */
+    NULL,
+    /* u32EndVersion */
+    PDM_DRVREG_VERSION
+};
Index: /trunk/src/VBox/Devices/Audio_old/DrvAudio.h
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DrvAudio.h	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DrvAudio.h	(revision 61413)
@@ -0,0 +1,204 @@
+/* $Id$ */
+/** @file
+ * Intermediate audio driver header.
+ */
+
+/*
+ * Copyright (C) 2006-2016 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 <iprt/critsect.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;
+    /** Critical section for serializing access. */
+    RTCRITSECT              CritSect;
+    /** Shutdown indicator. */
+    bool                    fTerminate;
+    /** Our audio connector interface. */
+    PDMIAUDIOCONNECTOR      IAudioConnector;
+    /** Pointer to the driver instance. */
+    PPDMDRVINS              pDrvIns;
+    /** Pointer to audio driver below us. */
+    PPDMIHOSTAUDIO          pHostDrvAudio;
+    /** List of host input streams. */
+    RTLISTANCHOR            lstHstStrmIn;
+    /** List of host output streams. */
+    RTLISTANCHOR            lstHstStrmOut;
+    /** Max. number of free input streams.
+     *  UINT32_MAX for unlimited streams. */
+    uint32_t                cFreeInputStreams;
+    /** Max. number of free output streams.
+     *  UINT32_MAX for unlimited streams. */
+    uint32_t                cFreeOutputStreams;
+    /** Audio configuration settings retrieved from the backend. */
+    PDMAUDIOBACKENDCFG      BackendCfg;
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    /** @todo Use a map with primary key set to the callback type? */
+    RTLISTANCHOR            lstCBIn;
+    RTLISTANCHOR            lstCBOut;
+#endif
+} DRVAUDIO, *PDRVAUDIO;
+
+/** Makes a PDRVAUDIO out of a PPDMIAUDIOCONNECTOR. */
+#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);
+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). */
+void DrvAudioClearBuf(PPDMPCMPROPS pPCMInfo, void *pvBuf, size_t cbBuf, uint32_t cSamples);
+int DrvAudioStreamCfgToProps(PPDMAUDIOSTREAMCFG pCfg, PPDMPCMPROPS pProps);
+
+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 */
+            PDMAUDIOHOSTENDIANNESS
+        }
+    },
+
+    /* Fixed input settings. */
+    {                           /* ADC fixed settings */
+        1,                      /* enabled */
+        2,                      /* cStreams */
+        1,                      /* greedy */
+        {
+            44100,              /* freq */
+            2,                  /* nchannels */
+            AUD_FMT_S16,        /* fmt */
+            PDMAUDIOHOSTENDIANNESS
+        }
+    },
+
+    { 200 },                    /* frequency (in Hz) */
+    0,                          /* plive */ /** @todo Disable pending live? */
+};
+#endif /* DRV_AUDIO_H */
+
Index: /trunk/src/VBox/Devices/Audio_old/DrvAudioCommon.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DrvAudioCommon.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DrvAudioCommon.cpp	(revision 61413)
@@ -0,0 +1,413 @@
+/* $Id$ */
+/** @file
+ * Intermedia audio driver, common routines. These are also used
+ * in the drivers which are bound to Main, e.g. the VRDE or the
+ * video audio recording drivers.
+ */
+
+/*
+ * 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: audio_template.h from QEMU AUDIO subsystem.
+ *
+ * QEMU Audio subsystem header
+ *
+ * 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.
+ */
+#define LOG_GROUP LOG_GROUP_DRV_AUDIO
+#include <VBox/log.h>
+#include <iprt/asm-math.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include <iprt/string.h>
+#include <iprt/alloc.h>
+
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdm.h>
+#include <VBox/err.h>
+#include <VBox/vmm/mm.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "DrvAudio.h"
+#include "AudioMixBuffer.h"
+
+bool drvAudioPCMPropsAreEqual(PPDMPCMPROPS pProps, PPDMAUDIOSTREAMCFG pCfg);
+
+const char *drvAudioRecSourceToString(PDMAUDIORECSOURCE enmRecSource)
+{
+    switch (enmRecSource)
+    {
+        case PDMAUDIORECSOURCE_MIC:     return "Microphone In";
+        case PDMAUDIORECSOURCE_CD:      return "CD";
+        case PDMAUDIORECSOURCE_VIDEO:   return "Video";
+        case PDMAUDIORECSOURCE_AUX:     return "AUX";
+        case PDMAUDIORECSOURCE_LINE_IN: return "Line In";
+        case PDMAUDIORECSOURCE_PHONE:   return "Phone";
+        default:
+            break;
+    }
+
+    AssertMsgFailed(("Bogus recording source %ld\n", enmRecSource));
+    return "Unknown";
+}
+
+const char *drvAudioHlpFormatToString(PDMAUDIOFMT enmFormat)
+{
+    switch (enmFormat)
+    {
+        case AUD_FMT_U8:
+            return "U8";
+
+        case AUD_FMT_U16:
+            return "U16";
+
+        case AUD_FMT_U32:
+            return "U32";
+
+        case AUD_FMT_S8:
+            return "S8";
+
+        case AUD_FMT_S16:
+            return "S16";
+
+        case AUD_FMT_S32:
+            return "S32";
+
+        default:
+            break;
+    }
+
+    AssertMsgFailed(("Bogus audio format %ld\n", enmFormat));
+    return "Invalid";
+}
+
+PDMAUDIOFMT drvAudioHlpStringToFormat(const char *pszFormat)
+{
+    if (!RTStrICmp(pszFormat, "u8"))
+        return AUD_FMT_U8;
+    else if (!RTStrICmp(pszFormat, "u16"))
+        return AUD_FMT_U16;
+    else if (!RTStrICmp(pszFormat, "u32"))
+        return AUD_FMT_U32;
+    else if (!RTStrICmp(pszFormat, "s8"))
+        return AUD_FMT_S8;
+    else if (!RTStrICmp(pszFormat, "s16"))
+        return AUD_FMT_S16;
+    else if (!RTStrICmp(pszFormat, "s32"))
+        return AUD_FMT_S32;
+
+    AssertMsgFailed(("Bogus audio format \"%s\"\n", pszFormat));
+    return AUD_FMT_INVALID;
+}
+
+/*********************************** In Stream Functions **********************************************/
+
+void drvAudioGstInFreeRes(PPDMAUDIOGSTSTRMIN pGstStrmIn)
+{
+    AssertPtrReturnVoid(pGstStrmIn);
+
+    if (pGstStrmIn->State.pszName)
+    {
+        RTStrFree(pGstStrmIn->State.pszName);
+        pGstStrmIn->State.pszName = NULL;
+    }
+
+    AudioMixBufDestroy(&pGstStrmIn->MixBuf);
+}
+
+void drvAudioHstInFreeRes(PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    AssertPtrReturnVoid(pHstStrmIn);
+    AudioMixBufDestroy(&pHstStrmIn->MixBuf);
+}
+
+void drvAudioGstOutFreeRes(PPDMAUDIOGSTSTRMOUT pGstStrmOut)
+{
+    if (!pGstStrmOut)
+        return;
+
+    if (pGstStrmOut->State.pszName)
+    {
+        RTStrFree(pGstStrmOut->State.pszName);
+        pGstStrmOut->State.pszName = NULL;
+    }
+
+    AudioMixBufDestroy(&pGstStrmOut->MixBuf);
+}
+
+#if 0
+
+/**
+ * Finds the minimum number of not yet captured samples of all
+ * attached guest input streams for a certain host input stream.
+ *
+ * @return  uint32_t            Minimum number of not yet captured samples.
+ *                              UINT32_MAX if none found.
+ * @param   pHstStrmIn          Host input stream to check for.
+ */
+inline uint32_t drvAudioHstInFindMinCaptured(PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    AssertPtrReturn(pHstStrmIn, 0);
+    uint32_t cMinSamples = UINT32_MAX;
+
+    PPDMAUDIOGSTSTRMIN pGstStrmIn;
+    RTListForEach(&pHstStrmIn->lstGstStrmIn, pGstStrmIn, PDMAUDIOGSTSTRMIN, Node)
+    {
+        if (pGstStrmIn->State.fActive)
+            cMinSamples = RT_MIN(cMinSamples, audioMixBufMixed(&pGstStrmIn->MixBuf));
+    }
+
+#ifdef DEBUG_andy
+    LogFlowFunc(("cMinSamples=%RU32\n", cMinSamples));
+#endif
+    return cMinSamples;
+}
+
+uint32_t drvAudioHstInGetFree(PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    AssertPtrReturn(pHstStrmIn, 0);
+
+    return audioMixBufSize(&pHstStrmIn->MixBuf) - drvAudioHstInGetLive(pHstStrmIn);
+}
+
+uint32_t drvAudioHstInGetLive(PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    AssertPtrReturn(pHstStrmIn, 0);
+
+    uint32_t cMinSamplesCaptured = drvAudioHstInFindMinCaptured(pHstStrmIn);
+    uint32_t cSamplesCaptured = audioMixBufMixed(&pHstStrmIn->MixBuf);
+
+    Assert(cSamplesCaptured >= cMinSamplesCaptured);
+    uint32_t cSamplesLive = cSamplesCaptured - cMinSamplesCaptured;
+    Assert(cSamplesLive <= audioMixBufSize(&pHstStrmIn->MixBuf));
+
+#ifdef DEBUG_andy
+    LogFlowFunc(("cSamplesLive=%RU32\n", cSamplesLive));
+#endif
+    return cSamplesLive;
+}
+#endif
+
+void drvAudioHstOutFreeRes(PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    AssertPtrReturnVoid(pHstStrmOut);
+    AudioMixBufDestroy(&pHstStrmOut->MixBuf);
+}
+
+#if 0
+/**
+ * Returns the number of live sample data (in bytes) of a certain
+ * guest input stream.
+ *
+ * @return  uint32_t            Live sample data (in bytes), 0 if none.
+ * @param   pGstStrmIn          Guest input stream to check for.
+ */
+uint32_t drvAudioGstInGetLiveBytes(PPDMAUDIOGSTSTRMIN pGstStrmIn)
+{
+    AssertPtrReturn(pGstStrmIn, 0);
+    AssertPtrReturn(pGstStrmIn->pHstStrmIn, 0);
+
+    Assert(pGstStrmIn->pHstStrmIn->cTotalSamplesCaptured >= pGstStrmIn->cTotalHostSamplesRead);
+    uint32_t cSamplesLive = pGstStrmIn->pHstStrmIn->cTotalSamplesCaptured - pGstStrmIn->cTotalHostSamplesRead;
+    if (!cSamplesLive)
+        return 0;
+    Assert(cSamplesLive <= pGstStrmIn->pHstStrmIn->cSamples);
+
+    /** @todo Document / refactor this! */
+    return (((int64_t) cSamplesLive << 32) / pGstStrmIn->State.uFreqRatio) << pGstStrmIn->Props.cShift;
+}
+
+
+/**
+ * Returns the total number of unused sample data (in bytes) of a certain
+ * guest output stream.
+ *
+ * @return  uint32_t            Number of unused sample data (in bytes), 0 if all used up.
+ * @param   pGstStrmOut         Guest output stream to check for.
+ */
+uint32_t drvAudioGstOutGetFreeBytes(PPDMAUDIOGSTSTRMOUT pGstStrmOut)
+{
+    AssertPtrReturn(pGstStrmOut, 0);
+
+    Assert(pGstStrmOut->cTotalSamplesWritten <= pGstStrmOut->pHstStrmOut->cSamples);
+    uint32_t cSamplesFree =   pGstStrmOut->pHstStrmOut->cSamples
+                            - pGstStrmOut->cTotalSamplesWritten;
+    if (!cSamplesFree)
+        return 0;
+
+    /** @todo Document / refactor this! */
+    return (((int64_t) cSamplesFree << 32) / pGstStrmOut->State.uFreqRatio) << pGstStrmOut->Props.cShift;
+}
+#endif
+
+bool drvAudioPCMPropsAreEqual(PPDMPCMPROPS pProps, PPDMAUDIOSTREAMCFG pCfg)
+{
+    int cBits = 8;
+    bool fSigned = false;
+
+    switch (pCfg->enmFormat)
+    {
+        case AUD_FMT_S8:
+            fSigned = true;
+        case AUD_FMT_U8:
+            break;
+
+        case AUD_FMT_S16:
+            fSigned = true;
+        case AUD_FMT_U16:
+            cBits = 16;
+            break;
+
+        case AUD_FMT_S32:
+            fSigned = true;
+        case AUD_FMT_U32:
+            cBits = 32;
+            break;
+
+        default:
+            AssertMsgFailed(("Unknown format %ld\n", pCfg->enmFormat));
+            break;
+    }
+
+    bool fEqual =    pProps->uHz         == pCfg->uHz
+                  && pProps->cChannels   == pCfg->cChannels
+                  && pProps->fSigned     == fSigned
+                  && pProps->cBits       == cBits
+                  && pProps->fSwapEndian == !(pCfg->enmEndianness == PDMAUDIOHOSTENDIANNESS);
+
+    LogFlowFunc(("fEqual=%RTbool\n", fEqual));
+    return fEqual;
+}
+
+/**
+ * Converts an audio stream configuration to matching PCM properties.
+ *
+ * @return  IPRT status code.
+ * @param   pCfg                    Audio stream configuration to convert.
+ * @param   pProps                  PCM properties to save result to.
+ */
+int DrvAudioStreamCfgToProps(PPDMAUDIOSTREAMCFG pCfg, PPDMPCMPROPS pProps)
+{
+    AssertPtrReturn(pCfg,   VERR_INVALID_POINTER);
+    AssertPtrReturn(pProps, VERR_INVALID_POINTER);
+
+    int rc = VINF_SUCCESS;
+
+    int cBits = 8, cShift = 0;
+    bool fSigned = false;
+
+    switch (pCfg->enmFormat)
+    {
+        case AUD_FMT_S8:
+            fSigned = true;
+        case AUD_FMT_U8:
+            break;
+
+        case AUD_FMT_S16:
+            fSigned = true;
+        case AUD_FMT_U16:
+            cBits = 16;
+            cShift = 1;
+            break;
+
+        case AUD_FMT_S32:
+            fSigned = true;
+        case AUD_FMT_U32:
+            cBits = 32;
+            cShift = 2;
+            break;
+
+        default:
+            AssertMsgFailed(("Unknown format %ld\n", pCfg->enmFormat));
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        pProps->uHz         = pCfg->uHz;
+        pProps->cBits       = cBits;
+        pProps->fSigned     = fSigned;
+        pProps->cChannels   = pCfg->cChannels;
+        pProps->cShift      = (pCfg->cChannels == 2) + cShift;
+        pProps->uAlign      = (1 << pProps->cShift) - 1;
+        pProps->cbPerSec    = pProps->uHz << pProps->cShift;
+        pProps->fSwapEndian = pCfg->enmEndianness != PDMAUDIOHOSTENDIANNESS;
+    }
+
+    return rc;
+}
+
+void drvAudioStreamCfgPrint(PPDMAUDIOSTREAMCFG pCfg)
+{
+    LogFlowFunc(("uHz=%RU32, cChannels=%RU8, enmFormat=",
+                 pCfg->uHz, pCfg->cChannels));
+
+    switch (pCfg->enmFormat)
+    {
+        case AUD_FMT_S8:
+            LogFlow(("S8"));
+            break;
+        case AUD_FMT_U8:
+            LogFlow(("U8"));
+            break;
+        case AUD_FMT_S16:
+            LogFlow(("S16"));
+            break;
+        case AUD_FMT_U16:
+            LogFlow(("U16"));
+            break;
+        case AUD_FMT_S32:
+            LogFlow(("S32"));
+            break;
+        case AUD_FMT_U32:
+            LogFlow(("U32"));
+            break;
+        default:
+            LogFlow(("invalid(%d)", pCfg->enmFormat));
+            break;
+    }
+
+    LogFlow((", endianness="));
+    switch (pCfg->enmEndianness)
+    {
+        case PDMAUDIOENDIANNESS_LITTLE:
+            LogFlow(("little\n"));
+            break;
+        case PDMAUDIOENDIANNESS_BIG:
+            LogFlow(("big\n"));
+            break;
+        default:
+            LogFlow(("invalid\n"));
+            break;
+    }
+}
Index: /trunk/src/VBox/Devices/Audio_old/DrvHostALSAAudio.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DrvHostALSAAudio.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DrvHostALSAAudio.cpp	(revision 61413)
@@ -0,0 +1,1421 @@
+/* $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                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
+#include <VBox/log.h>
+#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"
+
+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;
+
+/** Maximum number of tries to recover a broken pipe. */
+#define ALSA_RECOVERY_TRIES_MAX    5
+
+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, PDMAUDIOENDIANNESS *pEndianness)
+{
+    AssertPtrReturn(pFmt, VERR_INVALID_POINTER);
+    /* pEndianness is optional. */
+
+    switch (fmt)
+    {
+        case SND_PCM_FORMAT_S8:
+            *pFmt = AUD_FMT_S8;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+
+        case SND_PCM_FORMAT_U8:
+            *pFmt = AUD_FMT_U8;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+
+        case SND_PCM_FORMAT_S16_LE:
+            *pFmt = AUD_FMT_S16;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+
+        case SND_PCM_FORMAT_U16_LE:
+            *pFmt = AUD_FMT_U16;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+
+        case SND_PCM_FORMAT_S16_BE:
+            *pFmt = AUD_FMT_S16;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANNESS_BIG;
+            break;
+
+        case SND_PCM_FORMAT_U16_BE:
+            *pFmt = AUD_FMT_U16;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANNESS_BIG;
+            break;
+
+        case SND_PCM_FORMAT_S32_LE:
+            *pFmt = AUD_FMT_S32;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+
+        case SND_PCM_FORMAT_U32_LE:
+            *pFmt = AUD_FMT_U32;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+
+        case SND_PCM_FORMAT_S32_BE:
+            *pFmt = AUD_FMT_S32;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANNESS_BIG;
+            break;
+
+        case SND_PCM_FORMAT_U32_BE:
+            *pFmt = AUD_FMT_U32;
+            if (pEndianness)
+                *pEndianness = PDMAUDIOENDIANNESS_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 device: %s\n", pszDev, fIn ? "input" : "output", snd_strerror(err)));
+            rc = VERR_AUDIO_BACKEND_INIT_FAILED;
+            break;
+        }
+
+        LogRel(("ALSA: Using %s device \"%s\"\n", fIn ? "input" : "output", pszDev));
+
+        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_AUDIO_BACKEND_INIT_FAILED;
+            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_AUDIO_BACKEND_INIT_FAILED;
+            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_AUDIO_BACKEND_INIT_FAILED;
+            break;
+        }
+
+        err = snd_pcm_hw_params_set_rate_near(phPCM, pHWParms, &uFreq, 0);
+        if (err < 0)
+        {
+            LogRel(("ALSA: Failed to set frequency to %uHz: %s\n", pCfgReq->freq, snd_strerror(err)));
+            rc = VERR_AUDIO_BACKEND_INIT_FAILED;
+            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_AUDIO_BACKEND_INIT_FAILED;
+            break;
+        }
+
+        if (   cChannels != 1
+            && cChannels != 2)
+        {
+            LogRel(("ALSA: Number of audio channels (%u) not supported\n", cChannels));
+            rc = VERR_AUDIO_BACKEND_INIT_FAILED;
+            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_AUDIO_BACKEND_INIT_FAILED;
+                        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_AUDIO_BACKEND_INIT_FAILED;
+                    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_AUDIO_BACKEND_INIT_FAILED;
+                        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_AUDIO_BACKEND_INIT_FAILED;
+                        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_AUDIO_BACKEND_INIT_FAILED;
+                    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_AUDIO_BACKEND_INIT_FAILED;
+                    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_AUDIO_BACKEND_INIT_FAILED;
+            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_AUDIO_BACKEND_INIT_FAILED;
+            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_AUDIO_BACKEND_INIT_FAILED;
+            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_AUDIO_BACKEND_INIT_FAILED;
+            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)
+        {
+            LogRel(("ALSA: Error stopping stream %p: %s\n", phPCM, snd_strerror(err)));
+            return VERR_ACCESS_DENIED;
+        }
+    }
+    else
+    {
+        err = snd_pcm_prepare(phPCM);
+        if (err < 0)
+        {
+            LogRel(("ALSA: 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;
+        }
+    }
+
+    /*
+     * Check how much we can read from the capture device without overflowing
+     * the mixer buffer.
+     */
+    Assert(cAvail);
+    size_t cbMixFree = AudioMixBufFreeBytes(&pHstStrmIn->MixBuf);
+    size_t cbToRead = RT_MIN((size_t)AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cAvail), cbMixFree);
+
+    LogFlowFunc(("cbToRead=%zu, cAvail=%RI32\n", cbToRead, cAvail));
+
+    uint32_t cWrittenTotal = 0;
+    snd_pcm_uframes_t cToRead;
+    snd_pcm_sframes_t cRead;
+
+    while (   cbToRead
+           && RT_SUCCESS(rc))
+    {
+        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(("No input frames available\n"));
+                    rc = VERR_ACCESS_DENIED;
+                    break;
+                }
+
+                case -EAGAIN:
+                {
+                    /*
+                     * Don't set error here because EAGAIN means there are no further frames
+                     * available at the moment, try later. As we might have read some frames
+                     * already these need to be processed instead.
+                     */
+                    cbToRead = 0;
+                    break;
+                }
+
+                case -EPIPE:
+                {
+                    rc = drvHostALSAAudioRecover(pThisStrmIn->phPCM);
+                    if (RT_FAILURE(rc))
+                        break;
+
+                    LogFlowFunc(("Recovered from capturing\n"));
+                    continue;
+                }
+
+                default:
+                {
+                    LogFunc(("Failed to read input frames: %s\n", snd_strerror(cRead)));
+                    rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
+                    break;
+                }
+            }
+        }
+        else
+        {
+            uint32_t cWritten;
+            rc = AudioMixBufWriteCirc(&pHstStrmIn->MixBuf,
+                                      pThisStrmIn->pvBuf, AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cRead),
+                                      &cWritten);
+            if (RT_FAILURE(rc))
+                break;
+
+            /*
+             * We should not run into a full mixer buffer or we loose samples and
+             * run into an endless loop if ALSA keeps producing samples ("null"
+             * capture device for example).
+             */
+            AssertLogRelMsgBreakStmt(cWritten > 0, ("Mixer buffer shouldn't be full at this point!\n"),
+                                     rc = VERR_INTERNAL_ERROR);
+            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,
+                                                 (uint32_t)cAvail), /* cAvail is always >= 0 */
+                                 AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf,
+                                                 AudioMixBufAvail(&pHstStrmOut->MixBuf)));
+        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);
+
+            /* Don't try infinitely on recoverable errors. */
+            unsigned iTry;
+            for (iTry = 0; iTry < ALSA_RECOVERY_TRIES_MAX; iTry++)
+            {
+                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;
+                    }
+                }
+                else
+                    break;
+            } /* For number of tries. */
+
+            if (   iTry == ALSA_RECOVERY_TRIES_MAX
+                && cWritten <= 0)
+                rc = VERR_BROKEN_PIPE;
+
+            if (RT_FAILURE(rc))
+                break;
+
+            Assert(cbToRead >= cbRead);
+            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;
+        PDMAUDIOENDIANNESS 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;
+        PDMAUDIOENDIANNESS 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:
+        case PDMAUDIOSTREAMCMD_RESUME:
+            rc = drvHostALSAAudioStreamCtl(pThisStrmIn->phPCM, false /* fStop */);
+            break;
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        case PDMAUDIOSTREAMCMD_PAUSE:
+            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:
+        case PDMAUDIOSTREAMCMD_RESUME:
+            rc = drvHostALSAAudioStreamCtl(pThisStrmOut->phPCM, false /* fStop */);
+            break;
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        case PDMAUDIOSTREAMCMD_PAUSE:
+            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->cbStreamIn      = sizeof(ALSAAUDIOSTREAMIN);
+    pCfg->cbStreamOut     = sizeof(ALSAAUDIOSTREAMOUT);
+
+    /* ALSA only allows one input and one output used at a time for
+     * the selected device. */
+    pCfg->cMaxHstStrmsIn  = 1;
+    pCfg->cMaxHstStrmsOut = 1;
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(void) drvHostALSAAudioShutdown(PPDMIHOSTAUDIO pInterface)
+{
+    NOREF(pInterface);
+}
+
+/**
+ * @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;
+}
+
+/**
+ * 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 */
+    NULL,
+    /* 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_old/DrvHostCoreAudio.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DrvHostCoreAudio.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DrvHostCoreAudio.cpp	(revision 61413)
@@ -0,0 +1,2188 @@
+/* $Id$ */
+/** @file
+ * VBox audio devices: Mac OS X CoreAudio audio driver.
+ */
+
+/*
+ * Copyright (C) 2010-2016 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.
+ */
+#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
+#include <VBox/log.h>
+
+#include "DrvAudio.h"
+#include "AudioMixBuffer.h"
+
+#include "VBoxDD.h"
+
+#include <iprt/asm.h>
+#include <iprt/cdefs.h>
+#include <iprt/circbuf.h>
+#include <iprt/mem.h>
+
+#include <iprt/uuid.h>
+
+#include <CoreAudio/CoreAudio.h>
+#include <CoreServices/CoreServices.h>
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioConverter.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
+ *
+ ******************************************************************************/
+
+static void drvHostCoreAudioPrintASBDesc(const char *pszDesc, const AudioStreamBasicDescription *pStreamDesc)
+{
+    char pszSampleRate[32];
+    LogRel2(("CoreAudio: %s description:\n", pszDesc));
+    LogRel2(("CoreAudio: 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)));
+    LogRel2(("CoreAudio: Flags: %RU32", pStreamDesc->mFormatFlags));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsFloat)
+        LogRel2((" Float"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsBigEndian)
+        LogRel2((" BigEndian"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsSignedInteger)
+        LogRel2((" SignedInteger"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsPacked)
+        LogRel2((" Packed"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsAlignedHigh)
+        LogRel2((" AlignedHigh"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsNonInterleaved)
+        LogRel2((" NonInterleaved"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagIsNonMixable)
+        LogRel2((" NonMixable"));
+    if (pStreamDesc->mFormatFlags & kAudioFormatFlagsAreAllClear)
+        LogRel2((" AllClear"));
+    LogRel2(("\n"));
+    snprintf(pszSampleRate, 32, "%.2f", (float)pStreamDesc->mSampleRate); /** @todo r=andy Use RTStrPrint*. */
+    LogRel2(("CoreAudio: SampleRate      : %s\n", pszSampleRate));
+    LogRel2(("CoreAudio: ChannelsPerFrame: %RU32\n", pStreamDesc->mChannelsPerFrame));
+    LogRel2(("CoreAudio: FramesPerPacket : %RU32\n", pStreamDesc->mFramesPerPacket));
+    LogRel2(("CoreAudio: BitsPerChannel  : %RU32\n", pStreamDesc->mBitsPerChannel));
+    LogRel2(("CoreAudio: BytesPerFrame   : %RU32\n", pStreamDesc->mBytesPerFrame));
+    LogRel2(("CoreAudio: BytesPerPacket  : %RU32\n", pStreamDesc->mBytesPerPacket));
+}
+
+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 with VBox. */
+    AudioStreamBasicDescription streamFormat;
+    /** The audio device ID of the currently used device. */
+    AudioDeviceID               deviceID;
+    /** The AudioUnit being used. */
+    AudioUnit                   audioUnit;
+    /** A ring buffer for transferring data to the playback 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;
+    /** Flag whether the "default device changed" listener was registered. */
+    bool                        fDefDevChgListReg;
+} 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 with 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           pConverter;
+    /** Native buffer used for render the audio data in the recording thread. */
+    AudioBufferList             bufferList;
+    /** Reading offset for the bufferList's buffer. */
+    uint32_t                    offBufferRead;
+    /** The ratio between the device & the stream sample rate. */
+    Float64                     sampleRatio;
+    /** 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;
+    /** Flag whether the "default device changed" listener was registered. */
+    bool                        fDefDevChgListReg;
+} 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)
+{
+    OSStatus err = noErr;
+
+    LogFlowFunc(("propertyID=%u nAddresses=%u pvUser=%p\n", propertyID, nAddresses, pvUser));
+
+    for (UInt32 idxAddress = 0; idxAddress < nAddresses; idxAddress++)
+    {
+        const AudioObjectPropertyAddress *pProperty = &properties[idxAddress];
+
+        switch (pProperty->mSelector)
+        {
+            case kAudioHardwarePropertyDefaultInputDevice:
+            {
+                PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser;
+
+                /* This listener is called on every change of the hardware
+                 * device. So check if the default device has really changed. */
+                UInt32 uSize = sizeof(pStreamIn->deviceID);
+                UInt32 uResp;
+                err = AudioObjectGetPropertyData(kAudioObjectSystemObject, pProperty, 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;
+            }
+
+            case kAudioHardwarePropertyDefaultOutputDevice:
+            {
+                PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pvUser;
+
+                /* This listener is called on every change of the hardware
+                 * device. So check if the default device has really changed. */
+                AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice,
+                                                       kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+
+                UInt32 uSize = sizeof(pStreamOut->deviceID);
+                UInt32 uResp;
+                err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &uResp);
+
+                if (err == noErr)
+                {
+                    if (pStreamOut->deviceID != uResp)
+                    {
+                        LogRel(("CoreAudio: Default output 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(&pStreamOut->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;
+
+    int rc = drvHostCoreAudioFiniIn(pInterface, &pStreamIn->streamIn);
+    if (RT_SUCCESS(rc))
+    {
+        rc = drvHostCoreAudioInitInput(&pStreamIn->streamIn, NULL /* pcSamples */);
+        if (RT_SUCCESS(rc))
+            rc = drvHostCoreAudioControlIn(pInterface, &pStreamIn->streamIn, PDMAUDIOSTREAMCMD_ENABLE);
+    }
+
+    if (RT_FAILURE(rc))
+        LogRel(("CoreAudio: Unable to re-init input stream: %Rrc\n", rc));
+
+    return rc;
+}
+
+static int drvHostCoreAudioReinitOutput(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    PPDMDRVINS pDrvIns      = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
+
+    PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
+
+    int rc = drvHostCoreAudioFiniOut(pInterface, &pStreamOut->streamOut);
+    if (RT_SUCCESS(rc))
+    {
+        rc = drvHostCoreAudioInitOutput(&pStreamOut->streamOut, NULL /* pcSamples */);
+        if (RT_SUCCESS(rc))
+            rc = drvHostCoreAudioControlOut(pInterface, &pStreamOut->streamOut, PDMAUDIOSTREAMCMD_ENABLE);
+    }
+
+    if (RT_FAILURE(rc))
+        LogRel(("CoreAudio: Unable to re-init output stream: %Rrc\n", rc));
+
+    return rc;
+}
+
+/* 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->offBufferRead);
+    UInt32 cSize = RT_MIN(*pcPackets * pStreamIn->deviceFormat.mBytesPerPacket,
+                          pBufferList->mBuffers[0].mDataByteSize - pStreamIn->offBufferRead);
+
+    /* 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->offBufferRead;
+
+        pStreamIn->offBufferRead += 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->pConverter)
+        {
+            /* 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)
+            {
+                LogRel2(("CoreAudio: Failed rendering converted audio input 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;
+
+            /* Set the read position to zero. */
+            pStreamIn->offBufferRead = 0;
+
+            /* 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->pConverter);
+
+                err = AudioConverterFillComplexBuffer(pStreamIn->pConverter, 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. */
+        {
+            AssertBreakStmt(pStreamIn->streamFormat.mChannelsPerFrame >= 1,    rc = VERR_INVALID_PARAMETER);
+            AssertBreakStmt(pStreamIn->streamFormat.mBytesPerFrame >= 1,       rc = VERR_INVALID_PARAMETER);
+
+            AssertBreakStmt(pStreamIn->bufferList.mNumberBuffers >= 1,         rc = VERR_INVALID_PARAMETER);
+            AssertBreakStmt(pStreamIn->bufferList.mBuffers[0].mNumberChannels, rc = VERR_INVALID_PARAMETER);
+
+            pStreamIn->bufferList.mBuffers[0].mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;
+            pStreamIn->bufferList.mBuffers[0].mDataByteSize   = pStreamIn->streamFormat.mBytesPerFrame * cFrames;
+            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)
+            {
+                LogRel2(("CoreAudio: Failed rendering non-coverted audio input data (%RI32)\n", err));
+                rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */
+                break;
+            }
+
+            const uint32_t cbDataSize = pStreamIn->bufferList.mBuffers[0].mDataByteSize;
+            const size_t   cbBufFree  = RTCircBufFree(pStreamIn->pBuf);
+                  size_t   cbAvail    = RT_MIN(cbDataSize, cbBufFree);
+
+            LogFlowFunc(("cbDataSize=%RU32, cbBufFree=%zu, cbAvail=%zu\n", cbDataSize, cbBufFree, cbAvail));
+
+            /* 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;
+            }
+
+            LogFlowFunc(("cbWrittenTotal=%RU32, cbLeft=%zu\n", cbWrittenTotal, cbAvail));
+        }
+
+    } while (0);
+
+    if (pStreamIn->bufferList.mBuffers[0].mData)
+    {
+        RTMemFree(pStreamIn->bufferList.mBuffers[0].mData);
+        pStreamIn->bufferList.mBuffers[0].mData = NULL;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return err;
+}
+
+/** @todo Eventually split up this function, as this already is huge! */
+static int drvHostCoreAudioInitInput(PPDMAUDIOHSTSTRMIN pHstStrmIn, uint32_t *pcSamples)
+{
+    OSStatus err = noErr;
+
+    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 = kAudioDevicePropertyDeviceUID;
+            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. */
+    UInt32 cFrames;
+    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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    LogFlowFunc(("cFrames=%RU32\n", cFrames));
+
+    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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* Switch the I/O mode for output 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /*
+     * 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* Create an AudioStreamBasicDescription based on our required audio settings. */
+    drvHostCoreAudioPCMInfoToASBDesc(&pStreamIn->streamIn.Props, &pStreamIn->streamFormat);
+
+    drvHostCoreAudioPrintASBDesc("CoreAudio: Input device", &pStreamIn->deviceFormat);
+    drvHostCoreAudioPrintASBDesc("CoreAudio: Input stream", &pStreamIn->streamFormat);
+
+    /* 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)
+    {
+        LogRel(("CoreAudio: Input converter is active\n"));
+
+        err = AudioConverterNew(&pStreamIn->deviceFormat, &pStreamIn->streamFormat, &pStreamIn->pConverter);
+        if (RT_UNLIKELY(err != noErr))
+        {
+            LogRel(("CoreAudio: Failed to create the audio converter (%RI32)\n", err));
+            return VERR_AUDIO_BACKEND_INIT_FAILED;
+        }
+
+        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->pConverter, kAudioConverterChannelMap, sizeof(channelMap), channelMap);
+            if (err != noErr)
+            {
+                LogRel(("CoreAudio: Failed to set channel mapping (mono -> stereo) for the audio input converter (%RI32)\n", err));
+                return VERR_AUDIO_BACKEND_INIT_FAILED;
+            }
+        }
+#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
+
+        /* Set the new format description for the stream. */
+        err = AudioUnitSetProperty(pStreamIn->audioUnit,
+                                   kAudioUnitProperty_StreamFormat,
+                                   kAudioUnitScope_Output,
+                                   1,
+                                   &pStreamIn->deviceFormat,
+                                   sizeof(pStreamIn->deviceFormat));
+        if (RT_UNLIKELY(err != noErr))
+        {
+            LogRel(("CoreAudio: Failed to set input stream output format (%RI32)\n", err));
+            return VERR_AUDIO_BACKEND_INIT_FAILED;
+        }
+
+        err = AudioUnitSetProperty(pStreamIn->audioUnit,
+                                   kAudioUnitProperty_StreamFormat,
+                                   kAudioUnitScope_Input,
+                                   1,
+                                   &pStreamIn->deviceFormat,
+                                   sizeof(pStreamIn->deviceFormat));
+        if (RT_UNLIKELY(err != noErr))
+        {
+            LogRel(("CoreAudio: Failed to set stream input format (%RI32)\n", err));
+            return VERR_AUDIO_BACKEND_INIT_FAILED;
+        }
+    }
+    else
+    {
+
+        /* Set the new output format description for the input stream. */
+        err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
+                                   1, &pStreamIn->streamFormat, sizeof(pStreamIn->streamFormat));
+        if (err != noErr)
+        {
+            LogRel(("CoreAudio: Failed to set output format for input stream (%RI32)\n", err));
+            return VERR_AUDIO_BACKEND_INIT_FAILED;
+        }
+    }
+
+    /*
+     * 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /*
+     * 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* Destroy any former internal ring buffer. */
+    if (pStreamIn->pBuf)
+    {
+        RTCircBufDestroy(pStreamIn->pBuf);
+        pStreamIn->pBuf = NULL;
+    }
+
+    /* Calculate the ratio between the device and the stream sample rate. */
+    pStreamIn->sampleRatio = pStreamIn->streamFormat.mSampleRate / pStreamIn->deviceFormat.mSampleRate;
+
+    /* 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;
+
+    int rc = VINF_SUCCESS;
+
+    /*
+     * 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.
+     */
+    UInt32 cSamples = RT_MAX(cFrames,
+                             (cFrames * pStreamIn->deviceFormat.mBytesPerFrame * pStreamIn->sampleRatio)
+                              / pStreamIn->streamFormat.mBytesPerFrame)
+                             * pStreamIn->streamFormat.mChannelsPerFrame;
+    if (!cSamples)
+    {
+        LogRel(("CoreAudio: Failed to determine samples buffer count input stream\n"));
+        rc = VERR_INVALID_PARAMETER;
+    }
+
+    /* Create the internal ring buffer. */
+    if (RT_SUCCESS(rc))
+        rc = RTCircBufCreate(&pStreamIn->pBuf, cSamples << pHstStrmIn->Props.cShift);
+    if (RT_SUCCESS(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));
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_INIT);
+
+        if (pcSamples)
+            *pcSamples = cSamples;
+    }
+    else
+    {
+        AudioUnitUninitialize(pStreamIn->audioUnit);
+
+        if (pStreamIn->pBuf)
+        {
+            RTCircBufDestroy(pStreamIn->pBuf);
+            pStreamIn->pBuf = NULL;
+        }
+    }
+
+    LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc));
+    return rc;
+}
+
+/** @todo Eventually split up this function, as this already is huge! */
+static int drvHostCoreAudioInitOutput(PPDMAUDIOHSTSTRMOUT pHstStrmOut, uint32_t *pcSamples)
+{
+    PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
+
+    ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_INIT);
+
+    OSStatus err = noErr;
+
+    UInt32 uSize = 0;
+    if (pStreamOut->deviceID == kAudioDeviceUnknown)
+    {
+        /* Fetch the default audio input device currently in use. */
+        AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice,
+                                               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 output 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 = kAudioDevicePropertyDeviceUID;
+            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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /*
+     * 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* Create an AudioStreamBasicDescription based on our required audio settings. */
+    drvHostCoreAudioPCMInfoToASBDesc(&pStreamOut->streamOut.Props, &pStreamOut->streamFormat);
+
+    drvHostCoreAudioPrintASBDesc("CoreAudio: Output device", &pStreamOut->deviceFormat);
+    drvHostCoreAudioPrintASBDesc("CoreAudio: Output format", &pStreamOut->streamFormat);
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /*
+     * 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /* 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_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /*
+     * 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));
+
+        AudioUnitUninitialize(pStreamOut->audioUnit);
+        return VERR_AUDIO_BACKEND_INIT_FAILED;
+    }
+
+    /*
+     * 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.
+     */
+    int rc = VINF_SUCCESS;
+
+    UInt32 cSamples = cFrames * pStreamOut->streamFormat.mChannelsPerFrame;
+    if (!cSamples)
+    {
+        LogRel(("CoreAudio: Failed to determine samples buffer count output stream\n"));
+        rc = VERR_INVALID_PARAMETER;
+    }
+
+    /* Destroy any former internal ring buffer. */
+    if (pStreamOut->pBuf)
+    {
+        RTCircBufDestroy(pStreamOut->pBuf);
+        pStreamOut->pBuf = NULL;
+    }
+
+    /* Create the internal ring buffer. */
+    rc = RTCircBufCreate(&pStreamOut->pBuf, cSamples << pHstStrmOut->Props.cShift);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Register callbacks.
+         */
+#ifdef DEBUG
+        propAdr.mSelector = kAudioDeviceProcessorOverload;
+        propAdr.mScope    = kAudioUnitScope_Global;
+        err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr,
+                                             drvHostCoreAudioPlaybackAudioDevicePropertyChanged, (void *)pStreamOut);
+        if (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 (err != noErr)
+            LogRel(("CoreAudio: Failed to register sample rate changed listener for output stream (%RI32)\n", err));
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_INIT);
+
+        if (pcSamples)
+            *pcSamples = cSamples;
+    }
+    else
+    {
+        AudioUnitUninitialize(pStreamOut->audioUnit);
+
+        if (pStreamOut->pBuf)
+        {
+            RTCircBufDestroy(pStreamOut->pBuf);
+            pStreamOut->pBuf = NULL;
+        }
+    }
+
+    LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc));
+    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, RTCircBufUsed(pStreamIn->pBuf));
+
+        uint32_t cWritten, cbWritten;
+        uint8_t *puBuf;
+        size_t   cbToRead;
+
+        LogFlowFunc(("cbBuf=%zu, cbToWrite=%zu\n", cbBuf, cbToWrite));
+
+        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)
+                || !cWritten)
+            {
+                RTCircBufReleaseReadBlock(pStreamIn->pBuf, cbToRead);
+                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;
+        }
+
+        LogFlowFunc(("cbToWrite=%zu, cbToRead=%zu, cbWrittenTotal=%RU32, rc=%Rrc\n", cbToWrite, cbToRead, cbWrittenTotal, rc));
+    }
+    while (0);
+
+    if (RT_SUCCESS(rc))
+    {
+        uint32_t cCaptured     = 0;
+        uint32_t cWrittenTotal = AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, cbWrittenTotal);
+        if (cWrittenTotal)
+            rc = AudioMixBufMixToParent(&pHstStrmIn->MixBuf, cWrittenTotal, &cCaptured);
+
+        LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 bytes), cCaptured=%RU32, rc=%Rrc\n", cWrittenTotal, cbWrittenTotal, cCaptured, rc));
+
+        if (pcSamplesCaptured)
+            *pcSamplesCaptured = cCaptured;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    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)
+            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;
+        Assert(pBufData->mBuffers[0].mDataByteSize >= cbRead);
+
+        Assert(cbToRead <= cbDataAvail);
+        cbDataAvail -= 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;
+
+    int rc = VINF_SUCCESS;
+
+    /* Check if the audio device should be reinitialized. If so do it. */
+    if (ASMAtomicReadU32(&pStreamOut->status) == CA_STATUS_REINIT)
+    {
+        rc = drvHostCoreAudioReinitOutput(pInterface, &pStreamOut->streamOut);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    /* Not much else to do here. */
+
+    uint32_t cLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);;
+    if (!cLive) /* Not samples to play? Bail out. */
+    {
+        if (pcSamplesPlayed)
+            *pcSamplesPlayed = 0;
+        return VINF_SUCCESS;
+    }
+
+    uint32_t cbReadTotal = 0;
+    uint32_t cAvail = AudioMixBufAvail(&pHstStrmOut->MixBuf);
+    size_t cbAvail  = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cAvail);
+    size_t cbToRead = RT_MIN(cbAvail, RTCircBufFree(pStreamOut->pBuf));
+    LogFlowFunc(("cbToRead=%zu\n", cbToRead));
+
+    while (cbToRead)
+    {
+        uint32_t cRead, cbRead;
+        uint8_t *puBuf;
+        size_t   cbCopy;
+
+        /* Try to acquire the necessary space from the ring buffer. */
+        RTCircBufAcquireWriteBlock(pStreamOut->pBuf, cbToRead, (void **)&puBuf, &cbCopy);
+        if (!cbCopy)
+        {
+            RTCircBufReleaseWriteBlock(pStreamOut->pBuf, cbCopy);
+            break;
+        }
+
+        Assert(cbCopy <= cbToRead);
+
+        rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf,
+                                 puBuf, cbCopy, &cRead);
+
+        if (   RT_FAILURE(rc)
+            || !cRead)
+        {
+            RTCircBufReleaseWriteBlock(pStreamOut->pBuf, 0);
+            break;
+        }
+
+        cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
+
+        /* Release the ring buffer, so the read thread could start reading this data. */
+        RTCircBufReleaseWriteBlock(pStreamOut->pBuf, cbRead);
+
+        Assert(cbToRead >= cbRead);
+        cbToRead -= cbRead;
+        cbReadTotal += cbRead;
+    }
+
+    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;
+    OSStatus err;
+
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        case PDMAUDIOSTREAMCMD_RESUME:
+        {
+            /* Only start the device if it is actually stopped */
+            if (!drvHostCoreAudioIsRunning(pStreamOut->deviceID))
+            {
+                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));
+                    rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
+                }
+            }
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        case PDMAUDIOSTREAMCMD_PAUSE:
+        {
+            /* Only stop the device if it is actually running */
+            if (drvHostCoreAudioIsRunning(pStreamOut->deviceID))
+            {
+                err = AudioOutputUnitStop(pStreamOut->audioUnit);
+                if (err != noErr)
+                {
+                    LogRel(("CoreAudio: Failed to stop playback (%RI32)\n", err));
+                    rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
+                    break;
+                }
+
+                err = AudioUnitReset(pStreamOut->audioUnit, kAudioUnitScope_Input, 0);
+                if (err != noErr)
+                {
+                    LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
+                    rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
+                }
+            }
+            break;
+        }
+
+        default:
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    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;
+    OSStatus err;
+
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        case PDMAUDIOSTREAMCMD_RESUME:
+        {
+            /* 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;
+                }
+            }
+
+            if (err != noErr)
+            {
+                LogRel(("CoreAudio: Failed to start recording (%RI32)\n", err));
+                rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
+            }
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        case PDMAUDIOSTREAMCMD_PAUSE:
+        {
+            /* Only stop the device if it is actually running */
+            if (drvHostCoreAudioIsRunning(pStreamIn->deviceID))
+            {
+                err = AudioOutputUnitStop(pStreamIn->audioUnit);
+                if (err != noErr)
+                {
+                    LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));
+                    rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
+                    break;
+                }
+
+                err = AudioUnitReset(pStreamIn->audioUnit, kAudioUnitScope_Input, 0);
+                if (err != noErr)
+                {
+                    LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));
+                    rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
+                    break;
+                }
+            }
+            break;
+        }
+
+        default:
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN) pHstStrmIn;
+
+    PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTCOREAUDIO  pThis   = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO);
+
+    LogFlowFuncEnter();
+
+    uint32_t status = ASMAtomicReadU32(&pStreamIn->status);
+    if (!(   status == CA_STATUS_INIT
+          || status == CA_STATUS_REINIT))
+    {
+        return VINF_SUCCESS;
+    }
+
+    OSStatus err = noErr;
+
+    int rc = drvHostCoreAudioControlIn(pInterface, &pStreamIn->streamIn, PDMAUDIOSTREAMCMD_DISABLE);
+    if (RT_SUCCESS(rc))
+    {
+        ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_UNINIT);
+
+        /*
+         * Unregister input device callbacks.
+         */
+        AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,
+                                               kAudioObjectPropertyElementMaster };
+#ifdef DEBUG
+        err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,
+                                                drvHostCoreAudioRecordingAudioDevicePropertyChanged, pStreamIn);
+        /* 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, pStreamIn);
+        /* Not Fatal */
+        if (RT_UNLIKELY(err != noErr))
+            LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err));
+
+        if (pStreamIn->fDefDevChgListReg)
+        {
+            propAdr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
+            err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
+                                                    drvHostCoreAudioDefaultDeviceChanged, pStreamIn);
+            if (RT_LIKELY(err == noErr))
+            {
+                pStreamIn->fDefDevChgListReg = false;
+            }
+            else
+                LogRel(("CoreAudio: [Output] Failed to remove the default input device changed listener (%RI32)\n", err));
+        }
+
+        if (pStreamIn->pConverter)
+        {
+            AudioConverterDispose(pStreamIn->pConverter);
+            pStreamIn->pConverter = NULL;
+        }
+
+        err = AudioUnitUninitialize(pStreamIn->audioUnit);
+        if (RT_LIKELY(err == noErr))
+        {
+            err = CloseComponent(pStreamIn->audioUnit);
+            if (RT_LIKELY(err == noErr))
+            {
+                pStreamIn->deviceID      = kAudioDeviceUnknown;
+                pStreamIn->audioUnit     = NULL;
+                pStreamIn->offBufferRead = 0;
+                pStreamIn->sampleRatio   = 1;
+                if (pStreamIn->pBuf)
+                {
+                    RTCircBufDestroy(pStreamIn->pBuf);
+                    pStreamIn->pBuf = NULL;
+                }
+
+                ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT);
+            }
+            else
+            {
+                LogRel(("CoreAudio: Failed to close the AudioUnit (%RI32)\n", err));
+                rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
+            }
+        }
+        else
+        {
+            LogRel(("CoreAudio: Failed to uninitialize the AudioUnit (%RI32)\n", err));
+            rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
+        }
+    }
+    else
+    {
+        LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));
+        rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+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);
+
+        OSStatus err;
+
+        /*
+         * Unregister playback device callbacks.
+         */
+        AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,
+                                               kAudioObjectPropertyElementMaster };
+#ifdef DEBUG
+        err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr,
+                                                drvHostCoreAudioPlaybackAudioDevicePropertyChanged, pStreamOut);
+        /* 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(pStreamOut->deviceID, &propAdr,
+                                                drvHostCoreAudioPlaybackAudioDevicePropertyChanged, pStreamOut);
+        /* Not Fatal */
+        if (RT_UNLIKELY(err != noErr))
+            LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err));
+
+        if (pStreamOut->fDefDevChgListReg)
+        {
+            propAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
+            propAdr.mScope    = kAudioObjectPropertyScopeGlobal;
+            propAdr.mElement  = kAudioObjectPropertyElementMaster;
+            err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,
+                                                    drvHostCoreAudioDefaultDeviceChanged, pStreamOut);
+            if (RT_LIKELY(err == noErr))
+            {
+                pStreamOut->fDefDevChgListReg = false;
+            }
+            else
+                LogRel(("CoreAudio: [Output] Failed to remove the default playback device changed listener (%RI32)\n", err));
+        }
+
+        err = AudioUnitUninitialize(pStreamOut->audioUnit);
+        if (err == noErr)
+        {
+            err = CloseComponent(pStreamOut->audioUnit);
+            if (err == noErr)
+            {
+                pStreamOut->deviceID  = kAudioDeviceUnknown;
+                pStreamOut->audioUnit = NULL;
+                if (pStreamOut->pBuf)
+                {
+                    RTCircBufDestroy(pStreamOut->pBuf);
+                    pStreamOut->pBuf = NULL;
+                }
+
+                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));
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioInitIn(PPDMIHOSTAUDIO pInterface,
+                                                PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
+                                                PDMAUDIORECSOURCE enmRecSource,
+                                                uint32_t *pcSamples)
+{
+    PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pHstStrmIn;
+
+    LogFlowFunc(("enmRecSource=%ld\n", enmRecSource));
+
+    pStreamIn->deviceID                  = kAudioDeviceUnknown;
+    pStreamIn->audioUnit                 = NULL;
+    pStreamIn->pConverter                = NULL;
+    pStreamIn->bufferList.mNumberBuffers = 0;
+    pStreamIn->offBufferRead             = 0;
+    pStreamIn->sampleRatio               = 1;
+    pStreamIn->pBuf                      = NULL;
+    pStreamIn->status                    = CA_STATUS_UNINIT;
+    pStreamIn->fDefDevChgListReg         = false;
+
+    bool fDeviceByUser = false; /* Do we use a device which was set by the user? */
+
+    /* Initialize the hardware info section with the audio settings */
+    int rc = DrvAudioStreamCfgToProps(pCfg, &pStreamIn->streamIn.Props);
+    if (RT_SUCCESS(rc))
+    {
+#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_SUCCESS(rc))
+    {
+        /* When the devices isn't forced by the user, we want default device change notifications. */
+        if (!fDeviceByUser)
+        {
+            AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
+                                                   kAudioObjectPropertyElementMaster };
+            OSStatus err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
+                                                          drvHostCoreAudioDefaultDeviceChanged, (void *)pStreamIn);
+            /* Not fatal. */
+            if (RT_LIKELY(err == noErr))
+            {
+                pStreamIn->fDefDevChgListReg = true;
+            }
+            else
+                LogRel(("CoreAudio: Failed to add the default input device changed listener (%RI32)\n", err));
+        }
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostCoreAudioInitOut(PPDMIHOSTAUDIO pInterface,
+                                                 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
+                                                 uint32_t *pcSamples)
+{
+    PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pHstStrmOut;
+
+    LogFlowFuncEnter();
+
+    pStreamOut->deviceID                  = kAudioDeviceUnknown;
+    pStreamOut->audioUnit                 = NULL;
+    pStreamOut->pBuf                      = NULL;
+    pStreamOut->status                    = CA_STATUS_UNINIT;
+    pStreamOut->fDefDevChgListReg         = false;
+
+    bool fDeviceByUser = false; /* Do we use a device which was set by the user? */
+
+    /* Initialize the hardware info section with the audio settings */
+    int rc = DrvAudioStreamCfgToProps(pCfg, &pStreamOut->streamOut.Props);
+    if (RT_SUCCESS(rc))
+    {
+#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_SUCCESS(rc))
+    {
+        /* When the devices isn't forced by the user, we want default device change notifications. */
+        if (!fDeviceByUser)
+        {
+            AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
+                                                   kAudioObjectPropertyElementMaster };
+            OSStatus err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,
+                                                          drvHostCoreAudioDefaultDeviceChanged, (void *)pStreamOut);
+            /* Not fatal. */
+            if (RT_LIKELY(err == noErr))
+            {
+                pStreamOut->fDefDevChgListReg = true;
+            }
+            else
+                LogRel(("CoreAudio: Failed to add the default output device changed listener (%RI32)\n", err));
+        }
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+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) drvHostCoreAudioShutdown(PPDMIHOSTAUDIO pInterface)
+{
+    NOREF(pInterface);
+}
+
+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;
+}
+
+ /* 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 */
+    NULL,
+    /* 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_old/DrvHostDSound.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DrvHostDSound.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DrvHostDSound.cpp	(revision 61413)
@@ -0,0 +1,2289 @@
+/* $Id$ */
+/** @file
+ * Windows host backend driver using DirectSound.
+ */
+
+/*
+ * Copyright (C) 2006-2016 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.
+ * --------------------------------------------------------------------
+ *
+ */
+#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
+#include <VBox/log.h>
+#include <dsound.h>
+
+#include <iprt/alloc.h>
+#include <iprt/uuid.h>
+
+#include "AudioMixBuffer.h"
+#include "DrvAudio.h"
+#include "VBoxDD.h"
+
+/*
+ * IDirectSound* interface uses HRESULT status codes and the driver callbacks use
+ * the IPRT status codes. To minimize HRESULT->IPRT conversion most internal functions
+ * in the driver return HRESULT and conversion is done in the driver callbacks.
+ *
+ * Naming convention:
+ * 'dsound*' functions return IPRT status code;
+ * 'directSound*' - return HRESULT.
+ */
+
+/*
+ * Optional release logging, which a user can turn on with the
+ * 'VBoxManage debugvm' command.
+ * Debug logging still uses the common Log* macros from IPRT.
+ * Messages which always should go to the release log use LogRel.
+ */
+/* General code behavior. */
+#define DSLOG(a) do { LogRel2(a); } while(0)
+/* Something which produce a lot of logging during playback/recording. */
+#define DSLOGF(a) do { LogRel3(a); } while(0)
+/* Important messages like errors. Limited in the default release log to avoid log flood. */
+#define DSLOGREL(a)                 \
+    do {                            \
+        static int8_t scLogged = 0; \
+        if (scLogged < 8) {         \
+            ++scLogged;             \
+            LogRel(a);              \
+        }                           \
+        else {                      \
+            DSLOG(a);               \
+        }                           \
+    } while (0)
+
+/* Dynamically load dsound.dll. */
+typedef HRESULT WINAPI FNDIRECTSOUNDENUMERATEW(LPDSENUMCALLBACKW pDSEnumCallback, LPVOID pContext);
+typedef FNDIRECTSOUNDENUMERATEW *PFNDIRECTSOUNDENUMERATEW;
+typedef HRESULT WINAPI FNDIRECTSOUNDCAPTUREENUMERATEW(LPDSENUMCALLBACKW pDSEnumCallback, LPVOID pContext);
+typedef FNDIRECTSOUNDCAPTUREENUMERATEW *PFNDIRECTSOUNDCAPTUREENUMERATEW;
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+# define VBOX_DSOUND_MAX_EVENTS 3
+
+typedef enum DSOUNDEVENT
+{
+    DSOUNDEVENT_NOTIFY = 0,
+    DSOUNDEVENT_INPUT,
+    DSOUNDEVENT_OUTPUT,
+ } DSOUNDEVENT;
+#endif /* VBOX_WITH_AUDIO_CALLBACKS */
+
+typedef struct DSOUNDHOSTCFG
+{
+    DWORD   cbBufferIn;
+    DWORD   cbBufferOut;
+    RTUUID  uuidPlay;
+    LPCGUID pGuidPlay;
+    RTUUID  uuidCapture;
+    LPCGUID pGuidCapture;
+} DSOUNDHOSTCFG, *PDSOUNDHOSTCFG;
+
+typedef struct DSOUNDSTREAMOUT
+{
+    PDMAUDIOHSTSTRMOUT   strmOut; /* Always must come first! */
+    LPDIRECTSOUND8       pDS;
+    LPDIRECTSOUNDBUFFER8 pDSB;
+    DWORD                cbPlayWritePos;
+    DWORD                csPlaybackBufferSize;
+    bool                 fEnabled;
+    bool                 fRestartPlayback;
+    PDMAUDIOSTREAMCFG    streamCfg;
+} DSOUNDSTREAMOUT, *PDSOUNDSTREAMOUT;
+
+typedef struct DSOUNDSTREAMIN
+{
+    PDMAUDIOHSTSTRMIN           strmIn; /* Always must come first! */
+    LPDIRECTSOUNDCAPTURE8       pDSC;
+    LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB;
+    DWORD                       csCaptureReadPos;
+    DWORD                       csCaptureBufferSize;
+    HRESULT                     hrLastCaptureIn;
+    PDMAUDIORECSOURCE           enmRecSource;
+    bool                        fEnabled;
+    PDMAUDIOSTREAMCFG           streamCfg;
+} DSOUNDSTREAMIN, *PDSOUNDSTREAMIN;
+
+typedef struct DRVHOSTDSOUND
+{
+    /** Pointer to the driver instance structure. */
+    PPDMDRVINS          pDrvIns;
+    /** Our audio host audio interface. */
+    PDMIHOSTAUDIO       IHostAudio;
+    /** List of found host input devices. */
+    RTLISTANCHOR        lstDevInput;
+    /** List of found host output devices. */
+    RTLISTANCHOR        lstDevOutput;
+    /** DirectSound configuration options. */
+    DSOUNDHOSTCFG       cfg;
+    /** Whether this backend supports any audio input. */
+    bool                fEnabledIn;
+    /** Whether this backend supports any audio output. */
+    bool                fEnabledOut;
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    /** Pointer to the audio connector interface of the driver/device above us. */
+    PPDMIAUDIOCONNECTOR pUpIAudioConnector;
+    /** Stopped indicator. */
+    bool                fStopped;
+    /** Shutdown indicator. */
+    bool                fShutdown;
+    /** Notification thread. */
+    RTTHREAD            Thread;
+    /** Array of events to wait for in notification thread. */
+    HANDLE              aEvents[VBOX_DSOUND_MAX_EVENTS];
+    /** Number of events to wait for in notification thread.
+     *  Must not exceed VBOX_DSOUND_MAX_EVENTS. */
+    uint8_t             cEvents;
+    /** Pointer to the input stream. */
+    PDSOUNDSTREAMIN     pDSStrmIn;
+    /** Pointer to the output stream. */
+    PDSOUNDSTREAMOUT    pDSStrmOut;
+#endif
+} DRVHOSTDSOUND, *PDRVHOSTDSOUND;
+
+/** No flags specified. */
+#define DSOUNDENUMCBFLAGS_NONE          0
+/** (Release) log found devices. */
+#define DSOUNDENUMCBFLAGS_LOG           RT_BIT(0)
+
+/**
+ * Callback context for enumeration callbacks
+ */
+typedef struct DSOUNDENUMCBCTX
+{
+    PDRVHOSTDSOUND      pDrv;
+    PPDMAUDIOBACKENDCFG pCfg;
+    /** Enumeration flags. */
+    uint32_t            fFlags;
+} DSOUNDENUMCBCTX, *PDSOUNDENUMCBCTX;
+
+typedef struct DSOUNDDEV
+{
+    RTLISTNODE  Node;
+    char       *pszName;
+    GUID        Guid;
+} DSOUNDDEV, *PDSOUNDDEV;
+
+/** Maximum number of attempts to restore the sound buffer before giving up. */
+#define DRV_DSOUND_RESTORE_ATTEMPTS_MAX         3
+
+/** Makes DRVHOSTDSOUND out of PDMIHOSTAUDIO. */
+#define PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface) \
+    ( (PDRVHOSTDSOUND)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTDSOUND, IHostAudio)) )
+
+static HRESULT directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB);
+
+static void dsoundDeviceRemove(PDSOUNDDEV pDev);
+static int dsoundDevicesEnumerate(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDCFG pCfg);
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+static int dsoundNotifyThread(PDRVHOSTDSOUND pThis, bool fShutdown);
+#endif
+
+static DWORD dsoundRingDistance(DWORD offEnd, DWORD offBegin, DWORD cSize)
+{
+    return offEnd >= offBegin ? offEnd - offBegin : cSize - offBegin + offEnd;
+}
+
+static int dsoundWaveFmtFromCfg(PPDMAUDIOSTREAMCFG pCfg, PWAVEFORMATEX pFmt)
+{
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+    AssertPtrReturn(pFmt, VERR_INVALID_POINTER);
+
+    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 int dsoundGetPosOut(PDRVHOSTDSOUND   pThis,
+                           PDSOUNDSTREAMOUT pDSoundStrmOut, DWORD *pdwBuffer, DWORD *pdwFree, DWORD *pdwPlayPos)
+{
+    AssertPtrReturn(pThis,          VERR_INVALID_POINTER);
+    AssertPtrReturn(pDSoundStrmOut, VERR_INVALID_POINTER);
+
+    LPDIRECTSOUNDBUFFER8 pDSB = pDSoundStrmOut->pDSB;
+    if (!pDSB)
+        return VERR_INVALID_POINTER;
+
+    DWORD cbBuffer = AUDIOMIXBUF_S2B(&pDSoundStrmOut->strmOut.MixBuf, pDSoundStrmOut->csPlaybackBufferSize);
+
+    /* Get the current play position which is used for calculating the free space in the buffer. */
+    DWORD cbPlayPos;
+
+    HRESULT hr;
+    for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
+    {
+        hr = IDirectSoundBuffer8_GetCurrentPosition(pDSB, &cbPlayPos, NULL);
+        if (   SUCCEEDED(hr)
+            || hr != DSERR_BUFFERLOST) /** @todo: MSDN doesn't state this error for GetCurrentPosition(). */
+        {
+            break;
+        }
+        else
+        {
+            LogFlowFunc(("Getting playing position failed due to lost buffer, restoring ...\n"));
+            directSoundPlayRestore(pThis, pDSB);
+        }
+    }
+
+    int rc = VINF_SUCCESS;
+
+    if (FAILED(hr))
+    {
+        if (hr != DSERR_BUFFERLOST) /* Avoid log flooding if the error is still there. */
+            DSLOGREL(("DSound: Getting current playback position failed with %Rhrc\n", hr));
+        LogFlowFunc(("Failed with %Rhrc\n", hr));
+
+        rc = VERR_NOT_AVAILABLE;
+    }
+    else
+    {
+        if (pdwBuffer)
+            *pdwBuffer = cbBuffer;
+
+        if (pdwFree)
+            *pdwFree = cbBuffer - dsoundRingDistance(pDSoundStrmOut->cbPlayWritePos, cbPlayPos, cbBuffer);
+
+        if (pdwPlayPos)
+            *pdwPlayPos = cbPlayPos;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static char *dsoundGUIDToUtf8StrA(LPCGUID lpGUID)
+{
+    if (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 RTStrDup("{Default device}");
+}
+
+/**
+ * Clears the list of the host's playback + capturing devices.
+ *
+ * @param   pThis               Host audio driver instance.
+ */
+static void dsoundDevicesClear(PDRVHOSTDSOUND pThis)
+{
+    AssertPtrReturnVoid(pThis);
+
+    PDSOUNDDEV pDev;
+    while (!RTListIsEmpty(&pThis->lstDevInput))
+    {
+        pDev = RTListGetFirst(&pThis->lstDevInput, DSOUNDDEV, Node);
+        dsoundDeviceRemove(pDev);
+    }
+
+    while (!RTListIsEmpty(&pThis->lstDevOutput))
+    {
+        pDev = RTListGetFirst(&pThis->lstDevOutput, DSOUNDDEV, Node);
+        dsoundDeviceRemove(pDev);
+    }
+}
+
+static HRESULT directSoundPlayRestore(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB)
+{
+    HRESULT hr = IDirectSoundBuffer8_Restore(pDSB);
+    if (FAILED(hr))
+        DSLOGREL(("DSound: Restoring playback buffer failed with %Rhrc\n", hr));
+    return hr;
+}
+
+static HRESULT directSoundPlayUnlock(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB,
+                                     LPVOID pv1, LPVOID pv2,
+                                     DWORD cb1, DWORD cb2)
+{
+    HRESULT hr = IDirectSoundBuffer8_Unlock(pDSB, pv1, cb1, pv2, cb2);
+    if (FAILED(hr))
+        DSLOGREL(("DSound: Unlocking playback buffer failed with %Rhrc\n", hr));
+    return hr;
+}
+
+static HRESULT directSoundCaptureUnlock(LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB,
+                                        LPVOID pv1, LPVOID pv2,
+                                        DWORD cb1, DWORD cb2)
+{
+    HRESULT hr = IDirectSoundCaptureBuffer8_Unlock(pDSCB, pv1, cb1, pv2, cb2);
+    if (FAILED(hr))
+        DSLOGREL(("DSound: Unlocking capture buffer failed with %Rhrc\n", hr));
+    return hr;
+}
+
+static HRESULT directSoundPlayLock(PDRVHOSTDSOUND pThis,
+                                   LPDIRECTSOUNDBUFFER8 pDSB, PDMPCMPROPS *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;
+    for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
+    {
+        hr = IDirectSoundBuffer8_Lock(pDSB, dwOffset, dwBytes, &pv1, &cb1, &pv2, &cb2, dwFlags);
+        if (   SUCCEEDED(hr)
+            || hr != DSERR_BUFFERLOST)
+            break;
+        else
+        {
+            LogFlowFunc(("Locking failed due to lost buffer, restoring ...\n"));
+            directSoundPlayRestore(pThis, pDSB);
+        }
+    }
+
+    if (FAILED(hr))
+    {
+        DSLOGREL(("DSound: Locking playback buffer failed with %Rhrc\n", hr));
+        return hr;
+    }
+
+    if (   (pv1 && (cb1 & pProps->uAlign))
+        || (pv2 && (cb2 & pProps->uAlign)))
+    {
+        DSLOGREL(("DSound: Locking playback buffer returned misaligned buffer: cb1=%RI32, cb2=%RI32 (alignment: %RU32)\n",
+                  cb1, cb2, pProps->uAlign));
+        directSoundPlayUnlock(pThis, pDSB, pv1, pv2, cb1, cb2);
+        return E_FAIL;
+    }
+
+    *ppv1 = pv1;
+    *ppv2 = pv2;
+    *pcb1 = cb1;
+    *pcb2 = cb2;
+
+    return S_OK;
+}
+
+static HRESULT directSoundCaptureLock(LPDIRECTSOUNDCAPTUREBUFFER8 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 = IDirectSoundCaptureBuffer8_Lock(pDSCB, dwOffset, dwBytes,
+                                                 &pv1, &cb1, &pv2, &cb2, dwFlags);
+    if (FAILED(hr))
+    {
+        DSLOGREL(("DSound: Locking capture buffer failed with %Rhrc\n", hr));
+        return hr;
+    }
+
+    if (   (pv1 && (cb1 & pProps->uAlign))
+        || (pv2 && (cb2 & pProps->uAlign)))
+    {
+        DSLOGREL(("DSound: Locking capture buffer returned misaligned buffer: cb1=%RI32, cb2=%RI32 (alignment: %RU32)\n",
+                  cb1, cb2, pProps->uAlign));
+        directSoundCaptureUnlock(pDSCB, pv1, pv2, cb1, cb2);
+        return E_FAIL;
+    }
+
+    *ppv1 = pv1;
+    *ppv2 = pv2;
+    *pcb1 = cb1;
+    *pcb2 = cb2;
+
+    return S_OK;
+}
+
+
+/*
+ * DirectSound playback
+ */
+
+static void directSoundPlayInterfaceRelease(PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    if (pDSoundStrmOut->pDS)
+    {
+        IDirectSound8_Release(pDSoundStrmOut->pDS);
+        pDSoundStrmOut->pDS = NULL;
+    }
+}
+
+static HRESULT directSoundPlayInterfaceCreate(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    if (pDSoundStrmOut->pDS != NULL)
+    {
+        DSLOG(("DSound: DirectSound instance already exists\n"));
+        return S_OK;
+    }
+
+    HRESULT hr = CoCreateInstance(CLSID_DirectSound8, NULL, CLSCTX_ALL,
+                                  IID_IDirectSound8, (void **)&pDSoundStrmOut->pDS);
+    if (FAILED(hr))
+    {
+        DSLOGREL(("DSound: Creating playback instance failed with %Rhrc\n", hr));
+    }
+    else
+    {
+        hr = IDirectSound8_Initialize(pDSoundStrmOut->pDS, pThis->cfg.pGuidPlay);
+        if (SUCCEEDED(hr))
+        {
+            HWND hWnd = GetDesktopWindow();
+            hr = IDirectSound8_SetCooperativeLevel(pDSoundStrmOut->pDS, hWnd, DSSCL_PRIORITY);
+            if (FAILED(hr))
+                DSLOGREL(("DSound: Setting cooperative level for window %p failed with %Rhrc\n", hWnd, hr));
+        }
+
+        if (FAILED(hr))
+        {
+            if (hr == DSERR_NODRIVER) /* Usually means that no playback devices are attached. */
+                DSLOGREL(("DSound: DirectSound playback is currently unavailable\n"));
+            else
+                DSLOGREL(("DSound: DirectSound playback initialization failed with %Rhrc\n", hr));
+
+            directSoundPlayInterfaceRelease(pDSoundStrmOut);
+        }
+    }
+
+    return hr;
+}
+
+static HRESULT directSoundPlayClose(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    AssertPtrReturn(pThis, E_POINTER);
+    AssertPtrReturn(pDSoundStrmOut, E_POINTER);
+
+    DSLOG(("DSound: Closing playback stream %p, buffer %p\n", pDSoundStrmOut, pDSoundStrmOut->pDSB));
+
+    HRESULT hr = S_OK;
+
+    if (pDSoundStrmOut->pDSB)
+    {
+        hr = IDirectSoundBuffer8_Stop(pDSoundStrmOut->pDSB);
+        if (SUCCEEDED(hr))
+        {
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+            if (pThis->aEvents[DSOUNDEVENT_OUTPUT] != NULL)
+            {
+                CloseHandle(pThis->aEvents[DSOUNDEVENT_OUTPUT]);
+                pThis->aEvents[DSOUNDEVENT_OUTPUT] = NULL;
+
+                if (pThis->cEvents)
+                    pThis->cEvents--;
+
+                pThis->pDSStrmOut = NULL;
+            }
+
+            int rc2 = dsoundNotifyThread(pThis, false /* fShutdown */);
+            AssertRC(rc2);
+#endif
+            IDirectSoundBuffer8_Release(pDSoundStrmOut->pDSB);
+            pDSoundStrmOut->pDSB = NULL;
+        }
+        else
+            DSLOGREL(("DSound: Stop playback stream %p when closing %Rhrc\n", pDSoundStrmOut, hr));
+    }
+
+    if (SUCCEEDED(hr))
+        directSoundPlayInterfaceRelease(pDSoundStrmOut);
+
+    return hr;
+}
+
+static HRESULT directSoundPlayOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    AssertPtrReturn(pThis, E_POINTER);
+    AssertPtrReturn(pDSoundStrmOut, E_POINTER);
+
+    DSLOG(("DSound: pDSoundStrmOut=%p, cbBufferOut=%ld, uHz=%RU32, cChannels=%RU8, cBits=%RU8, fSigned=%RTbool\n",
+           pDSoundStrmOut,
+           pThis->cfg.cbBufferOut,
+           pDSoundStrmOut->strmOut.Props.uHz,
+           pDSoundStrmOut->strmOut.Props.cChannels,
+           pDSoundStrmOut->strmOut.Props.cBits,
+           pDSoundStrmOut->strmOut.Props.fSigned));
+
+    if (pDSoundStrmOut->pDSB != NULL)
+    {
+        /* Should not happen but be forgiving. */
+        DSLOGREL(("DSound: Playback buffer already exists\n"));
+        directSoundPlayClose(pThis, pDSoundStrmOut);
+    }
+
+    WAVEFORMATEX wfx;
+    int rc = dsoundWaveFmtFromCfg(&pDSoundStrmOut->streamCfg, &wfx);
+    if (RT_FAILURE(rc))
+        return E_INVALIDARG;
+
+    HRESULT hr = directSoundPlayInterfaceCreate(pThis, pDSoundStrmOut);
+    if (FAILED(hr))
+        return hr;
+
+    do /* To use breaks. */
+    {
+        LPDIRECTSOUNDBUFFER pDSB = NULL;
+
+        DSBUFFERDESC bd;
+        RT_ZERO(bd);
+        bd.dwSize      = sizeof(bd);
+        bd.lpwfxFormat = &wfx;
+
+        /*
+         * As we reuse our (secondary) buffer for playing out data as it comes in,
+         * we're using this buffer as a so-called static buffer.
+         *
+         * However, as we do not want to use memory on the sound device directly
+         * (as most modern audio hardware on the host doesn't have this anyway),
+         * we're *not* going to use DSBCAPS_STATIC for that.
+         *
+         * Instead we're specifying DSBCAPS_LOCSOFTWARE, as this fits the bill
+         * of copying own buffer data (from AudioMixBuf) to our secondary's Direct Sound buffer.
+         */
+        bd.dwFlags     = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE;
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+        bd.dwFlags    |= DSBCAPS_CTRLPOSITIONNOTIFY;
+#endif
+        bd.dwBufferBytes = pThis->cfg.cbBufferOut;
+
+        hr = IDirectSound8_CreateSoundBuffer(pDSoundStrmOut->pDS, &bd, &pDSB, NULL);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: Creating playback sound buffer failed with %Rhrc\n", hr));
+            break;
+        }
+
+        /* "Upgrade" to IDirectSoundBuffer8 interface. */
+        hr = IDirectSoundBuffer_QueryInterface(pDSB, IID_IDirectSoundBuffer8, (LPVOID *)&pDSoundStrmOut->pDSB);
+        IDirectSoundBuffer_Release(pDSB);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: Querying playback sound buffer interface failed with %Rhrc\n", hr));
+            break;
+        }
+
+        /*
+         * Query the actual parameters.
+         */
+        hr = IDirectSoundBuffer8_GetFormat(pDSoundStrmOut->pDSB, &wfx, sizeof(wfx), NULL);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: Getting playback format failed with %Rhrc\n", hr));
+            break;
+        }
+
+        DSBCAPS bc;
+        RT_ZERO(bc);
+        bc.dwSize = sizeof(bc);
+        hr = IDirectSoundBuffer8_GetCaps(pDSoundStrmOut->pDSB, &bc);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: Getting playback capabilities failed with %Rhrc\n", hr));
+            break;
+        }
+
+        DSLOG(("DSound: Playback format:\n"
+               "  dwBufferBytes   = %RI32\n"
+               "  dwFlags         = 0x%x\n"
+               "  wFormatTag      = %RI16\n"
+               "  nChannels       = %RI16\n"
+               "  nSamplesPerSec  = %RU32\n"
+               "  nAvgBytesPerSec = %RU32\n"
+               "  nBlockAlign     = %RI16\n"
+               "  wBitsPerSample  = %RI16\n"
+               "  cbSize          = %RI16\n",
+               bc.dwBufferBytes,
+               bc.dwFlags,
+               wfx.wFormatTag,
+               wfx.nChannels,
+               wfx.nSamplesPerSec,
+               wfx.nAvgBytesPerSec,
+               wfx.nBlockAlign,
+               wfx.wBitsPerSample,
+               wfx.cbSize));
+
+        if (bc.dwBufferBytes & pDSoundStrmOut->strmOut.Props.uAlign)
+            DSLOGREL(("DSound: Playback capabilities returned misaligned buffer: size %RU32, alignment %RU32\n",
+                      bc.dwBufferBytes, pDSoundStrmOut->strmOut.Props.uAlign + 1));
+
+        if (bc.dwBufferBytes != pThis->cfg.cbBufferOut)
+            DSLOGREL(("DSound: Playback buffer size mismatched: DirectSound %RU32, requested %RU32 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->strmOut.Props.cShift;
+        DSLOG(("DSound: csPlaybackBufferSize=%RU32\n", pDSoundStrmOut->csPlaybackBufferSize));
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+        /*
+         * Install notification.
+         */
+        pThis->aEvents[DSOUNDEVENT_OUTPUT] = CreateEvent(NULL /* Security attribute */,
+                                                         FALSE /* bManualReset */, FALSE /* bInitialState */,
+                                                         NULL /* lpName */);
+        if (pThis->aEvents[DSOUNDEVENT_OUTPUT] == NULL)
+        {
+            hr = HRESULT_FROM_WIN32(GetLastError());
+            DSLOGREL(("DSound: CreateEvent for output failed with %Rhrc\n", hr));
+            break;
+        }
+
+        LPDIRECTSOUNDNOTIFY8 pNotify;
+        hr = IDirectSoundNotify_QueryInterface(pDSoundStrmOut->pDSB, IID_IDirectSoundNotify8, (LPVOID *)&pNotify);
+        if (SUCCEEDED(hr))
+        {
+            DSBPOSITIONNOTIFY dsBufPosNotify;
+            RT_ZERO(dsBufPosNotify);
+            dsBufPosNotify.dwOffset     = DSBPN_OFFSETSTOP;
+            dsBufPosNotify.hEventNotify = pThis->aEvents[DSOUNDEVENT_OUTPUT];
+
+            hr = IDirectSoundNotify_SetNotificationPositions(pNotify, 1 /* Count */, &dsBufPosNotify);
+            if (FAILED(hr))
+                DSLOGREL(("DSound: Setting playback position notification failed with %Rhrc\n", hr));
+
+            IDirectSoundNotify_Release(pNotify);
+        }
+        else
+            DSLOGREL(("DSound: Querying interface for position notification failed with %Rhrc\n", hr));
+
+        if (FAILED(hr))
+            break;
+
+        pThis->pDSStrmOut = pDSoundStrmOut;
+
+        Assert(pThis->cEvents < VBOX_DSOUND_MAX_EVENTS);
+        pThis->cEvents++;
+
+        /* Let the thread know. */
+        dsoundNotifyThread(pThis, false /* fShutdown */);
+
+        /* Trigger the just installed output notification. */
+        hr = IDirectSoundBuffer8_Play(pDSoundStrmOut->pDSB, 0, 0, 0);
+
+#endif /* VBOX_WITH_AUDIO_CALLBACKS */
+
+    } while (0);
+
+    if (FAILED(hr))
+        directSoundPlayClose(pThis, pDSoundStrmOut);
+
+    return hr;
+}
+
+static void dsoundPlayClearSamples(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    AssertPtrReturnVoid(pDSoundStrmOut);
+
+    PPDMAUDIOHSTSTRMOUT pStrmOut = &pDSoundStrmOut->strmOut;
+
+    LPVOID pv1, pv2;
+    DWORD cb1, cb2;
+    HRESULT hr = directSoundPlayLock(pThis, pDSoundStrmOut->pDSB, &pDSoundStrmOut->strmOut.Props,
+                                     0 /* dwOffset */, AUDIOMIXBUF_S2B(&pStrmOut->MixBuf, pDSoundStrmOut->csPlaybackBufferSize),
+                                     &pv1, &pv2, &cb1, &cb2, DSBLOCK_ENTIREBUFFER);
+    if (SUCCEEDED(hr))
+    {
+        DWORD len1 = AUDIOMIXBUF_B2S(&pStrmOut->MixBuf, cb1);
+        DWORD len2 = AUDIOMIXBUF_B2S(&pStrmOut->MixBuf, cb2);
+
+        if (pv1 && len1)
+            DrvAudioClearBuf(&pDSoundStrmOut->strmOut.Props, pv1, cb1, len1);
+
+        if (pv2 && len2)
+            DrvAudioClearBuf(&pDSoundStrmOut->strmOut.Props, pv2, cb2, len2);
+
+        directSoundPlayUnlock(pThis, pDSoundStrmOut->pDSB, pv1, pv2, cb1, cb2);
+    }
+}
+
+static HRESULT directSoundPlayGetStatus(PDRVHOSTDSOUND pThis, LPDIRECTSOUNDBUFFER8 pDSB, DWORD *pdwStatus)
+{
+    AssertPtrReturn(pThis, E_POINTER);
+    AssertPtrReturn(pDSB,  E_POINTER);
+    /* pdwStatus is optional. */
+
+    DWORD dwStatus = 0;
+
+    HRESULT hr;
+    for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
+    {
+        hr = IDirectSoundBuffer8_GetStatus(pDSB, &dwStatus);
+        if (   hr == DSERR_BUFFERLOST
+            || (   SUCCEEDED(hr)
+                && (dwStatus & DSBSTATUS_BUFFERLOST)))
+        {
+            LogFlowFunc(("Getting status failed due to lost buffer, restoring ...\n"));
+            directSoundPlayRestore(pThis, pDSB);
+        }
+        else
+            break;
+    }
+
+    if (SUCCEEDED(hr))
+    {
+        if (pdwStatus)
+            *pdwStatus = dwStatus;
+    }
+    else
+        DSLOGREL(("DSound: Retrieving playback status failed with %Rhrc\n", hr));
+
+    return hr;
+}
+
+static HRESULT directSoundPlayStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    AssertPtrReturn(pThis,          E_POINTER);
+    AssertPtrReturn(pDSoundStrmOut, E_POINTER);
+
+    HRESULT hr;
+
+    if (pDSoundStrmOut->pDSB != NULL)
+    {
+        DSLOG(("DSound: Stopping playback\n"));
+
+        HRESULT hr2 = IDirectSoundBuffer8_Stop(pDSoundStrmOut->pDSB);
+        if (FAILED(hr2))
+        {
+            hr2 = directSoundPlayRestore(pThis, pDSoundStrmOut->pDSB);
+            if (FAILED(hr2))
+                hr2 = IDirectSoundBuffer8_Stop(pDSoundStrmOut->pDSB);
+        }
+
+        if (FAILED(hr2))
+            DSLOG(("DSound: Stopping playback failed with %Rhrc\n", hr2));
+
+        hr = S_OK; /* Always report success here. */
+    }
+    else
+        hr = E_UNEXPECTED;
+
+    if (SUCCEEDED(hr))
+    {
+        dsoundPlayClearSamples(pThis, pDSoundStrmOut);
+        pDSoundStrmOut->fEnabled = false;
+    }
+    else
+        DSLOGREL(("DSound: Stopping playback failed with %Rhrc\n", hr));
+
+    return hr;
+}
+
+static HRESULT directSoundPlayStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMOUT pDSoundStrmOut)
+{
+    AssertPtrReturn(pThis,          E_POINTER);
+    AssertPtrReturn(pDSoundStrmOut, E_POINTER);
+
+    HRESULT hr;
+    if (pDSoundStrmOut->pDSB != NULL)
+    {
+        DWORD dwStatus;
+        hr = directSoundPlayGetStatus(pThis, pDSoundStrmOut->pDSB, &dwStatus);
+        if (SUCCEEDED(hr))
+        {
+            if (dwStatus & DSBSTATUS_PLAYING)
+            {
+                DSLOG(("DSound: Already playing\n"));
+            }
+            else
+            {
+                dsoundPlayClearSamples(pThis, pDSoundStrmOut);
+
+                pDSoundStrmOut->fRestartPlayback = true;
+                pDSoundStrmOut->fEnabled         = true;
+
+                DSLOG(("DSound: Playback started\n"));
+
+                /*
+                 * The actual IDirectSoundBuffer8_Play call will be made in drvHostDSoundPlayOut,
+                 * because it is necessary to put some samples into the buffer first.
+                 */
+            }
+        }
+    }
+    else
+        hr = E_UNEXPECTED;
+
+    if (FAILED(hr))
+        DSLOGREL(("DSound: Starting playback failed with %Rhrc\n", hr));
+
+    return hr;
+}
+
+/*
+ * DirectSoundCapture
+ */
+
+static LPCGUID dsoundCaptureSelectDevice(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    AssertPtrReturn(pThis, NULL);
+    AssertPtrReturn(pDSoundStrmIn, NULL);
+
+    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")) /** @todo what is with non en_us windows versions? */
+                        break;
+                }
+                if (RTListNodeIsDummy(&pThis->lstDevInput, pDev, DSOUNDDEV, Node))
+                    pDev = NULL;    /* Found nothing. */
+
+                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;
+        }
+    }
+
+    char *pszGUID = dsoundGUIDToUtf8StrA(pGUID);
+    /* This always has to be in the release log. */
+    LogRel(("DSound: Guest \"%s\" is using host device with GUID: %s\n",
+            drvAudioRecSourceToString(pDSoundStrmIn->enmRecSource), pszGUID? pszGUID: "{?}"));
+    RTStrFree(pszGUID);
+
+    return pGUID;
+}
+
+static void directSoundCaptureInterfaceRelease(PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    if (pDSoundStrmIn->pDSC)
+    {
+        LogFlowFuncEnter();
+        IDirectSoundCapture_Release(pDSoundStrmIn->pDSC);
+        pDSoundStrmIn->pDSC = NULL;
+    }
+}
+
+static HRESULT directSoundCaptureInterfaceCreate(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    if (pDSoundStrmIn->pDSC != NULL)
+    {
+        DSLOG(("DSound: DirectSoundCapture instance already exists\n"));
+        return S_OK;
+    }
+
+    HRESULT hr = CoCreateInstance(CLSID_DirectSoundCapture8, NULL, CLSCTX_ALL,
+                                  IID_IDirectSoundCapture8, (void **)&pDSoundStrmIn->pDSC);
+    if (FAILED(hr))
+    {
+        DSLOGREL(("DSound: Creating capture instance failed with %Rhrc\n", hr));
+    }
+    else
+    {
+        LPCGUID pGUID = dsoundCaptureSelectDevice(pThis, pDSoundStrmIn);
+        hr = IDirectSoundCapture_Initialize(pDSoundStrmIn->pDSC, pGUID);
+        if (FAILED(hr))
+        {
+            if (hr == DSERR_NODRIVER) /* Usually means that no capture devices are attached. */
+                DSLOGREL(("DSound: Capture device currently is unavailable\n"));
+            else
+                DSLOGREL(("DSound: Initializing capturing device failed with %Rhrc\n", hr));
+
+            directSoundCaptureInterfaceRelease(pDSoundStrmIn);
+        }
+    }
+
+    LogFlowFunc(("Returning %Rhrc\n", hr));
+    return hr;
+}
+
+static HRESULT directSoundCaptureClose(PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    AssertPtrReturn(pDSoundStrmIn, E_POINTER);
+
+    DSLOG(("DSound: pDSoundStrmIn=%p, pDSCB=%p\n", pDSoundStrmIn, pDSoundStrmIn->pDSCB));
+
+    HRESULT hr = S_OK;
+
+    if (pDSoundStrmIn->pDSCB)
+    {
+        hr = IDirectSoundCaptureBuffer_Stop(pDSoundStrmIn->pDSCB);
+        if (SUCCEEDED(hr))
+        {
+            IDirectSoundCaptureBuffer8_Release(pDSoundStrmIn->pDSCB);
+            pDSoundStrmIn->pDSCB = NULL;
+        }
+        else
+            DSLOGREL(("DSound: Stopping capture buffer failed with %Rhrc\n", hr));
+    }
+
+    if (SUCCEEDED(hr))
+        directSoundCaptureInterfaceRelease(pDSoundStrmIn);
+
+    LogFlowFunc(("Returning %Rhrc\n", hr));
+    return hr;
+}
+
+static HRESULT directSoundCaptureOpen(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    AssertPtrReturn(pThis, E_POINTER);
+    AssertPtrReturn(pDSoundStrmIn, E_POINTER);
+
+    DSLOG(("DSound: pDSoundStrmIn=%p, cbBufferIn=%ld, uHz=%RU32, cChannels=%RU8, cBits=%RU8, fSigned=%RTbool\n",
+           pDSoundStrmIn,
+           pThis->cfg.cbBufferIn,
+           pDSoundStrmIn->strmIn.Props.uHz,
+           pDSoundStrmIn->strmIn.Props.cChannels,
+           pDSoundStrmIn->strmIn.Props.cBits,
+           pDSoundStrmIn->strmIn.Props.fSigned));
+
+    if (pDSoundStrmIn->pDSCB != NULL)
+    {
+        /* Should not happen but be forgiving. */
+        DSLOGREL(("DSound: DirectSoundCaptureBuffer already exists\n"));
+        directSoundCaptureClose(pDSoundStrmIn);
+    }
+
+    WAVEFORMATEX wfx;
+    int rc = dsoundWaveFmtFromCfg(&pDSoundStrmIn->streamCfg, &wfx);
+    if (RT_FAILURE(rc))
+        return E_INVALIDARG;
+
+    HRESULT hr = directSoundCaptureInterfaceCreate(pThis, pDSoundStrmIn);
+    if (FAILED(hr))
+        return hr;
+
+    do /* To use breaks. */
+    {
+        LPDIRECTSOUNDCAPTUREBUFFER pDSCB = NULL;
+        DSCBUFFERDESC bd;
+        RT_ZERO(bd);
+        bd.dwSize = sizeof(bd);
+        bd.lpwfxFormat = &wfx;
+        bd.dwBufferBytes = pThis->cfg.cbBufferIn;
+        hr = IDirectSoundCapture_CreateCaptureBuffer(pDSoundStrmIn->pDSC,
+                                                     &bd, &pDSCB, NULL);
+        if (FAILED(hr))
+        {
+            if (hr == E_ACCESSDENIED)
+            {
+                DSLOGREL(("DSound: Capturing input from host not possible, access denied\n"));
+            }
+            else
+                DSLOGREL(("DSound: Creating capture buffer failed with %Rhrc\n", hr));
+            break;
+        }
+
+        hr = IDirectSoundCaptureBuffer_QueryInterface(pDSCB, IID_IDirectSoundCaptureBuffer8, (void **)&pDSoundStrmIn->pDSCB);
+        IDirectSoundCaptureBuffer_Release(pDSCB);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: Querying interface for capture buffer failed with %Rhrc\n", hr));
+            break;
+        }
+
+        /*
+         * Query the actual parameters.
+         */
+        DWORD cbReadPos = 0;
+        hr = IDirectSoundCaptureBuffer8_GetCurrentPosition(pDSoundStrmIn->pDSCB, NULL, &cbReadPos);
+        if (FAILED(hr))
+        {
+            cbReadPos = 0;
+            DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr));
+        }
+
+        RT_ZERO(wfx);
+        hr = IDirectSoundCaptureBuffer8_GetFormat(pDSoundStrmIn->pDSCB, &wfx, sizeof(wfx), NULL);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: Getting capture format failed with %Rhrc\n", hr));
+            break;
+        }
+
+        DSCBCAPS bc;
+        RT_ZERO(bc);
+        bc.dwSize = sizeof(bc);
+        hr = IDirectSoundCaptureBuffer8_GetCaps(pDSoundStrmIn->pDSCB, &bc);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("Getting capture capabilities failed with %Rhrc\n", hr));
+            break;
+        }
+
+        DSLOG(("DSound: Capture format:\n"
+               "  dwBufferBytes   = %RI32\n"
+               "  dwFlags         = 0x%x\n"
+               "  wFormatTag      = %RI16\n"
+               "  nChannels       = %RI16\n"
+               "  nSamplesPerSec  = %RU32\n"
+               "  nAvgBytesPerSec = %RU32\n"
+               "  nBlockAlign     = %RI16\n"
+               "  wBitsPerSample  = %RI16\n"
+               "  cbSize          = %RI16\n",
+               bc.dwBufferBytes,
+               bc.dwFlags,
+               wfx.wFormatTag,
+               wfx.nChannels,
+               wfx.nSamplesPerSec,
+               wfx.nAvgBytesPerSec,
+               wfx.nBlockAlign,
+               wfx.wBitsPerSample,
+               wfx.cbSize));
+
+        if (bc.dwBufferBytes & pDSoundStrmIn->strmIn.Props.uAlign)
+            DSLOGREL(("DSound: Capture GetCaps returned misaligned buffer: size %RU32, alignment %RU32\n",
+                      bc.dwBufferBytes, pDSoundStrmIn->strmIn.Props.uAlign + 1));
+
+        if (bc.dwBufferBytes != pThis->cfg.cbBufferIn)
+            DSLOGREL(("DSound: Capture buffer size mismatched: DirectSound %RU32, requested %RU32 bytes\n",
+                      bc.dwBufferBytes, pThis->cfg.cbBufferIn));
+
+        /* Initial state: reading at the initial capture position, no error. */
+        pDSoundStrmIn->csCaptureReadPos    = cbReadPos >> pDSoundStrmIn->strmIn.Props.cShift;
+        pDSoundStrmIn->csCaptureBufferSize = bc.dwBufferBytes >> pDSoundStrmIn->strmIn.Props.cShift;
+        pDSoundStrmIn->hrLastCaptureIn = S_OK;
+
+        DSLOG(("DSound: csCaptureReadPos=%RU32, csCaptureBufferSize=%RU32\n",
+                     pDSoundStrmIn->csCaptureReadPos, pDSoundStrmIn->csCaptureBufferSize));
+
+    } while (0);
+
+    if (FAILED(hr))
+        directSoundCaptureClose(pDSoundStrmIn);
+
+    LogFlowFunc(("Returning %Rhrc\n", hr));
+    return hr;
+}
+
+static HRESULT directSoundCaptureStop(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    AssertPtrReturn(pThis        , E_POINTER);
+    AssertPtrReturn(pDSoundStrmIn, E_POINTER);
+
+    NOREF(pThis);
+
+    HRESULT hr;
+
+    if (pDSoundStrmIn->pDSCB)
+    {
+        DSLOG(("DSound: Stopping capture\n"));
+
+        hr = IDirectSoundCaptureBuffer_Stop(pDSoundStrmIn->pDSCB);
+        if (FAILED(hr))
+            DSLOGREL(("DSound: Stopping capture buffer failed with %Rhrc\n", hr));
+    }
+    else
+        hr = E_UNEXPECTED;
+
+    if (SUCCEEDED(hr))
+        pDSoundStrmIn->fEnabled = false;
+
+    LogFlowFunc(("Returning %Rhrc\n", hr));
+    return hr;
+}
+
+static HRESULT directSoundCaptureStart(PDRVHOSTDSOUND pThis, PDSOUNDSTREAMIN pDSoundStrmIn)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pDSoundStrmIn, VERR_INVALID_POINTER);
+
+    HRESULT hr;
+    if (pDSoundStrmIn->pDSCB != NULL)
+    {
+        DWORD dwStatus;
+        hr = IDirectSoundCaptureBuffer8_GetStatus(pDSoundStrmIn->pDSCB, &dwStatus);
+        if (FAILED(hr))
+        {
+            DSLOGREL(("DSound: Retrieving capture status failed with %Rhrc\n", hr));
+        }
+        else
+        {
+            if (dwStatus & DSCBSTATUS_CAPTURING)
+            {
+                DSLOG(("DSound: Already capturing\n"));
+            }
+            else
+            {
+                DWORD fFlags = 0;
+#ifndef VBOX_WITH_AUDIO_CALLBACKS
+                fFlags |= DSCBSTART_LOOPING;
+#endif
+                DSLOG(("DSound: Starting to capture\n"));
+                hr = IDirectSoundCaptureBuffer8_Start(pDSoundStrmIn->pDSCB, fFlags);
+                if (FAILED(hr))
+                    DSLOGREL(("DSound: Starting to capture failed with %Rhrc\n", hr));
+            }
+        }
+    }
+    else
+        hr = E_UNEXPECTED;
+
+    if (SUCCEEDED(hr))
+        pDSoundStrmIn->fEnabled = true;
+
+    LogFlowFunc(("Returning %Rhrc\n", hr));
+    return hr;
+}
+
+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 dsoundDeviceRemove(PDSOUNDDEV pDev)
+{
+    if (pDev)
+    {
+        RTStrFree(pDev->pszName);
+        pDev->pszName = NULL;
+
+        RTListNodeRemove(&pDev->Node);
+
+        RTMemFree(pDev);
+    }
+}
+
+static void dsoundLogDevice(const char *pszType, LPGUID lpGUID, LPCWSTR lpwstrDescription, LPCWSTR lpwstrModule)
+{
+    char *pszGUID = dsoundGUIDToUtf8StrA(lpGUID);
+    /* This always has to be in the release log. */
+    LogRel(("DSound: %s: GUID: %s [%ls] (Module: %ls)\n",
+            pszType, pszGUID? pszGUID: "{?}", lpwstrDescription, lpwstrModule));
+    RTStrFree(pszGUID);
+}
+
+static BOOL CALLBACK dsoundDevicesEnumCbPlayback(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;
+
+    AssertPtrReturn(lpwstrDescription, FALSE);
+    /* Do not care about lpwstrModule. */
+
+    if (pCtx->fFlags & DSOUNDENUMCBFLAGS_LOG)
+        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 dsoundDevicesEnumCbCapture(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;
+
+    if (pCtx->fFlags & DSOUNDENUMCBFLAGS_LOG)
+        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;
+}
+
+/**
+ * Does a (Re-)enumeration of the host's playback + capturing devices.
+ *
+ * @return  IPRT status code.
+ * @param   pThis               Host audio driver instance.
+ * @param   pCfg                Where to store the enumeration results.
+ * @param   fEnum               Enumeration flags.
+ */
+static int dsoundDevicesEnumerate(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg,  VERR_INVALID_POINTER);
+
+    dsoundDevicesClear(pThis);
+
+    pCfg->cMaxHstStrmsOut = 0;
+    pCfg->cMaxHstStrmsIn  = 0;
+
+    RTLDRMOD hDSound = NULL;
+    int rc = RTLdrLoadSystem("dsound.dll", true /*fNoUnload*/, &hDSound);
+    if (RT_SUCCESS(rc))
+    {
+        PFNDIRECTSOUNDENUMERATEW pfnDirectSoundEnumerateW = NULL;
+        PFNDIRECTSOUNDCAPTUREENUMERATEW pfnDirectSoundCaptureEnumerateW = NULL;
+
+        rc = RTLdrGetSymbol(hDSound, "DirectSoundEnumerateW", (void**)&pfnDirectSoundEnumerateW);
+        if (RT_SUCCESS(rc))
+            rc = RTLdrGetSymbol(hDSound, "DirectSoundCaptureEnumerateW", (void**)&pfnDirectSoundCaptureEnumerateW);
+
+        if (RT_SUCCESS(rc))
+        {
+            DSOUNDENUMCBCTX ctx = { pThis, pCfg, fEnum };
+
+            HRESULT hr = pfnDirectSoundEnumerateW(&dsoundDevicesEnumCbPlayback, &ctx);
+            if (FAILED(hr))
+                LogRel2(("DSound: Error enumerating host playback devices: %Rhrc\n", hr));
+
+            hr = pfnDirectSoundCaptureEnumerateW(&dsoundDevicesEnumCbCapture, &ctx);
+            if (FAILED(hr))
+                LogRel2(("DSound: Error enumerating host capturing devices: %Rhrc\n", hr));
+        }
+
+        RTLdrClose(hDSound);
+    }
+    else
+    {
+        /* No dsound.dll on this system. */
+        LogRel2(("DSound: Could not load dsound.dll: %Rrc\n", rc));
+    }
+
+    return rc;
+}
+
+/**
+ * Updates this host driver's internal status, according to the global, overall input/output
+ * state and all connected (native) audio streams.
+ *
+ * @param   pThis               Host audio driver instance.
+ * @param   pCfg                Where to store the backend configuration. Optional.
+ * @param   fEnum               Enumeration flags.
+ */
+void dsoundUpdateStatusInternalEx(PDRVHOSTDSOUND pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
+{
+    AssertPtrReturnVoid(pThis);
+    /* pCfg is optional. */
+
+    PDMAUDIOBACKENDCFG Cfg;
+    RT_ZERO(Cfg);
+
+    Cfg.cbStreamOut = sizeof(DSOUNDSTREAMOUT);
+    Cfg.cbStreamIn  = sizeof(DSOUNDSTREAMIN);
+
+    int rc = dsoundDevicesEnumerate(pThis, &Cfg, fEnum);
+    AssertRC(rc);
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    if (   pThis->fEnabledOut != RT_BOOL(Cfg.cMaxHstStrmsOut)
+        || pThis->fEnabledIn  != RT_BOOL(Cfg.cMaxHstStrmsIn))
+    {
+        /** @todo Use a registered callback to the audio connector (e.g "OnConfigurationChanged") to
+         *        let the connector know that something has changed within the host backend. */
+    }
+#else
+    pThis->fEnabledOut = RT_BOOL(Cfg.cMaxHstStrmsOut);
+    pThis->fEnabledIn  = RT_BOOL(Cfg.cMaxHstStrmsIn);
+#endif
+
+    if (pCfg)
+        memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG));
+
+    LogFlowFuncLeaveRC(rc);
+}
+
+void dsoundUpdateStatusInternal(PDRVHOSTDSOUND pThis)
+{
+    dsoundUpdateStatusInternalEx(pThis, NULL /* pCfg */, 0 /* fEnum */);
+}
+
+/*
+ * PDMIHOSTAUDIO
+ */
+
+static DECLCALLBACK(int) drvHostDSoundInitOut(PPDMIHOSTAUDIO pInterface,
+                                              PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
+                                              uint32_t *pcSamples)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+    /* pcSamples is optional. */
+
+    LogFlowFunc(("pHstStrmOut=%p, pCfg=%p\n", pHstStrmOut, pCfg));
+
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+    PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
+
+    pDSoundStrmOut->streamCfg = *pCfg;
+    pDSoundStrmOut->streamCfg.enmEndianness = PDMAUDIOHOSTENDIANNESS;
+
+    int rc = DrvAudioStreamCfgToProps(&pDSoundStrmOut->streamCfg, &pDSoundStrmOut->strmOut.Props);
+    if (RT_SUCCESS(rc))
+    {
+        pDSoundStrmOut->pDS = NULL;
+        pDSoundStrmOut->pDSB = NULL;
+        pDSoundStrmOut->cbPlayWritePos = 0;
+        pDSoundStrmOut->fRestartPlayback = true;
+        pDSoundStrmOut->csPlaybackBufferSize = 0;
+
+        if (pcSamples)
+            *pcSamples = pThis->cfg.cbBufferOut >> pHstStrmOut->Props.cShift;
+
+        /* Try to open playback in case the device is already there. */
+        directSoundPlayOpen(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;
+
+    HRESULT hr;
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        case PDMAUDIOSTREAMCMD_RESUME:
+        {
+            DSLOG(("DSound: Playback PDMAUDIOSTREAMCMD_ENABLE\n"));
+            /* Try to start playback. If it fails, then reopen and try again. */
+            hr = directSoundPlayStart(pThis, pDSoundStrmOut);
+            if (FAILED(hr))
+            {
+                hr = directSoundPlayClose(pThis, pDSoundStrmOut);
+                if (SUCCEEDED(hr))
+                    hr = directSoundPlayOpen(pThis, pDSoundStrmOut);
+                if (SUCCEEDED(hr))
+                    hr = directSoundPlayStart(pThis, pDSoundStrmOut);
+            }
+
+            if (FAILED(hr))
+                rc = VERR_NOT_SUPPORTED;
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        case PDMAUDIOSTREAMCMD_PAUSE:
+        {
+            DSLOG(("DSound: Playback PDMAUDIOSTREAMCMD_DISABLE\n"));
+            hr = directSoundPlayStop(pThis, pDSoundStrmOut);
+            if (FAILED(hr))
+                rc = VERR_NOT_SUPPORTED;
+            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)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+    /* pcSamplesPlayed is optional. */
+
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+    PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
+
+    int rc = VINF_SUCCESS;
+    uint32_t cReadTotal = 0;
+
+#ifdef DEBUG_andy
+    LogFlowFuncEnter();
+#endif
+
+    do /* to use 'break' */
+    {
+        DWORD cbBuffer, cbFree, cbPlayPos;
+        rc = dsoundGetPosOut(pThis, pDSoundStrmOut, &cbBuffer, &cbFree, &cbPlayPos);
+        if (RT_FAILURE(rc))
+            break;
+
+        /*
+         * Check for full buffer, do not allow the cbPlayWritePos to catch cbPlayPos during playback,
+         * i.e. always leave a free space for 1 audio sample.
+         */
+        const DWORD cbSample = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, 1);
+        if (cbFree <= cbSample)
+            break;
+        cbFree     -= cbSample;
+
+        uint32_t csLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
+        uint32_t cbLive = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, csLive);
+
+        /* 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=%RU32, cbBuffer=%ld, cbPlayWritePos=%ld, cbPlayPos=%ld\n",
+                   cbLive, cbBuffer, pDSoundStrmOut->cbPlayWritePos, cbPlayPos));
+            break;
+        }
+
+        LPDIRECTSOUNDBUFFER8 pDSB = pDSoundStrmOut->pDSB;
+        AssertPtr(pDSB);
+
+        LPVOID pv1, pv2;
+        DWORD cb1, cb2;
+        HRESULT hr = directSoundPlayLock(pThis, pDSB, &pHstStrmOut->Props, pDSoundStrmOut->cbPlayWritePos, cbLive,
+                                         &pv1, &pv2, &cb1, &cb2, 0 /* dwFlags */);
+        if (FAILED(hr))
+        {
+            rc = VERR_ACCESS_DENIED;
+            break;
+        }
+
+        DWORD len1 = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cb1);
+        DWORD len2 = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cb2);
+
+        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;
+        }
+
+        directSoundPlayUnlock(pThis, pDSB, pv1, pv2, cb1, cb2);
+
+        pDSoundStrmOut->cbPlayWritePos =
+            (pDSoundStrmOut->cbPlayWritePos + AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal)) % cbBuffer;
+
+        DSLOGF(("DSound: %RU32 (%RU32 samples) out of %RU32%s, buffer write pos %ld, rc=%Rrc\n",
+                AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal), cReadTotal, cbLive,
+                cbLive != AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cReadTotal) ? " !!!": "",
+                pDSoundStrmOut->cbPlayWritePos, rc));
+
+        if (cReadTotal)
+        {
+            AudioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
+            rc = VINF_SUCCESS; /* Played something. */
+        }
+
+        if (RT_FAILURE(rc))
+            break;
+
+        if (pDSoundStrmOut->fRestartPlayback)
+        {
+            /*
+             * The playback has been just started.
+             * Some samples of the new sound have been copied to the buffer
+             * and it can start playing.
+             */
+            pDSoundStrmOut->fRestartPlayback = false;
+
+            DWORD fFlags = 0;
+#ifndef VBOX_WITH_AUDIO_CALLBACKS
+            fFlags |= DSCBSTART_LOOPING;
+#endif
+            for (unsigned i = 0; i < DRV_DSOUND_RESTORE_ATTEMPTS_MAX; i++)
+            {
+                hr = IDirectSoundBuffer8_Play(pDSoundStrmOut->pDSB, 0, 0, fFlags);
+                if (   SUCCEEDED(hr)
+                    || hr != DSERR_BUFFERLOST)
+                    break;
+                else
+                {
+                    LogFlowFunc(("Restarting playback failed due to lost buffer, restoring ...\n"));
+                    directSoundPlayRestore(pThis, pDSoundStrmOut->pDSB);
+                }
+            }
+
+            if (FAILED(hr))
+            {
+                DSLOGREL(("DSound: Starting playback failed with %Rhrc\n", hr));
+                rc = VERR_NOT_SUPPORTED;
+                break;
+            }
+        }
+
+    } while (0);
+
+    if (RT_FAILURE(rc))
+    {
+        dsoundUpdateStatusInternal(pThis);
+    }
+    else
+    {
+        if (pcSamplesPlayed)
+            *pcSamplesPlayed = cReadTotal;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostDSoundFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+    PDSOUNDSTREAMOUT pDSoundStrmOut = (PDSOUNDSTREAMOUT)pHstStrmOut;
+
+    directSoundPlayClose(pThis, pDSoundStrmOut);
+
+    pDSoundStrmOut->cbPlayWritePos = 0;
+    pDSoundStrmOut->fRestartPlayback = 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 = PDMAUDIOHOSTENDIANNESS;
+
+    /** @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. */
+        directSoundCaptureOpen(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;
+
+    HRESULT hr;
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        case PDMAUDIOSTREAMCMD_RESUME:
+        {
+            /* Try to start capture. If it fails, then reopen and try again. */
+            hr = directSoundCaptureStart(pThis, pDSoundStrmIn);
+            if (FAILED(hr))
+            {
+                hr = directSoundCaptureClose(pDSoundStrmIn);
+                if (SUCCEEDED(hr))
+                {
+                    hr = directSoundCaptureOpen(pThis, pDSoundStrmIn);
+                    if (SUCCEEDED(hr))
+                        hr = directSoundCaptureStart(pThis, pDSoundStrmIn);
+                }
+            }
+
+            if (FAILED(hr))
+                rc = VERR_NOT_SUPPORTED;
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        case PDMAUDIOSTREAMCMD_PAUSE:
+        {
+            hr = directSoundCaptureStop(pThis, pDSoundStrmIn);
+            if (FAILED(hr))
+                rc = VERR_NOT_SUPPORTED;
+            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;
+    LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB         = pDSoundStrmIn->pDSCB;
+
+    int rc = VINF_SUCCESS;
+
+    uint32_t cCaptured = 0;
+
+    do
+    {
+        if (pDSCB == NULL)
+        {
+            rc = VERR_NOT_AVAILABLE;
+            break;
+        }
+
+        /* Get DirectSound capture position in bytes. */
+        DWORD cbReadPos;
+        HRESULT hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pDSCB, NULL, &cbReadPos);
+        if (FAILED(hr))
+        {
+            if (hr != pDSoundStrmIn->hrLastCaptureIn)
+            {
+                DSLOGREL(("DSound: Getting capture position failed with %Rhrc\n", hr));
+                pDSoundStrmIn->hrLastCaptureIn = hr;
+            }
+
+            rc = VERR_NOT_AVAILABLE;
+            break;
+        }
+
+        pDSoundStrmIn->hrLastCaptureIn = hr;
+
+        if (cbReadPos & pHstStrmIn->Props.uAlign)
+            DSLOGF(("DSound: Misaligned capture read position %ld (alignment: %RU32)\n", cbReadPos, pHstStrmIn->Props.uAlign));
+
+        /* Capture position in samples. */
+        DWORD 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)
+            break;
+
+        /* 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 */
+        uint32_t csMixFree = AudioMixBufFree(&pHstStrmIn->MixBuf);
+        if (csMixFree == 0)
+        {
+            DSLOGF(("DSound: Capture buffer full\n"));
+            break;
+        }
+
+        DSLOGF(("DSound: Capture csMixFree=%RU32, csReadPos=%ld, csCaptureReadPos=%ld, csCaptured=%ld\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;
+        hr = directSoundCaptureLock(pDSCB, &pHstStrmIn->Props,
+                                    AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, pDSoundStrmIn->csCaptureReadPos), /* dwOffset */
+                                    AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, csCaptured),                      /* dwBytes */
+                                    &pv1, &pv2, &cb1, &cb2,
+                                    0 /* dwFlags */);
+        if (FAILED(hr))
+        {
+            rc = VERR_ACCESS_DENIED;
+            break;
+        }
+
+        DWORD len1 = AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, cb1);
+        DWORD len2 = AUDIOMIXBUF_B2S(&pHstStrmIn->MixBuf, cb2);
+
+        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;
+        }
+
+        directSoundCaptureUnlock(pDSCB, pv1, pv2, cb1, cb2);
+
+        if (csWrittenTotal) /* Captured something? */
+            rc = AudioMixBufMixToParent(&pHstStrmIn->MixBuf, csWrittenTotal, &cCaptured);
+
+        if (RT_SUCCESS(rc))
+        {
+            pDSoundStrmIn->csCaptureReadPos = (pDSoundStrmIn->csCaptureReadPos + cCaptured) % pDSoundStrmIn->csCaptureBufferSize;
+            DSLOGF(("DSound: Capture %ld (%ld+%ld), processed %RU32/%RU32\n",
+                    csCaptured, len1, len2, cCaptured, csWrittenTotal));
+        }
+
+    } while (0);
+
+    if (RT_FAILURE(rc))
+    {
+        dsoundUpdateStatusInternal(pThis);
+    }
+    else
+    {
+        if (pcSamplesCaptured)
+            *pcSamplesCaptured = cCaptured;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostDSoundFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+    PDSOUNDSTREAMIN pDSoundStrmIn = (PDSOUNDSTREAMIN)pHstStrmIn;
+
+    directSoundCaptureClose(pDSoundStrmIn);
+
+    pDSoundStrmIn->csCaptureReadPos    = 0;
+    pDSoundStrmIn->csCaptureBufferSize = 0;
+    RT_ZERO(pDSoundStrmIn->streamCfg);
+
+    return VINF_SUCCESS;
+}
+
+/** @todo Replace PDMAUDIODIR with a (registered? unique) channel ID to provide multi-channel input/output. */
+static DECLCALLBACK(bool) drvHostDSoundIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
+{
+    AssertPtrReturn(pInterface, false);
+
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+
+    if (enmDir == PDMAUDIODIR_IN)
+        return pThis->fEnabledIn;
+
+    return pThis->fEnabledOut;
+}
+
+static DECLCALLBACK(int) drvHostDSoundGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg,       VERR_INVALID_POINTER);
+
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+
+    dsoundUpdateStatusInternalEx(pThis, pCfg, 0 /* fEnum */);
+
+    return VINF_SUCCESS;
+}
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+static int dsoundNotifyThread(PDRVHOSTDSOUND pThis, bool fShutdown)
+{
+    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+
+    if (fShutdown)
+    {
+        LogFlowFunc(("Shutting down thread ...\n"));
+        pThis->fShutdown = fShutdown;
+    }
+
+    /* Set the notification event so that the thread is being notified. */
+    BOOL fRc = SetEvent(pThis->aEvents[DSOUNDEVENT_NOTIFY]);
+    Assert(fRc);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostDSoundThread(RTTHREAD hThreadSelf, void *pvUser)
+{
+    PDRVHOSTDSOUND pThis = (PDRVHOSTDSOUND)pvUser;
+    AssertPtr(pThis);
+
+    LogFlowFuncEnter();
+
+    /* Let caller know that we're done initializing, regardless of the result. */
+    int rc = RTThreadUserSignal(hThreadSelf);
+    AssertRC(rc);
+
+    do
+    {
+        HANDLE aEvents[VBOX_DSOUND_MAX_EVENTS];
+        DWORD  cEvents = 0;
+        for (uint8_t i = 0; i < VBOX_DSOUND_MAX_EVENTS; i++)
+        {
+            if (pThis->aEvents[i])
+                aEvents[cEvents++] = pThis->aEvents[i];
+        }
+        Assert(cEvents);
+
+        LogFlowFunc(("Waiting: cEvents=%ld\n", cEvents));
+
+        DWORD dwObj = WaitForMultipleObjects(cEvents, aEvents, FALSE /* bWaitAll */, INFINITE);
+        switch (dwObj)
+        {
+            case WAIT_FAILED:
+            {
+                rc = VERR_CANCELLED;
+                break;
+            }
+
+            case WAIT_TIMEOUT:
+            {
+                rc = VERR_TIMEOUT;
+                break;
+            }
+
+            default:
+            {
+                dwObj = WAIT_OBJECT_0 + cEvents - 1;
+                if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_NOTIFY])
+                {
+                    LogFlowFunc(("Notify\n"));
+                }
+                else if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_INPUT])
+                {
+
+                }
+                else if (aEvents[dwObj] == pThis->aEvents[DSOUNDEVENT_OUTPUT])
+                {
+                    DWORD cbBuffer, cbFree, cbPlayPos;
+                    rc = dsoundGetPosOut(pThis->pDSStrmOut, &cbBuffer, &cbFree, &cbPlayPos);
+                    if (   RT_SUCCESS(rc)
+                        && cbFree)
+                    {
+                        PDMAUDIOCALLBACKDATAOUT Out;
+                        Out.cbInFree     = cbFree;
+                        Out.cbOutWritten = 0;
+
+                        while (!Out.cbOutWritten)
+                        {
+                            rc = pThis->pUpIAudioConnector->pfnCallback(pThis->pUpIAudioConnector,
+                                                                        PDMAUDIOCALLBACKTYPE_OUTPUT, &Out, sizeof(Out));
+                            if (RT_FAILURE(rc))
+                                break;
+                            RTThreadSleep(100);
+                        }
+
+                        LogFlowFunc(("Output: cbBuffer=%ld, cbFree=%ld, cbPlayPos=%ld, cbWritten=%RU32, rc=%Rrc\n",
+                                     cbBuffer, cbFree, cbPlayPos, Out.cbOutWritten, rc));
+                    }
+                }
+                break;
+            }
+        }
+
+        if (pThis->fShutdown)
+            break;
+
+    } while (RT_SUCCESS(rc));
+
+    pThis->fStopped = true;
+
+    LogFlowFunc(("Exited with fShutdown=%RTbool, rc=%Rrc\n", pThis->fShutdown, rc));
+    return rc;
+}
+#endif /* VBOX_WITH_AUDIO_CALLBACKS */
+
+static DECLCALLBACK(void) drvHostDSoundShutdown(PPDMIHOSTAUDIO pInterface)
+{
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+
+    LogFlowFuncEnter();
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    int rc = dsoundNotifyThread(pThis, true /* fShutdown */);
+    AssertRC(rc);
+
+    int rcThread;
+    rc = RTThreadWait(pThis->Thread,  15 * 1000 /* 15s timeout */, &rcThread);
+    LogFlowFunc(("rc=%Rrc, rcThread=%Rrc\n", rc, rcThread));
+
+    Assert(pThis->fStopped);
+
+    if (pThis->aEvents[DSOUNDEVENT_NOTIFY])
+    {
+        CloseHandle(pThis->aEvents[DSOUNDEVENT_NOTIFY]);
+        pThis->aEvents[DSOUNDEVENT_NOTIFY] = NULL;
+}
+#endif
+
+    LogFlowFuncLeave();
+}
+
+static DECLCALLBACK(int) drvHostDSoundInit(PPDMIHOSTAUDIO pInterface)
+{
+    PDRVHOSTDSOUND pThis = PDMIHOSTAUDIO_2_DRVHOSTDSOUND(pInterface);
+
+    LogFlowFuncEnter();
+
+    int rc;
+
+    /* 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);
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+        /* Create notification event. */
+        pThis->aEvents[DSOUNDEVENT_NOTIFY] = CreateEvent(NULL /* Security attribute */,
+                                                         FALSE /* bManualReset */, FALSE /* bInitialState */,
+                                                         NULL /* lpName */);
+        Assert(pThis->aEvents[DSOUNDEVENT_NOTIFY] != NULL);
+
+        /* Start notification thread. */
+        rc = RTThreadCreate(&pThis->Thread, drvHostDSoundThread,
+                            pThis /*pvUser*/, 0 /*cbStack*/,
+                            RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "dSoundNtfy");
+        if (RT_SUCCESS(rc))
+        {
+            /* Wait for the thread to initialize. */
+            rc = RTThreadUserWait(pThis->Thread, RT_MS_1MIN);
+            if (RT_FAILURE(rc))
+                DSLOGREL(("DSound: Waiting for thread to initialize failed with rc=%Rrc\n", rc));
+        }
+    else
+            DSLOGREL(("DSound: Creating thread failed with rc=%Rrc\n", rc));
+#else
+        rc = VINF_SUCCESS;
+#endif
+
+        PDMAUDIOBACKENDCFG Cfg;
+        dsoundUpdateStatusInternalEx(pThis, &Cfg, DSOUNDENUMCBFLAGS_LOG /* fEnum */);
+
+        DSLOGREL(("DSound: Found %RU32 host playback devices\n",  Cfg.cMaxHstStrmsOut));
+        DSLOGREL(("DSound: Found %RU32 host capturing devices\n", Cfg.cMaxHstStrmsIn));
+    }
+    else
+    {
+        DSLOGREL(("DSound: DirectSound not available: %Rhrc\n", hr));
+        rc = VERR_NOT_SUPPORTED;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+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;
+}
+
+static LPCGUID dsoundConfigQueryGUID(PCFGMNODE pCfg, const char *pszName, RTUUID *pUuid)
+{
+    LPCGUID pGuid = NULL;
+
+    char *pszGuid = NULL;
+    int rc = CFGMR3QueryStringAlloc(pCfg, pszName, &pszGuid);
+    if (RT_SUCCESS(rc))
+    {
+        rc = RTUuidFromStr(pUuid, pszGuid);
+        if (RT_SUCCESS(rc))
+            pGuid = (LPCGUID)&pUuid;
+        else
+            DSLOGREL(("DSound: Error parsing device GUID for device '%s': %Rrc\n", pszName, rc));
+
+        RTStrFree(pszGuid);
+    }
+
+    return pGuid;
+}
+
+static void dSoundConfigInit(PDRVHOSTDSOUND pThis, PCFGMNODE pCfg)
+{
+    unsigned int uBufsizeOut, uBufsizeIn;
+
+    CFGMR3QueryUIntDef(pCfg, "BufsizeOut", &uBufsizeOut, _16K);
+    CFGMR3QueryUIntDef(pCfg, "BufsizeIn",  &uBufsizeIn,  _16K);
+    pThis->cfg.cbBufferOut = uBufsizeOut;
+    pThis->cfg.cbBufferIn  = uBufsizeIn;
+
+    pThis->cfg.pGuidPlay    = dsoundConfigQueryGUID(pCfg, "DeviceGuidOut", &pThis->cfg.uuidPlay);
+    pThis->cfg.pGuidCapture = dsoundConfigQueryGUID(pCfg, "DeviceGuidIn",  &pThis->cfg.uuidCapture);
+
+    DSLOG(("DSound: BufsizeOut %u, BufsizeIn %u, DeviceGuidOut {%RTuuid}, DeviceGuidIn {%RTuuid}\n",
+           pThis->cfg.cbBufferOut,
+           pThis->cfg.cbBufferIn,
+           &pThis->cfg.uuidPlay,
+           &pThis->cfg.uuidCapture));
+}
+
+static DECLCALLBACK(void) drvHostDSoundDestruct(PPDMDRVINS pDrvIns)
+{
+    PDRVHOSTDSOUND pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDSOUND);
+    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+    LogFlowFuncEnter();
+
+    if (pThis->pDrvIns)
+        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);
+
+    LogRel(("Audio: Initializing DirectSound audio driver\n"));
+
+    PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+
+    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+    if (FAILED(hr))
+    {
+        DSLOGREL(("DSound: CoInitializeEx failed with %Rhrc\n", hr));
+        return VERR_NOT_SUPPORTED;
+    }
+
+    /*
+     * Init basic data members and interfaces.
+     */
+    pThis->pDrvIns                   = pDrvIns;
+    /* IBase */
+    pDrvIns->IBase.pfnQueryInterface = drvHostDSoundQueryInterface;
+    /* IHostAudio */
+    PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDSound);
+
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    /*
+     * Get the IAudioConnector interface of the above driver/device.
+     */
+    pThis->pUpIAudioConnector = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
+    if (!pThis->pUpIAudioConnector)
+    {
+        AssertMsgFailed(("Configuration error: No audio connector interface above!\n"));
+        return VERR_PDM_MISSING_INTERFACE_ABOVE;
+    }
+#endif
+
+    /*
+     * Init the static parts.
+     */
+    RTListInit(&pThis->lstDevInput);
+    RTListInit(&pThis->lstDevOutput);
+
+    pThis->fEnabledIn  = false;
+    pThis->fEnabledOut = false;
+#ifdef VBOX_WITH_AUDIO_CALLBACKS
+    pThis->fStopped    = false;
+    pThis->fShutdown   = false;
+
+    RT_ZERO(pThis->aEvents);
+    pThis->cEvents = 0;
+#endif
+
+    /*
+     * 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_old/DrvHostNullAudio.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DrvHostNullAudio.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DrvHostNullAudio.cpp	(revision 61413)
@@ -0,0 +1,336 @@
+/* $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.
+ */
+#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
+#include <VBox/log.h>
+#include "DrvAudio.h"
+#include "AudioMixBuffer.h"
+
+#include "VBoxDD.h"
+
+#include <iprt/alloc.h>
+#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
+#include <VBox/vmm/pdmaudioifs.h>
+
+typedef struct NULLAUDIOSTREAMOUT
+{
+    /** Note: Always must come first! */
+    PDMAUDIOHSTSTRMOUT streamOut;
+    uint64_t           u64TicksLast;
+    uint64_t           csPlayBuffer;
+    uint8_t           *pu8PlayBuffer;
+} NULLAUDIOSTREAMOUT, *PNULLAUDIOSTREAMOUT;
+
+typedef struct NULLAUDIOSTREAMIN
+{
+    /** Note: Always must come first! */
+    PDMAUDIOHSTSTRMIN  streamIn;
+} NULLAUDIOSTREAMIN, *PNULLAUDIOSTREAMIN;
+
+/**
+ * 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 = _1K;
+    }
+
+    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))
+    {
+        PNULLAUDIOSTREAMOUT pNullStrmOut = (PNULLAUDIOSTREAMOUT)pHstStrmOut;
+        pNullStrmOut->u64TicksLast  = 0;
+        pNullStrmOut->csPlayBuffer  = _1K;
+        pNullStrmOut->pu8PlayBuffer = (uint8_t *)RTMemAlloc(_1K << pHstStrmOut->Props.cShift);
+        if (pNullStrmOut->pu8PlayBuffer)
+        {
+            if (pcSamples)
+                *pcSamples = pNullStrmOut->csPlayBuffer;
+        }
+        else
+        {
+            rc = VERR_NO_MEMORY;
+        }
+    }
+
+    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)
+{
+    PDRVHOSTNULLAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTNULLAUDIO, IHostAudio);
+    PNULLAUDIOSTREAMOUT pNullStrmOut = (PNULLAUDIOSTREAMOUT)pHstStrmOut;
+
+    /* Consume as many samples as would be played at the current frequency since last call. */
+    uint32_t csLive          = AudioMixBufAvail(&pHstStrmOut->MixBuf);
+    uint64_t u64TicksNow     = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
+    uint64_t u64TicksElapsed = u64TicksNow  - pNullStrmOut->u64TicksLast;
+    uint64_t u64TicksFreq    = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
+
+    /* Remember when samples were consumed. */
+    pNullStrmOut->u64TicksLast = u64TicksNow;
+
+    /*
+     * Minimize the rounding error by adding 0.5: samples = int((u64TicksElapsed * samplesFreq) / u64TicksFreq + 0.5).
+     * If rounding is not taken into account then the playback rate will be consistently lower that expected.
+     */
+    uint64_t cSamplesPlayed = (2 * u64TicksElapsed * pHstStrmOut->Props.uHz + u64TicksFreq) / u64TicksFreq / 2;
+
+    /* Don't play more than available. */
+    if (cSamplesPlayed > csLive)
+        cSamplesPlayed = csLive;
+
+    cSamplesPlayed = RT_MIN(cSamplesPlayed, pNullStrmOut->csPlayBuffer);
+
+    uint32_t csRead = 0;
+    AudioMixBufReadCirc(&pHstStrmOut->MixBuf, pNullStrmOut->pu8PlayBuffer,
+                        AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cSamplesPlayed), &csRead);
+    AudioMixBufFinish(&pHstStrmOut->MixBuf, csRead);
+
+    if (pcSamplesPlayed)
+        *pcSamplesPlayed = csRead;
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostNullAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                   uint32_t *pcSamplesCaptured)
+{
+    /* Never capture anything. */
+    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)
+{
+    PNULLAUDIOSTREAMOUT pNullStrmOut = (PNULLAUDIOSTREAMOUT)pHstStrmOut;
+    if (   pNullStrmOut
+        && pNullStrmOut->pu8PlayBuffer)
+    {
+        RTMemFree(pNullStrmOut->pu8PlayBuffer);
+    }
+    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) drvHostNullAudioShutdown(PPDMIHOSTAUDIO pInterface)
+{
+    NOREF(pInterface);
+}
+
+/**
+ * Constructs a Null audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvHostNullAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+    AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
+    /* pCfg is optional. */
+
+    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 */
+    NULL,
+    /* 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_old/DrvHostOSSAudio.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DrvHostOSSAudio.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DrvHostOSSAudio.cpp	(revision 61413)
@@ -0,0 +1,1004 @@
+/* $Id$ */
+/** @file
+ * OSS (Open Sound System) host audio backend.
+ */
+
+/*
+ * Copyright (C) 2014-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.
+ * --------------------------------------------------------------------
+ */
+#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
+#include <VBox/log.h>
+#include "DrvAudio.h"
+#include "AudioMixBuffer.h"
+
+#include "VBoxDD.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/soundcard.h>
+#include <unistd.h>
+
+#include <iprt/alloc.h>
+#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
+#include <VBox/vmm/pdmaudioifs.h>
+
+/**
+ * OSS host audio driver instance data.
+ * @implements PDMIAUDIOCONNECTOR
+ */
+typedef struct DRVHOSTOSSAUDIO
+{
+    /** 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;
+} DRVHOSTOSSAUDIO, *PDRVHOSTOSSAUDIO;
+
+typedef struct OSSAUDIOSTREAMCFG
+{
+    PDMAUDIOFMT       enmFormat;
+    PDMAUDIOENDIANNESS enmENDIANNESS;
+    uint16_t          uFreq;
+    uint8_t           cChannels;
+    uint16_t          cFragments;
+    uint32_t          cbFragmentSize;
+} OSSAUDIOSTREAMCFG, *POSSAUDIOSTREAMCFG;
+
+typedef struct OSSAUDIOSTREAMIN
+{
+    /** Note: Always must come first! */
+    PDMAUDIOHSTSTRMIN  pStreamIn;
+    int                hFile;
+    int                cFragments;
+    int                cbFragmentSize;
+    /** Own PCM buffer. */
+    void              *pvPCMBuf;
+    /** Size (in bytes) of own PCM buffer. */
+    size_t             cbPCMBuf;
+    int                old_optr;
+} OSSAUDIOSTREAMIN, *POSSAUDIOSTREAMIN;
+
+typedef struct OSSAUDIOSTREAMOUT
+{
+    /** Note: Always must come first! */
+    PDMAUDIOHSTSTRMOUT  pStreamOut;
+    int                 hFile;
+    int                 cFragments;
+    int                 cbFragmentSize;
+#ifndef RT_OS_L4
+    /** Whether we use a memory mapped file instead of our
+     *  own allocated PCM buffer below. */
+    bool                fMemMapped;
+#endif
+    /** Own PCM buffer in case memory mapping is unavailable. */
+    void               *pvPCMBuf;
+    /** Size (in bytes) of own PCM buffer. */
+    size_t              cbPCMBuf;
+    int                 old_optr;
+} OSSAUDIOSTREAMOUT, *POSSAUDIOSTREAMOUT;
+
+typedef struct OSSAUDIOCFG
+{
+#ifndef RT_OS_L4
+    bool try_mmap;
+#endif
+    int nfrags;
+    int fragsize;
+    const char *devpath_out;
+    const char *devpath_in;
+    int debug;
+} OSSAUDIOCFG, *POSSAUDIOCFG;
+
+static OSSAUDIOCFG s_OSSConf =
+{
+#ifndef RT_OS_L4
+    false,
+#endif
+    4,
+    4096,
+    "/dev/dsp",
+    "/dev/dsp",
+    0
+};
+
+
+/* http://www.df.lth.se/~john_e/gems/gem002d.html */
+static uint32_t popcount(uint32_t u)
+{
+    u = ((u&0x55555555) + ((u>>1)&0x55555555));
+    u = ((u&0x33333333) + ((u>>2)&0x33333333));
+    u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
+    u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
+    u = ( u&0x0000ffff) + (u>>16);
+    return u;
+}
+
+static uint32_t lsbindex(uint32_t u)
+{
+    return popcount ((u&-u)-1);
+}
+
+static int drvHostOSSAudioFmtToOSS(PDMAUDIOFMT fmt)
+{
+    switch (fmt)
+    {
+        case AUD_FMT_S8:
+            return AFMT_S8;
+
+        case AUD_FMT_U8:
+            return AFMT_U8;
+
+        case AUD_FMT_S16:
+            return AFMT_S16_LE;
+
+        case AUD_FMT_U16:
+            return AFMT_U16_LE;
+
+        default:
+            break;
+    }
+
+    AssertMsgFailed(("Format %ld not supported\n", fmt));
+    return AFMT_U8;
+}
+
+static int drvHostOSSAudioOSSToFmt(int fmt,
+                                   PDMAUDIOFMT *pFmt, PDMAUDIOENDIANNESS *pENDIANNESS)
+{
+    switch (fmt)
+    {
+        case AFMT_S8:
+            *pFmt = AUD_FMT_S8;
+            if (pENDIANNESS)
+                *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+
+        case AFMT_U8:
+            *pFmt = AUD_FMT_U8;
+            if (pENDIANNESS)
+                *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+
+        case AFMT_S16_LE:
+            *pFmt = AUD_FMT_S16;
+            if (pENDIANNESS)
+                *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+
+        case AFMT_U16_LE:
+            *pFmt = AUD_FMT_U16;
+            if (pENDIANNESS)
+                *pENDIANNESS = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+
+        case AFMT_S16_BE:
+            *pFmt = AUD_FMT_S16;
+            if (pENDIANNESS)
+                *pENDIANNESS = PDMAUDIOENDIANNESS_BIG;
+            break;
+
+        case AFMT_U16_BE:
+            *pFmt = AUD_FMT_U16;
+            if (pENDIANNESS)
+                *pENDIANNESS = PDMAUDIOENDIANNESS_BIG;
+            break;
+
+        default:
+            AssertMsgFailed(("Format %ld not supported\n", fmt));
+            return VERR_NOT_SUPPORTED;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static int drvHostOSSAudioClose(int *phFile)
+{
+    if (!phFile || !*phFile)
+        return VINF_SUCCESS;
+
+    int rc;
+    if (close(*phFile))
+    {
+        LogRel(("OSS: Closing descriptor failed: %s\n",
+                strerror(errno)));
+        rc = VERR_GENERAL_FAILURE; /** @todo */
+    }
+    else
+    {
+        *phFile = -1;
+        rc = VINF_SUCCESS;
+    }
+
+    return rc;
+}
+
+static int drvHostOSSAudioOpen(bool fIn,
+                               POSSAUDIOSTREAMCFG pReq, POSSAUDIOSTREAMCFG pObt,
+                               int *phFile)
+{
+    AssertPtrReturn(pReq, VERR_INVALID_POINTER);
+    AssertPtrReturn(pObt, VERR_INVALID_POINTER);
+    AssertPtrReturn(phFile, VERR_INVALID_POINTER);
+
+    int rc;
+    int hFile;
+
+    do
+    {
+        const char *pszDev = fIn ? s_OSSConf.devpath_in : s_OSSConf.devpath_out;
+        if (!pszDev)
+        {
+            LogRel(("OSS: Invalid or no %s device name set\n",
+                    fIn ? "input" : "output"));
+            rc = VERR_INVALID_PARAMETER;
+            break;
+        }
+
+        hFile = open(pszDev, (fIn ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
+        if (hFile == -1)
+        {
+            LogRel(("OSS: Failed to open %s: %s(%d)\n", pszDev, strerror(errno), errno));
+            rc = RTErrConvertFromErrno(errno);
+            break;
+        }
+
+        int iFormat = drvHostOSSAudioFmtToOSS(pReq->enmFormat);
+        if (ioctl(hFile, SNDCTL_DSP_SAMPLESIZE, &iFormat))
+        {
+            LogRel(("OSS: Failed to set audio format to %ld errno=%s(%d)\n", iFormat, strerror(errno), errno));
+            rc = RTErrConvertFromErrno(errno);
+            break;
+        }
+
+        int cChannels = pReq->cChannels;
+        if (ioctl(hFile, SNDCTL_DSP_CHANNELS, &cChannels))
+        {
+            LogRel(("OSS: Failed to set number of audio channels (%d): %s(%d)\n", pReq->cChannels, strerror(errno), errno));
+            rc = RTErrConvertFromErrno(errno);
+            break;
+        }
+
+        int freq = pReq->uFreq;
+        if (ioctl(hFile, SNDCTL_DSP_SPEED, &freq))
+        {
+            LogRel(("OSS: Failed to set audio frequency (%dHZ): %s(%d)\n", pReq->uFreq, strerror(errno), errno));
+            rc = RTErrConvertFromErrno(errno);
+            break;
+        }
+
+        /* Obsolete on Solaris (using O_NONBLOCK is sufficient). */
+#if !(defined(VBOX) && defined(RT_OS_SOLARIS))
+        if (ioctl(hFile, SNDCTL_DSP_NONBLOCK))
+        {
+            LogRel(("OSS: Failed to set non-blocking mode: %s(%d)\n", strerror(errno), errno));
+            rc = RTErrConvertFromErrno(errno);
+            break;
+        }
+#endif
+        int mmmmssss = (pReq->cFragments << 16) | lsbindex(pReq->cbFragmentSize);
+        if (ioctl(hFile, SNDCTL_DSP_SETFRAGMENT, &mmmmssss))
+        {
+            LogRel(("OSS: Failed to set %RU16 fragments to %RU32 bytes each: %s(%d)\n",
+                    pReq->cFragments, pReq->cbFragmentSize, strerror(errno), errno));
+            rc = RTErrConvertFromErrno(errno);
+            break;
+        }
+
+        audio_buf_info abinfo;
+        if (ioctl(hFile, fIn ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo))
+        {
+            LogRel(("OSS: Failed to retrieve buffer length: %s(%d)\n", strerror(errno), errno));
+            rc = RTErrConvertFromErrno(errno);
+            break;
+        }
+
+        rc = drvHostOSSAudioOSSToFmt(iFormat, &pObt->enmFormat, &pObt->enmENDIANNESS);
+        if (RT_SUCCESS(rc))
+        {
+            pObt->cChannels      = cChannels;
+            pObt->uFreq          = freq;
+            pObt->cFragments     = abinfo.fragstotal;
+            pObt->cbFragmentSize = abinfo.fragsize;
+
+            *phFile = hFile;
+        }
+    }
+    while (0);
+
+    if (RT_FAILURE(rc))
+        drvHostOSSAudioClose(&hFile);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostOSSAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                   PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    NOREF(pInterface);
+    NOREF(pHstStrmIn);
+    NOREF(enmStreamCmd);
+
+    /** @todo Nothing to do here right now!? */
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostOSSAudioControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                                   PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+
+    POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
+
+#ifdef RT_OS_L4
+    return VINF_SUCCESS;
+#else
+    if (!pThisStrmOut->fMemMapped)
+        return VINF_SUCCESS;
+#endif
+
+    int rc = VINF_SUCCESS;
+    int mask;
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        case PDMAUDIOSTREAMCMD_RESUME:
+        {
+            DrvAudioClearBuf(&pHstStrmOut->Props,
+                             pThisStrmOut->pvPCMBuf, pThisStrmOut->cbPCMBuf, AudioMixBufSize(&pHstStrmOut->MixBuf));
+
+            mask = PCM_ENABLE_OUTPUT;
+            if (ioctl(pThisStrmOut->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
+            {
+                LogRel(("OSS: Failed to enable output stream: %s\n", strerror(errno)));
+                rc = RTErrConvertFromErrno(errno);
+            }
+
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        case PDMAUDIOSTREAMCMD_PAUSE:
+        {
+            mask = 0;
+            if (ioctl(pThisStrmOut->hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
+            {
+                LogRel(("OSS: Failed to disable output stream: %s\n", strerror(errno)));
+                rc = RTErrConvertFromErrno(errno);
+            }
+
+            break;
+        }
+
+        default:
+            AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
+            rc = VERR_INVALID_PARAMETER;
+            break;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostOSSAudioInit(PPDMIHOSTAUDIO pInterface)
+{
+    NOREF(pInterface);
+
+    LogFlowFuncEnter();
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostOSSAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                                  uint32_t *pcSamplesCaptured)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+
+    POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
+
+    int rc = VINF_SUCCESS;
+    size_t cbToRead = RT_MIN(pThisStrmIn->cbPCMBuf,
+                             AudioMixBufFreeBytes(&pHstStrmIn->MixBuf));
+
+    LogFlowFunc(("cbToRead=%zu\n", cbToRead));
+
+    uint32_t cWrittenTotal = 0;
+    uint32_t cbTemp;
+    ssize_t  cbRead;
+    size_t   offWrite = 0;
+
+    while (cbToRead)
+    {
+        cbTemp = RT_MIN(cbToRead, pThisStrmIn->cbPCMBuf);
+        AssertBreakStmt(cbTemp, rc = VERR_NO_DATA);
+        cbRead = read(pThisStrmIn->hFile, (uint8_t *)pThisStrmIn->pvPCMBuf + offWrite, cbTemp);
+
+        LogFlowFunc(("cbRead=%zi, cbTemp=%RU32, cbToRead=%zu\n",
+                     cbRead, cbTemp, cbToRead));
+
+        if (cbRead < 0)
+        {
+            switch (errno)
+            {
+                case 0:
+                {
+                    LogFunc(("Failed to read %z frames\n", cbRead));
+                    rc = VERR_ACCESS_DENIED;
+                    break;
+                }
+
+                case EINTR:
+                case EAGAIN:
+                    rc = VERR_NO_DATA;
+                    break;
+
+                default:
+                    LogFlowFunc(("Failed to read %zu input frames, rc=%Rrc\n",
+                                 cbTemp, rc));
+                    rc = VERR_GENERAL_FAILURE; /** @todo */
+                    break;
+            }
+
+            if (RT_FAILURE(rc))
+                break;
+        }
+        else if (cbRead)
+        {
+            uint32_t cWritten;
+            rc = AudioMixBufWriteCirc(&pHstStrmIn->MixBuf,
+                                      pThisStrmIn->pvPCMBuf, cbRead,
+                                      &cWritten);
+            if (RT_FAILURE(rc))
+                break;
+
+            uint32_t cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
+
+            Assert(cbToRead >= cbWritten);
+            cbToRead      -= cbWritten;
+            offWrite      += cbWritten;
+            cWrittenTotal += cWritten;
+        }
+        else /* No more data, try next round. */
+            break;
+    }
+
+    if (rc == VERR_NO_DATA)
+        rc = VINF_SUCCESS;
+
+    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) drvHostOSSAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+
+    POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
+
+    LogFlowFuncEnter();
+
+    if (pThisStrmIn->pvPCMBuf)
+    {
+        Assert(pThisStrmIn->cbPCMBuf);
+
+        RTMemFree(pThisStrmIn->pvPCMBuf);
+        pThisStrmIn->pvPCMBuf = NULL;
+    }
+
+    pThisStrmIn->cbPCMBuf = 0;
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostOSSAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+
+    POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
+
+    LogFlowFuncEnter();
+
+#ifndef RT_OS_L4
+    if (!pThisStrmOut->fMemMapped)
+    {
+        if (pThisStrmOut->pvPCMBuf)
+        {
+            Assert(pThisStrmOut->cbPCMBuf);
+
+            RTMemFree(pThisStrmOut->pvPCMBuf);
+            pThisStrmOut->pvPCMBuf = NULL;
+        }
+
+        pThisStrmOut->cbPCMBuf = 0;
+    }
+#endif
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostOSSAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
+{
+    NOREF(pInterface);
+
+    pCfg->cbStreamOut = sizeof(OSSAUDIOSTREAMOUT);
+    pCfg->cbStreamIn = sizeof(OSSAUDIOSTREAMIN);
+    pCfg->cMaxHstStrmsOut = INT_MAX;
+    pCfg->cMaxHstStrmsIn = INT_MAX;
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvHostOSSAudioInitIn(PPDMIHOSTAUDIO pInterface,
+                                               PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
+                                               PDMAUDIORECSOURCE enmRecSource,
+                                               uint32_t *pcSamples)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    POSSAUDIOSTREAMIN pThisStrmIn = (POSSAUDIOSTREAMIN)pHstStrmIn;
+
+    int rc;
+    int hFile = -1;
+
+    do
+    {
+        uint32_t cSamples;
+
+        OSSAUDIOSTREAMCFG reqStream, obtStream;
+        reqStream.enmFormat      = pCfg->enmFormat;
+        reqStream.uFreq          = pCfg->uHz;
+        reqStream.cChannels      = pCfg->cChannels;
+        reqStream.cFragments     = s_OSSConf.nfrags;
+        reqStream.cbFragmentSize = s_OSSConf.fragsize;
+
+        rc = drvHostOSSAudioOpen(true /* fIn */,
+                                 &reqStream, &obtStream, &hFile);
+        if (RT_SUCCESS(rc))
+        {
+            if (obtStream.cFragments * obtStream.cbFragmentSize & pHstStrmIn->Props.uAlign)
+                LogRel(("OSS: Warning: Misaligned DAC output buffer: Size = %zu, Alignment = %u\n",
+                        obtStream.cFragments * obtStream.cbFragmentSize,
+                        pHstStrmIn->Props.uAlign + 1));
+
+            pThisStrmIn->hFile = hFile;
+
+            PDMAUDIOSTREAMCFG streamCfg;
+            streamCfg.enmFormat     = obtStream.enmFormat;
+            streamCfg.uHz           = obtStream.uFreq;
+            streamCfg.cChannels     = pCfg->cChannels;
+            streamCfg.enmEndianness = obtStream.enmENDIANNESS;
+
+            rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmIn->Props);
+            if (RT_SUCCESS(rc))
+            {
+                cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
+                           >> pHstStrmIn->Props.cShift;
+                if (!cSamples)
+                    rc = VERR_INVALID_PARAMETER;
+            }
+        }
+
+        if (RT_SUCCESS(rc))
+        {
+            size_t cbSample = (1 << pHstStrmIn->Props.cShift);
+            size_t cbBuf    = cSamples * cbSample;
+            pThisStrmIn->pvPCMBuf = RTMemAlloc(cbBuf);
+            if (!pThisStrmIn->pvPCMBuf)
+            {
+                LogRel(("OSS: Failed allocating ADC buffer with %RU32 samples (%zu bytes per sample)\n", cSamples, cbSample));
+                rc = VERR_NO_MEMORY;
+            }
+
+            pThisStrmIn->cbPCMBuf = cbBuf;
+
+            if (pcSamples)
+                *pcSamples = cSamples;
+        }
+
+    } while (0);
+
+    if (RT_FAILURE(rc))
+        drvHostOSSAudioClose(&hFile);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(int) drvHostOSSAudioInitOut(PPDMIHOSTAUDIO pInterface,
+                                                PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
+                                                uint32_t *pcSamples)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
+
+    int rc;
+    int hFile = -1;
+
+    do
+    {
+        uint32_t cSamples;
+
+        OSSAUDIOSTREAMCFG reqStream, obtStream;
+        reqStream.enmFormat      = pCfg->enmFormat;
+        reqStream.uFreq          = pCfg->uHz;
+        reqStream.cChannels      = pCfg->cChannels;
+        reqStream.cFragments     = s_OSSConf.nfrags;
+        reqStream.cbFragmentSize = s_OSSConf.fragsize;
+
+        rc = drvHostOSSAudioOpen(false /* fIn */,
+                                 &reqStream, &obtStream, &hFile);
+        if (RT_SUCCESS(rc))
+        {
+            if (obtStream.cFragments * obtStream.cbFragmentSize & pHstStrmOut->Props.uAlign)
+                LogRel(("OSS: Warning: Misaligned DAC output buffer: Size = %zu, Alignment = %u\n",
+                        obtStream.cFragments * obtStream.cbFragmentSize,
+                        pHstStrmOut->Props.uAlign + 1));
+
+            pThisStrmOut->hFile = hFile;
+
+            PDMAUDIOSTREAMCFG streamCfg;
+            streamCfg.enmFormat     = obtStream.enmFormat;
+            streamCfg.uHz           = obtStream.uFreq;
+            streamCfg.cChannels     = pCfg->cChannels;
+            streamCfg.enmEndianness = obtStream.enmENDIANNESS;
+
+            rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props);
+            if (RT_SUCCESS(rc))
+                cSamples = (obtStream.cFragments * obtStream.cbFragmentSize)
+                           >> pHstStrmOut->Props.cShift;
+        }
+
+        if (RT_SUCCESS(rc))
+        {
+#ifndef RT_OS_L4
+            pThisStrmOut->fMemMapped = false;
+            if (s_OSSConf.try_mmap)
+            {
+                pThisStrmOut->pvPCMBuf = mmap(0, cSamples << pHstStrmOut->Props.cShift,
+                                              PROT_READ | PROT_WRITE, MAP_SHARED, hFile, 0);
+                if (pThisStrmOut->pvPCMBuf == MAP_FAILED)
+                {
+                    LogRel(("OSS: Failed to memory map %zu bytes of DAC output file: %s\n",
+                            cSamples << pHstStrmOut->Props.cShift, strerror(errno)));
+                    rc = RTErrConvertFromErrno(errno);
+                    break;
+                }
+                else
+                {
+                    int mask = 0;
+                    if (ioctl(hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
+                    {
+                        LogRel(("OSS: Failed to retrieve initial trigger mask: %s\n",
+                                strerror(errno)));
+                        rc = RTErrConvertFromErrno(errno);
+                        /* Note: No break here, need to unmap file first! */
+                    }
+                    else
+                    {
+                        mask = PCM_ENABLE_OUTPUT;
+                        if (ioctl (hFile, SNDCTL_DSP_SETTRIGGER, &mask) < 0)
+                        {
+                            LogRel(("OSS: Failed to retrieve PCM_ENABLE_OUTPUT mask: %s\n",
+                                    strerror(errno)));
+                            rc = RTErrConvertFromErrno(errno);
+                            /* Note: No break here, need to unmap file first! */
+                        }
+                        else
+                            pThisStrmOut->fMemMapped = true;
+                    }
+
+                    if (!pThisStrmOut->fMemMapped)
+                    {
+                        int rc2 = munmap(pThisStrmOut->pvPCMBuf,
+                                         cSamples << pHstStrmOut->Props.cShift);
+                        if (rc2)
+                            LogRel(("OSS: Failed to unmap DAC output file: %s\n", strerror(errno)));
+                        break;
+                    }
+                }
+            }
+#endif /* !RT_OS_L4 */
+
+            /* Memory mapping failed above? Try allocating an own buffer. */
+#ifndef RT_OS_L4
+            if (!pThisStrmOut->fMemMapped)
+            {
+#endif
+                size_t cbSample = (1 << pHstStrmOut->Props.cShift);
+                size_t cbPCMBuf = cSamples * cbSample;
+
+                LogFlowFunc(("cSamples=%RU32\n", cSamples));
+
+                pThisStrmOut->pvPCMBuf = RTMemAlloc(cbPCMBuf);
+                if (!pThisStrmOut->pvPCMBuf)
+                {
+                    LogRel(("OSS: Failed allocating DAC buffer with %RU32 samples (%zu bytes per sample)\n", cSamples, cbSample));
+                    rc = VERR_NO_MEMORY;
+                    break;
+                }
+
+                pThisStrmOut->cbPCMBuf = cbPCMBuf;
+#ifndef RT_OS_L4
+            }
+#endif
+            if (pcSamples)
+                *pcSamples = cSamples;
+        }
+
+    } while (0);
+
+    if (RT_FAILURE(rc))
+        drvHostOSSAudioClose(&hFile);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static DECLCALLBACK(bool) drvHostOSSAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
+{
+    NOREF(pInterface);
+    NOREF(enmDir);
+    return true; /* Always all enabled. */
+}
+
+static DECLCALLBACK(int) drvHostOSSAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                                uint32_t *pcSamplesPlayed)
+{
+    NOREF(pInterface);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+
+    POSSAUDIOSTREAMOUT pThisStrmOut = (POSSAUDIOSTREAMOUT)pHstStrmOut;
+
+    int rc = VINF_SUCCESS;
+    uint32_t cbReadTotal = 0;
+    count_info cntinfo;
+
+    do
+    {
+        size_t cbBuf = AudioMixBufSizeBytes(&pHstStrmOut->MixBuf);
+
+        uint32_t cLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
+        uint32_t cToRead;
+
+#ifndef RT_OS_L4
+        if (pThisStrmOut->fMemMapped)
+        {
+            /* Get current playback pointer. */
+            int rc2 = ioctl(pThisStrmOut->hFile, SNDCTL_DSP_GETOPTR, &cntinfo);
+            if (!rc2)
+            {
+                LogRel(("OSS: Failed to retrieve current playback pointer: %s\n",
+                        strerror(errno)));
+                rc = RTErrConvertFromErrno(errno);
+                break;
+            }
+
+            /* Nothing to play? */
+            if (cntinfo.ptr == pThisStrmOut->old_optr)
+                break;
+
+            int cbData;
+            if (cntinfo.ptr > pThisStrmOut->old_optr)
+                cbData = cntinfo.ptr - pThisStrmOut->old_optr;
+            else
+                cbData = cbBuf + cntinfo.ptr - pThisStrmOut->old_optr;
+            Assert(cbData);
+
+            cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbData),
+                             cLive);
+        }
+        else
+        {
+#endif
+            audio_buf_info abinfo;
+            int rc2 = ioctl(pThisStrmOut->hFile, SNDCTL_DSP_GETOSPACE, &abinfo);
+            if (rc2 < 0)
+            {
+                LogRel(("OSS: Failed to retrieve current playback buffer: %s\n",
+                        strerror(errno)));
+                rc = RTErrConvertFromErrno(errno);
+                break;
+            }
+
+            if ((size_t)abinfo.bytes > cbBuf)
+            {
+                LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%d\n",
+                             abinfo.bytes, cbBuf));
+                abinfo.bytes = cbBuf;
+                /* Keep going. */
+            }
+
+            if (abinfo.bytes < 0)
+            {
+                LogFlowFunc(("Warning: Invalid available size, size=%d, bufsize=%d\n",
+                             abinfo.bytes, cbBuf));
+                rc = VERR_INVALID_PARAMETER;
+                break;
+            }
+
+            cToRead = RT_MIN((uint32_t)AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, abinfo.bytes),
+                             cLive);
+            if (!cToRead)
+                break;
+#ifndef RT_OS_L4
+        }
+#endif
+        size_t cbToRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cToRead);
+        LogFlowFunc(("cbToRead=%zu\n", cbToRead));
+
+        uint32_t cRead, cbRead;
+        while (cbToRead)
+        {
+            rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf,
+                                     pThisStrmOut->pvPCMBuf, cbToRead, &cRead);
+            if (RT_FAILURE(rc))
+                break;
+
+            cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
+            ssize_t cbWritten = write(pThisStrmOut->hFile, pThisStrmOut->pvPCMBuf,
+                                      cbRead);
+            if (cbWritten == -1)
+            {
+                LogRel(("OSS: Failed writing output data: %s\n", strerror(errno)));
+                rc = RTErrConvertFromErrno(errno);
+                break;
+            }
+
+            Assert(cbToRead >= cbRead);
+            cbToRead -= cbRead;
+            cbReadTotal += cbRead;
+        }
+
+#ifndef RT_OS_L4
+        /* Update read pointer. */
+        if (pThisStrmOut->fMemMapped)
+            pThisStrmOut->old_optr = cntinfo.ptr;
+#endif
+
+    } 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(void) drvHostOSSAudioShutdown(PPDMIHOSTAUDIO pInterface)
+{
+    NOREF(pInterface);
+}
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvHostOSSAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVHOSTOSSAUDIO  pThis   = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
+
+    return NULL;
+}
+
+/**
+ * Constructs an OSS audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvHostOSSAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+    PDRVHOSTOSSAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTOSSAUDIO);
+    LogRel(("Audio: Initializing OSS driver\n"));
+
+    /*
+     * Init the static parts.
+     */
+    pThis->pDrvIns                   = pDrvIns;
+    /* IBase */
+    pDrvIns->IBase.pfnQueryInterface = drvHostOSSAudioQueryInterface;
+    /* IHostAudio */
+    PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostOSSAudio);
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Char driver registration record.
+ */
+const PDMDRVREG g_DrvHostOSSAudio =
+{
+    /* u32Version */
+    PDM_DRVREG_VERSION,
+    /* szName */
+    "OSSAudio",
+    /* szRCMod */
+    "",
+    /* szR0Mod */
+    "",
+    /* pszDescription */
+    "OSS audio host driver",
+    /* fFlags */
+    PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+    /* fClass. */
+    PDM_DRVREG_CLASS_AUDIO,
+    /* cMaxInstances */
+    ~0U,
+    /* cbInstance */
+    sizeof(DRVHOSTOSSAUDIO),
+    /* pfnConstruct */
+    drvHostOSSAudioConstruct,
+    /* pfnDestruct */
+    NULL,
+    /* 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_old/DrvHostPulseAudio.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/DrvHostPulseAudio.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/DrvHostPulseAudio.cpp	(revision 61413)
@@ -0,0 +1,1252 @@
+/* $Id$ */
+/** @file
+ * VBox audio devices: Pulse Audio audio driver.
+ */
+
+/*
+ * Copyright (C) 2006-2016 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                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
+#include <VBox/log.h>
+
+#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 "DrvAudio.h"
+#include "AudioMixBuffer.h"
+
+#include "VBoxDD.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;
+static volatile bool                g_fAbortMainLoop;
+
+/**
+ * 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;
+    /** Configuration option: stream name. */
+    char               *pszStreamName;
+} DRVHOSTPULSEAUDIO, *PDRVHOSTPULSEAUDIO;
+
+typedef struct PULSEAUDIOSTREAM
+{
+    /** Must come first, as this struct might be
+     *  casted to one of these structs. */
+    union
+    {
+        PDMAUDIOHSTSTRMIN  In;
+        PDMAUDIOHSTSTRMOUT Out;
+    };
+    /** Pointer to driver instance. */
+    PDRVHOSTPULSEAUDIO     pDrv;
+    /** DAC/ADC buffer. */
+    void                  *pvPCMBuf;
+    /** Size (in bytes) of DAC/ADC buffer. */
+    uint32_t               cbPCMBuf;
+    /** 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);
+
+/**
+ * Signal the main loop to abort. Just signalling isn't sufficient as the
+ * mainloop might not have been entered yet.
+ */
+static void drvHostPulseAudioAbortMainLoop(void)
+{
+    g_fAbortMainLoop = true;
+    pa_threaded_mainloop_signal(g_pMainLoop, 0);
+}
+
+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, PDMAUDIOENDIANNESS *pEndianness)
+{
+    switch (pulsefmt)
+    {
+        case PA_SAMPLE_U8:
+            *pFmt = AUD_FMT_U8;
+            *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+
+        case PA_SAMPLE_S16LE:
+            *pFmt = AUD_FMT_S16;
+            *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+
+        case PA_SAMPLE_S16BE:
+            *pFmt = AUD_FMT_S16;
+            *pEndianness = PDMAUDIOENDIANNESS_BIG;
+            break;
+
+#ifdef PA_SAMPLE_S32LE
+        case PA_SAMPLE_S32LE:
+            *pFmt = AUD_FMT_S32;
+            *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
+            break;
+#endif
+
+#ifdef PA_SAMPLE_S32BE
+        case PA_SAMPLE_S32BE:
+            *pFmt = AUD_FMT_S32;
+            *pEndianness = PDMAUDIOENDIANNESS_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();
+        while (pa_operation_get_state(pOP) == PA_OPERATION_RUNNING)
+        {
+            if (!g_fAbortMainLoop)
+                pa_threaded_mainloop_wait(g_pMainLoop);
+            g_fAbortMainLoop = false;
+
+            uint64_t 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 *pvUser)
+{
+    AssertPtrReturnVoid(pContext);
+    NOREF(pvUser);
+
+    switch (pa_context_get_state(pContext))
+    {
+        case PA_CONTEXT_READY:
+        case PA_CONTEXT_TERMINATED:
+            drvHostPulseAudioAbortMainLoop();
+            break;
+
+        case PA_CONTEXT_FAILED:
+            LogRel(("PulseAudio: Audio input/output stopped!\n"));
+            drvHostPulseAudioAbortMainLoop();
+            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:
+            drvHostPulseAudioAbortMainLoop();
+            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)
+        drvHostPulseAudioAbortMainLoop();
+    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_AUDIO_BACKEND_INIT_FAILED;
+                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_AUDIO_BACKEND_INIT_FAILED;
+                break;
+            }
+        }
+
+        /* Wait until the stream is ready. */
+        for (;;)
+        {
+            if (!g_fAbortMainLoop)
+                pa_threaded_mainloop_wait(g_pMainLoop);
+            g_fAbortMainLoop = false;
+
+            pa_stream_state_t 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_AUDIO_BACKEND_INIT_FAILED;
+                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));
+
+    }
+    while (0);
+
+    if (   RT_FAILURE(rc)
+        && pStream)
+        pa_stream_disconnect(pStream);
+
+    pa_threaded_mainloop_unlock(g_pMainLoop);
+
+    if (RT_FAILURE(rc))
+    {
+        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_AUDIO_BACKEND_INIT_FAILED;
+            break;
+        }
+
+        g_fAbortMainLoop = false;
+        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_AUDIO_BACKEND_INIT_FAILED;
+            break;
+        }
+
+        /* Wait until the g_pContext is ready */
+        for (;;)
+        {
+            if (!g_fAbortMainLoop)
+                pa_threaded_mainloop_wait(g_pMainLoop);
+            g_fAbortMainLoop = false;
+
+            pa_context_state_t 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_AUDIO_BACKEND_INIT_FAILED;
+                break;
+            }
+        }
+    }
+    while (0);
+
+    if (fLocked)
+        pa_threaded_mainloop_unlock(g_pMainLoop);
+
+    if (RT_FAILURE(rc))
+    {
+        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)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+    /* pcSamples is optional. */
+
+    PDRVHOSTPULSEAUDIO pDrv = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
+    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! */
+    char achName[64];
+    RTStrPrintf(achName, sizeof(achName), "%.32s (out)", pDrv->pszStreamName);
+    int rc = drvHostPulseAudioOpen(false /* fIn */, achName, &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))
+    {
+        uint32_t cbBuf  = RT_MIN(pThisStrmOut->BufAttr.tlength * 2,
+                                 pThisStrmOut->BufAttr.maxlength); /** @todo Make this configurable! */
+        if (cbBuf)
+        {
+            pThisStrmOut->pvPCMBuf = RTMemAllocZ(cbBuf);
+            if (pThisStrmOut->pvPCMBuf)
+            {
+                pThisStrmOut->cbPCMBuf = cbBuf;
+
+                uint32_t cSamples = cbBuf >> pHstStrmOut->Props.cShift;
+                if (pcSamples)
+                    *pcSamples = cSamples;
+
+                /* Save pointer to driver instance. */
+                pThisStrmOut->pDrv = pDrv;
+
+                LogFunc(("cbBuf=%RU32, cSamples=%RU32\n", cbBuf, cSamples));
+            }
+            else
+                rc = VERR_NO_MEMORY;
+        }
+        else
+            rc = VERR_INVALID_PARAMETER;
+    }
+
+    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)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+    /* pcSamples is optional. */
+
+    PDRVHOSTPULSEAUDIO pDrv = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
+    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. */
+
+    char achName[64];
+    RTStrPrintf(achName, sizeof(achName), "%.32s (in)", pDrv->pszStreamName);
+    int rc = drvHostPulseAudioOpen(true /* fIn */, achName, &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;
+
+        /* Save pointer to driver instance. */
+        pThisStrmIn->pDrv = pDrv;
+
+        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 = RT_MIN(pThisStrmIn->cbPeekBuf - pThisStrmIn->offPeekBuf, cbToRead);
+
+        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 = AudioMixBufAvail(&pHstStrmOut->MixBuf);
+    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->pvPCMBuf,
+                                     RT_MIN(cbToRead, pThisStrmOut->cbPCMBuf), &cRead);
+            if (   !cRead
+                || RT_FAILURE(rc))
+            {
+                break;
+            }
+
+            cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
+            if (pa_stream_write(pThisStrmOut->pStream, pThisStrmOut->pvPCMBuf, cbRead, NULL /* Cleanup callback */,
+                                0, PA_SEEK_RELATIVE) < 0)
+            {
+                rc = drvHostPulseAudioError(pThisStrmOut->pDrv, "Failed to write to output stream");
+                break;
+            }
+
+            Assert(cbToRead >= cbRead);
+            cbToRead    -= cbRead;
+            cbReadTotal += cbRead;
+
+            LogFlowFunc(("\tcRead=%RU32 (%zu bytes) cbReadTotal=%RU32, cbToRead=%RU32\n",
+                         cRead, AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead), cbReadTotal, cbToRead));
+        }
+
+    } 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->pvPCMBuf)
+    {
+        RTMemFree(pThisStrmOut->pvPCMBuf);
+        pThisStrmOut->pvPCMBuf = NULL;
+
+        pThisStrmOut->cbPCMBuf = 0;
+    }
+
+    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:
+        case PDMAUDIOSTREAMCMD_RESUME:
+        {
+            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:
+        case PDMAUDIOSTREAMCMD_PAUSE:
+        {
+            /* 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:
+        case PDMAUDIOSTREAMCMD_RESUME:
+        {
+            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:
+        case PDMAUDIOSTREAMCMD_PAUSE:
+        {
+            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 = UINT32_MAX;
+    pCfg->cMaxHstStrmsIn  = UINT32_MAX;
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(void) drvHostPulseAudioShutdown(PPDMIHOSTAUDIO pInterface)
+{
+    NOREF(pInterface);
+
+    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();
+}
+
+/**
+ * @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;
+}
+
+/**
+ * Constructs a PulseAudio Audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvHostPulseAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+    AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
+
+    PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
+    LogRel(("Audio: Initializing PulseAudio driver\n"));
+
+    CFGMR3QueryStringAlloc(pCfg, "StreamName", &pThis->pszStreamName);
+
+    pThis->pDrvIns                   = pDrvIns;
+    /* IBase */
+    pDrvIns->IBase.pfnQueryInterface = drvHostPulseAudioQueryInterface;
+    /* IHostAudio */
+    PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostPulseAudio);
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Destructs a PulseAudio Audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(void) drvHostPulseAudioDestruct(PPDMDRVINS pDrvIns)
+{
+    PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
+    LogFlowFuncEnter();
+    if (pThis->pszStreamName)
+    {
+        MMR3HeapFree(pThis->pszStreamName);
+        pThis->pszStreamName = NULL;
+    }
+}
+
+/**
+ * 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
+};
+
Index: /trunk/src/VBox/Devices/Audio_old/alsa_mangling.h
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/alsa_mangling.h	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/alsa_mangling.h	(revision 61413)
@@ -0,0 +1,55 @@
+/** @file
+ *
+ * Mangle libasound symbols. This is necessary on hosts which don't
+ * support the -fvisibility gcc switch.
+ */
+
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#ifndef AUDIO_ALSA_MANGLING_H
+#define AUDIO_ALSA_MANGLING_H
+
+#define ALSA_MANGLER(symbol) VBox_##symbol
+
+#define snd_pcm_hw_params_any                   ALSA_MANGLER(snd_pcm_hw_params_any)
+#define snd_pcm_close                           ALSA_MANGLER(snd_pcm_close)
+#define snd_pcm_avail_update                    ALSA_MANGLER(snd_pcm_avail_update)
+#define snd_pcm_hw_params_set_channels_near     ALSA_MANGLER(snd_pcm_hw_params_set_channels_near)
+#define snd_pcm_hw_params_set_period_time_near  ALSA_MANGLER(snd_pcm_hw_params_set_period_time_near)
+#define snd_pcm_prepare                         ALSA_MANGLER(snd_pcm_prepare)
+#define snd_pcm_sw_params_sizeof                ALSA_MANGLER(snd_pcm_sw_params_sizeof)
+#define snd_pcm_hw_params_set_period_size_near  ALSA_MANGLER(snd_pcm_hw_params_set_period_size_near)
+#define snd_pcm_hw_params_get_period_size       ALSA_MANGLER(snd_pcm_hw_params_get_period_size)
+#define snd_pcm_hw_params                       ALSA_MANGLER(snd_pcm_hw_params)
+#define snd_pcm_hw_params_sizeof                ALSA_MANGLER(snd_pcm_hw_params_sizeof)
+#define snd_pcm_state                           ALSA_MANGLER(snd_pcm_state)
+#define snd_pcm_open                            ALSA_MANGLER(snd_pcm_open)
+#define snd_lib_error_set_handler               ALSA_MANGLER(snd_lib_error_set_handler)
+#define snd_pcm_sw_params                       ALSA_MANGLER(snd_pcm_sw_params)
+#define snd_pcm_hw_params_get_period_size_min   ALSA_MANGLER(snd_pcm_hw_params_get_period_size_min)
+#define snd_pcm_writei                          ALSA_MANGLER(snd_pcm_writei)
+#define snd_pcm_readi                           ALSA_MANGLER(snd_pcm_readi)
+#define snd_strerror                            ALSA_MANGLER(snd_strerror)
+#define snd_pcm_drop                            ALSA_MANGLER(snd_pcm_drop)
+#define snd_pcm_resume                          ALSA_MANGLER(snd_pcm_resume)
+#define snd_pcm_hw_params_get_buffer_size       ALSA_MANGLER(snd_pcm_hw_params_get_buffer_size)
+#define snd_pcm_hw_params_set_rate_near         ALSA_MANGLER(snd_pcm_hw_params_set_rate_near)
+#define snd_pcm_hw_params_set_access            ALSA_MANGLER(snd_pcm_hw_params_set_access)
+#define snd_pcm_hw_params_set_buffer_time_near  ALSA_MANGLER(snd_pcm_hw_params_set_buffer_time_near)
+#define snd_pcm_hw_params_set_buffer_size_near  ALSA_MANGLER(snd_pcm_hw_params_set_buffer_size_near)
+#define snd_pcm_hw_params_get_buffer_size_min   ALSA_MANGLER(snd_pcm_hw_params_get_buffer_size_min)
+#define snd_pcm_hw_params_set_format            ALSA_MANGLER(snd_pcm_hw_params_set_format)
+#define snd_pcm_sw_params_current               ALSA_MANGLER(snd_pcm_sw_params_current)
+#define snd_pcm_sw_params_set_start_threshold   ALSA_MANGLER(snd_pcm_sw_params_set_start_threshold)
+
+#endif /* !AUDIO_ALSA_MANGLING_H */
Index: /trunk/src/VBox/Devices/Audio_old/alsa_stubs.c
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/alsa_stubs.c	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/alsa_stubs.c	(revision 61413)
@@ -0,0 +1,190 @@
+/* $Id$ */
+/** @file
+ * Stubs for libasound.
+ */
+
+/*
+ * 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.
+ */
+#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
+#include <iprt/assert.h>
+#include <iprt/ldr.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+
+#include <alsa/asoundlib.h>
+
+#include "alsa_stubs.h"
+
+#define VBOX_ALSA_LIB "libasound.so.2"
+
+#define PROXY_STUB(function, rettype, signature, shortsig) \
+    static rettype (*pfn_ ## function) signature; \
+    \
+    rettype VBox_##function signature; \
+    rettype VBox_##function signature \
+    { \
+        return pfn_ ## function shortsig; \
+    }
+
+PROXY_STUB(snd_pcm_hw_params_any, int,
+           (snd_pcm_t *pcm, snd_pcm_hw_params_t *params),
+           (pcm, params))
+PROXY_STUB(snd_pcm_close, int, (snd_pcm_t *pcm), (pcm))
+PROXY_STUB(snd_pcm_avail_update, snd_pcm_sframes_t, (snd_pcm_t *pcm),
+           (pcm))
+PROXY_STUB(snd_pcm_hw_params_set_channels_near, int,
+           (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val),
+           (pcm, params, val))
+PROXY_STUB(snd_pcm_hw_params_set_period_time_near, int,
+           (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir),
+           (pcm, params, val, dir))
+PROXY_STUB(snd_pcm_prepare, int, (snd_pcm_t *pcm), (pcm))
+PROXY_STUB(snd_pcm_sw_params_sizeof, size_t, (void), ())
+PROXY_STUB(snd_pcm_hw_params_set_period_size_near, int,
+           (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir),
+           (pcm, params, val, dir))
+PROXY_STUB(snd_pcm_hw_params_get_period_size, int,
+           (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir),
+           (params, frames, dir))
+PROXY_STUB(snd_pcm_hw_params, int,
+           (snd_pcm_t *pcm, snd_pcm_hw_params_t *params),
+           (pcm, params))
+PROXY_STUB(snd_pcm_hw_params_sizeof, size_t, (void), ())
+PROXY_STUB(snd_pcm_state, snd_pcm_state_t, (snd_pcm_t *pcm), (pcm))
+PROXY_STUB(snd_pcm_open, int,
+           (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode),
+           (pcm, name, stream, mode))
+PROXY_STUB(snd_lib_error_set_handler, int, (snd_lib_error_handler_t handler),
+           (handler))
+PROXY_STUB(snd_pcm_sw_params, int,
+           (snd_pcm_t *pcm, snd_pcm_sw_params_t *params),
+           (pcm, params))
+PROXY_STUB(snd_pcm_hw_params_get_period_size_min, int,
+           (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir),
+           (params, frames, dir))
+PROXY_STUB(snd_pcm_writei, snd_pcm_sframes_t,
+           (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size),
+           (pcm, buffer, size))
+PROXY_STUB(snd_pcm_readi, snd_pcm_sframes_t,
+           (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size),
+           (pcm, buffer, size))
+PROXY_STUB(snd_strerror, const char *, (int errnum), (errnum))
+PROXY_STUB(snd_pcm_drop, int, (snd_pcm_t *pcm), (pcm))
+PROXY_STUB(snd_pcm_resume, int, (snd_pcm_t *pcm), (pcm))
+PROXY_STUB(snd_pcm_hw_params_get_buffer_size, int,
+           (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val),
+           (params, val))
+PROXY_STUB(snd_pcm_hw_params_set_rate_near, int,
+           (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir),
+           (pcm, params, val, dir))
+PROXY_STUB(snd_pcm_hw_params_set_access, int,
+           (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access),
+           (pcm, params, _access))
+PROXY_STUB(snd_pcm_hw_params_set_buffer_time_near, int,
+           (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir),
+           (pcm, params, val, dir))
+PROXY_STUB(snd_pcm_hw_params_set_buffer_size_near, int,
+           (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val),
+           (pcm, params, val))
+PROXY_STUB(snd_pcm_hw_params_get_buffer_size_min, int,
+           (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val),
+           (params, val))
+PROXY_STUB(snd_pcm_hw_params_set_format, int,
+           (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val),
+           (pcm, params, val))
+PROXY_STUB(snd_pcm_sw_params_current, int,
+           (snd_pcm_t *pcm, snd_pcm_sw_params_t *params),
+           (pcm, params))
+PROXY_STUB(snd_pcm_sw_params_set_start_threshold, int,
+           (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val),
+           (pcm, params, val))
+
+typedef struct
+{
+    const char *name;
+    void (**fn)(void);
+} SHARED_FUNC;
+
+#define ELEMENT(function) { #function , (void (**)(void)) & pfn_ ## function }
+static SHARED_FUNC SharedFuncs[] =
+{
+    ELEMENT(snd_pcm_hw_params_any),
+    ELEMENT(snd_pcm_close),
+    ELEMENT(snd_pcm_avail_update),
+    ELEMENT(snd_pcm_hw_params_set_channels_near),
+    ELEMENT(snd_pcm_hw_params_set_period_time_near),
+    ELEMENT(snd_pcm_prepare),
+    ELEMENT(snd_pcm_sw_params_sizeof),
+    ELEMENT(snd_pcm_hw_params_set_period_size_near),
+    ELEMENT(snd_pcm_hw_params_get_period_size),
+    ELEMENT(snd_pcm_hw_params),
+    ELEMENT(snd_pcm_hw_params_sizeof),
+    ELEMENT(snd_pcm_state),
+    ELEMENT(snd_pcm_open),
+    ELEMENT(snd_lib_error_set_handler),
+    ELEMENT(snd_pcm_sw_params),
+    ELEMENT(snd_pcm_hw_params_get_period_size_min),
+    ELEMENT(snd_pcm_writei),
+    ELEMENT(snd_pcm_readi),
+    ELEMENT(snd_strerror),
+    ELEMENT(snd_pcm_drop),
+    ELEMENT(snd_pcm_resume),
+    ELEMENT(snd_pcm_hw_params_get_buffer_size),
+    ELEMENT(snd_pcm_hw_params_set_rate_near),
+    ELEMENT(snd_pcm_hw_params_set_access),
+    ELEMENT(snd_pcm_hw_params_set_buffer_time_near),
+    ELEMENT(snd_pcm_hw_params_set_buffer_size_near),
+    ELEMENT(snd_pcm_hw_params_get_buffer_size_min),
+    ELEMENT(snd_pcm_hw_params_set_format),
+    ELEMENT(snd_pcm_sw_params_current),
+    ELEMENT(snd_pcm_sw_params_set_start_threshold),
+};
+#undef ELEMENT
+
+/**
+ * Try to dynamically load the ALSA libraries.  This function is not
+ * thread-safe, and should be called before attempting to use any of the
+ * ALSA functions.
+ *
+ * @returns iprt status code
+ */
+int audioLoadAlsaLib(void)
+{
+    int rc = VINF_SUCCESS;
+    unsigned i;
+    static enum { NO = 0, YES, FAIL } isLibLoaded = NO;
+    RTLDRMOD hLib;
+
+    LogFlowFunc(("\n"));
+    /* If this is not NO then the function has obviously been called twice,
+       which is likely to be a bug. */
+    if (NO != isLibLoaded)
+    {
+        AssertMsgFailed(("isLibLoaded == %s\n", YES == isLibLoaded ? "YES" : "NO"));
+        return YES == isLibLoaded ? VINF_SUCCESS : VERR_NOT_SUPPORTED;
+    }
+    isLibLoaded = FAIL;
+    rc = RTLdrLoad(VBOX_ALSA_LIB, &hLib);
+    if (RT_FAILURE(rc))
+    {
+        LogRelFunc(("Failed to load library %s\n", VBOX_ALSA_LIB));
+        return rc;
+    }
+    for (i=0; i<RT_ELEMENTS(SharedFuncs); i++)
+    {
+        rc = RTLdrGetSymbol(hLib, SharedFuncs[i].name, (void**)SharedFuncs[i].fn);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+    isLibLoaded = YES;
+    return rc;
+}
Index: /trunk/src/VBox/Devices/Audio_old/alsa_stubs.h
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/alsa_stubs.h	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/alsa_stubs.h	(revision 61413)
@@ -0,0 +1,21 @@
+/** @file
+ *
+ * Stubs for libasound.
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef AUDIO_ALSA_STUBS_H
+# define AUDIO_ALSA_STUBS_H
+extern int audioLoadAlsaLib(void);
+#endif
Index: /trunk/src/VBox/Devices/Audio_old/pulse_mangling.h
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/pulse_mangling.h	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/pulse_mangling.h	(revision 61413)
@@ -0,0 +1,69 @@
+/** @file
+ *
+ * Mangle libpulse symbols. This is necessary on hosts which don't
+ * support the -fvisibility gcc switch.
+ */
+
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#ifndef AUDIO_PULSE_MANGLING_H
+#define AUDIO_PULSE_MANGLING_H
+
+#define PULSE_MANGLER(symbol) VBox_##symbol
+
+#define pa_stream_connect_playback              PULSE_MANGLER(pa_stream_connect_playback)
+#define pa_stream_connect_record                PULSE_MANGLER(pa_stream_connect_record)
+#define pa_stream_disconnect                    PULSE_MANGLER(pa_stream_disconnect)
+#define pa_stream_get_sample_spec               PULSE_MANGLER(pa_stream_get_sample_spec)
+#define pa_stream_set_latency_update_callback   PULSE_MANGLER(pa_stream_set_latency_update_callback)
+#define pa_stream_write                         PULSE_MANGLER(pa_stream_write)
+#define pa_stream_unref                         PULSE_MANGLER(pa_stream_unref)
+#define pa_stream_get_state                     PULSE_MANGLER(pa_stream_get_state)
+#define pa_stream_set_state_callback            PULSE_MANGLER(pa_stream_set_state_callback)
+#define pa_stream_flush                         PULSE_MANGLER(pa_stream_flush)
+#define pa_stream_drain                         PULSE_MANGLER(pa_stream_drain)
+#define pa_stream_trigger                       PULSE_MANGLER(pa_stream_trigger)
+#define pa_stream_new                           PULSE_MANGLER(pa_stream_new)
+#define pa_stream_get_buffer_attr               PULSE_MANGLER(pa_stream_get_buffer_attr)
+#define pa_stream_peek                          PULSE_MANGLER(pa_stream_peek)
+#define pa_stream_cork                          PULSE_MANGLER(pa_stream_cork)
+#define pa_stream_drop                          PULSE_MANGLER(pa_stream_drop)
+#define pa_stream_writable_size                 PULSE_MANGLER(pa_stream_writable_size)
+#define pa_context_connect                      PULSE_MANGLER(pa_context_connect)
+#define pa_context_disconnect                   PULSE_MANGLER(pa_context_disconnect)
+#define pa_context_get_state                    PULSE_MANGLER(pa_context_get_state)
+#define pa_context_unref                        PULSE_MANGLER(pa_context_unref)
+#define pa_context_errno                        PULSE_MANGLER(pa_context_errno)
+#define pa_context_new                          PULSE_MANGLER(pa_context_new)
+#define pa_context_set_state_callback           PULSE_MANGLER(pa_context_set_state_callback)
+#define pa_threaded_mainloop_stop               PULSE_MANGLER(pa_threaded_mainloop_stop)
+#define pa_threaded_mainloop_get_api            PULSE_MANGLER(pa_threaded_mainloop_get_api)
+#define pa_threaded_mainloop_free               PULSE_MANGLER(pa_threaded_mainloop_free)
+#define pa_threaded_mainloop_signal             PULSE_MANGLER(pa_threaded_mainloop_signal)
+#define pa_threaded_mainloop_unlock             PULSE_MANGLER(pa_threaded_mainloop_unlock)
+#define pa_threaded_mainloop_new                PULSE_MANGLER(pa_threaded_mainloop_new)
+#define pa_threaded_mainloop_wait               PULSE_MANGLER(pa_threaded_mainloop_wait)
+#define pa_threaded_mainloop_start              PULSE_MANGLER(pa_threaded_mainloop_start)
+#define pa_threaded_mainloop_lock               PULSE_MANGLER(pa_threaded_mainloop_lock)
+#define pa_bytes_per_second                     PULSE_MANGLER(pa_bytes_per_second)
+#define pa_frame_size                           PULSE_MANGLER(pa_frame_size)
+#define pa_sample_format_to_string              PULSE_MANGLER(pa_sample_format_to_string)
+#define pa_sample_spec_valid                    PULSE_MANGLER(pa_sample_spec_valid)
+#define pa_channel_map_init_auto                PULSE_MANGLER(pa_channel_map_init_auto)
+#define pa_operation_unref                      PULSE_MANGLER(pa_operation_unref)
+#define pa_operation_get_state                  PULSE_MANGLER(pa_operation_get_state)
+#define pa_operation_cancel                     PULSE_MANGLER(pa_operation_cancel)
+#define pa_strerror                             PULSE_MANGLER(pa_strerror)
+#define pa_stream_readable_size                 PULSE_MANGLER(pa_stream_readable_size)
+
+#endif /* !AUDIO_PULSE_MANGLING_H */
Index: /trunk/src/VBox/Devices/Audio_old/pulse_stubs.c
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/pulse_stubs.c	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/pulse_stubs.c	(revision 61413)
@@ -0,0 +1,287 @@
+/* $Id$ */
+/** @file
+ * Stubs for libpulse.
+ */
+
+/*
+ * 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.
+ */
+#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
+#include <iprt/assert.h>
+#include <iprt/ldr.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse_stubs.h"
+
+#define VBOX_PULSE_LIB "libpulse.so.0"
+
+#define PROXY_STUB(function, rettype, signature, shortsig) \
+    static rettype (*g_pfn_ ## function) signature; \
+    \
+    rettype VBox_##function signature; \
+    rettype VBox_##function signature \
+    { \
+        return g_pfn_ ## function shortsig; \
+    }
+
+#define PROXY_STUB_VOID(function, signature, shortsig) \
+    static void (*g_pfn_ ## function) signature; \
+    \
+    void VBox_##function signature; \
+    void VBox_##function signature \
+    { \
+        g_pfn_ ## function shortsig; \
+    }
+
+#if PA_PROTOCOL_VERSION >= 16
+PROXY_STUB     (pa_stream_connect_playback, int,
+                (pa_stream *s, const char *dev, const pa_buffer_attr *attr,
+                 pa_stream_flags_t flags, const pa_cvolume *volume, pa_stream *sync_stream),
+                (s, dev, attr, flags, volume, sync_stream))
+#else
+PROXY_STUB     (pa_stream_connect_playback, int,
+                (pa_stream *s, const char *dev, const pa_buffer_attr *attr,
+                 pa_stream_flags_t flags, pa_cvolume *volume, pa_stream *sync_stream),
+                (s, dev, attr, flags, volume, sync_stream))
+#endif
+PROXY_STUB     (pa_stream_connect_record, int,
+                (pa_stream *s, const char *dev, const pa_buffer_attr *attr,
+                pa_stream_flags_t flags),
+                (s, dev, attr, flags))
+PROXY_STUB     (pa_stream_disconnect, int,
+                (pa_stream *s),
+                (s))
+PROXY_STUB     (pa_stream_get_sample_spec, const pa_sample_spec*,
+                (pa_stream *s),
+                (s))
+PROXY_STUB_VOID(pa_stream_set_latency_update_callback,
+                (pa_stream *p, pa_stream_notify_cb_t cb, void *userdata),
+                (p, cb, userdata))
+PROXY_STUB     (pa_stream_write, int,
+                (pa_stream *p, const void *data, size_t bytes, pa_free_cb_t free_cb,
+                 int64_t offset, pa_seek_mode_t seek),
+                (p, data, bytes, free_cb, offset, seek))
+PROXY_STUB_VOID(pa_stream_unref,
+                (pa_stream *s),
+                (s))
+PROXY_STUB     (pa_stream_get_state, pa_stream_state_t,
+                (pa_stream *p),
+                (p))
+PROXY_STUB_VOID(pa_stream_set_state_callback,
+                (pa_stream *s, pa_stream_notify_cb_t cb, void *userdata),
+                (s, cb, userdata))
+PROXY_STUB     (pa_stream_flush, pa_operation*,
+                (pa_stream *s, pa_stream_success_cb_t cb, void *userdata),
+                (s, cb, userdata))
+PROXY_STUB     (pa_stream_drain, pa_operation*,
+                (pa_stream *s, pa_stream_success_cb_t cb, void *userdata),
+                (s, cb, userdata))
+PROXY_STUB     (pa_stream_trigger, pa_operation*,
+                (pa_stream *s, pa_stream_success_cb_t cb, void *userdata),
+                (s, cb, userdata))
+PROXY_STUB     (pa_stream_new, pa_stream*,
+                (pa_context *c, const char *name, const pa_sample_spec *ss,
+                 const pa_channel_map *map),
+                (c, name, ss, map))
+PROXY_STUB     (pa_stream_get_buffer_attr, const pa_buffer_attr*,
+                (pa_stream *s),
+                (s))
+PROXY_STUB     (pa_stream_peek, int,
+                (pa_stream *p, const void **data, size_t *bytes),
+                (p, data, bytes))
+PROXY_STUB     (pa_stream_cork, pa_operation*,
+                (pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata),
+                (s, b, cb, userdata))
+PROXY_STUB     (pa_stream_drop, int,
+                (pa_stream *p),
+                (p))
+PROXY_STUB     (pa_stream_writable_size, size_t,
+                (pa_stream *p),
+                (p))
+PROXY_STUB     (pa_context_connect, int,
+                (pa_context *c, const char *server, pa_context_flags_t flags,
+                 const pa_spawn_api *api),
+                (c, server, flags, api))
+PROXY_STUB_VOID(pa_context_disconnect,
+                (pa_context *c),
+                (c))
+PROXY_STUB     (pa_context_get_state, pa_context_state_t,
+                (pa_context *c),
+                (c))
+PROXY_STUB_VOID(pa_context_unref,
+                (pa_context *c),
+                (c))
+PROXY_STUB     (pa_context_errno, int,
+                (pa_context *c),
+                (c))
+PROXY_STUB     (pa_context_new, pa_context*,
+                (pa_mainloop_api *mainloop, const char *name),
+                (mainloop, name))
+PROXY_STUB_VOID(pa_context_set_state_callback,
+                (pa_context *c, pa_context_notify_cb_t cb, void *userdata),
+                (c, cb, userdata))
+PROXY_STUB_VOID(pa_threaded_mainloop_stop,
+                (pa_threaded_mainloop *m),
+                (m))
+PROXY_STUB     (pa_threaded_mainloop_get_api, pa_mainloop_api*,
+                (pa_threaded_mainloop *m),
+                (m))
+PROXY_STUB_VOID(pa_threaded_mainloop_free,
+                (pa_threaded_mainloop* m),
+                (m))
+PROXY_STUB_VOID(pa_threaded_mainloop_signal,
+                (pa_threaded_mainloop *m, int wait_for_accept),
+                (m, wait_for_accept))
+PROXY_STUB_VOID(pa_threaded_mainloop_unlock,
+                (pa_threaded_mainloop *m),
+                (m))
+PROXY_STUB     (pa_threaded_mainloop_new, pa_threaded_mainloop *,
+                (void),
+                ())
+PROXY_STUB_VOID(pa_threaded_mainloop_wait,
+                (pa_threaded_mainloop *m),
+                (m))
+PROXY_STUB     (pa_threaded_mainloop_start, int,
+                (pa_threaded_mainloop *m),
+                (m))
+PROXY_STUB_VOID(pa_threaded_mainloop_lock,
+                (pa_threaded_mainloop *m),
+                (m))
+PROXY_STUB     (pa_bytes_per_second, size_t,
+                (const pa_sample_spec *spec),
+                (spec))
+PROXY_STUB     (pa_frame_size, size_t,
+                (const pa_sample_spec *spec),
+                (spec))
+PROXY_STUB     (pa_sample_format_to_string, const char*,
+                (pa_sample_format_t f),
+                (f))
+PROXY_STUB     (pa_sample_spec_valid, int,
+                (const pa_sample_spec *spec),
+                (spec))
+PROXY_STUB     (pa_channel_map_init_auto, pa_channel_map*,
+                (pa_channel_map *m, unsigned channels, pa_channel_map_def_t def),
+                (m, channels, def))
+PROXY_STUB_VOID(pa_operation_unref,
+                (pa_operation *o),
+                (o))
+PROXY_STUB     (pa_operation_get_state, pa_operation_state_t,
+                (pa_operation *o),
+                (o))
+PROXY_STUB_VOID(pa_operation_cancel,
+                (pa_operation *o),
+                (o))
+PROXY_STUB     (pa_strerror, const char*,
+                (int error),
+                (error))
+PROXY_STUB     (pa_stream_readable_size, size_t,
+                (pa_stream *p),
+                (p))
+
+
+typedef struct
+{
+    const char *name;
+    void (**fn)(void);
+} SHARED_FUNC;
+
+#define ELEMENT(function) { #function , (void (**)(void)) & g_pfn_ ## function }
+static SHARED_FUNC SharedFuncs[] =
+{
+    ELEMENT(pa_stream_connect_playback),
+    ELEMENT(pa_stream_connect_record),
+    ELEMENT(pa_stream_disconnect),
+    ELEMENT(pa_stream_get_sample_spec),
+    ELEMENT(pa_stream_set_latency_update_callback),
+    ELEMENT(pa_stream_write),
+    ELEMENT(pa_stream_unref),
+    ELEMENT(pa_stream_get_state),
+    ELEMENT(pa_stream_set_state_callback),
+    ELEMENT(pa_stream_flush),
+    ELEMENT(pa_stream_drain),
+    ELEMENT(pa_stream_trigger),
+    ELEMENT(pa_stream_new),
+    ELEMENT(pa_stream_get_buffer_attr),
+    ELEMENT(pa_stream_peek),
+    ELEMENT(pa_stream_cork),
+    ELEMENT(pa_stream_drop),
+    ELEMENT(pa_stream_writable_size),
+    ELEMENT(pa_context_connect),
+    ELEMENT(pa_context_disconnect),
+    ELEMENT(pa_context_get_state),
+    ELEMENT(pa_context_unref),
+    ELEMENT(pa_context_errno),
+    ELEMENT(pa_context_new),
+    ELEMENT(pa_context_set_state_callback),
+    ELEMENT(pa_threaded_mainloop_stop),
+    ELEMENT(pa_threaded_mainloop_get_api),
+    ELEMENT(pa_threaded_mainloop_free),
+    ELEMENT(pa_threaded_mainloop_signal),
+    ELEMENT(pa_threaded_mainloop_unlock),
+    ELEMENT(pa_threaded_mainloop_new),
+    ELEMENT(pa_threaded_mainloop_wait),
+    ELEMENT(pa_threaded_mainloop_start),
+    ELEMENT(pa_threaded_mainloop_lock),
+    ELEMENT(pa_bytes_per_second),
+    ELEMENT(pa_frame_size),
+    ELEMENT(pa_sample_format_to_string),
+    ELEMENT(pa_sample_spec_valid),
+    ELEMENT(pa_channel_map_init_auto),
+    ELEMENT(pa_operation_unref),
+    ELEMENT(pa_operation_get_state),
+    ELEMENT(pa_operation_cancel),
+    ELEMENT(pa_strerror),
+    ELEMENT(pa_stream_readable_size)
+};
+#undef ELEMENT
+
+/**
+ * Try to dynamically load the PulseAudio libraries.  This function is not
+ * thread-safe, and should be called before attempting to use any of the
+ * PulseAudio functions.
+ *
+ * @returns iprt status code
+ */
+int audioLoadPulseLib(void)
+{
+    int rc = VINF_SUCCESS;
+    unsigned i;
+    static enum { NO = 0, YES, FAIL } isLibLoaded = NO;
+    RTLDRMOD hLib;
+
+    LogFlowFunc(("\n"));
+    /* If this is not NO then the function has obviously been called twice,
+       which is likely to be a bug. */
+    if (NO != isLibLoaded)
+    {
+        AssertMsgFailed(("isLibLoaded == %s\n", YES == isLibLoaded ? "YES" : "NO"));
+        return YES == isLibLoaded ? VINF_SUCCESS : VERR_NOT_SUPPORTED;
+    }
+    isLibLoaded = FAIL;
+    rc = RTLdrLoad(VBOX_PULSE_LIB, &hLib);
+    if (RT_FAILURE(rc))
+    {
+        LogRelFunc(("Failed to load library %s\n", VBOX_PULSE_LIB));
+        return rc;
+    }
+    for (i=0; i<RT_ELEMENTS(SharedFuncs); i++)
+    {
+        rc = RTLdrGetSymbol(hLib, SharedFuncs[i].name, (void**)SharedFuncs[i].fn);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+    isLibLoaded = YES;
+    return rc;
+}
+
Index: /trunk/src/VBox/Devices/Audio_old/pulse_stubs.h
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/pulse_stubs.h	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/pulse_stubs.h	(revision 61413)
@@ -0,0 +1,21 @@
+/** @file
+ *
+ * Stubs for libpulse.
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef AUDIO_PULSE_STUBS_H
+#define AUDIO_PULSE_STUBS_H
+extern int audioLoadPulseLib(void);
+#endif
Index: /trunk/src/VBox/Devices/Audio_old/sys-queue.h
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/sys-queue.h	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/sys-queue.h	(revision 61413)
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)queue.h	8.3 (Berkeley) 12/13/93
+ */
+
+#ifndef	_SYS_QUEUE_H
+#define	_SYS_QUEUE_H 1
+
+/*
+ * This file defines three types of data structures: lists, tail queues,
+ * and circular queues.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element or at the head of the list. A list may only be
+ * traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A tail queue may only be traversed in the forward direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type)						\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List functions.
+ */
+#define	LIST_INIT(head) {						\
+	(head)->lh_first = NULL;					\
+}
+
+#define LIST_INSERT_AFTER(listelm, elm, field) {			\
+	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\
+		(listelm)->field.le_next->field.le_prev =		\
+		    &(elm)->field.le_next;				\
+	(listelm)->field.le_next = (elm);				\
+	(elm)->field.le_prev = &(listelm)->field.le_next;		\
+}
+
+#define LIST_INSERT_HEAD(head, elm, field) {				\
+	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\
+		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+	(head)->lh_first = (elm);					\
+	(elm)->field.le_prev = &(head)->lh_first;			\
+}
+
+#define LIST_REMOVE(elm, field) {					\
+	if ((elm)->field.le_next != NULL)				\
+		(elm)->field.le_next->field.le_prev = 			\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = (elm)->field.le_next;			\
+}
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+}
+
+#define TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_INIT(head) {						\
+	(head)->tqh_first = NULL;					\
+	(head)->tqh_last = &(head)->tqh_first;				\
+}
+
+#define TAILQ_INSERT_HEAD(head, elm, field) {				\
+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(head)->tqh_first = (elm);					\
+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
+}
+
+#define TAILQ_INSERT_TAIL(head, elm, field) {				\
+	(elm)->field.tqe_next = NULL;					\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &(elm)->field.tqe_next;			\
+}
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) {			\
+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+		(elm)->field.tqe_next->field.tqe_prev = 		\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(listelm)->field.tqe_next = (elm);				\
+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
+}
+
+#define TAILQ_REMOVE(head, elm, field) {				\
+	if (((elm)->field.tqe_next) != NULL)				\
+		(elm)->field.tqe_next->field.tqe_prev = 		\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
+}
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *cqh_first;		/* first element */		\
+	struct type *cqh_last;		/* last element */		\
+}
+
+#define CIRCLEQ_ENTRY(type)						\
+struct {								\
+	struct type *cqe_next;		/* next element */		\
+	struct type *cqe_prev;		/* previous element */		\
+}
+
+/*
+ * Circular queue functions.
+ */
+#define	CIRCLEQ_INIT(head) {						\
+	(head)->cqh_first = (void *)(head);				\
+	(head)->cqh_last = (void *)(head);				\
+}
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) {		\
+	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\
+	(elm)->field.cqe_prev = (listelm);				\
+	if ((listelm)->field.cqe_next == (void *)(head))		\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\
+	(listelm)->field.cqe_next = (elm);				\
+}
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) {		\
+	(elm)->field.cqe_next = (listelm);				\
+	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\
+	if ((listelm)->field.cqe_prev == (void *)(head))		\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\
+	(listelm)->field.cqe_prev = (elm);				\
+}
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) {				\
+	(elm)->field.cqe_next = (head)->cqh_first;			\
+	(elm)->field.cqe_prev = (void *)(head);				\
+	if ((head)->cqh_last == (void *)(head))				\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(head)->cqh_first->field.cqe_prev = (elm);		\
+	(head)->cqh_first = (elm);					\
+}
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) {				\
+	(elm)->field.cqe_next = (void *)(head);				\
+	(elm)->field.cqe_prev = (head)->cqh_last;			\
+	if ((head)->cqh_first == (void *)(head))			\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(head)->cqh_last->field.cqe_next = (elm);		\
+	(head)->cqh_last = (elm);					\
+}
+
+#define	CIRCLEQ_REMOVE(head, elm, field) {				\
+	if ((elm)->field.cqe_next == (void *)(head))			\
+		(head)->cqh_last = (elm)->field.cqe_prev;		\
+	else								\
+		(elm)->field.cqe_next->field.cqe_prev =			\
+		    (elm)->field.cqe_prev;				\
+	if ((elm)->field.cqe_prev == (void *)(head))			\
+		(head)->cqh_first = (elm)->field.cqe_next;		\
+	else								\
+		(elm)->field.cqe_prev->field.cqe_next =			\
+		    (elm)->field.cqe_next;				\
+}
+#endif	/* sys/queue.h */
Index: /trunk/src/VBox/Devices/Audio_old/testcase/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/testcase/Makefile.kmk	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/testcase/Makefile.kmk	(revision 61413)
@@ -0,0 +1,41 @@
+# $Id$
+## @file
+# Sub-Makefile for the audio testcases.
+#
+
+#
+# Copyright (C) 2014-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.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK)
+
+ PROGRAMS += tstAudioMixBuffer
+ TESTING  += $(tstAudioMixBuffer_0_OUTDIR)/tstAudioMixBuffer.run
+
+ tstAudioMixBuffer_TEMPLATE = VBOXR3TSTEXE
+ tstAudioMixBuffer_DEFS    += TESTCASE
+ tstAudioMixBuffer_SOURCES  = \
+	tstAudioMixBuffer.cpp \
+	../AudioMixBuffer.cpp \
+	../DrvAudioCommon.cpp
+ tstAudioMixBuffer_LIBS     = $(LIB_RUNTIME)
+
+ $$(tstAudioMixBuffer_0_OUTDIR)/tstAudioMixBuffer.run: $$(tstAudioMixBuffer_1_STAGE_TARGET)
+	export VBOX_LOG_DEST=nofile; $(tstAudioMixBuffer_1_STAGE_TARGET) quiet
+	$(QUIET)$(APPEND) -t "$@" "done"
+
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
Index: /trunk/src/VBox/Devices/Audio_old/testcase/tstAudioMixBuffer.cpp
===================================================================
--- /trunk/src/VBox/Devices/Audio_old/testcase/tstAudioMixBuffer.cpp	(revision 61413)
+++ /trunk/src/VBox/Devices/Audio_old/testcase/tstAudioMixBuffer.cpp	(revision 61413)
@@ -0,0 +1,592 @@
+/* $Id$ */
+/** @file
+ * Audio testcase - Mixing buffer.
+ */
+
+/*
+ * Copyright (C) 2014-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 <iprt/err.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/rand.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+
+
+#include "../AudioMixBuffer.h"
+#include "../DrvAudio.h"
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+
+static int tstSingle(RTTEST hTest)
+{
+    RTTestSubF(hTest, "Single buffer");
+
+    PDMAUDIOSTREAMCFG config =
+    {
+        44100,                   /* Hz */
+        2                        /* Channels */,
+        AUD_FMT_S16              /* Format */,
+        PDMAUDIOENDIANNESS_LITTLE /* ENDIANNESS */
+    };
+    PDMPCMPROPS props;
+
+    int rc = DrvAudioStreamCfgToProps(&config, &props);
+    AssertRC(rc);
+
+    uint32_t cBufSize = _1K;
+
+    /*
+     * General stuff.
+     */
+    PDMAUDIOMIXBUF mb;
+    RTTESTI_CHECK_RC_OK(AudioMixBufInit(&mb, "Single", &props, cBufSize));
+    RTTESTI_CHECK(AudioMixBufSize(&mb) == cBufSize);
+    RTTESTI_CHECK(AUDIOMIXBUF_B2S(&mb, AudioMixBufSizeBytes(&mb)) == cBufSize);
+    RTTESTI_CHECK(AUDIOMIXBUF_S2B(&mb, AudioMixBufSize(&mb)) == AudioMixBufSizeBytes(&mb));
+    RTTESTI_CHECK(AudioMixBufFree(&mb) == cBufSize);
+    RTTESTI_CHECK(AUDIOMIXBUF_S2B(&mb, AudioMixBufFree(&mb)) == AudioMixBufFreeBytes(&mb));
+
+    /*
+     * Absolute writes.
+     */
+    uint32_t read  = 0, written = 0, written_abs = 0;
+    int8_t  samples8 [2] = { 0x12, 0x34 };
+    int16_t samples16[2] = { 0xAA, 0xBB };
+    int32_t samples32[2] = { 0xCC, 0xDD };
+    int64_t samples64[2] = { 0xEE, 0xFF };
+
+    RTTESTI_CHECK_RC_OK(AudioMixBufWriteAt(&mb, 0, &samples8, sizeof(samples8), &written));
+    RTTESTI_CHECK(written == 0 /* Samples */);
+
+    RTTESTI_CHECK_RC_OK(AudioMixBufWriteAt(&mb, 0, &samples16, sizeof(samples16), &written));
+    RTTESTI_CHECK(written == 1 /* Samples */);
+
+    RTTESTI_CHECK_RC_OK(AudioMixBufWriteAt(&mb, 2, &samples32, sizeof(samples32), &written));
+    RTTESTI_CHECK(written == 2 /* Samples */);
+    written_abs = 0;
+
+    /* Beyond buffer. */
+    RTTESTI_CHECK_RC(AudioMixBufWriteAt(&mb, AudioMixBufSize(&mb) + 1, &samples16, sizeof(samples16),
+                                        &written), VERR_BUFFER_OVERFLOW);
+
+    /*
+     * Circular writes.
+     */
+    uint32_t cToWrite = AudioMixBufSize(&mb) - written_abs - 1; /* -1 as padding plus -2 samples for above. */
+    for (uint32_t i = 0; i < cToWrite; i++)
+    {
+        RTTESTI_CHECK_RC_OK(AudioMixBufWriteCirc(&mb, &samples16, sizeof(samples16), &written));
+        RTTESTI_CHECK(written == 1);
+    }
+    RTTESTI_CHECK(!AudioMixBufIsEmpty(&mb));
+    RTTESTI_CHECK(AudioMixBufFree(&mb) == 1);
+    RTTESTI_CHECK(AudioMixBufFreeBytes(&mb) == AUDIOMIXBUF_S2B(&mb, 1U));
+    RTTESTI_CHECK(AudioMixBufProcessed(&mb) == cToWrite + written_abs /* + last absolute write */);
+
+    RTTESTI_CHECK_RC_OK(AudioMixBufWriteCirc(&mb, &samples16, sizeof(samples16), &written));
+    RTTESTI_CHECK(written == 1);
+    RTTESTI_CHECK(AudioMixBufFree(&mb) == 0);
+    RTTESTI_CHECK(AudioMixBufFreeBytes(&mb) == AUDIOMIXBUF_S2B(&mb, 0));
+    RTTESTI_CHECK(AudioMixBufProcessed(&mb) == cBufSize);
+
+    /* Circular reads. */
+    uint32_t cToRead = AudioMixBufSize(&mb) - written_abs - 1;
+    for (uint32_t i = 0; i < cToWrite; i++)
+    {
+        RTTESTI_CHECK_RC_OK(AudioMixBufReadCirc(&mb, &samples16, sizeof(samples16), &read));
+        RTTESTI_CHECK(read == 1);
+        AudioMixBufFinish(&mb, read);
+    }
+    RTTESTI_CHECK(!AudioMixBufIsEmpty(&mb));
+    RTTESTI_CHECK(AudioMixBufFree(&mb) == AudioMixBufSize(&mb) - written_abs - 1);
+    RTTESTI_CHECK(AudioMixBufFreeBytes(&mb) == AUDIOMIXBUF_S2B(&mb, cBufSize - written_abs - 1));
+    RTTESTI_CHECK(AudioMixBufProcessed(&mb) == cBufSize - cToRead + written_abs);
+
+    RTTESTI_CHECK_RC_OK(AudioMixBufReadCirc(&mb, &samples16, sizeof(samples16), &read));
+    RTTESTI_CHECK(read == 1);
+    AudioMixBufFinish(&mb, read);
+    RTTESTI_CHECK(AudioMixBufFree(&mb) == cBufSize - written_abs);
+    RTTESTI_CHECK(AudioMixBufFreeBytes(&mb) == AUDIOMIXBUF_S2B(&mb, cBufSize - written_abs));
+    RTTESTI_CHECK(AudioMixBufProcessed(&mb) == written_abs);
+
+    AudioMixBufDestroy(&mb);
+
+    return RTTestSubErrorCount(hTest) ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
+}
+
+static int tstParentChild(RTTEST hTest)
+{
+    RTTestSubF(hTest, "2 Children -> Parent");
+
+    uint32_t cBufSize = _1K;
+
+    PDMAUDIOSTREAMCFG cfg_p =
+    {
+        44100,                   /* Hz */
+        2                        /* Channels */,
+        AUD_FMT_S16              /* Format */,
+        PDMAUDIOENDIANNESS_LITTLE /* ENDIANNESS */
+    };
+    PDMPCMPROPS props;
+
+    int rc = DrvAudioStreamCfgToProps(&cfg_p, &props);
+    AssertRC(rc);
+
+    PDMAUDIOMIXBUF parent;
+    RTTESTI_CHECK_RC_OK(AudioMixBufInit(&parent, "Parent", &props, cBufSize));
+
+    PDMAUDIOSTREAMCFG cfg_c1 = /* Upmixing to parent */
+    {
+        22100,                   /* Hz */
+        2                        /* Channels */,
+        AUD_FMT_S16              /* Format */,
+        PDMAUDIOENDIANNESS_LITTLE /* ENDIANNESS */
+    };
+
+    rc = DrvAudioStreamCfgToProps(&cfg_c1, &props);
+    AssertRC(rc);
+
+    PDMAUDIOMIXBUF child1;
+    RTTESTI_CHECK_RC_OK(AudioMixBufInit(&child1, "Child1", &props, cBufSize));
+    RTTESTI_CHECK_RC_OK(AudioMixBufLinkTo(&child1, &parent));
+
+    PDMAUDIOSTREAMCFG cfg_c2 = /* Downmixing to parent */
+    {
+        48000,                   /* Hz */
+        2                        /* Channels */,
+        AUD_FMT_S16              /* Format */,
+        PDMAUDIOENDIANNESS_LITTLE /* ENDIANNESS */
+    };
+
+    rc = DrvAudioStreamCfgToProps(&cfg_c2, &props);
+    AssertRC(rc);
+
+    PDMAUDIOMIXBUF child2;
+    RTTESTI_CHECK_RC_OK(AudioMixBufInit(&child2, "Child2", &props, cBufSize));
+    RTTESTI_CHECK_RC_OK(AudioMixBufLinkTo(&child2, &parent));
+
+    /*
+     * Writing + mixing from child/children -> parent, sequential.
+     */
+    uint32_t cbBuf = _1K;
+    char pvBuf[_1K];
+    int16_t samples[32] = { 0xAA, 0xBB };
+    uint32_t read , written, mixed, temp;
+
+    uint32_t cChild1Free     = cBufSize;
+    uint32_t cChild1Mixed    = 0;
+    uint32_t cSamplesParent1 = 16;
+    uint32_t cSamplesChild1  = 16;
+
+    uint32_t cChild2Free     = cBufSize;
+    uint32_t cChild2Mixed    = 0;
+    uint32_t cSamplesParent2 = 16;
+    uint32_t cSamplesChild2  = 16;
+
+    uint32_t t = RTRandU32() % 64;
+
+    for (uint32_t i = 0; i < t; i++)
+    {
+        RTTestPrintf(hTest, RTTESTLVL_DEBUG, "i=%RU32\n", i);
+        RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufWriteAt(&child1, 0, &samples, sizeof(samples), &written));
+        RTTESTI_CHECK_MSG_BREAK(written == cSamplesChild1, ("Child1: Expected %RU32 written samples, got %RU32\n", cSamplesChild1, written));
+        RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufMixToParent(&child1, written, &mixed));
+        temp = AudioMixBufProcessed(&parent) - AudioMixBufMixed(&child2);
+        RTTESTI_CHECK_MSG_BREAK(AudioMixBufMixed(&child1) == temp, ("Child1: Expected %RU32 mixed samples, got %RU32\n", AudioMixBufMixed(&child1), temp));
+
+        RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufWriteAt(&child2, 0, &samples, sizeof(samples), &written));
+        RTTESTI_CHECK_MSG_BREAK(written == cSamplesChild2, ("Child2: Expected %RU32 written samples, got %RU32\n", cSamplesChild2, written));
+        RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufMixToParent(&child2, written, &mixed));
+        temp = AudioMixBufProcessed(&parent) - AudioMixBufMixed(&child1);
+        RTTESTI_CHECK_MSG_BREAK(AudioMixBufMixed(&child2) == temp, ("Child2: Expected %RU32 mixed samples, got %RU32\n", AudioMixBufMixed(&child2), temp));
+    }
+
+    RTTESTI_CHECK(AudioMixBufProcessed(&parent) == AudioMixBufMixed(&child1) + AudioMixBufMixed(&child2));
+
+    for (;;)
+    {
+        RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufReadCirc(&parent, pvBuf, cbBuf, &read));
+        if (!read)
+            break;
+        AudioMixBufFinish(&parent, read);
+    }
+
+    RTTESTI_CHECK(AudioMixBufProcessed(&parent) == 0);
+    RTTESTI_CHECK(AudioMixBufMixed(&child1) == 0);
+    RTTESTI_CHECK(AudioMixBufMixed(&child2) == 0);
+
+    AudioMixBufDestroy(&parent);
+    AudioMixBufDestroy(&child1);
+    AudioMixBufDestroy(&child2);
+
+    return RTTestSubErrorCount(hTest) ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
+}
+
+/* Test 8-bit sample conversion (8-bit -> internal -> 8-bit). */
+static int tstConversion8(RTTEST hTest)
+{
+    unsigned        i;
+    uint32_t        cBufSize = 256;
+    PDMPCMPROPS     props;
+
+
+    RTTestSubF(hTest, "Sample conversion");
+
+    PDMAUDIOSTREAMCFG cfg_p =
+    {
+        44100,                   /* Hz */
+        1                        /* Channels */,
+        AUD_FMT_U8               /* Format */,
+        PDMAUDIOENDIANNESS_LITTLE /* ENDIANNESS */
+    };
+
+    int rc = DrvAudioStreamCfgToProps(&cfg_p, &props);
+    AssertRC(rc);
+
+    PDMAUDIOMIXBUF parent;
+    RTTESTI_CHECK_RC_OK(AudioMixBufInit(&parent, "Parent", &props, cBufSize));
+
+    /* Child uses half the sample rate; that ensures the mixing engine can't
+     * take shortcuts and performs conversion. Because conversion to double
+     * the sample rate effectively inserts one additional sample between every
+     * two source samples, N source samples will be converted to N * 2 - 1
+     * samples. However, the last source sample will be saved for later
+     * interpolation and not immediately output.
+     */
+    PDMAUDIOSTREAMCFG cfg_c =   /* Upmixing to parent */
+    {
+        22050,                   /* Hz */
+        1                        /* Channels */,
+        AUD_FMT_U8               /* Format */,
+        PDMAUDIOENDIANNESS_LITTLE /* ENDIANNESS */
+    };
+
+    rc = DrvAudioStreamCfgToProps(&cfg_c, &props);
+    AssertRC(rc);
+
+    PDMAUDIOMIXBUF child;
+    RTTESTI_CHECK_RC_OK(AudioMixBufInit(&child, "Child", &props, cBufSize));
+    RTTESTI_CHECK_RC_OK(AudioMixBufLinkTo(&child, &parent));
+
+    /* 8-bit unsigned samples. Often used with SB16 device. */
+    uint8_t     samples[16]  = { 0xAA, 0xBB, 0, 1, 43, 125, 126, 127,
+                                 128, 129, 130, 131, 132, UINT8_MAX - 1, UINT8_MAX, 0 };
+
+    /*
+     * Writing + mixing from child -> parent, sequential.
+     */
+    uint32_t    cbBuf = 256;
+    char        achBuf[256];
+    uint32_t    read, written, mixed, temp;
+
+    uint32_t cChildFree     = cBufSize;
+    uint32_t cChildMixed    = 0;
+    uint32_t cSamplesChild  = 16;
+    uint32_t cSamplesParent = cSamplesChild * 2 - 2;
+    uint32_t cSamplesRead   = 0;
+
+    /**** 8-bit unsigned samples ****/
+    RTTestPrintf(hTest, RTTESTLVL_DEBUG, "Conversion test %uHz %uch 8-bit\n", cfg_c.uHz, cfg_c.cChannels);
+    RTTESTI_CHECK_RC_OK(AudioMixBufWriteAt(&child, 0, &samples, sizeof(samples), &written));
+    RTTESTI_CHECK_MSG(written == cSamplesChild, ("Child: Expected %RU32 written samples, got %RU32\n", cSamplesChild, written));
+    RTTESTI_CHECK_RC_OK(AudioMixBufMixToParent(&child, written, &mixed));
+    temp = AudioMixBufProcessed(&parent);
+    RTTESTI_CHECK_MSG(AudioMixBufMixed(&child) == temp, ("Child: Expected %RU32 mixed samples, got %RU32\n", AudioMixBufMixed(&child), temp));
+
+    RTTESTI_CHECK(AudioMixBufProcessed(&parent) == AudioMixBufMixed(&child));
+
+    for (;;)
+    {
+        RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufReadCirc(&parent, achBuf, cbBuf, &read));
+        if (!read)
+            break;
+        cSamplesRead += read;
+        AudioMixBufFinish(&parent, read);
+    }
+    RTTESTI_CHECK_MSG(cSamplesRead == cSamplesParent, ("Parent: Expected %RU32 mixed samples, got %RU32\n", cSamplesParent, cSamplesRead));
+
+    /* Check that the samples came out unharmed. Every other sample is interpolated and we ignore it. */
+    /* NB: This also checks that the default volume setting is 0dB attenuation. */
+    uint8_t *pSrc8 = &samples[0];
+    uint8_t *pDst8 = (uint8_t *)achBuf;
+
+    for (i = 0; i < cSamplesChild - 1; ++i)
+    {
+        RTTESTI_CHECK_MSG(*pSrc8 == *pDst8, ("index %u: Dst=%d, Src=%d\n", i, *pDst8, *pSrc8));
+        pSrc8 += 1;
+        pDst8 += 2;
+    }
+
+    RTTESTI_CHECK(AudioMixBufProcessed(&parent) == 0);
+    RTTESTI_CHECK(AudioMixBufMixed(&child) == 0);
+
+    AudioMixBufDestroy(&parent);
+    AudioMixBufDestroy(&child);
+
+    return RTTestSubErrorCount(hTest) ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
+}
+
+/* Test 16-bit sample conversion (16-bit -> internal -> 16-bit). */
+static int tstConversion16(RTTEST hTest)
+{
+    unsigned        i;
+    uint32_t        cBufSize = 256;
+    PDMPCMPROPS     props;
+
+
+    RTTestSubF(hTest, "Sample conversion 16-bit");
+
+    PDMAUDIOSTREAMCFG cfg_p =
+    {
+        44100,                   /* Hz */
+        1                        /* Channels */,
+        AUD_FMT_S16              /* Format */,
+        PDMAUDIOENDIANNESS_LITTLE /* ENDIANNESS */
+    };
+
+    int rc = DrvAudioStreamCfgToProps(&cfg_p, &props);
+    AssertRC(rc);
+
+    PDMAUDIOMIXBUF parent;
+    RTTESTI_CHECK_RC_OK(AudioMixBufInit(&parent, "Parent", &props, cBufSize));
+
+    PDMAUDIOSTREAMCFG cfg_c =   /* Upmixing to parent */
+    {
+        22050,                   /* Hz */
+        1                        /* Channels */,
+        AUD_FMT_S16              /* Format */,
+        PDMAUDIOENDIANNESS_LITTLE /* ENDIANNESS */
+    };
+
+    rc = DrvAudioStreamCfgToProps(&cfg_c, &props);
+    AssertRC(rc);
+
+    PDMAUDIOMIXBUF child;
+    RTTESTI_CHECK_RC_OK(AudioMixBufInit(&child, "Child", &props, cBufSize));
+    RTTESTI_CHECK_RC_OK(AudioMixBufLinkTo(&child, &parent));
+
+    /* 16-bit signed. More or less exclusively used as output, and usually as input, too. */
+    int16_t     samples[16] = { 0xAA, 0xBB, INT16_MIN, INT16_MIN + 1, INT16_MIN / 2, -3, -2, -1,
+                                0, 1, 2, 3, INT16_MAX / 2, INT16_MAX - 1, INT16_MAX, 0 };
+
+    /*
+     * Writing + mixing from child -> parent, sequential.
+     */
+    uint32_t    cbBuf = 256;
+    char        achBuf[256];
+    uint32_t    read, written, mixed, temp;
+
+    uint32_t cChildFree     = cBufSize;
+    uint32_t cChildMixed    = 0;
+    uint32_t cSamplesChild  = 16;
+    uint32_t cSamplesParent = cSamplesChild * 2 - 2;
+    uint32_t cSamplesRead   = 0;
+
+    /**** 16-bit signed samples ****/
+    RTTestPrintf(hTest, RTTESTLVL_DEBUG, "Conversion test %uHz %uch 16-bit\n", cfg_c.uHz, cfg_c.cChannels);
+    RTTESTI_CHECK_RC_OK(AudioMixBufWriteAt(&child, 0, &samples, sizeof(samples), &written));
+    RTTESTI_CHECK_MSG(written == cSamplesChild, ("Child: Expected %RU32 written samples, got %RU32\n", cSamplesChild, written));
+    RTTESTI_CHECK_RC_OK(AudioMixBufMixToParent(&child, written, &mixed));
+    temp = AudioMixBufProcessed(&parent);
+    RTTESTI_CHECK_MSG(AudioMixBufMixed(&child) == temp, ("Child: Expected %RU32 mixed samples, got %RU32\n", AudioMixBufMixed(&child), temp));
+
+    RTTESTI_CHECK(AudioMixBufProcessed(&parent) == AudioMixBufMixed(&child));
+
+    for (;;)
+    {
+        RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufReadCirc(&parent, achBuf, cbBuf, &read));
+        if (!read)
+            break;
+        cSamplesRead += read;
+        AudioMixBufFinish(&parent, read);
+    }
+    RTTESTI_CHECK_MSG(cSamplesRead == cSamplesParent, ("Parent: Expected %RU32 mixed samples, got %RU32\n", cSamplesParent, cSamplesRead));
+
+    /* Check that the samples came out unharmed. Every other sample is interpolated and we ignore it. */
+    /* NB: This also checks that the default volume setting is 0dB attenuation. */
+    int16_t *pSrc16 = &samples[0];
+    int16_t *pDst16 = (int16_t *)achBuf;
+
+    for (i = 0; i < cSamplesChild - 1; ++i)
+    {
+        RTTESTI_CHECK_MSG(*pSrc16 == *pDst16, ("index %u: Dst=%d, Src=%d\n", i, *pDst16, *pSrc16));
+        pSrc16 += 1;
+        pDst16 += 2;
+    }
+
+    RTTESTI_CHECK(AudioMixBufProcessed(&parent) == 0);
+    RTTESTI_CHECK(AudioMixBufMixed(&child) == 0);
+
+    AudioMixBufDestroy(&parent);
+    AudioMixBufDestroy(&child);
+
+    return RTTestSubErrorCount(hTest) ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
+}
+
+/* Test volume control. */
+static int tstVolume(RTTEST hTest)
+{
+    unsigned        i;
+    uint32_t        cBufSize = 256;
+    PDMPCMPROPS     props;
+
+
+    RTTestSubF(hTest, "Volume control");
+
+    /* Same for parent/child. */
+    PDMAUDIOSTREAMCFG cfg =
+    {
+        44100,                   /* Hz */
+        2                        /* Channels */,
+        AUD_FMT_S16              /* Format */,
+        PDMAUDIOENDIANNESS_LITTLE /* ENDIANNESS */
+    };
+
+    int rc = DrvAudioStreamCfgToProps(&cfg, &props);
+    AssertRC(rc);
+
+    PDMAUDIOVOLUME vol = { false, 0, 0 };   /* Not muted. */
+    PDMAUDIOMIXBUF parent;
+    RTTESTI_CHECK_RC_OK(AudioMixBufInit(&parent, "Parent", &props, cBufSize));
+
+    PDMAUDIOMIXBUF child;
+    RTTESTI_CHECK_RC_OK(AudioMixBufInit(&child, "Child", &props, cBufSize));
+    RTTESTI_CHECK_RC_OK(AudioMixBufLinkTo(&child, &parent));
+
+    /* A few 16-bit signed samples. */
+    int16_t     samples[16] = { INT16_MIN, INT16_MIN + 1, -128, -64, -4, -1, 0, 1,
+                                2, 255, 256, INT16_MAX / 2, INT16_MAX - 2, INT16_MAX - 1, INT16_MAX, 0 };
+
+    /*
+     * Writing + mixing from child -> parent.
+     */
+    uint32_t    cbBuf = 256;
+    char        achBuf[256];
+    uint32_t    read, written, mixed;
+
+    uint32_t cChildFree     = cBufSize;
+    uint32_t cChildMixed    = 0;
+    uint32_t cSamplesChild  = 8;
+    uint32_t cSamplesParent = cSamplesChild;
+    uint32_t cSamplesRead;
+    int16_t *pSrc16;
+    int16_t *pDst16;
+
+    /**** Volume control test ****/
+    RTTestPrintf(hTest, RTTESTLVL_DEBUG, "Volume control test %uHz %uch \n", cfg.uHz, cfg.cChannels);
+
+    /* 1) Full volume/0dB attenuation (255). */
+    vol.uLeft = vol.uRight = 255;
+    AudioMixBufSetVolume(&child, &vol);
+
+    RTTESTI_CHECK_RC_OK(AudioMixBufWriteAt(&child, 0, &samples, sizeof(samples), &written));
+    RTTESTI_CHECK_MSG(written == cSamplesChild, ("Child: Expected %RU32 written samples, got %RU32\n", cSamplesChild, written));
+    RTTESTI_CHECK_RC_OK(AudioMixBufMixToParent(&child, written, &mixed));
+
+    cSamplesRead = 0;
+    for (;;)
+    {
+        RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufReadCirc(&parent, achBuf, cbBuf, &read));
+        if (!read)
+            break;
+        cSamplesRead += read;
+        AudioMixBufFinish(&parent, read);
+    }
+    RTTESTI_CHECK_MSG(cSamplesRead == cSamplesParent, ("Parent: Expected %RU32 mixed samples, got %RU32\n", cSamplesParent, cSamplesRead));
+
+    /* Check that at 0dB the samples came out unharmed. */
+    pSrc16 = &samples[0];
+    pDst16 = (int16_t *)achBuf;
+
+    for (i = 0; i < cSamplesParent * 2 /* stereo */; ++i)
+    {
+        RTTESTI_CHECK_MSG(*pSrc16 == *pDst16, ("index %u: Dst=%d, Src=%d\n", i, *pDst16, *pSrc16));
+        ++pSrc16;
+        ++pDst16;
+    }
+    AudioMixBufReset(&child);
+
+    /* 2) Half volume/-6dB attenuation (16 steps down). */
+    vol.uLeft = vol.uRight = 255 - 16;
+    AudioMixBufSetVolume(&child, &vol);
+
+    RTTESTI_CHECK_RC_OK(AudioMixBufWriteAt(&child, 0, &samples, sizeof(samples), &written));
+    RTTESTI_CHECK_MSG(written == cSamplesChild, ("Child: Expected %RU32 written samples, got %RU32\n", cSamplesChild, written));
+    RTTESTI_CHECK_RC_OK(AudioMixBufMixToParent(&child, written, &mixed));
+
+    cSamplesRead = 0;
+    for (;;)
+    {
+        RTTESTI_CHECK_RC_OK_BREAK(AudioMixBufReadCirc(&parent, achBuf, cbBuf, &read));
+        if (!read)
+            break;
+        cSamplesRead += read;
+        AudioMixBufFinish(&parent, read);
+    }
+    RTTESTI_CHECK_MSG(cSamplesRead == cSamplesParent, ("Parent: Expected %RU32 mixed samples, got %RU32\n", cSamplesParent, cSamplesRead));
+
+    /* Check that at -6dB the sample values are halved. */
+    pSrc16 = &samples[0];
+    pDst16 = (int16_t *)achBuf;
+
+    for (i = 0; i < cSamplesParent * 2 /* stereo */; ++i)
+    {
+        /* Watch out! For negative values, x >> 1 is not the same as x / 2. */
+        RTTESTI_CHECK_MSG(*pSrc16 >> 1 == *pDst16, ("index %u: Dst=%d, Src=%d\n", i, *pDst16, *pSrc16));
+        ++pSrc16;
+        ++pDst16;
+    }
+
+    AudioMixBufDestroy(&parent);
+    AudioMixBufDestroy(&child);
+
+    return RTTestSubErrorCount(hTest) ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
+}
+
+int main(int argc, char **argv)
+{
+    RTR3InitExe(argc, &argv, 0);
+
+    /*
+     * Initialize IPRT and create the test.
+     */
+    RTTEST hTest;
+    int rc = RTTestInitAndCreate("tstAudioMixBuffer", &hTest);
+    if (rc)
+        return rc;
+    RTTestBanner(hTest);
+
+    rc = tstSingle(hTest);
+    if (RT_SUCCESS(rc))
+        rc = tstParentChild(hTest);
+    if (RT_SUCCESS(rc))
+        rc = tstConversion8(hTest);
+    if (RT_SUCCESS(rc))
+        rc = tstConversion16(hTest);
+    if (RT_SUCCESS(rc))
+        rc = tstVolume(hTest);
+
+    /*
+     * Summary
+     */
+    return RTTestSummaryAndDestroy(hTest);
+}
Index: /trunk/src/VBox/Devices/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Devices/Makefile.kmk	(revision 61412)
+++ /trunk/src/VBox/Devices/Makefile.kmk	(revision 61413)
@@ -154,8 +154,4 @@
  	VMMDev/VMMDevTesting.cpp \
  	Network/DevPCNet.cpp \
- 	Audio/DevIchAc97.cpp \
- 	Audio/DevSB16.cpp \
- 	Audio/DevIchHda.cpp \
- 	Audio/DevIchHdaCodec.cpp \
  	PC/DevDMA.cpp \
  	PC/DevHPET.cpp \
@@ -544,24 +540,30 @@
  # --- Audio bits. ---
 
- if 0
+ if 0 # Not stable yet.
   VBoxDD_DEFS += VBOX_WITH_HDA_INTERLEAVING_STREAMS_SUPPORT
   VBoxDD_DEFS += VBOX_WITH_HDA_51_SURROUND
  endif
 
+ VBoxDD_DEFS  += $(if $(VBOX_WITH_AUDIO_STABLE),VBOX_WITH_AUDIO_STABLE,)
+
  VBoxDD_SOURCES         += \
-   Audio/AudioMixBuffer.cpp \
-   Audio/AudioMixer.cpp \
-   Audio/DrvAudio.cpp \
-   Audio/DrvAudioCommon.cpp \
-   Audio/DrvHostNullAudio.cpp
+   $(VBOX_AUDIO_PATH_SOURCES)/DevIchAc97.cpp \
+   $(VBOX_AUDIO_PATH_SOURCES)/DevSB16.cpp \
+   $(VBOX_AUDIO_PATH_SOURCES)/DevIchHda.cpp \
+   $(VBOX_AUDIO_PATH_SOURCES)/DevIchHdaCodec.cpp \
+   $(VBOX_AUDIO_PATH_SOURCES)/AudioMixBuffer.cpp \
+   $(VBOX_AUDIO_PATH_SOURCES)/AudioMixer.cpp \
+   $(VBOX_AUDIO_PATH_SOURCES)/DrvAudio.cpp \
+   $(VBOX_AUDIO_PATH_SOURCES)/DrvAudioCommon.cpp \
+   $(VBOX_AUDIO_PATH_SOURCES)/DrvHostNullAudio.cpp
 
  ifeq ($(KBUILD_TARGET),darwin)
   VBoxDD_SOURCES += \
-   Audio/DrvHostCoreAudio.cpp
+   $(VBOX_AUDIO_PATH_SOURCES)/DrvHostCoreAudio.cpp
  endif
 
  ifeq ($(KBUILD_TARGET),win)
   VBoxDD_SOURCES += \
-   Audio/DrvHostDSound.cpp
+   $(VBOX_AUDIO_PATH_SOURCES)/DrvHostDSound.cpp
  endif
 
@@ -570,5 +572,5 @@
    VBoxDD_DEFS    += VBOX_WITH_OSS
    VBoxDD_SOURCES += \
-        Audio/DrvHostOSSAudio.cpp
+        $(VBOX_AUDIO_PATH_SOURCES)/DrvHostOSSAudio.cpp
   endif
 
@@ -576,6 +578,6 @@
    VBoxDD_DEFS    += VBOX_WITH_PULSE
    VBoxDD_SOURCES += \
-   Audio/DrvHostPulseAudio.cpp \
-   Audio/pulse_stubs.c
+   $(VBOX_AUDIO_PATH_SOURCES)/DrvHostPulseAudio.cpp \
+   $(VBOX_AUDIO_PATH_SOURCES)/pulse_stubs.c
   endif
 
@@ -583,6 +585,6 @@
    VBoxDD_DEFS    += VBOX_WITH_ALSA
    VBoxDD_SOURCES += \
-   	Audio/DrvHostALSAAudio.cpp \
-   	Audio/alsa_stubs.c
+   	$(VBOX_AUDIO_PATH_SOURCES)/DrvHostALSAAudio.cpp \
+   	$(VBOX_AUDIO_PATH_SOURCES)/alsa_stubs.c
   endif
  endif
@@ -592,11 +594,11 @@
    VBoxDD_DEFS    += VBOX_WITH_OSS
    VBoxDD_SOURCES += \
-    	Audio/DrvHostOSSAudio.cpp
+    	$(VBOX_AUDIO_PATH_SOURCES)/DrvHostOSSAudio.cpp
   endif
   ifdef VBOX_WITH_PULSE
    VBoxDD_DEFS    += VBOX_WITH_PULSE
    VBoxDD_SOURCES += \
-   Audio/DrvHostPulseAudio.cpp \
-   Audio/pulse_stubs.c
+   $(VBOX_AUDIO_PATH_SOURCES)/DrvHostPulseAudio.cpp \
+   $(VBOX_AUDIO_PATH_SOURCES)/pulse_stubs.c
   endif
  endif
@@ -604,5 +606,5 @@
  ifeq ($(KBUILD_TARGET),solaris)
   ifdef VBOX_WITH_OSS
-   VBoxDD_SOURCES += Audio/DrvHostOSSAudio.cpp
+   VBoxDD_SOURCES += $(VBOX_AUDIO_PATH_SOURCES)/DrvHostOSSAudio.cpp
    VBoxDD_DEFS    += VBOX_WITH_OSS
   endif
Index: /trunk/src/VBox/Main/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Main/Makefile.kmk	(revision 61412)
+++ /trunk/src/VBox/Main/Makefile.kmk	(revision 61413)
@@ -777,8 +777,14 @@
 	src-client/VMMDevInterface.cpp \
 	$(VBOX_AUTOGEN_EVENT_CPP) \
-	$(VBOX_XML_SCHEMADEFS_CPP) \
-	../Devices/Audio/AudioMixBuffer.cpp \
-	../Devices/Audio/DrvAudioCommon.cpp \
-    $(if $(VBOX_WITH_VRDE_AUDIO),src-client/DrvAudioVRDE.cpp,)
+	$(VBOX_XML_SCHEMADEFS_CPP)
+
+# Audio bits.
+VBoxC_DEFS    += \
+    $(if $(VBOX_WITH_AUDIO_STABLE), VBOX_WITH_AUDIO_STABLE,)
+
+VBoxC_SOURCES += \
+	../Devices/$(VBOX_AUDIO_PATH_SOURCES)/AudioMixBuffer.cpp \
+	../Devices/$(VBOX_AUDIO_PATH_SOURCES)/DrvAudioCommon.cpp \
+    $(if $(VBOX_WITH_VRDE_AUDIO),src-client/DrvAudioVRDE$(VBOX_AUDIO_FILE_SUFFIX).cpp,)
 
 VBoxC_SOURCES.win = \
Index: /trunk/src/VBox/Main/include/DrvAudioVRDE_old.h
===================================================================
--- /trunk/src/VBox/Main/include/DrvAudioVRDE_old.h	(revision 61413)
+++ /trunk/src/VBox/Main/include/DrvAudioVRDE_old.h	(revision 61413)
@@ -0,0 +1,64 @@
+/* $Id$ */
+/** @file
+ * VirtualBox driver interface to VRDE backend.
+ */
+
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#ifndef ____H_DRVAUDIOVRDE
+#define ____H_DRVAUDIOVRDE
+
+#include <VBox/com/ptr.h>
+#include <VBox/RemoteDesktop/VRDE.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmifs.h>
+
+class Console;
+
+class AudioVRDE
+{
+
+public:
+
+    AudioVRDE(Console *pConsole);
+    virtual ~AudioVRDE(void);
+
+public:
+
+    static const PDMDRVREG DrvReg;
+
+    Console *getParent(void) { return mParent; }
+
+public:
+
+    int onVRDEControl(bool fEnable, uint32_t uFlags);
+    int onVRDEInputBegin(void *pvContext, PVRDEAUDIOINBEGIN pVRDEAudioBegin);
+    int onVRDEInputData(void *pvContext, const void *pvData, uint32_t cbData);
+    int onVRDEInputEnd(void *pvContext);
+    int onVRDEInputIntercept(bool fIntercept);
+
+public:
+
+    static DECLCALLBACK(int) drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags);
+    static DECLCALLBACK(void) drvDestruct(PPDMDRVINS pDrvIns);
+
+private:
+
+    /** Pointer to the associated VRDE audio driver. */
+    struct DRVAUDIOVRDE *mpDrv;
+    /** Pointer to parent. */
+    Console * const mParent;
+};
+
+#endif /* !____H_DRVAUDIOVRDE */
+
Index: /trunk/src/VBox/Main/include/DrvAudioVideoRec_old.h
===================================================================
--- /trunk/src/VBox/Main/include/DrvAudioVideoRec_old.h	(revision 61413)
+++ /trunk/src/VBox/Main/include/DrvAudioVideoRec_old.h	(revision 61413)
@@ -0,0 +1,62 @@
+/* $Id$ */
+/** @file
+ * VirtualBox driver interface to video recording backend.
+ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ____H_DRVAUDIOVIDEOREC
+#define ____H_DRVAUDIOVIDEOREC
+
+#include <VBox/com/ptr.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmifs.h>
+
+class Console;
+
+class AudioVideoRec
+{
+
+public:
+
+    AudioVideoRec(Console *pConsole);
+    virtual ~AudioVideoRec(void);
+
+public:
+
+    static const PDMDRVREG DrvReg;
+
+    Console *getParent(void) { return mParent; }
+
+public:
+
+    int handleVideoRecSvrCmdAudioInputIntercept(bool fIntercept);
+    int handleVideoRecSvrCmdAudioInputEventBegin(void *pvContext, int iSampleHz, int cChannels, int cBits, bool fUnsigned);
+    int handleVideoRecSvrCmdAudioInputEventData(void *pvContext, const void *pvData, uint32_t cbData);
+    int handleVideoRecSvrCmdAudioInputEventEnd(void *pvContext);
+
+public:
+
+    static DECLCALLBACK(int) drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags);
+    static DECLCALLBACK(void) drvDestruct(PPDMDRVINS pDrvIns);
+
+private:
+
+    /** Pointer to the associated video recording audio driver. */
+    struct DRVAUDIOVIDEOREC *mpDrv;
+    /** Pointer to parent. */
+    Console * const mParent;
+};
+
+#endif /* !____H_DRVAUDIOVIDEOREC */
+
Index: /trunk/src/VBox/Main/src-client/DrvAudioVRDE_old.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/DrvAudioVRDE_old.cpp	(revision 61413)
+++ /trunk/src/VBox/Main/src-client/DrvAudioVRDE_old.cpp	(revision 61413)
@@ -0,0 +1,613 @@
+/* $Id$ */
+/** @file
+ * VRDE audio backend for Main.
+ */
+
+/*
+ * Copyright (C) 2013-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                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_VRDE_AUDIO
+#include <VBox/log.h>
+#include "DrvAudioVRDE_old.h"
+#include "ConsoleImpl.h"
+#include "ConsoleVRDPServer.h"
+
+#include "Logging.h"
+
+#include "../../Devices/Audio_old/DrvAudio.h"
+#include "../../Devices/Audio_old/AudioMixBuffer.h"
+
+#include <iprt/mem.h>
+#include <iprt/cdefs.h>
+#include <iprt/circbuf.h>
+
+#include <VBox/vmm/pdmaudioifs.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/RemoteDesktop/VRDE.h>
+#include <VBox/vmm/cfgm.h>
+#include <VBox/err.h>
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+/**
+ * Audio VRDE driver instance data.
+ */
+typedef struct DRVAUDIOVRDE
+{
+    /** Pointer to audio VRDE object. */
+    AudioVRDE           *pAudioVRDE;
+    PPDMDRVINS           pDrvIns;
+    /** Pointer to the driver instance structure. */
+    PDMIHOSTAUDIO        IHostAudio;
+    /** Pointer to the VRDP's console object. */
+    ConsoleVRDPServer   *pConsoleVRDPServer;
+    /** Pointer to the DrvAudio port interface that is above us. */
+    PPDMIAUDIOCONNECTOR  pDrvAudio;
+    /** Whether this driver is enabled or not. */
+    bool                 fEnabled;
+} DRVAUDIOVRDE, *PDRVAUDIOVRDE;
+
+typedef struct VRDESTREAMIN
+{
+    /** Associated host input stream. */
+    PDMAUDIOHSTSTRMIN    HstStrmIn;
+    /** Number of samples captured asynchronously in the
+     *  onVRDEInputXXX callbacks. */
+    uint32_t             cSamplesCaptured;
+    /** Critical section. */
+    RTCRITSECT           CritSect;
+} VRDESTREAMIN, *PVRDESTREAMIN;
+
+typedef struct VRDESTREAMOUT
+{
+    /** Associated host output stream. */
+    PDMAUDIOHSTSTRMOUT HstStrmOut;
+    uint64_t old_ticks;
+    uint64_t cSamplesSentPerSec;
+} VRDESTREAMOUT, *PVRDESTREAMOUT;
+
+
+
+static DECLCALLBACK(int) drvAudioVRDEInit(PPDMIHOSTAUDIO pInterface)
+{
+    LogFlowFuncEnter();
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvAudioVRDEInitIn(PPDMIHOSTAUDIO pInterface,
+                                            PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
+                                            PDMAUDIORECSOURCE enmRecSource,
+                                            uint32_t *pcSamples)
+{
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    AssertPtrReturn(pDrv, VERR_INVALID_POINTER);
+
+    PVRDESTREAMIN pVRDEStrmIn = (PVRDESTREAMIN)pHstStrmIn;
+    AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
+
+    if (pcSamples)
+        *pcSamples = _4K; /** @todo Make this configurable. */
+
+    return DrvAudioStreamCfgToProps(pCfg, &pVRDEStrmIn->HstStrmIn.Props);
+}
+
+static DECLCALLBACK(int) drvAudioVRDEInitOut(PPDMIHOSTAUDIO pInterface,
+                                             PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
+                                             uint32_t *pcSamples)
+{
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    AssertPtrReturn(pDrv, VERR_INVALID_POINTER);
+
+    LogFlowFunc(("pHstStrmOut=%p, pCfg=%p\n", pHstStrmOut, pCfg));
+
+    PVRDESTREAMOUT pVRDEStrmOut = (PVRDESTREAMOUT)pHstStrmOut;
+    AssertPtrReturn(pVRDEStrmOut, VERR_INVALID_POINTER);
+
+    if (pcSamples)
+        *pcSamples = _4K; /** @todo Make this configurable. */
+
+    return DrvAudioStreamCfgToProps(pCfg, &pVRDEStrmOut->HstStrmOut.Props);
+}
+
+static DECLCALLBACK(bool) drvAudioVRDEIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
+{
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    AssertPtrReturn(pDrv, false);
+
+    NOREF(enmDir);
+
+    if (!pDrv->fEnabled)
+        return false;
+
+    return true;
+}
+
+/**
+ * {FIXME - Missing brief description - FIXME}
+ *
+ * Transfers audio input formerly sent by a connected RDP client / VRDE backend
+ * (using the onVRDEInputXXX methods) over to the VRDE host (VM). The audio device
+ * emulation then will read and send the data to the guest.
+ *
+ * @return  IPRT status code.
+ * @param   pInterface
+ * @param   pHstStrmIn
+ * @param   pcSamplesCaptured
+ */
+static DECLCALLBACK(int) drvAudioVRDECaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                               uint32_t *pcSamplesCaptured)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+    AssertPtrReturn(pcSamplesCaptured, VERR_INVALID_POINTER);
+
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    AssertPtrReturn(pDrv, VERR_INVALID_POINTER);
+
+    PVRDESTREAMIN pVRDEStrmIn = (PVRDESTREAMIN)pHstStrmIn;
+    AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
+
+    /** @todo Use CritSect! */
+
+    int rc;
+
+    uint32_t cProcessed = 0;
+    if (pVRDEStrmIn->cSamplesCaptured)
+    {
+        rc = AudioMixBufMixToParent(&pVRDEStrmIn->HstStrmIn.MixBuf, pVRDEStrmIn->cSamplesCaptured,
+                                    &cProcessed);
+    }
+    else
+        rc = VINF_SUCCESS;
+
+    if (RT_SUCCESS(rc))
+    {
+        *pcSamplesCaptured = cProcessed;
+
+        Assert(pVRDEStrmIn->cSamplesCaptured >= cProcessed);
+        pVRDEStrmIn->cSamplesCaptured -= cProcessed;
+    }
+
+    LogFlowFunc(("cSamplesCaptured=%RU32, cProcessed=%RU32 rc=%Rrc\n", pVRDEStrmIn->cSamplesCaptured, cProcessed, rc));
+    return rc;
+}
+
+/**
+ * Transfers VM audio output to remote client.
+ *
+ * Transfers VM audio output over to the VRDE instance for playing remotely
+ * on the client.
+ *
+ * @return  IPRT status code.
+ * @param   pInterface
+ * @param   pHstStrmOut
+ * @param   pcSamplesPlayed
+ */
+static DECLCALLBACK(int) drvAudioVRDEPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                             uint32_t *pcSamplesPlayed)
+{
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+    AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
+    /* pcSamplesPlayed is optional. */
+
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    AssertPtrReturn(pDrv, VERR_INVALID_POINTER);
+
+    PVRDESTREAMOUT pVRDEStrmOut = (PVRDESTREAMOUT)pHstStrmOut;
+    AssertPtrReturn(pVRDEStrmOut, VERR_INVALID_POINTER);
+
+    uint32_t live = AudioMixBufAvail(&pHstStrmOut->MixBuf);
+    uint64_t now = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
+    uint64_t ticks = now  - pVRDEStrmOut->old_ticks;
+    uint64_t ticks_per_second = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
+
+    /* Minimize the rounding error: samples = int((ticks * freq) / ticks_per_second + 0.5). */
+    uint32_t cSamplesPlayed = (int)((2 * ticks * pHstStrmOut->Props.uHz + ticks_per_second) / ticks_per_second / 2);
+
+    /* Don't play more than available. */
+    if (cSamplesPlayed > live)
+        cSamplesPlayed = live;
+
+    /* Remember when samples were consumed. */
+    pVRDEStrmOut->old_ticks = now;
+
+    VRDEAUDIOFORMAT format = VRDE_AUDIO_FMT_MAKE(pHstStrmOut->Props.uHz,
+                                                 pHstStrmOut->Props.cChannels,
+                                                 pHstStrmOut->Props.cBits,
+                                                 pHstStrmOut->Props.fSigned);
+
+    int cSamplesToSend = cSamplesPlayed;
+
+    LogFlowFunc(("uFreq=%RU32, cChan=%RU8, cBits=%RU8, fSigned=%RTbool, enmFormat=%ld, cSamplesToSend=%RU32\n",
+                 pHstStrmOut->Props.uHz, pHstStrmOut->Props.cChannels,
+                 pHstStrmOut->Props.cBits, pHstStrmOut->Props.fSigned,
+                 format, cSamplesToSend));
+
+    /*
+     * Call the VRDP server with the data.
+     */
+    uint32_t cReadTotal = 0;
+
+    PPDMAUDIOSAMPLE pSamples;
+    uint32_t cRead;
+    int rc = AudioMixBufAcquire(&pHstStrmOut->MixBuf, cSamplesToSend,
+                                &pSamples, &cRead);
+    if (   RT_SUCCESS(rc)
+        && cRead)
+    {
+        cReadTotal = cRead;
+        pDrv->pConsoleVRDPServer->SendAudioSamples(pSamples, cRead, format);
+
+        if (rc == VINF_TRY_AGAIN)
+        {
+            rc = AudioMixBufAcquire(&pHstStrmOut->MixBuf, cSamplesToSend - cRead,
+                                    &pSamples, &cRead);
+            if (RT_SUCCESS(rc))
+                pDrv->pConsoleVRDPServer->SendAudioSamples(pSamples, cRead, format);
+
+            cReadTotal += cRead;
+        }
+    }
+
+    AudioMixBufFinish(&pHstStrmOut->MixBuf, cSamplesToSend);
+
+    /*
+     * Always report back all samples acquired, regardless of whether the
+     * VRDP server actually did process those.
+     */
+    if (pcSamplesPlayed)
+        *pcSamplesPlayed = cReadTotal;
+
+    LogFlowFunc(("cReadTotal=%RU32, rc=%Rrc\n", cReadTotal, rc));
+    return rc;
+}
+
+static DECLCALLBACK(int) drvAudioVRDEFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
+{
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    AssertPtrReturn(pDrv, VERR_INVALID_POINTER);
+
+    if (pDrv->pConsoleVRDPServer)
+        pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvAudioVRDEFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
+{
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    AssertPtrReturn(pDrv, VERR_INVALID_POINTER);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvAudioVRDEControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
+                                                PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    AssertPtrReturn(pDrv, VERR_INVALID_POINTER);
+
+    PVRDESTREAMOUT pVRDEStrmOut = (PVRDESTREAMOUT)pHstStrmOut;
+    AssertPtrReturn(pVRDEStrmOut, VERR_INVALID_POINTER);
+
+    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
+
+    AudioMixBufReset(&pHstStrmOut->MixBuf);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvAudioVRDEControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
+                                               PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    AssertPtrReturn(pDrv, VERR_INVALID_POINTER);
+
+    PVRDESTREAMIN pVRDEStrmIn = (PVRDESTREAMIN)pHstStrmIn;
+    AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
+
+    PPDMAUDIOHSTSTRMIN pThisStrmIn = &pVRDEStrmIn->HstStrmIn;
+
+    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
+
+    if (!pDrv->pConsoleVRDPServer)
+        return VINF_SUCCESS;
+
+    AudioMixBufReset(&pThisStrmIn->MixBuf);
+
+    /* Initialize only if not already done. */
+    int rc;
+    if (enmStreamCmd == PDMAUDIOSTREAMCMD_ENABLE)
+    {
+        rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pVRDEStrmIn, AudioMixBufSize(&pThisStrmIn->MixBuf),
+                                                           pThisStrmIn->Props.uHz,
+                                                           pThisStrmIn->Props.cChannels, pThisStrmIn->Props.cBits);
+        if (rc == VERR_NOT_SUPPORTED)
+        {
+            LogFlowFunc(("No RDP client connected, so no input recording supported\n"));
+            rc = VINF_SUCCESS;
+        }
+    }
+    else if (enmStreamCmd == PDMAUDIOSTREAMCMD_DISABLE)
+    {
+        pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL /* pvUserCtx */);
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_INVALID_PARAMETER;
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvAudioVRDEGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
+{
+    pCfg->cbStreamOut     = sizeof(VRDESTREAMOUT);
+    pCfg->cbStreamIn      = sizeof(VRDESTREAMIN);
+    pCfg->cMaxHstStrmsOut = 1;
+    pCfg->cMaxHstStrmsIn  = 2; /* Microphone in + Line in. */
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(void) drvAudioVRDEShutdown(PPDMIHOSTAUDIO pInterface)
+{
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    AssertPtrReturnVoid(pDrv);
+
+    if (pDrv->pConsoleVRDPServer)
+        pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
+}
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvAudioVRDEQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
+
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
+    return NULL;
+}
+
+AudioVRDE::AudioVRDE(Console *pConsole)
+    : mpDrv(NULL),
+      mParent(pConsole)
+{
+}
+
+AudioVRDE::~AudioVRDE(void)
+{
+    if (mpDrv)
+    {
+        mpDrv->pAudioVRDE = NULL;
+        mpDrv = NULL;
+    }
+}
+
+int AudioVRDE::onVRDEControl(bool fEnable, uint32_t uFlags)
+{
+    LogFlowThisFunc(("fEnable=%RTbool, uFlags=0x%x\n", fEnable, uFlags));
+
+    if (mpDrv == NULL)
+        return VERR_INVALID_STATE;
+
+    mpDrv->fEnabled = fEnable;
+
+    return VINF_SUCCESS; /* Never veto. */
+}
+
+/**
+ * Marks the beginning of sending captured audio data from a connected
+ * RDP client.
+ *
+ * @return  IPRT status code.
+ * @param   pvContext               The context; in this case a pointer to a
+ *                                  VRDESTREAMIN structure.
+ * @param   pVRDEAudioBegin         Pointer to a VRDEAUDIOINBEGIN structure.
+ */
+int AudioVRDE::onVRDEInputBegin(void *pvContext, PVRDEAUDIOINBEGIN pVRDEAudioBegin)
+{
+    AssertPtrReturn(pvContext, VERR_INVALID_POINTER);
+    AssertPtrReturn(pVRDEAudioBegin, VERR_INVALID_POINTER);
+
+    PVRDESTREAMIN pVRDEStrmIn = (PVRDESTREAMIN)pvContext;
+    AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
+
+    VRDEAUDIOFORMAT audioFmt = pVRDEAudioBegin->fmt;
+
+    int iSampleHz  = VRDE_AUDIO_FMT_SAMPLE_FREQ(audioFmt);
+    int cChannels  = VRDE_AUDIO_FMT_CHANNELS(audioFmt);
+    int cBits      = VRDE_AUDIO_FMT_BITS_PER_SAMPLE(audioFmt);
+    bool fUnsigned = VRDE_AUDIO_FMT_SIGNED(audioFmt);
+
+    LogFlowFunc(("cbSample=%RU32, iSampleHz=%d, cChannels=%d, cBits=%d, fUnsigned=%RTbool\n",
+                 VRDE_AUDIO_FMT_BYTES_PER_SAMPLE(audioFmt), iSampleHz, cChannels, cBits, fUnsigned));
+
+    return VINF_SUCCESS;
+}
+
+int AudioVRDE::onVRDEInputData(void *pvContext, const void *pvData, uint32_t cbData)
+{
+    PVRDESTREAMIN pVRDEStrmIn = (PVRDESTREAMIN)pvContext;
+    AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
+
+    PPDMAUDIOHSTSTRMIN pHstStrmIn = &pVRDEStrmIn->HstStrmIn;
+    AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
+
+    /** @todo Use CritSect! */
+
+    uint32_t cWritten;
+    int rc = AudioMixBufWriteCirc(&pHstStrmIn->MixBuf, pvData, cbData, &cWritten);
+    if (RT_SUCCESS(rc))
+        pVRDEStrmIn->cSamplesCaptured += cWritten;
+
+    LogFlowFunc(("cbData=%RU32, cWritten=%RU32, cSamplesCaptured=%RU32, rc=%Rrc\n",
+                 cbData, cWritten, pVRDEStrmIn->cSamplesCaptured, rc));
+    return rc;
+}
+
+int AudioVRDE::onVRDEInputEnd(void *pvContext)
+{
+    NOREF(pvContext);
+
+    return VINF_SUCCESS;
+}
+
+int AudioVRDE::onVRDEInputIntercept(bool fEnabled)
+{
+    return VINF_SUCCESS; /* Never veto. */
+}
+
+/**
+ * Construct a VRDE audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+/* static */
+DECLCALLBACK(int) AudioVRDE::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+    PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+    PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
+    AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+    LogRel(("Audio: Initializing VRDE driver\n"));
+    LogFlowFunc(("fFlags=0x%x\n", fFlags));
+
+    AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
+                    ("Configuration error: Not possible to attach anything to this driver!\n"),
+                    VERR_PDM_DRVINS_NO_ATTACH);
+
+    /*
+     * Init the static parts.
+     */
+    pThis->pDrvIns                   = pDrvIns;
+    /* IBase */
+    pDrvIns->IBase.pfnQueryInterface = drvAudioVRDEQueryInterface;
+    /* IHostAudio */
+    PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvAudioVRDE);
+
+    /* Init defaults. */
+    pThis->fEnabled = false;
+
+    /*
+     * Get the ConsoleVRDPServer object pointer.
+     */
+    void *pvUser;
+    int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser);
+    AssertMsgRCReturn(rc, ("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc), rc);
+
+    /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVRDE. */
+    pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
+
+    /*
+     * Get the AudioVRDE object pointer.
+     */
+    pvUser = NULL;
+    rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser);
+    AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc);
+
+    pThis->pAudioVRDE = (AudioVRDE *)pvUser;
+    pThis->pAudioVRDE->mpDrv = pThis;
+
+    /*
+     * Get the interface for the above driver (DrvAudio) to make mixer/conversion calls.
+     * Described in CFGM tree.
+     */
+    pThis->pDrvAudio = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
+    AssertMsgReturn(pThis->pDrvAudio, ("Configuration error: No upper interface specified!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnDestruct}
+ */
+/* static */
+DECLCALLBACK(void) AudioVRDE::drvDestruct(PPDMDRVINS pDrvIns)
+{
+    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+    PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
+    LogFlowFuncEnter();
+
+    /*
+     * If the AudioVRDE object is still alive, we must clear it's reference to
+     * us since we'll be invalid when we return from this method.
+     */
+    if (pThis->pAudioVRDE)
+    {
+        pThis->pAudioVRDE->mpDrv = NULL;
+        pThis->pAudioVRDE = NULL;
+    }
+}
+
+
+/**
+ * VRDE audio driver registration record.
+ */
+const PDMDRVREG AudioVRDE::DrvReg =
+{
+    PDM_DRVREG_VERSION,
+    /* szName */
+    "AudioVRDE",
+    /* szRCMod */
+    "",
+    /* szR0Mod */
+    "",
+    /* pszDescription */
+    "Audio driver for VRDE backend",
+    /* fFlags */
+    PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+    /* fClass. */
+    PDM_DRVREG_CLASS_AUDIO,
+    /* cMaxInstances */
+    ~0U,
+    /* cbInstance */
+    sizeof(DRVAUDIOVRDE),
+    /* pfnConstruct */
+    AudioVRDE::drvConstruct,
+    /* pfnDestruct */
+    AudioVRDE::drvDestruct,
+    /* 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/Main/src-client/DrvAudioVideoRec_old.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/DrvAudioVideoRec_old.cpp	(revision 61413)
+++ /trunk/src/VBox/Main/src-client/DrvAudioVideoRec_old.cpp	(revision 61413)
@@ -0,0 +1,911 @@
+/* $Id$ */
+/** @file
+ * Video recording audio backend for Main.
+ */
+
+/*
+ * Copyright (C) 2014-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 "DrvAudioVideoRec_old.h"
+#include "ConsoleImpl.h"
+#include "ConsoleVRDPServer.h"
+
+#include "Logging.h"
+
+#include <iprt/mem.h>
+#include <iprt/cdefs.h>
+#include <iprt/circbuf.h>
+
+#include <VBox/vmm/pdmaudioifs.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/RemoteDesktop/VRDE.h>
+#include <VBox/vmm/cfgm.h>
+#include <VBox/err.h>
+
+#ifdef LOG_GROUP
+ #undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_DEV_AUDIO
+#include <VBox/log.h>
+
+/* 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 */
+
+//@todo move t_sample as a PDM interface
+//typedef struct { int mute;  uint32_t r; uint32_t l; } volume_t;
+
+#define INT_MAX         0x7fffffff
+volume_t videorec_nominal_volume = {
+    0,
+    INT_MAX,
+    INT_MAX
+};
+
+/* 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.)
+ * In case its need to be used. Currently its not used.
+ */
+#if 0
+static struct
+{
+    int         buffer_msecs_out;
+    int         buffer_msecs_in;
+} confAudioVideoRec
+=
+{
+    INIT_FIELD (.buffer_msecs_out = ) 100,
+    INIT_FIELD (.buffer_msecs_in  = ) 100,
+};
+#endif
+
+/**
+ * Audio video recording driver instance data.
+ *
+ * @extends PDMIAUDIOSNIFFERCONNECTOR
+ */
+typedef struct DRVAUDIOVIDEOREC
+{
+    /** Pointer to audio video recording object. */
+    AudioVideoRec      *pAudioVideoRec;
+    PPDMDRVINS          pDrvIns;
+    /** Pointer to the driver instance structure. */
+    PDMIHOSTAUDIO       IHostAudio;
+    ConsoleVRDPServer *pConsoleVRDPServer;
+    /** Pointer to the DrvAudio port interface that is above it. */
+    PPDMIAUDIOCONNECTOR       pUpPort;
+} DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC;
+typedef struct PDMAUDIOHSTSTRMOUT PDMAUDIOHSTSTRMOUT;
+typedef PDMAUDIOHSTSTRMOUT *PPDMAUDIOHSTSTRMOUT;
+
+typedef struct VIDEORECAUDIOIN
+{
+    /* Audio and audio details for recording */
+    PDMAUDIOHSTSTRMIN   pHostVoiceIn;
+    void * pvUserCtx;
+    /* Number of bytes per frame (bitsPerSample * channels) of the actual input format. */
+    uint32_t cBytesPerFrame;
+    /* Frequency of the actual audio format. */
+    uint32_t uFrequency;
+    /* If the actual format frequence differs from the requested format, this is not NULL. */
+    void *rate;
+    /* Temporary buffer for st_sample_t representation of the input audio data. */
+    void *pvSamplesBuffer;
+    /* buffer for bytes of samples (not rate converted) */
+    uint32_t cbSamplesBufferAllocated;
+    /* Temporary buffer for frequency conversion. */
+    void *pvRateBuffer;
+    /* buffer for bytes rate converted samples */
+    uint32_t cbRateBufferAllocated;
+    /* A ring buffer for transferring data to the playback thread */
+    PRTCIRCBUF pRecordedVoiceBuf;
+    t_sample * convAudioDevFmtToStSampl;
+    uint32_t fIsInit;
+    uint32_t status;
+} VIDEORECAUDIOIN, *PVIDEORECAUDIOIN;
+
+typedef struct VIDEORECAUDIOOUT
+{
+    PDMAUDIOHSTSTRMOUT pHostVoiceOut;
+    uint64_t old_ticks;
+    uint64_t cSamplesSentPerSec;
+} VIDEORECAUDIOOUT, *PVIDEORECAUDIOOUT;
+
+static DECLCALLBACK(int) drvAudioVideoRecInit(PPDMIHOSTAUDIO pInterface)
+{
+    LogFlowFuncEnter();
+
+    return VINF_SUCCESS;
+}
+
+/** @todo Replace this with drvAudioHlpPcmPropsFromCfg(). */
+static int drvAudioVideoRecPcmInitInfo(PDMPCMPROPS * pProps, PPDMAUDIOSTREAMCFG as)
+{
+    int rc = VINF_SUCCESS;
+
+    uint8_t cBits = 8, cShift = 0;
+    bool fSigned = false;
+
+    switch (as->enmFormat)
+    {
+        case AUD_FMT_S8:
+            fSigned = 1;
+        case AUD_FMT_U8:
+            break;
+
+        case AUD_FMT_S16:
+            fSigned = 1;
+        case AUD_FMT_U16:
+            cBits = 16;
+            cShift = 1;
+            break;
+
+        case AUD_FMT_S32:
+            fSigned = 1;
+        case AUD_FMT_U32:
+            cBits = 32;
+            cShift = 2;
+            break;
+
+        default:
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+
+    pProps->uHz = as->uHz;
+    pProps->cBits = cBits;
+    pProps->fSigned = fSigned;
+    pProps->cChannels = as->cChannels;
+    pProps->cShift = (as->cChannels == 2) + cShift;
+    pProps->uAlign = (1 << pProps->cShift) - 1;
+    pProps->cbPerSec = pProps->uHz << pProps->cShift;
+    pProps->fSwapEndian = (as->enmEndianness != PDMAUDIOHOSTENDIANESS);
+
+    return rc;
+}
+
+/*
+ * Hard voice (playback)
+ */
+static int audio_pcm_hw_find_min_out (PPDMAUDIOHSTSTRMOUT hw, int *nb_livep)
+{
+    PPDMAUDIOGSTSTRMOUT sw;
+    PPDMAUDIOGSTSTRMOUT pIter;
+    int m = INT_MAX;
+    int nb_live = 0;
+
+    RTListForEach(&hw->lstGstStrmOut, pIter, PDMAUDIOGSTSTRMOUT, Node)
+    {
+        sw = pIter;
+        if (sw->State.fActive || !sw->State.fEmpty)
+        {
+            m = RT_MIN (m, sw->cTotalSamplesWritten);
+            nb_live += 1;
+        }
+    }
+
+    *nb_livep = nb_live;
+    return m;
+}
+
+static int audio_pcm_hw_get_live_out2 (PPDMAUDIOHSTSTRMOUT hw, int *nb_live)
+{
+    int smin;
+
+    smin = audio_pcm_hw_find_min_out (hw, nb_live);
+
+    if (!*nb_live) {
+        return 0;
+    }
+    else
+    {
+        int live = smin;
+
+        if (live < 0 || live > hw->cSamples)
+        {
+            LogFlowFunc(("Error: live=%d hw->samples=%d\n", live, hw->cSamples));
+            return 0;
+        }
+        return live;
+    }
+}
+
+
+static int audio_pcm_hw_get_live_out (PPDMAUDIOHSTSTRMOUT hw)
+{
+    int nb_live;
+    int live;
+
+    live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
+    if (live < 0 || live > hw->cSamples)
+    {
+        LogFlowFunc(("Error: live=%d hw->samples=%d\n", live, hw->cSamples));
+        return 0;
+    }
+    return live;
+}
+
+/*
+ * Hard voice (capture)
+ */
+static int audio_pcm_hw_find_min_in (PPDMAUDIOHSTSTRMIN hw)
+{
+    PPDMAUDIOGSTSTRMIN pIter;
+    int m = hw->cTotalSamplesCaptured;
+
+    RTListForEach(&hw->lstGstStreamsIn, pIter, PDMAUDIOGSTSTRMIN, Node)
+    {
+        if (pIter->State.fActive)
+        {
+            m = RT_MIN (m, pIter->cTotalHostSamplesRead);
+        }
+    }
+    return m;
+}
+
+int audio_pcm_hw_get_live_in (PPDMAUDIOHSTSTRMIN hw)
+{
+    int live = hw->cTotalSamplesCaptured - audio_pcm_hw_find_min_in (hw);
+    if (live < 0 || live > hw->cSamples)
+    {
+        LogFlowFunc(("Error: live=%d hw->samples=%d\n", live, hw->cSamples));
+        return 0;
+    }
+    return live;
+}
+
+static inline void *advance (void *p, int incr)
+{
+    uint8_t *d = (uint8_t*)p;
+    return (d + incr);
+}
+
+static int vrdeReallocSampleBuf(PVIDEORECAUDIOIN pVRDEVoice, uint32_t cSamples)
+{
+    uint32_t cbBuffer = cSamples * sizeof(PDMAUDIOSAMPLE);
+    if (cbBuffer > pVRDEVoice->cbSamplesBufferAllocated)
+    {
+        /** @todo r=andy Why not using RTMemReAlloc? */
+        if (pVRDEVoice->pvSamplesBuffer)
+        {
+            RTMemFree(pVRDEVoice->pvSamplesBuffer);
+            pVRDEVoice->pvSamplesBuffer = NULL;
+        }
+        pVRDEVoice->pvSamplesBuffer = RTMemAlloc(cbBuffer);
+        if (pVRDEVoice->pvSamplesBuffer)
+            pVRDEVoice->cbSamplesBufferAllocated = cbBuffer;
+        else
+            pVRDEVoice->cbSamplesBufferAllocated = 0;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static int vrdeReallocRateAdjSampleBuf(PVIDEORECAUDIOIN pVRDEVoice, uint32_t cSamples)
+{
+    uint32_t cbBuffer = cSamples * sizeof(PDMAUDIOSAMPLE);
+    if (cbBuffer > pVRDEVoice->cbRateBufferAllocated)
+    {
+        RTMemFree(pVRDEVoice->pvRateBuffer);
+        pVRDEVoice->pvRateBuffer = RTMemAlloc(cbBuffer);
+        if (pVRDEVoice->pvRateBuffer)
+            pVRDEVoice->cbRateBufferAllocated = cbBuffer;
+        else
+            pVRDEVoice->cbRateBufferAllocated = 0;
+    }
+
+    return VINF_SUCCESS;
+}
+
+/*******************************************************************************
+ *
+ * AudioVideoRec input section
+ *
+ ******************************************************************************/
+
+/*
+ * Callback to feed audio input buffer. Samples format is be the same as
+ * in the voice. The caller prepares st_sample_t.
+ *
+ * @param cbSamples Size of pvSamples array in bytes.
+ * @param pvSamples Points to an array of samples.
+ *
+ * @return IPRT status code.
+ */
+static int vrdeRecordingCallback(PVIDEORECAUDIOIN pVRDEVoice, uint32_t cbSamples, const void *pvSamples)
+{
+    int rc = VINF_SUCCESS;
+    size_t csWritten = 0;
+
+    Assert((cbSamples % sizeof(PDMAUDIOSAMPLE)) == 0);
+
+    if (!pVRDEVoice->fIsInit)
+        return VINF_SUCCESS;
+
+    /* If nothing is pending return immediately. */
+    if (cbSamples == 0)
+        return VINF_SUCCESS;
+
+    /* How much space is free in the ring buffer? */
+    size_t csAvail = RTCircBufFree(pVRDEVoice->pRecordedVoiceBuf) / sizeof(PDMAUDIOSAMPLE); /* bytes -> samples */
+
+    /* How much space is used in the audio buffer. Use the smaller size of the too. */
+    csAvail = RT_MIN(csAvail, cbSamples / sizeof(PDMAUDIOSAMPLE));
+
+    /* Iterate as long as data is available. */
+    while (csWritten < csAvail)
+    {
+        /* How much is left? */
+        size_t csToWrite = csAvail - csWritten;
+        size_t cbToWrite = csToWrite * sizeof(PDMAUDIOSAMPLE);
+
+        /* Try to acquire the necessary space from the ring buffer. */
+        void *pcDst;
+        RTCircBufAcquireWriteBlock(pVRDEVoice->pRecordedVoiceBuf, cbToWrite, &pcDst, &cbToWrite);
+
+        /* How much do we get? */
+        csToWrite = cbToWrite / sizeof(PDMAUDIOSAMPLE);
+
+        /* Copy the data from the audio buffer to the ring buffer in PVRDEVoice. */
+        if (csToWrite)
+        {
+            memcpy(pcDst, (uint8_t *)pvSamples + (csWritten * sizeof(PDMAUDIOSAMPLE)), cbToWrite);
+            csWritten += csToWrite;
+        }
+
+        /* Release the ring buffer, so the main thread could start reading this data. */
+        RTCircBufReleaseWriteBlock(pVRDEVoice->pRecordedVoiceBuf, cbToWrite);
+
+        if (RT_UNLIKELY(csToWrite == 0))
+            break;
+    }
+
+    LogFlowFunc(("Finished writing buffer with %RU32 samples (%RU32 bytes)\n",
+                 csWritten, csWritten * sizeof(PDMAUDIOSAMPLE)));
+
+    return rc;
+}
+
+static DECLCALLBACK(int) drvAudioVideoRecInitOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHostVoiceOut, PPDMAUDIOSTREAMCFG pCfg)
+{
+    LogFlowFuncEnter();
+    PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudio);
+
+    PVIDEORECAUDIOOUT pVRDEVoiceOut = (PVIDEORECAUDIOOUT)pHostVoiceOut;
+    pHostVoiceOut->cSamples = _4K; /* 4096 samples * 4 = 16K bytes total. */
+
+    return drvAudioVideoRecPcmInitInfo(&pVRDEVoiceOut->pHostVoiceOut.Props, pCfg);
+}
+
+static DECLCALLBACK(int) drvAudioVideoRecInitIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHostVoiceIn, PPDMAUDIOSTREAMCFG pCfg)
+{
+    LogFlowFuncEnter();
+    PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudio);
+
+    PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pHostVoiceIn;
+    pHostVoiceIn->cSamples = _4K; /* 4096 samples * 4 = 16K bytes total. */
+
+    return drvAudioVideoRecPcmInitInfo(&pVRDEVoice->pHostVoiceIn.Props, pCfg);
+}
+
+static DECLCALLBACK(int) drvAudioVideoRecCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHostVoiceIn,
+                                                   uint32_t *pcSamplesCaptured)
+{
+    /** @todo Take care of the size of the buffer allocated to pHostVoiceIn. */
+    PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pHostVoiceIn;
+
+    /* use this from DrvHostCoreAudio.c */
+    if (ASMAtomicReadU32(&pVRDEVoice->status) != CA_STATUS_INIT)
+    {
+        LogFlowFunc(("VRDE voice not initialized\n"));
+
+        *pcSamplesCaptured = 0;
+        return VERR_GENERAL_FAILURE; /** @todo Fudge! */
+    }
+
+    /* how much space is used in the ring buffer in pRecordedVocieBuf with pAudioVideoRec . Bytes-> samples*/
+    size_t cSamplesRingBuffer = RTCircBufUsed(pVRDEVoice->pRecordedVoiceBuf) / sizeof(PDMAUDIOSAMPLE);
+
+    /* How much space is available in the mix buffer. Use the smaller size of the too. */
+    cSamplesRingBuffer = RT_MIN(cSamplesRingBuffer, (uint32_t)(pVRDEVoice->pHostVoiceIn.cSamples -
+                                audio_pcm_hw_get_live_in (&pVRDEVoice->pHostVoiceIn)));
+
+    LogFlowFunc(("Start reading buffer with %d samples (%d bytes)\n", cSamplesRingBuffer,
+                 cSamplesRingBuffer * sizeof(PDMAUDIOSAMPLE)));
+
+    /* Iterate as long as data is available */
+    size_t cSamplesRead = 0;
+    while (cSamplesRead < cSamplesRingBuffer)
+    {
+        /* How much is left? Split request at the end of our samples buffer. */
+        size_t cSamplesToRead = RT_MIN(cSamplesRingBuffer - cSamplesRead,
+                                       (uint32_t)(pVRDEVoice->pHostVoiceIn.cSamples - pVRDEVoice->pHostVoiceIn.offSamplesWritten));
+        size_t cbToRead = cSamplesToRead * sizeof(PDMAUDIOSAMPLE);
+        LogFlowFunc(("Try reading %zu samples (%zu bytes)\n", cSamplesToRead, cbToRead));
+
+        /* Try to acquire the necessary block from the ring buffer. Remeber in fltRecrodCallback we
+         * we are filling this buffer with the audio data available from VRDP. Here we are reading it
+         */
+        /*todo do I need to introduce a thread to fill the buffer in fltRecordcallback. So that
+         * filling is in separate thread and the reading of that buffer is in separate thread
+         */
+        void *pvSrc;
+        RTCircBufAcquireReadBlock(pVRDEVoice->pRecordedVoiceBuf, cbToRead, &pvSrc, &cbToRead);
+
+        /* How much to we get? */
+        cSamplesToRead = cbToRead / sizeof(PDMAUDIOSAMPLE);
+        LogFlowFunc(("AuderVRDE: There are %d samples (%d bytes) available\n", cSamplesToRead, cbToRead));
+
+        /* Break if nothing is used anymore. */
+        if (cSamplesToRead)
+        {
+            /* Copy the data from our ring buffer to the mix buffer. */
+            PPDMAUDIOSAMPLE psDst = pVRDEVoice->pHostVoiceIn.paSamples + pVRDEVoice->pHostVoiceIn.offSamplesWritten;
+            memcpy(psDst, pvSrc, cbToRead);
+        }
+
+        /* Release the read buffer, so it could be used for new data. */
+        RTCircBufReleaseReadBlock(pVRDEVoice->pRecordedVoiceBuf, cbToRead);
+
+        if (!cSamplesToRead)
+            break;
+
+        pVRDEVoice->pHostVoiceIn.offSamplesWritten = (pVRDEVoice->pHostVoiceIn.offSamplesWritten + cSamplesToRead)
+                                              % pVRDEVoice->pHostVoiceIn.cSamples;
+
+        /* How much have we reads so far. */
+        cSamplesRead += cSamplesToRead;
+    }
+
+    LogFlowFunc(("Finished reading buffer with %zu samples (%zu bytes)\n",
+                 cSamplesRead, cSamplesRead * sizeof(PDMAUDIOSAMPLE)));
+
+    *pcSamplesCaptured = cSamplesRead;
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvAudioVideoRecPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHostVoiceOut,
+                                                 uint32_t *pcSamplesPlayed)
+{
+    PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudio);
+    PVIDEORECAUDIOOUT pVRDEVoiceOut = (PVIDEORECAUDIOOUT)pHostVoiceOut;
+
+    /*
+     * Just call the VRDP server with the data.
+     */
+    int live = audio_pcm_hw_get_live_out(pHostVoiceOut);
+    uint64_t now = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
+    uint64_t ticks = now  - pVRDEVoiceOut->old_ticks;
+    uint64_t ticks_per_second = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
+
+    int cSamplesPlayed = (int)((2 * ticks * pHostVoiceOut->Props.uHz + ticks_per_second) / ticks_per_second / 2);
+    if (cSamplesPlayed < 0)
+        cSamplesPlayed = live;
+
+    pHostVoiceOut->Props.cBits = 128; /** @todo Make this configurable (or at least a define)? */
+    VRDEAUDIOFORMAT format = VRDE_AUDIO_FMT_MAKE(pHostVoiceOut->Props.uHz,
+                                                 pHostVoiceOut->Props.cChannels,
+                                                 pHostVoiceOut->Props.cBits, /* bits per sample */
+                                                 !pHostVoiceOut->Props.fSigned);
+
+    LogFlowFunc(("freq=%d, chan=%d, cBits = %d, fsigned = %d, cSamples=%d format=%d\n",
+                 pHostVoiceOut->Props.uHz, pHostVoiceOut->Props.cChannels,
+                 pHostVoiceOut->Props.cBits, pHostVoiceOut->Props.fSigned,
+                 pHostVoiceOut->cSamples, format));
+
+    pVRDEVoiceOut->old_ticks = now;
+    int cSamplesToSend = RT_MIN(live, cSamplesPlayed);
+
+    if (pHostVoiceOut->cOffSamplesRead + cSamplesToSend > pHostVoiceOut->cSamples)
+    {
+        /* send the samples till the end of pHostStereoSampleBuf */
+        pDrv->pConsoleVRDPServer->SendAudioSamples(&pHostVoiceOut->paSamples[pHostVoiceOut->cOffSamplesRead],
+                                                   (pHostVoiceOut->cSamples - pHostVoiceOut->cOffSamplesRead), format);
+        /*pHostStereoSampleBuff already has the samples which exceeded its space. They have overwriten the old
+         * played sampled starting from offset 0. So based on the number of samples that we had to play,
+         * read the number of samples from offset 0 .
+         */
+        pDrv->pConsoleVRDPServer->SendAudioSamples(&pHostVoiceOut->paSamples[0],
+                                                   (cSamplesToSend - (pHostVoiceOut->cSamples -
+                                                                      pHostVoiceOut->cOffSamplesRead)),
+                                                   format);
+    }
+    else
+    {
+        pDrv->pConsoleVRDPServer->SendAudioSamples(&pHostVoiceOut->paSamples[pHostVoiceOut->cOffSamplesRead],
+                                                   cSamplesToSend, format);
+    }
+
+    pHostVoiceOut->cOffSamplesRead = (pHostVoiceOut->cOffSamplesRead + cSamplesToSend) % pHostVoiceOut->cSamples;
+
+    *pcSamplesPlayed = cSamplesToSend;
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvAudioVideoRecFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN hw)
+{
+    PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudio);
+    LogFlowFuncEnter();
+    pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvAudioVideoRecFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHostVoiceOut)
+{
+    PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudio);
+    LogFlowFuncEnter();
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvAudioVideoRecControlOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT hw,
+                                                    PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudio);
+    LogFlowFuncEnter();
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvAudioVideoRecControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHostVoiceIn,
+                                                   PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    PDRVAUDIOVIDEOREC pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVIDEOREC, IHostAudio);
+
+    /* Initialize  VRDEVoice and return to VRDP server which returns this struct back to us
+     * in the form void * pvContext
+     */
+    PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pHostVoiceIn;
+    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
+
+    /* initialize only if not already done */
+    if (enmStreamCmd == PDMAUDIOSTREAMCMD_ENABLE)
+    {
+        //@todo if (!pVRDEVoice->fIsInit)
+        //    RTCircBufReset(pVRDEVoice->pRecordedVoiceBuf);
+        pVRDEVoice->fIsInit = 1;
+        pVRDEVoice->pHostVoiceIn = *pHostVoiceIn;
+        pVRDEVoice->cBytesPerFrame = 1;
+        pVRDEVoice->uFrequency = 0;
+        pVRDEVoice->rate = NULL;
+        pVRDEVoice->cbSamplesBufferAllocated = 0;
+        pVRDEVoice->pvRateBuffer = NULL;
+        pVRDEVoice->cbRateBufferAllocated = 0;
+
+        pVRDEVoice->pHostVoiceIn.cSamples = 2048;
+        /* Initialize the hardware info section with the audio settings */
+
+        ASMAtomicWriteU32(&pVRDEVoice->status, CA_STATUS_IN_INIT);
+
+        /* Create the internal ring buffer. */
+        RTCircBufCreate(&pVRDEVoice->pRecordedVoiceBuf,
+                        pVRDEVoice->pHostVoiceIn.cSamples * sizeof(PDMAUDIOSAMPLE));
+
+        if (!RT_VALID_PTR(pVRDEVoice->pRecordedVoiceBuf))
+        {
+            LogRel(("Failed to create internal ring buffer\n"));
+            return  VERR_NO_MEMORY;
+        }
+
+        ASMAtomicWriteU32(&pVRDEVoice->status, CA_STATUS_INIT);
+        return pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pVRDEVoice, pHostVoiceIn->cSamples,
+                                                             pHostVoiceIn->Props.uHz,
+                                                             pHostVoiceIn->Props.cChannels, pHostVoiceIn->Props.cBits);
+    }
+    else if (enmStreamCmd == PDMAUDIOSTREAMCMD_DISABLE)
+    {
+        pVRDEVoice->fIsInit = 0;
+        ASMAtomicWriteU32(&pVRDEVoice->status, CA_STATUS_IN_UNINIT);
+        RTCircBufDestroy(pVRDEVoice->pRecordedVoiceBuf);
+        pVRDEVoice->pRecordedVoiceBuf = NULL;
+        ASMAtomicWriteU32(&pVRDEVoice->status, CA_STATUS_UNINIT);
+        pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
+    }
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) drvAudioVideoRecGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pAudioConf)
+{
+    LogFlowFunc(("pAudioConf=%p\n", pAudioConf));
+
+    pAudioConf->cbStreamOut = sizeof(VIDEORECAUDIOOUT);
+    pAudioConf->cbStreamIn = sizeof(VIDEORECAUDIOIN);
+    pAudioConf->cMaxHstStrmsOut = 1;
+    pAudioConf->cMaxHstStrmsIn = 1;
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(void) drvAudioVideoRecShutdown(PPDMIHOSTAUDIO pInterface)
+{
+    NOREF(pInterface);
+}
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvAudioVideoRecQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+    PDRVAUDIOVIDEOREC  pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
+    return NULL;
+}
+
+AudioVideoRec::AudioVideoRec(Console *pConsole)
+    : mpDrv(NULL),
+      mParent(pConsole)
+{
+}
+
+AudioVideoRec::~AudioVideoRec(void)
+{
+    if (mpDrv)
+    {
+        mpDrv->pAudioVideoRec = NULL;
+        mpDrv = NULL;
+    }
+}
+
+int AudioVideoRec::handleVideoRecSvrCmdAudioInputIntercept(bool fIntercept)
+{
+    LogFlowThisFunc(("fIntercept=%RTbool\n", fIntercept));
+    return VINF_SUCCESS;
+}
+
+int AudioVideoRec::handleVideoRecSvrCmdAudioInputEventBegin(void *pvContext, int iSampleHz, int cChannels,
+                                                            int cBits, bool fUnsigned)
+{
+    int bitIdx;
+    PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pvContext;
+    LogFlowFunc(("handleVRDPCmdInputEventBegin\n"));
+    /* Prepare a format convertion for the actually used format. */
+    pVRDEVoice->cBytesPerFrame = ((cBits + 7) / 8) * cChannels;
+    if (cBits == 16)
+    {
+        bitIdx = 1;
+    }
+    else if (cBits == 32)
+    {
+        bitIdx = 2;
+    }
+    else
+    {
+        bitIdx = 0;
+    }
+
+    //PPDMIAUDIOCONNECTOR pPort = server->mConsole->getAudioVideoRec()->getDrvAudioPort();
+    /* Call DrvAudio interface to get the t_sample type conversion function */
+    /*mpDrv->pUpPort->pfnConvDevFmtToStSample(mpDrv->pUpPort,
+                                            (cChannels == 2) ? 1 : 0,
+                                            !fUnsigned, 0, bitIdx,
+                                            pVRDEVoice->convAudioDevFmtToStSampl);*/
+    if (pVRDEVoice->convAudioDevFmtToStSampl)
+    {
+        LogFlowFunc(("Failed to get the conversion function \n"));
+    }
+    LogFlowFunc(("Required freq as requested by VRDP Server = %d\n", iSampleHz));
+    //if (iSampleHz && iSampleHz != pVRDEVoice->pHostVoiceIn.Props.uFrequency)
+    {
+        /* @todo if the above condition is false then pVRDEVoice->uFrequency will remain 0 */
+        /*mpDrv->pUpPort->pfnPrepareAudioConversion(mpDrv->pUpPort, iSampleHz,
+                                                  pVRDEVoice->pHostVoiceIn.Props.uFrequency,
+                                                  &pVRDEVoice->rate);*/
+        pVRDEVoice->uFrequency = iSampleHz;
+        LogFlowFunc(("pVRDEVoice assigned requested freq =%d\n", pVRDEVoice->uFrequency));
+    }
+    return VINF_SUCCESS;
+}
+
+/*
+ * pvContext: pointer to VRDP voice returned by the VRDP server. The is same pointer that we initialized in
+ *            drvAudioVideoRecDisableEnableIn VOICE_ENABLE case.
+ */
+int AudioVideoRec::handleVideoRecSvrCmdAudioInputEventData(void *pvContext, const void *pvData, uint32_t cbData)
+{
+    PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pvContext;
+    PPDMAUDIOSAMPLE pHostStereoSampleBuf; /* target sample buffer */
+    PPDMAUDIOSAMPLE pConvertedSampleBuf; /* samples adjusted for rate */
+    uint32_t cSamples = cbData / pVRDEVoice->cBytesPerFrame; /* Count of samples */
+    void * pTmpSampleBuf = NULL;
+    uint32_t cConvertedSamples; /* samples adjusted for rate */
+    uint32_t cbSamples = 0; /* count of bytes occupied by samples */
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("handleVRDPCmdInputEventData cbData = %d, bytesperfram=%d\n",
+              cbData, pVRDEVoice->cBytesPerFrame));
+
+    vrdeReallocSampleBuf(pVRDEVoice, cSamples);
+    pHostStereoSampleBuf = (PPDMAUDIOSAMPLE)pVRDEVoice->pvSamplesBuffer;
+    pVRDEVoice->convAudioDevFmtToStSampl(pHostStereoSampleBuf, pvData, cSamples, &videorec_nominal_volume);
+
+    /* count of rate adjusted samples */
+    pVRDEVoice->uFrequency = 22100; /* @todo handle this. How pVRDEVoice will get proper value */
+    cConvertedSamples = (cSamples * pVRDEVoice->pHostVoiceIn.Props.uHz) / pVRDEVoice->uFrequency;
+    vrdeReallocRateAdjSampleBuf(pVRDEVoice, cConvertedSamples);
+
+    pConvertedSampleBuf = (PPDMAUDIOSAMPLE)pVRDEVoice->pvRateBuffer;
+    if (pConvertedSampleBuf)
+    {
+        uint32_t cSampleSrc = cSamples;
+        uint32_t cSampleDst = cConvertedSamples;
+        /*mpDrv->pUpPort->pfnDoRateConversion(mpDrv->pUpPort, pVRDEVoice->rate, pHostStereoSampleBuf,
+                                            pConvertedSampleBuf, &cSampleSrc, &cConvertedSamples);*/
+        pTmpSampleBuf = pConvertedSampleBuf;
+        cbSamples =  cConvertedSamples * sizeof(PDMAUDIOSAMPLE);
+    }
+
+    if (cbSamples)
+        rc = vrdeRecordingCallback(pVRDEVoice, cbSamples, pTmpSampleBuf);
+
+    return rc;
+}
+
+/*
+ * pvContext: pointer to VRDP voice returned by the VRDP server. The is same pointer that we initialized in
+ *            drvAudioVideoRecDisableEnableIn VOICE_ENABLE case.
+ */
+int AudioVideoRec::handleVideoRecSvrCmdAudioInputEventEnd(void *pvContext)
+{
+    LogFlowFuncEnter();
+
+    PVIDEORECAUDIOIN pVRDEVoice = (PVIDEORECAUDIOIN)pvContext;
+    AssertPtrReturn(pVRDEVoice, VERR_INVALID_POINTER);
+
+    /* The caller will not use this context anymore. */
+    if (pVRDEVoice->rate)
+    {
+        //mpDrv->pUpPort->pfnEndAudioConversion(mpDrv->pUpPort, pVRDEVoice->rate);
+    }
+
+    if (pVRDEVoice->pvSamplesBuffer)
+    {
+        RTMemFree(pVRDEVoice->pvSamplesBuffer);
+        pVRDEVoice->pvSamplesBuffer = NULL;
+    }
+
+    if (pVRDEVoice->pvRateBuffer)
+    {
+        RTMemFree(pVRDEVoice->pvRateBuffer);
+        pVRDEVoice->pvRateBuffer = NULL;
+    }
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Construct a VRDE audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+/* static */
+DECLCALLBACK(int) AudioVideoRec::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+    PDRVAUDIOVIDEOREC pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVIDEOREC);
+    LogRel(("Audio: Initializing VRDE driver\n"));
+    LogFlowFunc(("fFlags=0x%x\n", fFlags));
+
+    /* we save the address of AudioVideoRec in Object node in CFGM tree and address of VRDP server in
+     * ObjectVRDPServer node. So presence of both is necessary.
+     */
+    //if (!CFGMR3AreValuesValid(pCfg, "Object\0") || !CFGMR3AreValuesValid(pCfg, "ObjectVRDPServer\0"))
+    //   return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
+    AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
+                    ("Configuration error: Not possible to attach anything to this driver!\n"),
+                    VERR_PDM_DRVINS_NO_ATTACH);
+
+    /*
+     * Init the static parts.
+     */
+    pThis->pDrvIns                   = pDrvIns;
+    /* IBase */
+    pDrvIns->IBase.pfnQueryInterface = drvAudioVideoRecQueryInterface;
+    /* IHostAudio */
+    PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvAudioVideoRec);
+
+    /* Get VRDPServer pointer. */
+    void *pvUser;
+    int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser);
+    if (RT_FAILURE(rc))
+    {
+        AssertMsgFailed(("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc));
+        return rc;
+    }
+
+    /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVideoRec. */
+    pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
+
+    pvUser = NULL;
+    rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser);
+    if (RT_FAILURE(rc))
+    {
+        AssertMsgFailed(("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc));
+        return rc;
+    }
+
+    pThis->pAudioVideoRec = (AudioVideoRec *)pvUser;
+    pThis->pAudioVideoRec->mpDrv = pThis;
+
+    /*
+     * Get the interface for the above driver (DrvAudio) to make mixer/conversion calls.
+     * Described in CFGM tree.
+     */
+    pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
+    if (!pThis->pUpPort)
+    {
+        AssertMsgFailed(("Configuration error: No upper interface specified!\n"));
+        return VERR_PDM_MISSING_INTERFACE_ABOVE;
+    }
+
+    return VINF_SUCCESS;
+}
+
+/* static */
+DECLCALLBACK(void) AudioVideoRec::drvDestruct(PPDMDRVINS pDrvIns)
+{
+    LogFlowFuncEnter();
+}
+
+/**
+ * VRDE audio driver registration record.
+ */
+const PDMDRVREG AudioVideoRec::DrvReg =
+{
+    PDM_DRVREG_VERSION,
+    /* szName */
+    "AudioVideoRec",
+    /* szRCMod */
+    "",
+    /* szR0Mod */
+    "",
+    /* pszDescription */
+    "Audio driver for video recording",
+    /* fFlags */
+    PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+    /* fClass. */
+    PDM_DRVREG_CLASS_AUDIO,
+    /* cMaxInstances */
+    ~0U,
+    /* cbInstance */
+    sizeof(DRVAUDIOVIDEOREC),
+    /* pfnConstruct */
+    AudioVideoRec::drvConstruct,
+    /* pfnDestruct */
+    AudioVideoRec::drvDestruct,
+    /* pfnRelocate */
+    NULL,
+    /* pfnIOCtl */
+    NULL,
+    /* pfnPowerOn */
+    NULL,
+    /* pfnReset */
+    NULL,
+    /* pfnSuspend */
+    NULL,
+    /* pfnResume */
+    NULL,
+    /* pfnAttach */
+    NULL,
+    /* pfnDetach */
+    NULL,
+    /* pfnPowerOff */
+    NULL,
+    /* pfnSoftReset */
+    NULL,
+    /* u32EndVersion */
+    PDM_DRVREG_VERSION
+};
+
