Beispiel #1
0
static void DSOUND_ReleaseDevice(DirectSoundDevice *device)
{
    if(device->client){
        IAudioClient_Release(device->client);
        device->client = NULL;
    }
    if(device->render){
        IAudioRenderClient_Release(device->render);
        device->render = NULL;
    }
    if(device->volume){
        IAudioStreamVolume_Release(device->volume);
        device->volume = NULL;
    }

    if (device->pad) {
        device->playpos += device->pad;
        device->playpos %= device->buflen;
        device->pad = 0;
    }
}
Beispiel #2
0
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
{
    UINT prebuf_frames;
    REFERENCE_TIME prebuf_rt;
    HRESULT hres;

    TRACE("(%p, %d)\n", device, forcewave);

    if(device->client){
        IAudioClient_Release(device->client);
        device->client = NULL;
    }
    if(device->render){
        IAudioRenderClient_Release(device->render);
        device->render = NULL;
    }
    if(device->clock){
        IAudioClock_Release(device->clock);
        device->clock = NULL;
    }
    if(device->volume){
        IAudioStreamVolume_Release(device->volume);
        device->volume = NULL;
    }

    hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
            CLSCTX_INPROC_SERVER, NULL, (void **)&device->client);
    if(FAILED(hres)){
        WARN("Activate failed: %08x\n", hres);
        return hres;
    }

    prebuf_frames = device->prebuf * DSOUND_fraglen(device->pwfx->nSamplesPerSec, device->pwfx->nBlockAlign) / device->pwfx->nBlockAlign;
    prebuf_rt = (10000000 * (UINT64)prebuf_frames) / device->pwfx->nSamplesPerSec;

    hres = IAudioClient_Initialize(device->client,
            AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST,
            prebuf_rt, 0, device->pwfx, NULL);
    if(FAILED(hres)){
        IAudioClient_Release(device->client);
        device->client = NULL;
        WARN("Initialize failed: %08x\n", hres);
        return hres;
    }

    hres = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
            (void**)&device->render);
    if(FAILED(hres)){
        IAudioClient_Release(device->client);
        device->client = NULL;
        WARN("GetService failed: %08x\n", hres);
        return hres;
    }

    hres = IAudioClient_GetService(device->client, &IID_IAudioClock,
            (void**)&device->clock);
    if(FAILED(hres)){
        IAudioClient_Release(device->client);
        IAudioRenderClient_Release(device->render);
        device->client = NULL;
        device->render = NULL;
        WARN("GetService failed: %08x\n", hres);
        return hres;
    }

    hres = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
            (void**)&device->volume);
    if(FAILED(hres)){
        IAudioClient_Release(device->client);
        IAudioRenderClient_Release(device->render);
        IAudioClock_Release(device->clock);
        device->client = NULL;
        device->render = NULL;
        device->clock = NULL;
        WARN("GetService failed: %08x\n", hres);
        return hres;
    }

    return S_OK;
}
Beispiel #3
0
static void test_padding(void)
{
    HRESULT hr;
    IAudioClient *ac;
    IAudioRenderClient *arc;
    WAVEFORMATEX *pwfx;
    REFERENCE_TIME minp, defp;
    BYTE *buf;
    UINT32 psize, pad, written;

    hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER,
            NULL, (void**)&ac);
    ok(hr == S_OK, "Activation failed with %08x\n", hr);
    if(hr != S_OK)
        return;

    hr = IAudioClient_GetMixFormat(ac, &pwfx);
    ok(hr == S_OK, "GetMixFormat failed: %08x\n", hr);
    if(hr != S_OK)
        return;

    hr = IAudioClient_Initialize(ac, AUDCLNT_SHAREMODE_SHARED,
            0, 5000000, 0, pwfx, NULL);
    ok(hr == S_OK, "Initialize failed: %08x\n", hr);

    hr = IAudioClient_GetDevicePeriod(ac, &defp, &minp);
    ok(hr == S_OK, "GetDevicePeriod failed: %08x\n", hr);
    ok(defp != 0, "Default period is 0\n");
    ok(minp != 0, "Minimum period is 0\n");
    ok(minp <= defp, "Mininum period is greater than default period\n");

    hr = IAudioClient_GetService(ac, &IID_IAudioRenderClient, (void**)&arc);
    ok(hr == S_OK, "GetService failed: %08x\n", hr);

    psize = (defp / 10000000.) * pwfx->nSamplesPerSec * pwfx->nBlockAlign;

    written = 0;
    hr = IAudioClient_GetCurrentPadding(ac, &pad);
    ok(hr == S_OK, "GetCurrentPadding failed: %08x\n", hr);
    ok(pad == written, "GetCurrentPadding returned %u, should be %u\n", pad, written);

    hr = IAudioRenderClient_GetBuffer(arc, psize, &buf);
    ok(hr == S_OK, "GetBuffer failed: %08x\n", hr);
    ok(buf != NULL, "NULL buffer returned\n");

    hr = IAudioRenderClient_ReleaseBuffer(arc, psize,
            AUDCLNT_BUFFERFLAGS_SILENT);
    ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr);
    written += psize;

    hr = IAudioClient_GetCurrentPadding(ac, &pad);
    ok(hr == S_OK, "GetCurrentPadding failed: %08x\n", hr);
    ok(pad == written, "GetCurrentPadding returned %u, should be %u\n", pad, written);

    psize = (minp / 10000000.) * pwfx->nSamplesPerSec * pwfx->nBlockAlign;

    hr = IAudioRenderClient_GetBuffer(arc, psize, &buf);
    ok(hr == S_OK, "GetBuffer failed: %08x\n", hr);
    ok(buf != NULL, "NULL buffer returned\n");

    hr = IAudioRenderClient_ReleaseBuffer(arc, psize,
            AUDCLNT_BUFFERFLAGS_SILENT);
    ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr);
    written += psize;

    hr = IAudioClient_GetCurrentPadding(ac, &pad);
    ok(hr == S_OK, "GetCurrentPadding failed: %08x\n", hr);
    ok(pad == written, "GetCurrentPadding returned %u, should be %u\n", pad, written);

    /* overfull buffer. requested 1/2s buffer size, so try
     * to get a 1/2s buffer, which should fail */
    psize = pwfx->nSamplesPerSec / 2.;
    hr = IAudioRenderClient_GetBuffer(arc, psize, &buf);
    ok(hr == AUDCLNT_E_BUFFER_TOO_LARGE, "GetBuffer gave wrong error: %08x\n", hr);

    hr = IAudioRenderClient_ReleaseBuffer(arc, psize, 0);
    ok(hr == AUDCLNT_E_OUT_OF_ORDER, "ReleaseBuffer gave wrong error: %08x\n", hr);

    hr = IAudioClient_GetCurrentPadding(ac, &pad);
    ok(hr == S_OK, "GetCurrentPadding failed: %08x\n", hr);
    ok(pad == written, "GetCurrentPadding returned %u, should be %u\n", pad, written);

    CoTaskMemFree(pwfx);

    IAudioRenderClient_Release(arc);
    IAudioClient_Release(ac);
}
Beispiel #4
0
static void test_references(void)
{
    IAudioClient *ac;
    IAudioRenderClient *rc;
    ISimpleAudioVolume *sav;
    IAudioStreamVolume *asv;
    IAudioClock *acl;
    WAVEFORMATEX *pwfx;
    HRESULT hr;
    ULONG ref;

    /* IAudioRenderClient */
    hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER,
            NULL, (void**)&ac);
    ok(hr == S_OK, "Activation failed with %08x\n", hr);
    if(hr != S_OK)
        return;

    hr = IAudioClient_GetMixFormat(ac, &pwfx);
    ok(hr == S_OK, "GetMixFormat failed: %08x\n", hr);
    if(hr != S_OK)
        return;

    hr = IAudioClient_Initialize(ac, AUDCLNT_SHAREMODE_SHARED, 0, 5000000,
            0, pwfx, NULL);
    ok(hr == S_OK, "Initialize failed: %08x\n", hr);

    CoTaskMemFree(pwfx);

    hr = IAudioClient_GetService(ac, &IID_IAudioRenderClient, (void**)&rc);
    ok(hr == S_OK, "GetService failed: %08x\n", hr);

    IAudioRenderClient_AddRef(rc);
    ref = IAudioRenderClient_Release(rc);
    ok(ref != 0, "RenderClient_Release gave wrong refcount: %u\n", ref);

    ref = IAudioClient_Release(ac);
    ok(ref != 0, "Client_Release gave wrong refcount: %u\n", ref);

    ref = IAudioRenderClient_Release(rc);
    ok(ref == 0, "RenderClient_Release gave wrong refcount: %u\n", ref);

    /* ISimpleAudioVolume */
    hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER,
            NULL, (void**)&ac);
    ok(hr == S_OK, "Activation failed with %08x\n", hr);
    if(hr != S_OK)
        return;

    hr = IAudioClient_GetMixFormat(ac, &pwfx);
    ok(hr == S_OK, "GetMixFormat failed: %08x\n", hr);

    hr = IAudioClient_Initialize(ac, AUDCLNT_SHAREMODE_SHARED, 0, 5000000,
            0, pwfx, NULL);
    ok(hr == S_OK, "Initialize failed: %08x\n", hr);

    CoTaskMemFree(pwfx);

    hr = IAudioClient_GetService(ac, &IID_ISimpleAudioVolume, (void**)&sav);
    ok(hr == S_OK, "GetService failed: %08x\n", hr);

    ISimpleAudioVolume_AddRef(sav);
    ref = ISimpleAudioVolume_Release(sav);
    ok(ref != 0, "SimpleAudioVolume_Release gave wrong refcount: %u\n", ref);

    ref = IAudioClient_Release(ac);
    ok(ref != 0, "Client_Release gave wrong refcount: %u\n", ref);

    ref = ISimpleAudioVolume_Release(sav);
    ok(ref == 0, "SimpleAudioVolume_Release gave wrong refcount: %u\n", ref);

    /* IAudioClock */
    hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER,
            NULL, (void**)&ac);
    ok(hr == S_OK, "Activation failed with %08x\n", hr);
    if(hr != S_OK)
        return;

    hr = IAudioClient_GetMixFormat(ac, &pwfx);
    ok(hr == S_OK, "GetMixFormat failed: %08x\n", hr);

    hr = IAudioClient_Initialize(ac, AUDCLNT_SHAREMODE_SHARED, 0, 5000000,
            0, pwfx, NULL);
    ok(hr == S_OK, "Initialize failed: %08x\n", hr);

    CoTaskMemFree(pwfx);

    hr = IAudioClient_GetService(ac, &IID_IAudioClock, (void**)&acl);
    ok(hr == S_OK, "GetService failed: %08x\n", hr);

    IAudioClock_AddRef(acl);
    ref = IAudioClock_Release(acl);
    ok(ref != 0, "AudioClock_Release gave wrong refcount: %u\n", ref);

    ref = IAudioClient_Release(ac);
    ok(ref != 0, "Client_Release gave wrong refcount: %u\n", ref);

    ref = IAudioClock_Release(acl);
    ok(ref == 0, "AudioClock_Release gave wrong refcount: %u\n", ref);

    /* IAudioStreamVolume */
    hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER,
            NULL, (void**)&ac);
    ok(hr == S_OK, "Activation failed with %08x\n", hr);
    if(hr != S_OK)
        return;

    hr = IAudioClient_GetMixFormat(ac, &pwfx);
    ok(hr == S_OK, "GetMixFormat failed: %08x\n", hr);

    hr = IAudioClient_Initialize(ac, AUDCLNT_SHAREMODE_SHARED, 0, 5000000,
            0, pwfx, NULL);
    ok(hr == S_OK, "Initialize failed: %08x\n", hr);

    hr = IAudioClient_GetService(ac, &IID_IAudioStreamVolume, (void**)&asv);
    ok(hr == S_OK, "GetService failed: %08x\n", hr);

    IAudioStreamVolume_AddRef(asv);
    ref = IAudioStreamVolume_Release(asv);
    ok(ref != 0, "AudioStreamVolume_Release gave wrong refcount: %u\n", ref);

    ref = IAudioClient_Release(ac);
    ok(ref != 0, "Client_Release gave wrong refcount: %u\n", ref);

    ref = IAudioStreamVolume_Release(asv);
    ok(ref == 0, "AudioStreamVolume_Release gave wrong refcount: %u\n", ref);
}
Beispiel #5
0
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
{
    HRESULT hres;
    REFERENCE_TIME period;
    UINT32 frames;
    DWORD period_ms;
    IAudioClient *client = NULL;
    IAudioRenderClient *render = NULL;
    IAudioStreamVolume *volume = NULL;
    DWORD fraglen;
    WAVEFORMATEX *wfx = NULL;
    DWORD oldspeakerconfig = device->speaker_config;

    TRACE("(%p, %d)\n", device, forcewave);

    hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
            CLSCTX_INPROC_SERVER, NULL, (void **)&client);
    if(FAILED(hres)){
        WARN("Activate failed: %08x\n", hres);
        return hres;
    }

    hres = DSOUND_WaveFormat(device, client, forcewave, &wfx);
    if (FAILED(hres)) {
        IAudioClient_Release(client);
        return hres;
    }

    hres = IAudioClient_Initialize(client,
            AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST |
            AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 800000, 0, wfx, NULL);
    if(FAILED(hres)){
        IAudioClient_Release(client);
        ERR("Initialize failed: %08x\n", hres);
        return hres;
    }

    IAudioClient_SetEventHandle(client, device->sleepev);

    hres = IAudioClient_GetService(client, &IID_IAudioRenderClient, (void**)&render);
    if(FAILED(hres))
        goto err_service;

    hres = IAudioClient_GetService(client, &IID_IAudioStreamVolume, (void**)&volume);
    if(FAILED(hres))
        goto err_service;

    /* Now kick off the timer so the event fires periodically */
    hres = IAudioClient_Start(client);
    if (FAILED(hres)) {
        WARN("Start failed with %08x\n", hres);
        goto err;
    }
    hres = IAudioClient_GetStreamLatency(client, &period);
    if (FAILED(hres)) {
        WARN("GetStreamLatency failed with %08x\n", hres);
        goto err;
    }
    hres = IAudioClient_GetBufferSize(client, &frames);
    if (FAILED(hres)) {
        WARN("GetBufferSize failed with %08x\n", hres);
        goto err;
    }

    period_ms = (period + 9999) / 10000;
    fraglen = MulDiv(wfx->nSamplesPerSec, period, 10000000) * wfx->nBlockAlign;
    TRACE("period %u ms fraglen %u buflen %u\n", period_ms, fraglen, frames * wfx->nBlockAlign);

    hres = DSOUND_PrimaryOpen(device, wfx, frames, forcewave);
    if(FAILED(hres))
        goto err;

    DSOUND_ReleaseDevice(device);
    device->client = client;
    device->render = render;
    device->volume = volume;
    device->fraglen = fraglen;
    device->aclen = frames * wfx->nBlockAlign;

    if (period_ms < 3)
        device->sleeptime = 5;
    else
        device->sleeptime = period_ms * 5 / 2;

    return S_OK;

