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@codeweavers.com
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);