dsound_main.c 23.2 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*  			DirectSound
2
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
3
 * Copyright 1998 Marcus Meissner
4
 * Copyright 1998 Rob Riggs
5
 * Copyright 2000-2002 TransGaming Technologies, Inc.
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20
 *
21 22
 * Most thread locking is complete. There may be a few race
 * conditions still lurking.
Alexandre Julliard's avatar
Alexandre Julliard committed
23
 *
24 25 26
 * TODO:
 *	Implement SetCooperativeLevel properly (need to address focus issues)
 *	Implement DirectSound3DBuffers (stubs in place)
27
 *	Use hardware 3D support if available
28
 *      Add critical section locking inside Release and AddRef methods
29 30
 *      Handle static buffers - put those in hardware, non-static not in hardware
 *      Hardware DuplicateSoundBuffer
31 32
 *      Proper volume calculation for 3d buffers
 *      Remove DS_HEL_FRAGS and use mixer fragment length for it
Alexandre Julliard's avatar
Alexandre Julliard committed
33 34
 */

35
#include <stdarg.h>
36

37
#define COBJMACROS
38
#define NONAMELESSUNION
39

40
#include "windef.h"
41
#include "winbase.h"
42
#include "winuser.h"
Robert Reif's avatar
Robert Reif committed
43
#include "winnls.h"
44
#include "winreg.h"
45
#include "mmsystem.h"
46
#include "winternl.h"
47
#include "mmddk.h"
48
#include "wine/debug.h"
49
#include "dsound.h"
50
#include "dsconf.h"
51
#include "ks.h"
52
#include "rpcproxy.h"
53 54 55 56 57
#include "rpc.h"
#include "rpcndr.h"
#include "unknwn.h"
#include "oleidl.h"
#include "shobjidl.h"
58
#include "strmif.h"
59

60
#include "initguid.h"
61
#include "ksmedia.h"
62 63
#include "propkey.h"
#include "devpkey.h"
64

65
#include "dsound_private.h"
66

67
WINE_DEFAULT_DEBUG_CHANNEL(dsound);
68

69 70
struct list DSOUND_renderers = LIST_INIT(DSOUND_renderers);
CRITICAL_SECTION DSOUND_renderers_lock;
71 72 73 74 75 76 77
static CRITICAL_SECTION_DEBUG DSOUND_renderers_lock_debug =
{
    0, 0, &DSOUND_renderers_lock,
    { &DSOUND_renderers_lock_debug.ProcessLocksList, &DSOUND_renderers_lock_debug.ProcessLocksList },
      0, 0, { (DWORD_PTR)(__FILE__ ": DSOUND_renderers_lock") }
};
CRITICAL_SECTION DSOUND_renderers_lock = { &DSOUND_renderers_lock_debug, -1, 0, 0, 0, 0 };
78

79 80
struct list DSOUND_capturers = LIST_INIT(DSOUND_capturers);
CRITICAL_SECTION DSOUND_capturers_lock;
81 82 83 84 85 86 87
static CRITICAL_SECTION_DEBUG DSOUND_capturers_lock_debug =
{
    0, 0, &DSOUND_capturers_lock,
    { &DSOUND_capturers_lock_debug.ProcessLocksList, &DSOUND_capturers_lock_debug.ProcessLocksList },
      0, 0, { (DWORD_PTR)(__FILE__ ": DSOUND_capturers_lock") }
};
CRITICAL_SECTION DSOUND_capturers_lock = { &DSOUND_capturers_lock_debug, -1, 0, 0, 0, 0 };
88

89 90
GUID                    DSOUND_renderer_guids[MAXWAVEDRIVERS];
GUID                    DSOUND_capture_guids[MAXWAVEDRIVERS];
91

92 93
WCHAR wine_vxd_drv[] = { 'w','i','n','e','m','m','.','v','x','d', 0 };

94
/* All default settings, you most likely don't want to touch these, see wiki on UsefulRegistryKeys */
95
int ds_hel_buflen = 32768 * 2;
96
int ds_snd_queue_max = 10;
97
static HINSTANCE instance;
98

99 100 101 102
/*
 * Get a config key from either the app-specific or the default config
 */

103
static inline DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
104 105
                                    char *buffer, DWORD size )
{
106 107
    if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
    if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
108
    return ERROR_FILE_NOT_FOUND;
109 110 111 112 113 114 115
}


