ds3d.c 51.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * Tests the panning and 3D functions of DirectSound
 *
 * Part of this test involves playing test tones. But this only makes
 * sense if someone is going to carefully listen to it, and would only
 * bother everyone else.
 * So this is only done if the test is being run in interactive mode.
 *
 * Copyright (c) 2002-2004 Francois Gouget
 *
 * 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
23
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 25 26 27 28 29 30 31 32 33
 */

#define NONAMELESSSTRUCT
#define NONAMELESSUNION
#include <windows.h>

#include <math.h>

#include "wine/test.h"
#include "dsound.h"
34
#include "dxerr8.h"
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

#include "dsound_test.h"

#define PI 3.14159265358979323846
char* wave_generate_la(WAVEFORMATEX* wfx, double duration, DWORD* size)
{
    int i;
    int nb_samples;
    char* buf;
    char* b;

    nb_samples=(int)(duration*wfx->nSamplesPerSec);
    *size=nb_samples*wfx->nBlockAlign;
    b=buf=malloc(*size);
    for (i=0;i<nb_samples;i++) {
        double y=sin(440.0*2*PI*i/wfx->nSamplesPerSec);
        if (wfx->wBitsPerSample==8) {
            unsigned char sample=(unsigned char)((double)127.5*(y+1.0));
            *b++=sample;
            if (wfx->nChannels==2)
                *b++=sample;
        } else {
            signed short sample=(signed short)((double)32767.5*y-0.5);
            b[0]=sample & 0xff;
            b[1]=sample >> 8;
            b+=2;
            if (wfx->nChannels==2) {
                b[0]=sample & 0xff;
                b[1]=sample >> 8;
                b+=2;
            }
        }
    }
    return buf;
}

71 72 73
const char * getDSBCAPS(DWORD xmask) {
    static struct {
        DWORD   mask;
74
        const char    *name;
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
    } flags[] = {
#define FE(x) { x, #x },
        FE(DSBCAPS_PRIMARYBUFFER)
        FE(DSBCAPS_STATIC)
        FE(DSBCAPS_LOCHARDWARE)
        FE(DSBCAPS_LOCSOFTWARE)
        FE(DSBCAPS_CTRL3D)
        FE(DSBCAPS_CTRLFREQUENCY)
        FE(DSBCAPS_CTRLPAN)
        FE(DSBCAPS_CTRLVOLUME)
        FE(DSBCAPS_CTRLPOSITIONNOTIFY)
        FE(DSBCAPS_STICKYFOCUS)
        FE(DSBCAPS_GLOBALFOCUS)
        FE(DSBCAPS_GETCURRENTPOSITION2)
        FE(DSBCAPS_MUTE3DATMAXDISTANCE)
#undef FE
    };
    static char buffer[512];
93
    unsigned int i;
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
    BOOL first = TRUE;

    buffer[0] = 0;

    for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++) {
        if ((flags[i].mask & xmask) == flags[i].mask) {
            if (first)
                first = FALSE;
            else
                strcat(buffer, "|");
            strcat(buffer, flags[i].name);
        }
    }

    return buffer;
}

111 112 113 114 115 116 117 118
HWND get_hwnd()
{
    HWND hwnd=GetForegroundWindow();
    if (!hwnd)
        hwnd=GetDesktopWindow();
    return hwnd;
}

119 120
void init_format(WAVEFORMATEX* wfx, int format, int rate, int depth,
                 int channels)
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
{
    wfx->wFormatTag=format;
    wfx->nChannels=channels;
    wfx->wBitsPerSample=depth;
    wfx->nSamplesPerSec=rate;
    wfx->nBlockAlign=wfx->nChannels*wfx->wBitsPerSample/8;
    /* FIXME: Shouldn't this test be if (format!=WAVE_FORMAT_PCM) */
    if (wfx->nBlockAlign==0)
    {
        /* align compressed formats to byte boundary */
        wfx->nBlockAlign=1;
    }
    wfx->nAvgBytesPerSec=wfx->nSamplesPerSec*wfx->nBlockAlign;
    wfx->cbSize=0;
}

typedef struct {
    char* wave;
    DWORD wave_len;

    LPDIRECTSOUNDBUFFER dsbo;
    LPWAVEFORMATEX wfx;
    DWORD buffer_size;
    DWORD written;
145
    DWORD played;
146 147 148 149 150 151 152 153 154 155 156 157 158 159
    DWORD offset;
} play_state_t;

static int buffer_refill(play_state_t* state, DWORD size)
{
    LPVOID ptr1,ptr2;
    DWORD len1,len2;
    HRESULT rc;

    if (size>state->wave_len-state->written)
        size=state->wave_len-state->written;

    rc=IDirectSoundBuffer_Lock(state->dsbo,state->offset,size,
                               &ptr1,&len1,&ptr2,&len2,0);
160 161
    ok(rc==DS_OK,"IDirectSoundBuffer_Lock() failed: %s\n",
       DXGetErrorString8(rc));
162 163 164 165 166 167 168 169 170 171 172
    if (rc!=DS_OK)
        return -1;

    memcpy(ptr1,state->wave+state->written,len1);
    state->written+=len1;
    if (ptr2!=NULL) {
        memcpy(ptr2,state->wave+state->written,len2);
        state->written+=len2;
    }
    state->offset=state->written % state->buffer_size;
    rc=IDirectSoundBuffer_Unlock(state->dsbo,ptr1,len1,ptr2,len2);
173 174
    ok(rc==DS_OK,"IDirectSoundBuffer_Unlock() failed: %s\n",
       DXGetErrorString8(rc));
175 176 177 178 179 180 181 182 183 184 185 186 187 188
    if (rc!=DS_OK)
        return -1;
    return size;
}

static int buffer_silence(play_state_t* state, DWORD size)
{
    LPVOID ptr1,ptr2;
    DWORD len1,len2;
    HRESULT rc;
    BYTE s;

    rc=IDirectSoundBuffer_Lock(state->dsbo,state->offset,size,
                               &ptr1,&len1,&ptr2,&len2,0);
189 190
    ok(rc==DS_OK,"IDirectSoundBuffer_Lock() failed: %s\n",
       DXGetErrorString8(rc));
191 192 193 194 195 196 197 198 199 200
    if (rc!=DS_OK)
        return -1;

    s=(state->wfx->wBitsPerSample==8?0x80:0);
    memset(ptr1,s,len1);
    if (ptr2!=NULL) {
        memset(ptr2,s,len2);
    }
    state->offset=(state->offset+size) % state->buffer_size;
    rc=IDirectSoundBuffer_Unlock(state->dsbo,ptr1,len1,ptr2,len2);
201 202
    ok(rc==DS_OK,"IDirectSoundBuffer_Unlock() failed: %s\n",
       DXGetErrorString8(rc));
203 204 205 206 207 208 209
    if (rc!=DS_OK)
        return -1;
    return size;
}

