Release 1.5.29.
[wine] / dlls / dsound / tests / ds3d.c
1 /*
2  * Tests the panning and 3D functions of DirectSound
3  *
4  * Part of this test involves playing test tones. But this only makes
5  * sense if someone is going to carefully listen to it, and would only
6  * bother everyone else.
7  * So this is only done if the test is being run in interactive mode.
8  *
9  * Copyright (c) 2002-2004 Francois Gouget
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * 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 #include <windows.h>
27
28 #include <math.h>
29
30 #include "wine/test.h"
31 #include "dsound.h"
32 #include "mmreg.h"
33 #include "ks.h"
34 #include "ksmedia.h"
35 #include "dsound_test.h"
36
37 #define PI 3.14159265358979323846
38
39
40 static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA,LPVOID)=NULL;
41 static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID,LPDIRECTSOUND*,
42     LPUNKNOWN)=NULL;
43
44 char* wave_generate_la(WAVEFORMATEX* wfx, double duration, DWORD* size, BOOL ieee)
45 {
46     int i;
47     int nb_samples;
48     char* buf;
49     char* b;
50
51     nb_samples=(int)(duration*wfx->nSamplesPerSec);
52     *size=nb_samples*wfx->nBlockAlign;
53     b=buf=HeapAlloc(GetProcessHeap(), 0, *size);
54     for (i=0;i<nb_samples;i++) {
55         double y=sin(440.0*2*PI*i/wfx->nSamplesPerSec);
56         if (wfx->wBitsPerSample==8) {
57             unsigned char sample=127.5*(y+1.0);
58             *b++=sample;
59             if (wfx->nChannels==2)
60                 *b++=sample;
61         } else if (wfx->wBitsPerSample == 16) {
62             signed short sample=32767.5*y-0.5;
63             b[0]=sample & 0xff;
64             b[1]=sample >> 8;
65             b+=2;
66             if (wfx->nChannels==2) {
67                 b[0]=sample & 0xff;
68                 b[1]=sample >> 8;
69                 b+=2;
70             }
71         } else if (wfx->wBitsPerSample == 24) {
72             signed int sample=8388607.5*y-0.5;
73             b[0]=sample & 0xff;
74             b[1]=(sample >> 8)&0xff;
75             b[2]=sample >> 16;
76             b+=3;
77             if (wfx->nChannels==2) {
78                 b[0]=sample & 0xff;
79                 b[1]=(sample >> 8)&0xff;
80                 b[2]=sample >> 16;
81                 b+=3;
82             }
83         } else if (wfx->wBitsPerSample == 32) {
84             if (ieee) {
85                 float *ptr = (float *) b;
86                 *ptr = y;
87
88                 ptr++;
89                 b+=4;
90
91                 if (wfx->nChannels==2) {
92                     *ptr = y;
93                     b+=4;
94                 }
95             } else {
96                 signed int sample=2147483647.5*y-0.5;
97                 b[0]=sample & 0xff;
98                 b[1]=(sample >> 8)&0xff;
99                 b[2]=(sample >> 16)&0xff;
100                 b[3]=sample >> 24;
101                 b+=4;
102                 if (wfx->nChannels==2) {
103                     b[0]=sample & 0xff;
104                     b[1]=(sample >> 8)&0xff;
105                     b[2]=(sample >> 16)&0xff;
106                     b[3]=sample >> 24;
107                     b+=4;
108                 }
109             }
110         }
111     }
112     return buf;
113 }
114
115 const char * getDSBCAPS(DWORD xmask) {
116     static struct {
117         DWORD   mask;
118         const char    *name;
119     } flags[] = {
120 #define FE(x) { x, #x },
121         FE(DSBCAPS_PRIMARYBUFFER)
122         FE(DSBCAPS_STATIC)
123         FE(DSBCAPS_LOCHARDWARE)
124         FE(DSBCAPS_LOCSOFTWARE)
125         FE(DSBCAPS_CTRL3D)
126         FE(DSBCAPS_CTRLFREQUENCY)
127         FE(DSBCAPS_CTRLPAN)
128         FE(DSBCAPS_CTRLVOLUME)
129         FE(DSBCAPS_CTRLPOSITIONNOTIFY)
130         FE(DSBCAPS_STICKYFOCUS)
131         FE(DSBCAPS_GLOBALFOCUS)
132         FE(DSBCAPS_GETCURRENTPOSITION2)
133         FE(DSBCAPS_MUTE3DATMAXDISTANCE)
134 #undef FE
135     };
136     static char buffer[512];
137     unsigned int i;
138     BOOL first = TRUE;
139
140     buffer[0] = 0;
141
142     for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++) {
143         if ((flags[i].mask & xmask) == flags[i].mask) {
144             if (first)
145                 first = FALSE;
146             else
147                 strcat(buffer, "|");
148             strcat(buffer, flags[i].name);
149         }
150     }
151
152     return buffer;
153 }
154
155 HWND get_hwnd(void)
156 {
157     HWND hwnd=GetForegroundWindow();
158     if (!hwnd)
159         hwnd=GetDesktopWindow();
160     return hwnd;
161 }
162
163 void init_format(WAVEFORMATEX* wfx, int format, int rate, int depth,
164                  int channels)
165 {
166     wfx->wFormatTag=format;
167     wfx->nChannels=channels;
168     wfx->wBitsPerSample=depth;
169     wfx->nSamplesPerSec=rate;
170     wfx->nBlockAlign=wfx->nChannels*wfx->wBitsPerSample/8;
171     /* FIXME: Shouldn't this test be if (format!=WAVE_FORMAT_PCM) */
172     if (wfx->nBlockAlign==0)
173     {
174         /* align compressed formats to byte boundary */
175         wfx->nBlockAlign=1;
176     }
177     wfx->nAvgBytesPerSec=wfx->nSamplesPerSec*wfx->nBlockAlign;
178     wfx->cbSize=0;
179 }
180
181 typedef struct {
182     char* wave;
183     DWORD wave_len;
184
185     LPDIRECTSOUNDBUFFER dsbo;
186     LPWAVEFORMATEX wfx;
187     DWORD buffer_size;
188     DWORD written;
189     DWORD played;
190     DWORD offset;
191 } play_state_t;
192
193 static int buffer_refill(play_state_t* state, DWORD size)
194 {
195     LPVOID ptr1,ptr2;
196     DWORD len1,len2;
197     HRESULT rc;
198
199     if (size>state->wave_len-state->written)
200         size=state->wave_len-state->written;
201
202     /* some broken apps like Navyfield mistakenly pass NULL for a ppValue */
203     rc=IDirectSoundBuffer_Lock(state->dsbo,state->offset,size,
204                                &ptr1,NULL,&ptr2,&len2,0);
205     ok(rc==DSERR_INVALIDPARAM,"expected %08x got %08x\n",DSERR_INVALIDPARAM, rc);
206     rc=IDirectSoundBuffer_Lock(state->dsbo,state->offset,size,
207                                &ptr1,&len1,&ptr2,&len2,0);
208     ok(rc==DS_OK,"IDirectSoundBuffer_Lock() failed: %08x\n", rc);
209     if (rc!=DS_OK)
210         return -1;
211
212     memcpy(ptr1,state->wave+state->written,len1);
213     state->written+=len1;
214     if (ptr2!=NULL) {
215         memcpy(ptr2,state->wave+state->written,len2);
216         state->written+=len2;
217     }
218     state->offset=state->written % state->buffer_size;
219     /* some apps blindly pass &ptr1 instead of ptr1 */
220     rc=IDirectSoundBuffer_Unlock(state->dsbo,&ptr1,len1,ptr2,len2);
221     ok(rc==DSERR_INVALIDPARAM, "IDDirectSoundBuffer_Unlock(): expected %08x got %08x, %p %p\n",DSERR_INVALIDPARAM, rc, &ptr1, ptr1);
222     rc=IDirectSoundBuffer_Unlock(state->dsbo,ptr1,len1,ptr2,len2);
223     ok(rc==DS_OK,"IDirectSoundBuffer_Unlock() failed: %08x\n", rc);
224     if (rc!=DS_OK)
225         return -1;
226     return size;
227 }
228
229 static int buffer_silence(play_state_t* state, DWORD size)
230 {
231     LPVOID ptr1,ptr2;
232     DWORD len1,len2;
233     HRESULT rc;
234     BYTE s;
235
236     rc=IDirectSoundBuffer_Lock(state->dsbo,state->offset,size,
237                                &ptr1,&len1,&ptr2,&len2,0);
238     ok(rc==DS_OK,"IDirectSoundBuffer_Lock() failed: %08x\n", rc);
239     if (rc!=DS_OK)
240         return -1;
241
242     s=(state->wfx->wBitsPerSample==8?0x80:0);
243     memset(ptr1,s,len1);
244     if (ptr2!=NULL) {
245         memset(ptr2,s,len2);
246     }
247     state->offset=(state->offset+size) % state->buffer_size;
248     rc=IDirectSoundBuffer_Unlock(state->dsbo,ptr1,len1,ptr2,len2);
249     ok(rc==DS_OK,"IDirectSoundBuffer_Unlock() failed: %08x\n", rc);
250     if (rc!=DS_OK)
251         return -1;
252     return size;
253 }
254
255 static int buffer_service(play_state_t* state)
256 {
257     DWORD last_play_pos,play_pos,buf_free;
258     HRESULT rc;
259
260     rc=IDirectSoundBuffer_GetCurrentPosition(state->dsbo,&play_pos,NULL);
261     ok(rc==DS_OK,"IDirectSoundBuffer_GetCurrentPosition() failed: %08x\n", rc);
262     if (rc!=DS_OK) {
263         goto STOP;
264     }
265
266     /* Update the amount played */
267     last_play_pos=state->played % state->buffer_size;
268     if (play_pos<last_play_pos)
269         state->played+=state->buffer_size-last_play_pos+play_pos;
270     else
271         state->played+=play_pos-last_play_pos;
272
273     if (winetest_debug > 1)
274         trace("buf size=%d last_play_pos=%d play_pos=%d played=%d / %d\n",
275               state->buffer_size,last_play_pos,play_pos,state->played,
276               state->wave_len);
277
278     if (state->played>state->wave_len)
279     {
280         /* Everything has been played */
281         goto STOP;
282     }
283
284     /* Refill the buffer */
285     if (state->offset<=play_pos)
286         buf_free=play_pos-state->offset;
287     else
288         buf_free=state->buffer_size-state->offset+play_pos;
289
290     if (winetest_debug > 1)
291         trace("offset=%d free=%d written=%d / %d\n",
292               state->offset,buf_free,state->written,state->wave_len);
293     if (buf_free==0)
294         return 1;
295
296     if (state->written<state->wave_len)
297     {
298         int w=buffer_refill(state,buf_free);
299         if (w==-1)
300             goto STOP;
301         buf_free-=w;
302         if (state->written==state->wave_len && winetest_debug > 1)
303             trace("last sound byte at %d\n",
304                   (state->written % state->buffer_size));
305     }
306
307     if (buf_free>0) {
308         /* Fill with silence */
309         if (winetest_debug > 1)
310             trace("writing %d bytes of silence\n",buf_free);
311         if (buffer_silence(state,buf_free)==-1)
312             goto STOP;
313     }
314     return 1;
315
316 STOP:
317     if (winetest_debug > 1)
318         trace("stopping playback\n");
319     rc=IDirectSoundBuffer_Stop(state->dsbo);
320     ok(rc==DS_OK,"IDirectSoundBuffer_Stop() failed: %08x\n", rc);
321     return 0;
322 }
323
324 void test_buffer(LPDIRECTSOUND dso, LPDIRECTSOUNDBUFFER *dsbo,
325                  BOOL is_primary, BOOL set_volume, LONG volume,
326                  BOOL set_pan, LONG pan, BOOL play, double duration,
327                  BOOL buffer3d, LPDIRECTSOUND3DLISTENER listener,
328                  BOOL move_listener, BOOL move_sound,
329                  BOOL set_frequency, DWORD frequency)
330 {
331     HRESULT rc;
332     DSBCAPS dsbcaps;
333     WAVEFORMATEX wfx,wfx2;
334     DWORD size,status,freq;
335     BOOL ieee = FALSE;
336     int ref;
337
338     if (set_frequency) {
339         rc=IDirectSoundBuffer_SetFrequency(*dsbo,frequency);
340         ok(rc==DS_OK||rc==DSERR_CONTROLUNAVAIL,
341            "IDirectSoundBuffer_SetFrequency() failed to set frequency %08x\n",rc);
342         if (rc!=DS_OK)
343             return;
344     }
345
346     /* DSOUND: Error: Invalid caps pointer */
347     rc=IDirectSoundBuffer_GetCaps(*dsbo,0);
348     ok(rc==DSERR_INVALIDPARAM,"IDirectSoundBuffer_GetCaps() should have "
349        "returned DSERR_INVALIDPARAM, returned: %08x\n",rc);
350
351     ZeroMemory(&dsbcaps, sizeof(dsbcaps));
352
353     /* DSOUND: Error: Invalid caps pointer */
354     rc=IDirectSoundBuffer_GetCaps(*dsbo,&dsbcaps);
355     ok(rc==DSERR_INVALIDPARAM,"IDirectSoundBuffer_GetCaps() should have "
356        "returned DSERR_INVALIDPARAM, returned: %08x\n",rc);
357
358     dsbcaps.dwSize=sizeof(dsbcaps);
359     rc=IDirectSoundBuffer_GetCaps(*dsbo,&dsbcaps);
360     ok(rc==DS_OK,"IDirectSoundBuffer_GetCaps() failed: %08x\n", rc);
361     if (rc==DS_OK && winetest_debug > 1) {
362         trace("    Caps: flags=0x%08x size=%d\n",dsbcaps.dwFlags,
363               dsbcaps.dwBufferBytes);
364     }
365
366     /* Query the format size. */
367     size=0;
368     rc=IDirectSoundBuffer_GetFormat(*dsbo,NULL,0,&size);
369     ok(rc==DS_OK && size!=0,"IDirectSoundBuffer_GetFormat() should have "
370        "returned the needed size: rc=%08x size=%d\n",rc,size);
371
372     ok(size == sizeof(WAVEFORMATEX) || size == sizeof(WAVEFORMATEXTENSIBLE),
373        "Expected a correct structure size, got %d\n", size);
374
375     if (size == sizeof(WAVEFORMATEX)) {
376         rc=IDirectSoundBuffer_GetFormat(*dsbo,&wfx,size,NULL);
377         ieee = (wfx.wFormatTag == WAVE_FORMAT_IEEE_FLOAT);
378     }
379     else if (size == sizeof(WAVEFORMATEXTENSIBLE)) {
380         WAVEFORMATEXTENSIBLE wfxe;
381         rc=IDirectSoundBuffer_GetFormat(*dsbo,(WAVEFORMATEX*)&wfxe,size,NULL);
382         wfx = wfxe.Format;
383         ieee = IsEqualGUID(&wfxe.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT);
384     } else
385         return;
386
387     ok(rc==DS_OK,
388         "IDirectSoundBuffer_GetFormat() failed: %08x\n", rc);
389     if (rc==DS_OK && winetest_debug > 1) {
390         trace("    Format: %s tag=0x%04x %dx%dx%d avg.B/s=%d align=%d\n",
391               is_primary ? "Primary" : "Secondary",
392               wfx.wFormatTag,wfx.nSamplesPerSec,wfx.wBitsPerSample,
393               wfx.nChannels,wfx.nAvgBytesPerSec,wfx.nBlockAlign);
394     }
395
396     /* DSOUND: Error: Invalid frequency buffer */
397     rc=IDirectSoundBuffer_GetFrequency(*dsbo,0);
398     ok(rc==DSERR_INVALIDPARAM,"IDirectSoundBuffer_GetFrequency() should have "
399        "returned DSERR_INVALIDPARAM, returned: %08x\n",rc);
400
401     /* DSOUND: Error: Primary buffers don't support CTRLFREQUENCY */
402     rc=IDirectSoundBuffer_GetFrequency(*dsbo,&freq);
403     ok((rc==DS_OK && !is_primary) || (rc==DSERR_CONTROLUNAVAIL&&is_primary) ||
404        (rc==DSERR_CONTROLUNAVAIL&&!(dsbcaps.dwFlags&DSBCAPS_CTRLFREQUENCY)),
405        "IDirectSoundBuffer_GetFrequency() failed: %08x\n",rc);
406     if (rc==DS_OK) {
407         DWORD f = set_frequency?frequency:wfx.nSamplesPerSec;
408         ok(freq==f,"The frequency returned by GetFrequency "
409            "%d does not match the format %d\n",freq,f);
410     }
411
412     /* DSOUND: Error: Invalid status pointer */
413     rc=IDirectSoundBuffer_GetStatus(*dsbo,0);
414     ok(rc==DSERR_INVALIDPARAM,"IDirectSoundBuffer_GetStatus() should have "
415        "returned DSERR_INVALIDPARAM, returned: %08x\n",rc);
416
417     rc=IDirectSoundBuffer_GetStatus(*dsbo,&status);
418     ok(rc==DS_OK,"IDirectSoundBuffer_GetStatus() failed: %08x\n", rc);
419     ok(status==0,"status=0x%x instead of 0\n",status);
420
421     if (is_primary) {
422         DSBCAPS new_dsbcaps;
423         /* We must call SetCooperativeLevel to be allowed to call SetFormat */
424         /* DSOUND: Setting DirectSound cooperative level to DSSCL_PRIORITY */
425         rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_PRIORITY);
426         ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_PRIORITY) failed: %08x\n",rc);
427         if (rc!=DS_OK)
428             return;
429
430         /* DSOUND: Error: Invalid format pointer */
431         rc=IDirectSoundBuffer_SetFormat(*dsbo,0);
432         ok(rc==DSERR_INVALIDPARAM,"IDirectSoundBuffer_SetFormat() should have "
433            "returned DSERR_INVALIDPARAM, returned: %08x\n",rc);
434
435         init_format(&wfx2,WAVE_FORMAT_PCM,11025,16,2);
436         rc=IDirectSoundBuffer_SetFormat(*dsbo,&wfx2);
437         ok(rc==DS_OK,"IDirectSoundBuffer_SetFormat(%s) failed: %08x\n",
438            format_string(&wfx2), rc);
439
440         /* There is no guarantee that SetFormat will actually change the
441          * format to what we asked for. It depends on what the soundcard
442          * supports. So we must re-query the format.
443          */
444         rc=IDirectSoundBuffer_GetFormat(*dsbo,&wfx,sizeof(wfx),NULL);
445         ok(rc==DS_OK,"IDirectSoundBuffer_GetFormat() failed: %08x\n", rc);
446         if (rc==DS_OK &&
447             (wfx.wFormatTag!=wfx2.wFormatTag ||
448              wfx.nSamplesPerSec!=wfx2.nSamplesPerSec ||
449              wfx.wBitsPerSample!=wfx2.wBitsPerSample ||
450              wfx.nChannels!=wfx2.nChannels)) {
451             trace("Requested format tag=0x%04x %dx%dx%d avg.B/s=%d align=%d\n",
452                   wfx2.wFormatTag,wfx2.nSamplesPerSec,wfx2.wBitsPerSample,
453                   wfx2.nChannels,wfx2.nAvgBytesPerSec,wfx2.nBlockAlign);
454             trace("Got tag=0x%04x %dx%dx%d avg.B/s=%d align=%d\n",
455                   wfx.wFormatTag,wfx.nSamplesPerSec,wfx.wBitsPerSample,
456                   wfx.nChannels,wfx.nAvgBytesPerSec,wfx.nBlockAlign);
457         }
458
459         ZeroMemory(&new_dsbcaps, sizeof(new_dsbcaps));
460         new_dsbcaps.dwSize = sizeof(new_dsbcaps);
461         rc=IDirectSoundBuffer_GetCaps(*dsbo,&new_dsbcaps);
462         ok(rc==DS_OK,"IDirectSoundBuffer_GetCaps() failed: %08x\n", rc);
463         if (rc==DS_OK && winetest_debug > 1) {
464             trace("    new Caps: flags=0x%08x size=%d\n",new_dsbcaps.dwFlags,
465                   new_dsbcaps.dwBufferBytes);
466         }
467
468         /* Check for primary buffer size change */
469         ok(new_dsbcaps.dwBufferBytes == dsbcaps.dwBufferBytes,
470            "    buffer size changed after SetFormat() - "
471            "previous size was %u, current size is %u\n",
472            dsbcaps.dwBufferBytes, new_dsbcaps.dwBufferBytes);
473         dsbcaps.dwBufferBytes = new_dsbcaps.dwBufferBytes;
474
475         /* Check for primary buffer flags change */
476         ok(new_dsbcaps.dwFlags == dsbcaps.dwFlags,
477            "    flags changed after SetFormat() - "
478            "previous flags were %08x, current flags are %08x\n",
479            dsbcaps.dwFlags, new_dsbcaps.dwFlags);
480
481         /* Set the CooperativeLevel back to normal */
482         /* DSOUND: Setting DirectSound cooperative level to DSSCL_NORMAL */
483         rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
484         ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_NORMAL) failed: %08x\n",rc);
485     }
486
487     if (play) {
488         play_state_t state;
489         DS3DLISTENER listener_param;
490         LPDIRECTSOUND3DBUFFER buffer=NULL;
491         DS3DBUFFER buffer_param;
492         DWORD start_time,now;
493         LPVOID buffer1;
494         DWORD length1;
495
496         if (winetest_interactive) {
497             if (set_frequency)
498                 trace("    Playing %g second 440Hz tone at %dx%dx%d with a "
499                       "frequency of %d (%dHz)\n", duration,
500                       wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nChannels,
501                       frequency, (440 * frequency) / wfx.nSamplesPerSec);
502             else
503                 trace("    Playing %g second 440Hz tone at %dx%dx%d\n", duration,
504                       wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nChannels);
505         }
506
507         if (is_primary) {
508             /* We must call SetCooperativeLevel to be allowed to call Lock */
509             /* DSOUND: Setting DirectSound cooperative level to
510              * DSSCL_WRITEPRIMARY */
511             rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),
512                                                 DSSCL_WRITEPRIMARY);
513             ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_WRITEPRIMARY) "
514                "failed: %08x\n",rc);
515             if (rc!=DS_OK)
516                 return;
517         }
518         if (buffer3d) {
519             LPDIRECTSOUNDBUFFER temp_buffer;
520
521             rc=IDirectSoundBuffer_QueryInterface(*dsbo,&IID_IDirectSound3DBuffer,
522                                                  (LPVOID *)&buffer);
523             ok(rc==DS_OK,"IDirectSoundBuffer_QueryInterface() failed: %08x\n", rc);
524             if (rc!=DS_OK)
525                 return;
526
527             /* check the COM interface */
528             rc=IDirectSoundBuffer_QueryInterface(*dsbo, &IID_IDirectSoundBuffer,
529                                                  (LPVOID *)&temp_buffer);
530             ok(rc==DS_OK && temp_buffer!=NULL,
531                "IDirectSoundBuffer_QueryInterface() failed: %08x\n", rc);
532             ok(temp_buffer==*dsbo,"COM interface broken: %p != %p\n",
533                temp_buffer,*dsbo);
534             ref=IDirectSoundBuffer_Release(temp_buffer);
535             ok(ref==1,"IDirectSoundBuffer_Release() has %d references, "
536                "should have 1\n",ref);
537
538             temp_buffer=NULL;
539             rc=IDirectSound3DBuffer_QueryInterface(*dsbo,
540                                                    &IID_IDirectSoundBuffer,
541                                                    (LPVOID *)&temp_buffer);
542             ok(rc==DS_OK && temp_buffer!=NULL,
543                "IDirectSound3DBuffer_QueryInterface() failed: %08x\n", rc);
544             ok(temp_buffer==*dsbo,"COM interface broken: %p != %p\n",
545                temp_buffer,*dsbo);
546             ref=IDirectSoundBuffer_Release(temp_buffer);
547             ok(ref==1,"IDirectSoundBuffer_Release() has %d references, "
548                "should have 1\n",ref);
549
550             ref=IDirectSoundBuffer_Release(*dsbo);
551             ok(ref==0,"IDirectSoundBuffer_Release() has %d references, "
552                "should have 0\n",ref);
553
554             rc=IDirectSound3DBuffer_QueryInterface(buffer,
555                                                    &IID_IDirectSoundBuffer,
556                                                    (LPVOID *)dsbo);
557             ok(rc==DS_OK && *dsbo!=NULL,"IDirectSound3DBuffer_QueryInterface() "
558                "failed: %08x\n",rc);
559
560             /* DSOUND: Error: Invalid buffer */
561             rc=IDirectSound3DBuffer_GetAllParameters(buffer,0);
562             ok(rc==DSERR_INVALIDPARAM,"IDirectSound3DBuffer_GetAllParameters() "
563                "failed: %08x\n",rc);
564
565             ZeroMemory(&buffer_param, sizeof(buffer_param));
566
567             /* DSOUND: Error: Invalid buffer */
568             rc=IDirectSound3DBuffer_GetAllParameters(buffer,&buffer_param);
569             ok(rc==DSERR_INVALIDPARAM,"IDirectSound3DBuffer_GetAllParameters() "
570                "failed: %08x\n",rc);
571
572             buffer_param.dwSize=sizeof(buffer_param);
573             rc=IDirectSound3DBuffer_GetAllParameters(buffer,&buffer_param);
574             ok(rc==DS_OK,"IDirectSound3DBuffer_GetAllParameters() failed: %08x\n", rc);
575         }
576         if (set_volume) {
577             if (dsbcaps.dwFlags & DSBCAPS_CTRLVOLUME) {
578                 LONG val;
579                 rc=IDirectSoundBuffer_GetVolume(*dsbo,&val);
580                 ok(rc==DS_OK,"IDirectSoundBuffer_GetVolume() failed: %08x\n", rc);
581
582                 rc=IDirectSoundBuffer_SetVolume(*dsbo,volume);
583                 ok(rc==DS_OK,"IDirectSoundBuffer_SetVolume() failed: %08x\n", rc);
584             } else {
585                 /* DSOUND: Error: Buffer does not have CTRLVOLUME */
586                 rc=IDirectSoundBuffer_GetVolume(*dsbo,&volume);
587                 ok(rc==DSERR_CONTROLUNAVAIL,"IDirectSoundBuffer_GetVolume() "
588                    "should have returned DSERR_CONTROLUNAVAIL, returned: %08x\n", rc);
589             }
590         }
591
592         if (set_pan) {
593             if (dsbcaps.dwFlags & DSBCAPS_CTRLPAN) {
594                 LONG val;
595                 rc=IDirectSoundBuffer_GetPan(*dsbo,&val);
596                 ok(rc==DS_OK,"IDirectSoundBuffer_GetPan() failed: %08x\n", rc);
597
598                 rc=IDirectSoundBuffer_SetPan(*dsbo,pan);
599                 ok(rc==DS_OK,"IDirectSoundBuffer_SetPan() failed: %08x\n", rc);
600             } else {
601                 /* DSOUND: Error: Buffer does not have CTRLPAN */
602                 rc=IDirectSoundBuffer_GetPan(*dsbo,&pan);
603                 ok(rc==DSERR_CONTROLUNAVAIL,"IDirectSoundBuffer_GetPan() "
604                    "should have returned DSERR_CONTROLUNAVAIL, returned: %08x\n", rc);
605             }
606         }
607
608         /* try an offset past the end of the buffer */
609         rc = IDirectSoundBuffer_Lock(*dsbo, dsbcaps.dwBufferBytes, 0, &buffer1,
610                                       &length1, NULL, NULL,
611                                       DSBLOCK_ENTIREBUFFER);
612         ok(rc==DSERR_INVALIDPARAM, "IDirectSoundBuffer_Lock() should have "
613            "returned DSERR_INVALIDPARAM, returned %08x\n", rc);
614
615         /* try a size larger than the buffer */
616         rc = IDirectSoundBuffer_Lock(*dsbo, 0, dsbcaps.dwBufferBytes + 1,
617                                      &buffer1, &length1, NULL, NULL,
618                                      DSBLOCK_FROMWRITECURSOR);
619         ok(rc==DSERR_INVALIDPARAM, "IDirectSoundBuffer_Lock() should have "
620            "returned DSERR_INVALIDPARAM, returned %08x\n", rc);
621
622         if (set_frequency)
623             state.wave=wave_generate_la(&wfx,(duration*frequency)/wfx.nSamplesPerSec,&state.wave_len,ieee);
624         else
625             state.wave=wave_generate_la(&wfx,duration,&state.wave_len,ieee);
626
627         state.dsbo=*dsbo;
628         state.wfx=&wfx;
629         state.buffer_size=dsbcaps.dwBufferBytes;
630         state.played=state.written=state.offset=0;
631         buffer_refill(&state,state.buffer_size);
632
633         rc=IDirectSoundBuffer_Play(*dsbo,0,0,DSBPLAY_LOOPING);
634         ok(rc==DS_OK,"IDirectSoundBuffer_Play() failed: %08x\n", rc);
635
636         rc=IDirectSoundBuffer_GetStatus(*dsbo,&status);
637         ok(rc==DS_OK,"IDirectSoundBuffer_GetStatus() failed: %08x\n", rc);
638         ok(status==(DSBSTATUS_PLAYING|DSBSTATUS_LOOPING),
639            "GetStatus: bad status: %x\n",status);
640
641         if (listener) {
642             ZeroMemory(&listener_param,sizeof(listener_param));
643             listener_param.dwSize=sizeof(listener_param);
644             rc=IDirectSound3DListener_GetAllParameters(listener,
645                                                        &listener_param);
646             ok(rc==DS_OK,"IDirectSound3dListener_GetAllParameters() "
647                "failed: %08x\n",rc);
648             if (move_listener) {
649                 listener_param.vPosition.x = -5.0f;
650                 listener_param.vVelocity.x = (float)(10.0/duration);
651             }
652             rc=IDirectSound3DListener_SetAllParameters(listener,
653                                                        &listener_param,
654                                                        DS3D_IMMEDIATE);
655             ok(rc==DS_OK,"IDirectSound3dListener_SetPosition() failed: %08x\n", rc);
656         }
657         if (buffer3d) {
658             if (move_sound) {
659                 buffer_param.vPosition.x = 100.0f;
660                 buffer_param.vVelocity.x = (float)(-200.0/duration);
661             }
662             buffer_param.flMinDistance = 10;
663             rc=IDirectSound3DBuffer_SetAllParameters(buffer,&buffer_param,
664                                                      DS3D_IMMEDIATE);
665             ok(rc==DS_OK,"IDirectSound3dBuffer_SetPosition() failed: %08x\n", rc);
666         }
667
668         start_time=GetTickCount();
669         while (buffer_service(&state)) {
670             WaitForSingleObject(GetCurrentProcess(),TIME_SLICE);
671             now=GetTickCount();
672             if (listener && move_listener) {
673                 listener_param.vPosition.x = (float)(-5.0+10.0*(now-start_time)/1000/duration);
674                 if (winetest_debug>2)
675                     trace("listener position=%g\n",listener_param.vPosition.x);
676                 rc=IDirectSound3DListener_SetPosition(listener,
677                     listener_param.vPosition.x,listener_param.vPosition.y,
678                     listener_param.vPosition.z,DS3D_IMMEDIATE);
679                 ok(rc==DS_OK,"IDirectSound3dListener_SetPosition() failed: %08x\n",rc);
680             }
681             if (buffer3d && move_sound) {
682                 buffer_param.vPosition.x = (float)(100-200.0*(now-start_time)/1000/duration);
683                 if (winetest_debug>2)
684                     trace("sound position=%g\n",buffer_param.vPosition.x);
685                 rc=IDirectSound3DBuffer_SetPosition(buffer,
686                     buffer_param.vPosition.x,buffer_param.vPosition.y,
687                     buffer_param.vPosition.z,DS3D_IMMEDIATE);
688                 ok(rc==DS_OK,"IDirectSound3dBuffer_SetPosition() failed: %08x\n", rc);
689             }
690         }
691         /* Check the sound duration was within 10% of the expected value */
692         now=GetTickCount();
693         ok(fabs(1000*duration-now+start_time)<=100*duration,
694            "The sound played for %d ms instead of %g ms\n",
695            now-start_time,1000*duration);
696
697         HeapFree(GetProcessHeap(), 0, state.wave);
698         if (is_primary) {
699             /* Set the CooperativeLevel back to normal */
700             /* DSOUND: Setting DirectSound cooperative level to DSSCL_NORMAL */
701             rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
702             ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_NORMAL) "
703                "failed: %08x\n",rc);
704         }
705         if (buffer3d) {
706             ref=IDirectSound3DBuffer_Release(buffer);
707             ok(ref==0,"IDirectSound3DBuffer_Release() has %d references, "
708                "should have 0\n",ref);
709         }
710     }
711 }
712
713 static HRESULT test_secondary(LPGUID lpGuid, int play,
714                               int has_3d, int has_3dbuffer,
715                               int has_listener, int has_duplicate,
716                               int move_listener, int move_sound)
717 {
718     HRESULT rc;
719     LPDIRECTSOUND dso=NULL;
720     LPDIRECTSOUNDBUFFER primary=NULL,secondary=NULL;
721     LPDIRECTSOUND3DLISTENER listener=NULL;
722     DSBUFFERDESC bufdesc;
723     WAVEFORMATEX wfx, wfx1;
724     int ref;
725
726     /* Create the DirectSound object */
727     rc=pDirectSoundCreate(lpGuid,&dso,NULL);
728     ok(rc==DS_OK||rc==DSERR_NODRIVER,"DirectSoundCreate() failed: %08x\n", rc);
729     if (rc!=DS_OK)
730         return rc;
731
732     /* We must call SetCooperativeLevel before creating primary buffer */
733     /* DSOUND: Setting DirectSound cooperative level to DSSCL_PRIORITY */
734     rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_PRIORITY);
735     ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_PRIORITY) failed: %08x\n",rc);
736     if (rc!=DS_OK)
737         goto EXIT;
738
739     ZeroMemory(&bufdesc, sizeof(bufdesc));
740     bufdesc.dwSize=sizeof(bufdesc);
741     bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER;
742     if (has_3d)
743         bufdesc.dwFlags|=DSBCAPS_CTRL3D;
744     else
745         bufdesc.dwFlags|=(DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLPAN);
746     rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&primary,NULL);
747     ok((rc==DS_OK && primary!=NULL) || (rc==DSERR_CONTROLUNAVAIL),
748        "IDirectSound_CreateSoundBuffer() failed to create a %sprimary buffer: %08x\n",has_3d?"3D ":"", rc);
749     if (rc==DSERR_CONTROLUNAVAIL)
750         trace("  No Primary\n");
751     else if (rc==DS_OK && primary!=NULL) {
752         rc=IDirectSoundBuffer_GetFormat(primary,&wfx1,sizeof(wfx1),NULL);
753         ok(rc==DS_OK,"IDirectSoundBuffer8_Getformat() failed: %08x\n", rc);
754         if (rc!=DS_OK)
755             goto EXIT1;
756
757         if (has_listener) {
758             rc=IDirectSoundBuffer_QueryInterface(primary,
759                                                  &IID_IDirectSound3DListener,
760                                                  (void **)&listener);
761             ok(rc==DS_OK && listener!=NULL,
762                "IDirectSoundBuffer_QueryInterface() failed to get a 3D listener: %08x\n",rc);
763             ref=IDirectSoundBuffer_Release(primary);
764             ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
765                "should have 0\n",ref);
766             if (rc==DS_OK && listener!=NULL) {
767                 DS3DLISTENER listener_param;
768                 ZeroMemory(&listener_param,sizeof(listener_param));
769                 /* DSOUND: Error: Invalid buffer */
770                 rc=IDirectSound3DListener_GetAllParameters(listener,0);
771                 ok(rc==DSERR_INVALIDPARAM,
772                    "IDirectSound3dListener_GetAllParameters() should have "
773                    "returned DSERR_INVALIDPARAM, returned: %08x\n", rc);
774
775                 /* DSOUND: Error: Invalid buffer */
776                 rc=IDirectSound3DListener_GetAllParameters(listener,
777                                                            &listener_param);
778                 ok(rc==DSERR_INVALIDPARAM,
779                    "IDirectSound3dListener_GetAllParameters() should have "
780                    "returned DSERR_INVALIDPARAM, returned: %08x\n", rc);
781
782                 listener_param.dwSize=sizeof(listener_param);
783                 rc=IDirectSound3DListener_GetAllParameters(listener,
784                                                            &listener_param);
785                 ok(rc==DS_OK,"IDirectSound3dListener_GetAllParameters() "
786                    "failed: %08x\n",rc);
787             } else {
788                 ok(listener==NULL, "IDirectSoundBuffer_QueryInterface() "
789                    "failed but returned a listener anyway\n");
790                 ok(rc!=DS_OK, "IDirectSoundBuffer_QueryInterface() succeeded "
791                    "but returned a NULL listener\n");
792                 if (listener) {
793                     ref=IDirectSound3DListener_Release(listener);
794                     ok(ref==0,"IDirectSound3dListener_Release() listener has "
795                        "%d references, should have 0\n",ref);
796                 }
797                 goto EXIT2;
798             }
799         }
800
801         init_format(&wfx,WAVE_FORMAT_PCM,22050,16,2);
802         secondary=NULL;
803         ZeroMemory(&bufdesc, sizeof(bufdesc));
804         bufdesc.dwSize=sizeof(bufdesc);
805         bufdesc.dwFlags=DSBCAPS_GETCURRENTPOSITION2;
806         if (has_3dbuffer)
807             bufdesc.dwFlags|=DSBCAPS_CTRL3D;
808         else
809             bufdesc.dwFlags|=
810                 (DSBCAPS_CTRLFREQUENCY|DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLPAN);
811         bufdesc.dwBufferBytes=align(wfx.nAvgBytesPerSec*BUFFER_LEN/1000,
812                                     wfx.nBlockAlign);
813         bufdesc.lpwfxFormat=&wfx;
814         if (winetest_interactive) {
815             trace("  Testing a %s%ssecondary buffer %s%s%s%sat %dx%dx%d "
816                   "with a primary buffer at %dx%dx%d\n",
817                   has_3dbuffer?"3D ":"",
818                   has_duplicate?"duplicated ":"",
819                   listener!=NULL||move_sound?"with ":"",
820                   move_listener?"moving ":"",
821                   listener!=NULL?"listener ":"",
822                   listener&&move_sound?"and moving sound ":move_sound?
823                   "moving sound ":"",
824                   wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels,
825                   wfx1.nSamplesPerSec,wfx1.wBitsPerSample,wfx1.nChannels);
826         }
827         rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&secondary,NULL);
828         ok((rc==DS_OK && secondary!=NULL) || broken(rc == DSERR_CONTROLUNAVAIL), /* vmware drivers on w2k */
829            "IDirectSound_CreateSoundBuffer() failed to create a %s%ssecondary buffer %s%s%s%sat %dx%dx%d (%s): %08x\n",
830            has_3dbuffer?"3D ":"", has_duplicate?"duplicated ":"",
831            listener!=NULL||move_sound?"with ":"", move_listener?"moving ":"",
832            listener!=NULL?"listener ":"",
833            listener&&move_sound?"and moving sound ":move_sound?
834            "moving sound ":"",
835            wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels,
836            getDSBCAPS(bufdesc.dwFlags),rc);
837         if (rc==DS_OK && secondary!=NULL) {
838             IDirectSound3DBuffer *ds3d;
839
840             rc=IDirectSoundBuffer_QueryInterface(secondary, &IID_IDirectSound3DBuffer, (void**)&ds3d);
841             ok((has_3dbuffer && rc==DS_OK) || (!has_3dbuffer && rc==E_NOINTERFACE),
842                     "Wrong return trying to get 3D buffer on %s3D secondary interface: %08x\n", has_3dbuffer ? "" : "non-", rc);
843             if(rc==DS_OK)
844                 IDirectSound3DBuffer_Release(ds3d);
845
846             if (!has_3d) {
847                 LONG refvol,vol,refpan,pan;
848
849                 /* Check the initial secondary buffer's volume and pan */
850                 rc=IDirectSoundBuffer_GetVolume(secondary,&vol);
851                 ok(rc==DS_OK,"IDirectSoundBuffer_GetVolume(secondary) failed: %08x\n",rc);
852                 ok(vol==0,"wrong volume for a new secondary buffer: %d\n",vol);
853                 rc=IDirectSoundBuffer_GetPan(secondary,&pan);
854                 ok(rc==DS_OK,"IDirectSoundBuffer_GetPan(secondary) failed: %08x\n",rc);
855                 ok(pan==0,"wrong pan for a new secondary buffer: %d\n",pan);
856
857                 /* Check that changing the secondary buffer's volume and pan
858                  * does not impact the primary buffer's volume and pan
859                  */
860                 rc=IDirectSoundBuffer_GetVolume(primary,&refvol);
861                 ok(rc==DS_OK,"IDirectSoundBuffer_GetVolume(primary) failed: %08x\n",rc);
862                 rc=IDirectSoundBuffer_GetPan(primary,&refpan);
863                 ok(rc==DS_OK,"IDirectSoundBuffer_GetPan(primary) failed: %08x\n", rc);
864
865                 rc=IDirectSoundBuffer_SetVolume(secondary,-1000);
866                 ok(rc==DS_OK,"IDirectSoundBuffer_SetVolume(secondary) failed: %08x\n",rc);
867                 rc=IDirectSoundBuffer_GetVolume(secondary,&vol);
868                 ok(rc==DS_OK,"IDirectSoundBuffer_SetVolume(secondary) failed: %08x\n",rc);
869                 ok(vol==-1000,"secondary: wrong volume %d instead of -1000\n",
870                    vol);
871                 rc=IDirectSoundBuffer_SetPan(secondary,-1000);
872                 ok(rc==DS_OK,"IDirectSoundBuffer_SetPan(secondary) failed: %08x\n",rc);
873                 rc=IDirectSoundBuffer_GetPan(secondary,&pan);
874                 ok(rc==DS_OK,"IDirectSoundBuffer_SetPan(secondary) failed: %08x\n",rc);
875                 ok(pan==-1000,"secondary: wrong pan %d instead of -1000\n",
876                    pan);
877
878                 rc=IDirectSoundBuffer_GetVolume(primary,&vol);
879                 ok(rc==DS_OK,"IDirectSoundBuffer_GetVolume(primary) failed: %08x\n",rc);
880                 ok(vol==refvol,"The primary volume changed from %d to %d\n",
881                    refvol,vol);
882                 rc=IDirectSoundBuffer_GetPan(primary,&pan);
883                 ok(rc==DS_OK,"IDirectSoundBuffer_GetPan(primary) failed: %08x\n", rc);
884                 ok(pan==refpan,"The primary pan changed from %d to %d\n",
885                    refpan,pan);
886
887                 rc=IDirectSoundBuffer_SetVolume(secondary,0);
888                 ok(rc==DS_OK,"IDirectSoundBuffer_SetVolume(secondary) failed: %08x\n",rc);
889                 rc=IDirectSoundBuffer_SetPan(secondary,0);
890                 ok(rc==DS_OK,"IDirectSoundBuffer_SetPan(secondary) failed: %08x\n",rc);
891             }
892             if (has_duplicate) {
893                 LPDIRECTSOUNDBUFFER duplicated=NULL;
894
895                 /* DSOUND: Error: Invalid source buffer */
896                 rc=IDirectSound_DuplicateSoundBuffer(dso,0,0);
897                 ok(rc==DSERR_INVALIDPARAM,
898                    "IDirectSound_DuplicateSoundBuffer() should have returned "
899                    "DSERR_INVALIDPARAM, returned: %08x\n",rc);
900
901                 /* DSOUND: Error: Invalid dest buffer */
902                 rc=IDirectSound_DuplicateSoundBuffer(dso,secondary,0);
903                 ok(rc==DSERR_INVALIDPARAM,
904                    "IDirectSound_DuplicateSoundBuffer() should have returned "
905                    "DSERR_INVALIDPARAM, returned: %08x\n",rc);
906
907                 /* DSOUND: Error: Invalid source buffer */
908                 rc=IDirectSound_DuplicateSoundBuffer(dso,0,&duplicated);
909                 ok(rc==DSERR_INVALIDPARAM,
910                   "IDirectSound_DuplicateSoundBuffer() should have returned "
911                   "DSERR_INVALIDPARAM, returned: %08x\n",rc);
912
913                 duplicated=NULL;
914                 rc=IDirectSound_DuplicateSoundBuffer(dso,secondary,
915                                                      &duplicated);
916                 ok(rc==DS_OK && duplicated!=NULL,
917                    "IDirectSound_DuplicateSoundBuffer() failed to duplicate "
918                    "a secondary buffer: %08x\n",rc);
919
920                 if (rc==DS_OK && duplicated!=NULL) {
921                     ref=IDirectSoundBuffer_Release(secondary);
922                     ok(ref==0,"IDirectSoundBuffer_Release() secondary has %d "
923                       "references, should have 0\n",ref);
924                     secondary=duplicated;
925                 }
926             }
927
928             if (rc==DS_OK && secondary!=NULL) {
929                 double duration;
930                 duration=(move_listener || move_sound?4.0:1.0);
931                 test_buffer(dso,&secondary,0,FALSE,0,FALSE,0,
932                             winetest_interactive,duration,has_3dbuffer,
933                             listener,move_listener,move_sound,FALSE,0);
934                 ref=IDirectSoundBuffer_Release(secondary);
935                 ok(ref==0,"IDirectSoundBuffer_Release() %s has %d references, "
936                    "should have 0\n",has_duplicate?"duplicated":"secondary",
937                    ref);
938             }
939         }
940 EXIT1:
941         if (has_listener) {
942             ref=IDirectSound3DListener_Release(listener);
943             ok(ref==0,"IDirectSound3dListener_Release() listener has %d "
944                "references, should have 0\n",ref);
945         } else {
946             ref=IDirectSoundBuffer_Release(primary);
947             ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
948                "should have 0\n",ref);
949         }
950     } else {
951         ok(primary==NULL,"IDirectSound_CreateSoundBuffer(primary) failed "
952            "but primary created anyway\n");
953         ok(rc!=DS_OK,"IDirectSound_CreateSoundBuffer(primary) succeeded "
954            "but primary not created\n");
955         if (primary) {
956             ref=IDirectSoundBuffer_Release(primary);
957             ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
958                "should have 0\n",ref);
959         }
960     }
961 EXIT2:
962     /* Set the CooperativeLevel back to normal */
963     /* DSOUND: Setting DirectSound cooperative level to DSSCL_NORMAL */
964     rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
965     ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_NORMAL) failed: %08x\n", rc);
966
967 EXIT:
968     ref=IDirectSound_Release(dso);
969     ok(ref==0,"IDirectSound_Release() has %d references, should have 0\n",ref);
970     if (ref!=0)
971         return DSERR_GENERIC;
972
973     return rc;
974 }
975
976 static HRESULT test_for_driver(LPGUID lpGuid)
977 {
978     HRESULT rc;
979     LPDIRECTSOUND dso=NULL;
980     int ref;
981
982     /* Create the DirectSound object */
983     rc=pDirectSoundCreate(lpGuid,&dso,NULL);
984     ok(rc==DS_OK||rc==DSERR_NODRIVER||rc==DSERR_ALLOCATED||rc==E_FAIL,
985        "DirectSoundCreate() failed: %08x\n",rc);
986     if (rc!=DS_OK)
987         return rc;
988
989     ref=IDirectSound_Release(dso);
990     ok(ref==0,"IDirectSound_Release() has %d references, should have 0\n",ref);
991     if (ref!=0)
992         return DSERR_GENERIC;
993
994     return rc;
995 }
996
997 static HRESULT test_primary(LPGUID lpGuid)
998 {
999     HRESULT rc;
1000     LPDIRECTSOUND dso=NULL;
1001     LPDIRECTSOUNDBUFFER primary=NULL;
1002     DSBUFFERDESC bufdesc;
1003     DSCAPS dscaps;
1004     int ref, i;
1005
1006     /* Create the DirectSound object */
1007     rc=pDirectSoundCreate(lpGuid,&dso,NULL);
1008     ok(rc==DS_OK||rc==DSERR_NODRIVER,"DirectSoundCreate() failed: %08x\n", rc);
1009     if (rc!=DS_OK)
1010         return rc;
1011
1012     /* Get the device capabilities */
1013     ZeroMemory(&dscaps, sizeof(dscaps));
1014     dscaps.dwSize=sizeof(dscaps);
1015     rc=IDirectSound_GetCaps(dso,&dscaps);
1016     ok(rc==DS_OK,"IDirectSound_GetCaps() failed: %08x\n",rc);
1017     if (rc!=DS_OK)
1018         goto EXIT;
1019
1020     /* We must call SetCooperativeLevel before calling CreateSoundBuffer */
1021     /* DSOUND: Setting DirectSound cooperative level to DSSCL_PRIORITY */
1022     rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_PRIORITY);
1023     ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_PRIORITY) failed: %08x\n",rc);
1024     if (rc!=DS_OK)
1025         goto EXIT;
1026
1027     /* Testing the primary buffer */
1028     primary=NULL;
1029     ZeroMemory(&bufdesc, sizeof(bufdesc));
1030     bufdesc.dwSize=sizeof(bufdesc);
1031     bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLPAN;
1032     rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&primary,NULL);
1033     ok((rc==DS_OK && primary!=NULL) || (rc==DSERR_CONTROLUNAVAIL),
1034        "IDirectSound_CreateSoundBuffer() failed to create a primary buffer: %08x\n",rc);
1035     if (rc==DSERR_CONTROLUNAVAIL)
1036         trace("  No Primary\n");
1037     else if (rc==DS_OK && primary!=NULL) {
1038         test_buffer(dso,&primary,1,TRUE,0,TRUE,0,winetest_interactive &&
1039                     !(dscaps.dwFlags & DSCAPS_EMULDRIVER),1.0,0,NULL,0,0,
1040                     FALSE,0);
1041         if (winetest_interactive) {
1042             LONG volume,pan;
1043
1044             volume = DSBVOLUME_MAX;
1045             for (i = 0; i < 6; i++) {
1046                 test_buffer(dso,&primary,1,TRUE,volume,TRUE,0,
1047                             winetest_interactive &&
1048                             !(dscaps.dwFlags & DSCAPS_EMULDRIVER),
1049                             1.0,0,NULL,0,0,FALSE,0);
1050                 volume -= ((DSBVOLUME_MAX-DSBVOLUME_MIN) / 40);
1051             }
1052
1053             pan = DSBPAN_LEFT;
1054             for (i = 0; i < 7; i++) {
1055                 test_buffer(dso,&primary,1,TRUE,0,TRUE,pan,
1056                             winetest_interactive &&
1057                             !(dscaps.dwFlags & DSCAPS_EMULDRIVER),1.0,0,0,0,0,FALSE,0);
1058                 pan += ((DSBPAN_RIGHT-DSBPAN_LEFT) / 6);
1059             }
1060         }
1061         ref=IDirectSoundBuffer_Release(primary);
1062         ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
1063            "should have 0\n",ref);
1064     }
1065
1066     /* Set the CooperativeLevel back to normal */
1067     /* DSOUND: Setting DirectSound cooperative level to DSSCL_NORMAL */
1068     rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
1069     ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_NORMAL) failed: %08x\n", rc);
1070
1071 EXIT:
1072     ref=IDirectSound_Release(dso);
1073     ok(ref==0,"IDirectSound_Release() has %d references, should have 0\n",ref);
1074     if (ref!=0)
1075         return DSERR_GENERIC;
1076
1077     return rc;
1078 }
1079
1080 static HRESULT test_primary_3d(LPGUID lpGuid)
1081 {
1082     HRESULT rc;
1083     LPDIRECTSOUND dso=NULL;
1084     LPDIRECTSOUNDBUFFER primary=NULL;
1085     DSBUFFERDESC bufdesc;
1086     DSCAPS dscaps;
1087     int ref;
1088
1089     /* Create the DirectSound object */
1090     rc=pDirectSoundCreate(lpGuid,&dso,NULL);
1091     ok(rc==DS_OK||rc==DSERR_NODRIVER,"DirectSoundCreate() failed: %08x\n", rc);
1092     if (rc!=DS_OK)
1093         return rc;
1094
1095     /* Get the device capabilities */
1096     ZeroMemory(&dscaps, sizeof(dscaps));
1097     dscaps.dwSize=sizeof(dscaps);
1098     rc=IDirectSound_GetCaps(dso,&dscaps);
1099     ok(rc==DS_OK,"IDirectSound_GetCaps() failed: %08x\n",rc);
1100     if (rc!=DS_OK)
1101         goto EXIT;
1102
1103     /* We must call SetCooperativeLevel before calling CreateSoundBuffer */
1104     /* DSOUND: Setting DirectSound cooperative level to DSSCL_PRIORITY */
1105     rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_PRIORITY);
1106     ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_PRIORITY) failed: %08x\n",rc);
1107     if (rc!=DS_OK)
1108         goto EXIT;
1109
1110     primary=NULL;
1111     ZeroMemory(&bufdesc, sizeof(bufdesc));
1112     bufdesc.dwSize=sizeof(bufdesc);
1113     bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER;
1114     rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&primary,NULL);
1115     ok(rc==DS_OK && primary!=NULL,"IDirectSound_CreateSoundBuffer() failed "
1116        "to create a primary buffer: %08x\n",rc);
1117     if (rc==DS_OK && primary!=NULL) {
1118         ref=IDirectSoundBuffer_Release(primary);
1119         ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
1120            "should have 0\n",ref);
1121         primary=NULL;
1122         ZeroMemory(&bufdesc, sizeof(bufdesc));
1123         bufdesc.dwSize=sizeof(bufdesc);
1124         bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRL3D;
1125         rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&primary,NULL);
1126         ok(rc==DS_OK && primary!=NULL,"IDirectSound_CreateSoundBuffer() "
1127            "failed to create a 3D primary buffer: %08x\n",rc);
1128         if (rc==DS_OK && primary!=NULL) {
1129             test_buffer(dso,&primary,1,FALSE,0,FALSE,0,winetest_interactive &&
1130                         !(dscaps.dwFlags & DSCAPS_EMULDRIVER),1.0,0,0,0,0,
1131                         FALSE,0);
1132             ref=IDirectSoundBuffer_Release(primary);
1133             ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
1134                "should have 0\n",ref);
1135         }
1136     }
1137     /* Set the CooperativeLevel back to normal */
1138     /* DSOUND: Setting DirectSound cooperative level to DSSCL_NORMAL */
1139     rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
1140     ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_NORMAL) failed: %08x\n", rc);
1141
1142 EXIT:
1143     ref=IDirectSound_Release(dso);
1144     ok(ref==0,"IDirectSound_Release() has %d references, should have 0\n",ref);
1145     if (ref!=0)
1146         return DSERR_GENERIC;
1147
1148     return rc;
1149 }
1150
1151 static HRESULT test_primary_3d_with_listener(LPGUID lpGuid)
1152 {
1153     HRESULT rc;
1154     LPDIRECTSOUND dso=NULL;
1155     LPDIRECTSOUNDBUFFER primary=NULL;
1156     DSBUFFERDESC bufdesc;
1157     DSCAPS dscaps;
1158     int ref;
1159
1160     /* Create the DirectSound object */
1161     rc=pDirectSoundCreate(lpGuid,&dso,NULL);
1162     ok(rc==DS_OK||rc==DSERR_NODRIVER,"DirectSoundCreate() failed: %08x\n", rc);
1163     if (rc!=DS_OK)
1164         return rc;
1165
1166     /* Get the device capabilities */
1167     ZeroMemory(&dscaps, sizeof(dscaps));
1168     dscaps.dwSize=sizeof(dscaps);
1169     rc=IDirectSound_GetCaps(dso,&dscaps);
1170     ok(rc==DS_OK,"IDirectSound_GetCaps() failed: %08x\n",rc);
1171     if (rc!=DS_OK)
1172         goto EXIT;
1173
1174     /* We must call SetCooperativeLevel before calling CreateSoundBuffer */
1175     /* DSOUND: Setting DirectSound cooperative level to DSSCL_PRIORITY */
1176     rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_PRIORITY);
1177     ok(rc==DS_OK,"IDirectSound_SetCooperativeLevel(DSSCL_PRIORITY) failed: %08x\n",rc);
1178     if (rc!=DS_OK)
1179         goto EXIT;
1180     primary=NULL;
1181     ZeroMemory(&bufdesc, sizeof(bufdesc));
1182     bufdesc.dwSize=sizeof(bufdesc);
1183     bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRL3D;
1184     rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&primary,NULL);
1185     ok(rc==DS_OK && primary!=NULL,"IDirectSound_CreateSoundBuffer() failed "
1186        "to create a 3D primary buffer: %08x\n",rc);
1187     if (rc==DS_OK && primary!=NULL) {
1188         LPDIRECTSOUND3DLISTENER listener=NULL;
1189         LPDIRECTSOUNDBUFFER temp_buffer=NULL;
1190         rc=IDirectSoundBuffer_QueryInterface(primary,
1191             &IID_IDirectSound3DListener,(void **)&listener);
1192         ok(rc==DS_OK && listener!=NULL,"IDirectSoundBuffer_QueryInterface() "
1193            "failed to get a 3D listener: %08x\n",rc);
1194         if (rc==DS_OK && listener!=NULL) {
1195             /* Checking the COM interface */
1196             rc=IDirectSoundBuffer_QueryInterface(primary,
1197                 &IID_IDirectSoundBuffer,(LPVOID *)&temp_buffer);
1198             ok(rc==DS_OK && temp_buffer!=NULL,
1199                "IDirectSoundBuffer_QueryInterface() failed: %08x\n", rc);
1200             ok(temp_buffer==primary,
1201                "COM interface broken: %p != %p\n",
1202                temp_buffer,primary);
1203             if (rc==DS_OK && temp_buffer!=NULL) {
1204                 ref=IDirectSoundBuffer_Release(temp_buffer);
1205                 ok(ref==1,"IDirectSoundBuffer_Release() has %d references, "
1206                    "should have 1\n",ref);
1207
1208                 temp_buffer=NULL;
1209                 rc=IDirectSound3DListener_QueryInterface(listener,
1210                     &IID_IDirectSoundBuffer,(LPVOID *)&temp_buffer);
1211                 ok(rc==DS_OK && temp_buffer!=NULL,
1212                    "IDirectSoundBuffer_QueryInterface() failed: %08x\n", rc);
1213                 ok(temp_buffer==primary,
1214                    "COM interface broken: %p != %p\n",
1215                    temp_buffer,primary);
1216                 ref=IDirectSoundBuffer_Release(temp_buffer);
1217                 ok(ref==1,"IDirectSoundBuffer_Release() has %d references, "
1218                    "should have 1\n",ref);
1219
1220                 /* Testing the buffer */
1221                 test_buffer(dso,&primary,1,FALSE,0,FALSE,0,
1222                             winetest_interactive &&
1223                             !(dscaps.dwFlags & DSCAPS_EMULDRIVER),1.0,0,
1224                             listener,0,0,FALSE,0);
1225
1226                 temp_buffer = NULL;
1227                 rc = IDirectSound3DListener_QueryInterface(listener, &IID_IKsPropertySet,
1228                         (void **)&temp_buffer);
1229                 ok(rc==DS_OK && temp_buffer!=NULL,
1230                         "IDirectSound3DListener_QueryInterface didn't handle IKsPropertySet: ret = %08x\n", rc);
1231                 if(temp_buffer)
1232                     IKsPropertySet_Release(temp_buffer);
1233             }
1234
1235             /* Testing the reference counting */
1236             ref=IDirectSound3DListener_Release(listener);
1237             ok(ref==0,"IDirectSound3DListener_Release() listener has %d "
1238                "references, should have 0\n",ref);
1239         }
1240
1241         temp_buffer = NULL;
1242         rc = IDirectSoundBuffer_QueryInterface(primary, &IID_IKsPropertySet, (void **)&temp_buffer);
1243         ok(rc==DS_OK && temp_buffer!=NULL,
1244                 "IDirectSoundBuffer_QueryInterface didn't handle IKsPropertySet on primary buffer: ret = %08x\n", rc);
1245         if(temp_buffer)
1246             IKsPropertySet_Release(temp_buffer);
1247
1248         /* Testing the reference counting */
1249         ref=IDirectSoundBuffer_Release(primary);
1250         ok(ref==0,"IDirectSoundBuffer_Release() primary has %d references, "
1251            "should have 0\n",ref);
1252     }
1253
1254 EXIT:
1255     ref=IDirectSound_Release(dso);
1256     ok(ref==0,"IDirectSound_Release() has %d references, should have 0\n",ref);
1257     if (ref!=0)
1258 return DSERR_GENERIC;
1259
1260     return rc;
1261 }
1262
1263 static unsigned driver_count = 0;
1264
1265 static BOOL WINAPI dsenum_callback(LPGUID lpGuid, LPCSTR lpcstrDescription,
1266                                    LPCSTR lpcstrModule, LPVOID lpContext)
1267 {
1268     HRESULT rc;
1269     trace("*** Testing %s - %s ***\n",lpcstrDescription,lpcstrModule);
1270     driver_count++;
1271
1272     rc = test_for_driver(lpGuid);
1273     if (rc == DSERR_NODRIVER) {
1274         trace("  No Driver\n");
1275         return 1;
1276     } else if (rc == DSERR_ALLOCATED) {
1277         trace("  Already In Use\n");
1278         return 1;
1279     } else if (rc == E_FAIL) {
1280         trace("  No Device\n");
1281         return 1;
1282     }
1283
1284     trace("  Testing the primary buffer\n");
1285     test_primary(lpGuid);
1286
1287     trace("  Testing 3D primary buffer\n");
1288     test_primary_3d(lpGuid);
1289
1290     trace("  Testing 3D primary buffer with listener\n");
1291     test_primary_3d_with_listener(lpGuid);
1292
1293     /* Testing secondary buffers */
1294     test_secondary(lpGuid,winetest_interactive,0,0,0,0,0,0);
1295     test_secondary(lpGuid,winetest_interactive,0,0,0,1,0,0);
1296
1297     /* Testing 3D secondary buffers */
1298     test_secondary(lpGuid,winetest_interactive,1,0,0,0,0,0);
1299     test_secondary(lpGuid,winetest_interactive,1,1,0,0,0,0);
1300     test_secondary(lpGuid,winetest_interactive,1,1,0,1,0,0);
1301     test_secondary(lpGuid,winetest_interactive,1,0,1,0,0,0);
1302     test_secondary(lpGuid,winetest_interactive,1,0,1,1,0,0);
1303     test_secondary(lpGuid,winetest_interactive,1,1,1,0,0,0);
1304     test_secondary(lpGuid,winetest_interactive,1,1,1,1,0,0);
1305     test_secondary(lpGuid,winetest_interactive,1,1,1,0,1,0);
1306     test_secondary(lpGuid,winetest_interactive,1,1,1,0,0,1);
1307     test_secondary(lpGuid,winetest_interactive,1,1,1,0,1,1);
1308
1309     return 1;
1310 }
1311
1312 static void ds3d_tests(void)
1313 {
1314     HRESULT rc;
1315     rc=pDirectSoundEnumerateA(&dsenum_callback,NULL);
1316     ok(rc==DS_OK,"DirectSoundEnumerateA() failed: %08x\n",rc);
1317     trace("tested %u DirectSound drivers\n", driver_count);
1318 }
1319
1320 START_TEST(ds3d)
1321 {
1322     HMODULE hDsound;
1323
1324     CoInitialize(NULL);
1325
1326     hDsound = LoadLibrary("dsound.dll");
1327     if (hDsound)
1328     {
1329
1330         pDirectSoundEnumerateA = (void*)GetProcAddress(hDsound,
1331             "DirectSoundEnumerateA");
1332         pDirectSoundCreate = (void*)GetProcAddress(hDsound,
1333             "DirectSoundCreate");
1334
1335         ds3d_tests();
1336
1337         FreeLibrary(hDsound);
1338     }
1339     else
1340         skip("dsound.dll not found - skipping all tests\n");
1341
1342     CoUninitialize();
1343 }