Index: /trunk/src/VBox/Devices/Audio/filteraudio.c
===================================================================
--- /trunk/src/VBox/Devices/Audio/filteraudio.c	(revision 34968)
+++ /trunk/src/VBox/Devices/Audio/filteraudio.c	(revision 34969)
@@ -257,4 +257,7 @@
      * or the device itself is changed during the runtime. */
     volatile uint32_t status;
+
+    /* the stream has been successfully initialized by host. */
+    bool fHostOK;
 
     /* Whether the input stream is used by the filter. */
@@ -508,14 +511,7 @@
  ******************************************************************************/
 
-/* We need some forward declarations */
-static int filteraudio_run_in(HWVoiceIn *hw);
-static int filteraudio_read(SWVoiceIn *sw, void *buf, int size);
-static int filteraudio_ctl_in(HWVoiceIn *hw, int cmd, ...);
-static void filteraudio_fini_in(HWVoiceIn *hw);
-static int filteraudio_init_in(HWVoiceIn *hw, audsettings_t *as);
-
 /*
  * Callback to feed audio input buffer. Samples format is be the same as
- * in the voice. The caller prepares stsample_t.
+ * in the voice. The caller prepares st_sample_t.
  *
  * @param cbSamples Size of pvSamples array in bytes.
@@ -595,6 +591,13 @@
     char *pcSrc;
     st_sample_t *psDst;
-
-    filterVoiceIn *pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
+    filterVoiceIn *pVoice;
+
+    if (!filter_conf.pDrv)
+    {
+        AssertFailed();
+        return -1;
+    }
+
+    pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
 
     if (!pVoice->fIntercepted)
@@ -662,56 +665,92 @@
 {
     int rc = VINF_SUCCESS;
-
-    filterVoiceIn *pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
-
-    if (!pVoice->fIntercepted)
-    {
-        /* Note: audio.c does not use variable parameters '...', so ok to forward only 'phw' and 'cmd'. */
-        Log(("FilterAudio: [Input]: forwarding ctl_in for voice %p (hw %p)\n", pVoice, pVoice->phw));
-        return filter_conf.pDrv->pcm_ops->ctl_in(phw, cmd);
-    }
-
-    Log(("FilterAudio: [Input]: ctl_in for voice %p (hw %p), cmd %d\n", pVoice, pVoice->phw, cmd));
-
-    if (ASMAtomicReadU32(&pVoice->status) != CA_STATUS_INIT)
-        return 0;
-
-    switch (cmd)
-    {
-        case VOICE_ENABLE:
+    filterVoiceIn *pVoice;
+
+    if (!filter_conf.pDrv)
+    {
+        AssertFailed();
+        return -1;
+    }
+
+    pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
+
+    if (cmd == VOICE_ENABLE)
+    {
+        /* Decide who will provide input audio: filter or host driver. */
+        if (!filter_input_intercepted())
         {
-            /* Only start the device if it is actually stopped */
-            if (!pVoice->fIsRunning)
+            if (!pVoice->fHostOK)
             {
-                IORingBufferReset(pVoice->pBuf);
-
-                /* Sniffer will inform us on a second thread for new incoming audio data.
-                 * Therefore register an callback function, which will process the new data.
-                 * */
-                rc = filter_input_begin(&pVoice->pvInputCtx, fltRecordingCallback, pVoice, pVoice->phw, pVoice->phw->samples);
-                if (RT_SUCCESS(rc))
-                {
-                    pVoice->fIsRunning = true;
-                }
-            }
-            if (RT_FAILURE(rc))
-            {
-                LogRel(("FilterAudio: [Input] Failed to start recording (%Rrc)\n", rc));
+                /* Host did not initialize the voice. */
+                Log(("FilterAudio: [Input]: ctl_in ENABLE voice %p (hw %p) not available on host\n", pVoice, pVoice->phw));
                 return -1;
             }
-            break;
+
+            /* Note: audio.c does not use variable parameters '...', so ok to forward only 'phw' and 'cmd'. */
+            Log(("FilterAudio: [Input]: forwarding ctl_in ENABLE for voice %p (hw %p)\n", pVoice, pVoice->phw));
+            return filter_conf.pDrv->pcm_ops->ctl_in(phw, cmd);
         }
-        case VOICE_DISABLE:
+
+        /* The filter will use this voice. */
+        Log(("FilterAudio: [Input]: ctl_in ENABLE for voice %p (hw %p), cmd %d\n", pVoice, pVoice->phw, cmd));
+
+        if (ASMAtomicReadU32(&pVoice->status) != CA_STATUS_INIT)
+            return -1;
+
+        /* Only start the device if it is actually stopped */
+        if (!pVoice->fIsRunning)
         {
-            /* Only stop the device if it is actually running */
-            if (pVoice->fIsRunning)
+            IORingBufferReset(pVoice->pBuf);
+
+            /* Sniffer will inform us on a second thread for new incoming audio data.
+             * Therefore register an callback function, which will process the new data.
+             * */
+            rc = filter_input_begin(&pVoice->pvInputCtx, fltRecordingCallback, pVoice, pVoice->phw, pVoice->phw->samples);
+            if (RT_SUCCESS(rc))
             {
-                pVoice->fIsRunning = false;
-                /* Tell the sniffer to not to use this context anymore. */
-                filter_input_end(pVoice->pvInputCtx);
+                pVoice->fIsRunning = true;
+
+                /* Remember that this voice is used by the filter. */
+                pVoice->fIntercepted = true;
             }
-            break;
         }
-    }
+        if (RT_FAILURE(rc))
+        {
+            LogRel(("FilterAudio: [Input] Failed to start recording (%Rrc)\n", rc));
+            return -1;
+        }
+    }
+    else if (cmd == VOICE_DISABLE)
+    {
+        if (ASMAtomicReadU32(&pVoice->status) != CA_STATUS_INIT)
+            return -1;
+
+        /* Check if the voice has been intercepted. */
+        if (!pVoice->fIntercepted)
+        {
+            /* Note: audio.c does not use variable parameters '...', so ok to forward only 'phw' and 'cmd'. */
+            Log(("FilterAudio: [Input]: forwarding ctl_in DISABLE for voice %p (hw %p)\n", pVoice, pVoice->phw));
+            return filter_conf.pDrv->pcm_ops->ctl_in(phw, cmd);
+        }
+
+        /* The filter used this voice. */
+        Log(("FilterAudio: [Input]: ctl_in DISABLE for voice %p (hw %p), cmd %d\n", pVoice, pVoice->phw, cmd));
+
+        /* Only stop the device if it is actually running */
+        if (pVoice->fIsRunning)
+        {
+            pVoice->fIsRunning = false;
+            /* Tell the sniffer to not to use this context anymore. */
+            filter_input_end(pVoice->pvInputCtx);
+        }
+
+        /* This voice is no longer used by the filter. */
+        pVoice->fIntercepted = false;
+    }
+    else
+    {
+        return -1; /* Unknown command. */
+    }
+
     return 0;
 }