static int buffer_service(play_state_t* state)
{
210
    DWORD last_play_pos,play_pos,buf_free;
211 212
    HRESULT rc;

213
    rc=IDirectSoundBuffer_GetCurrentPosition(state->dsbo,&play_pos,NULL);
214 215
    ok(rc==DS_OK,"IDirectSoundBuffer_GetCurrentPosition() failed: %s\n",
       DXGetErrorString8(rc));
216 217 218 219
    if (rc!=DS_OK) {
        goto STOP;
    }

220 221 222 223 224 225 226 227
    /* Update the amount played */
    last_play_pos=state->played % state->buffer_size;
    if (play_pos<last_play_pos)
        state->played+=state->buffer_size-last_play_pos+play_pos;
    else
        state->played+=play_pos-last_play_pos;

    if (winetest_debug > 1)
228
        trace("buf size=%d last_play_pos=%d play_pos=%d played=%d / %d\n",
229 230
              state->buffer_size,last_play_pos,play_pos,state->played,
              state->wave_len);
231 232 233 234 235 236 237

    if (state->played>state->wave_len)
    {
        /* Everything has been played */
        goto STOP;
    }

238
    /* Refill the buffer */
239
    if (state->offset<=play_pos)
240
        buf_free=play_pos-state->offset;
241
    else
242
        buf_free=state->buffer_size-state->offset+play_pos;
243

244
    if (winetest_debug > 1)
245
        trace("offset=%d free=%d written=%d / %d\n",
246
              state->offset,buf_free,state->written,state->wave_len);
247 248 249
    if (buf_free==0)
        return 1;

250 251
    if (state->written<state->wave_len)
    {
252 253 254 255
        int w=buffer_refill(state,buf_free);
        if (w==-1)
            goto STOP;
        buf_free-=w;
256
        if (state->written==state->wave_len && winetest_debug > 1)
257
            trace("last sound byte at %d\n",
258
                  (state->written % state->buffer_size));
259 260 261 262 263
    }

    if (buf_free>0) {
        /* Fill with silence */
        if (winetest_debug > 1)
264
            trace("writing %d bytes of silence\n",buf_free);
265 266 267 268 269 270 271 272 273
        if (buffer_silence(state,buf_free)==-1)
            goto STOP;
    }
    return 1;

STOP:
    if (winetest_debug > 1)
        trace("stopping playback\n");
    rc=IDirectSoundBuffer_Stop(state->dsbo);
274 275
    ok(rc==DS_OK,"IDirectSoundBuffer_Stop() failed: %s\n",
       DXGetErrorString8(rc));
276 277 278
    return 0;
}

279
void test_buffer(LPDIRECTSOUND dso, LPDIRECTSOUNDBUFFER *dsbo,
280 281 282
                 BOOL is_primary, BOOL set_volume, LONG volume,
                 BOOL set_pan, LONG pan, BOOL play, double duration,
                 BOOL buffer3d, LPDIRECTSOUND3DLISTENER listener,
283 284
                 BOOL move_listener, BOOL move_sound,
                 BOOL set_frequency, DWORD frequency)
285 286 287 288 289 290 291
{
    HRESULT rc;
    DSBCAPS dsbcaps;
    WAVEFORMATEX wfx,wfx2;
    DWORD size,status,freq;
    int ref;

292
    if (set_frequency) {
293
        rc=IDirectSoundBuffer_SetFrequency(*dsbo,frequency);
294 295 296 297 298 299 300
        ok(rc==DS_OK||rc==DSERR_CONTROLUNAVAIL,
           "IDirectSoundBuffer_SetFrequency() failed to set frequency "
           "%s\n",DXGetErrorString8(rc));
        if (rc!=DS_OK)
            return;
    }

301
    /* DSOUND: Error: Invalid caps pointer */
302
    rc=IDirectSoundBuffer_GetCaps(*dsbo,0);
303 304
    ok(rc==DSERR_INVALIDPARAM,"IDirectSoundBuffer_GetCaps() should have "
       "returned DSERR_INVALIDPARAM, returned: %s\n",DXGetErrorString8(rc));
305 306 307 308

    ZeroMemory(&dsbcaps, sizeof(dsbcaps));

    /* DSOUND: Error: Invalid caps pointer */
309
    rc=IDirectSoundBuffer_GetCaps(*dsbo,&dsbcaps);
310 311
    ok(rc==DSERR_INVALIDPARAM,"IDirectSoundBuffer_GetCaps() should have "
       "returned DSERR_INVALIDPARAM, returned: %s\n",DXGetErrorString8(rc));
312 313

    dsbcaps.dwSize=sizeof(dsbcaps);
314
    rc=IDirectSoundBuffer_GetCaps(*dsbo,&dsbcaps);
315 316 317
    ok(rc==DS_OK,"IDirectSoundBuffer_GetCaps() failed: %s\n",
       DXGetErrorString8(rc));
    if (rc==DS_OK && winetest_debug > 1) {
318
        trace("    Caps: flags=0x%08x size=%d\n",dsbcaps.dwFlags,
319 320 321 322 323
              dsbcaps.dwBufferBytes);
    }

    /* Query the format size. Note that it may not match sizeof(wfx) */
    size=0;
324
    rc=IDirectSoundBuffer_GetFormat(*dsbo,NULL,0,&size);
325
    ok(rc==DS_OK && size!=0,"IDirectSoundBuffer_GetFormat() should have "
326
       "returned the needed size: rc=%s size=%d\n",DXGetErrorString8(rc),size);
327

328
    rc=IDirectSoundBuffer_GetFormat(*dsbo,&wfx,sizeof(wfx),NULL);
329 330
    ok(rc==DS_OK,"IDirectSoundBuffer_GetFormat() failed: %s\n",
       DXGetErrorString8(rc));
331
    if (rc==DS_OK && winetest_debug > 1) {
332
        trace("    Format: %s tag=0x%04x %dx%dx%d avg.B/s=%d align=%d\n",
333
              is_primary ? "Primary" : "Secondary",
334 335 336 337 338
              wfx.wFormatTag,wfx.nSamplesPerSec,wfx.wBitsPerSample,
              wfx.nChannels,wfx.nAvgBytesPerSec,wfx.nBlockAlign);
    }

    /* DSOUND: Error: Invalid frequency buffer */
339
    rc=IDirectSoundBuffer_GetFrequency(*dsbo,0);
340 341
    ok(rc==DSERR_INVALIDPARAM,"IDirectSoundBuffer_GetFrequency() should have "
       "returned DSERR_INVALIDPARAM, returned: %s\n",DXGetErrorString8(rc));
342 343

    /* DSOUND: Error: Primary buffers don't support CTRLFREQUENCY */
344
    rc=IDirectSoundBuffer_GetFrequency(*dsbo,&freq);
345 346
    ok((rc==DS_OK && !is_primary) || (rc==DSERR_CONTROLUNAVAIL&&is_primary) ||
       (rc==DSERR_CONTROLUNAVAIL&&!(dsbcaps.dwFlags&DSBCAPS_CTRLFREQUENCY)),
347
       "IDirectSoundBuffer_GetFrequency() failed: %s\n",DXGetErrorString8(rc));
348
    if (rc==DS_OK) {
349 350
        DWORD f = set_frequency?frequency:wfx.nSamplesPerSec;
        ok(freq==f,"The frequency returned by GetFrequency "
351
           "%d does not match the format %d\n",freq,f);
352 353 354
    }

    /* DSOUND: Error: Invalid status pointer */
355
    rc=IDirectSoundBuffer_GetStatus(*dsbo,0);
356 357
    ok(rc==DSERR_INVALIDPARAM,"IDirectSoundBuffer_GetStatus() should have "
       "returned DSERR_INVALIDPARAM, returned: %s\n",DXGetErrorString8(rc));
358

359
    rc=IDirectSoundBuffer_GetStatus(*dsbo,&status);
360 361
    ok(rc==DS_OK,"IDirectSoundBuffer_GetStatus() failed: %s\n",
       DXGetErrorString8(rc));
362
    ok(status==0,"status=0x%x instead of 0\n",status);
363 364

    if (is_primary) {
365
        DSBCAPS new_dsbcaps;
366 367 368
        /* We must call SetCooperativeLevel to be allowed to call SetFormat */
        /* DSOUND: Setting DirectSound cooperative level to DSSCL_PRIORITY */
        rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_PRIORITY);
369 370
        ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_PRIORITY) failed: "
           "%s\n",DXGetErrorString8(rc));
371 372 373 374
        if (rc!=DS_OK)
            return;

        /* DSOUND: Error: Invalid format pointer */
375
        rc=IDirectSoundBuffer_SetFormat(*dsbo,0);
376 377
        ok(rc==DSERR_INVALIDPARAM,"IDirectSoundBuffer_SetFormat() should have "
           "returned DSERR_INVALIDPARAM, returned: %s\n",DXGetErrorString8(rc));
378 379

        init_format(&wfx2,WAVE_FORMAT_PCM,11025,16,2);
380
        rc=IDirectSoundBuffer_SetFormat(*dsbo,&wfx2);