/*
 * Setup the dsound options.
 */

116
void setup_dsound_options(void)
117
{
118
    char buffer[MAX_PATH+16];
119
    HKEY hkey, appkey = 0;
120
    DWORD len;
121

122
    buffer[MAX_PATH]='\0';
123

124 125
    /* @@ Wine registry key: HKCU\Software\Wine\DirectSound */
    if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\DirectSound", &hkey )) hkey = 0;
126

127 128
    len = GetModuleFileNameA( 0, buffer, MAX_PATH );
    if (len && len < MAX_PATH)
129 130
    {
        HKEY tmpkey;
131 132
        /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DirectSound */
        if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
133
        {
134 135 136 137
            char *p, *appname = buffer;
            if ((p = strrchr( appname, '/' ))) appname = p + 1;
            if ((p = strrchr( appname, '\\' ))) appname = p + 1;
            strcat( appname, "\\DirectSound" );
138
            TRACE("appname = [%s]\n", appname);
139 140
            if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
            RegCloseKey( tmpkey );
141 142 143 144 145
        }
    }

    /* get options */

146 147 148
    if (!get_config_key( hkey, appkey, "HelBuflen", buffer, MAX_PATH ))
        ds_hel_buflen = atoi(buffer);

149 150 151
    if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
        ds_snd_queue_max = atoi(buffer);

152

153
    if (appkey) RegCloseKey( appkey );
154
    if (hkey) RegCloseKey( hkey );
155

156 157
    TRACE("ds_hel_buflen = %d\n", ds_hel_buflen);
    TRACE("ds_snd_queue_max = %d\n", ds_snd_queue_max);
158 159
}

160
static const char * get_device_id(LPCGUID pGuid)
161 162 163 164 165 166 167 168 169 170 171
{
    if (IsEqualGUID(&DSDEVID_DefaultPlayback, pGuid))
        return "DSDEVID_DefaultPlayback";
    else if (IsEqualGUID(&DSDEVID_DefaultVoicePlayback, pGuid))
        return "DSDEVID_DefaultVoicePlayback";
    else if (IsEqualGUID(&DSDEVID_DefaultCapture, pGuid))
        return "DSDEVID_DefaultCapture";
    else if (IsEqualGUID(&DSDEVID_DefaultVoiceCapture, pGuid))
        return "DSDEVID_DefaultVoiceCapture";
    return debugstr_guid(pGuid);
}
172

173
static HRESULT get_mmdevenum(IMMDeviceEnumerator **devenum)
174
{
175
    HRESULT hr, init_hr;
176

177
    init_hr = CoInitialize(NULL);
178 179

    hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
180
            CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)devenum);
181
    if(FAILED(hr)){
182 183
        if(SUCCEEDED(init_hr))
            CoUninitialize();
184 185 186
        *devenum = NULL;
        ERR("CoCreateInstance failed: %08x\n", hr);
        return hr;
187 188
    }

189
    return init_hr;
190 191
}

192
static void release_mmdevenum(IMMDeviceEnumerator *devenum, HRESULT init_hr)
193
{
194 195 196
    IMMDeviceEnumerator_Release(devenum);
    if(SUCCEEDED(init_hr))
        CoUninitialize();
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
}

static HRESULT get_mmdevice_guid(IMMDevice *device, IPropertyStore *ps,
        GUID *guid)
{
    PROPVARIANT pv;
    HRESULT hr;

    if(!ps){
        hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
        if(FAILED(hr)){
            WARN("OpenPropertyStore failed: %08x\n", hr);
            return hr;
        }
    }else
        IPropertyStore_AddRef(ps);

    PropVariantInit(&pv);

    hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_GUID, &pv);
    if(FAILED(hr)){
        IPropertyStore_Release(ps);
        WARN("GetValue(GUID) failed: %08x\n", hr);
        return hr;
    }

    CLSIDFromString(pv.u.pwszVal, guid);

    PropVariantClear(&pv);
    IPropertyStore_Release(ps);

    return S_OK;
}

