audio: support audio hot-plug event for Windows
authorByeongki Shin <bk0121.shin@samsung.com>
Tue, 23 Feb 2016 06:27:04 +0000 (15:27 +0900)
committerSeokYeon Hwang <syeon.hwang@samsung.com>
Tue, 8 Mar 2016 08:07:52 +0000 (17:07 +0900)
Because dsound audio doesn't support audio endpoint event,
it has failed to initialize without speaker or mic connection.
Now, dsound supports audio endpoint event.
Therefore, emulator can play sound
even if a speaker is connected to host lately.

- Because the COM initialization requires for each thread,
  in order to share the object, a thread is used as init and event handler.
- When event occurs, the notification callback sends it to init thread.
  If we handle the event in notification callback, it would make deadlock.

Change-Id: Iaeef62d6c5b0952f77f0040abea9bfc7778be536
Signed-off-by: Byeongki Shin <bk0121.shin@samsung.com>
audio/audio.c
audio/dsound_template.h
audio/dsoundaudio.c

index 4636a88..87d4a86 100644 (file)
@@ -1170,6 +1170,7 @@ int AUD_get_buffer_size_out (SWVoiceOut *sw)
 void AUD_set_active_out (SWVoiceOut *sw, int on)
 {
     HWVoiceOut *hw;
+    int ret;
 
     if (!sw) {
         return;
@@ -1186,8 +1187,17 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
             if (!hw->enabled) {
                 hw->enabled = 1;
                 if (s->vm_running) {
-                    hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out);
-                    audio_reset_timer (s);
+                    /* To support hot-plugging event, such like dsoundaudio,
+                     * we should check success of ctl_out, since default
+                     * total_hw_samples_mixed might exceed its real range
+                     * after audio jack was connected.
+                     */
+                    ret = hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out);
+                    if (ret < 0) {
+                        hw->enabled = 0;
+                    } else {
+                        audio_reset_timer (s);
+                    }
                 }
             }
         }
index b439f33..e55857f 100644 (file)
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
+
+/*
+ * Patched audio jack hot-plug event handling
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ * Contact:
+ * Byeongki Shin <bk0121.shin@samsung.com>
+ */
+
 #ifdef DSBTYPE_IN
 #define NAME "capture buffer"
 #define NAME2 "DirectSoundCapture"
@@ -155,10 +166,10 @@ static void dsound_fini_out (HWVoiceOut *hw)
 }
 
 #ifdef DSBTYPE_IN
-static int dsound_init_in(HWVoiceIn *hw, struct audsettings *as,
+static int _dsound_init_in(HWVoiceIn *hw, struct audsettings *as,
                           void *drv_opaque)
 #else
-static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
+static int _dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
                            void *drv_opaque)
 #endif
 {
@@ -173,15 +184,19 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
     DSCBUFFERDESC bd;
     DSCBCAPS bc;
+    s->dsi = ds;
 #else
     const char *typ = "DAC";
     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
     DSBUFFERDESC bd;
     DSBCAPS bc;
+    s->dso = ds;
 #endif
 
+    dolog2("(%s) Starting.\n", __func__);
+
     if (!s->FIELD2) {
-        dolog ("Attempt to initialize voice without " NAME2 " object\n");
+        dolog2 ("Attempt to initialize voice without " NAME2 " object\n");
         return -1;
     }
 
@@ -210,6 +225,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
         &ds->dsound_buffer,
         NULL
         );
+    dolog2 ("(%s) ds(%p), ds->dsound_buffer(%p)\n", __func__,
+                 ds, &ds->dsound_buffer);
 #endif
 
     if (FAILED (hr)) {
@@ -254,13 +271,13 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
     }
     hw->samples = bc.dwBufferBytes >> hw->info.shift;
     ds->s = s;
-
+    s->settings = obt_as;
 #ifdef DEBUG_DSOUND
     dolog ("caps %ld, desc %ld\n",
            bc.dwBufferBytes, bd.dwBufferBytes);
 
-    dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
-           hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
+    dolog ("freq %d, chan %d, fmt %d\n",
+            s->settings.freq, s->settings.nchannels, s->settings.fmt);
 #endif
     return 0;
 
@@ -269,6 +286,66 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
     return -1;
 }
 