381 382
        ok(rc==DS_OK,"IDirectSoundBuffer_SetFormat(%s) failed: %s\n",
           format_string(&wfx2), DXGetErrorString8(rc));
383

384
        /* There is no guarantee that SetFormat will actually change the
385 386 387
	 * format to what we asked for. It depends on what the soundcard
	 * supports. So we must re-query the format.
	 */
388
        rc=IDirectSoundBuffer_GetFormat(*dsbo,&wfx,sizeof(wfx),NULL);
389 390
        ok(rc==DS_OK,"IDirectSoundBuffer_GetFormat() failed: %s\n",
           DXGetErrorString8(rc));
391 392 393 394 395
        if (rc==DS_OK &&
            (wfx.wFormatTag!=wfx2.wFormatTag ||
             wfx.nSamplesPerSec!=wfx2.nSamplesPerSec ||
             wfx.wBitsPerSample!=wfx2.wBitsPerSample ||
             wfx.nChannels!=wfx2.nChannels)) {
396
            trace("Requested format tag=0x%04x %dx%dx%d avg.B/s=%d align=%d\n",
397 398
                  wfx2.wFormatTag,wfx2.nSamplesPerSec,wfx2.wBitsPerSample,
                  wfx2.nChannels,wfx2.nAvgBytesPerSec,wfx2.nBlockAlign);
399
            trace("Got tag=0x%04x %dx%dx%d avg.B/s=%d align=%d\n",
400 401 402 403
                  wfx.wFormatTag,wfx.nSamplesPerSec,wfx.wBitsPerSample,
                  wfx.nChannels,wfx.nAvgBytesPerSec,wfx.nBlockAlign);
        }

404 405 406 407 408 409
        ZeroMemory(&new_dsbcaps, sizeof(new_dsbcaps));
        new_dsbcaps.dwSize = sizeof(new_dsbcaps);
        rc=IDirectSoundBuffer_GetCaps(*dsbo,&new_dsbcaps);
        ok(rc==DS_OK,"IDirectSoundBuffer_GetCaps() failed: %s\n",
           DXGetErrorString8(rc));
        if (rc==DS_OK && winetest_debug > 1) {
410
            trace("    new Caps: flags=0x%08x size=%d\n",new_dsbcaps.dwFlags,
411 412
                  new_dsbcaps.dwBufferBytes);
        }
413

414
        /* Check for primary buffer size change */
415 416
        ok(new_dsbcaps.dwBufferBytes == dsbcaps.dwBufferBytes,
           "    buffer size changed after SetFormat() - "
417
           "previous size was %u, current size is %u\n",
418
           dsbcaps.dwBufferBytes, new_dsbcaps.dwBufferBytes);
419 420

        /* Check for primary buffer flags change */
421 422
        ok(new_dsbcaps.dwFlags == dsbcaps.dwFlags,
           "    flags changed after SetFormat() - "
423
           "previous flags were %08x, current flags are %08x\n",
424
           dsbcaps.dwFlags, new_dsbcaps.dwFlags);
425

426 427 428
        /* Set the CooperativeLevel back to normal */
        /* DSOUND: Setting DirectSound cooperative level to DSSCL_NORMAL */
        rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
429 430
        ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_NORMAL) failed: "
           "%s\n",DXGetErrorString8(rc));
431 432 433 434 435 436 437
    }

    if (play) {
        play_state_t state;
        DS3DLISTENER listener_param;
        LPDIRECTSOUND3DBUFFER buffer=NULL;
        DS3DBUFFER buffer_param;
438
        DWORD start_time,now;
439 440
        LPVOID buffer1;
        DWORD length1;
441

442
        if (winetest_interactive) {
443
            if (set_frequency)
444 445
                trace("    Playing %g second 440Hz tone at %dx%dx%d with a "
                      "frequency of %d (%dHz)\n", duration,
446 447 448
                      wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nChannels,
                      frequency, (440 * frequency) / wfx.nSamplesPerSec);
            else
449
                trace("    Playing %g second 440Hz tone at %dx%dx%d\n", duration,
450
                      wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nChannels);
451
        }
452 453 454

        if (is_primary) {
            /* We must call SetCooperativeLevel to be allowed to call Lock */
455 456 457 458
            /* DSOUND: Setting DirectSound cooperative level to
             * DSSCL_WRITEPRIMARY */
            rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),
                                                DSSCL_WRITEPRIMARY);
459 460
            ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_WRITEPRIMARY) "
               "failed: %s\n",DXGetErrorString8(rc));
461 462 463 464 465 466
            if (rc!=DS_OK)
                return;
        }
        if (buffer3d) {
            LPDIRECTSOUNDBUFFER temp_buffer;

467
            rc=IDirectSoundBuffer_QueryInterface(*dsbo,&IID_IDirectSound3DBuffer,
468 469 470
                                                 (LPVOID *)&buffer);
            ok(rc==DS_OK,"IDirectSoundBuffer_QueryInterface() failed: %s\n",
               DXGetErrorString8(rc));
471 472 473 474
            if (rc!=DS_OK)
                return;

            /* check the COM interface */
475
            rc=IDirectSoundBuffer_QueryInterface(*dsbo, &IID_IDirectSoundBuffer,
476 477 478 479
                                                 (LPVOID *)&temp_buffer);
            ok(rc==DS_OK && temp_buffer!=NULL,
               "IDirectSoundBuffer_QueryInterface() failed: %s\n",
               DXGetErrorString8(rc));
480 481
            ok(temp_buffer==*dsbo,"COM interface broken: %p != %p\n",
               temp_buffer,*dsbo);
482
            ref=IDirectSoundBuffer_Release(temp_buffer);
483 484
            ok(ref==1,"IDirectSoundBuffer_Release() has %d references, "
               "should have 1\n",ref);
485 486

            temp_buffer=NULL;
487
            rc=IDirectSound3DBuffer_QueryInterface(*dsbo,
488
                                                   &IID_IDirectSoundBuffer,
489 490 491 492
                                                   (LPVOID *)&temp_buffer);
            ok(rc==DS_OK && temp_buffer!=NULL,
               "IDirectSound3DBuffer_QueryInterface() failed: %s\n",
               DXGetErrorString8(rc));
493 494
            ok(temp_buffer==*dsbo,"COM interface broken: %p != %p\n",
               temp_buffer,*dsbo);
495
            ref=IDirectSoundBuffer_Release(temp_buffer);
496 497
            ok(ref==1,"IDirectSoundBuffer_Release() has %d references, "
               "should have 1\n",ref);
498

499
            ref=IDirectSoundBuffer_Release(*dsbo);
500 501 502 503 504
            ok(ref==0,"IDirectSoundBuffer_Release() has %d references, "
               "should have 0\n",ref);

            rc=IDirectSound3DBuffer_QueryInterface(buffer,
                                                   &IID_IDirectSoundBuffer,
505 506
                                                   (LPVOID *)dsbo);
            ok(rc==DS_OK && *dsbo!=NULL,"IDirectSound3DBuffer_QueryInterface() "
507
               "failed: %s\n",DXGetErrorString8(rc));
508 509 510

            /* DSOUND: Error: Invalid buffer */
            rc=IDirectSound3DBuffer_GetAllParameters(buffer,0);
511 512
            ok(rc==DSERR_INVALIDPARAM,"IDirectSound3DBuffer_GetAllParameters() "
               "failed: %s\n",DXGetErrorString8(rc));
513 514 515 516 517

            ZeroMemory(&buffer_param, sizeof(buffer_param));

            /* DSOUND: Error: Invalid buffer */
            rc=IDirectSound3DBuffer_GetAllParameters(buffer,&buffer_param);
518 519
            ok(rc==DSERR_INVALIDPARAM,"IDirectSound3DBuffer_GetAllParameters() "
               "failed: %s\n",DXGetErrorString8(rc));
520 521 522

            buffer_param.dwSize=sizeof(buffer_param);
            rc=IDirectSound3DBuffer_GetAllParameters(buffer,&buffer_param);
523 524
            ok(rc==DS_OK,"IDirectSound3DBuffer_GetAllParameters() failed: %s\n",
               DXGetErrorString8(rc));
525
        }
