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