231
/***************************************************************************
232
 * GetDeviceID	[DSOUND.9]
233 234 235
 *
 * Retrieves unique identifier of default device specified
 *
236 237 238 239
 * PARAMS
 *    pGuidSrc  [I] Address of device GUID.
 *    pGuidDest [O] Address to receive unique device GUID.
 *
240 241 242
 * RETURNS
 *    Success: DS_OK
 *    Failure: DSERR_INVALIDPARAM
243 244 245
 *
 * NOTES
 *    pGuidSrc is a valid device GUID or DSDEVID_DefaultPlayback,
246
 *    DSDEVID_DefaultCapture, DSDEVID_DefaultVoicePlayback, or
247 248 249
 *    DSDEVID_DefaultVoiceCapture.
 *    Returns pGuidSrc if pGuidSrc is a valid device or the device
 *    GUID for the specified constants.
250 251 252
 */
HRESULT WINAPI GetDeviceID(LPCGUID pGuidSrc, LPGUID pGuidDest)
{
253 254 255
    IMMDeviceEnumerator *devenum;
    EDataFlow flow = (EDataFlow)-1;
    ERole role = (ERole)-1;
256
    HRESULT hr, init_hr;
257

258
    TRACE("(%s,%p)\n", get_device_id(pGuidSrc),pGuidDest);
259

260 261
    if(!pGuidSrc || !pGuidDest)
        return DSERR_INVALIDPARAM;
262

263
    init_hr = get_mmdevenum(&devenum);
264
    if(!devenum)
265
        return init_hr;
266 267 268 269 270 271 272 273 274 275 276 277 278

    if(IsEqualGUID(&DSDEVID_DefaultPlayback, pGuidSrc)){
        role = eMultimedia;
        flow = eRender;
    }else if(IsEqualGUID(&DSDEVID_DefaultVoicePlayback, pGuidSrc)){
        role = eCommunications;
        flow = eRender;
    }else if(IsEqualGUID(&DSDEVID_DefaultCapture, pGuidSrc)){
        role = eMultimedia;
        flow = eCapture;
    }else if(IsEqualGUID(&DSDEVID_DefaultVoiceCapture, pGuidSrc)){
        role = eCommunications;
        flow = eCapture;
279 280
    }

281 282 283 284 285 286 287
    if(role != (ERole)-1 && flow != (EDataFlow)-1){
        IMMDevice *device;

        hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum,
                flow, role, &device);
        if(FAILED(hr)){
            WARN("GetDefaultAudioEndpoint failed: %08x\n", hr);
288
            release_mmdevenum(devenum, init_hr);
289 290
            return DSERR_NODRIVER;
        }
291

292 293 294
        hr = get_mmdevice_guid(device, NULL, pGuidDest);
        IMMDevice_Release(device);

295 296
        release_mmdevenum(devenum, init_hr);

297
        return (hr == S_OK) ? DS_OK : hr;
298 299
    }

300 301
    release_mmdevenum(devenum, init_hr);

302
    *pGuidDest = *pGuidSrc;
303 304 305 306

    return DS_OK;
}

307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
struct morecontext
{
    LPDSENUMCALLBACKA callA;
    LPVOID data;
};

static BOOL CALLBACK a_to_w_callback(LPGUID guid, LPCWSTR descW, LPCWSTR modW, LPVOID data)
{
    struct morecontext *context = data;
    char descA[MAXPNAMELEN], modA[MAXPNAMELEN];

    WideCharToMultiByte(CP_ACP, 0, descW, -1, descA, sizeof(descA), NULL, NULL);
    WideCharToMultiByte(CP_ACP, 0, modW, -1, modA, sizeof(modA), NULL, NULL);

    return context->callA(guid, descA, modA, context->data);
}
323

324
/***************************************************************************
325
 * DirectSoundEnumerateA [DSOUND.2]
326 327 328
 *
 * Enumerate all DirectSound drivers installed in the system
 *
329 330 331 332
 * PARAMS
 *    lpDSEnumCallback  [I] Address of callback function.
 *    lpContext         [I] Address of user defined context passed to callback function.
 *
333 334 335
 * RETURNS
 *    Success: DS_OK
 *    Failure: DSERR_INVALIDPARAM
336
 */
337
HRESULT WINAPI DirectSoundEnumerateA(
338 339
    LPDSENUMCALLBACKA lpDSEnumCallback,
    LPVOID lpContext)
340
{
341
    struct morecontext context;
342 343

    if (lpDSEnumCallback == NULL) {
344 345
        WARN("invalid parameter: lpDSEnumCallback == NULL\n");
        return DSERR_INVALIDPARAM;
346 347
    }

348 349
    context.callA = lpDSEnumCallback;
    context.data = lpContext;
350

351
    return DirectSoundEnumerateW(a_to_w_callback, &context);
Alexandre Julliard's avatar
Alexandre Julliard committed
352 353
}