526 527 528
        if (set_volume) {
            if (dsbcaps.dwFlags & DSBCAPS_CTRLVOLUME) {
                LONG val;
529
                rc=IDirectSoundBuffer_GetVolume(*dsbo,&val);
530 531
                ok(rc==DS_OK,"IDirectSoundBuffer_GetVolume() failed: %s\n",
                   DXGetErrorString8(rc));
532

533
                rc=IDirectSoundBuffer_SetVolume(*dsbo,volume);
534 535
                ok(rc==DS_OK,"IDirectSoundBuffer_SetVolume() failed: %s\n",
                   DXGetErrorString8(rc));
536 537
            } else {
                /* DSOUND: Error: Buffer does not have CTRLVOLUME */
538
                rc=IDirectSoundBuffer_GetVolume(*dsbo,&volume);
539
                ok(rc==DSERR_CONTROLUNAVAIL,"IDirectSoundBuffer_GetVolume() "
540 541
                   "should have returned DSERR_CONTROLUNAVAIL, returned: %s\n",
                   DXGetErrorString8(rc));
542 543 544 545 546 547
            }
        }

        if (set_pan) {
            if (dsbcaps.dwFlags & DSBCAPS_CTRLPAN) {
                LONG val;
548
                rc=IDirectSoundBuffer_GetPan(*dsbo,&val);
549 550
                ok(rc==DS_OK,"IDirectSoundBuffer_GetPan() failed: %s\n",
                   DXGetErrorString8(rc));
551

552
                rc=IDirectSoundBuffer_SetPan(*dsbo,pan);
553 554
                ok(rc==DS_OK,"IDirectSoundBuffer_SetPan() failed: %s\n",
                   DXGetErrorString8(rc));
555 556
            } else {
                /* DSOUND: Error: Buffer does not have CTRLPAN */
557
                rc=IDirectSoundBuffer_GetPan(*dsbo,&pan);
558 559 560
                ok(rc==DSERR_CONTROLUNAVAIL,"IDirectSoundBuffer_GetPan() "
                   "should have returned DSERR_CONTROLUNAVAIL, returned: %s\n",
                   DXGetErrorString8(rc));
561 562 563
            }
        }

564
        /* try an offset past the end of the buffer */
565
        rc = IDirectSoundBuffer_Lock(*dsbo, dsbcaps.dwBufferBytes, 0, &buffer1,
566 567 568 569 570 571
                                      &length1, NULL, NULL,
                                      DSBLOCK_ENTIREBUFFER);
        ok(rc==DSERR_INVALIDPARAM, "IDirectSoundBuffer_Lock() should have "
           "returned DSERR_INVALIDPARAM, returned %s\n", DXGetErrorString8(rc));

        /* try a size larger than the buffer */
572
        rc = IDirectSoundBuffer_Lock(*dsbo, 0, dsbcaps.dwBufferBytes + 1,
573 574 575 576 577
                                     &buffer1, &length1, NULL, NULL,
                                     DSBLOCK_FROMWRITECURSOR);
        ok(rc==DSERR_INVALIDPARAM, "IDirectSoundBuffer_Lock() should have "
           "returned DSERR_INVALIDPARAM, returned %s\n", DXGetErrorString8(rc));

578 579 580 581
        if (set_frequency)
            state.wave=wave_generate_la(&wfx,(duration*frequency)/wfx.nSamplesPerSec,&state.wave_len);
        else
            state.wave=wave_generate_la(&wfx,duration,&state.wave_len);
582

583
        state.dsbo=*dsbo;
584 585
        state.wfx=&wfx;
        state.buffer_size=dsbcaps.dwBufferBytes;
586
        state.played=state.written=state.offset=0;
587 588
        buffer_refill(&state,state.buffer_size);

589
        rc=IDirectSoundBuffer_Play(*dsbo,0,0,DSBPLAY_LOOPING);
590 591
        ok(rc==DS_OK,"IDirectSoundBuffer_Play() failed: %s\n",
           DXGetErrorString8(rc));
592

593
        rc=IDirectSoundBuffer_GetStatus(*dsbo,&status);
594 595
        ok(rc==DS_OK,"IDirectSoundBuffer_GetStatus() failed: %s\n",
           DXGetErrorString8(rc));
596
        ok(status==(DSBSTATUS_PLAYING|DSBSTATUS_LOOPING),
597
           "GetStatus: bad status: %x\n",status);
598 599 600 601

        if (listener) {
            ZeroMemory(&listener_param,sizeof(listener_param));
            listener_param.dwSize=sizeof(listener_param);
602 603
            rc=IDirectSound3DListener_GetAllParameters(listener,
                                                       &listener_param);
604 605
            ok(rc==DS_OK,"IDirectSound3dListener_GetAllParameters() "
               "failed: %s\n",DXGetErrorString8(rc));
606
            if (move_listener) {
607
                listener_param.vPosition.x = -5.0;
608 609
                listener_param.vVelocity.x = 10.0/duration;
            }
610 611 612 613 614
            rc=IDirectSound3DListener_SetAllParameters(listener,
                                                       &listener_param,
                                                       DS3D_IMMEDIATE);
            ok(rc==DS_OK,"IDirectSound3dListener_SetPosition() failed: %s\n",
               DXGetErrorString8(rc));
615 616
        }
        if (buffer3d) {
617
            if (move_sound) {
618 619 620 621
                buffer_param.vPosition.x = 100.0;
                buffer_param.vVelocity.x = -200.0/duration;
            }
            buffer_param.flMinDistance = 10;
622 623 624 625
            rc=IDirectSound3DBuffer_SetAllParameters(buffer,&buffer_param,
                                                     DS3D_IMMEDIATE);
            ok(rc==DS_OK,"IDirectSound3dBuffer_SetPosition() failed: %s\n",
               DXGetErrorString8(rc));
626 627
        }

628
        start_time=GetTickCount();
629
        while (buffer_service(&state)) {
630
            WaitForSingleObject(GetCurrentProcess(),TIME_SLICE);
631
            now=GetTickCount();
632
            if (listener && move_listener) {
633 634
                listener_param.vPosition.x = -5.0+10.0*(now-start_time)/
                    1000/duration;
635 636
                if (winetest_debug>2)
                    trace("listener position=%g\n",listener_param.vPosition.x);
637 638 639
                rc=IDirectSound3DListener_SetPosition(listener,
                    listener_param.vPosition.x,listener_param.vPosition.y,
                    listener_param.vPosition.z,DS3D_IMMEDIATE);
640 641
                ok(rc==DS_OK,"IDirectSound3dListener_SetPosition() failed: "
                   "%s\n",DXGetErrorString8(rc));
642 643
            }
            if (buffer3d && move_sound) {
644 645
                buffer_param.vPosition.x = 100-200.0*(now-start_time)/
                    1000/duration;
646 647
                if (winetest_debug>2)
                    trace("sound position=%g\n",buffer_param.vPosition.x);
648 649 650 651 652
                rc=IDirectSound3DBuffer_SetPosition(buffer,
                    buffer_param.vPosition.x,buffer_param.vPosition.y,
                    buffer_param.vPosition.z,DS3D_IMMEDIATE);
                ok(rc==DS_OK,"IDirectSound3dBuffer_SetPosition() failed: %s\n",
                   DXGetErrorString8(rc));
653 654
            }
        }
655 656
        /* Check the sound duration was within 10% of the expected value */
        now=GetTickCount();
657
        ok(fabs(1000*duration-now+start_time)<=100*duration,
658
           "The sound played for %d ms instead of %g ms\n",
659
           now-start_time,1000*duration);
660 661 662 663 664 665

        free(state.wave);
        if (is_primary) {
            /* Set the CooperativeLevel back to normal */
            /* DSOUND: Setting DirectSound cooperative level to DSSCL_NORMAL */
            rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
666 667
            ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_NORMAL) "
               "failed: %s\n",DXGetErrorString8(rc));
