Move control panel applet enumeration to cpanelfolder.c.
[wine] / dlls / dsound / tests / capture.c
1 /*
2  * Unit tests for capture functions
3  *
4  * Copyright (c) 2002 Francois Gouget
5  * Copyright (c) 2003 Robert Reif
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #define NONAMELESSSTRUCT
23 #define NONAMELESSUNION
24 #include <windows.h>
25
26 #include <math.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29
30 #include "wine/test.h"
31 #include "windef.h"
32 #include "wingdi.h"
33 #include "dsound.h"
34 #include "mmreg.h"
35 #include "dxerr9.h"
36
37 static const unsigned int formats[][3]={
38     { 8000,  8, 1},
39     { 8000,  8, 2},
40     { 8000, 16, 1},
41     { 8000, 16, 2},
42     {11025,  8, 1},
43     {11025,  8, 2},
44     {11025, 16, 1},
45     {11025, 16, 2},
46     {22050,  8, 1},
47     {22050,  8, 2},
48     {22050, 16, 1},
49     {22050, 16, 2},
50     {44100,  8, 1},
51     {44100,  8, 2},
52     {44100, 16, 1},
53     {44100, 16, 2},
54     {48000,  8, 1},
55     {48000,  8, 2},
56     {48000, 16, 1},
57     {48000, 16, 2},
58     {96000,  8, 1},
59     {96000,  8, 2},
60     {96000, 16, 1},
61     {96000, 16, 2}
62 };
63 #define NB_FORMATS (sizeof(formats)/sizeof(*formats))
64
65 #define NOTIFICATIONS    5
66
67 static void init_format(WAVEFORMATEX* wfx, int format, int rate, int depth, int channels)
68 {
69     wfx->wFormatTag=format;
70     wfx->nChannels=channels;
71     wfx->wBitsPerSample=depth;
72     wfx->nSamplesPerSec=rate;
73     wfx->nBlockAlign=wfx->nChannels*wfx->wBitsPerSample/8;
74     if (wfx->nBlockAlign==0)    /* align compressed formats to byte boundry */
75         wfx->nBlockAlign=1;
76     wfx->nAvgBytesPerSec=wfx->nSamplesPerSec*wfx->nChannels*wfx->wBitsPerSample/8;
77     wfx->cbSize=0;
78 }
79
80 static char * format_string(WAVEFORMATEX* wfx)
81 {
82     static char str[64];
83
84     sprintf(str, "%ldx%dx%d %s",
85         wfx->nSamplesPerSec, wfx->wBitsPerSample, wfx->nChannels,
86         wfx->wFormatTag == WAVE_FORMAT_PCM ? "PCM" :
87         wfx->wFormatTag == WAVE_FORMAT_MULAW ? "MULAW" :
88         wfx->wFormatTag == WAVE_FORMAT_IMA_ADPCM ? "IMA ADPCM" : "Unknown");
89
90     return str;
91 }
92
93 typedef struct {
94     char* wave;
95     DWORD wave_len;
96
97     LPDIRECTSOUNDCAPTUREBUFFER dscbo;
98     LPWAVEFORMATEX wfx;
99     DSBPOSITIONNOTIFY posnotify[NOTIFICATIONS];
100     HANDLE event[NOTIFICATIONS];
101     LPDIRECTSOUNDNOTIFY notify;
102
103     DWORD buffer_size;
104     DWORD read;
105     DWORD offset;
106     DWORD size;
107
108     DWORD last_pos;
109 } capture_state_t;
110
111 static int capture_buffer_service(capture_state_t* state)
112 {
113     HRESULT rc;
114     LPVOID ptr1,ptr2;
115     DWORD len1,len2;
116     DWORD capture_pos,read_pos;
117
118     rc=IDirectSoundCaptureBuffer_GetCurrentPosition(state->dscbo,&capture_pos,&read_pos);
119     ok(rc==DS_OK,"GetCurrentPosition failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
120     if (rc!=DS_OK)
121         return 0;
122
123     rc=IDirectSoundCaptureBuffer_Lock(state->dscbo,state->offset,state->size,&ptr1,&len1,&ptr2,&len2,0);
124     ok(rc==DS_OK,"Lock failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
125     if (rc!=DS_OK)
126         return 0;
127
128     rc=IDirectSoundCaptureBuffer_Unlock(state->dscbo,ptr1,len1,ptr2,len2);
129     ok(rc==DS_OK,"Unlock failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
130     if (rc!=DS_OK)
131         return 0;
132
133     state->offset = (state->offset + state->size) % state->buffer_size;
134
135     return 1;
136 }
137
138 static void test_capture_buffer(LPDIRECTSOUNDCAPTURE dsco, 
139                                 LPDIRECTSOUNDCAPTUREBUFFER dscbo, int record)
140 {
141     HRESULT rc;
142     DSCBCAPS dscbcaps;
143     WAVEFORMATEX wfx;
144     DWORD size,status;
145     capture_state_t state;
146     int i;
147
148     /* Private dsound.dll: Error: Invalid caps pointer */
149     rc=IDirectSoundCaptureBuffer_GetCaps(dscbo,0);
150     ok(rc==DSERR_INVALIDPARAM,"GetCaps should have failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
151
152     /* Private dsound.dll: Error: Invalid caps pointer */
153     dscbcaps.dwSize=0;
154     rc=IDirectSoundCaptureBuffer_GetCaps(dscbo,&dscbcaps);
155     ok(rc==DSERR_INVALIDPARAM,"GetCaps should have failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
156
157     dscbcaps.dwSize=sizeof(dscbcaps);
158     rc=IDirectSoundCaptureBuffer_GetCaps(dscbo,&dscbcaps);
159     ok(rc==DS_OK,"GetCaps failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
160     if (rc==DS_OK) {
161         trace("    Caps: size = %ld flags=0x%08lx buffer size=%ld\n",
162             dscbcaps.dwSize,dscbcaps.dwFlags,dscbcaps.dwBufferBytes);
163     }
164
165     /* Query the format size. Note that it may not match sizeof(wfx) */
166     /* Private dsound.dll: Error: Either pwfxFormat or pdwSizeWritten must be non-NULL */
167     rc=IDirectSoundCaptureBuffer_GetFormat(dscbo,NULL,0,NULL);
168     ok(rc==DSERR_INVALIDPARAM,
169        "GetFormat should have returned an error: rc=0x%lx(%s)\n",rc,DXGetErrorString9(rc));
170
171     size=0;
172     rc=IDirectSoundCaptureBuffer_GetFormat(dscbo,NULL,0,&size);
173     ok(rc==DS_OK && size!=0,
174        "GetFormat should have returned the needed size: rc=0x%lx(%s) size=%ld\n",
175        rc,DXGetErrorString9(rc),size);
176
177     rc=IDirectSoundCaptureBuffer_GetFormat(dscbo,&wfx,sizeof(wfx),NULL);
178     ok(rc==DS_OK,"GetFormat failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
179     if (rc==DS_OK) {
180         trace("    tag=0x%04x %ldx%dx%d avg.B/s=%ld align=%d\n",
181               wfx.wFormatTag,wfx.nSamplesPerSec,wfx.wBitsPerSample,
182               wfx.nChannels,wfx.nAvgBytesPerSec,wfx.nBlockAlign);
183     }
184
185     /* Private dsound.dll: Error: Invalid status pointer */
186     rc=IDirectSoundCaptureBuffer_GetStatus(dscbo,0);
187     ok(rc==DSERR_INVALIDPARAM,"GetStatus should have failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
188
189     rc=IDirectSoundCaptureBuffer_GetStatus(dscbo,&status);
190     ok(rc==DS_OK,"GetStatus failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
191     if (rc==DS_OK) {
192         trace("    status=0x%04lx\n",status);
193     }
194
195     ZeroMemory(&state, sizeof(state));
196     state.dscbo=dscbo;
197     state.wfx=&wfx;
198     state.buffer_size = dscbcaps.dwBufferBytes;
199     for (i = 0; i < NOTIFICATIONS; i++)
200         state.event[i] = CreateEvent( NULL, FALSE, FALSE, NULL );
201     state.size = dscbcaps.dwBufferBytes / NOTIFICATIONS;
202
203     rc=IDirectSoundCaptureBuffer_QueryInterface(dscbo,&IID_IDirectSoundNotify,(void **)&(state.notify));
204     ok((rc==DS_OK)&&(state.notify!=NULL),"QueryInterface failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
205     if (rc!=DS_OK)
206         return;
207
208     for (i = 0; i < NOTIFICATIONS; i++) {
209         state.posnotify[i].dwOffset = (i * state.size) + state.size - 1;
210         state.posnotify[i].hEventNotify = state.event[i];
211     }
212
213     rc=IDirectSoundNotify_SetNotificationPositions(state.notify,NOTIFICATIONS,state.posnotify);
214     ok(rc==DS_OK,"SetNotificationPositions failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
215     if (rc!=DS_OK)
216         return;
217
218     rc=IDirectSoundNotify_Release(state.notify);
219     ok(rc==0,"Release: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
220     if (rc!=0)
221         return;
222
223     if (record) {
224         rc=IDirectSoundCaptureBuffer_Start(dscbo,DSCBSTART_LOOPING);
225         ok(rc==DS_OK,"Start: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
226         if (rc!=DS_OK)
227             return;
228
229         rc=IDirectSoundCaptureBuffer_GetStatus(dscbo,&status);
230         ok(rc==DS_OK,"GetStatus failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
231         ok(status==(DSCBSTATUS_CAPTURING|DSCBSTATUS_LOOPING),
232            "GetStatus: bad status: %lx\n",status);
233         if (rc!=DS_OK)
234             return;
235
236         /* wait for the notifications */
237         for (i = 0; i < (NOTIFICATIONS * 2); i++) {
238             rc=MsgWaitForMultipleObjects( NOTIFICATIONS, state.event, FALSE, 3000, QS_ALLEVENTS );
239             ok(rc==(WAIT_OBJECT_0+(i%NOTIFICATIONS)),"MsgWaitForMultipleObjects failed: 0x%lx\n",rc);
240             if (rc!=(WAIT_OBJECT_0+(i%NOTIFICATIONS))) {
241                 ok((rc==WAIT_TIMEOUT)||(rc==WAIT_FAILED),"Wrong notification: should be %d, got %ld\n", 
242                     i%NOTIFICATIONS,rc-WAIT_OBJECT_0);
243             }
244             if (!capture_buffer_service(&state))
245                 break;
246         }
247
248         rc=IDirectSoundCaptureBuffer_Stop(dscbo);
249         ok(rc==DS_OK,"Stop: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
250         if (rc!=DS_OK)
251             return;
252     }
253 }
254
255 static BOOL WINAPI dscenum_callback(LPGUID lpGuid, LPCSTR lpcstrDescription,
256                                     LPCSTR lpcstrModule, LPVOID lpContext)
257 {
258     HRESULT rc;
259     LPDIRECTSOUNDCAPTURE dsco=NULL;
260     LPDIRECTSOUNDCAPTUREBUFFER dscbo=NULL;
261     DSCBUFFERDESC bufdesc;
262     WAVEFORMATEX wfx;
263     DSCCAPS dsccaps;
264     int f, ref;
265
266     /* Private dsound.dll: Error: Invalid interface buffer */
267     trace("Testing %s - %s\n",lpcstrDescription,lpcstrModule);
268     rc=DirectSoundCaptureCreate(lpGuid,NULL,NULL);
269     ok(rc==DSERR_INVALIDPARAM,"DirectSoundCaptureCreate didn't fail: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
270     if (rc==DS_OK) {
271         ref=IDirectSoundCapture_Release(dsco);
272         ok(ref==0,"IDirectSoundCapture_Release has %d references, should have 0\n",ref);
273     }
274
275     rc=DirectSoundCaptureCreate(lpGuid,&dsco,NULL);
276     ok((rc==DS_OK)||(rc==DSERR_NODRIVER),"DirectSoundCaptureCreate failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
277     if (rc!=DS_OK)
278         goto EXIT;
279
280     /* Private dsound.dll: Error: Invalid caps buffer */
281     rc=IDirectSoundCapture_GetCaps(dsco,NULL);
282     ok(rc==DSERR_INVALIDPARAM,"GetCaps should have failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
283
284     /* Private dsound.dll: Error: Invalid caps buffer */
285     dsccaps.dwSize=0;
286     rc=IDirectSoundCapture_GetCaps(dsco,&dsccaps);
287     ok(rc==DSERR_INVALIDPARAM,"GetCaps should have failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
288
289     dsccaps.dwSize=sizeof(dsccaps);
290     rc=IDirectSoundCapture_GetCaps(dsco,&dsccaps);
291     ok(rc==DS_OK,"GetCaps failed: 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
292     if (rc==DS_OK) {
293         trace("  DirectSoundCapture Caps: size=%ld flags=0x%08lx formats=%05lx channels=%ld\n",
294               dsccaps.dwSize,dsccaps.dwFlags,dsccaps.dwFormats,dsccaps.dwChannels);
295     }
296
297     /* Private dsound.dll: Error: Invalid size */
298     /* Private dsound.dll: Error: Invalid capture buffer description */
299     ZeroMemory(&bufdesc, sizeof(bufdesc));
300     bufdesc.dwSize=0;
301     bufdesc.dwFlags=0;
302     bufdesc.dwBufferBytes=0;
303     bufdesc.dwReserved=0;
304     bufdesc.lpwfxFormat=NULL;
305     rc=IDirectSoundCapture_CreateCaptureBuffer(dsco,&bufdesc,&dscbo,NULL);
306     ok(rc==DSERR_INVALIDPARAM,"CreateCaptureBuffer should have failed to create a capture buffer 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
307     if (rc==DS_OK) {
308         ref=IDirectSoundCaptureBuffer_Release(dscbo);
309         ok(ref==0,"IDirectSoundCaptureBuffer_Release has %d references, should have 0\n",ref);
310     }
311
312     /* Private dsound.dll: Error: Invalid buffer size */
313     /* Private dsound.dll: Error: Invalid capture buffer description */
314     ZeroMemory(&bufdesc, sizeof(bufdesc));
315     bufdesc.dwSize=sizeof(bufdesc);
316     bufdesc.dwFlags=0;
317     bufdesc.dwBufferBytes=0;
318     bufdesc.dwReserved=0;
319     bufdesc.lpwfxFormat=NULL;
320     rc=IDirectSoundCapture_CreateCaptureBuffer(dsco,&bufdesc,&dscbo,NULL);
321     ok(rc==DSERR_INVALIDPARAM,"CreateCaptureBuffer should have failed to create a capture buffer 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
322     if (rc==DS_OK) {
323         ref=IDirectSoundCaptureBuffer_Release(dscbo);
324         ok(ref==0,"IDirectSoundCaptureBuffer_Release has %d references, should have 0\n",ref);
325     }
326
327     /* Private dsound.dll: Error: Invalid buffer size */
328     /* Private dsound.dll: Error: Invalid capture buffer description */
329     ZeroMemory(&bufdesc, sizeof(bufdesc));
330     ZeroMemory(&wfx, sizeof(wfx));
331     bufdesc.dwSize=sizeof(bufdesc);
332     bufdesc.dwFlags=0;
333     bufdesc.dwBufferBytes=0;
334     bufdesc.dwReserved=0;
335     bufdesc.lpwfxFormat=&wfx;
336     rc=IDirectSoundCapture_CreateCaptureBuffer(dsco,&bufdesc,&dscbo,NULL);
337     ok(rc==DSERR_INVALIDPARAM,"CreateCaptureBuffer should have failed to create a capture buffer 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
338     if (rc==DS_OK) {
339         ref=IDirectSoundCaptureBuffer_Release(dscbo);
340         ok(ref==0,"IDirectSoundCaptureBuffer_Release has %d references, should have 0\n",ref);
341     }
342
343     /* Private dsound.dll: Error: Invalid buffer size */
344     /* Private dsound.dll: Error: Invalid capture buffer description */
345     init_format(&wfx,WAVE_FORMAT_PCM,11025,8,1);
346     ZeroMemory(&bufdesc, sizeof(bufdesc));
347     bufdesc.dwSize=sizeof(bufdesc);
348     bufdesc.dwFlags=0;
349     bufdesc.dwBufferBytes=0;
350     bufdesc.dwReserved=0;
351     bufdesc.lpwfxFormat=&wfx;
352     rc=IDirectSoundCapture_CreateCaptureBuffer(dsco,&bufdesc,&dscbo,NULL);
353     ok(rc==DSERR_INVALIDPARAM,"CreateCaptureBuffer should have failed to create a capture buffer 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
354     if (rc==DS_OK) {
355         ref=IDirectSoundCaptureBuffer_Release(dscbo);
356         ok(ref==0,"IDirectSoundCaptureBuffer_Release has %d references, should have 0\n",ref);
357     }
358
359     for (f=0;f<NB_FORMATS;f++) {
360         dscbo=NULL;
361         init_format(&wfx,WAVE_FORMAT_PCM,formats[f][0],formats[f][1],formats[f][2]);
362         ZeroMemory(&bufdesc, sizeof(bufdesc));
363         bufdesc.dwSize=sizeof(bufdesc);
364         bufdesc.dwFlags=0;
365         bufdesc.dwBufferBytes=wfx.nAvgBytesPerSec;
366         bufdesc.dwReserved=0;
367         bufdesc.lpwfxFormat=&wfx;
368         trace("  Testing the capture buffer at %s\n", format_string(&wfx));
369         rc=IDirectSoundCapture_CreateCaptureBuffer(dsco,&bufdesc,&dscbo,NULL);
370         ok((rc==DS_OK)&&(dscbo!=NULL),"CreateCaptureBuffer failed to create a capture buffer 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
371         if (rc==DS_OK) {
372             test_capture_buffer(dsco, dscbo, winetest_interactive);
373             ref=IDirectSoundCaptureBuffer_Release(dscbo);
374             ok(ref==0,"IDirectSoundCaptureBuffer_Release has %d references, should have 0\n",ref);
375         }
376     }
377
378     /* try a non PCM format */
379 #if 0
380     init_format(&wfx,WAVE_FORMAT_MULAW,8000,8,1);
381     ZeroMemory(&bufdesc, sizeof(bufdesc));
382     bufdesc.dwSize=sizeof(bufdesc);
383     bufdesc.dwFlags=DSCBCAPS_WAVEMAPPED;
384     bufdesc.dwBufferBytes=wfx.nAvgBytesPerSec;
385     bufdesc.dwReserved=0;
386     bufdesc.lpwfxFormat=&wfx;
387     trace("  Testing the capture buffer at %s\n", format_string(&wfx));
388     rc=IDirectSoundCapture_CreateCaptureBuffer(dsco,&bufdesc,&dscbo,NULL);
389     ok((rc==DS_OK)&&(dscbo!=NULL),"CreateCaptureBuffer failed to create a capture buffer 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
390     if ((rc==DS_OK)&&(dscbo!=NULL)) {
391         test_capture_buffer(dsco, dscbo, winetest_interactive);
392         ref=IDirectSoundCaptureBuffer_Release(dscbo);
393         ok(ref==0,"IDirectSoundCaptureBuffer_Release has %d references, should have 0\n",ref);
394     }
395 #endif
396
397     /* Try an invalid format to test error handling */
398 #if 0
399     init_format(&wfx,WAVE_FORMAT_PCM,2000000,16,2);
400     ZeroMemory(&bufdesc, sizeof(bufdesc));
401     bufdesc.dwSize=sizeof(bufdesc);
402     bufdesc.dwFlags=DSCBCAPS_WAVEMAPPED;
403     bufdesc.dwBufferBytes=wfx.nAvgBytesPerSec;
404     bufdesc.dwReserved=0;
405     bufdesc.lpwfxFormat=&wfx;
406     trace("  Testing the capture buffer at %s\n", format_string(&wfx));
407     rc=IDirectSoundCapture_CreateCaptureBuffer(dsco,&bufdesc,&dscbo,NULL);
408     ok(rc!=DS_OK,"CreateCaptureBuffer should have failed at 2 MHz 0x%lx(%s)\n",rc,DXGetErrorString9(rc));
409 #endif
410
411 EXIT:
412     if (dsco!=NULL) {
413         ref=IDirectSoundCapture_Release(dsco);
414         ok(ref==0,"IDirectSoundCapture_Release has %d references, should have 0\n",ref);
415     }
416
417     return TRUE;
418 }
419
420 static void capture_tests()
421 {
422     HRESULT rc;
423     rc=DirectSoundCaptureEnumerateA(&dscenum_callback,NULL);
424     ok(rc==DS_OK,"DirectSoundCaptureEnumerate failed: 0x%08lx(%s)\n",rc,DXGetErrorString9(rc));
425 }
426
427 START_TEST(capture)
428 {
429     capture_tests();
430 }