354 355 356 357 358
HRESULT get_mmdevice(EDataFlow flow, const GUID *tgt, IMMDevice **device)
{
    IMMDeviceEnumerator *devenum;
    IMMDeviceCollection *coll;
    UINT count, i;
359
    HRESULT hr, init_hr;
360

361
    init_hr = get_mmdevenum(&devenum);
362
    if(!devenum)
363
        return init_hr;
364 365 366 367 368

    hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flow,
            DEVICE_STATE_ACTIVE, &coll);
    if(FAILED(hr)){
        WARN("EnumAudioEndpoints failed: %08x\n", hr);
369
        release_mmdevenum(devenum, init_hr);
370 371 372 373 374 375
        return hr;
    }

    hr = IMMDeviceCollection_GetCount(coll, &count);
    if(FAILED(hr)){
        IMMDeviceCollection_Release(coll);
376
        release_mmdevenum(devenum, init_hr);
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
        WARN("GetCount failed: %08x\n", hr);
        return hr;
    }

    for(i = 0; i < count; ++i){
        GUID guid;

        hr = IMMDeviceCollection_Item(coll, i, device);
        if(FAILED(hr))
            continue;

        hr = get_mmdevice_guid(*device, NULL, &guid);
        if(FAILED(hr)){
            IMMDevice_Release(*device);
            continue;
        }

394 395 396
        if(IsEqualGUID(&guid, tgt)){
            IMMDeviceCollection_Release(coll);
            release_mmdevenum(devenum, init_hr);
397
            return DS_OK;
398
        }
399 400 401 402 403 404

        IMMDevice_Release(*device);
    }

    WARN("No device with GUID %s found!\n", wine_dbgstr_guid(tgt));

405 406 407
    IMMDeviceCollection_Release(coll);
    release_mmdevenum(devenum, init_hr);

408 409 410
    return DSERR_INVALIDPARAM;
}

411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
static BOOL send_device(IMMDevice *device, GUID *guid,
        LPDSENUMCALLBACKW cb, void *user)
{
    IPropertyStore *ps;
    PROPVARIANT pv;
    BOOL keep_going;
    HRESULT hr;

    PropVariantInit(&pv);

    hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
    if(FAILED(hr)){
        WARN("OpenPropertyStore failed: %08x\n", hr);
        return TRUE;
    }

    hr = get_mmdevice_guid(device, ps, guid);
    if(FAILED(hr)){
        IPropertyStore_Release(ps);
        return TRUE;
    }

    hr = IPropertyStore_GetValue(ps,
            (const PROPERTYKEY *)&DEVPKEY_Device_FriendlyName, &pv);
    if(FAILED(hr)){
        IPropertyStore_Release(ps);
        WARN("GetValue(FriendlyName) failed: %08x\n", hr);
        return TRUE;
    }

    TRACE("Calling back with %s (%s)\n", wine_dbgstr_guid(guid),
            wine_dbgstr_w(pv.u.pwszVal));

    keep_going = cb(guid, pv.u.pwszVal, wine_vxd_drv, user);

    PropVariantClear(&pv);
    IPropertyStore_Release(ps);

    return keep_going;
}

452 453 454
/* S_FALSE means the callback returned FALSE at some point
 * S_OK means the callback always returned TRUE */
HRESULT enumerate_mmdevices(EDataFlow flow, GUID *guids,
455 456 457 458
        LPDSENUMCALLBACKW cb, void *user)
{
    IMMDeviceEnumerator *devenum;
    IMMDeviceCollection *coll;
459 460
    IMMDevice *defdev = NULL;
    UINT count, i, n;
461
    BOOL keep_going;
462
    HRESULT hr, init_hr;
463 464 465 466 467

    static const WCHAR primary_desc[] = {'P','r','i','m','a','r','y',' ',
        'S','o','u','n','d',' ','D','r','i','v','e','r',0};
    static const WCHAR empty_drv[] = {0};

468
    init_hr = get_mmdevenum(&devenum);
469
    if(!devenum)
470
        return init_hr;
471

472
    hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flow,
473 474
            DEVICE_STATE_ACTIVE, &coll);
    if(FAILED(hr)){
475
        release_mmdevenum(devenum, init_hr);
476 477 478 479 480 481 482
        WARN("EnumAudioEndpoints failed: %08x\n", hr);
        return DS_OK;
    }

    hr = IMMDeviceCollection_GetCount(coll, &count);
    if(FAILED(hr)){
        IMMDeviceCollection_Release(coll);
483
        release_mmdevenum(devenum, init_hr);
484 485 486 487
        WARN("GetCount failed: %08x\n", hr);
        return DS_OK;
    }