err_service:
    WARN("GetService failed: %08x\n", hres);
err:
    device->speaker_config = oldspeakerconfig;
    DSOUND_ParseSpeakerConfig(device);
    if (volume)
        IAudioStreamVolume_Release(volume);
    if (render)
        IAudioRenderClient_Release(render);
    IAudioClient_Release(client);
    HeapFree(GetProcessHeap(), 0, wfx);
    return hres;
}
Beispiel #6
0
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
{
    UINT prebuf_frames;
    REFERENCE_TIME prebuf_rt;
    WAVEFORMATEX *wfx = NULL;
    HRESULT hres;
    REFERENCE_TIME period;
    DWORD period_ms;

    TRACE("(%p, %d)\n", device, forcewave);

    if(device->client){
        IAudioClient_Release(device->client);
        device->client = NULL;
    }
    if(device->render){
        IAudioRenderClient_Release(device->render);
        device->render = NULL;
    }
    if(device->clock){
        IAudioClock_Release(device->clock);
        device->clock = NULL;
    }
    if(device->volume){
        IAudioStreamVolume_Release(device->volume);
        device->volume = NULL;
    }

    hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
            CLSCTX_INPROC_SERVER, NULL, (void **)&device->client);
    if(FAILED(hres)) {
        WARN("Activate failed: %08x\n", hres);
        return hres;
    }

    hres = DSOUND_WaveFormat(device, device->client, forcewave, &wfx);
    if (FAILED(hres)) {
        IAudioClient_Release(device->client);
        device->client = NULL;
        return hres;
    }
    HeapFree(GetProcessHeap(), 0, device->pwfx);
    device->pwfx = wfx;

    prebuf_frames = device->prebuf * DSOUND_fraglen(device) / device->pwfx->nBlockAlign;
    prebuf_rt = (10000000 * (UINT64)prebuf_frames) / device->pwfx->nSamplesPerSec;

    hres = IAudioClient_Initialize(device->client,
            AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST |
            AUDCLNT_STREAMFLAGS_EVENTCALLBACK, prebuf_rt, 0, device->pwfx, NULL);
    if(FAILED(hres)){
        IAudioClient_Release(device->client);
        device->client = NULL;
        WARN("Initialize failed: %08x\n", hres);
        return hres;
    }
    IAudioClient_SetEventHandle(device->client, device->sleepev);

    hres = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
            (void**)&device->render);
    if(FAILED(hres)){
        IAudioClient_Release(device->client);
        device->client = NULL;
        WARN("GetService failed: %08x\n", hres);
        return hres;
    }

    hres = IAudioClient_GetService(device->client, &IID_IAudioClock,
            (void**)&device->clock);
    if(FAILED(hres)){
        IAudioClient_Release(device->client);
        IAudioRenderClient_Release(device->render);
        device->client = NULL;
        device->render = NULL;
        WARN("GetService failed: %08x\n", hres);
        return hres;
    }

    hres = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
            (void**)&device->volume);
    if(FAILED(hres)){
        IAudioClient_Release(device->client);
        IAudioRenderClient_Release(device->render);
        IAudioClock_Release(device->clock);
        device->client = NULL;
        device->render = NULL;
        device->clock = NULL;
        WARN("GetService failed: %08x\n", hres);
        return hres;
    }

    /* Now kick off the timer so the event fires periodically */
    hres = IAudioClient_Start(device->client);
    if (FAILED(hres))
        WARN("starting failed with %08x\n", hres);

    hres = IAudioClient_GetStreamLatency(device->client, &period);
    if (FAILED(hres)) {
        WARN("GetStreamLatency failed with %08x\n", hres);
        period_ms = 10;
    } else
        period_ms = (period + 9999) / 10000;
    TRACE("period %u ms fraglen %u prebuf %u\n", period_ms, device->fraglen, device->prebuf);

    if (period_ms < 3)
        device->sleeptime = 5;
    else
        device->sleeptime = period_ms * 5 / 2;

    return S_OK;
}
JNIEXPORT void JNICALL
Java_org_jitsi_impl_neomedia_jmfext_media_protocol_wasapi_WASAPI_IAudioRenderClient_1Release
    (JNIEnv *env, jclass clazz, jlong thiz)
{
    IAudioRenderClient_Release((IAudioRenderClient *) (intptr_t) thiz);
}
Beispiel #8
0
static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
{
    ThreadRequest *req = ptr;
    IMMDeviceEnumerator *Enumerator;
    ALuint deviceCount = 0;
    MMDevApiData *data;
    ALCdevice *device;
    HRESULT hr, cohr;
    MSG msg;

    TRACE("Starting message thread\n");

    cohr = CoInitialize(NULL);
    if(FAILED(cohr))
    {
        WARN("Failed to initialize COM: 0x%08lx\n", cohr);
        req->result = cohr;
        SetEvent(req->FinishedEvt);
        return 0;
    }

    hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
    if(FAILED(hr))
    {
        WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
        CoUninitialize();
        req->result = hr;
        SetEvent(req->FinishedEvt);
        return 0;
    }
    Enumerator = ptr;
    IMMDeviceEnumerator_Release(Enumerator);
    Enumerator = NULL;

    CoUninitialize();

    req->result = S_OK;
    SetEvent(req->FinishedEvt);

    TRACE("Starting message loop\n");
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TRACE("Got message %u\n", msg.message);
        switch(msg.message)
        {
        case WM_USER_OpenDevice:
            req = (ThreadRequest*)msg.wParam;
            device = (ALCdevice*)msg.lParam;
            data = device->ExtraData;

            hr = cohr = S_OK;
            if(++deviceCount == 1)
                hr = cohr = CoInitialize(NULL);
            if(SUCCEEDED(hr))
                hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
            if(SUCCEEDED(hr))
            {
                Enumerator = ptr;
                if(!data->devid)
                    hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
                else
                    hr = IMMDeviceEnumerator_GetDevice(Enumerator, data->devid, &data->mmdev);
                IMMDeviceEnumerator_Release(Enumerator);
                Enumerator = NULL;
            }
            if(SUCCEEDED(hr))
                hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
            if(SUCCEEDED(hr))
            {
                data->client = ptr;
                device->DeviceName = get_device_name(data->mmdev);
            }

            if(FAILED(hr))
            {
                if(data->mmdev)
                    IMMDevice_Release(data->mmdev);
                data->mmdev = NULL;
                if(--deviceCount == 0 && SUCCEEDED(cohr))
                    CoUninitialize();
            }

            req->result = hr;
            SetEvent(req->FinishedEvt);
            continue;

        case WM_USER_ResetDevice:
            req = (ThreadRequest*)msg.wParam;
            device = (ALCdevice*)msg.lParam;

            req->result = DoReset(device);
            SetEvent(req->FinishedEvt);
            continue;

        case WM_USER_StartDevice:
            req = (ThreadRequest*)msg.wParam;
            device = (ALCdevice*)msg.lParam;
            data = device->ExtraData;

            ResetEvent(data->NotifyEvent);
            hr = IAudioClient_SetEventHandle(data->client, data->NotifyEvent);
            if(FAILED(hr))
                ERR("Failed to set event handle: 0x%08lx\n", hr);
            else
            {
                hr = IAudioClient_Start(data->client);
                if(FAILED(hr))
                    ERR("Failed to start audio client: 0x%08lx\n", hr);
            }

            if(SUCCEEDED(hr))
                hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &ptr);
            if(SUCCEEDED(hr))
            {
                data->render = ptr;
                data->thread = StartThread(MMDevApiProc, device);
                if(!data->thread)
                {
                    if(data->render)
                        IAudioRenderClient_Release(data->render);
                    data->render = NULL;
                    IAudioClient_Stop(data->client);
                    ERR("Failed to start thread\n");
                    hr = E_FAIL;
                }
            }

            req->result = hr;
            SetEvent(req->FinishedEvt);
            continue;

        case WM_USER_StopDevice:
            req = (ThreadRequest*)msg.wParam;
            device = (ALCdevice*)msg.lParam;
            data = device->ExtraData;

            if(data->thread)
            {
                data->killNow = 1;
                StopThread(data->thread);
                data->thread = NULL;

                data->killNow = 0;

                IAudioRenderClient_Release(data->render);
                data->render = NULL;
                IAudioClient_Stop(data->client);
            }

            req->result = S_OK;
            SetEvent(req->FinishedEvt);
            continue;

        case WM_USER_CloseDevice:
            req = (ThreadRequest*)msg.wParam;
            device = (ALCdevice*)msg.lParam;
            data = device->ExtraData;

            IAudioClient_Release(data->client);
            data->client = NULL;

            IMMDevice_Release(data->mmdev);
            data->mmdev = NULL;

            if(--deviceCount == 0)
                CoUninitialize();

            req->result = S_OK;
            SetEvent(req->FinishedEvt);
            continue;

        case WM_USER_Enumerate:
            req = (ThreadRequest*)msg.wParam;

            hr = cohr = S_OK;
            if(++deviceCount == 1)
                hr = cohr = CoInitialize(NULL);
            if(SUCCEEDED(hr))
                hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
            if(SUCCEEDED(hr))
            {
                EDataFlow flowdir;
                DevMap **devlist;
                ALuint *numdevs;
                ALuint i;

                Enumerator = ptr;
                if(msg.lParam == CAPTURE_DEVICE_PROBE)
                {
                    flowdir = eCapture;
                    devlist = &CaptureDeviceList;
                    numdevs = &NumCaptureDevices;
                }
                else
                {
                    flowdir = eRender;
                    devlist = &PlaybackDeviceList;
                    numdevs = &NumPlaybackDevices;
                }

                for(i = 0;i < *numdevs;i++)
                {
                    free((*devlist)[i].name);
                    free((*devlist)[i].devid);
                }
                free(*devlist);
                *devlist = NULL;
                *numdevs = 0;

                *devlist = ProbeDevices(Enumerator, flowdir, numdevs);

                IMMDeviceEnumerator_Release(Enumerator);
                Enumerator = NULL;
            }

            if(--deviceCount == 0 && SUCCEEDED(cohr))
                CoUninitialize();

            req->result = S_OK;
            SetEvent(req->FinishedEvt);
            continue;

        default:
            ERR("Unexpected message: %u\n", msg.message);
            continue;
        }
    }
    TRACE("Message loop finished\n");

    return 0;
}