668 669 670
        }
        if (buffer3d) {
            ref=IDirectSound3DBuffer_Release(buffer);
671 672
            ok(ref==0,"IDirectSound3DBuffer_Release() has %d references, "
               "should have 0\n",ref);
673 674 675 676 677 678 679 680 681 682 683 684 685 686
        }
    }
}

static HRESULT test_secondary(LPGUID lpGuid, int play,
                              int has_3d, int has_3dbuffer,
                              int has_listener, int has_duplicate,
                              int move_listener, int move_sound)
{
    HRESULT rc;
    LPDIRECTSOUND dso=NULL;
    LPDIRECTSOUNDBUFFER primary=NULL,secondary=NULL;
    LPDIRECTSOUND3DLISTENER listener=NULL;
    DSBUFFERDESC bufdesc;
687
    WAVEFORMATEX wfx, wfx1;
688 689 690 691
    int ref;

    /* Create the DirectSound object */
    rc=DirectSoundCreate(lpGuid,&dso,NULL);
692 693
    ok(rc==DS_OK||rc==DSERR_NODRIVER,"DirectSoundCreate() failed: %s\n",
       DXGetErrorString8(rc));
694 695 696 697 698 699
    if (rc!=DS_OK)
        return rc;

    /* We must call SetCooperativeLevel before creating primary buffer */
    /* DSOUND: Setting DirectSound cooperative level to DSSCL_PRIORITY */
    rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_PRIORITY);
700 701
    ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_PRIORITY) failed: "
       "%s\n",DXGetErrorString8(rc));
702 703 704 705 706 707 708 709 710 711 712
    if (rc!=DS_OK)
        goto EXIT;

    ZeroMemory(&bufdesc, sizeof(bufdesc));
    bufdesc.dwSize=sizeof(bufdesc);
    bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER;
    if (has_3d)
        bufdesc.dwFlags|=DSBCAPS_CTRL3D;
    else
        bufdesc.dwFlags|=(DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLPAN);
    rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&primary,NULL);
713 714 715 716 717 718
    ok((rc==DS_OK && primary!=NULL) || (rc==DSERR_CONTROLUNAVAIL),
       "IDirectSound_CreateSoundBuffer() failed to create a %sprimary buffer: "
       "%s\n",has_3d?"3D ":"", DXGetErrorString8(rc));
    if (rc==DSERR_CONTROLUNAVAIL)
        trace("  No Primary\n");
    else if (rc==DS_OK && primary!=NULL) {
719 720 721 722 723 724
        rc=IDirectSoundBuffer_GetFormat(primary,&wfx1,sizeof(wfx1),NULL);
        ok(rc==DS_OK,"IDirectSoundBuffer8_Getformat() failed: %s\n",
           DXGetErrorString8(rc));
        if (rc!=DS_OK)
            goto EXIT1;

725
        if (has_listener) {
726
            rc=IDirectSoundBuffer_QueryInterface(primary,
727 728
                                                 &IID_IDirectSound3DListener,
                                                 (void **)&listener);
729 730 731
            ok(rc==DS_OK && listener!=NULL,
               "IDirectSoundBuffer_QueryInterface() failed to get a 3D "
               "listener: %s\n",DXGetErrorString8(rc));
732
            ref=IDirectSoundBuffer_Release(primary);
733 734
            ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
               "should have 0\n",ref);
735 736 737 738 739
            if (rc==DS_OK && listener!=NULL) {
                DS3DLISTENER listener_param;
                ZeroMemory(&listener_param,sizeof(listener_param));
                /* DSOUND: Error: Invalid buffer */
                rc=IDirectSound3DListener_GetAllParameters(listener,0);
740
                ok(rc==DSERR_INVALIDPARAM,
741 742
                   "IDirectSound3dListener_GetAllParameters() should have "
                   "returned DSERR_INVALIDPARAM, returned: %s\n",
743
                   DXGetErrorString8(rc));
744 745

                /* DSOUND: Error: Invalid buffer */
746 747 748
                rc=IDirectSound3DListener_GetAllParameters(listener,
                                                           &listener_param);
                ok(rc==DSERR_INVALIDPARAM,
749 750
                   "IDirectSound3dListener_GetAllParameters() should have "
                   "returned DSERR_INVALIDPARAM, returned: %s\n",
751
                   DXGetErrorString8(rc));
752 753

                listener_param.dwSize=sizeof(listener_param);
754 755 756 757
                rc=IDirectSound3DListener_GetAllParameters(listener,
                                                           &listener_param);
                ok(rc==DS_OK,"IDirectSound3dListener_GetAllParameters() "
                   "failed: %s\n",DXGetErrorString8(rc));
758 759 760 761 762 763 764 765 766 767 768
            } else {
                ok(listener==NULL, "IDirectSoundBuffer_QueryInterface() "
                   "failed but returned a listener anyway\n");
                ok(rc!=DS_OK, "IDirectSoundBuffer_QueryInterface() succeeded "
                   "but returned a NULL listener\n");
                if (listener) {
                    ref=IDirectSound3DListener_Release(listener);
                    ok(ref==0,"IDirectSound3dListener_Release() listener has "
                       "%d references, should have 0\n",ref);
                }
                goto EXIT2;
769 770 771 772 773 774 775 776 777 778 779
            }
        }

        init_format(&wfx,WAVE_FORMAT_PCM,22050,16,2);
        secondary=NULL;
        ZeroMemory(&bufdesc, sizeof(bufdesc));
        bufdesc.dwSize=sizeof(bufdesc);
        bufdesc.dwFlags=DSBCAPS_GETCURRENTPOSITION2;
        if (has_3d)
            bufdesc.dwFlags|=DSBCAPS_CTRL3D;
        else
780 781
            bufdesc.dwFlags|=
                (DSBCAPS_CTRLFREQUENCY|DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLPAN);
782 783
        bufdesc.dwBufferBytes=align(wfx.nAvgBytesPerSec*BUFFER_LEN/1000,
                                    wfx.nBlockAlign);
784
        bufdesc.lpwfxFormat=&wfx;
785
        if (winetest_interactive) {
786 787
            trace("  Testing a %s%ssecondary buffer %s%s%s%sat %dx%dx%d "
                  "with a primary buffer at %dx%dx%d\n",
788 789 790 791 792 793 794
                  has_3dbuffer?"3D ":"",
                  has_duplicate?"duplicated ":"",
                  listener!=NULL||move_sound?"with ":"",
                  move_listener?"moving ":"",
                  listener!=NULL?"listener ":"",
                  listener&&move_sound?"and moving sound ":move_sound?
                  "moving sound ":"",
795 796
                  wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels,
                  wfx1.nSamplesPerSec,wfx1.wBitsPerSample,wfx1.nChannels);
797
        }
798
        rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&secondary,NULL);
799
        ok(rc==DS_OK && secondary!=NULL,"IDirectSound_CreateSoundBuffer() "
800
           "failed to create a %s%ssecondary buffer %s%s%s%sat %dx%dx%d (%s): %s\n",
801 802 803 804 805 806 807
           has_3dbuffer?"3D ":"", has_duplicate?"duplicated ":"",
           listener!=NULL||move_sound?"with ":"", move_listener?"moving ":"",
           listener!=NULL?"listener ":"",
           listener&&move_sound?"and moving sound ":move_sound?
           "moving sound ":"",
           wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels,
           getDSBCAPS(bufdesc.dwFlags),DXGetErrorString8(rc));