488 489
    if(count == 0){
        release_mmdevenum(devenum, init_hr);
490
        return DS_OK;
491
    }
492

493
    TRACE("Calling back with NULL (%s)\n", wine_dbgstr_w(primary_desc));
494 495
    keep_going = cb(NULL, primary_desc, empty_drv, user);

496 497 498 499 500 501 502 503 504 505 506 507 508
    /* always send the default device first */
    if(keep_going){
        hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flow,
                eMultimedia, &defdev);
        if(FAILED(hr)){
            defdev = NULL;
            n = 0;
        }else{
            keep_going = send_device(defdev, &guids[0], cb, user);
            n = 1;
        }
    }

509 510 511 512 513 514 515 516 517
    for(i = 0; keep_going && i < count; ++i){
        IMMDevice *device;

        hr = IMMDeviceCollection_Item(coll, i, &device);
        if(FAILED(hr)){
            WARN("Item failed: %08x\n", hr);
            continue;
        }

518 519 520
        if(device != defdev){
            send_device(device, &guids[n], cb, user);
            ++n;
521 522 523 524 525
        }

        IMMDevice_Release(device);
    }

526 527
    if(defdev)
        IMMDevice_Release(defdev);
528 529
    IMMDeviceCollection_Release(coll);

530 531
    release_mmdevenum(devenum, init_hr);

532
    return keep_going ? S_OK : S_FALSE;
533 534
}

535
/***************************************************************************
536
 * DirectSoundEnumerateW [DSOUND.3]
537 538 539
 *
 * Enumerate all DirectSound drivers installed in the system
 *
540 541 542 543
 * PARAMS
 *    lpDSEnumCallback  [I] Address of callback function.
 *    lpContext         [I] Address of user defined context passed to callback function.
 *
544 545 546 547 548
 * RETURNS
 *    Success: DS_OK
 *    Failure: DSERR_INVALIDPARAM
 */
HRESULT WINAPI DirectSoundEnumerateW(
549
	LPDSENUMCALLBACKW lpDSEnumCallback,
550 551
	LPVOID lpContext )
{
552 553
    HRESULT hr;

554
    TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext);
555 556

    if (lpDSEnumCallback == NULL) {
557 558
        WARN("invalid parameter: lpDSEnumCallback == NULL\n");
        return DSERR_INVALIDPARAM;
559 560
    }

561 562
    setup_dsound_options();

563
    hr = enumerate_mmdevices(eRender, DSOUND_renderer_guids,
564
            lpDSEnumCallback, lpContext);
565
    return SUCCEEDED(hr) ? DS_OK : hr;
566 567
}

568 569 570 571 572 573 574 575 576 577 578 579 580
/***************************************************************************
 * DirectSoundCaptureEnumerateA [DSOUND.7]
 *
 * Enumerate all DirectSound drivers installed in the system.
 *
 * PARAMS
 *    lpDSEnumCallback  [I] Address of callback function.
 *    lpContext         [I] Address of user defined context passed to callback function.
 *
 * RETURNS
 *    Success: DS_OK
 *    Failure: DSERR_INVALIDPARAM
 */
581
HRESULT WINAPI DirectSoundCaptureEnumerateA(
582 583 584
    LPDSENUMCALLBACKA lpDSEnumCallback,
    LPVOID lpContext)
{
585
    struct morecontext context;
586 587

    if (lpDSEnumCallback == NULL) {
588
        WARN("invalid parameter: lpDSEnumCallback == NULL\n");
589 590 591
        return DSERR_INVALIDPARAM;
    }

592 593
    context.callA = lpDSEnumCallback;
    context.data = lpContext;
594

595
    return DirectSoundCaptureEnumerateW(a_to_w_callback, &context);
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
}

