ok() does not support '%S'. Store the Ansi version, convert to Unicode
[wine] / dlls / dsound / tests / dsound.c
1 /*
2  * Unit tests for dsound functions
3  *
4  * Copyright (c) 2002 Francois Gouget
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <malloc.h>
22 #include <math.h>
23
24 #include "wine/test.h"
25 #include "dsound.h"
26
27 /* http://www.gamedev.net/reference/articles/article710.asp */
28
29 /* The time slice determines how often we will service the buffer and the
30  * buffer will be four time slices long
31  */
32 #define TIME_SLICE    100
33 #define BUFFER_LEN    (4*TIME_SLICE)
34 #define TONE_DURATION (6*TIME_SLICE)
35
36 /* This test can play a test tone. But this only makes sense if someone
37  * is going to carefully listen to it, and would only bother everyone else.
38  * So this is only done if the test is being run in interactive mode.
39  */
40
41 #define PI 3.14159265358979323846
42 static char* wave_generate_la(WAVEFORMATEX* wfx, double duration, DWORD* size)
43 {
44     int i;
45     int nb_samples;
46     char* buf;
47     char* b;
48
49     nb_samples=(int)(duration*wfx->nSamplesPerSec);
50     *size=nb_samples*wfx->nBlockAlign;
51     b=buf=malloc(*size);
52     for (i=0;i<nb_samples;i++) {
53         double y=sin(440.0*2*PI*i/wfx->nSamplesPerSec);
54         if (wfx->wBitsPerSample==8) {
55             unsigned char sample=(unsigned char)((double)127.5*(y+1.0));
56             *b++=sample;
57             if (wfx->nChannels==2)
58                *b++=sample;
59         } else {
60             signed short sample=(signed short)((double)32767.5*y-0.5);
61             b[0]=sample & 0xff;
62             b[1]=sample >> 8;
63             b+=2;
64             if (wfx->nChannels==2) {
65                 b[0]=sample & 0xff;
66                 b[1]=sample >> 8;
67                 b+=2;
68             }
69         }
70     }
71     return buf;
72 }
73
74 static HWND get_hwnd()
75 {
76     HWND hwnd=GetForegroundWindow();
77     if (!hwnd)
78         hwnd=GetDesktopWindow();
79     return hwnd;
80 }
81
82 static void init_format(WAVEFORMATEX* wfx, int rate, int depth, int channels)
83 {
84     wfx->wFormatTag=WAVE_FORMAT_PCM;
85     wfx->nChannels=channels;
86     wfx->wBitsPerSample=depth;
87     wfx->nSamplesPerSec=rate;
88     wfx->nBlockAlign=wfx->nChannels*wfx->wBitsPerSample/8;
89     wfx->nAvgBytesPerSec=wfx->nSamplesPerSec*wfx->nBlockAlign;
90     wfx->cbSize=0;
91 }
92
93 typedef struct {
94     char* wave;
95     DWORD wave_len;
96
97     LPDIRECTSOUNDBUFFER dsbo;
98     LPWAVEFORMATEX wfx;
99     DWORD buffer_size;
100     DWORD written;
101     DWORD offset;
102
103     DWORD last_pos;
104 } play_state_t;
105
106 static int buffer_refill(play_state_t* state, DWORD size)
107 {
108     LPVOID ptr1,ptr2;
109     DWORD len1,len2;
110     HRESULT rc;
111
112     if (size>state->wave_len-state->written)
113         size=state->wave_len-state->written;
114
115     rc=IDirectSoundBuffer_Lock(state->dsbo,state->offset,size,
116                                &ptr1,&len1,&ptr2,&len2,0);
117     ok(rc==DS_OK,"Lock: 0x%lx",rc);
118     if (rc!=DS_OK)
119         return -1;
120
121     memcpy(ptr1,state->wave+state->written,len1);
122     state->written+=len1;
123     if (ptr2!=NULL) {
124         memcpy(ptr2,state->wave+state->written,len2);
125         state->written+=len2;
126     }
127     state->offset=state->written % state->buffer_size;
128     rc=IDirectSoundBuffer_Unlock(state->dsbo,ptr1,len1,ptr2,len2);
129     ok(rc==DS_OK,"Unlock: 0x%lx",rc);
130     if (rc!=DS_OK)
131         return -1;
132     return size;
133 }
134
135 static int buffer_silence(play_state_t* state, DWORD size)
136 {
137     LPVOID ptr1,ptr2;
138     DWORD len1,len2;
139     HRESULT rc;
140     BYTE s;
141
142     rc=IDirectSoundBuffer_Lock(state->dsbo,state->offset,size,
143                                &ptr1,&len1,&ptr2,&len2,0);
144     ok(rc==DS_OK,"Lock: 0x%lx",rc);
145     if (rc!=DS_OK)
146         return -1;
147
148     s=(state->wfx->wBitsPerSample==8?0x80:0);
149     memset(ptr1,s,len1);
150     if (ptr2!=NULL) {
151         memset(ptr2,s,len2);
152     }
153     state->offset=(state->offset+size) % state->buffer_size;
154     rc=IDirectSoundBuffer_Unlock(state->dsbo,ptr1,len1,ptr2,len2);
155     ok(rc==DS_OK,"Unlock: 0x%lx",rc);
156     if (rc!=DS_OK)
157         return -1;
158     return size;
159 }
160
161 static int buffer_service(play_state_t* state)
162 {
163     DWORD play_pos,write_pos,buf_free;
164     HRESULT rc;
165
166     rc=IDirectSoundBuffer_GetCurrentPosition(state->dsbo,&play_pos,&write_pos);
167     ok(rc==DS_OK,"GetCurrentPosition: %lx",rc);
168     if (rc!=DS_OK) {
169         goto STOP;
170     }
171
172     /* Refill the buffer */
173     if (state->offset<=play_pos) {
174         buf_free=play_pos-state->offset;
175     } else {
176         buf_free=state->buffer_size-state->offset+play_pos;
177     }
178     if (winetest_debug > 1)
179         trace("buf pos=%ld free=%ld written=%ld / %ld\n",
180               play_pos,buf_free,state->written,state->wave_len);
181     if (buf_free==0)
182         return 1;
183
184     if (state->written<state->wave_len) {
185         int w=buffer_refill(state,buf_free);
186         if (w==-1)
187             goto STOP;
188         buf_free-=w;
189         if (state->written==state->wave_len) {
190             state->last_pos=(state->offset<play_pos)?play_pos:0;
191             if (winetest_debug > 1)
192                 trace("last sound byte at %ld\n",
193                       (state->written % state->buffer_size));
194         }
195     } else {
196         if (state->last_pos!=0 && play_pos<state->last_pos) {
197             /* We wrapped around the end of the buffer */
198             state->last_pos=0;
199         }
200         if (state->last_pos==0 &&
201             play_pos>(state->written % state->buffer_size)) {
202             /* Now everything has been played */
203             goto STOP;
204         }
205     }
206
207     if (buf_free>0) {
208         /* Fill with silence */
209         if (winetest_debug > 1)
210             trace("writing %ld bytes of silence\n",buf_free);
211         if (buffer_silence(state,buf_free)==-1)
212             goto STOP;
213     }
214     return 1;
215
216 STOP:
217     if (winetest_debug > 1)
218         trace("stopping playback\n");
219     rc=IDirectSoundBuffer_Stop(state->dsbo);
220     ok(rc==DS_OK,"Stop failed: rc=%ld",rc);
221     return 0;
222 }
223
224 static void test_buffer(LPDIRECTSOUND dso, LPDIRECTSOUNDBUFFER dsbo,
225                         int primary, int play)
226 {
227     HRESULT rc;
228     DSBCAPS dsbcaps;
229     WAVEFORMATEX wfx,wfx2;
230     DWORD size,status,freq;
231
232     dsbcaps.dwSize=0;
233     rc=IDirectSoundBuffer_GetCaps(dsbo,&dsbcaps);
234     ok(rc==DSERR_INVALIDPARAM,"GetCaps should have failed: 0x%lx\n",rc);
235
236     dsbcaps.dwSize=sizeof(dsbcaps);
237     rc=IDirectSoundBuffer_GetCaps(dsbo,&dsbcaps);
238     ok(rc==DS_OK,"GetCaps failed: 0x%lx\n",rc);
239     if (rc==DS_OK) {
240         trace("    Caps: flags=0x%08lx size=%ld\n",dsbcaps.dwFlags,
241               dsbcaps.dwBufferBytes);
242     }
243
244     /* Query the format size. Note that it may not match sizeof(wfx) */
245     size=0;
246     rc=IDirectSoundBuffer_GetFormat(dsbo,NULL,0,&size);
247     ok(rc==DS_OK && size!=0,
248        "GetFormat should have returned the needed size: rc=0x%lx size=%ld\n",
249        rc,size);
250
251     rc=IDirectSoundBuffer_GetFormat(dsbo,&wfx,sizeof(wfx),NULL);
252     ok(rc==DS_OK,"GetFormat failed: 0x%lx\n",rc);
253     if (rc==DS_OK) {
254         trace("    tag=0x%04x %ldx%dx%d avg.B/s=%ld align=%d\n",
255               wfx.wFormatTag,wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels,
256               wfx.nAvgBytesPerSec,wfx.nBlockAlign);
257     }
258
259     rc=IDirectSoundBuffer_GetFrequency(dsbo,&freq);
260     ok(rc==DS_OK || rc==DSERR_CONTROLUNAVAIL,"GetFrequency failed: 0x%lx\n",rc);
261     if (rc==DS_OK) {
262         ok(freq==wfx.nSamplesPerSec,
263            "The frequency returned by GetFrequency %ld does not match the format %ld\n",
264            freq,wfx.nSamplesPerSec);
265     }
266
267     rc=IDirectSoundBuffer_GetStatus(dsbo,&status);
268     ok(rc==DS_OK,"GetStatus failed: 0x%lx\n",rc);
269     if (rc==DS_OK) {
270         trace("    status=0x%04lx\n",status);
271     }
272
273     if (primary) {
274         /* We must call SetCooperativeLevel to be allowed to call SetFormat */
275         rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_PRIORITY);
276         ok(rc==DS_OK,"SetCooperativeLevel failed: 0x%lx\n",rc);
277         if (rc!=DS_OK)
278             return;
279
280         init_format(&wfx2,11025,16,2);
281         rc=IDirectSoundBuffer_SetFormat(dsbo,&wfx2);
282         ok(rc==DS_OK,"SetFormat failed: 0x%lx\n",rc);
283
284         rc=IDirectSoundBuffer_GetFormat(dsbo,&wfx,sizeof(wfx),NULL);
285         ok(rc==DS_OK,"GetFormat failed: 0x%lx\n",rc);
286         if (rc==DS_OK) {
287             ok(wfx.wFormatTag==wfx2.wFormatTag &&
288                wfx.nChannels==wfx2.nChannels &&
289                wfx.wBitsPerSample==wfx2.wBitsPerSample &&
290                wfx.nSamplesPerSec==wfx2.nSamplesPerSec &&
291                wfx.nBlockAlign==wfx2.nBlockAlign &&
292                wfx.nAvgBytesPerSec==wfx2.nAvgBytesPerSec,
293                "SetFormat did not work right: tag=0x%04x %ldx%dx%d avg.B/s=%ld align=%d\n",
294                wfx.wFormatTag,wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels,
295                wfx.nAvgBytesPerSec,wfx.nBlockAlign);
296         }
297
298         /* Set the CooperativeLevel back to normal */
299         rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
300         ok(rc==DS_OK,"SetCooperativeLevel failed: 0x%lx\n",rc);
301     }
302
303     if (play) {
304         play_state_t state;
305         LONG volume;
306
307         trace("    Playing 440Hz LA at %ldx%2dx%d\n",
308               wfx.nSamplesPerSec, wfx.wBitsPerSample,wfx.nChannels);
309
310         if (primary) {
311             /* We must call SetCooperativeLevel to be allowed to call Lock */
312             rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_WRITEPRIMARY);
313             ok(rc==DS_OK,"SetCooperativeLevel failed: 0x%lx\n",rc);
314             if (rc!=DS_OK)
315                 return;
316         }
317
318         if (dsbcaps.dwFlags & DSBCAPS_CTRLVOLUME) {
319             rc=IDirectSoundBuffer_GetVolume(dsbo,&volume);
320             ok(rc==DS_OK,"GetVolume failed: 0x%lx\n",rc);
321             if (rc==DS_OK) {
322                 trace("    volume=%ld\n",volume);
323             }
324
325             rc=IDirectSoundBuffer_SetVolume(dsbo,-1200);
326             ok(rc==DS_OK,"SetVolume failed: 0x%lx\n",rc);
327         }
328
329         state.wave=wave_generate_la(&wfx,((double)TONE_DURATION)/1000,&state.wave_len);
330
331         state.dsbo=dsbo;
332         state.wfx=&wfx;
333         state.buffer_size=dsbcaps.dwBufferBytes;
334         state.written=state.offset=0;
335         buffer_refill(&state,state.buffer_size);
336
337         rc=IDirectSoundBuffer_Play(dsbo,0,0,DSBPLAY_LOOPING);
338         ok(rc==DS_OK,"Play: 0x%lx\n",rc);
339
340         rc=IDirectSoundBuffer_GetStatus(dsbo,&status);
341         ok(rc==DS_OK,"GetStatus failed: 0x%lx\n",rc);
342         ok(status==(DSBSTATUS_PLAYING|DSBSTATUS_LOOPING),
343            "GetStatus: bad status: %lx",status);
344
345         while (buffer_service(&state)) {
346             WaitForSingleObject(GetCurrentProcess(),TIME_SLICE/2);
347         }
348
349         if (dsbcaps.dwFlags & DSBCAPS_CTRLVOLUME) {
350             rc=IDirectSoundBuffer_SetVolume(dsbo,volume);
351             ok(rc==DS_OK,"SetVolume failed: 0x%lx\n",rc);
352         }
353
354         free(state.wave);
355         if (primary) {
356             /* Set the CooperativeLevel back to normal */
357             rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
358             ok(rc==DS_OK,"SetCooperativeLevel failed: 0x%lx\n",rc);
359         }
360     }
361 }
362
363 static BOOL WINAPI dsenum_callback(LPGUID lpGuid, LPCSTR lpcstrDescription,
364                                    LPCSTR lpcstrModule, LPVOID lpContext)
365 {
366     HRESULT rc;
367     LPDIRECTSOUND dso=NULL;
368     LPDIRECTSOUNDBUFFER dsbo=NULL;
369     DSBUFFERDESC bufdesc;
370     WAVEFORMATEX wfx;
371     DSCAPS dscaps;
372
373     trace("Testing %s - %s\n",lpcstrDescription,lpcstrModule);
374     rc=DirectSoundCreate(lpGuid,&dso,NULL);
375     ok(rc==DS_OK,"DirectSoundCreate failed: 0x%lx\n",rc);
376     if (rc!=DS_OK)
377         goto EXIT;
378
379     dscaps.dwSize=0;
380     rc=IDirectSound_GetCaps(dso,&dscaps);
381     ok(rc==DSERR_INVALIDPARAM,"GetCaps should have failed: 0x%lx\n",rc);
382
383     dscaps.dwSize=sizeof(dscaps);
384     rc=IDirectSound_GetCaps(dso,&dscaps);
385     ok(rc==DS_OK,"GetCaps failed: 0x%lx\n",rc);
386     if (rc==DS_OK) {
387         trace("  DirectSound Caps: flags=0x%08lx secondary min=%ld max=%ld\n",
388               dscaps.dwFlags,dscaps.dwMinSecondarySampleRate,
389               dscaps.dwMaxSecondarySampleRate);
390     }
391
392     /* Testing the primary buffer */
393     bufdesc.dwSize=sizeof(bufdesc);
394     bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER;
395     bufdesc.dwBufferBytes=0;
396     bufdesc.dwReserved=0;
397     bufdesc.lpwfxFormat=NULL;
398     trace("  Testing the primary buffer\n");
399     rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&dsbo,NULL);
400     ok(rc==DS_OK,"CreateSoundBuffer failed to create a primary buffer 0x%lx\n",rc);
401     if (rc==DS_OK) {
402         test_buffer(dso,dsbo,1,winetest_interactive && !(dscaps.dwFlags & DSCAPS_EMULDRIVER));
403         IDirectSoundBuffer_Release(dsbo);
404     }
405
406     /* Testing secondary buffers */
407     init_format(&wfx,11025,8,1);
408     bufdesc.dwSize=sizeof(bufdesc);
409     bufdesc.dwFlags=DSBCAPS_CTRLDEFAULT|DSBCAPS_GETCURRENTPOSITION2;
410     bufdesc.dwBufferBytes=wfx.nAvgBytesPerSec*BUFFER_LEN/1000;
411     bufdesc.dwReserved=0;
412     bufdesc.lpwfxFormat=&wfx;
413     trace("  Testing a secondary buffer at %ldx%dx%d\n",
414           wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels);
415     rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&dsbo,NULL);
416     ok(rc==DS_OK,"CreateSoundBuffer failed to create a secondary buffer 0x%lx\n",rc);
417     if (rc==DS_OK) {
418         test_buffer(dso,dsbo,0,winetest_interactive);
419         IDirectSoundBuffer_Release(dsbo);
420     }
421
422     init_format(&wfx,48000,16,2);
423     bufdesc.dwSize=sizeof(bufdesc);
424     bufdesc.dwFlags=DSBCAPS_CTRLDEFAULT|DSBCAPS_GETCURRENTPOSITION2;
425     bufdesc.dwBufferBytes=wfx.nAvgBytesPerSec*BUFFER_LEN/1000;
426     bufdesc.dwReserved=0;
427     bufdesc.lpwfxFormat=&wfx;
428     trace("  Testing a secondary buffer at %ldx%dx%d\n",
429           wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels);
430     rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&dsbo,NULL);
431     ok(rc==DS_OK,"CreateSoundBuffer failed to create a secondary buffer 0x%lx\n",rc);
432     if (rc==DS_OK) {
433         test_buffer(dso,dsbo,0,winetest_interactive);
434         IDirectSoundBuffer_Release(dsbo);
435     }
436
437 EXIT:
438     if (dso!=NULL)
439         IDirectSound_Release(dso);
440     return 1;
441 }
442
443 static void dsound_out_tests()
444 {
445     HRESULT rc;
446     rc=DirectSoundEnumerateA(&dsenum_callback,NULL);
447     ok(rc==DS_OK,"DirectSoundEnumerate failed: %ld\n",rc);
448 }
449
450 START_TEST(dsound)
451 {
452     dsound_out_tests();
453 }