808
        if (rc==DS_OK && secondary!=NULL) {
809
            if (!has_3d) {
Mike McCormack's avatar
Mike McCormack committed
810
                LONG refvol,vol,refpan,pan;
811 812 813

                /* Check the initial secondary buffer's volume and pan */
                rc=IDirectSoundBuffer_GetVolume(secondary,&vol);
814 815
                ok(rc==DS_OK,"IDirectSoundBuffer_GetVolume(secondary) failed: "
                   "%s\n",DXGetErrorString8(rc));
816
                ok(vol==0,"wrong volume for a new secondary buffer: %d\n",vol);
817
                rc=IDirectSoundBuffer_GetPan(secondary,&pan);
818 819
                ok(rc==DS_OK,"IDirectSoundBuffer_GetPan(secondary) failed: "
                   "%s\n",DXGetErrorString8(rc));
820
                ok(pan==0,"wrong pan for a new secondary buffer: %d\n",pan);
821 822 823 824 825

                /* Check that changing the secondary buffer's volume and pan
                 * does not impact the primary buffer's volume and pan
                 */
                rc=IDirectSoundBuffer_GetVolume(primary,&refvol);
826 827
                ok(rc==DS_OK,"IDirectSoundBuffer_GetVolume(primary) failed: "
                   "%s\n",DXGetErrorString8(rc));
828
                rc=IDirectSoundBuffer_GetPan(primary,&refpan);
829 830
                ok(rc==DS_OK,"IDirectSoundBuffer_GetPan(primary) failed: %s\n",
                   DXGetErrorString8(rc));
831 832

                rc=IDirectSoundBuffer_SetVolume(secondary,-1000);
833 834
                ok(rc==DS_OK,"IDirectSoundBuffer_SetVolume(secondary) failed: "
                   "%s\n",DXGetErrorString8(rc));
835
                rc=IDirectSoundBuffer_GetVolume(secondary,&vol);
836 837
                ok(rc==DS_OK,"IDirectSoundBuffer_SetVolume(secondary) failed: "
                   "%s\n",DXGetErrorString8(rc));
838
                ok(vol==-1000,"secondary: wrong volume %d instead of -1000\n",
839
                   vol);
840
                rc=IDirectSoundBuffer_SetPan(secondary,-1000);
841 842
                ok(rc==DS_OK,"IDirectSoundBuffer_SetPan(secondary) failed: "
                   "%s\n",DXGetErrorString8(rc));
843
                rc=IDirectSoundBuffer_GetPan(secondary,&pan);
844 845
                ok(rc==DS_OK,"IDirectSoundBuffer_SetPan(secondary) failed: "
                   "%s\n",DXGetErrorString8(rc));
846
                ok(pan==-1000,"secondary: wrong pan %d instead of -1000\n",
847
                   pan);
848 849

                rc=IDirectSoundBuffer_GetVolume(primary,&vol);
850 851
                ok(rc==DS_OK,"IDirectSoundBuffer_GetVolume(primary) failed: "
                   "%s\n",DXGetErrorString8(rc));
852
                ok(vol==refvol,"The primary volume changed from %d to %d\n",
853
                   refvol,vol);
854
                rc=IDirectSoundBuffer_GetPan(primary,&pan);
855 856
                ok(rc==DS_OK,"IDirectSoundBuffer_GetPan(primary) failed: %s\n",
                   DXGetErrorString8(rc));
857
                ok(pan==refpan,"The primary pan changed from %d to %d\n",
858
                   refpan,pan);
859 860

                rc=IDirectSoundBuffer_SetVolume(secondary,0);
861 862
                ok(rc==DS_OK,"IDirectSoundBuffer_SetVolume(secondary) failed: "
                   "%s\n",DXGetErrorString8(rc));
863
                rc=IDirectSoundBuffer_SetPan(secondary,0);
864 865
                ok(rc==DS_OK,"IDirectSoundBuffer_SetPan(secondary) failed: "
                   "%s\n",DXGetErrorString8(rc));
866
            }
867 868 869 870 871
            if (has_duplicate) {
                LPDIRECTSOUNDBUFFER duplicated=NULL;

                /* DSOUND: Error: Invalid source buffer */
                rc=IDirectSound_DuplicateSoundBuffer(dso,0,0);
872 873 874
                ok(rc==DSERR_INVALIDPARAM,
                   "IDirectSound_DuplicateSoundBuffer() should have returned "
                   "DSERR_INVALIDPARAM, returned: %s\n",DXGetErrorString8(rc));
875 876 877

                /* DSOUND: Error: Invalid dest buffer */
                rc=IDirectSound_DuplicateSoundBuffer(dso,secondary,0);
878 879 880
                ok(rc==DSERR_INVALIDPARAM,
                   "IDirectSound_DuplicateSoundBuffer() should have returned "
                   "DSERR_INVALIDPARAM, returned: %s\n",DXGetErrorString8(rc));
881 882 883

                /* DSOUND: Error: Invalid source buffer */
                rc=IDirectSound_DuplicateSoundBuffer(dso,0,&duplicated);
884 885 886
                ok(rc==DSERR_INVALIDPARAM,
                  "IDirectSound_DuplicateSoundBuffer() should have returned "
                  "DSERR_INVALIDPARAM, returned: %s\n",DXGetErrorString8(rc));
887 888

                duplicated=NULL;
889 890
                rc=IDirectSound_DuplicateSoundBuffer(dso,secondary,
                                                     &duplicated);
891 892 893
                ok(rc==DS_OK && duplicated!=NULL,
                   "IDirectSound_DuplicateSoundBuffer() failed to duplicate "
                   "a secondary buffer: %s\n",DXGetErrorString8(rc));
894 895 896

                if (rc==DS_OK && duplicated!=NULL) {
                    ref=IDirectSoundBuffer_Release(secondary);
897 898
                    ok(ref==0,"IDirectSoundBuffer_Release() secondary has %d "
                      "references, should have 0\n",ref);
899 900 901 902 903 904 905
                    secondary=duplicated;
                }
            }

            if (rc==DS_OK && secondary!=NULL) {
                double duration;
                duration=(move_listener || move_sound?4.0:1.0);
906
                test_buffer(dso,&secondary,0,FALSE,0,FALSE,0,
907
                            winetest_interactive,duration,has_3dbuffer,
908
                            listener,move_listener,move_sound,FALSE,0);
909
                ref=IDirectSoundBuffer_Release(secondary);
910
                ok(ref==0,"IDirectSoundBuffer_Release() %s has %d references, "
911 912
                   "should have 0\n",has_duplicate?"duplicated":"secondary",
                   ref);
913 914
            }
        }
915
EXIT1:
916 917 918 919 920 921 922 923 924
        if (has_listener) {
            ref=IDirectSound3DListener_Release(listener);
            ok(ref==0,"IDirectSound3dListener_Release() listener has %d "
               "references, should have 0\n",ref);
        } else {
            ref=IDirectSoundBuffer_Release(primary);
            ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
               "should have 0\n",ref);
        }
925
    } else {
926 927 928 929 930 931 932 933 934
        ok(primary==NULL,"IDirectSound_CreateSoundBuffer(primary) failed "
           "but primary created anyway\n");
        ok(rc!=DS_OK,"IDirectSound_CreateSoundBuffer(primary) succeeded "
           "but primary not created\n");
        if (primary) {
            ref=IDirectSoundBuffer_Release(primary);
            ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
               "should have 0\n",ref);
        }
935
    }
936
EXIT2:
937 938 939
    /* Set the CooperativeLevel back to normal */
    /* DSOUND: Setting DirectSound cooperative level to DSSCL_NORMAL */
    rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
940
    ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_NORMAL) failed: %s\n",
941
       DXGetErrorString8(rc));
942 943 944

EXIT:
    ref=IDirectSound_Release(dso);
945
    ok(ref==0,"IDirectSound_Release() has %d references, should have 0\n",ref);
946 947 948 949 950 951
    if (ref!=0)
        return DSERR_GENERIC;

    return rc;
}

952 953 954 955 956 957 958 959
static HRESULT test_for_driver(LPGUID lpGuid)
{
    HRESULT rc;
    LPDIRECTSOUND dso=NULL;
    int ref;

    /* Create the DirectSound object */
    rc=DirectSoundCreate(lpGuid,&dso,NULL);
960
    ok(rc==DS_OK||rc==DSERR_NODRIVER||rc==DSERR_ALLOCATED||rc==E_FAIL,
961
       "DirectSoundCreate() failed: %s\n",DXGetErrorString8(rc));
962 963 964 965 966 967 968 969 970 971 972
    if (rc!=DS_OK)
        return rc;

    ref=IDirectSound_Release(dso);
    ok(ref==0,"IDirectSound_Release() has %d references, should have 0\n",ref);
    if (ref!=0)
        return DSERR_GENERIC;

    return rc;
}