/***************************************************************************
 * DirectSoundCaptureEnumerateW [DSOUND.8]
 *
 * Enumerate all DirectSound drivers installed in the system.
 *
 * PARAMS
 *    lpDSEnumCallback  [I] Address of callback function.
 *    lpContext         [I] Address of user defined context passed to callback function.
 *
 * RETURNS
 *    Success: DS_OK
 *    Failure: DSERR_INVALIDPARAM
 */
HRESULT WINAPI
DirectSoundCaptureEnumerateW(
    LPDSENUMCALLBACKW lpDSEnumCallback,
    LPVOID lpContext)
{
616 617
    HRESULT hr;

618 619 620
    TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext );

    if (lpDSEnumCallback == NULL) {
621
        WARN("invalid parameter: lpDSEnumCallback == NULL\n");
622 623 624
        return DSERR_INVALIDPARAM;
    }

625 626
    setup_dsound_options();

627
    hr = enumerate_mmdevices(eCapture, DSOUND_capture_guids,
628
            lpDSEnumCallback, lpContext);
629
    return SUCCEEDED(hr) ? DS_OK : hr;
630 631
}

632 633 634
/*******************************************************************************
 * DirectSound ClassFactory
 */
635

636 637 638
typedef  HRESULT (*FnCreateInstance)(REFIID riid, LPVOID *ppobj);
 
typedef struct {
639
    IClassFactory IClassFactory_iface;
640 641 642
    REFCLSID rclsid;
    FnCreateInstance pfnCreateInstance;
} IClassFactoryImpl;
643

644 645 646 647 648
static inline IClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface)
{
    return CONTAINING_RECORD(iface, IClassFactoryImpl, IClassFactory_iface);
}

649
static HRESULT WINAPI
650
DSCF_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppobj)
651
{
652
    IClassFactoryImpl *This = impl_from_IClassFactory(iface);
653
    TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj);
654 655
    if (ppobj == NULL)
        return E_POINTER;
656 657 658 659
    if (IsEqualIID(riid, &IID_IUnknown) ||
        IsEqualIID(riid, &IID_IClassFactory))
    {
        *ppobj = iface;
660
        IClassFactory_AddRef(iface);
661 662 663
        return S_OK;
    }
    *ppobj = NULL;
664
    return E_NOINTERFACE;
665 666
}

667 668
static ULONG WINAPI DSCF_AddRef(LPCLASSFACTORY iface)
{
669
    return 2;
670 671
}

672 673 674
static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface)
{
    /* static class, won't be freed */
675
    return 1;
676 677 678
}

static HRESULT WINAPI DSCF_CreateInstance(
679 680 681 682
    LPCLASSFACTORY iface,
    LPUNKNOWN pOuter,
    REFIID riid,
    LPVOID *ppobj)
683
{
684
    IClassFactoryImpl *This = impl_from_IClassFactory(iface);
685
    TRACE("(%p, %p, %s, %p)\n", This, pOuter, debugstr_guid(riid), ppobj);
686

687 688
    if (pOuter)
        return CLASS_E_NOAGGREGATION;
689

690 691 692 693 694 695
    if (ppobj == NULL) {
        WARN("invalid parameter\n");
        return DSERR_INVALIDPARAM;
    }
    *ppobj = NULL;
    return This->pfnCreateInstance(riid, ppobj);
696
}
697 698 699
 
static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface, BOOL dolock)
{
700
    IClassFactoryImpl *This = impl_from_IClassFactory(iface);
701 702
    FIXME("(%p, %d) stub!\n", This, dolock);
    return S_OK;
703 704
}

705 706 707 708 709 710
static const IClassFactoryVtbl DSCF_Vtbl = {
    DSCF_QueryInterface,
    DSCF_AddRef,
    DSCF_Release,
    DSCF_CreateInstance,
    DSCF_LockServer
711 712
};

713
static IClassFactoryImpl DSOUND_CF[] = {
714 715
    { { &DSCF_Vtbl }, &CLSID_DirectSound, DSOUND_Create },
    { { &DSCF_Vtbl }, &CLSID_DirectSound8, DSOUND_Create8 },
716 717
    { { &DSCF_Vtbl }, &CLSID_DirectSoundCapture, DSOUND_CaptureCreate },
    { { &DSCF_Vtbl }, &CLSID_DirectSoundCapture8, DSOUND_CaptureCreate8 },
718
    { { &DSCF_Vtbl }, &CLSID_DirectSoundFullDuplex, DSOUND_FullDuplexCreate },
719
    { { &DSCF_Vtbl }, &CLSID_DirectSoundPrivate, IKsPrivatePropertySetImpl_Create },
720
    { { NULL }, NULL, NULL }
721
};
Alexandre Julliard's avatar
Alexandre Julliard committed
722 723

