[PATCH 0/3] MR10175: winmm/tests: Test a curated list of wave formats.
This has of course a lot of code in common with the mmdevapi tests, but I don't know what would be a good place to share it. Happy to take suggestions. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10175
From: Giovanni Mascellani <gmascellani@codeweavers.com> PCM 24-bit samples can either be stored packed or aligned to 32 bits. It's useful to test both variants, since some audio backend don't handle them properly. --- dlls/winmm/tests/rsrc.rc | 10 ++++++++++ dlls/winmm/tests/test_s24_32le.wav | Bin 0 -> 17742 bytes dlls/winmm/tests/test_s24le.wav | Bin 0 -> 13332 bytes dlls/winmm/tests/wave.c | 6 ++++++ 4 files changed, 16 insertions(+) create mode 100644 dlls/winmm/tests/test_s24_32le.wav create mode 100644 dlls/winmm/tests/test_s24le.wav diff --git a/dlls/winmm/tests/rsrc.rc b/dlls/winmm/tests/rsrc.rc index b36fa3c407d..b77f501344e 100644 --- a/dlls/winmm/tests/rsrc.rc +++ b/dlls/winmm/tests/rsrc.rc @@ -24,6 +24,16 @@ /* @makedep: test.mpg */ test.mpg RCDATA "test.mpg" +/* ffmpeg -f lavfi -i "sine=frequency=1000" -t 0.1 -c:a pcm_s24le test_s24le.wav */ +/* @makedep: test_s24le.wav */ +test_s24le.wav WAVE "test_s24le.wav" + +/* ffmpeg does't directly support 24 useful bits in 32 bit words, so let's generate 32 bit and than patch the header. */ +/* ffmpeg -f lavfi -i "sine=frequency=1000" -t 0.1 -c:a pcm_s32le test_s24_32le.wav */ +/* xxd -r <(echo 00000026: 1800) test_s24_32le.wav */ +/* @makedep: test_s24_32le.wav */ +test_s24_32le.wav WAVE "test_s24_32le.wav" + /* ffmpeg -f lavfi -i "sine=frequency=1000" -t 0.1 -c:a pcm_alaw test_alaw.wav */ /* @makedep: test_alaw.wav */ test_alaw.wav WAVE "test_alaw.wav" diff --git a/dlls/winmm/tests/test_s24_32le.wav b/dlls/winmm/tests/test_s24_32le.wav new file mode 100644 index 0000000000000000000000000000000000000000..31f397b6aa6901b8830ed8a870acf6532d29c694 GIT binary patch literal 17742 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 literal 0 HcmV?d00001 diff --git a/dlls/winmm/tests/test_s24le.wav b/dlls/winmm/tests/test_s24le.wav new file mode 100644 index 0000000000000000000000000000000000000000..1de39ff2965a60087cde4e3af48be12e830dbc8a GIT binary patch literal 13332 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 CDm<zH literal 0 HcmV?d00001 diff --git a/dlls/winmm/tests/wave.c b/dlls/winmm/tests/wave.c index 3846fad0908..dd349d0d9da 100644 --- a/dlls/winmm/tests/wave.c +++ b/dlls/winmm/tests/wave.c @@ -1910,6 +1910,12 @@ static void test_PlaySound(void) } /* Test a few more exotic formats. */ + br = PlaySoundA("test_s24le.wav", GetModuleHandleA(NULL), SND_RESOURCE | SND_NODEFAULT); + ok(br, "PlaySound failed, got %d\n", br); + + br = PlaySoundA("test_s24_32le.wav", GetModuleHandleA(NULL), SND_RESOURCE | SND_NODEFAULT); + ok(br, "PlaySound failed, got %d\n", br); + br = PlaySoundA("test_alaw.wav", GetModuleHandleA(NULL), SND_RESOURCE | SND_NODEFAULT); ok(br, "PlaySound failed, got %d\n", br); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10175
From: Giovanni Mascellani <gmascellani@codeweavers.com> This reverts commit c18070ca049d2b598550fce5cc5fb46a61dc2d54, in preparation to a more systematic way to test format compatibility in winmm. --- dlls/winmm/tests/wave.c | 62 ++++++----------------------------------- 1 file changed, 9 insertions(+), 53 deletions(-) diff --git a/dlls/winmm/tests/wave.c b/dlls/winmm/tests/wave.c index dd349d0d9da..43779ed8519 100644 --- a/dlls/winmm/tests/wave.c +++ b/dlls/winmm/tests/wave.c @@ -1771,8 +1771,7 @@ static void test_reentrant_callback(void) CloseHandle(hevent); } -static void create_wav_file(char *temp_file, WORD format_tag, unsigned int channels, - unsigned int bits_per_sample, unsigned int samples_per_sec) +static void create_wav_file(char *temp_file) { WAVEFORMATEX format; HMMIO h; @@ -1782,13 +1781,13 @@ static void create_wav_file(char *temp_file, WORD format_tag, unsigned int chann DWORD length; char *buffer; - format.wFormatTag = format_tag; + format.wFormatTag=WAVE_FORMAT_PCM; format.cbSize = 0; - format.nChannels = channels; - format.wBitsPerSample = bits_per_sample; - format.nSamplesPerSec = samples_per_sec; - format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; - format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + format.nChannels=1; + format.wBitsPerSample=8; + format.nSamplesPerSec=8000; + format.nBlockAlign=format.nChannels*format.wBitsPerSample/8; + format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign; h = mmioOpenA(temp_file, NULL, MMIO_ALLOCBUF | MMIO_WRITE | MMIO_CREATE); ok(h != NULL, "Can't open temp_file\n"); @@ -1824,11 +1823,6 @@ static void create_wav_file(char *temp_file, WORD format_tag, unsigned int chann ok(rc == MMSYSERR_NOERROR, "mmioClose failed, got %u\n", rc); } -static const unsigned int sampling_rates[] = { 8000, 44100, 96000 }; -static const unsigned int channel_counts[] = { 1, 2 }; -static const unsigned int sample_formats[][2] = { {WAVE_FORMAT_PCM, 8}, {WAVE_FORMAT_PCM, 16}, - {WAVE_FORMAT_PCM, 32}, {WAVE_FORMAT_IEEE_FLOAT, 32} }; - static void test_PlaySound(void) { BOOL br; @@ -1847,10 +1841,7 @@ static void test_PlaySound(void) GetTempPathA(sizeof(test_file), test_file); strcat(test_file, "mysound.wav"); - - /* Test some filename quirks. */ - - create_wav_file(test_file, WAVE_FORMAT_PCM, 1, 8, 8000); + create_wav_file(test_file); br = PlaySoundA(test_file, NULL, SND_FILENAME | SND_NODEFAULT); ok(br, "PlaySound failed, got %d\n", br); @@ -1874,42 +1865,7 @@ static void test_PlaySound(void) DeleteFileA(test_file); - /* Test many different formats. */ - for (unsigned int i = 0; i < ARRAY_SIZE(sampling_rates); ++i) - { - winetest_push_context("rate %u", sampling_rates[i]); - - for (unsigned int j = 0; j < ARRAY_SIZE(channel_counts); ++j) - { - winetest_push_context("channel count %u", channel_counts[j]); - - for (unsigned int k = 0; k < ARRAY_SIZE(sample_formats); ++k) - { - winetest_push_context("type %s, depth %u", sample_formats[k][0] == WAVE_FORMAT_PCM ? "PCM" : "float", - sample_formats[k][1]); - - create_wav_file(test_file, sample_formats[k][0], channel_counts[j], - sample_formats[k][1], sampling_rates[i]); - - br = PlaySoundA(test_file, NULL, SND_FILENAME | SND_NODEFAULT); - ok(br, "PlaySound failed, got %d\n", br); - - /* SND_ALIAS fallbacks to SND_FILENAME */ - br = PlaySoundA(test_file, NULL, SND_ALIAS | SND_NODEFAULT); - ok(br, "PlaySound failed, got %d\n", br); - - DeleteFileA(test_file); - - winetest_pop_context(); - } - - winetest_pop_context(); - } - - winetest_pop_context(); - } - - /* Test a few more exotic formats. */ + /* Test a few exotic formats. */ br = PlaySoundA("test_s24le.wav", GetModuleHandleA(NULL), SND_RESOURCE | SND_NODEFAULT); ok(br, "PlaySound failed, got %d\n", br); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10175
From: Giovanni Mascellani <gmascellani@codeweavers.com> --- dlls/winmm/tests/wave.c | 542 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 542 insertions(+) diff --git a/dlls/winmm/tests/wave.c b/dlls/winmm/tests/wave.c index 43779ed8519..9a457ef72d4 100644 --- a/dlls/winmm/tests/wave.c +++ b/dlls/winmm/tests/wave.c @@ -22,6 +22,7 @@ #include <stdio.h> #include <stdlib.h> #include <math.h> +#include <assert.h> #include "wine/test.h" #include "windef.h" @@ -1895,6 +1896,546 @@ static void test_PlaySound(void) ok(br, "PlaySound failed, got %d\n", br); } +static MMRESULT validate_fmt(const WAVEFORMATEXTENSIBLE *fmt, BOOL direct) +{ + WAVEFORMATEXTENSIBLE fmt2 = *fmt; + BOOL extensible = TRUE; + MMRESULT ret; + + /* Reduce non-extensible formats to extensible ones. */ + if (fmt2.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE) + { + extensible = FALSE; + + switch (fmt2.Format.wFormatTag) + { + case WAVE_FORMAT_PCM: fmt2.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; break; + case WAVE_FORMAT_IEEE_FLOAT: fmt2.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; break; + case WAVE_FORMAT_ALAW: fmt2.SubFormat = KSDATAFORMAT_SUBTYPE_ALAW; break; + case WAVE_FORMAT_MULAW: fmt2.SubFormat = KSDATAFORMAT_SUBTYPE_MULAW; break; + default: return WAVERR_BADFORMAT; + } + + if (fmt2.Format.nChannels > 2) + return WAVERR_BADFORMAT; + + fmt2.dwChannelMask = (1u << fmt2.Format.nChannels) - 1; + fmt2.Samples.wValidBitsPerSample = fmt2.Format.wBitsPerSample; + fmt2.Format.cbSize = sizeof(fmt2) - sizeof(fmt2.Format); + } + + if (fmt2.Format.cbSize < sizeof(fmt2) - sizeof(fmt2.Format)) + ret = direct ? MMSYSERR_INVALPARAM : WAVERR_BADFORMAT; + else if ((fmt2.Format.nChannels == 0) || fmt2.Format.nSamplesPerSec == 0) + ret = WAVERR_BADFORMAT; + else if (fmt2.Format.nBlockAlign != fmt2.Format.nChannels * fmt2.Format.wBitsPerSample / 8) + ret = WAVERR_BADFORMAT; + else if (extensible && fmt2.Format.nAvgBytesPerSec != fmt2.Format.nBlockAlign * fmt2.Format.nSamplesPerSec) + ret = direct ? MMSYSERR_INVALPARAM : WAVERR_BADFORMAT; + else if (IsEqualGUID(&fmt2.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) + { + if (fmt2.Format.wBitsPerSample % 8 != 0) + ret = WAVERR_BADFORMAT; + else if (fmt2.Format.wBitsPerSample == 0) + ret = WAVERR_BADFORMAT; + else if (fmt2.Format.wBitsPerSample == 32 && fmt2.Samples.wValidBitsPerSample == 0) + ret = WAVERR_BADFORMAT; + else if (fmt2.Format.wBitsPerSample == 32 && fmt2.Samples.wValidBitsPerSample > fmt2.Format.wBitsPerSample) + ret = WAVERR_BADFORMAT; + else if (fmt2.Format.wBitsPerSample != 8 && fmt2.Format.wBitsPerSample != 16 + && fmt2.Format.wBitsPerSample != 24 && fmt2.Format.wBitsPerSample != 32) + ret = direct ? MMSYSERR_INVALPARAM : WAVERR_BADFORMAT; + else if (fmt2.Format.wBitsPerSample == 32 && fmt2.Samples.wValidBitsPerSample == 24) + ret = MMSYSERR_NOERROR; + else if (fmt2.Samples.wValidBitsPerSample != fmt2.Format.wBitsPerSample) + ret = WAVERR_BADFORMAT; + else + ret = MMSYSERR_NOERROR; + } + else if (IsEqualGUID(&fmt2.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) + { + if (fmt2.Format.wBitsPerSample % 8 != 0) + ret = WAVERR_BADFORMAT; + else if (fmt2.Format.wBitsPerSample == 0) + ret = WAVERR_BADFORMAT; + else if (fmt2.Samples.wValidBitsPerSample == 0) + ret = WAVERR_BADFORMAT; + else if (fmt2.Samples.wValidBitsPerSample > fmt2.Format.wBitsPerSample) + ret = WAVERR_BADFORMAT; + else if (fmt2.Format.wBitsPerSample != 32 && fmt2.Format.wBitsPerSample != 64) + ret = direct ? MMSYSERR_INVALPARAM : WAVERR_BADFORMAT; + else if (fmt2.Format.wBitsPerSample != 32) + ret = WAVERR_BADFORMAT; + else if (fmt2.Samples.wValidBitsPerSample != fmt2.Format.wBitsPerSample) + ret = WAVERR_BADFORMAT; + else + ret = MMSYSERR_NOERROR; + } + else if (IsEqualGUID(&fmt2.SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW) || IsEqualGUID(&fmt2.SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW)) + { + if (direct || extensible) + ret = WAVERR_BADFORMAT; + else if (fmt2.Format.wBitsPerSample != 8) + ret = WAVERR_BADFORMAT; + else if (fmt2.Samples.wValidBitsPerSample != fmt2.Format.wBitsPerSample) + ret = WAVERR_BADFORMAT; + else + ret = MMSYSERR_NOERROR; + } + else + ret = WAVERR_BADFORMAT; + + return ret; +} + +static ULONG popcount(ULONG val) +{ +#if HAVE___BUILTIN_POPCOUNT + return __builtin_popcount(val); +#else + val -= val >> 1 & 0x55555555; + val = (val & 0x33333333) + (val >> 2 & 0x33333333); + return ((val + (val >> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24; +#endif +} + +static void test_format(WAVEFORMATEXTENSIBLE *fmt) +{ + BOOL bit_mismatch = FALSE; + MMRESULT mmr, expected; + HWAVEOUT hwo; + + bit_mismatch |= fmt->Format.wFormatTag == WAVE_FORMAT_PCM && fmt->Format.wBitsPerSample == 64; + bit_mismatch |= fmt->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->Format.wBitsPerSample != 32; + bit_mismatch |= fmt->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE + && fmt->Samples.wValidBitsPerSample != fmt->Format.wBitsPerSample; + + hwo = (void *)0xdeadf00d; + expected = validate_fmt(fmt, FALSE); + mmr = waveOutOpen(&hwo, WAVE_MAPPER, (const WAVEFORMATEX *)fmt, 0, 0, CALLBACK_NULL | WAVE_FORMAT_QUERY); + ok(hwo == (void *)0xdeadf00d, "Unexpected waveout %p\n", hwo); + if (expected == MMSYSERR_NOERROR) + { + /* Correct formats should be accepted. */ + todo_wine_if(mmr != expected && + /* Wine currently rejects more aggressively formats with broken bitness. */ + (bit_mismatch + /* winecoreaudio.drv specifically doesn't like having zero channels. */ + || fmt->Format.nChannels == 0 + /* Wine's G.711 decoder only accept some standard sampling rates. */ + || fmt->Format.wFormatTag == WAVE_FORMAT_ALAW || fmt->Format.wFormatTag == WAVE_FORMAT_MULAW + /* Wine needs cbSize to be precise, native accepts it when it's too large. */ + || fmt->Format.cbSize > sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))) + ok(mmr == expected, "waveOutOpen(QUERY) got result %#08x, expected %#08x\n", mmr, expected); + } + else + { + /* With incorrect formats it's a mess. Native emits all sorts of possible + * error codes, including MMSYSERR_NOERROR, without any apparent logic. + * I tried to find some regularity, but it seems hopeless. */ + ok(mmr == MMSYSERR_NOERROR || mmr == WAVERR_BADFORMAT || mmr == MMSYSERR_INVALPARAM, + "waveOutOpen(QUERY) got result %#08x\n", mmr); + } + + hwo = (void *)0xdeadf00d; + expected = validate_fmt(fmt, TRUE); + mmr = waveOutOpen(&hwo, WAVE_MAPPER, (const WAVEFORMATEX *)fmt, 0, 0, CALLBACK_NULL | WAVE_FORMAT_QUERY | WAVE_FORMAT_DIRECT); + ok(hwo == (void *)0xdeadf00d, "Unexpected waveout %p\n", hwo); + if (expected == MMSYSERR_NOERROR) + { + todo_wine_if(mmr != expected && + /* Wine currently rejects more aggressively formats with broken bitness. */ + (bit_mismatch + /* winecoreaudio.drv specifically doesn't like having zero channels. */ + || fmt->Format.nChannels == 0 + /* Wine's G.711 decoder only accept some standard sampling rates. */ + || fmt->Format.wFormatTag == WAVE_FORMAT_ALAW || fmt->Format.wFormatTag == WAVE_FORMAT_MULAW + /* Wine needs cbSize to be precise, native accepts it when it's too large. */ + || fmt->Format.cbSize > sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))) + ok(mmr == expected, "waveOutOpen(QUERY | DIRECT) got result %#08x, expected %#08x\n", mmr, expected); + } + else + { + ok(mmr == MMSYSERR_NOERROR || mmr == WAVERR_BADFORMAT || mmr == MMSYSERR_INVALPARAM, + "waveOutOpen(QUERY | DIRECT) got result %#08x\n", mmr); + } + + hwo = (void *)0xdeadf00d; + expected = validate_fmt(fmt, FALSE); + mmr = waveOutOpen(&hwo, WAVE_MAPPER, (const WAVEFORMATEX *)fmt, 0, 0, CALLBACK_NULL); + todo_wine_if(mmr != expected && + (expected != MMSYSERR_NOERROR + || fmt->Format.wFormatTag == WAVE_FORMAT_ALAW || fmt->Format.wFormatTag == WAVE_FORMAT_MULAW + || fmt->Format.cbSize > sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + /* Native seems to largely ignore when the channel mask has nonsensical values, while Wine is more picky. */ + || (fmt->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && popcount(fmt->dwChannelMask) != fmt->Format.nChannels))) + ok(mmr == expected, "waveOutOpen(0) got result %#08x, expected %#08x\n", mmr, expected); + todo_wine_if((mmr == MMSYSERR_NOERROR) != !!hwo) + ok((mmr == MMSYSERR_NOERROR) == !!hwo, "Unexpected waveout %p\n", hwo); + + if (hwo && hwo != (void *)0xdeadf00d) + { + mmr = waveOutClose(hwo); + ok(mmr == MMSYSERR_NOERROR, "Unexpected result %#08x\n", mmr); + } + + hwo = (void *)0xdeadf00d; + expected = validate_fmt(fmt, TRUE); + mmr = waveOutOpen(&hwo, WAVE_MAPPER, (const WAVEFORMATEX *)fmt, 0, 0, CALLBACK_NULL | WAVE_FORMAT_DIRECT); + todo_wine_if(mmr != expected && !(mmr == MMSYSERR_INVALPARAM && expected == WAVERR_BADFORMAT) && + (expected != MMSYSERR_NOERROR + || fmt->Format.cbSize > sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + /* Wine currently doesn't accept 24-bit PCM in direct mode. */ + || fmt->Format.wBitsPerSample == 24 + || (fmt->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && popcount(fmt->dwChannelMask) != fmt->Format.nChannels))) + ok(mmr == expected || (mmr == MMSYSERR_INVALPARAM && expected == WAVERR_BADFORMAT), + "waveOutOpen(DIRECT) got result %#08x, expected %#08x\n", mmr, expected); + todo_wine_if((mmr == MMSYSERR_NOERROR) != !!hwo) + ok((mmr == MMSYSERR_NOERROR) == !!hwo, "Unexpected waveout %p\n", hwo); + + if (hwo && hwo != (void *)0xdeadf00d) + { + mmr = waveOutClose(hwo); + ok(mmr == MMSYSERR_NOERROR, "Unexpected result %#08x\n", mmr); + } +} + +static void push_format_context(const WAVEFORMATEXTENSIBLE *fmt) +{ + static const char *format_str[] = + { + [WAVE_FORMAT_PCM] = "P", + [WAVE_FORMAT_IEEE_FLOAT] = "F", + [WAVE_FORMAT_ALAW] = "A", + [WAVE_FORMAT_MULAW] = "MU", + }; + + if (fmt->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + winetest_push_context("%sX%u(%u)x%lux%u:%lx", format_str[fmt->SubFormat.Data1], + fmt->Format.wBitsPerSample, fmt->Samples.wValidBitsPerSample, + fmt->Format.nSamplesPerSec, fmt->Format.nChannels, fmt->dwChannelMask); + } + else + { + winetest_push_context("%s%ux%lux%u", format_str[fmt->Format.wFormatTag], + fmt->Format.wBitsPerSample, fmt->Format.nSamplesPerSec, fmt->Format.nChannels); + } +} + +struct wave_format +{ + WAVEFORMATEXTENSIBLE format; + const char *additional_context; +}; + +struct wave_format *wave_formats = NULL; +size_t wave_format_count = 0; +size_t wave_format_capacity = 0; + +static WAVEFORMATEXTENSIBLE *push_wave_format_with_context(const WAVEFORMATEXTENSIBLE *base_fmt, + const char *additional_context) +{ + if (wave_format_count == wave_format_capacity) + { + wave_format_capacity = max(1, 2 * wave_format_capacity); + + wave_formats = realloc(wave_formats, + sizeof(*wave_formats) * wave_format_capacity); + assert(wave_formats); + } + + wave_formats[wave_format_count].format = *base_fmt; + wave_formats[wave_format_count].additional_context = additional_context; + + return &wave_formats[wave_format_count++].format; +} + +static WAVEFORMATEXTENSIBLE *push_wave_format(const WAVEFORMATEXTENSIBLE *base_fmt) +{ + return push_wave_format_with_context(base_fmt, NULL); +} + +static void convert_to_unextensible(WAVEFORMATEXTENSIBLE *fmt) +{ + assert(fmt->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE); + + fmt->Format.wFormatTag = fmt->SubFormat.Data1; + fmt->Format.cbSize = 0; + memset((&fmt->Format) + 1, 0, sizeof(*fmt) - sizeof(fmt->Format)); +} + +static WAVEFORMATEX *repush_wave_format_as_unextensible(void) +{ + WAVEFORMATEXTENSIBLE *fmt; + + fmt = push_wave_format_with_context(&wave_formats[wave_format_count - 1].format, + wave_formats[wave_format_count - 1].additional_context); + + convert_to_unextensible(fmt); + + return &fmt->Format; +} + +void fill_wave_formats(const WAVEFORMATEXTENSIBLE *base_fmt) +{ + static const DWORD channel_count_mask[][2] = + { + {0, 0}, + + {1, KSAUDIO_SPEAKER_DIRECTOUT}, + {1, KSAUDIO_SPEAKER_MONO}, + {1, KSAUDIO_SPEAKER_STEREO}, + {1, SPEAKER_BACK_LEFT}, + {1, SPEAKER_BACK_LEFT | SPEAKER_TOP_BACK_CENTER}, + {1, KSAUDIO_SPEAKER_7POINT1_SURROUND}, + {1, KSAUDIO_SPEAKER_MONO | SPEAKER_ALL}, + {1, SPEAKER_ALL}, + {1, KSAUDIO_SPEAKER_MONO | SPEAKER_RESERVED}, + {1, SPEAKER_RESERVED}, + + {2, KSAUDIO_SPEAKER_DIRECTOUT}, + {2, KSAUDIO_SPEAKER_MONO}, + {2, KSAUDIO_SPEAKER_STEREO}, + {2, SPEAKER_BACK_LEFT}, + {2, SPEAKER_BACK_LEFT | SPEAKER_TOP_BACK_CENTER}, + {2, KSAUDIO_SPEAKER_7POINT1_SURROUND}, + {2, KSAUDIO_SPEAKER_MONO | SPEAKER_ALL}, + {2, KSAUDIO_SPEAKER_STEREO | SPEAKER_ALL}, + {2, SPEAKER_ALL}, + {2, KSAUDIO_SPEAKER_STEREO | SPEAKER_RESERVED}, + {2, SPEAKER_RESERVED}, + + {4, KSAUDIO_SPEAKER_DIRECTOUT}, + {4, KSAUDIO_SPEAKER_QUAD}, + {4, KSAUDIO_SPEAKER_QUAD | SPEAKER_ALL}, + {4, SPEAKER_ALL}, + {4, KSAUDIO_SPEAKER_QUAD | SPEAKER_RESERVED}, + {4, SPEAKER_RESERVED}, + + {4, KSAUDIO_SPEAKER_DIRECTOUT}, + {4, KSAUDIO_SPEAKER_5POINT1}, + {4, KSAUDIO_SPEAKER_5POINT1 | SPEAKER_ALL}, + {4, SPEAKER_ALL}, + {4, KSAUDIO_SPEAKER_5POINT1 | SPEAKER_RESERVED}, + {4, SPEAKER_RESERVED}, + + {8, KSAUDIO_SPEAKER_DIRECTOUT}, + {8, KSAUDIO_SPEAKER_MONO}, + {8, KSAUDIO_SPEAKER_STEREO}, + {8, KSAUDIO_SPEAKER_7POINT1_SURROUND}, + {8, KSAUDIO_SPEAKER_7POINT1_SURROUND & ~SPEAKER_SIDE_LEFT}, + {8, (KSAUDIO_SPEAKER_7POINT1_SURROUND & ~SPEAKER_SIDE_LEFT) | SPEAKER_FRONT_RIGHT_OF_CENTER}, + {8, KSAUDIO_SPEAKER_7POINT1_SURROUND | SPEAKER_ALL}, + {8, SPEAKER_ALL}, + {8, KSAUDIO_SPEAKER_7POINT1_SURROUND | SPEAKER_RESERVED}, + {8, SPEAKER_RESERVED}, + }; + + static const DWORD sample_formats[][3] = + { + {WAVE_FORMAT_PCM, 0, 0}, + {WAVE_FORMAT_PCM, 1, 1}, + {WAVE_FORMAT_PCM, 15, 15}, + {WAVE_FORMAT_PCM, 16, 0}, + {WAVE_FORMAT_PCM, 16, 1}, + {WAVE_FORMAT_PCM, 16, 8}, + {WAVE_FORMAT_PCM, 16, 15}, + {WAVE_FORMAT_PCM, 16, 16}, + {WAVE_FORMAT_PCM, 16, 17}, + {WAVE_FORMAT_PCM, 24, 16}, + {WAVE_FORMAT_PCM, 24, 23}, + {WAVE_FORMAT_PCM, 24, 24}, + {WAVE_FORMAT_PCM, 24, 25}, + {WAVE_FORMAT_PCM, 32, 0}, + {WAVE_FORMAT_PCM, 32, 1}, + {WAVE_FORMAT_PCM, 32, 8}, + {WAVE_FORMAT_PCM, 32, 16}, + {WAVE_FORMAT_PCM, 32, 17}, + {WAVE_FORMAT_PCM, 32, 24}, + {WAVE_FORMAT_PCM, 32, 31}, + {WAVE_FORMAT_PCM, 32, 33}, + {WAVE_FORMAT_PCM, 32, 32}, + {WAVE_FORMAT_PCM, 64, 64}, + {WAVE_FORMAT_PCM, 96, 96}, + {WAVE_FORMAT_PCM, 100, 100}, + + {WAVE_FORMAT_IEEE_FLOAT, 0, 0}, + {WAVE_FORMAT_IEEE_FLOAT, 1, 1}, + {WAVE_FORMAT_IEEE_FLOAT, 15, 15}, + {WAVE_FORMAT_IEEE_FLOAT, 16, 16}, + {WAVE_FORMAT_IEEE_FLOAT, 24, 24}, + {WAVE_FORMAT_IEEE_FLOAT, 32, 0}, + {WAVE_FORMAT_IEEE_FLOAT, 32, 1}, + {WAVE_FORMAT_IEEE_FLOAT, 32, 16}, + {WAVE_FORMAT_IEEE_FLOAT, 32, 31}, + {WAVE_FORMAT_IEEE_FLOAT, 32, 32}, + {WAVE_FORMAT_IEEE_FLOAT, 32, 33}, + {WAVE_FORMAT_IEEE_FLOAT, 64, 0}, + {WAVE_FORMAT_IEEE_FLOAT, 64, 32}, + {WAVE_FORMAT_IEEE_FLOAT, 64, 63}, + {WAVE_FORMAT_IEEE_FLOAT, 64, 64}, + {WAVE_FORMAT_IEEE_FLOAT, 64, 65}, + {WAVE_FORMAT_IEEE_FLOAT, 96, 96}, + {WAVE_FORMAT_IEEE_FLOAT, 100, 100}, + + {WAVE_FORMAT_ALAW, 8, 0}, + {WAVE_FORMAT_ALAW, 8, 1}, + {WAVE_FORMAT_ALAW, 8, 7}, + {WAVE_FORMAT_ALAW, 8, 8}, + {WAVE_FORMAT_ALAW, 8, 9}, + {WAVE_FORMAT_ALAW, 16, 0}, + {WAVE_FORMAT_ALAW, 16, 1}, + {WAVE_FORMAT_ALAW, 16, 8}, + {WAVE_FORMAT_ALAW, 16, 16}, + {WAVE_FORMAT_ALAW, 16, 17}, + + {WAVE_FORMAT_MULAW, 8, 0}, + {WAVE_FORMAT_MULAW, 8, 1}, + {WAVE_FORMAT_MULAW, 8, 7}, + {WAVE_FORMAT_MULAW, 8, 8}, + {WAVE_FORMAT_MULAW, 8, 9}, + {WAVE_FORMAT_MULAW, 16, 0}, + {WAVE_FORMAT_MULAW, 16, 1}, + {WAVE_FORMAT_MULAW, 16, 8}, + {WAVE_FORMAT_MULAW, 16, 16}, + {WAVE_FORMAT_MULAW, 16, 17}, + }; + + static const DWORD sample_rates[] = + { + 0, + 100, + 8000, + 11025, + 16000, + 22050, + 43123, + 44100, + 48000, + 96000, + 192000, + 384000, + }; + + WAVEFORMATEXTENSIBLE *fmt; + unsigned int i; + + wave_format_count = 0; + + push_wave_format(base_fmt); + repush_wave_format_as_unextensible(); + + /* Change channel count or mask. */ + for (i = 0; i < ARRAY_SIZE(channel_count_mask); ++i) + { + fmt = push_wave_format(base_fmt); + fmt->Format.nChannels = channel_count_mask[i][0]; + fmt->dwChannelMask = channel_count_mask[i][1]; + + if (i == 0 || channel_count_mask[i][0] != channel_count_mask[i - 1][0]) + repush_wave_format_as_unextensible(); + } + + /* Change sample format. */ + for (i = 0; i < ARRAY_SIZE(sample_formats); ++i) + { + fmt = push_wave_format(base_fmt); + fmt->SubFormat.Data1 = sample_formats[i][0]; + fmt->Format.wBitsPerSample = sample_formats[i][1]; + fmt->Samples.wValidBitsPerSample = sample_formats[i][2]; + + if (fmt->Format.wBitsPerSample == fmt->Samples.wValidBitsPerSample) + repush_wave_format_as_unextensible(); + } + + /* Change the sample rate. */ + for (i = 0; i < ARRAY_SIZE(sample_rates); ++i) + { + fmt = push_wave_format(base_fmt); + fmt->Format.nSamplesPerSec = sample_rates[i]; + repush_wave_format_as_unextensible(); + } + + /* Fix nBlockAlign and nAvgBytesPerSec up to here. */ + for (i = 0; i < wave_format_count; ++i) + { + fmt = &wave_formats[i].format; + + fmt->Format.nBlockAlign = fmt->Format.nChannels * fmt->Format.wBitsPerSample / CHAR_BIT; + fmt->Format.nAvgBytesPerSec = fmt->Format.nBlockAlign * fmt->Format.nSamplesPerSec; + } + + /* Break nAvgBytesPerSec. */ + fmt = push_wave_format_with_context(base_fmt, "nAvgBytesPerSec = 0"); + fmt->Format.nAvgBytesPerSec = 0; + repush_wave_format_as_unextensible(); + + fmt = push_wave_format_with_context(base_fmt, "nAvgBytesPerSec += 1"); + fmt->Format.nAvgBytesPerSec += 1; + repush_wave_format_as_unextensible(); + + fmt = push_wave_format_with_context(base_fmt, "nAvgBytesPerSec -= 1"); + fmt->Format.nAvgBytesPerSec -= 1; + repush_wave_format_as_unextensible(); + + /* Break nBlockAlign. */ + fmt = push_wave_format_with_context(base_fmt, "nBlockAlign = 0"); + fmt->Format.nBlockAlign = 0; + repush_wave_format_as_unextensible(); + + fmt = push_wave_format_with_context(base_fmt, "nBlockAlign += 1"); + fmt->Format.nBlockAlign += 1; + repush_wave_format_as_unextensible(); + + fmt = push_wave_format_with_context(base_fmt, "nBlockAlign -= 1"); + fmt->Format.nBlockAlign -= 1; + repush_wave_format_as_unextensible(); + + /* Break cbSize. */ + fmt = push_wave_format_with_context(base_fmt, "cbSize = 0"); + fmt->Format.cbSize = 0; + + fmt = push_wave_format_with_context(base_fmt, "cbSize += 1"); + fmt->Format.cbSize += 1; + + fmt = push_wave_format_with_context(base_fmt, "cbSize -= 1"); + fmt->Format.cbSize -= 1; +} + +static void test_formats(void) +{ + static const WAVEFORMATEXTENSIBLE base_fmt = + { + .Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE, + .Format.nChannels = 2, + .Format.nSamplesPerSec = 48000, + .Format.wBitsPerSample = 16, + .Format.nBlockAlign = (2 * 16) / 8, + .Format.nAvgBytesPerSec = (2 * 16) / 8 * 48000, + .Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX), + .Samples.wValidBitsPerSample = 16, + .dwChannelMask = KSAUDIO_SPEAKER_STEREO, + .SubFormat = KSDATAFORMAT_SUBTYPE_PCM, + }; + unsigned int i; + + fill_wave_formats(&base_fmt); + + for (i = 0; i < wave_format_count; ++i) + { + const char *additional_context = wave_formats[i].additional_context; + WAVEFORMATEXTENSIBLE fmt = wave_formats[i].format; + + winetest_push_context("test %u%s%s", i, additional_context ? ", " : "", + additional_context ? additional_context : ""); + push_format_context(&fmt); + test_format(&fmt); + winetest_pop_context(); + winetest_pop_context(); + } +} + START_TEST(wave) { test_multiple_waveopens(); @@ -1903,4 +2444,5 @@ START_TEST(wave) test_fragmentsize(); test_reentrant_callback(); test_PlaySound(); + test_formats(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10175
participants (2)
-
Giovanni Mascellani -
Giovanni Mascellani (@giomasce)