973 974 975 976 977 978 979 980 981 982 983
static HRESULT test_primary(LPGUID lpGuid)
{
    HRESULT rc;
    LPDIRECTSOUND dso=NULL;
    LPDIRECTSOUNDBUFFER primary=NULL;
    DSBUFFERDESC bufdesc;
    DSCAPS dscaps;
    int ref, i;

    /* Create the DirectSound object */
    rc=DirectSoundCreate(lpGuid,&dso,NULL);
984 985
    ok(rc==DS_OK||rc==DSERR_NODRIVER,"DirectSoundCreate() failed: %s\n",
       DXGetErrorString8(rc));
986 987 988 989 990 991 992
    if (rc!=DS_OK)
        return rc;

    /* Get the device capabilities */
    ZeroMemory(&dscaps, sizeof(dscaps));
    dscaps.dwSize=sizeof(dscaps);
    rc=IDirectSound_GetCaps(dso,&dscaps);
993
    ok(rc==DS_OK,"IDirectSound_GetCaps() failed: %s\n",DXGetErrorString8(rc));
994 995 996 997 998 999
    if (rc!=DS_OK)
        goto EXIT;

    /* We must call SetCooperativeLevel before calling CreateSoundBuffer */
    /* DSOUND: Setting DirectSound cooperative level to DSSCL_PRIORITY */
    rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_PRIORITY);
1000 1001
    ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_PRIORITY) failed: "
       "%s\n",DXGetErrorString8(rc));
1002 1003 1004 1005 1006 1007 1008 1009 1010
    if (rc!=DS_OK)
        goto EXIT;

    /* Testing the primary buffer */
    primary=NULL;
    ZeroMemory(&bufdesc, sizeof(bufdesc));
    bufdesc.dwSize=sizeof(bufdesc);
    bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLPAN;
    rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&primary,NULL);
1011 1012 1013 1014 1015 1016
    ok((rc==DS_OK && primary!=NULL) || (rc==DSERR_CONTROLUNAVAIL),
       "IDirectSound_CreateSoundBuffer() failed to create a primary buffer: "
       "%s\n",DXGetErrorString8(rc));
    if (rc==DSERR_CONTROLUNAVAIL)
        trace("  No Primary\n");
    else if (rc==DS_OK && primary!=NULL) {
1017
        test_buffer(dso,&primary,1,TRUE,0,TRUE,0,winetest_interactive &&
1018 1019
                    !(dscaps.dwFlags & DSCAPS_EMULDRIVER),1.0,0,NULL,0,0,
                    FALSE,0);
1020 1021 1022 1023 1024
        if (winetest_interactive) {
            LONG volume,pan;

            volume = DSBVOLUME_MAX;
            for (i = 0; i < 6; i++) {
1025
                test_buffer(dso,&primary,1,TRUE,volume,TRUE,0,
1026 1027
                            winetest_interactive &&
                            !(dscaps.dwFlags & DSCAPS_EMULDRIVER),
1028
                            1.0,0,NULL,0,0,FALSE,0);
1029 1030 1031 1032 1033
                volume -= ((DSBVOLUME_MAX-DSBVOLUME_MIN) / 40);
            }

            pan = DSBPAN_LEFT;
            for (i = 0; i < 7; i++) {
1034
                test_buffer(dso,&primary,1,TRUE,0,TRUE,pan,
1035
                            winetest_interactive &&
1036
                            !(dscaps.dwFlags & DSCAPS_EMULDRIVER),1.0,0,0,0,0,FALSE,0);
1037 1038 1039 1040
                pan += ((DSBPAN_RIGHT-DSBPAN_LEFT) / 6);
            }
        }
        ref=IDirectSoundBuffer_Release(primary);
1041 1042
        ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
           "should have 0\n",ref);
1043 1044 1045 1046 1047
    }

    /* Set the CooperativeLevel back to normal */
    /* DSOUND: Setting DirectSound cooperative level to DSSCL_NORMAL */
    rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
1048
    ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_NORMAL) failed: %s\n",
1049
       DXGetErrorString8(rc));
1050 1051 1052

EXIT:
    ref=IDirectSound_Release(dso);
1053
    ok(ref==0,"IDirectSound_Release() has %d references, should have 0\n",ref);
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
    if (ref!=0)
        return DSERR_GENERIC;

    return rc;
}

static HRESULT test_primary_3d(LPGUID lpGuid)
{
    HRESULT rc;
    LPDIRECTSOUND dso=NULL;
    LPDIRECTSOUNDBUFFER primary=NULL;
    DSBUFFERDESC bufdesc;
    DSCAPS dscaps;
    int ref;

    /* Create the DirectSound object */
    rc=DirectSoundCreate(lpGuid,&dso,NULL);
1071 1072
    ok(rc==DS_OK||rc==DSERR_NODRIVER,"DirectSoundCreate() failed: %s\n",
       DXGetErrorString8(rc));
1073 1074 1075 1076 1077 1078 1079
    if (rc!=DS_OK)
        return rc;

    /* Get the device capabilities */
    ZeroMemory(&dscaps, sizeof(dscaps));
    dscaps.dwSize=sizeof(dscaps);
    rc=IDirectSound_GetCaps(dso,&dscaps);
1080
    ok(rc==DS_OK,"IDirectSound_GetCaps() failed: %s\n",DXGetErrorString8(rc));
1081 1082 1083 1084 1085 1086
    if (rc!=DS_OK)
        goto EXIT;

    /* We must call SetCooperativeLevel before calling CreateSoundBuffer */
    /* DSOUND: Setting DirectSound cooperative level to DSSCL_PRIORITY */
    rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_PRIORITY);
1087 1088
    ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_PRIORITY) failed: "
       "%s\n",DXGetErrorString8(rc));
1089 1090 1091 1092 1093 1094 1095 1096
    if (rc!=DS_OK)
        goto EXIT;

    primary=NULL;
    ZeroMemory(&bufdesc, sizeof(bufdesc));
    bufdesc.dwSize=sizeof(bufdesc);
    bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER;
    rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&primary,NULL);
1097 1098
    ok(rc==DS_OK && primary!=NULL,"IDirectSound_CreateSoundBuffer() failed "
       "to create a primary buffer: %s\n",DXGetErrorString8(rc));
1099 1100
    if (rc==DS_OK && primary!=NULL) {
        ref=IDirectSoundBuffer_Release(primary);
1101 1102
        ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
           "should have 0\n",ref);
1103 1104 1105 1106 1107
        primary=NULL;
        ZeroMemory(&bufdesc, sizeof(bufdesc));
        bufdesc.dwSize=sizeof(bufdesc);
        bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRL3D;
        rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&primary,NULL);
1108 1109
        ok(rc==DS_OK && primary!=NULL,"IDirectSound_CreateSoundBuffer() "
           "failed to create a 3D primary buffer: %s\n",DXGetErrorString8(rc));
1110
        if (rc==DS_OK && primary!=NULL) {
1111
            test_buffer(dso,&primary,1,FALSE,0,FALSE,0,winetest_interactive &&
1112 1113
                        !(dscaps.dwFlags & DSCAPS_EMULDRIVER),1.0,0,0,0,0,
                        FALSE,0);
1114
            ref=IDirectSoundBuffer_Release(primary);
1115 1116
            ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
               "should have 0\n",ref);
1117 1118 1119 1120 1121
        }
    }
    /* Set the CooperativeLevel back to normal */
    /* DSOUND: Setting DirectSound cooperative level to DSSCL_NORMAL */
    rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
1122
    ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_NORMAL) failed: %s\n",
1123
       DXGetErrorString8(rc));
1124 1125 1126

EXIT:
    ref=IDirectSound_Release(dso);
1127
    ok(ref==0,"IDirectSound_Release() has %d references, should have 0\n",ref);
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
    if (ref!=0)
        return DSERR_GENERIC;

    return rc;
}

