Add 'audiophile' DirectSound tests
Francois Gouget
fgouget at codeweavers.com
Sat Dec 28 02:16:28 CST 2002
This patch adds tests similar to those in the winmm test. More
precisely, when in interactive mode (see recent patch) the dsound test
will now play a 440Hz tone three times for each DirectSound device that
it finds:
* once using a primary buffer (except if it's an emulated buffer
because such buffers don't support it)
* once using a secondary buffer and an 11025x16x2 signal
* once using a secondary buffer and a 48000x16x2 signal
This is also an opportunity to make a few calls to {Get,Set}Volume,
GetStatus and a few other similar methods.
So if everything goes fine you should hear the same tone being played 3
or more times, possibly with the first time in the three being louder if
your primary buffer does not support volume adjustment.
For now the test uses what is essentially a timer to refill the sound
buffer periodically. Later I hope to modify it to use
IDirectSoundNotify. But I preferred to keep things simple at first :-)
Changelog:
* dlls/dsound/tests/dsound.c
Add the ability of playing a test tone when in interactive mode
Test both primary and secondary buffers
--
Francois Gouget
fgouget at codeweavers.com
-------------- next part --------------
Index: dlls/dsound/tests/dsound.c
===================================================================
RCS file: /home/wine/wine/dlls/dsound/tests/dsound.c,v
retrieving revision 1.2
diff -u -r1.2 dsound.c
--- dlls/dsound/tests/dsound.c 13 Dec 2002 20:27:36 -0000 1.2
+++ dlls/dsound/tests/dsound.c 28 Dec 2002 07:57:36 -0000
@@ -18,57 +18,221 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <malloc.h>
+#include <math.h>
+
#include "wine/test.h"
#include "dsound.h"
+/* http://www.gamedev.net/reference/articles/article710.asp */
+
+/* Winelib does not support importing variables so we have to define
+ * our own here
+ */
+const GUID MY_GUID_NULL = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
+
+/* The time slice determines how often we will service the buffer and the
+ * buffer will be four time slices long
+ */
+#define TIME_SLICE 100
+#define BUFFER_LEN (4*TIME_SLICE)
+#define TONE_DURATION (6*TIME_SLICE)
+
+/* This test can play a test tone. But this only makes sense if someone
+ * is going to carefully listen to it, and would only bother everyone else.
+ * So this is only done if the test is being run in interactive mode.
+ */
+
+#define PI 3.14159265358979323846
+static char* wave_generate_la(WAVEFORMATEX* wfx, double duration, DWORD* size)
+{
+ int i;
+ int nb_samples;
+ char* buf;
+ char* b;
+
+ nb_samples=(int)(duration*wfx->nSamplesPerSec);
+ *size=nb_samples*wfx->nBlockAlign;
+ b=buf=malloc(*size);
+ for (i=0;i<nb_samples;i++) {
+ double y=sin(440.0*2*PI*i/wfx->nSamplesPerSec);
+ if (wfx->wBitsPerSample==8) {
+ unsigned char sample=(unsigned char)((double)127.5*(y+1.0));
+ *b++=sample;
+ if (wfx->nChannels==2)
+ *b++=sample;
+ } else {
+ signed short sample=(signed short)((double)32767.5*y-0.5);
+ b[0]=sample & 0xff;
+ b[1]=sample >> 8;
+ b+=2;
+ if (wfx->nChannels==2) {
+ b[0]=sample & 0xff;
+ b[1]=sample >> 8;
+ b+=2;
+ }
+ }
+ }
+ return buf;
+}
+
+static HWND get_hwnd()
+{
+ HWND hwnd=GetForegroundWindow();
+ if (!hwnd)
+ hwnd=GetDesktopWindow();
+ return hwnd;
+}
+
+static void init_format(WAVEFORMATEX* wfx, int rate, int depth, int channels)
+{
+ wfx->wFormatTag=WAVE_FORMAT_PCM;
+ wfx->nChannels=channels;
+ wfx->wBitsPerSample=depth;
+ wfx->nSamplesPerSec=rate;
+ wfx->nBlockAlign=wfx->nChannels*wfx->wBitsPerSample/8;
+ wfx->nAvgBytesPerSec=wfx->nSamplesPerSec*wfx->nBlockAlign;
+ wfx->cbSize=0;
+}
+
+typedef struct {
+ char* wave;
+ DWORD wave_len;
+
+ LPDIRECTSOUNDBUFFER dsbo;
+ LPWAVEFORMATEX wfx;
+ DWORD buffer_size;
+ DWORD written;
+ DWORD offset;
+ DWORD last_pos;
+} play_state_t;
-BOOL WINAPI dsenum_callback(LPGUID lpGuid, LPCSTR lpcstrDescription,
- LPCSTR lpcstrModule, LPVOID lpContext)
+static int buffer_refill(play_state_t* state, DWORD size)
{
+ LPVOID ptr1,ptr2;
+ DWORD len1,len2;
HRESULT rc;
- LPDIRECTSOUND dso=NULL;
- LPDIRECTSOUNDBUFFER dsbo=NULL;
- DSCAPS dscaps;
- DSBCAPS dsbcaps;
- DSBUFFERDESC bufdesc;
- WAVEFORMATEX wfx,wfx2;
- DWORD size,status,freq;
- trace("Testing %s - %s\n",lpcstrDescription,lpcstrModule);
- rc=DirectSoundCreate(lpGuid,&dso,NULL);
- ok(rc==DS_OK,"DirectSoundCreate failed: 0x%lx\n",rc);
- if (rc!=DS_OK)
- goto EXIT;
+ if (size>state->wave_len-state->written)
+ size=state->wave_len-state->written;
- dscaps.dwSize=0;
- rc=IDirectSound_GetCaps(dso,&dscaps);
- ok(rc==DSERR_INVALIDPARAM,"GetCaps should have failed: 0x%lx\n",rc);
+ rc=IDirectSoundBuffer_Lock(state->dsbo,state->offset,size,
+ &ptr1,&len1,&ptr2,&len2,0);
+ ok(rc==DS_OK,"Lock: 0x%lx",rc);
+ if (rc!=DS_OK)
+ return -1;
- dscaps.dwSize=sizeof(dscaps);
- rc=IDirectSound_GetCaps(dso,&dscaps);
- ok(rc==DS_OK,"GetCaps failed: 0x%lx\n",rc);
- if (rc==DS_OK) {
- trace(" DirectSound Caps: flags=0x%08lx secondary min=%ld max=%ld\n",
- dscaps.dwFlags,dscaps.dwMinSecondarySampleRate,
- dscaps.dwMaxSecondarySampleRate);
+ memcpy(ptr1,state->wave+state->written,len1);
+ state->written+=len1;
+ if (ptr2!=NULL) {
+ memcpy(ptr2,state->wave+state->written,len2);
+ state->written+=len2;
}
+ state->offset=state->written % state->buffer_size;
+ rc=IDirectSoundBuffer_Unlock(state->dsbo,ptr1,len1,ptr2,len2);
+ ok(rc==DS_OK,"Unlock: 0x%lx",rc);
+ if (rc!=DS_OK)
+ return -1;
+ return size;
+}
+
+static int buffer_silence(play_state_t* state, DWORD size)
+{
+ LPVOID ptr1,ptr2;
+ DWORD len1,len2;
+ HRESULT rc;
+ BYTE s;
- /* Testing the primary buffers */
- rc=IDirectSound_SetCooperativeLevel(dso,GetDesktopWindow(),DSSCL_PRIORITY);
- ok(rc==DS_OK,"SetCooperativeLevel failed: 0x%lx\n",rc);
+ rc=IDirectSoundBuffer_Lock(state->dsbo,state->offset,size,
+ &ptr1,&len1,&ptr2,&len2,0);
+ ok(rc==DS_OK,"Lock: 0x%lx",rc);
if (rc!=DS_OK)
- goto EXIT;
+ return -1;
- bufdesc.dwSize=sizeof(bufdesc);
- bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER;
- bufdesc.dwBufferBytes=0;
- bufdesc.dwReserved=0;
- bufdesc.lpwfxFormat=NULL;
- rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&dsbo,NULL);
- ok(rc==DS_OK,"CreateSoundBuffer failed to create a primary buffer 0x%lx\n",rc);
+ s=(state->wfx->wBitsPerSample==8?0x80:0);
+ memset(ptr1,s,len1);
+ if (ptr2!=NULL) {
+ memset(ptr2,s,len2);
+ }
+ state->offset=(state->offset+size) % state->buffer_size;
+ rc=IDirectSoundBuffer_Unlock(state->dsbo,ptr1,len1,ptr2,len2);
+ ok(rc==DS_OK,"Unlock: 0x%lx",rc);
if (rc!=DS_OK)
- goto EXIT;
+ return -1;
+ return size;
+}
+
+static int buffer_service(play_state_t* state)
+{
+ DWORD play_pos,write_pos,buf_free;
+ HRESULT rc;
+
+ rc=IDirectSoundBuffer_GetCurrentPosition(state->dsbo,&play_pos,&write_pos);
+ ok(rc==DS_OK,"GetCurrentPosition: %lx",rc);
+ if (rc!=DS_OK) {
+ goto STOP;
+ }
+
+ /* Refill the buffer */
+ if (state->offset<=play_pos) {
+ buf_free=play_pos-state->offset;
+ } else {
+ buf_free=state->buffer_size-state->offset+play_pos;
+ }
+ if (winetest_debug > 1)
+ trace("buf pos=%ld free=%ld written=%ld / %ld\n",
+ play_pos,buf_free,state->written,state->wave_len);
+ if (buf_free==0)
+ return 1;
+
+ if (state->written<state->wave_len) {
+ int w=buffer_refill(state,buf_free);
+ if (w==-1)
+ goto STOP;
+ buf_free-=w;
+ if (state->written==state->wave_len) {
+ state->last_pos=(state->offset<play_pos)?play_pos:0;
+ if (winetest_debug > 1)
+ trace("last sound byte at %ld\n",
+ (state->written % state->buffer_size));
+ }
+ } else {
+ if (state->last_pos!=0 && play_pos<state->last_pos) {
+ /* We wrapped around the end of the buffer */
+ state->last_pos=0;
+ }
+ if (state->last_pos==0 &&
+ play_pos>(state->written % state->buffer_size)) {
+ /* Now everything has been played */
+ goto STOP;
+ }
+ }
+
+ if (buf_free>0) {
+ /* Fill with silence */
+ if (winetest_debug > 1)
+ trace("writing %ld bytes of silence\n",buf_free);
+ if (buffer_silence(state,buf_free)==-1)
+ goto STOP;
+ }
+ return 1;
+
+STOP:
+ if (winetest_debug > 1)
+ trace("stopping playback\n");
+ rc=IDirectSoundBuffer_Stop(state->dsbo);
+ ok(rc==DS_OK,"Stop failed: rc=%ld",rc);
+ return 0;
+}
+
+static void test_buffer(LPDIRECTSOUND dso, LPDIRECTSOUNDBUFFER dsbo,
+ int primary, int play)
+{
+ HRESULT rc;
+ DSBCAPS dsbcaps;
+ WAVEFORMATEX wfx,wfx2;
+ DWORD size,status,freq;
dsbcaps.dwSize=0;
rc=IDirectSoundBuffer_GetCaps(dsbo,&dsbcaps);
@@ -78,7 +242,8 @@
rc=IDirectSoundBuffer_GetCaps(dsbo,&dsbcaps);
ok(rc==DS_OK,"GetCaps failed: 0x%lx\n",rc);
if (rc==DS_OK) {
- trace(" PrimaryBuffer Caps: flags=0x%08lx size=%ld\n",dsbcaps.dwFlags,dsbcaps.dwBufferBytes);
+ trace(" Caps: flags=0x%08lx size=%ld\n",dsbcaps.dwFlags,
+ dsbcaps.dwBufferBytes);
}
/* Query the format size. Note that it may not match sizeof(wfx) */
@@ -91,7 +256,7 @@
rc=IDirectSoundBuffer_GetFormat(dsbo,&wfx,sizeof(wfx),NULL);
ok(rc==DS_OK,"GetFormat failed: 0x%lx\n",rc);
if (rc==DS_OK) {
- trace(" tag=0x%04x %ldx%dx%d avg.B/s=%ld align=%d\n",
+ trace(" tag=0x%04x %ldx%dx%d avg.B/s=%ld align=%d\n",
wfx.wFormatTag,wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels,
wfx.nAvgBytesPerSec,wfx.nBlockAlign);
}
@@ -107,39 +272,180 @@
rc=IDirectSoundBuffer_GetStatus(dsbo,&status);
ok(rc==DS_OK,"GetStatus failed: 0x%lx\n",rc);
if (rc==DS_OK) {
- trace(" status=0x%04lx\n",status);
+ trace(" status=0x%04lx\n",status);
}
- wfx2.wFormatTag=WAVE_FORMAT_PCM;
- wfx2.nChannels=2;
- wfx2.wBitsPerSample=16;
- wfx2.nSamplesPerSec=11025;
- wfx2.nBlockAlign=wfx2.nChannels*wfx2.wBitsPerSample/8;
- wfx2.nAvgBytesPerSec=wfx2.nSamplesPerSec*wfx2.nBlockAlign;
- wfx2.cbSize=0;
- rc=IDirectSoundBuffer_SetFormat(dsbo,&wfx2);
- ok(rc==DS_OK,"SetFormat failed: 0x%lx\n",rc);
+ if (primary) {
+ /* We must call SetCooperativeLevel to be allowed to call SetFormat */
+ rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_PRIORITY);
+ ok(rc==DS_OK,"SetCooperativeLevel failed: 0x%lx\n",rc);
+ if (rc!=DS_OK)
+ return;
+
+ init_format(&wfx2,11025,16,2);
+ rc=IDirectSoundBuffer_SetFormat(dsbo,&wfx2);
+ ok(rc==DS_OK,"SetFormat failed: 0x%lx\n",rc);
+
+ rc=IDirectSoundBuffer_GetFormat(dsbo,&wfx,sizeof(wfx),NULL);
+ ok(rc==DS_OK,"GetFormat failed: 0x%lx\n",rc);
+ if (rc==DS_OK) {
+ ok(wfx.wFormatTag==wfx2.wFormatTag &&
+ wfx.nChannels==wfx2.nChannels &&
+ wfx.wBitsPerSample==wfx2.wBitsPerSample &&
+ wfx.nSamplesPerSec==wfx2.nSamplesPerSec &&
+ wfx.nBlockAlign==wfx2.nBlockAlign &&
+ wfx.nAvgBytesPerSec==wfx2.nAvgBytesPerSec,
+ "SetFormat did not work right: tag=0x%04x %ldx%dx%d avg.B/s=%ld align=%d\n",
+ wfx.wFormatTag,wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels,
+ wfx.nAvgBytesPerSec,wfx.nBlockAlign);
+ }
+
+ /* Set the CooperativeLevel back to normal */
+ rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
+ ok(rc==DS_OK,"SetCooperativeLevel failed: 0x%lx\n",rc);
+ }
- rc=IDirectSoundBuffer_GetFormat(dsbo,&wfx,sizeof(wfx),NULL);
- ok(rc==DS_OK,"GetFormat failed: 0x%lx\n",rc);
+ if (play) {
+ play_state_t state;
+ LONG volume;
+
+ trace(" Playing 440Hz LA at %ldx%2dx%d\n",
+ wfx.nSamplesPerSec, wfx.wBitsPerSample,wfx.nChannels);
+
+ if (primary) {
+ /* We must call SetCooperativeLevel to be allowed to call Lock */
+ rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_WRITEPRIMARY);
+ ok(rc==DS_OK,"SetCooperativeLevel failed: 0x%lx\n",rc);
+ if (rc!=DS_OK)
+ return;
+ }
+
+ if (dsbcaps.dwFlags & DSBCAPS_CTRLVOLUME) {
+ rc=IDirectSoundBuffer_GetVolume(dsbo,&volume);
+ ok(rc==DS_OK,"GetVolume failed: 0x%lx\n",rc);
+ if (rc==DS_OK) {
+ trace(" volume=%ld\n",volume);
+ }
+
+ rc=IDirectSoundBuffer_SetVolume(dsbo,-1200);
+ ok(rc==DS_OK,"SetVolume failed: 0x%lx\n",rc);
+ }
+
+ state.wave=wave_generate_la(&wfx,((double)TONE_DURATION)/1000,&state.wave_len);
+
+ state.dsbo=dsbo;
+ state.wfx=&wfx;
+ state.buffer_size=dsbcaps.dwBufferBytes;
+ state.written=state.offset=0;
+ buffer_refill(&state,state.buffer_size);
+
+ rc=IDirectSoundBuffer_Play(dsbo,0,0,DSBPLAY_LOOPING);
+ ok(rc==DS_OK,"Play: 0x%lx\n",rc);
+
+ rc=IDirectSoundBuffer_GetStatus(dsbo,&status);
+ ok(rc==DS_OK,"GetStatus failed: 0x%lx\n",rc);
+ ok(status==(DSBSTATUS_PLAYING|DSBSTATUS_LOOPING),
+ "GetStatus: bad status: %lx",status);
+
+ while (buffer_service(&state)) {
+ WaitForSingleObject(GetCurrentProcess(),TIME_SLICE/2);
+ }
+
+ if (dsbcaps.dwFlags & DSBCAPS_CTRLVOLUME) {
+ rc=IDirectSoundBuffer_SetVolume(dsbo,volume);
+ ok(rc==DS_OK,"SetVolume failed: 0x%lx\n",rc);
+ }
+
+ free(state.wave);
+ if (primary) {
+ /* Set the CooperativeLevel back to normal */
+ rc=IDirectSound_SetCooperativeLevel(dso,get_hwnd(),DSSCL_NORMAL);
+ ok(rc==DS_OK,"SetCooperativeLevel failed: 0x%lx\n",rc);
+ }
+ }
+}
+
+static BOOL WINAPI dsenum_callback(LPGUID lpGuid, LPCSTR lpcstrDescription,
+ LPCSTR lpcstrModule, LPVOID lpContext)
+{
+ HRESULT rc;
+ LPDIRECTSOUND dso=NULL;
+ LPDIRECTSOUNDBUFFER dsbo=NULL;
+ DSBUFFERDESC bufdesc;
+ WAVEFORMATEX wfx;
+ DSCAPS dscaps;
+
+ trace("Testing %s - %s\n",lpcstrDescription,lpcstrModule);
+ rc=DirectSoundCreate(lpGuid,&dso,NULL);
+ ok(rc==DS_OK,"DirectSoundCreate failed: 0x%lx\n",rc);
+ if (rc!=DS_OK)
+ goto EXIT;
+
+ dscaps.dwSize=0;
+ rc=IDirectSound_GetCaps(dso,&dscaps);
+ ok(rc==DSERR_INVALIDPARAM,"GetCaps should have failed: 0x%lx\n",rc);
+
+ dscaps.dwSize=sizeof(dscaps);
+ rc=IDirectSound_GetCaps(dso,&dscaps);
+ ok(rc==DS_OK,"GetCaps failed: 0x%lx\n",rc);
if (rc==DS_OK) {
- ok(wfx.wFormatTag==wfx2.wFormatTag && wfx.nChannels==wfx2.nChannels &&
- wfx.wBitsPerSample==wfx2.wBitsPerSample && wfx.nSamplesPerSec==wfx2.nSamplesPerSec &&
- wfx.nBlockAlign==wfx2.nBlockAlign && wfx.nAvgBytesPerSec==wfx2.nAvgBytesPerSec,
- "SetFormat did not work right: tag=0x%04x %ldx%dx%d avg.B/s=%ld align=%d\n",
- wfx.wFormatTag,wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels,
- wfx.nAvgBytesPerSec,wfx.nBlockAlign);
+ trace(" DirectSound Caps: flags=0x%08lx secondary min=%ld max=%ld\n",
+ dscaps.dwFlags,dscaps.dwMinSecondarySampleRate,
+ dscaps.dwMaxSecondarySampleRate);
}
-EXIT:
- if (dsbo!=NULL)
+ /* Testing the primary buffer */
+ bufdesc.dwSize=sizeof(bufdesc);
+ bufdesc.dwFlags=DSBCAPS_PRIMARYBUFFER;
+ bufdesc.dwBufferBytes=0;
+ bufdesc.dwReserved=0;
+ bufdesc.lpwfxFormat=NULL;
+ trace(" Testing the primary buffer\n");
+ rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&dsbo,NULL);
+ ok(rc==DS_OK,"CreateSoundBuffer failed to create a primary buffer 0x%lx\n",rc);
+ if (rc==DS_OK) {
+ test_buffer(dso,dsbo,1,winetest_interactive && !(dscaps.dwFlags & DSCAPS_EMULDRIVER));
IDirectSoundBuffer_Release(dsbo);
+ }
+
+ /* Testing secondary buffers */
+ init_format(&wfx,11025,8,1);
+ bufdesc.dwSize=sizeof(bufdesc);
+ bufdesc.dwFlags=DSBCAPS_CTRLDEFAULT|DSBCAPS_GETCURRENTPOSITION2;
+ bufdesc.dwBufferBytes=wfx.nAvgBytesPerSec*BUFFER_LEN/1000;
+ bufdesc.dwReserved=0;
+ bufdesc.lpwfxFormat=&wfx;
+ trace(" Testing a secondary buffer at %ldx%dx%d\n",
+ wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels);
+ rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&dsbo,NULL);
+ ok(rc==DS_OK,"CreateSoundBuffer failed to create a secondary buffer 0x%lx\n",rc);
+ if (rc==DS_OK) {
+ test_buffer(dso,dsbo,0,winetest_interactive);
+ IDirectSoundBuffer_Release(dsbo);
+ }
+
+ init_format(&wfx,48000,16,2);
+ bufdesc.dwSize=sizeof(bufdesc);
+ bufdesc.dwFlags=DSBCAPS_CTRLDEFAULT|DSBCAPS_GETCURRENTPOSITION2;
+ bufdesc.dwBufferBytes=wfx.nAvgBytesPerSec*BUFFER_LEN/1000;
+ bufdesc.dwReserved=0;
+ bufdesc.lpwfxFormat=&wfx;
+ trace(" Testing a secondary buffer at %ldx%dx%d\n",
+ wfx.nSamplesPerSec,wfx.wBitsPerSample,wfx.nChannels);
+ rc=IDirectSound_CreateSoundBuffer(dso,&bufdesc,&dsbo,NULL);
+ ok(rc==DS_OK,"CreateSoundBuffer failed to create a secondary buffer 0x%lx\n",rc);
+ if (rc==DS_OK) {
+ test_buffer(dso,dsbo,0,winetest_interactive);
+ IDirectSoundBuffer_Release(dsbo);
+ }
+
+EXIT:
if (dso!=NULL)
IDirectSound_Release(dso);
return 1;
}
-void dsound_out_tests()
+static void dsound_out_tests()
{
HRESULT rc;
rc=DirectSoundEnumerateA(&dsenum_callback,NULL);
More information about the wine-patches
mailing list