+#ifdef DSBTYPE_IN
+static int dsound_default_init_in(HWVoiceIn *hw, struct audsettings *as,
+                           void *drv_opaque)
+#else
+static int dsound_default_init_out(HWVoiceOut *hw, struct audsettings *as,
+                           void *drv_opaque)
+#endif
+{
+    dsound *s = drv_opaque;
+    DSoundConf *conf = &s->conf;
+#ifdef DSBTYPE_IN
+    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+#else
+    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+#endif
+
+    dolog2("(%s) s(%p) hw(%p)\n", __func__, s, hw);
+
+    hw->samples = conf->bufsize_out >> hw->info.shift;
+    ds->s = s;
+#ifdef DSBTYPE_IN
+    s->dsi = ds;
+#else
+    s->dso = ds;
+#endif
+    s->settings = *as;
+    audio_pcm_init_info (&hw->info, as);
+
+    return 0;
+}
+
+#ifdef DSBTYPE_IN
+static int dsound_init_in(HWVoiceIn *hw, struct audsettings *as,
+                          void *drv_opaque)
+#else
+static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
+                           void *drv_opaque)
+#endif
+{
+    dsound *s = drv_opaque;
+
+#ifdef DSBTYPE_IN
+    dolog2("(%s) s(%p) hw(%p) s->in_init_success=%d\n",
+           __func__, s, hw, s->in_init_success);
+    if (s->in_init_success == true) {
+#else
+    dolog2("(%s) s(%p) hw(%p) s->out_init_success=%d\n",
+           __func__, s, hw, s->out_init_success);
+    if (s->out_init_success == true) {
+#endif
+        return (glue (_dsound_init_, TYPE) (hw, as, s));
+    } else {
+        /* We can't get buffer size and hw info yet,
+         * because failed to initialize.
+         * So, we use conf as default
+         */
+        return (glue (dsound_default_init_, TYPE) (hw, as, s));
+    }
+}
+
 #undef NAME
 #undef NAME2
 #undef TYPE
index e9472c1..05a1403 100644 (file)
  * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
  */
 
+/*
+ * Patched audio jack hot-plug event handling
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ * Contact:
+ * Byeongki Shin <bk0121.shin@samsung.com>
+ */
+
 #include "qemu-common.h"
 #include "audio.h"
+#include "qemu/thread.h"
 
 #define AUDIO_CAP "dsound"
+#define COBJMACROS
 #include "audio_int.h"
 
 #include <windows.h>
 #include <mmsystem.h>
 #include <objbase.h>
 #include <dsound.h>
+#include <initguid.h>
+#include <mmdeviceapi.h>
 
 #include "audio_win_int.h"
 
 /* #define DEBUG_DSOUND */
 
+#ifdef DEBUG_DSOUND
+#define dolog2 dolog
+#else
+#define dolog2(...)
+#endif
+
+/* Assume that each plugging event cannot occur at the same time */
+enum dsound_plugging_event {
+    OUT_UNPLUGGED,
+    OUT_PLUGGED,
+    IN_UNPLUGGED,
+    IN_PLUGGED,
+    NOT_SUPPORTED
+};
+
 typedef struct {
     int bufsize_in;
     int bufsize_out;
@@ -48,18 +77,11 @@ typedef struct {
 } DSoundConf;
 
 typedef struct {
-    LPDIRECTSOUND dsound;
-    LPDIRECTSOUNDCAPTURE dsound_capture;
-    struct audsettings settings;
-    DSoundConf conf;
-} dsound;
-
-typedef struct {
     HWVoiceOut hw;
     LPDIRECTSOUNDBUFFER dsound_buffer;
     DWORD old_pos;
     int first_time;
-    dsound *s;
+    struct dsound *s;
 #ifdef DEBUG_DSOUND
     DWORD old_ppos;
     DWORD played;
@@ -71,9 +93,34 @@ typedef struct {
     HWVoiceIn hw;
     int first_time;
     LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
-    dsound *s;
+    struct dsound *s;
 } DSoundVoiceIn;
 
+typedef struct dsound {
+    LPDIRECTSOUND dsound;
+    LPDIRECTSOUNDCAPTURE dsound_capture;
+    DSoundVoiceIn *dsi;
+    DSoundVoiceOut *dso;
+    struct audsettings settings;
+    DSoundConf conf;
+    IMMDeviceEnumerator *mmdev_enumerator;
+    IMMNotificationClient noti_client;
+
+    enum dsound_plugging_event event;
+    bool out_init_success;
+    bool in_init_success;
+
+    QemuThread th;
+    QemuMutex th_mutex;
+    QemuMutex init_mutex;
+    QemuCond th_cond;
+    QemuCond init_cond;
+    bool th_initialized;
+    bool th_exit;
+} dsound;
+
+static void dsound_audio_fini (void *opaque);
+
 static void dsound_log_hresult (HRESULT hr)
 {
     const char *str = "BUG";
@@ -294,9 +341,12 @@ static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
         return -1;
     }
 
+    /* Because ctl_out of pcm_ops changed to be checked its success,
+     * this function should be able to return success
+     * in the DSERR_BUFFERLOST case.
+     */
     if (*statusp & DSERR_BUFFERLOST) {
-        dsound_restore_out(dsb, s);
-        return -1;
+        return dsound_restore_out(dsb, s);
     }
 
     return 0;
@@ -414,10 +464,12 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
     dsound *s = ds->s;
 
     if (!dsb) {
-        dolog ("Attempt to control voice without a buffer\n");
-        return 0;
+        dolog2 ("Attempt to control voice without a buffer\n");
+        return -1;
     }
 
+    dolog2 ("(%s) ds(%p), ds->dsound_buffer(%p)\n", __func__,
+                 ds, &ds->dsound_buffer);
     switch (cmd) {
     case VOICE_ENABLE:
         if (dsound_get_status_out (dsb, &status, s)) {
@@ -480,7 +532,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
     DSoundConf *conf = &s->conf;
 
     if (!dsb) {
-        dolog ("Attempt to run empty with playback buffer\n");
+        dolog2 ("Attempt to run empty with playback buffer\n");
         return 0;
     }
 
@@ -528,7 +580,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
         }
 
 #ifdef DEBUG_DSOUND
-        ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
+        ds->played += audio_ring_dist (ds->old_pos, ppos, bufsize);
 #endif
         old_pos = ds->old_pos;
     }
@@ -605,7 +657,7 @@ static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
     LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
 
     if (!dscb) {
-        dolog ("Attempt to control capture voice without a buffer\n");
+        dolog2 ("Attempt to control capture voice without a buffer\n");
         return -1;
     }
 
@@ -670,7 +722,7 @@ static int dsound_run_in (HWVoiceIn *hw)
     dsound *s = ds->s;
 
     if (!dscb) {
-        dolog ("Attempt to run without capture buffer\n");
+        dolog2 ("Attempt to run without capture buffer\n");
         return 0;
     }
 
@@ -746,6 +798,173 @@ static int dsound_run_in (HWVoiceIn *hw)
     return decr;
 }
 
+static enum dsound_plugging_event dsound_determine_event(DWORD state, EDataFlow flow)
+{
+    enum dsound_plugging_event event;
+
+    /* XXX: is it possible for speaker and mic to be plugged at the same time? */
+    if ((state != DEVICE_STATE_ACTIVE && state != DEVICE_STATE_UNPLUGGED) ||
+       (flow != eRender && flow != eCapture)) {
+        dolog("(%s) not supported event. state=%#lx, flow=%d\n", __func__, state, flow);
+        return NOT_SUPPORTED;
+    }
+
+    if (state == DEVICE_STATE_ACTIVE) {
+        if (flow == eRender) {
+            event = OUT_PLUGGED;
+        } else if (flow == eCapture) {
+            event = IN_PLUGGED;
+        }
+    } else {
+        if (flow == eRender) {
+            event = OUT_UNPLUGGED;
+        } else if (flow == eCapture) {
+            event = IN_UNPLUGGED;
+        }
+    }
+
+    return event;
+}
+
+static int dsound_audio_out_init(dsound *s)
+{
+    HRESULT hr;
+    int err;
+
+    dolog2("(%s) Starting. s->dsound(%p)\n", __func__, s->dsound);
+
+    hr = IDirectSound_Initialize (s->dsound, NULL);
+    if (hr != DSERR_ALREADYINITIALIZED && FAILED (hr)) {
+        dsound_logerr (hr, "Could not initialize DirectSound\n");
+        return -1;
+    }
+
+    err = dsound_open (s);
+    if (err) {
+            dsound_audio_fini (s);
+            return -1;
+    }
+
+    dolog2("(%s) success\n", __func__);
+
+    return 0;
+}
+
+static int dsound_audio_in_init(dsound *s)
+{
+    HRESULT hr;
+
+    dolog2("(%s) Starting. s->dsound_capture(%p)\n", __func__, s->dsound_capture);
+    hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
+    if (hr != DSERR_ALREADYINITIALIZED && FAILED (hr)) {
+        dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
+        return -1;
+    }
+
+    dolog("(%s) success\n", __func__);
+
+    return 0;
+}
+
+/* Implementation of IMMNotificationClient callbacks */
+static HRESULT STDMETHODCALLTYPE dsound_QueryInterface(IMMNotificationClient *This,
+        REFIID riid, void **ppvObject)
+{
+    dolog2("(%s)\n", __func__);
+    return E_NOTIMPL;
+}
+
+static ULONG STDMETHODCALLTYPE dsound_AddRef(IMMNotificationClient *This)
+{
+    dolog2("(%s)\n", __func__);
+    return E_NOTIMPL;
+}
+
+static ULONG STDMETHODCALLTYPE dsound_Release(IMMNotificationClient *This)
+{
+    dolog2("(%s)\n", __func__);
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE dsound_OnDeviceStateChanged(IMMNotificationClient* This,
+        LPCWSTR pwstrDeviceId, DWORD dwNewState)
+{
+    dsound *s = CONTAINING_RECORD(This, dsound, noti_client);
+    HRESULT hr;
+    IMMDevice *mmdev;
+    IMMEndpoint *ep;
+    EDataFlow flow;
+
+    /* To decide whether IN or OUT, get IMMEndpoint interface */
+    hr = IMMDeviceEnumerator_GetDevice(s->mmdev_enumerator, pwstrDeviceId, &mmdev);
+    if (FAILED (hr)) {
+        dolog("(%s) Could not get mmdevice\n", __func__);
+        return E_FAIL;
+    }
+    dolog2("(%s) mmdev(%p)\n", __func__, mmdev);
+
+    hr = IMMDevice_QueryInterface(mmdev, &IID_IMMEndpoint, (void **)&ep);
+    if (FAILED (hr)) {
+        dolog("(%s) Could not get enpoint\n", __func__);
+        return E_FAIL;
+    }
+
+    hr = IMMEndpoint_GetDataFlow(ep, &flow);
+    if (FAILED (hr)) {
+        dolog("(%s) Could not get dataflow\n", __func__);
+        return E_FAIL;
+    }
+    dolog2("(%s) ep(%p), flow=%d\n", __func__, ep, flow);
+
+    qemu_mutex_lock(&s->th_mutex);
+    s->event = dsound_determine_event(dwNewState, flow);
+    if (s->event != NOT_SUPPORTED) {
+        qemu_cond_signal(&s->th_cond);
+    }
+    qemu_mutex_unlock(&s->th_mutex);
+
+    return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE dsound_OnDeviceAdded(IMMNotificationClient* This,
+        LPCWSTR pwstrDeviceId)
+{
+    dolog2("(%s)\n", __func__);
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE dsound_OnDeviceRemoved(IMMNotificationClient* This,
+        LPCWSTR pwstrDeviceId)
+{
+    dolog2("(%s)\n", __func__);
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE dsound_OnDefaultDeviceChanged(IMMNotificationClient* This,
+        EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
+{
+    dolog2("(%s)\n", __func__);
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE dsound_OnPropertyValueChanged(IMMNotificationClient* This,
+        LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
+{
+    dolog2("(%s)\n", __func__);
+    return E_NOTIMPL;
+}
+
+static IMMNotificationClientVtbl dsound_dsound_vtbl = {
+    dsound_QueryInterface,
+    dsound_AddRef,
+    dsound_Release,
+    dsound_OnDeviceStateChanged,
+    dsound_OnDeviceAdded,
+    dsound_OnDeviceRemoved,
+    dsound_OnDefaultDeviceChanged,
+    dsound_OnPropertyValueChanged
+};
+
 static DSoundConf glob_conf = {
     .bufsize_in         = 16384,
     .bufsize_out        = 16384,
@@ -757,6 +976,17 @@ static void dsound_audio_fini (void *opaque)
     HRESULT hr;
     dsound *s = opaque;
 
+    qemu_mutex_lock(&s->th_mutex);
+    s->th_exit = true;
+    qemu_cond_signal(&s->th_cond);
+    qemu_mutex_unlock(&s->th_mutex);
+
+    qemu_thread_join(&s->th);
+    qemu_cond_destroy(&s->th_cond);
+    qemu_cond_destroy(&s->init_cond);
+    qemu_mutex_destroy(&s->th_mutex);
+    qemu_mutex_destroy(&s->init_mutex);
+
     if (!s->dsound) {
         g_free(s);
         return;
@@ -782,20 +1012,27 @@ static void dsound_audio_fini (void *opaque)
     g_free(s);
 }
 
-static void *dsound_audio_init (void)
+/* Because a trial initializing in noti-handler makes some deadlock,
+ * the initialization of DirectSound with COM
+ * should be executed by one thread.
+ */
+static void *dsound_initialization_thread(void *opaque)
 {
-    int err;
     HRESULT hr;
-    dsound *s = g_malloc0(sizeof(dsound));
+    dsound *s = (dsound *)opaque;
+    HWVoiceOut *hwo;
+    HWVoiceIn *hwi;
+
+    dolog2("(%s) Starting. dsound(%p)\n", __func__, s);
 
-    s->conf = glob_conf;
     hr = CoInitialize (NULL);
     if (FAILED (hr)) {
         dsound_logerr (hr, "Could not initialize COM\n");
-        g_free(s);
-        return NULL;
+        goto CoFailed;
     }
+    dolog2("(%s) CoInitialize succeeded\n", __func__);
 
+    /* For Playback */
     hr = CoCreateInstance (
         &CLSID_DirectSound,
         NULL,
@@ -805,22 +1042,20 @@ static void *dsound_audio_init (void)
         );
     if (FAILED (hr)) {
         dsound_logerr (hr, "Could not create DirectSound instance\n");
-        g_free(s);
-        return NULL;
+        goto CoFailed;
     }
-
-    hr = IDirectSound_Initialize (s->dsound, NULL);
-    if (FAILED (hr)) {
-        dsound_logerr (hr, "Could not initialize DirectSound\n");
-
-        hr = IDirectSound_Release (s->dsound);
-        if (FAILED (hr)) {
-            dsound_logerr (hr, "Could not release DirectSound\n");
-        }
-        g_free(s);
-        return NULL;
+    dolog2("(%s) CoCreateInstance succeeded. s->dsound(%p)\n", __func__, s->dsound);
+
+    /* try once at init time */
+    if (dsound_audio_out_init(s) < 0) {
+        dolog2("(%s) dsound_audio_out_init failed.\n", __func__);
+        s->out_init_success = false;
+    } else {
+        dolog2("(%s) dsound_audio_out_init success.\n", __func__);
+        s->out_init_success = true;
     }
 
+    /* For Capture */
     hr = CoCreateInstance (
         &CLSID_DirectSoundCapture,
         NULL,
@@ -830,26 +1065,182 @@ static void *dsound_audio_init (void)
         );
     if (FAILED (hr)) {
         dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
+        goto CoFailed;
     }
-    else {
-        hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
-        if (FAILED (hr)) {
-            dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
 
-            hr = IDirectSoundCapture_Release (s->dsound_capture);
-            if (FAILED (hr)) {
-                dsound_logerr (hr, "Could not release DirectSoundCapture\n");
+    /* try once at init time */
+    if (dsound_audio_in_init(s) < 0) {
+        dolog2("(%s) dsound_audio_in_init failed.\n", __func__);
+        s->in_init_success = false;
+    } else {
+        dolog2("(%s) dsound_audio_in_init success.\n", __func__);
+        s->in_init_success = true;
+    }
+
+CoFailed:
+    qemu_mutex_lock(&s->init_mutex);
+    s->th_initialized = true;
+    qemu_cond_signal(&s->init_cond);
+    qemu_mutex_unlock(&s->init_mutex);
+
+    if (FAILED (hr)) {
+        dolog("(%s) terminating.\n", __func__);
+        return NULL;
+    }
+
+    /* event handling loop */
+    qemu_mutex_lock(&s->th_mutex);
+    while (1) {
+        qemu_cond_wait(&s->th_cond, &s->th_mutex);
+
+        if (s->th_exit) {
+            break;
+        }
+
+        switch (s->event) {
+        case OUT_UNPLUGGED:
+            hwo = (HWVoiceOut *)s->dso;
+            assert(hwo != NULL);
+            assert(s->dsound != NULL);
+
+            dsound_fini_out(hwo);
+            s->out_init_success = false;
+            dolog("(%s) handled OUT_UNPLUGGED.\n", __func__);
+            break;
+
+        case OUT_PLUGGED:
+            hwo = (HWVoiceOut *)s->dso;
+            assert(hwo != NULL);
+
+            if (dsound_audio_out_init(s) < 0) {
+                dolog("(%s) dsound_audio_out_init failed.\n", __func__);
+                s->out_init_success = false;
+                break;
             }
-            s->dsound_capture = NULL;
+            s->out_init_success = true;
+
+            /* After audio system init finished,
+             * pcm_ops.init_out is not called any more.
+             * So, we should update backend data here.
+             */
+            if (dsound_init_out(hwo, &s->settings, s) < 0 ) {
+                dolog2("(%s) dsound_init_out failed\n", __func__);
+            } else {
+                dolog2("(%s) dsound_init_out succeeded\n", __func__);
+            }
+            dolog("(%s) handled OUT_PLUGGED.\n", __func__);
+            break;
+
+        case IN_UNPLUGGED:
+            hwi = (HWVoiceIn *)s->dsi;
+            assert(hwi != NULL);
+            assert(s->dsound_capture != NULL);
+
+            dsound_fini_in(hwi);
+            s->in_init_success = false;
+
+            dolog("(%s) handled IN_UNPLUGGED\n", __func__);
+            break;
+
+        case IN_PLUGGED:
+            hwi = (HWVoiceIn *)s->dsi;
+            assert(hwi != NULL);
+
+            if (dsound_audio_in_init(s) < 0) {
+                dolog("(%s) dsound_audio_in_init failed.\n", __func__);
+                s->in_init_success = false;
+                break;
+            }
+            s->in_init_success = true;
+
+            /* Since, IN has same reason to out case */
+            if (dsound_init_in(hwi, &s->settings, s) < 0 ) {
+                dolog2("(%s) dsound_init_in failed\n", __func__);
+            } else {
+                dolog2("(%s) dsound_init_in succeeded\n", __func__);
+            }
+
+            dolog("(%s) handled IN_PLUGGED\n", __func__);
+            break;
+
+        default:
+            dolog("(%s) Unknown event\n", __func__);
+            break;
         }
     }
+    qemu_mutex_unlock(&s->th_mutex);
 
-    err = dsound_open (s);
-    if (err) {
-        dsound_audio_fini (s);
+    return NULL;
+}
+
+static void dsound_create_thread(dsound *s)
+{
+    qemu_cond_init(&s->th_cond);
+    qemu_cond_init(&s->init_cond);
+    qemu_mutex_init(&s->th_mutex);
+    qemu_mutex_init(&s->init_mutex);
+
+    qemu_thread_create(&s->th,
+                       "dsound-init-thread",
+                       dsound_initialization_thread,
+                       (void *)s,
+                       QEMU_THREAD_JOINABLE);
+
+    dolog("(%s) Done\n", __func__);
+}
+
+static void *dsound_audio_init (void)
+{
+    HRESULT hr;
+    dsound *s = g_malloc0(sizeof(dsound));
+
+    s->conf = glob_conf;
+
+    hr = CoInitialize (NULL);
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not initialize COM\n");
+        g_free(s);
         return NULL;
     }
 
+    hr = CoCreateInstance (
+        &CLSID_MMDeviceEnumerator,
+        NULL,
+        CLSCTX_ALL,
+        &IID_IMMDeviceEnumerator,
+        (void **) &s->mmdev_enumerator
+        );
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not create MMDeviceEnumerator instance\n");
+        g_free(s);
+        return NULL;
+    }
+
+    s->noti_client.lpVtbl = &dsound_dsound_vtbl;
+    hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(s->mmdev_enumerator, &s->noti_client);
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not register endpoint notification callbacks\n");
+        g_free(s);
+        return NULL;
+    }
+    dolog("Registering endpoint notification callback succeeded.\n");
+
+    dsound_create_thread(s);
+
+    /* For serialization with init thread
+     * If audio.c invoks pcm_ops.init_out before the init thread finishes it's work,
+     * init_out callback works with undetermined status that is 'out_init_success'.
+     */
+    qemu_mutex_lock(&s->init_mutex);
+    while (!s->th_initialized) {
+        dolog2("(%s) Waiting for finishing initialization of thread\n", __func__);
+        qemu_cond_wait(&s->init_cond, &s->init_mutex);
+    }
+    qemu_mutex_unlock(&s->init_mutex);
+
+
+    dolog ("(%s) finished\n", __func__);
+
     return s;
 }