static HRESULT test_primary_3d_with_listener(LPGUID lpGuid)
{
    HRESULT rc;
    LPDIRECTSOUND dso=NULL;
    LPDIRECTSOUNDBUFFER primary=NULL;
    DSBUFFERDESC bufdesc;
    DSCAPS dscaps;
    int ref;

    /* Create the DirectSound object */
    rc=DirectSoundCreate(lpGuid,&dso,NULL);
1145 1146
    ok(rc==DS_OK||rc==DSERR_NODRIVER,"DirectSoundCreate() failed: %s\n",
       DXGetErrorString8(rc));
1147 1148 1149 1150 1151 1152 1153
    if (rc!=DS_OK)
        return rc;

    /* Get the device capabilities */
    ZeroMemory(&dscaps, sizeof(dscaps));
    dscaps.dwSize=sizeof(dscaps);
    rc=IDirectSound_GetCaps(dso,&dscaps);
1154
    ok(rc==DS_OK,"IDirectSound_GetCaps() failed: %s\n",DXGetErrorString8(rc));
1155 1156 1157 1158 1159 1160
    if (rc!=DS_OK)
        goto EXIT;

    /* We must call SetCooperativeLevel before calling CreateSoundBuffer */
    /* DSOUND: Setting DirectSound cooperative level to DSSCL_PRIORITY */
    rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_PRIORITY);
1161 1162
    ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_PRIORITY) failed: "
       "%s\n",DXGetErrorString8(rc));
1163 1164 1165 1166 1167 1168 1169
    if (rc!=DS_OK)
        goto EXIT;
    primary=NULL;
    ZeroMemory(&bufdesc, sizeof(bufdesc));
    bufdesc.dwSize=sizeof(bufdesc);
    bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRL3D;
    rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&primary,NULL);
1170 1171
    ok(rc==DS_OK && primary!=NULL,"IDirectSound_CreateSoundBuffer() failed "
       "to create a 3D primary buffer: %s\n",DXGetErrorString8(rc));
1172 1173
    if (rc==DS_OK && primary!=NULL) {
        LPDIRECTSOUND3DLISTENER listener=NULL;
1174 1175 1176 1177
        rc=IDirectSoundBuffer_QueryInterface(primary,
            &IID_IDirectSound3DListener,(void **)&listener);
        ok(rc==DS_OK && listener!=NULL,"IDirectSoundBuffer_QueryInterface() "
           "failed to get a 3D listener: %s\n",DXGetErrorString8(rc));
1178 1179 1180 1181
        if (rc==DS_OK && listener!=NULL) {
            LPDIRECTSOUNDBUFFER temp_buffer=NULL;

            /* Checking the COM interface */
1182 1183 1184 1185 1186 1187
            rc=IDirectSoundBuffer_QueryInterface(primary,
                &IID_IDirectSoundBuffer,(LPVOID *)&temp_buffer);
            ok(rc==DS_OK && temp_buffer!=NULL,
               "IDirectSoundBuffer_QueryInterface() failed: %s\n",
               DXGetErrorString8(rc));
            ok(temp_buffer==primary,
1188 1189
               "COM interface broken: %p != %p\n",
               temp_buffer,primary);
1190 1191
            if (rc==DS_OK && temp_buffer!=NULL) {
                ref=IDirectSoundBuffer_Release(temp_buffer);
1192 1193
                ok(ref==1,"IDirectSoundBuffer_Release() has %d references, "
                   "should have 1\n",ref);
1194 1195

                temp_buffer=NULL;
1196 1197 1198 1199 1200 1201
                rc=IDirectSound3DListener_QueryInterface(listener,
                    &IID_IDirectSoundBuffer,(LPVOID *)&temp_buffer);
                ok(rc==DS_OK && temp_buffer!=NULL,
                   "IDirectSoundBuffer_QueryInterface() failed: %s\n",
                   DXGetErrorString8(rc));
                ok(temp_buffer==primary,
1202 1203
                   "COM interface broken: %p != %p\n",
                   temp_buffer,primary);
1204
                ref=IDirectSoundBuffer_Release(temp_buffer);
1205 1206
                ok(ref==1,"IDirectSoundBuffer_Release() has %d references, "
                   "should have 1\n",ref);
1207 1208

                /* Testing the buffer */
1209
                test_buffer(dso,&primary,1,FALSE,0,FALSE,0,
1210 1211
                            winetest_interactive &&
                            !(dscaps.dwFlags & DSCAPS_EMULDRIVER),1.0,0,
1212
                            listener,0,0,FALSE,0);
1213 1214 1215 1216
            }

            /* Testing the reference counting */
            ref=IDirectSound3DListener_Release(listener);
1217 1218
            ok(ref==0,"IDirectSound3DListener_Release() listener has %d "
               "references, should have 0\n",ref);
1219 1220 1221 1222
        }

        /* Testing the reference counting */
        ref=IDirectSoundBuffer_Release(primary);
1223 1224
        ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
           "should have 0\n",ref);
1225 1226 1227 1228
    }

EXIT:
    ref=IDirectSound_Release(dso);
1229
    ok(ref==0,"IDirectSound_Release() has %d references, should have 0\n",ref);
1230 1231 1232 1233 1234 1235 1236 1237 1238
    if (ref!=0)
return DSERR_GENERIC;

    return rc;
}

static BOOL WINAPI dsenum_callback(LPGUID lpGuid, LPCSTR lpcstrDescription,
                                   LPCSTR lpcstrModule, LPVOID lpContext)
{
1239
    HRESULT rc;
1240
    trace("*** Testing %s - %s ***\n",lpcstrDescription,lpcstrModule);
1241

1242 1243 1244 1245
    rc = test_for_driver(lpGuid);
    if (rc == DSERR_NODRIVER) {
        trace("  No Driver\n");
        return 1;
1246 1247 1248
    } else if (rc == DSERR_ALLOCATED) {
        trace("  Already In Use\n");
        return 1;
1249 1250 1251
    } else if (rc == E_FAIL) {
        trace("  No Device\n");
        return 1;
1252 1253
    }

1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
    trace("  Testing the primary buffer\n");
    test_primary(lpGuid);

    trace("  Testing 3D primary buffer\n");
    test_primary_3d(lpGuid);

    trace("  Testing 3D primary buffer with listener\n");
    test_primary_3d_with_listener(lpGuid);

    /* Testing secondary buffers */
    test_secondary(lpGuid,winetest_interactive,0,0,0,0,0,0);
    test_secondary(lpGuid,winetest_interactive,0,0,0,1,0,0);

    /* Testing 3D secondary buffers */
    test_secondary(lpGuid,winetest_interactive,1,0,0,0,0,0);
    test_secondary(lpGuid,winetest_interactive,1,1,0,0,0,0);
    test_secondary(lpGuid,winetest_interactive,1,1,0,1,0,0);
    test_secondary(lpGuid,winetest_interactive,1,0,1,0,0,0);
    test_secondary(lpGuid,winetest_interactive,1,0,1,1,0,0);
    test_secondary(lpGuid,winetest_interactive,1,1,1,0,0,0);
    test_secondary(lpGuid,winetest_interactive,1,1,1,1,0,0);
    test_secondary(lpGuid,winetest_interactive,1,1,1,0,1,0);
    test_secondary(lpGuid,winetest_interactive,1,1,1,0,0,1);
    test_secondary(lpGuid,winetest_interactive,1,1,1,0,1,1);

    return 1;
}

1282
static void ds3d_tests(void)
1283 1284 1285
{
    HRESULT rc;
    rc=DirectSoundEnumerateA(&dsenum_callback,NULL);
1286
    ok(rc==DS_OK,"DirectSoundEnumerateA() failed: %s\n",DXGetErrorString8(rc));
1287 1288 1289 1290
}

START_TEST(ds3d)
{
1291 1292
    CoInitialize(NULL);

1293 1294
    trace("DLL Version: %s\n", get_file_version("dsound.dll"));

1295
    ds3d_tests();
1296 1297

    CoUninitialize();
1298
}