Robert Reif wrote:
dlls/winmm/wavemap/wavemap.c Correctly handle where waveOutGetPosition changes timepos.wType because the requested type is not supported.
dlls/winmm/tests/wave.c Changed regression test to make sure position is correctly checked when a different type is returned.
This revised patch adds the following:
dlls/winmm/wavemap/wavemap.c Adds Jeremy White's waveOutGetPosition fix to waveInGetPosition.
dlls/winmm/tests/capture.c Adds Jeremy White's WAVE_FORMAT_DIRECT/WAVE_MAPPER fix. Adds waveInGetPosition checks.
Index: dlls/winmm/tests/capture.c =================================================================== RCS file: /home/wine/wine/dlls/winmm/tests/capture.c,v retrieving revision 1.15 diff -u -p -r1.15 capture.c --- dlls/winmm/tests/capture.c 3 Nov 2004 22:13:44 -0000 1.15 +++ dlls/winmm/tests/capture.c 3 Apr 2005 17:30:59 -0000 @@ -50,6 +50,87 @@ static const char * wave_in_error(MMRESU return long_msg; }
+static void check_position(int device, HWAVEIN win, DWORD bytes, + LPWAVEFORMATEX pwfx ) +{ + MMTIME mmtime; + DWORD samples; + double duration; + MMRESULT rc; + DWORD returned; + + samples=bytes/(pwfx->wBitsPerSample/8*pwfx->nChannels); + duration=((double)samples)/pwfx->nSamplesPerSec; + + mmtime.wType = TIME_BYTES; + rc=waveInGetPosition(win, &mmtime, sizeof(mmtime)); + ok(rc==MMSYSERR_NOERROR, + "waveInGetPosition(%s): rc=%s\n",dev_name(device),wave_in_error(rc)); + if (mmtime.wType != TIME_BYTES && winetest_debug > 1) + trace("waveInGetPosition(%s): TIME_BYTES not supported, returned %s\n", + dev_name(device),wave_time_format(mmtime.wType)); + returned = time_to_bytes(&mmtime, pwfx); + ok(returned == bytes, "waveInGetPosition(%s): returned %ld bytes, " + "should be %ld\n", dev_name(device), returned, bytes); + + mmtime.wType = TIME_SAMPLES; + rc=waveInGetPosition(win, &mmtime, sizeof(mmtime)); + ok(rc==MMSYSERR_NOERROR, + "waveInGetPosition(%s): rc=%s\n",dev_name(device),wave_in_error(rc)); + if (mmtime.wType != TIME_SAMPLES && winetest_debug > 1) + trace("waveInGetPosition(%s): TIME_SAMPLES not supported, " + "returned %s\n",dev_name(device),wave_time_format(mmtime.wType)); + returned = time_to_bytes(&mmtime, pwfx); + ok(returned == bytes, "waveInGetPosition(%s): returned %ld samples, " + "should be %ld\n", dev_name(device), bytes_to_samples(returned, pwfx), + bytes_to_samples(bytes, pwfx)); + + mmtime.wType = TIME_MS; + rc=waveInGetPosition(win, &mmtime, sizeof(mmtime)); + ok(rc==MMSYSERR_NOERROR, + "waveInGetPosition(%s): rc=%s\n",dev_name(device),wave_in_error(rc)); + if (mmtime.wType != TIME_MS && winetest_debug > 1) + trace("waveInGetPosition(%s): TIME_MS not supported, returned %s\n", + dev_name(device), wave_time_format(mmtime.wType)); + returned = time_to_bytes(&mmtime, pwfx); + ok(returned == bytes, "waveInGetPosition(%s): returned %ld ms, " + "should be %ld\n", dev_name(device), bytes_to_ms(returned, pwfx), + bytes_to_ms(bytes, pwfx)); + + mmtime.wType = TIME_SMPTE; + rc=waveInGetPosition(win, &mmtime, sizeof(mmtime)); + ok(rc==MMSYSERR_NOERROR, + "waveInGetPosition(%s): rc=%s\n",dev_name(device),wave_in_error(rc)); + if (mmtime.wType != TIME_SMPTE && winetest_debug > 1) + trace("waveInGetPosition(%s): TIME_SMPTE not supported, returned %s\n", + dev_name(device),wave_time_format(mmtime.wType)); + returned = time_to_bytes(&mmtime, pwfx); + ok(returned == bytes, "waveInGetPosition(%s): SMPTE test failed\n", + dev_name(device)); + + mmtime.wType = TIME_MIDI; + rc=waveInGetPosition(win, &mmtime, sizeof(mmtime)); + ok(rc==MMSYSERR_NOERROR, + "waveInGetPosition(%s): rc=%s\n",dev_name(device),wave_in_error(rc)); + if (mmtime.wType != TIME_MIDI && winetest_debug > 1) + trace("waveInGetPosition(%s): TIME_MIDI not supported, returned %s\n", + dev_name(device),wave_time_format(mmtime.wType)); + returned = time_to_bytes(&mmtime, pwfx); + ok(returned == bytes, "waveInGetPosition(%s): MIDI test failed\n", + dev_name(device)); + + mmtime.wType = TIME_TICKS; + rc=waveInGetPosition(win, &mmtime, sizeof(mmtime)); + ok(rc==MMSYSERR_NOERROR, + "waveInGetPosition(%s): rc=%s\n",dev_name(device),wave_in_error(rc)); + if (mmtime.wType != TIME_TICKS && winetest_debug > 1) + trace("waveInGetPosition(%s): TIME_TICKS not supported, returned %s\n", + dev_name(device),wave_time_format(mmtime.wType)); + returned = time_to_bytes(&mmtime, pwfx); + ok(returned == bytes, "waveInGetPosition(%s): TICKS test failed\n", + dev_name(device)); +} + static void wave_in_test_deviceIn(int device, LPWAVEFORMATEX pwfx, DWORD format, DWORD flags, LPWAVEINCAPS pcaps) { HWAVEIN win; @@ -107,6 +188,9 @@ static void wave_in_test_deviceIn(int de pwfx->nSamplesPerSec, pwfx->wBitsPerSample, pwfx->nChannels, nSamplesPerSec, wBitsPerSample, nChannels);
+ /* Check that the position is 0 at start */ + check_position(device, win, 0, pwfx); + frag.lpData=malloc(pwfx->nAvgBytesPerSec); frag.dwBufferLength=pwfx->nAvgBytesPerSec; frag.dwBytesRecorded=0; @@ -131,6 +215,9 @@ static void wave_in_test_deviceIn(int de ok(rc==MMSYSERR_NOERROR,"waveInAddBuffer(%s): rc=%s\n", dev_name(device),wave_in_error(rc));
+ /* Check that the position is 0 at start */ + check_position(device, win, 0, pwfx); + rc=waveInStart(win); ok(rc==MMSYSERR_NOERROR,"waveInStart(%s): rc=%s\n", dev_name(device),wave_in_error(rc)); @@ -141,6 +228,7 @@ static void wave_in_test_deviceIn(int de ok(frag.dwBytesRecorded==pwfx->nAvgBytesPerSec, "frag.dwBytesRecorded=%ld, should=%ld\n", frag.dwBytesRecorded,pwfx->nAvgBytesPerSec); + /* stop playing on error */ if (res!=WAIT_OBJECT_0) { rc=waveInStop(win); @@ -316,11 +404,12 @@ static void wave_in_test_device(int devi format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign; format.cbSize=0; wave_in_test_deviceIn(device,&format,win_formats[f][0],0, &capsA); - wave_in_test_deviceIn(device,&format,win_formats[f][0], - WAVE_FORMAT_DIRECT, &capsA); - if (device != WAVE_MAPPER) + if (device != WAVE_MAPPER) { + wave_in_test_deviceIn(device,&format,win_formats[f][0], + WAVE_FORMAT_DIRECT, &capsA); wave_in_test_deviceIn(device,&format,win_formats[f][0], WAVE_MAPPED, &capsA); + } }
/* Try a PCMWAVEFORMAT aligned next to an unaccessible page for bounds @@ -342,11 +431,12 @@ static void wave_in_test_device(int devi pwfx->nBlockAlign=pwfx->nChannels*pwfx->wBitsPerSample/8; pwfx->nAvgBytesPerSec=pwfx->nSamplesPerSec*pwfx->nBlockAlign; wave_in_test_deviceIn(device,pwfx,WAVE_FORMAT_2M08,0, &capsA); - wave_in_test_deviceIn(device,pwfx,WAVE_FORMAT_2M08, - WAVE_FORMAT_DIRECT, &capsA); - if (device != WAVE_MAPPER) + if (device != WAVE_MAPPER) { + wave_in_test_deviceIn(device,pwfx,WAVE_FORMAT_2M08, + WAVE_FORMAT_DIRECT, &capsA); wave_in_test_deviceIn(device,pwfx,WAVE_FORMAT_2M08, WAVE_MAPPED, &capsA); + } } VirtualFree(twoPages, 2 * dwPageSize, MEM_RELEASE); } Index: dlls/winmm/tests/wave.c =================================================================== RCS file: /home/wine/wine/dlls/winmm/tests/wave.c,v retrieving revision 1.50 diff -u -p -r1.50 wave.c --- dlls/winmm/tests/wave.c 18 Mar 2005 10:25:48 -0000 1.50 +++ dlls/winmm/tests/wave.c 3 Apr 2005 17:31:00 -0000 @@ -303,7 +303,7 @@ static const char * wave_out_caps(DWORD #undef ADD_FLAG }
-static const char * wave_time_format(UINT type) +const char * wave_time_format(UINT type) { static char msg[32]; #define TIME_FORMAT(f) case f: return #f @@ -373,6 +373,34 @@ const char * get_format_str(WORD format) return msg; }
+DWORD bytes_to_samples(DWORD bytes, LPWAVEFORMATEX pwfx) +{ + return bytes / pwfx->nBlockAlign; +} + +DWORD bytes_to_ms(DWORD bytes, LPWAVEFORMATEX pwfx) +{ + return bytes_to_samples(bytes, pwfx) * 1000 / pwfx->nSamplesPerSec; +} + +DWORD time_to_bytes(LPMMTIME mmtime, LPWAVEFORMATEX pwfx) +{ + if (mmtime->wType == TIME_BYTES) + return mmtime->u.cb; + else if (mmtime->wType == TIME_SAMPLES) + return mmtime->u.sample * pwfx->nBlockAlign; + else if (mmtime->wType == TIME_MS) + return mmtime->u.ms * pwfx->nAvgBytesPerSec / 1000; + else if (mmtime->wType == TIME_SMPTE) + return ((mmtime->u.smpte.hour * 60.0 * 60.0) + + (mmtime->u.smpte.min * 60.0) + + (mmtime->u.smpte.sec) + + (mmtime->u.smpte.frame / 30.0)) * pwfx->nAvgBytesPerSec; + + trace("FIXME: time_to_bytes() type not supported\n"); + return -1; +} + static void check_position(int device, HWAVEOUT wout, DWORD bytes, LPWAVEFORMATEX pwfx ) { @@ -380,6 +408,7 @@ static void check_position(int device, H DWORD samples; double duration; MMRESULT rc; + DWORD returned;
samples=bytes/(pwfx->wBitsPerSample/8*pwfx->nChannels); duration=((double)samples)/pwfx->nSamplesPerSec; @@ -388,60 +417,69 @@ static void check_position(int device, H rc=waveOutGetPosition(wout, &mmtime, sizeof(mmtime)); ok(rc==MMSYSERR_NOERROR, "waveOutGetPosition(%s): rc=%s\n",dev_name(device),wave_out_error(rc)); - if (mmtime.wType == TIME_BYTES) - ok(mmtime.u.cb==bytes, - "waveOutGetPosition(%s): returned %ld bytes, should be %ld\n", - dev_name(device),mmtime.u.cb, bytes); - else + if (mmtime.wType != TIME_BYTES && winetest_debug > 1) trace("waveOutGetPosition(%s): TIME_BYTES not supported, returned %s\n", dev_name(device),wave_time_format(mmtime.wType)); + returned = time_to_bytes(&mmtime, pwfx); + ok(returned == bytes, "waveOutGetPosition(%s): returned %ld bytes, " + "should be %ld\n", dev_name(device), returned, bytes);
mmtime.wType = TIME_SAMPLES; rc=waveOutGetPosition(wout, &mmtime, sizeof(mmtime)); ok(rc==MMSYSERR_NOERROR, "waveOutGetPosition(%s): rc=%s\n",dev_name(device),wave_out_error(rc)); - if (mmtime.wType == TIME_SAMPLES) - ok(mmtime.u.sample==samples, - "waveOutGetPosition(%s): returned %ld samples, should be %ld\n", - dev_name(device), mmtime.u.sample, samples); - else + if (mmtime.wType != TIME_SAMPLES && winetest_debug > 1) trace("waveOutGetPosition(%s): TIME_SAMPLES not supported, " "returned %s\n",dev_name(device),wave_time_format(mmtime.wType)); + returned = time_to_bytes(&mmtime, pwfx); + ok(returned == bytes, "waveOutGetPosition(%s): returned %ld samples, " + "should be %ld\n", dev_name(device), bytes_to_samples(returned, pwfx), + bytes_to_samples(bytes, pwfx));
mmtime.wType = TIME_MS; rc=waveOutGetPosition(wout, &mmtime, sizeof(mmtime)); ok(rc==MMSYSERR_NOERROR, "waveOutGetPosition(%s): rc=%s\n",dev_name(device),wave_out_error(rc)); - if (mmtime.wType == TIME_MS) - ok(mmtime.u.ms==floor(duration*1000.0), - "waveOutGetPosition(%s): returned %ld ms, should be %ld\n", - dev_name(device), mmtime.u.ms, (long)floor(duration*1000.0)); - else + if (mmtime.wType != TIME_MS && winetest_debug > 1) trace("waveOutGetPosition(%s): TIME_MS not supported, returned %s\n", - dev_name(device),wave_time_format(mmtime.wType)); + dev_name(device), wave_time_format(mmtime.wType)); + returned = time_to_bytes(&mmtime, pwfx); + ok(returned == bytes, "waveOutGetPosition(%s): returned %ld ms, " + "should be %ld\n", dev_name(device), bytes_to_ms(returned, pwfx), + bytes_to_ms(bytes, pwfx));
mmtime.wType = TIME_SMPTE; rc=waveOutGetPosition(wout, &mmtime, sizeof(mmtime)); ok(rc==MMSYSERR_NOERROR, "waveOutGetPosition(%s): rc=%s\n",dev_name(device),wave_out_error(rc)); - if (mmtime.wType == TIME_SMPTE) - { - BYTE frames=(BYTE)ceil(fmod(duration*mmtime.u.smpte.fps, mmtime.u.smpte.fps)); - ok(mmtime.u.smpte.hour==(BYTE)(floor(duration/(60*60))) && - mmtime.u.smpte.min==(BYTE)(fmod(floor(duration/60), 60)) && - mmtime.u.smpte.sec==(BYTE)(fmod(duration,60)) && - mmtime.u.smpte.frame==frames, - "waveOutGetPosition(%s): returned %d:%d:%d %d, " - "should be %d:%d:%d %d\n", dev_name(device), mmtime.u.smpte.hour, - mmtime.u.smpte.min, mmtime.u.smpte.sec, mmtime.u.smpte.frame, - (BYTE)(floor(duration/(60*60))), - (BYTE)(fmod(floor(duration/60), 60)), - (BYTE)(fmod(duration,60)), - frames); - } - else + if (mmtime.wType != TIME_SMPTE && winetest_debug > 1) trace("waveOutGetPosition(%s): TIME_SMPTE not supported, returned %s\n", dev_name(device),wave_time_format(mmtime.wType)); + returned = time_to_bytes(&mmtime, pwfx); + ok(returned == bytes, "waveOutGetPosition(%s): SMPTE test failed\n", + dev_name(device)); + + mmtime.wType = TIME_MIDI; + rc=waveOutGetPosition(wout, &mmtime, sizeof(mmtime)); + ok(rc==MMSYSERR_NOERROR, + "waveOutGetPosition(%s): rc=%s\n",dev_name(device),wave_out_error(rc)); + if (mmtime.wType != TIME_MIDI && winetest_debug > 1) + trace("waveOutGetPosition(%s): TIME_MIDI not supported, returned %s\n", + dev_name(device),wave_time_format(mmtime.wType)); + returned = time_to_bytes(&mmtime, pwfx); + ok(returned == bytes, "waveOutGetPosition(%s): MIDI test failed\n", + dev_name(device)); + + mmtime.wType = TIME_TICKS; + rc=waveOutGetPosition(wout, &mmtime, sizeof(mmtime)); + ok(rc==MMSYSERR_NOERROR, + "waveOutGetPosition(%s): rc=%s\n",dev_name(device),wave_out_error(rc)); + if (mmtime.wType != TIME_TICKS && winetest_debug > 1) + trace("waveOutGetPosition(%s): TIME_TICKS not supported, returned %s\n", + dev_name(device),wave_time_format(mmtime.wType)); + returned = time_to_bytes(&mmtime, pwfx); + ok(returned == bytes, "waveOutGetPosition(%s): TICKS test failed\n", + dev_name(device)); }
static void wave_out_test_deviceOut(int device, double duration, @@ -1030,7 +1068,7 @@ static void wave_out_tests() "waveOutGetDevCapsW(%s): MMSYSERR_NOERROR or MMSYSERR_NOTSUPPORTED " "expected, got %s\n",dev_name(WAVE_MAPPER),mmsys_error(rc)); else - ok(rc==MMSYSERR_BADDEVICEID || rc==MMSYSERR_NODRIVER || + ok(rc==MMSYSERR_BADDEVICEID || rc==MMSYSERR_NODRIVER || rc==MMSYSERR_NOTSUPPORTED, "waveOutGetDevCapsW(%s): MMSYSERR_BADDEVICEID or MMSYSERR_NODRIVER " " or MMSYSERR_NOTSUPPORTED expected, got %s\n", Index: dlls/winmm/tests/winmm_test.h =================================================================== RCS file: /home/wine/wine/dlls/winmm/tests/winmm_test.h,v retrieving revision 1.8 diff -u -p -r1.8 winmm_test.h --- dlls/winmm/tests/winmm_test.h 19 Aug 2004 01:13:58 -0000 1.8 +++ dlls/winmm/tests/winmm_test.h 3 Apr 2005 17:31:00 -0000 @@ -72,3 +72,7 @@ extern const char* wave_open_flags(DWORD extern const char* mmsys_error(MMRESULT); extern const char* wave_out_error(MMRESULT); extern const char* get_format_str(WORD format); +extern const char* wave_time_format(UINT type); +extern DWORD bytes_to_samples(DWORD bytes, LPWAVEFORMATEX pwfx); +extern DWORD bytes_to_ms(DWORD bytes, LPWAVEFORMATEX pwfx); +extern DWORD time_to_bytes(LPMMTIME mmtime, LPWAVEFORMATEX pwfx); Index: dlls/winmm/wavemap/wavemap.c =================================================================== RCS file: /home/wine/wine/dlls/winmm/wavemap/wavemap.c,v retrieving revision 1.42 diff -u -p -r1.42 wavemap.c --- dlls/winmm/wavemap/wavemap.c 15 Mar 2005 15:39:18 -0000 1.42 +++ dlls/winmm/wavemap/wavemap.c 3 Apr 2005 17:31:01 -0000 @@ -428,9 +428,10 @@ static DWORD wodGetPosition(WAVEMAPDATA* if (lpTime->wType == TIME_MS) timepos.wType = TIME_BYTES;
+ /* This can change timepos.wType if the requested type is not supported */ val = waveOutGetPosition(wom->u.out.hInnerWave, &timepos, dwParam2);
- if (lpTime->wType == TIME_BYTES || lpTime->wType == TIME_MS) + if (timepos.wType == TIME_BYTES) { DWORD dwInnerSamplesPerOuter = wom->nSamplesPerSecInner / wom->nSamplesPerSecOuter; if (dwInnerSamplesPerOuter > 0) @@ -464,10 +465,12 @@ static DWORD wodGetPosition(WAVEMAPDATA*
/* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */ if (lpTime->wType == TIME_MS) - lpTime->u.cb = MulDiv(lpTime->u.cb, 1000, wom->avgSpeedOuter); + lpTime->u.ms = MulDiv(lpTime->u.cb, 1000, wom->avgSpeedOuter); + else + lpTime->wType = TIME_BYTES; } - else if (lpTime->wType == TIME_SAMPLES) - lpTime->u.cb = MulDiv(timepos.u.cb, wom->nSamplesPerSecOuter, wom->nSamplesPerSecInner); + else if (lpTime->wType == TIME_SAMPLES && timepos.wType == TIME_SAMPLES) + lpTime->u.sample = MulDiv(timepos.u.sample, wom->nSamplesPerSecOuter, wom->nSamplesPerSecInner); else /* other time types don't require conversion */ lpTime->u = timepos.u; @@ -962,15 +965,62 @@ static DWORD widUnprepare(WAVEMAPDATA* w static DWORD widGetPosition(WAVEMAPDATA* wim, LPMMTIME lpTime, DWORD dwParam2) { DWORD val; - + MMTIME timepos; TRACE("(%p %p %08lx)\n", wim, lpTime, dwParam2);
- val = waveInGetPosition(wim->u.in.hInnerWave, lpTime, dwParam2); - if (lpTime->wType == TIME_BYTES) - lpTime->u.cb = MulDiv(lpTime->u.cb, wim->avgSpeedOuter, wim->avgSpeedInner); - if (lpTime->wType == TIME_SAMPLES) - lpTime->u.cb = MulDiv(lpTime->u.cb, wim->nSamplesPerSecOuter, wim->nSamplesPerSecInner); - /* other time types don't require conversion */ + memcpy(&timepos, lpTime, sizeof(timepos)); + + /* For TIME_MS, we're going to recalculate using TIME_BYTES */ + if (lpTime->wType == TIME_MS) + timepos.wType = TIME_BYTES; + + /* This can change timepos.wType if the requested type is not supported */ + val = waveInGetPosition(wim->u.in.hInnerWave, &timepos, dwParam2); + + if (timepos.wType == TIME_BYTES) + { + DWORD dwInnerSamplesPerOuter = wim->nSamplesPerSecInner / wim->nSamplesPerSecOuter; + if (dwInnerSamplesPerOuter > 0) + { + DWORD dwInnerBytesPerSample = wim->avgSpeedInner / wim->nSamplesPerSecInner; + DWORD dwInnerBytesPerOuterSample = dwInnerBytesPerSample * dwInnerSamplesPerOuter; + DWORD remainder = 0; + + /* If we are up sampling (going from lower sample rate to higher), + ** we need to make a special accomodation for times when we've + ** written a partial output sample. This happens frequently + ** to us because we use msacm to do our up sampling, and it + ** will up sample on an unaligned basis. + ** For example, if you convert a 2 byte wide 8,000 'outer' + ** buffer to a 2 byte wide 48,000 inner device, you would + ** expect 2 bytes of input to produce 12 bytes of output. + ** Instead, msacm will produce 8 bytes of output. + ** But reporting our position as 1 byte of output is + ** nonsensical; the output buffer position needs to be + ** aligned on outer sample size, and aggressively rounded up. + */ + remainder = timepos.u.cb % dwInnerBytesPerOuterSample; + if (remainder > 0) + { + timepos.u.cb -= remainder; + timepos.u.cb += dwInnerBytesPerOuterSample; + } + } + + lpTime->u.cb = MulDiv(timepos.u.cb, wim->avgSpeedOuter, wim->avgSpeedInner); + + /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */ + if (lpTime->wType == TIME_MS) + lpTime->u.ms = MulDiv(lpTime->u.cb, 1000, wim->avgSpeedOuter); + else + lpTime->wType = TIME_BYTES; + } + else if (lpTime->wType == TIME_SAMPLES && timepos.wType == TIME_SAMPLES) + lpTime->u.sample = MulDiv(timepos.u.sample, wim->nSamplesPerSecOuter, wim->nSamplesPerSecInner); + else + /* other time types don't require conversion */ + lpTime->u = timepos.u; + return val; }