-- v2: windows.media.speech: Disable format check when initializing mmdevapi. winmm: Disable format check when initializing mmdevapi. libs/faudio: Disable format check when initializing mmdevapi. mmdevapi: Disable format check when initializing spatial audio. mmdevapi: Introduce a private interface to disable the format check. mmdevapi/tests: Test 32-bit PCM sample formats. winepulse.drv: Allow 32-bit PCM audio samples. mmdevapi: Allow a sampling rate mismatch when rate adjust is supported. mmdevapi/tests: Test audio client initialization with rate adjust flag. mmdevapi/tests: Add a couple of spatialaudio checks.
From: Giovanni Mascellani gmascellani@codeweavers.com
--- dlls/mmdevapi/tests/capture.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/mmdevapi/tests/capture.c b/dlls/mmdevapi/tests/capture.c index 678fda5baca..86187609e96 100644 --- a/dlls/mmdevapi/tests/capture.c +++ b/dlls/mmdevapi/tests/capture.c @@ -657,6 +657,9 @@ static void test_formats(AUDCLNT_SHAREMODE mode) "Initialize(noexcl., %c%lux%2ux%u) returns %08lx(%08lx)\n", format_chr, fmt.nSamplesPerSec, fmt.wBitsPerSample, fmt.nChannels, hr, hrs); else + /* When both AUDCLNT_E_UNSUPPORTED_FORMAT and AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED apply, + * IsFormatSupported() should return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, but currently doesn't. */ + todo_if(hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED) ok(hrs == S_OK ? hr == S_OK : hr == AUDCLNT_E_ENDPOINT_CREATE_FAILED || hr == AUDCLNT_E_UNSUPPORTED_FORMAT, "Initialize(exclus., %c%lux%2ux%u) returns %08lx\n",
From: Giovanni Mascellani gmascellani@codeweavers.com
This segfaults on my Windows 11 system. It's not a particularly useful test anyway. --- dlls/mmdevapi/tests/capture.c | 3 --- dlls/mmdevapi/tests/render.c | 3 --- 2 files changed, 6 deletions(-)
diff --git a/dlls/mmdevapi/tests/capture.c b/dlls/mmdevapi/tests/capture.c index 86187609e96..a912ef3f014 100644 --- a/dlls/mmdevapi/tests/capture.c +++ b/dlls/mmdevapi/tests/capture.c @@ -435,9 +435,6 @@ static void test_audioclient(void)
handle = CreateEventW(NULL, FALSE, FALSE, NULL);
- hr = IAudioClient_QueryInterface(ac, &IID_IUnknown, NULL); - ok(hr == E_POINTER, "QueryInterface(NULL) returned %08lx\n", hr); - unk = (void*)(LONG_PTR)0x12345678; hr = IAudioClient_QueryInterface(ac, &IID_NULL, (void**)&unk); ok(hr == E_NOINTERFACE, "QueryInterface(IID_NULL) returned %08lx\n", hr); diff --git a/dlls/mmdevapi/tests/render.c b/dlls/mmdevapi/tests/render.c index f674935de00..8a3d732a99b 100644 --- a/dlls/mmdevapi/tests/render.c +++ b/dlls/mmdevapi/tests/render.c @@ -196,9 +196,6 @@ static void test_audioclient(void)
handle = CreateEventW(NULL, FALSE, FALSE, NULL);
- hr = IAudioClient_QueryInterface(ac, &IID_IUnknown, NULL); - ok(hr == E_POINTER, "QueryInterface(NULL) returned %08lx\n", hr); - unk = (void*)(LONG_PTR)0x12345678; hr = IAudioClient_QueryInterface(ac, &IID_NULL, (void**)&unk); ok(hr == E_NOINTERFACE, "QueryInterface(IID_NULL) returned %08lx\n", hr);
From: Giovanni Mascellani gmascellani@codeweavers.com
The current method relies on IsFormatSupported() accepting formats which differs by channel count from the mix format. However that was changed in 00211db0d08d60ee9a0e40206bf7cf9b5b88987b, because it doesn't match native behavior.
The check in SpatialAudioClient_Create() doesn't look particularly useful anyway; the real format check is done in ActivateSpatialAudioStream(), where all the parameters (including channel count) are known. --- dlls/mmdevapi/spatialaudio.c | 42 ------------------------------------ 1 file changed, 42 deletions(-)
diff --git a/dlls/mmdevapi/spatialaudio.c b/dlls/mmdevapi/spatialaudio.c index b721af6229b..58d1005d741 100644 --- a/dlls/mmdevapi/spatialaudio.c +++ b/dlls/mmdevapi/spatialaudio.c @@ -931,9 +931,6 @@ static IAudioFormatEnumeratorVtbl IAudioFormatEnumerator_vtbl = { HRESULT SpatialAudioClient_Create(IMMDevice *mmdev, ISpatialAudioClient **out) { SpatialAudioImpl *obj; - IAudioClient *aclient; - WAVEFORMATEX *closest; - HRESULT hr;
obj = calloc(1, sizeof(*obj));
@@ -949,45 +946,6 @@ HRESULT SpatialAudioClient_Create(IMMDevice *mmdev, ISpatialAudioClient **out) obj->object_fmtex.Format.nAvgBytesPerSec = obj->object_fmtex.Format.nSamplesPerSec * obj->object_fmtex.Format.nBlockAlign; obj->object_fmtex.Format.cbSize = 0;
- hr = IMMDevice_Activate(mmdev, &IID_IAudioClient, - CLSCTX_INPROC_SERVER, NULL, (void**)&aclient); - if(FAILED(hr)){ - WARN("Activate failed: %08lx\n", hr); - free(obj); - return hr; - } - - hr = IAudioClient_IsFormatSupported(aclient, AUDCLNT_SHAREMODE_SHARED, &obj->object_fmtex.Format, &closest); - - IAudioClient_Release(aclient); - - if(hr == S_FALSE){ - if(sizeof(WAVEFORMATEX) + closest->cbSize > sizeof(obj->object_fmtex)){ - ERR("Returned format too large: %s\n", debugstr_fmtex(closest)); - CoTaskMemFree(closest); - free(obj); - return AUDCLNT_E_UNSUPPORTED_FORMAT; - }else if(!((closest->wFormatTag == WAVE_FORMAT_IEEE_FLOAT || - (closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE && - IsEqualGUID(&((WAVEFORMATEXTENSIBLE *)closest)->SubFormat, - &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) && - closest->wBitsPerSample == 32)){ - ERR("Returned format not 32-bit float: %s\n", debugstr_fmtex(closest)); - CoTaskMemFree(closest); - free(obj); - return AUDCLNT_E_UNSUPPORTED_FORMAT; - } - WARN("The audio stack doesn't support 48kHz 32bit float. Using the closest match. Audio may be glitchy. %s\n", debugstr_fmtex(closest)); - memcpy(&obj->object_fmtex, - closest, - sizeof(WAVEFORMATEX) + closest->cbSize); - CoTaskMemFree(closest); - } else if(hr != S_OK){ - WARN("Checking supported formats failed: %08lx\n", hr); - free(obj); - return hr; - } - obj->mmdev = mmdev; IMMDevice_AddRef(mmdev);
From: Giovanni Mascellani gmascellani@codeweavers.com
It was already confirmed in ActivateSpatialAudioStream() that the format is 32-bit floating point, which is hardcoded in SpatialAudioClient_Create(). --- dlls/mmdevapi/spatialaudio.c | 8 -------- 1 file changed, 8 deletions(-)
diff --git a/dlls/mmdevapi/spatialaudio.c b/dlls/mmdevapi/spatialaudio.c index 58d1005d741..eab3ceade6b 100644 --- a/dlls/mmdevapi/spatialaudio.c +++ b/dlls/mmdevapi/spatialaudio.c @@ -702,17 +702,9 @@ static void static_mask_to_channels(AudioObjectType static_mask, WORD *count, DW
static HRESULT activate_stream(SpatialAudioStreamImpl *stream) { - WAVEFORMATEXTENSIBLE *object_fmtex = (WAVEFORMATEXTENSIBLE *)stream->params.ObjectFormat; HRESULT hr; REFERENCE_TIME period;
- if(!(object_fmtex->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT || - (object_fmtex->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && - IsEqualGUID(&object_fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))){ - FIXME("Only float formats are supported for now\n"); - return E_INVALIDARG; - } - hr = IMMDevice_Activate(stream->sa_client->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void**)&stream->client); if(FAILED(hr)){
From: Giovanni Mascellani gmascellani@codeweavers.com
They're not particularly important, but since I happened to write them it shouldn't hurt to keep them. --- dlls/mmdevapi/tests/spatialaudio.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/dlls/mmdevapi/tests/spatialaudio.c b/dlls/mmdevapi/tests/spatialaudio.c index 2a7fd041060..2bc672da413 100644 --- a/dlls/mmdevapi/tests/spatialaudio.c +++ b/dlls/mmdevapi/tests/spatialaudio.c @@ -56,6 +56,8 @@ static void test_formats(void) ok(hr == S_OK, "Getting format failed: 0x%08lx\n", hr); ok(fmt != NULL, "Expected to get non-NULL format\n");
+ /* On native 48kHz is reported even when a different sampling rate is configured + * on the device (and returned as IAudioClient mix format). */ ok(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT, "Wrong format, expected WAVE_FORMAT_IEEE_FLOAT got %hx\n", fmt->wFormatTag); ok(fmt->nChannels == 1, "Wrong number of channels, expected 1 got %hu\n", fmt->nChannels); ok(fmt->nSamplesPerSec == 48000, "Wrong sample ret, expected 48000 got %lu\n", fmt->nSamplesPerSec); @@ -272,6 +274,15 @@ static void test_audio_object_activation(void) hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_Dynamic, &sao2); ok(hr == SPTLAUDCLNT_E_NO_MORE_OBJECTS, "Expected to not have no more dynamic objects: 0x%08lx\n", hr);
+ hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_SideLeft | AudioObjectType_SideRight | AudioObjectType_FrontLeft, &sao2); + todo_wine + ok(hr == SPTLAUDCLNT_E_OBJECT_ALREADY_ACTIVE, "Expected audio object to be already active: 0x%08lx\n", hr); + + hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_SideLeft | AudioObjectType_SideRight, &sao2); + todo_wine + ok(hr == S_OK, "Cannot create spatial audio object with two types: 0x%08lx\n", hr); + + ISpatialAudioObject_Release(sao2); ISpatialAudioObject_Release(sao1); ISpatialAudioObjectRenderStream_Release(sas); }
From: Giovanni Mascellani gmascellani@codeweavers.com
--- dlls/mmdevapi/tests/render.c | 42 +++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-)
diff --git a/dlls/mmdevapi/tests/render.c b/dlls/mmdevapi/tests/render.c index 8a3d732a99b..ea0caf4b6b0 100644 --- a/dlls/mmdevapi/tests/render.c +++ b/dlls/mmdevapi/tests/render.c @@ -532,6 +532,8 @@ static void test_formats(AUDCLNT_SHAREMODE mode) fmt.cbSize = 0;
for(i = 0; i < ARRAY_SIZE(win_formats); i++) { + HRESULT expected; + BOOL compatible; char format_chr;
hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, @@ -571,8 +573,8 @@ static void test_formats(AUDCLNT_SHAREMODE mode) /* In shared mode you can only change bit width, not sampling rate or channel count. */ if (mode == AUDCLNT_SHAREMODE_SHARED) { - BOOL compatible = fmt.nSamplesPerSec == pwfx->nSamplesPerSec && fmt.nChannels == pwfx->nChannels; - HRESULT expected = compatible ? S_OK : S_FALSE; + compatible = fmt.nSamplesPerSec == pwfx->nSamplesPerSec && fmt.nChannels == pwfx->nChannels; + expected = compatible ? S_OK : S_FALSE; ok(hr == expected, "Got %lx expected %lx\n", hr, expected); }
@@ -586,7 +588,6 @@ static void test_formats(AUDCLNT_SHAREMODE mode) format_chr, pwfx2->nSamplesPerSec, pwfx2->wBitsPerSample, pwfx2->nChannels); }
- /* Vista returns E_INVALIDARG upon AUDCLNT_STREAMFLAGS_RATEADJUST */ hr = IAudioClient_Initialize(ac, mode, 0, 5000000, 0, &fmt, NULL); if ((hrs == S_OK) ^ (hr == S_OK)) trace("Initialize (%s, %c%lux%2ux%u) returns %08lx unlike IsFormatSupported\n", @@ -613,6 +614,41 @@ static void test_formats(AUDCLNT_SHAREMODE mode) "Initialize(exclus., %c%lux%2ux%u) returns %08lx\n", format_chr, fmt.nSamplesPerSec, fmt.wBitsPerSample, fmt.nChannels, hr);
+ IAudioClient_Release(ac); + + hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, + NULL, (void**)&ac); + ok(hr == S_OK, "Activation failed with %08lx\n", hr); + if(hr != S_OK) + continue; + + /* With AUDCLNT_STREAMFLAGS_RATEADJUST compatibility is relaxed. */ + compatible = fmt.nChannels == pwfx->nChannels; + expected = compatible ? S_OK : AUDCLNT_E_UNSUPPORTED_FORMAT; + + hr = IAudioClient_Initialize(ac, mode, AUDCLNT_STREAMFLAGS_RATEADJUST, 5000000, 0, &fmt, NULL); + if (mode == AUDCLNT_SHAREMODE_SHARED) + todo_wine_if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT && compatible) + ok(hr == expected || broken(hr == E_INVALIDARG) /* Vista */, + "Initialize(shared, %c%lux%2ux%u) returns %08lx\n", + format_chr, fmt.nSamplesPerSec, fmt.wBitsPerSample, fmt.nChannels, hr); + else if (hrs == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED) + /* Unsupported format implies "create failed" and shadows "not allowed" */ + ok(hrs == hexcl && (hr == AUDCLNT_E_ENDPOINT_CREATE_FAILED || hr == hrs), + "Initialize(noexcl., %c%lux%2ux%u) returns %08lx(%08lx)\n", + format_chr, fmt.nSamplesPerSec, fmt.wBitsPerSample, fmt.nChannels, hr, hrs); + else + /* On testbot 48000x16x1 claims support, but does not Initialize. + * Some cards Initialize 44100|48000x16x1 yet claim no support; + * F. Gouget's w7 bots do that for 12000|96000x8|16x1|2 */ + ok(hrs == S_OK ? hr == S_OK || broken(hr == AUDCLNT_E_ENDPOINT_CREATE_FAILED) + : hr == AUDCLNT_E_ENDPOINT_CREATE_FAILED || hr == AUDCLNT_E_UNSUPPORTED_FORMAT || + broken(hr == S_OK && + ((fmt.nChannels == 1 && fmt.wBitsPerSample == 16) || + (fmt.nSamplesPerSec == 12000 || fmt.nSamplesPerSec == 96000))), + "Initialize(exclus., %c%lux%2ux%u) returns %08lx\n", + format_chr, fmt.nSamplesPerSec, fmt.wBitsPerSample, fmt.nChannels, hr); + /* Bug in native (Vista/w2k8/w7): after Initialize failed, better * Release this ac and Activate a new one. * A second call (with a known working format) would yield
From: Giovanni Mascellani gmascellani@codeweavers.com
--- dlls/mmdevapi/client.c | 7 ++++++- dlls/mmdevapi/tests/render.c | 1 - 2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/dlls/mmdevapi/client.c b/dlls/mmdevapi/client.c index 61b9167f3c6..6401f231e4f 100644 --- a/dlls/mmdevapi/client.c +++ b/dlls/mmdevapi/client.c @@ -400,12 +400,17 @@ static HRESULT stream_init(struct audio_client *client, const BOOLEAN force_def_
if (mode == AUDCLNT_SHAREMODE_SHARED) { WAVEFORMATEX *mix_fmt; + BOOL compatible; HRESULT hr;
if (FAILED(hr = IAudioClient3_GetMixFormat(&client->IAudioClient3_iface, &mix_fmt))) return hr;
- if (fmt->nChannels != mix_fmt->nChannels || fmt->nSamplesPerSec != mix_fmt->nSamplesPerSec) + compatible = fmt->nChannels == mix_fmt->nChannels; + if (!(flags & AUDCLNT_STREAMFLAGS_RATEADJUST)) + compatible &= fmt->nSamplesPerSec == mix_fmt->nSamplesPerSec; + + if (!compatible) { CoTaskMemFree(mix_fmt); return AUDCLNT_E_UNSUPPORTED_FORMAT; diff --git a/dlls/mmdevapi/tests/render.c b/dlls/mmdevapi/tests/render.c index ea0caf4b6b0..7ad890320e0 100644 --- a/dlls/mmdevapi/tests/render.c +++ b/dlls/mmdevapi/tests/render.c @@ -628,7 +628,6 @@ static void test_formats(AUDCLNT_SHAREMODE mode)
hr = IAudioClient_Initialize(ac, mode, AUDCLNT_STREAMFLAGS_RATEADJUST, 5000000, 0, &fmt, NULL); if (mode == AUDCLNT_SHAREMODE_SHARED) - todo_wine_if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT && compatible) ok(hr == expected || broken(hr == E_INVALIDARG) /* Vista */, "Initialize(shared, %c%lux%2ux%u) returns %08lx\n", format_chr, fmt.nSamplesPerSec, fmt.wBitsPerSample, fmt.nChannels, hr);
From: Giovanni Mascellani gmascellani@codeweavers.com
--- dlls/winepulse.drv/pulse.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 4b287578e8b..9a218e75c1b 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -962,6 +962,8 @@ static HRESULT pulse_spec_from_waveformat(struct pulse_stream *stream, const WAV stream->ss.format = PA_SAMPLE_U8; else if (fmt->wBitsPerSample == 16) stream->ss.format = PA_SAMPLE_S16LE; + else if (fmt->wBitsPerSample == 32) + stream->ss.format = PA_SAMPLE_S32LE; else return AUDCLNT_E_UNSUPPORTED_FORMAT; pa_channel_map_init_auto(&stream->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
From: Giovanni Mascellani gmascellani@codeweavers.com
--- dlls/mmdevapi/tests/capture.c | 16 ++++++++-------- dlls/mmdevapi/tests/render.c | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/dlls/mmdevapi/tests/capture.c b/dlls/mmdevapi/tests/capture.c index a912ef3f014..b9b14fcc25d 100644 --- a/dlls/mmdevapi/tests/capture.c +++ b/dlls/mmdevapi/tests/capture.c @@ -40,14 +40,14 @@ #define FLOAT WAVE_FORMAT_IEEE_FLOAT
static const unsigned int win_formats[][4] = { - {PCM, 8000, 8, 1}, {PCM, 8000, 8, 2}, {PCM, 8000, 16, 1}, {PCM, 8000, 16, 2}, - {PCM, 11025, 8, 1}, {PCM, 11025, 8, 2}, {PCM, 11025, 16, 1}, {PCM, 11025, 16, 2}, - {PCM, 12000, 8, 1}, {PCM, 12000, 8, 2}, {PCM, 12000, 16, 1}, {PCM, 12000, 16, 2}, - {PCM, 16000, 8, 1}, {PCM, 16000, 8, 2}, {PCM, 16000, 16, 1}, {PCM, 16000, 16, 2}, - {PCM, 22050, 8, 1}, {PCM, 22050, 8, 2}, {PCM, 22050, 16, 1}, {PCM, 22050, 16, 2}, - {PCM, 44100, 8, 1}, {PCM, 44100, 8, 2}, {PCM, 44100, 16, 1}, {PCM, 44100, 16, 2}, - {PCM, 48000, 8, 1}, {PCM, 48000, 8, 2}, {PCM, 48000, 16, 1}, {PCM, 48000, 16, 2}, - {PCM, 96000, 8, 1}, {PCM, 96000, 8, 2}, {PCM, 96000, 16, 1}, {PCM, 96000, 16, 2}, + {PCM, 8000, 8, 1}, {PCM, 8000, 8, 2}, {PCM, 8000, 16, 1}, {PCM, 8000, 16, 2}, {PCM, 8000, 32, 1}, {PCM, 8000, 32, 2}, + {PCM, 11025, 8, 1}, {PCM, 11025, 8, 2}, {PCM, 11025, 16, 1}, {PCM, 11025, 16, 2}, {PCM, 11025, 32, 1}, {PCM, 11025, 32, 2}, + {PCM, 12000, 8, 1}, {PCM, 12000, 8, 2}, {PCM, 12000, 16, 1}, {PCM, 12000, 16, 2}, {PCM, 12000, 32, 1}, {PCM, 12000, 32, 2}, + {PCM, 16000, 8, 1}, {PCM, 16000, 8, 2}, {PCM, 16000, 16, 1}, {PCM, 16000, 16, 2}, {PCM, 16000, 32, 1}, {PCM, 16000, 32, 2}, + {PCM, 22050, 8, 1}, {PCM, 22050, 8, 2}, {PCM, 22050, 16, 1}, {PCM, 22050, 16, 2}, {PCM, 22050, 32, 1}, {PCM, 22050, 32, 2}, + {PCM, 44100, 8, 1}, {PCM, 44100, 8, 2}, {PCM, 44100, 16, 1}, {PCM, 44100, 16, 2}, {PCM, 44100, 32, 1}, {PCM, 44100, 32, 2}, + {PCM, 48000, 8, 1}, {PCM, 48000, 8, 2}, {PCM, 48000, 16, 1}, {PCM, 48000, 16, 2}, {PCM, 48000, 32, 1}, {PCM, 48000, 32, 2}, + {PCM, 96000, 8, 1}, {PCM, 96000, 8, 2}, {PCM, 96000, 16, 1}, {PCM, 96000, 16, 2}, {PCM, 96000, 32, 1}, {PCM, 96000, 32, 2}, {FLOAT, 8000, 32, 1}, {FLOAT, 8000, 32, 2}, {FLOAT, 11025, 32, 1}, {FLOAT, 11025, 32, 2}, {FLOAT, 12000, 32, 1}, {FLOAT, 12000, 32, 2}, diff --git a/dlls/mmdevapi/tests/render.c b/dlls/mmdevapi/tests/render.c index 7ad890320e0..1c67f06a32e 100644 --- a/dlls/mmdevapi/tests/render.c +++ b/dlls/mmdevapi/tests/render.c @@ -45,14 +45,14 @@ #define FLOAT WAVE_FORMAT_IEEE_FLOAT
static const unsigned int win_formats[][4] = { - {PCM, 8000, 8, 1}, {PCM, 8000, 8, 2}, {PCM, 8000, 16, 1}, {PCM, 8000, 16, 2}, - {PCM, 11025, 8, 1}, {PCM, 11025, 8, 2}, {PCM, 11025, 16, 1}, {PCM, 11025, 16, 2}, - {PCM, 12000, 8, 1}, {PCM, 12000, 8, 2}, {PCM, 12000, 16, 1}, {PCM, 12000, 16, 2}, - {PCM, 16000, 8, 1}, {PCM, 16000, 8, 2}, {PCM, 16000, 16, 1}, {PCM, 16000, 16, 2}, - {PCM, 22050, 8, 1}, {PCM, 22050, 8, 2}, {PCM, 22050, 16, 1}, {PCM, 22050, 16, 2}, - {PCM, 44100, 8, 1}, {PCM, 44100, 8, 2}, {PCM, 44100, 16, 1}, {PCM, 44100, 16, 2}, - {PCM, 48000, 8, 1}, {PCM, 48000, 8, 2}, {PCM, 48000, 16, 1}, {PCM, 48000, 16, 2}, - {PCM, 96000, 8, 1}, {PCM, 96000, 8, 2}, {PCM, 96000, 16, 1}, {PCM, 96000, 16, 2}, + {PCM, 8000, 8, 1}, {PCM, 8000, 8, 2}, {PCM, 8000, 16, 1}, {PCM, 8000, 16, 2}, {PCM, 8000, 32, 1}, {PCM, 8000, 32, 2}, + {PCM, 11025, 8, 1}, {PCM, 11025, 8, 2}, {PCM, 11025, 16, 1}, {PCM, 11025, 16, 2}, {PCM, 11025, 32, 1}, {PCM, 11025, 32, 2}, + {PCM, 12000, 8, 1}, {PCM, 12000, 8, 2}, {PCM, 12000, 16, 1}, {PCM, 12000, 16, 2}, {PCM, 12000, 32, 1}, {PCM, 12000, 32, 2}, + {PCM, 16000, 8, 1}, {PCM, 16000, 8, 2}, {PCM, 16000, 16, 1}, {PCM, 16000, 16, 2}, {PCM, 16000, 32, 1}, {PCM, 16000, 32, 2}, + {PCM, 22050, 8, 1}, {PCM, 22050, 8, 2}, {PCM, 22050, 16, 1}, {PCM, 22050, 16, 2}, {PCM, 22050, 32, 1}, {PCM, 22050, 32, 2}, + {PCM, 44100, 8, 1}, {PCM, 44100, 8, 2}, {PCM, 44100, 16, 1}, {PCM, 44100, 16, 2}, {PCM, 44100, 32, 1}, {PCM, 44100, 32, 2}, + {PCM, 48000, 8, 1}, {PCM, 48000, 8, 2}, {PCM, 48000, 16, 1}, {PCM, 48000, 16, 2}, {PCM, 48000, 32, 1}, {PCM, 48000, 32, 2}, + {PCM, 96000, 8, 1}, {PCM, 96000, 8, 2}, {PCM, 96000, 16, 1}, {PCM, 96000, 16, 2}, {PCM, 96000, 32, 1}, {PCM, 96000, 32, 2}, {FLOAT, 8000, 32, 1}, {FLOAT, 8000, 32, 2}, {FLOAT, 11025, 32, 1}, {FLOAT, 11025, 32, 2}, {FLOAT, 12000, 32, 1}, {FLOAT, 12000, 32, 2},
From: Giovanni Mascellani gmascellani@codeweavers.com
Since 00211db0d08d60ee9a0e40206bf7cf9b5b88987b, and on native, IAudioClient::Initialize() rejects formats that differ by channel count or sample rate from the mix format. However, a number of other audio interfaces in Wine are wrappers on top of mmdevapi, and rely on the ability to do channel count and sample rate conversion. Therefore we introduce a private API to bypass the format check. --- dlls/mmdevapi/client.c | 209 ++++++++++++++++++++----------- dlls/mmdevapi/mmdevapi_private.h | 4 +- dlls/mmdevapi/session.c | 4 +- include/Makefile.in | 1 + include/wine/winemmdevapi.idl | 45 +++++++ 5 files changed, 189 insertions(+), 74 deletions(-) create mode 100644 include/wine/winemmdevapi.idl
diff --git a/dlls/mmdevapi/client.c b/dlls/mmdevapi/client.c index 6401f231e4f..11a345c6908 100644 --- a/dlls/mmdevapi/client.c +++ b/dlls/mmdevapi/client.c @@ -32,6 +32,7 @@
#include <wine/debug.h> #include <wine/unixlib.h> +#include <wine/winemmdevapi.h>
#include "mmdevapi_private.h"
@@ -77,9 +78,9 @@ static inline struct audio_client *impl_from_IAudioCaptureClient(IAudioCaptureCl return CONTAINING_RECORD(iface, struct audio_client, IAudioCaptureClient_iface); }
-static inline struct audio_client *impl_from_IAudioClient3(IAudioClient3 *iface) +static inline struct audio_client *impl_from_IWineAudioClient(IWineAudioClient *iface) { - return CONTAINING_RECORD(iface, struct audio_client, IAudioClient3_iface); + return CONTAINING_RECORD(iface, struct audio_client, IWineAudioClient_iface); }
static inline struct audio_client *impl_from_IAudioClock(IAudioClock *iface) @@ -367,6 +368,7 @@ skip: }
static HRESULT stream_init(struct audio_client *client, const BOOLEAN force_def_period, + const BOOL format_check, const AUDCLNT_SHAREMODE mode, const DWORD flags, REFERENCE_TIME duration, REFERENCE_TIME period, const WAVEFORMATEX *fmt, const GUID *sessionguid) @@ -398,12 +400,12 @@ static HRESULT stream_init(struct audio_client *client, const BOOLEAN force_def_ return E_INVALIDARG; }
- if (mode == AUDCLNT_SHAREMODE_SHARED) { + if (format_check && mode == AUDCLNT_SHAREMODE_SHARED) { WAVEFORMATEX *mix_fmt; BOOL compatible; HRESULT hr;
- if (FAILED(hr = IAudioClient3_GetMixFormat(&client->IAudioClient3_iface, &mix_fmt))) + if (FAILED(hr = IWineAudioClient_GetMixFormat(&client->IWineAudioClient_iface, &mix_fmt))) return hr;
compatible = fmt->nChannels == mix_fmt->nChannels; @@ -542,13 +544,13 @@ static HRESULT WINAPI capture_QueryInterface(IAudioCaptureClient *iface, REFIID static ULONG WINAPI capture_AddRef(IAudioCaptureClient *iface) { struct audio_client *This = impl_from_IAudioCaptureClient(iface); - return IAudioClient3_AddRef(&This->IAudioClient3_iface); + return IWineAudioClient_AddRef(&This->IWineAudioClient_iface); }
static ULONG WINAPI capture_Release(IAudioCaptureClient *iface) { struct audio_client *This = impl_from_IAudioCaptureClient(iface); - return IAudioClient3_Release(&This->IAudioClient3_iface); + return IWineAudioClient_Release(&This->IWineAudioClient_iface); }
static HRESULT WINAPI capture_GetBuffer(IAudioCaptureClient *iface, BYTE **data, UINT32 *frames, @@ -631,9 +633,9 @@ const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl = capture_GetNextPacketSize };
-static HRESULT WINAPI client_QueryInterface(IAudioClient3 *iface, REFIID riid, void **ppv) +static HRESULT WINAPI client_QueryInterface(IWineAudioClient *iface, REFIID riid, void **ppv) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface);
TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
@@ -643,12 +645,13 @@ static HRESULT WINAPI client_QueryInterface(IAudioClient3 *iface, REFIID riid, v if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient) || IsEqualIID(riid, &IID_IAudioClient2) || - IsEqualIID(riid, &IID_IAudioClient3)) + IsEqualIID(riid, &IID_IAudioClient3) || + IsEqualIID(riid, &IID_IWineAudioClient)) *ppv = iface; else if (IsEqualIID(riid, &IID_IAudioClockAdjustment)) *ppv = &This->IAudioClockAdjustment_iface; else if(IsEqualIID(riid, &IID_IMarshal)) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); return IUnknown_QueryInterface(This->marshal, riid, ppv); } else { *ppv = NULL; @@ -660,22 +663,22 @@ static HRESULT WINAPI client_QueryInterface(IAudioClient3 *iface, REFIID riid, v return S_OK; }
-static ULONG WINAPI client_AddRef(IAudioClient3 *iface) +static ULONG WINAPI client_AddRef(IWineAudioClient *iface) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) Refcount now %lu\n", This, ref); return ref; }
-static ULONG WINAPI client_Release(IAudioClient3 *iface) +static ULONG WINAPI client_Release(IWineAudioClient *iface) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) Refcount now %lu\n", This, ref);
if (!ref) { - IAudioClient3_Stop(iface); + IWineAudioClient_Stop(iface); IMMDevice_Release(This->parent); IUnknown_Release(This->marshal);
@@ -697,22 +700,22 @@ static ULONG WINAPI client_Release(IAudioClient3 *iface) return ref; }
-static HRESULT WINAPI client_Initialize(IAudioClient3 *iface, AUDCLNT_SHAREMODE mode, DWORD flags, +static HRESULT WINAPI client_Initialize(IWineAudioClient *iface, AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration, REFERENCE_TIME period, const WAVEFORMATEX *fmt, const GUID *sessionguid) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface);
TRACE("(%p)->(%x, %lx, %s, %s, %p, %s)\n", This, mode, flags, wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
- return stream_init(This, TRUE, mode, flags, duration, period, fmt, sessionguid); + return stream_init(This, TRUE, TRUE, mode, flags, duration, period, fmt, sessionguid); }
-static HRESULT WINAPI client_GetBufferSize(IAudioClient3 *iface, UINT32 *out) +static HRESULT WINAPI client_GetBufferSize(IWineAudioClient *iface, UINT32 *out) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); struct get_buffer_size_params params;
TRACE("(%p)->(%p)\n", This, out); @@ -731,9 +734,9 @@ static HRESULT WINAPI client_GetBufferSize(IAudioClient3 *iface, UINT32 *out) return params.result; }
-static HRESULT WINAPI client_GetStreamLatency(IAudioClient3 *iface, REFERENCE_TIME *latency) +static HRESULT WINAPI client_GetStreamLatency(IWineAudioClient *iface, REFERENCE_TIME *latency) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); struct get_latency_params params;
TRACE("(%p)->(%p)\n", This, latency); @@ -752,9 +755,9 @@ static HRESULT WINAPI client_GetStreamLatency(IAudioClient3 *iface, REFERENCE_TI return params.result; }
-static HRESULT WINAPI client_GetCurrentPadding(IAudioClient3 *iface, UINT32 *out) +static HRESULT WINAPI client_GetCurrentPadding(IWineAudioClient *iface, UINT32 *out) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); struct get_current_padding_params params;
TRACE("(%p)->(%p)\n", This, out); @@ -773,10 +776,10 @@ static HRESULT WINAPI client_GetCurrentPadding(IAudioClient3 *iface, UINT32 *out return params.result; }
-static HRESULT WINAPI client_IsFormatSupported(IAudioClient3 *iface, AUDCLNT_SHAREMODE mode, +static HRESULT WINAPI client_IsFormatSupported(IWineAudioClient *iface, AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt, WAVEFORMATEX **out) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); struct is_format_supported_params params;
TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out); @@ -788,7 +791,7 @@ static HRESULT WINAPI client_IsFormatSupported(IAudioClient3 *iface, AUDCLNT_SHA WAVEFORMATEX *mix_fmt; HRESULT hr;
- if (FAILED(hr = IAudioClient3_GetMixFormat(iface, &mix_fmt))) + if (FAILED(hr = IWineAudioClient_GetMixFormat(iface, &mix_fmt))) return hr;
if (fmt->nChannels != mix_fmt->nChannels || fmt->nSamplesPerSec != mix_fmt->nSamplesPerSec) { @@ -822,9 +825,9 @@ static HRESULT WINAPI client_IsFormatSupported(IAudioClient3 *iface, AUDCLNT_SHA return params.result; }
-static HRESULT WINAPI client_GetMixFormat(IAudioClient3 *iface, WAVEFORMATEX **pwfx) +static HRESULT WINAPI client_GetMixFormat(IWineAudioClient *iface, WAVEFORMATEX **pwfx) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); struct get_mix_format_params params;
TRACE("(%p)->(%p)\n", This, pwfx); @@ -851,10 +854,10 @@ static HRESULT WINAPI client_GetMixFormat(IAudioClient3 *iface, WAVEFORMATEX **p return params.result; }
-static HRESULT WINAPI client_GetDevicePeriod(IAudioClient3 *iface, REFERENCE_TIME *defperiod, +static HRESULT WINAPI client_GetDevicePeriod(IWineAudioClient *iface, REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface);
TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
@@ -864,9 +867,9 @@ static HRESULT WINAPI client_GetDevicePeriod(IAudioClient3 *iface, REFERENCE_TIM return get_periods(This, defperiod, minperiod); }
-static HRESULT WINAPI client_Start(IAudioClient3 *iface) +static HRESULT WINAPI client_Start(IWineAudioClient *iface) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); struct start_params params;
TRACE("(%p)\n", This); @@ -885,7 +888,7 @@ static HRESULT WINAPI client_Start(IAudioClient3 *iface) if ((This->timer_thread = CreateThread(NULL, 0, timer_loop_func, This, 0, NULL))) SetThreadPriority(This->timer_thread, THREAD_PRIORITY_TIME_CRITICAL); else { - IAudioClient3_Stop(&This->IAudioClient3_iface); + IWineAudioClient_Stop(&This->IWineAudioClient_iface); params.result = E_FAIL; } } @@ -895,9 +898,9 @@ static HRESULT WINAPI client_Start(IAudioClient3 *iface) return params.result; }
-static HRESULT WINAPI client_Stop(IAudioClient3 *iface) +static HRESULT WINAPI client_Stop(IWineAudioClient *iface) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); struct stop_params params;
TRACE("(%p)\n", This); @@ -912,9 +915,9 @@ static HRESULT WINAPI client_Stop(IAudioClient3 *iface) return params.result; }
-static HRESULT WINAPI client_Reset(IAudioClient3 *iface) +static HRESULT WINAPI client_Reset(IWineAudioClient *iface) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); struct reset_params params;
TRACE("(%p)\n", This); @@ -929,9 +932,9 @@ static HRESULT WINAPI client_Reset(IAudioClient3 *iface) return params.result; }
-static HRESULT WINAPI client_SetEventHandle(IAudioClient3 *iface, HANDLE event) +static HRESULT WINAPI client_SetEventHandle(IWineAudioClient *iface, HANDLE event) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); struct set_event_handle_params params;
TRACE("(%p)->(%p)\n", This, event); @@ -950,9 +953,9 @@ static HRESULT WINAPI client_SetEventHandle(IAudioClient3 *iface, HANDLE event) return params.result; }
-static HRESULT WINAPI client_GetService(IAudioClient3 *iface, REFIID riid, void **ppv) +static HRESULT WINAPI client_GetService(IWineAudioClient *iface, REFIID riid, void **ppv) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); HRESULT hr;
TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv); @@ -1028,10 +1031,10 @@ exit: return hr; }
-static HRESULT WINAPI client_IsOffloadCapable(IAudioClient3 *iface, AUDIO_STREAM_CATEGORY category, +static HRESULT WINAPI client_IsOffloadCapable(IWineAudioClient *iface, AUDIO_STREAM_CATEGORY category, BOOL *offload_capable) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface);
TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable);
@@ -1043,10 +1046,10 @@ static HRESULT WINAPI client_IsOffloadCapable(IAudioClient3 *iface, AUDIO_STREAM return S_OK; }
-static HRESULT WINAPI client_SetClientProperties(IAudioClient3 *iface, +static HRESULT WINAPI client_SetClientProperties(IWineAudioClient *iface, const AudioClientProperties *prop) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop;
TRACE("(%p)->(%p)\n", This, prop); @@ -1072,23 +1075,23 @@ static HRESULT WINAPI client_SetClientProperties(IAudioClient3 *iface, return S_OK; }
-static HRESULT WINAPI client_GetBufferSizeLimits(IAudioClient3 *iface, const WAVEFORMATEX *format, +static HRESULT WINAPI client_GetBufferSizeLimits(IWineAudioClient *iface, const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration, REFERENCE_TIME *max_duration) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); FIXME("(%p)->(%p, %u, %p, %p) - stub\n", This, format, event_driven, min_duration, max_duration); return E_NOTIMPL; }
-static HRESULT WINAPI client_GetSharedModeEnginePeriod(IAudioClient3 *iface, +static HRESULT WINAPI client_GetSharedModeEnginePeriod(IWineAudioClient *iface, const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames, UINT32 *min_period_frames, UINT32 *max_period_frames) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); REFERENCE_TIME def_period, min_period; HRESULT hr;
@@ -1108,11 +1111,11 @@ static HRESULT WINAPI client_GetSharedModeEnginePeriod(IAudioClient3 *iface, return hr; }
-static HRESULT WINAPI client_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface, +static HRESULT WINAPI client_GetCurrentSharedModeEnginePeriod(IWineAudioClient *iface, WAVEFORMATEX **cur_format, UINT32 *cur_period_frames) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); UINT32 dummy; HRESULT hr;
@@ -1127,12 +1130,12 @@ static HRESULT WINAPI client_GetCurrentSharedModeEnginePeriod(IAudioClient3 *ifa return client_GetSharedModeEnginePeriod(iface, *cur_format, cur_period_frames, &dummy, &dummy, &dummy); }
-static HRESULT WINAPI client_InitializeSharedAudioStream(IAudioClient3 *iface, DWORD flags, +static HRESULT WINAPI client_InitializeSharedAudioStream(IWineAudioClient *iface, DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format, const GUID *session_guid) { - struct audio_client *This = impl_from_IAudioClient3(iface); + struct audio_client *This = impl_from_IWineAudioClient(iface); REFERENCE_TIME period;
TRACE("(%p)->(0x%lx, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid)); @@ -1142,10 +1145,72 @@ static HRESULT WINAPI client_InitializeSharedAudioStream(IAudioClient3 *iface, D
period = period_frames * (REFERENCE_TIME)10000000 / format->nSamplesPerSec;
- return stream_init(This, FALSE, AUDCLNT_SHAREMODE_SHARED, flags, 0, period, format, session_guid); + return stream_init(This, FALSE, TRUE, AUDCLNT_SHAREMODE_SHARED, flags, 0, period, format, session_guid); +} + +static HRESULT WINAPI client_InitializeWine(IWineAudioClient *iface, BOOL format_check, + AUDCLNT_SHAREMODE mode, DWORD flags, + REFERENCE_TIME duration, REFERENCE_TIME period, + const WAVEFORMATEX *fmt, const GUID *sessionguid) +{ + struct audio_client *This = impl_from_IWineAudioClient(iface); + + TRACE("(%p)->(%x, %x, %lx, %s, %s, %p, %s)\n", This, mode, format_check, flags, wine_dbgstr_longlong(duration), + wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid)); + + return stream_init(This, TRUE, format_check, mode, flags, duration, period, fmt, sessionguid); +} + +static HRESULT WINAPI client_IsFormatSupportedWine(IWineAudioClient *iface, BOOL format_check, + AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt, WAVEFORMATEX **out) +{ + struct audio_client *This = impl_from_IWineAudioClient(iface); + struct is_format_supported_params params; + + TRACE("(%p)->(%x, %x, %p, %p)\n", This, format_check, mode, fmt, out); + + if (fmt) { + dump_fmt(fmt); + + if (format_check && mode == AUDCLNT_SHAREMODE_SHARED) { + WAVEFORMATEX *mix_fmt; + HRESULT hr; + + if (FAILED(hr = IWineAudioClient_GetMixFormat(iface, &mix_fmt))) + return hr; + + if (fmt->nChannels != mix_fmt->nChannels || fmt->nSamplesPerSec != mix_fmt->nSamplesPerSec) { + *out = mix_fmt; + return S_FALSE; + } + + CoTaskMemFree(mix_fmt); + } + } + + params.device = This->device_name; + params.flow = This->dataflow; + params.share = mode; + params.fmt_in = fmt; + params.fmt_out = NULL; + + if (out) { + *out = NULL; + if (mode == AUDCLNT_SHAREMODE_SHARED) + params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out)); + } + + wine_unix_call(is_format_supported, ¶ms); + + if (params.result == S_FALSE) + *out = ¶ms.fmt_out->Format; + else + CoTaskMemFree(params.fmt_out); + + return params.result; }
-const IAudioClient3Vtbl AudioClient3_Vtbl = +const IWineAudioClientVtbl WineAudioClient_Vtbl = { client_QueryInterface, client_AddRef, @@ -1168,6 +1233,8 @@ const IAudioClient3Vtbl AudioClient3_Vtbl = client_GetSharedModeEnginePeriod, client_GetCurrentSharedModeEnginePeriod, client_InitializeSharedAudioStream, + client_InitializeWine, + client_IsFormatSupportedWine, };
static HRESULT WINAPI clock_QueryInterface(IAudioClock *iface, REFIID riid, void **ppv) @@ -1199,13 +1266,13 @@ static HRESULT WINAPI clock_QueryInterface(IAudioClock *iface, REFIID riid, void static ULONG WINAPI clock_AddRef(IAudioClock *iface) { struct audio_client *This = impl_from_IAudioClock(iface); - return IAudioClient3_AddRef(&This->IAudioClient3_iface); + return IWineAudioClient_AddRef(&This->IWineAudioClient_iface); }
static ULONG WINAPI clock_Release(IAudioClock *iface) { struct audio_client *This = impl_from_IAudioClock(iface); - return IAudioClient3_Release(&This->IAudioClient3_iface); + return IWineAudioClient_Release(&This->IWineAudioClient_iface); }
static HRESULT WINAPI clock_GetFrequency(IAudioClock *iface, UINT64 *freq) @@ -1282,13 +1349,13 @@ static HRESULT WINAPI clock2_QueryInterface(IAudioClock2 *iface, REFIID riid, vo static ULONG WINAPI clock2_AddRef(IAudioClock2 *iface) { struct audio_client *This = impl_from_IAudioClock2(iface); - return IAudioClient3_AddRef(&This->IAudioClient3_iface); + return IWineAudioClient_AddRef(&This->IWineAudioClient_iface); }
static ULONG WINAPI clock2_Release(IAudioClock2 *iface) { struct audio_client *This = impl_from_IAudioClock2(iface); - return IAudioClient3_Release(&This->IAudioClient3_iface); + return IWineAudioClient_Release(&This->IWineAudioClient_iface); }
static HRESULT WINAPI clock2_GetDevicePosition(IAudioClock2 *iface, UINT64 *pos, UINT64 *qpctime) @@ -1326,19 +1393,19 @@ static HRESULT WINAPI AudioClockAdjustment_QueryInterface(IAudioClockAdjustment REFIID riid, void **ppv) { struct audio_client *This = impl_from_IAudioClockAdjustment(iface); - return IAudioClient3_QueryInterface(&This->IAudioClient3_iface, riid, ppv); + return IWineAudioClient_QueryInterface(&This->IWineAudioClient_iface, riid, ppv); }
static ULONG WINAPI AudioClockAdjustment_AddRef(IAudioClockAdjustment *iface) { struct audio_client *This = impl_from_IAudioClockAdjustment(iface); - return IAudioClient3_AddRef(&This->IAudioClient3_iface); + return IWineAudioClient_AddRef(&This->IWineAudioClient_iface); }
static ULONG WINAPI AudioClockAdjustment_Release(IAudioClockAdjustment *iface) { struct audio_client *This = impl_from_IAudioClockAdjustment(iface); - return IAudioClient3_Release(&This->IAudioClient3_iface); + return IWineAudioClient_Release(&This->IWineAudioClient_iface); }
static HRESULT WINAPI AudioClockAdjustment_SetSampleRate(IAudioClockAdjustment *iface, float rate) @@ -1395,13 +1462,13 @@ static HRESULT WINAPI render_QueryInterface(IAudioRenderClient *iface, REFIID ri static ULONG WINAPI render_AddRef(IAudioRenderClient *iface) { struct audio_client *This = impl_from_IAudioRenderClient(iface); - return IAudioClient3_AddRef(&This->IAudioClient3_iface); + return IWineAudioClient_AddRef(&This->IWineAudioClient_iface); }
static ULONG WINAPI render_Release(IAudioRenderClient *iface) { struct audio_client *This = impl_from_IAudioRenderClient(iface); - return IAudioClient3_Release(&This->IAudioClient3_iface); + return IWineAudioClient_Release(&This->IWineAudioClient_iface); }
static HRESULT WINAPI render_GetBuffer(IAudioRenderClient *iface, UINT32 frames, BYTE **data) @@ -1483,13 +1550,13 @@ static HRESULT WINAPI streamvolume_QueryInterface(IAudioStreamVolume *iface, REF static ULONG WINAPI streamvolume_AddRef(IAudioStreamVolume *iface) { struct audio_client *This = impl_from_IAudioStreamVolume(iface); - return IAudioClient3_AddRef(&This->IAudioClient3_iface); + return IWineAudioClient_AddRef(&This->IWineAudioClient_iface); }
static ULONG WINAPI streamvolume_Release(IAudioStreamVolume *iface) { struct audio_client *This = impl_from_IAudioStreamVolume(iface); - return IAudioClient3_Release(&This->IAudioClient3_iface); + return IWineAudioClient_Release(&This->IWineAudioClient_iface); }
static HRESULT WINAPI streamvolume_GetChannelCount(IAudioStreamVolume *iface, UINT32 *out) @@ -1648,7 +1715,7 @@ HRESULT AudioClient_Create(GUID *guid, IMMDevice *device, IAudioClient **out) This->device_name = name;
This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl; - This->IAudioClient3_iface.lpVtbl = &AudioClient3_Vtbl; + This->IWineAudioClient_iface.lpVtbl = &WineAudioClient_Vtbl; This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl; This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl; This->IAudioClockAdjustment_iface.lpVtbl = &AudioClockAdjustment_Vtbl; @@ -1658,7 +1725,7 @@ HRESULT AudioClient_Create(GUID *guid, IMMDevice *device, IAudioClient **out) This->dataflow = dataflow; This->parent = device;
- hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient3_iface, &This->marshal); + hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IWineAudioClient_iface, &This->marshal); if (FAILED(hr)) { free(This->device_name); free(This); @@ -1667,8 +1734,8 @@ HRESULT AudioClient_Create(GUID *guid, IMMDevice *device, IAudioClient **out)
IMMDevice_AddRef(This->parent);
- *out = (IAudioClient *)&This->IAudioClient3_iface; - IAudioClient3_AddRef(&This->IAudioClient3_iface); + *out = (IAudioClient *)&This->IWineAudioClient_iface; + IWineAudioClient_AddRef(&This->IWineAudioClient_iface);
return S_OK; } diff --git a/dlls/mmdevapi/mmdevapi_private.h b/dlls/mmdevapi/mmdevapi_private.h index 8dff5aa0805..fbfeb9af333 100644 --- a/dlls/mmdevapi/mmdevapi_private.h +++ b/dlls/mmdevapi/mmdevapi_private.h @@ -25,6 +25,8 @@ #include <wine/list.h> #include <wine/unixlib.h>
+#include <wine/winemmdevapi.h> + #include "unixlib.h"
typedef struct audio_session { @@ -57,7 +59,7 @@ typedef struct audio_session_wrapper { } AudioSessionWrapper;
struct audio_client { - IAudioClient3 IAudioClient3_iface; + IWineAudioClient IWineAudioClient_iface; IAudioRenderClient IAudioRenderClient_iface; IAudioCaptureClient IAudioCaptureClient_iface; IAudioClock IAudioClock_iface; diff --git a/dlls/mmdevapi/session.c b/dlls/mmdevapi/session.c index 9b97cef9df8..4a8659f44f9 100644 --- a/dlls/mmdevapi/session.c +++ b/dlls/mmdevapi/session.c @@ -105,7 +105,7 @@ static ULONG WINAPI control_Release(IAudioSessionControl2 *iface) sessions_lock(); This->client->session_wrapper = NULL; sessions_unlock(); - IAudioClient3_Release(&This->client->IAudioClient3_iface); + IWineAudioClient_Release(&This->client->IWineAudioClient_iface); }
free(This); @@ -710,7 +710,7 @@ struct audio_session_wrapper *session_wrapper_create(struct audio_client *client
if (client) { ret->session = client->session; - IAudioClient3_AddRef(&client->IAudioClient3_iface); + IWineAudioClient_AddRef(&client->IWineAudioClient_iface); }
return ret; diff --git a/include/Makefile.in b/include/Makefile.in index 1c2c3a44a75..3f21de85658 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -986,6 +986,7 @@ SOURCES = \ wine/wined3d.h \ wine/winedmo.h \ wine/winedxgi.idl \ + wine/winemmdevapi.idl \ wine/wingdi16.h \ wine/winnet16.h \ wine/winuser16.h \ diff --git a/include/wine/winemmdevapi.idl b/include/wine/winemmdevapi.idl new file mode 100644 index 00000000000..243572baafc --- /dev/null +++ b/include/wine/winemmdevapi.idl @@ -0,0 +1,45 @@ +/* + * Copyright 2025 Giovanni Mascellani for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#pragma makedep header + +import "audioclient.idl"; + +[ + object, + local, + uuid(2ba738f3-da6d-4bd2-98f2-abad9b4aec1d) +] +interface IWineAudioClient : IAudioClient3 +{ + HRESULT InitializeWine( + [in] BOOL FormatCheck, + [in] AUDCLNT_SHAREMODE ShareMode, + [in] DWORD StreamFlags, + [in] REFERENCE_TIME hnsBufferDuration, + [in] REFERENCE_TIME hnsPeriodicity, + [in] const WAVEFORMATEX *pFormat, + [in] LPCGUID AudioSessionGuid + ); + HRESULT IsFormatSupportedWine( + [in] BOOL FormatCheck, + [in] AUDCLNT_SHAREMODE ShareMode, + [in] const WAVEFORMATEX *pFormat, + [out,unique] WAVEFORMATEX **ppClosestMatch + ); +}
From: Giovanni Mascellani gmascellani@codeweavers.com
--- dlls/mmdevapi/spatialaudio.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/dlls/mmdevapi/spatialaudio.c b/dlls/mmdevapi/spatialaudio.c index eab3ceade6b..b28a033eebf 100644 --- a/dlls/mmdevapi/spatialaudio.c +++ b/dlls/mmdevapi/spatialaudio.c @@ -702,6 +702,7 @@ static void static_mask_to_channels(AudioObjectType static_mask, WORD *count, DW
static HRESULT activate_stream(SpatialAudioStreamImpl *stream) { + IWineAudioClient *wine_audio_client; HRESULT hr; REFERENCE_TIME period;
@@ -731,9 +732,17 @@ static HRESULT activate_stream(SpatialAudioStreamImpl *stream) stream->stream_fmtex.Samples.wValidBitsPerSample = stream->stream_fmtex.Format.wBitsPerSample; stream->stream_fmtex.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- hr = IAudioClient_Initialize(stream->client, AUDCLNT_SHAREMODE_SHARED, + hr = IAudioClient_QueryInterface(stream->client, &IID_IWineAudioClient, (void**)&wine_audio_client); + if(FAILED(hr)){ + WARN("Getting the private interface failed: %08lx\n", hr); + IAudioClient_Release(stream->client); + return hr; + } + + hr = IWineAudioClient_InitializeWine(wine_audio_client, FALSE, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, period, 0, &stream->stream_fmtex.Format, NULL); + IWineAudioClient_Release(wine_audio_client); if(FAILED(hr)){ WARN("Initialize failed: %08lx\n", hr); IAudioClient_Release(stream->client);
From: Giovanni Mascellani gmascellani@codeweavers.com
--- libs/faudio/src/FAudio_platform_win32.c | 60 ++++++++++++++++++------- 1 file changed, 45 insertions(+), 15 deletions(-)
diff --git a/libs/faudio/src/FAudio_platform_win32.c b/libs/faudio/src/FAudio_platform_win32.c index 20f15e8fb96..9587f0c0872 100644 --- a/libs/faudio/src/FAudio_platform_win32.c +++ b/libs/faudio/src/FAudio_platform_win32.c @@ -43,6 +43,8 @@ #include <mmdeviceapi.h> #include <devpkey.h>
+#include <wine/winemmdevapi.h> + DEFINE_GUID(CLSID_CWMADecMediaObject, 0x2eeb4adf, 0x4578, 0x4d10, 0xbc, 0xa7, 0xbb, 0x95, 0x5f, 0x56, 0x32, 0x0a); DEFINE_MEDIATYPE_GUID(MFAudioFormat_XMAudio2, FAUDIO_FORMAT_XMAUDIO2);
@@ -343,6 +345,7 @@ void FAudio_PlatformInit( uint32_t *updateSize, void** platformDevice ) { + IWineAudioClient *wine_audio_client = NULL; struct FAudioAudioClientThreadArgs *args; struct FAudioWin32PlatformData *data; REFERENCE_TIME duration; @@ -410,15 +413,27 @@ void FAudio_PlatformInit( FAudio_assert(!FAILED(hr) && "Failed to create audio client!"); IMMDevice_Release(device);
+ if (FAILED(IAudioClient_QueryInterface(data->client, &IID_IWineAudioClient, (void**)&wine_audio_client))) + wine_audio_client = NULL; + if (flags & FAUDIO_1024_QUANTUM) duration = 213333; else duration = 100000;
- hr = IAudioClient_IsFormatSupported( - data->client, - AUDCLNT_SHAREMODE_SHARED, - &args->format.Format, - &closest - ); + if (wine_audio_client) + hr = IWineAudioClient_IsFormatSupportedWine( + wine_audio_client, + FALSE, + AUDCLNT_SHAREMODE_SHARED, + &args->format.Format, + &closest + ); + else + hr = IAudioClient_IsFormatSupported( + data->client, + AUDCLNT_SHAREMODE_SHARED, + &args->format.Format, + &closest + ); FAudio_assert(!FAILED(hr) && "Failed to find supported audio format!");
if (closest) @@ -428,17 +443,32 @@ void FAudio_PlatformInit( CoTaskMemFree(closest); }
- hr = IAudioClient_Initialize( - data->client, - AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - duration * 3, - 0, - &args->format.Format, - &GUID_NULL - ); + if (wine_audio_client) + hr = IWineAudioClient_InitializeWine( + wine_audio_client, + FALSE, + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + duration * 3, + 0, + &args->format.Format, + &GUID_NULL + ); + else + hr = IAudioClient_Initialize( + data->client, + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + duration * 3, + 0, + &args->format.Format, + &GUID_NULL + ); FAudio_assert(!FAILED(hr) && "Failed to initialize audio client!");
+ if (wine_audio_client) + IWineAudioClient_Release(wine_audio_client); + hr = IAudioClient_SetEventHandle(data->client, audioEvent); FAudio_assert(!FAILED(hr) && "Failed to set audio client event!");
From: Giovanni Mascellani gmascellani@codeweavers.com
--- dlls/winmm/waveform.c | 83 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 16 deletions(-)
diff --git a/dlls/winmm/waveform.c b/dlls/winmm/waveform.c index f7fe81710e0..f8e5dd0a309 100644 --- a/dlls/winmm/waveform.c +++ b/dlls/winmm/waveform.c @@ -44,6 +44,7 @@ #include "audiopolicy.h"
#include "wine/debug.h" +#include "wine/winemmdevapi.h"
WINE_DEFAULT_DEBUG_CHANNEL(winmm);
@@ -429,8 +430,8 @@ static HRESULT WINMM_GetFriendlyName(IMMDevice *device, WCHAR *out, return S_OK; }
-static HRESULT WINMM_TestFormat(IAudioClient *client, DWORD rate, DWORD depth, - WORD channels) +static HRESULT WINMM_TestFormat(IAudioClient *client, IWineAudioClient *wine_audio_client, + DWORD rate, DWORD depth, WORD channels) { WAVEFORMATEX fmt, *junk; HRESULT hr; @@ -443,8 +444,12 @@ static HRESULT WINMM_TestFormat(IAudioClient *client, DWORD rate, DWORD depth, fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign; fmt.cbSize = 0;
- hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, - &fmt, &junk); + if (wine_audio_client) + hr = IWineAudioClient_IsFormatSupportedWine(wine_audio_client, + FALSE, AUDCLNT_SHAREMODE_SHARED, &fmt, &junk); + else + hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, + &fmt, &junk); if(SUCCEEDED(hr)) CoTaskMemFree(junk);
@@ -486,18 +491,24 @@ static DWORD WINMM_GetSupportedFormats(IMMDevice *device) HRESULT hr; struct _TestFormat *fmt; IAudioClient *client; + IWineAudioClient *wine_audio_client;
hr = IMMDevice_Activate(device, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void**)&client); if(FAILED(hr)) return 0;
+ if (FAILED(IAudioClient_QueryInterface(client, &IID_IWineAudioClient, (void**)&wine_audio_client))) + wine_audio_client = NULL; + for(fmt = formats_to_test; fmt->flag; ++fmt){ - hr = WINMM_TestFormat(client, fmt->rate, fmt->depth, fmt->channels); + hr = WINMM_TestFormat(client, wine_audio_client, fmt->rate, fmt->depth, fmt->channels); if(hr == S_OK) flags |= fmt->flag; }
+ if (wine_audio_client) + IWineAudioClient_Release(wine_audio_client); IAudioClient_Release(client);
return flags; @@ -882,6 +893,7 @@ static MMRESULT WINMM_TryDeviceMapping(WINMM_Device *device, WAVEFORMATEX *fmt, WORD channels, DWORD freq, DWORD bits_per_samp, BOOL is_query, BOOL is_out) { WAVEFORMATEX target, *closer_fmt = NULL; + IWineAudioClient *wine_audio_client; HRESULT hr; MMRESULT mr;
@@ -896,11 +908,21 @@ static MMRESULT WINMM_TryDeviceMapping(WINMM_Device *device, WAVEFORMATEX *fmt, target.nAvgBytesPerSec = target.nSamplesPerSec * target.nBlockAlign; target.cbSize = 0;
- hr = IAudioClient_IsFormatSupported(device->client, - AUDCLNT_SHAREMODE_SHARED, &target, &closer_fmt); + if (FAILED(IAudioClient_QueryInterface(device->client, &IID_IWineAudioClient, (void**)&wine_audio_client))) + wine_audio_client = NULL; + + if (wine_audio_client) + hr = IWineAudioClient_IsFormatSupportedWine(wine_audio_client, FALSE, + AUDCLNT_SHAREMODE_SHARED, &target, &closer_fmt); + else + hr = IAudioClient_IsFormatSupported(device->client, + AUDCLNT_SHAREMODE_SHARED, &target, &closer_fmt); CoTaskMemFree(closer_fmt); - if(hr != S_OK) + if(hr != S_OK) { + if (wine_audio_client) + IWineAudioClient_Release(wine_audio_client); return WAVERR_BADFORMAT; + }
/* device supports our target format, so see if MSACM can * do the conversion */ @@ -911,18 +933,31 @@ static MMRESULT WINMM_TryDeviceMapping(WINMM_Device *device, WAVEFORMATEX *fmt, mr = acmStreamOpen(&device->acm_handle, NULL, &target, fmt, NULL, 0, 0, 0); if(mr != MMSYSERR_NOERROR) + { + if (wine_audio_client) + IWineAudioClient_Release(wine_audio_client); return mr; + }
/* yes it can. initialize the audioclient and return success */ if(is_query){ acmStreamClose(device->acm_handle, 0); device->acm_handle = NULL; + if (wine_audio_client) + IWineAudioClient_Release(wine_audio_client); return MMSYSERR_NOERROR; }
- hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - AC_BUFLEN, 0, &target, &device->parent->session); + if (wine_audio_client) { + hr = IWineAudioClient_InitializeWine(wine_audio_client, FALSE, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + AC_BUFLEN, 0, &target, &device->parent->session); + IWineAudioClient_Release(wine_audio_client); + } else { + hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + AC_BUFLEN, 0, &target, &device->parent->session); + } if(hr != S_OK){ WARN("Initialize failed: %08lx\n", hr); acmStreamClose(device->acm_handle, 0); @@ -1062,6 +1097,7 @@ static MMRESULT WINMM_MapDevice(WINMM_Device *device, BOOL is_query, BOOL is_out static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info, BOOL is_out) { + IWineAudioClient *wine_audio_client; LRESULT ret = MMSYSERR_NOMEM; HRESULT hr;
@@ -1118,11 +1154,20 @@ static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info, sizeof(WAVEFORMATEX) + info->format->cbSize); }
+ if (FAILED(IAudioClient_QueryInterface(device->client, &IID_IWineAudioClient, (void**)&wine_audio_client))) + wine_audio_client = NULL; + if(info->flags & WAVE_FORMAT_QUERY){ WAVEFORMATEX *closer_fmt = NULL;
- hr = IAudioClient_IsFormatSupported(device->client, - AUDCLNT_SHAREMODE_SHARED, device->orig_fmt, &closer_fmt); + if (wine_audio_client) { + hr = IWineAudioClient_IsFormatSupportedWine(wine_audio_client, FALSE, + AUDCLNT_SHAREMODE_SHARED, device->orig_fmt, &closer_fmt); + IWineAudioClient_Release(wine_audio_client); + } else { + hr = IAudioClient_IsFormatSupported(device->client, + AUDCLNT_SHAREMODE_SHARED, device->orig_fmt, &closer_fmt); + } CoTaskMemFree(closer_fmt); if((hr == S_FALSE || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && !(info->flags & WAVE_FORMAT_DIRECT)) ret = WINMM_MapDevice(device, TRUE, is_out); @@ -1131,9 +1176,15 @@ static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info, goto error; }
- hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - AC_BUFLEN, 0, device->orig_fmt, &device->parent->session); + if (wine_audio_client) { + hr = IWineAudioClient_InitializeWine(wine_audio_client, FALSE, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + AC_BUFLEN, 0, device->orig_fmt, &device->parent->session); + IWineAudioClient_Release(wine_audio_client); + } else + hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, + AC_BUFLEN, 0, device->orig_fmt, &device->parent->session); if(FAILED(hr)){ if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT && !(info->flags & WAVE_FORMAT_DIRECT)){ ret = WINMM_MapDevice(device, FALSE, is_out);
From: Giovanni Mascellani gmascellani@codeweavers.com
--- dlls/windows.media.speech/recognizer.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 5c83beec063..f448010583b 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -24,6 +24,7 @@ #include "mmdeviceapi.h"
#include "wine/debug.h" +#include "wine/winemmdevapi.h"
WINE_DEFAULT_DEBUG_CHANNEL(speech);
@@ -1042,6 +1043,7 @@ DEFINE_IINSPECTABLE(recognizer_factory, ISpeechRecognizerFactory, struct recogni static HRESULT recognizer_factory_create_audio_capture(struct session *session) { const REFERENCE_TIME buffer_duration = 5000000; /* 0.5 second */ + IWineAudioClient *wine_audio_client = NULL; IMMDeviceEnumerator *mm_enum = NULL; IMMDevice *mm_device = NULL; WAVEFORMATEX wfx = { 0 }; @@ -1071,8 +1073,16 @@ static HRESULT recognizer_factory_create_audio_capture(struct session *session) wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; TRACE("wfx tag %u, channels %u, samples %lu, bits %u, align %u.\n", wfx.wFormatTag, wfx.nChannels, wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nBlockAlign);
- if (FAILED(hr = IAudioClient_Initialize(session->audio_client, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buffer_duration, 0, &wfx, NULL))) - goto cleanup; + if (FAILED(IAudioClient_QueryInterface(session->audio_client, &IID_IWineAudioClient, (void**)&wine_audio_client))) + wine_audio_client = NULL; + + if (wine_audio_client) { + if (FAILED(hr = IWineAudioClient_InitializeWine(wine_audio_client, FALSE, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buffer_duration, 0, &wfx, NULL))) + goto cleanup; + } else { + if (FAILED(hr = IAudioClient_Initialize(session->audio_client, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buffer_duration, 0, &wfx, NULL))) + goto cleanup; + }
if (FAILED(hr = IAudioClient_SetEventHandle(session->audio_client, session->audio_buf_event))) goto cleanup; @@ -1082,6 +1092,7 @@ static HRESULT recognizer_factory_create_audio_capture(struct session *session) session->capture_wfx = wfx;
cleanup: + if (wine_audio_client) IWineAudioClient_Release(wine_audio_client); if (mm_device) IMMDevice_Release(mm_device); if (mm_enum) IMMDeviceEnumerator_Release(mm_enum); CoTaskMemFree(str);
The latest revision is based on adding a Wine-specific interface to `IAudioClient` which allows other audio libraries wrapping over mmdevapi to request the old behavior in which there is no format checking. Adding a Wine-specific interface is not great (especially considering that it'll have to leak into FAudio), but all the alternatives I could come up seemed even worse or didn't work outright.
I'm not sure that's ready for review yet, I'll probably have to go through it at least once again, but in the meantime I'd like to see what tests look like.
It would be weird to have a Wine specific interface, I think if channel mapping is unsupported in WASAPI that is supposed to be done in users. But it feels like some parts of the picture are yet missing to come up with the final solution for all the usages. A few notes: - it looks like at least stereo format is advertised on 5 or 7 channel systems (while 5 or 7 channels vs other channel counts are not tested); - the tests only use IAudioClient. It might happen that IAudioClient3 has some differences, we already have a special case for initializing a stream from it.
Also, see https://learn.microsoft.com/en-us/windows/win32/coreaudio/audclnt-streamflag..., AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM flag description mentions "channel matrixer". Maybe there are actually some ways to control channel translation at least on newer audio client interfaces?
Honestly, it seems like the original change which enforced format check requires a lot of work throughout, if there is no way to force the old behaviour in a Windows-compatible way at least (but yet underexplored parts leave a big room for regressions in the wild). Maybe the best way forward would be to remove the check for render client for now (as far as I understand the initial trigger for the change only depends on that check for capture)? It is probably easier to fix the capture uses of WASAPI, because there is less of those and it might be less multivariant than render. And then once maybe the potentially missing and helpful parts (between the lines of those flags maybe) are implemented and the internal usages rely on that instead of old behaviour the extra checks may be added back.