@@ -720,14 +759,17 @@
 {
     int ret = -1;
-
-    filterVoiceIn *pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
-
-    /* Only pass voices, which are not intercepted. */
-    if (!pVoice->fIntercepted)
-    {
-        Log(("FilterAudio: [Input]: forwarding fini_in for voice %p (hw %p)\n", pVoice, pVoice->phw));
-        filter_conf.pDrv->pcm_ops->fini_in(phw);
+    filterVoiceIn *pVoice;
+
+    if (!filter_conf.pDrv)
+    {
+        AssertFailed();
         return;
     }
+
+    pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
+
+    /* Uninitialize both host and filter parts of the voice. */
+    Log(("FilterAudio: [Input]: forwarding fini_in for voice %p (hw %p)\n", pVoice, pVoice->phw));
+    filter_conf.pDrv->pcm_ops->fini_in(phw);
 
     Log(("FilterAudio: [Input]: fini_in for voice %p (hw %p)\n", pVoice, pVoice->phw));
@@ -736,5 +778,14 @@
         return;
 
-    ret = filteraudio_ctl_in(phw, VOICE_DISABLE);
+    /* If this voice is intercepted by filter, try to stop it. */
+    if (pVoice->fIntercepted)
+    {
+        ret = filteraudio_ctl_in(phw, VOICE_DISABLE);
+    }
+    else
+    {
+        ret = 0;
+    }
+
     if (RT_LIKELY(ret == 0))
     {
@@ -751,14 +802,20 @@
 static int filteraudio_init_in(HWVoiceIn *phw, audsettings_t *as)
 {
-    filterVoiceIn *pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
-
-    if (!filter_input_intercepted())
-    {
-        Log(("FilterAudio: [Input]: forwarding init_in for voice %p (hw %p)\n", pVoice, pVoice->phw));
-        pVoice->fIntercepted = false;
-        return filter_conf.pDrv->pcm_ops->init_in(phw, as);
-    }
-
-    Log(("FilterAudio: [Input]: init_in for voice %p (hw %p)\n", pVoice, pVoice->phw));
+    int hostret = -1;
+    filterVoiceIn *pVoice;
+
+    if (!filter_conf.pDrv)
+    {
+        AssertFailed();
+        return -1;
+    }
+
+    pVoice = (filterVoiceIn *)((uint8_t *)phw + filter_conf.pDrv->voice_size_in);
+
+    /* Initialize both host and filter parts of the voice. */
+    Log(("FilterAudio: [Input]: forwarding init_in for voice %p (hw %p)\n", pVoice, pVoice->phw));
+    hostret = filter_conf.pDrv->pcm_ops->init_in(phw, as);
+
+    Log(("FilterAudio: [Input]: init_in for voice %p (hw %p), hostret = %d\n", pVoice, pVoice->phw, hostret));
 
     ASMAtomicWriteU32(&pVoice->status, CA_STATUS_UNINIT);
@@ -767,16 +824,17 @@
     pVoice->rpos = 0;
     pVoice->pBuf = NULL;
-    /* Remember that this voice belongs to the filter. */
-    pVoice->fIntercepted = true;
+    pVoice->fHostOK = (hostret == 0);
+    pVoice->fIntercepted = false;
     pVoice->fIsRunning = false;
     pVoice->pvInputCtx = NULL;
 
-    /* The ring buffer must be big enough to hold specified number of samples.
-     * A sample includes all channels.
-     */
-    pVoice->phw->samples = 2048;
-
-    /* Initialize the hardware info section with the audio settings */
-    audio_pcm_init_info(&pVoice->phw->info, as);
+    if (!pVoice->fHostOK)
+    {
+        /* Initialize required fields of the common part of the voice. */
+        pVoice->phw->samples = 2048;
+
+        /* Initialize the hardware info section with the audio settings */
+        audio_pcm_init_info(&pVoice->phw->info, as);
+    }
 
     ASMAtomicWriteU32(&pVoice->status, CA_STATUS_IN_INIT);
@@ -794,5 +852,4 @@
 
     Log(("FilterAudio: [Input] HW samples: %d\n", pVoice->phw->samples));
-
     return 0;
 }
@@ -806,10 +863,20 @@
 static void *filteraudio_audio_init(void)
 {
-    return &filter_conf;
+    /* This is not supposed to be called. */
+    Log(("FilterAudio: Init\n"));
+    AssertFailed();
+    return NULL;
 }
 
 static void filteraudio_audio_fini(void *opaque)
 {
-    NOREF(opaque);
+    Log(("FilterAudio: Init fini %p\n", opaque));
+    /* Forward to the host driver. */
+    Assert(opaque == filter_conf.pDrvOpaque);
+    if (filter_conf.pDrv)
+    {
+        filter_conf.pDrv->fini(opaque);
+        filter_conf.pDrv = NULL;
+    }
 }
 
Index: /trunk/src/VBox/Main/ConsoleVRDPServer.cpp
===================================================================
--- /trunk/src/VBox/Main/ConsoleVRDPServer.cpp	(revision 34968)
+++ /trunk/src/VBox/Main/ConsoleVRDPServer.cpp	(revision 34969)
@@ -863,5 +863,5 @@
         if (pPort)
         {
-             // @todo dynamic filter attach does not yet work pPort->pfnAudioInputIntercept(pPort, false);
+             pPort->pfnAudioInputIntercept(pPort, false);
         }
         else
@@ -920,5 +920,5 @@
                 if (pPort)
                 {
-                     // @todo dynamic filter attach does not yet work pPort->pfnAudioInputIntercept(pPort, true);
+                     pPort->pfnAudioInputIntercept(pPort, true);
                      if (ppvIntercept)
                      {
@@ -2269,5 +2269,5 @@
                                             audioFormat,
                                             cSamples);
-            *ppvUserCtx = NULL; /* This is the ConsoleVRDP server context.
+            *ppvUserCtx = NULL; /* This is the ConsoleVRDPServer context.
                                  * Currently not used because only one client is allowed to
                                  * do audio input and the client id is saved by the ConsoleVRDPServer.