/*******************************************************************************
724
 * DllGetClassObject [DSOUND.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
 * Retrieves class object from a DLL object
 *
 * NOTES
 *    Docs say returns STDAPI
 *
 * PARAMS
 *    rclsid [I] CLSID for the class object
 *    riid   [I] Reference to identifier of interface for class object
 *    ppv    [O] Address of variable to receive interface pointer for riid
 *
 * RETURNS
 *    Success: S_OK
 *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
 *             E_UNEXPECTED
 */
740
HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
Alexandre Julliard's avatar
Alexandre Julliard committed
741
{
742 743
    int i = 0;
    TRACE("(%s, %s, %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
744 745

    if (ppv == NULL) {
746 747
        WARN("invalid parameter\n");
        return E_INVALIDARG;
748 749 750 751
    }

    *ppv = NULL;

752 753 754 755
    if (!IsEqualIID(riid, &IID_IClassFactory) &&
        !IsEqualIID(riid, &IID_IUnknown)) {
        WARN("no interface for %s\n", debugstr_guid(riid));
        return E_NOINTERFACE;
756
    }
757

758 759
    while (NULL != DSOUND_CF[i].rclsid) {
        if (IsEqualGUID(rclsid, DSOUND_CF[i].rclsid)) {
760
            DSCF_AddRef(&DSOUND_CF[i].IClassFactory_iface);
761 762 763 764
            *ppv = &DSOUND_CF[i];
            return S_OK;
        }
        i++;
765
    }
766

767 768
    WARN("(%s, %s, %p): no class found.\n", debugstr_guid(rclsid),
         debugstr_guid(riid), ppv);
769
    return CLASS_E_CLASSNOTAVAILABLE;
Alexandre Julliard's avatar
Alexandre Julliard committed
770 771 772 773
}


/*******************************************************************************
774
 * DllCanUnloadNow [DSOUND.4]
775
 * Determines whether the DLL is in use.
Alexandre Julliard's avatar
Alexandre Julliard committed
776 777
 *
 * RETURNS
778 779
 *    Can unload now: S_OK
 *    Cannot unload now (the DLL is still active): S_FALSE
Alexandre Julliard's avatar
Alexandre Julliard committed
780
 */
781
HRESULT WINAPI DllCanUnloadNow(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
782 783 784
{
    return S_FALSE;
}
785 786 787 788 789 790 791 792 793 794 795 796

#define INIT_GUID(guid, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8)      \
        guid.Data1 = l; guid.Data2 = w1; guid.Data3 = w2;               \
        guid.Data4[0] = b1; guid.Data4[1] = b2; guid.Data4[2] = b3;     \
        guid.Data4[3] = b4; guid.Data4[4] = b5; guid.Data4[5] = b6;     \
        guid.Data4[6] = b7; guid.Data4[7] = b8;

/***********************************************************************
 *           DllMain (DSOUND.init)
 */
BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
797
    TRACE("(%p %d %p)\n", hInstDLL, fdwReason, lpvReserved);
798 799 800

    switch (fdwReason) {
    case DLL_PROCESS_ATTACH:
801
        instance = hInstDLL;
802 803 804
        DisableThreadLibraryCalls(hInstDLL);
        /* Increase refcount on dsound by 1 */
        GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)hInstDLL, &hInstDLL);
805 806
        break;
    case DLL_PROCESS_DETACH:
807
        if (lpvReserved) break;
808 809
        DeleteCriticalSection(&DSOUND_renderers_lock);
        DeleteCriticalSection(&DSOUND_capturers_lock);
810 811 812 813
        break;
    }
    return TRUE;
}
814 815 816 817 818 819

/***********************************************************************
 *		DllRegisterServer (DSOUND.@)
 */
HRESULT WINAPI DllRegisterServer(void)
{
820
    return __wine_register_resources( instance );
821 822 823 824 825 826 827
}

/***********************************************************************
 *		DllUnregisterServer (DSOUND.@)
 */
HRESULT WINAPI DllUnregisterServer(void)
{
828
    return __wine_unregister_resources( instance );
829
}