-- v3: dsound: Disable format check when initializing mmdevapi.
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);
From: Giovanni Mascellani gmascellani@codeweavers.com
--- dlls/dsound/capture.c | 25 +++++++++++++--- dlls/dsound/dsound.c | 56 +++++++++++++++++++++--------------- dlls/dsound/dsound_private.h | 7 +++-- dlls/dsound/primary.c | 34 +++++++++++++++++----- 4 files changed, 85 insertions(+), 37 deletions(-)
diff --git a/dlls/dsound/capture.c b/dlls/dsound/capture.c index e9012600cfa..54cd59f7be6 100644 --- a/dlls/dsound/capture.c +++ b/dlls/dsound/capture.c @@ -712,6 +712,7 @@ static HRESULT IDirectSoundCaptureBufferImpl_Create( HRESULT err = DS_OK; LPBYTE newbuf; DWORD buflen; + IWineAudioClient *wine_audio_client;
This->numIfaces = 0; This->ref = 0; @@ -743,9 +744,19 @@ static HRESULT IDirectSoundCaptureBufferImpl_Create( return err; }
- err = IAudioClient_Initialize(device->client, - AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - 200 * 100000, 0, device->pwfx, NULL); + if (FAILED(IAudioClient_QueryInterface(device->client, &IID_IWineAudioClient, (void**)&wine_audio_client))) + wine_audio_client = NULL; + + if (wine_audio_client) { + err = IWineAudioClient_InitializeWine(wine_audio_client, FALSE, + AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + 200 * 100000, 0, device->pwfx, NULL); + IWineAudioClient_Release(wine_audio_client); + } else { + err = IAudioClient_Initialize(device->client, + AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + 200 * 100000, 0, device->pwfx, NULL); + } if(FAILED(err)){ WARN("Initialize failed: %08lx\n", err); IAudioClient_Release(device->client); @@ -1004,6 +1015,7 @@ static HRESULT DirectSoundCaptureDevice_Initialize( struct _TestFormat *fmt; DirectSoundCaptureDevice *device; IAudioClient *client; + IWineAudioClient *wine_audio_client;
TRACE("(%p, %s)\n", ppDevice, debugstr_guid(lpcGUID));
@@ -1047,13 +1059,18 @@ static HRESULT DirectSoundCaptureDevice_Initialize( return DSERR_NODRIVER; }
+ if (FAILED(IAudioClient_QueryInterface(client, &IID_IWineAudioClient, (void**)&wine_audio_client))) + wine_audio_client = NULL; + for(fmt = formats_to_test; fmt->flag; ++fmt){ - if(DSOUND_check_supported(client, fmt->rate, fmt->depth, fmt->channels)){ + if(DSOUND_check_supported(client, wine_audio_client, fmt->rate, fmt->depth, fmt->channels)){ device->drvcaps.dwFormats |= fmt->flag; if(fmt->channels > device->drvcaps.dwChannels) device->drvcaps.dwChannels = fmt->channels; } } + if (wine_audio_client) + IWineAudioClient_Release(wine_audio_client); IAudioClient_Release(client);
*ppDevice = device; diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c index 7629810db50..6042c612957 100644 --- a/dlls/dsound/dsound.c +++ b/dlls/dsound/dsound.c @@ -247,8 +247,8 @@ static ULONG DirectSoundDevice_Release(DirectSoundDevice * device) return ref; }
-BOOL DSOUND_check_supported(IAudioClient *client, DWORD rate, - DWORD depth, WORD channels) +BOOL DSOUND_check_supported(IAudioClient *client, IWineAudioClient *wine_audio_client, + DWORD rate, DWORD depth, WORD channels) { WAVEFORMATEX fmt, *junk; HRESULT hr; @@ -261,7 +261,10 @@ BOOL DSOUND_check_supported(IAudioClient *client, DWORD rate, 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);
@@ -274,6 +277,7 @@ static HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGU GUID devGUID; DirectSoundDevice *device; IMMDevice *mmdevice; + IWineAudioClient *wine_audio_client;
TRACE("(%p,%s)\n",ppDevice,debugstr_guid(lpcGUID));
@@ -338,34 +342,40 @@ static HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGU
ZeroMemory(&device->drvcaps, sizeof(device->drvcaps));
- if(DSOUND_check_supported(device->client, 11025, 8, 1) || - DSOUND_check_supported(device->client, 22050, 8, 1) || - DSOUND_check_supported(device->client, 44100, 8, 1) || - DSOUND_check_supported(device->client, 48000, 8, 1) || - DSOUND_check_supported(device->client, 96000, 8, 1)) + if (FAILED(IAudioClient_QueryInterface(device->client, &IID_IWineAudioClient, (void**)&wine_audio_client))) + wine_audio_client = NULL; + + if(DSOUND_check_supported(device->client, wine_audio_client, 11025, 8, 1) || + DSOUND_check_supported(device->client, wine_audio_client, 22050, 8, 1) || + DSOUND_check_supported(device->client, wine_audio_client, 44100, 8, 1) || + DSOUND_check_supported(device->client, wine_audio_client, 48000, 8, 1) || + DSOUND_check_supported(device->client, wine_audio_client, 96000, 8, 1)) device->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYMONO;
- if(DSOUND_check_supported(device->client, 11025, 16, 1) || - DSOUND_check_supported(device->client, 22050, 16, 1) || - DSOUND_check_supported(device->client, 44100, 16, 1) || - DSOUND_check_supported(device->client, 48000, 16, 1) || - DSOUND_check_supported(device->client, 96000, 16, 1)) + if(DSOUND_check_supported(device->client, wine_audio_client, 11025, 16, 1) || + DSOUND_check_supported(device->client, wine_audio_client, 22050, 16, 1) || + DSOUND_check_supported(device->client, wine_audio_client, 44100, 16, 1) || + DSOUND_check_supported(device->client, wine_audio_client, 48000, 16, 1) || + DSOUND_check_supported(device->client, wine_audio_client, 96000, 16, 1)) device->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYMONO;
- if(DSOUND_check_supported(device->client, 11025, 8, 2) || - DSOUND_check_supported(device->client, 22050, 8, 2) || - DSOUND_check_supported(device->client, 44100, 8, 2) || - DSOUND_check_supported(device->client, 48000, 8, 2) || - DSOUND_check_supported(device->client, 96000, 8, 2)) + if(DSOUND_check_supported(device->client, wine_audio_client, 11025, 8, 2) || + DSOUND_check_supported(device->client, wine_audio_client, 22050, 8, 2) || + DSOUND_check_supported(device->client, wine_audio_client, 44100, 8, 2) || + DSOUND_check_supported(device->client, wine_audio_client, 48000, 8, 2) || + DSOUND_check_supported(device->client, wine_audio_client, 96000, 8, 2)) device->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYSTEREO;
- if(DSOUND_check_supported(device->client, 11025, 16, 2) || - DSOUND_check_supported(device->client, 22050, 16, 2) || - DSOUND_check_supported(device->client, 44100, 16, 2) || - DSOUND_check_supported(device->client, 48000, 16, 2) || - DSOUND_check_supported(device->client, 96000, 16, 2)) + if(DSOUND_check_supported(device->client, wine_audio_client, 11025, 16, 2) || + DSOUND_check_supported(device->client, wine_audio_client, 22050, 16, 2) || + DSOUND_check_supported(device->client, wine_audio_client, 44100, 16, 2) || + DSOUND_check_supported(device->client, wine_audio_client, 48000, 16, 2) || + DSOUND_check_supported(device->client, wine_audio_client, 96000, 16, 2)) device->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
+ if (wine_audio_client) + IWineAudioClient_Release(wine_audio_client); + /* the dsound mixer supports all of the following */ device->drvcaps.dwFlags |= DSCAPS_SECONDARY8BIT | DSCAPS_SECONDARY16BIT; device->drvcaps.dwFlags |= DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO; diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h index 28bba6cd64e..6f718440de9 100644 --- a/dlls/dsound/dsound_private.h +++ b/dlls/dsound/dsound_private.h @@ -31,6 +31,7 @@ #include "uuids.h"
#include "wine/list.h" +#include "wine/winemmdevapi.h"
#define DS_MAX_CHANNELS 6
@@ -242,7 +243,7 @@ DWORD CALLBACK DSOUND_mixthread(void *ptr); void DSOUND_Calc3DBuffer(IDirectSoundBufferImpl *dsb);
/* capture.c */ - + HRESULT DSOUND_CaptureCreate(REFIID riid, void **ppv); HRESULT DSOUND_CaptureCreate8(REFIID riid, void **ppv); HRESULT IDirectSoundCaptureImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_dsc8); @@ -265,7 +266,7 @@ void setup_dsound_options(void);
HRESULT get_mmdevice(EDataFlow flow, const GUID *tgt, IMMDevice **device);
-BOOL DSOUND_check_supported(IAudioClient *client, DWORD rate, - DWORD depth, WORD channels); +BOOL DSOUND_check_supported(IAudioClient *client, IWineAudioClient *wine_audio_client, + DWORD rate, DWORD depth, WORD channels); HRESULT enumerate_mmdevices(EDataFlow flow, GUID *guids, LPDSENUMCALLBACKW cb, void *user); diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c index 2073a32240c..0e3fdaebbc6 100644 --- a/dlls/dsound/primary.c +++ b/dlls/dsound/primary.c @@ -108,7 +108,7 @@ static DWORD DSOUND_FindSpeakerConfig(IMMDevice *mmdevice, int channels) }
static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, - BOOL forcewave, WAVEFORMATEX **wfx) + IWineAudioClient *wine_audio_client, BOOL forcewave, WAVEFORMATEX **wfx) { WAVEFORMATEXTENSIBLE *retwfe = NULL; WAVEFORMATEX *w; @@ -139,7 +139,12 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client testwfe.Format.nBlockAlign = testwfe.Format.nChannels * testwfe.Format.wBitsPerSample / 8; testwfe.Format.nAvgBytesPerSec = testwfe.Format.nSamplesPerSec * testwfe.Format.nBlockAlign;
- if (FAILED(IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, &testwfe.Format, (WAVEFORMATEX**)&retwfe))) + if (wine_audio_client) + hr = IWineAudioClient_IsFormatSupportedWine(wine_audio_client, FALSE, AUDCLNT_SHAREMODE_SHARED, &testwfe.Format, (WAVEFORMATEX**)&retwfe); + else + hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, &testwfe.Format, (WAVEFORMATEX**)&retwfe); + + if (FAILED(hr)) w = DSOUND_CopyFormat(&mixwfe->Format); else if (retwfe) w = DSOUND_CopyFormat(&retwfe->Format); @@ -180,7 +185,10 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client if (!w) return DSERR_OUTOFMEMORY;
- hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, w, (WAVEFORMATEX**)&retwfe); + if (wine_audio_client) + hr = IWineAudioClient_IsFormatSupportedWine(wine_audio_client, FALSE, AUDCLNT_SHAREMODE_SHARED, w, (WAVEFORMATEX**)&retwfe); + else + hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, w, (WAVEFORMATEX**)&retwfe); if (retwfe) { memcpy(w, retwfe, sizeof(WAVEFORMATEX) + retwfe->Format.cbSize); CoTaskMemFree(retwfe); @@ -288,6 +296,7 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave) DWORD frag_frames; WAVEFORMATEX *wfx = NULL; DWORD oldspeakerconfig = device->speaker_config; + IWineAudioClient *wine_audio_client;
TRACE("(%p, %d)\n", device, forcewave);
@@ -298,15 +307,26 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave) return hres; }
- hres = DSOUND_WaveFormat(device, client, forcewave, &wfx); + if (FAILED(IAudioClient_QueryInterface(client, &IID_IWineAudioClient, (void**)&wine_audio_client))) + wine_audio_client = NULL; + + hres = DSOUND_WaveFormat(device, client, wine_audio_client, forcewave, &wfx); if (FAILED(hres)) { + IWineAudioClient_Release(wine_audio_client); IAudioClient_Release(client); return hres; }
- hres = IAudioClient_Initialize(client, - AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST | - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 800000, 0, wfx, NULL); + if (wine_audio_client) { + hres = IWineAudioClient_InitializeWine(wine_audio_client, FALSE, + AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST | + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 800000, 0, wfx, NULL); + IWineAudioClient_Release(wine_audio_client); + } else { + hres = IAudioClient_Initialize(client, + AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST | + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 800000, 0, wfx, NULL); + } if(FAILED(hres)){ IAudioClient_Release(client); ERR("Initialize failed: %08lx\n", hres);
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.
I don't think that reasoning applies here; the "users" here are not regular Windows programs, but other Wine components. I don't think there is any reason to believe that on Windows those components (like dsound, qcap, winmm) are implemented on top of the public mmdevapi interface (especially considering how the public mmdevapi interface is constrained); it's more likely they have access to internal interfaces, so I don't see it wrong in principle to have an internal interface in Wine too.
I'd be happy to avoid the internal interface if possible, but it's not really obvious that it's possible to me.
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);
Yes, that's something to test. Another reason for putting this MR to draft mode. Hopefully in the next days I should receive some 7.1-capable hardware to test.
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?
I had missed it, and it's certainly interesting. I had noticed that AUDCLNT_STREAMFLAGS_RATEADJUST allows setting an arbitrary sampling rate, but not an arbitrary channel count. If AUTOCONVERTPCM indeed allows setting both an arbitrary sampling rate and channel count like the documentation seems to imply (I will check it), then it does half of the work of the private interface I added to this MR. Unfortunately it doesn't do the other half, i.e., the equivalent of `IsFormatSupported()`, because you can't pass stream flags to it.
A possible way forward might be to rely on the fact that in practice all audio backends support all possible sample format/channel count/sampling rate combinations, therefore there is nothing to check. I can try to rewrite this MR with this philosophy and see what happens.
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)?
Ok, I can do that. It doesn't seem a terrible regression to handle, since bisection should point to the relevant commit rather quickly, but taking the safest path is probably not a bad idea either.
On Fri Jul 25 20:35:01 2025 +0000, Giovanni Mascellani wrote:
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. I don't think that reasoning applies here; the "users" here are not regular Windows programs, but other Wine components. I don't think there is any reason to believe that on Windows those components (like dsound, qcap, winmm) are implemented on top of the public mmdevapi interface (especially considering how the public mmdevapi interface is constrained); it's more likely they have access to internal interfaces, so I don't see it wrong in principle to have an internal interface in Wine too. I'd be happy to avoid the internal interface if possible, but it's not really obvious that it's possible to me.
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); Yes, that's something to test. Another reason for putting this MR to draft mode. Hopefully in the next days I should receive some 7.1-capable hardware to test.
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? I had missed it, and it's certainly interesting. I had noticed that AUDCLNT_STREAMFLAGS_RATEADJUST allows setting an arbitrary sampling rate, but not an arbitrary channel count. If AUTOCONVERTPCM indeed allows setting both an arbitrary sampling rate and channel count like the documentation seems to imply (I will check it), then it does half of the work of the private interface I added to this MR. Unfortunately it doesn't do the other half, i.e., the equivalent of `IsFormatSupported()`, because you can't pass stream flags to it. A possible way forward might be to rely on the fact that in practice all audio backends support all possible sample format/channel count/sampling rate combinations, therefore there is nothing to check. I can try to rewrite this MR with this philosophy and see what happens.
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)? Ok, I can do that. It doesn't seem a terrible regression to handle, since bisection should point to the relevant commit rather quickly, but taking the safest path is probably not a bad idea either.
I thought FNA-XNA/FAudio works on Windows in principle, am I wrong?
Also, I don't know how native dsound / xaudio are implemented on Windows, but it would be weird if that was using some direct driver interfacing these days bypassing WASAPI, maybe using something undocumented there of course which we are not aware about. Did you test all the same with audio IAudioClient3? It might behave overall different in theory. In any case, custom interfaces are quite not favoured in Wine, in this case it looks rather obvious if WASAPI really doesn't allow for those conversions, through some flags or else, that should be done in the users of those.
Also, I just tried Wine's dsound_test.exe with Wine's dsound.dll on Windows. On Windows it fails in IDirectSoundFullDuplex_tests() only, but test_COM() passes. On Wine test_COM() fails now, but works with 00211db0d08d60ee9a0e40206bf7cf9b5b88987b reverted. IMO it might mean that actually something is not working quite right with commit itself too, not just with its usage.
Although this bit probably does not mean anything, test_COM() creates dsound interfaces with CoCreateInstance() and must be still pulling native dsound.dll.
Also, I don't know how native dsound / xaudio are implemented on Windows, but it would be weird if that was using some direct driver interfacing these days bypassing WASAPI, maybe using something undocumented there of course which we are not aware about.
Hmm, it seems you're right, that's also suggested by Microsoft's documentation: https://learn.microsoft.com/en-us/windows/win32/coreaudio/user-mode-audio-co...
Still, either they're using some private interface, or I can't see how they're doing format support checking, given that `IsFormatSupported()` is both documented and tested to always return false if channel count or sampling rate is different from the mix format, and it has no flags to alter its behavior.
Did you test all the same with audio IAudioClient3? It might behave overall different in theory.
You mean calling, say, `IAudioClient3_Initialize()` instead of `IAudioClient_Initialize()`? I can test that, but shouldn't they point to the same code in theory? Is it common to have the same method implemented differently in the subinterface and superinterface?
On Fri Jul 25 21:08:48 2025 +0000, Paul Gofman wrote:
Although this bit probably does not mean anything, test_COM() creates dsound interfaces with CoCreateInstance() and must be still pulling native dsound.dll.
It shouldn't be hard to check whether it's using native or Wine's dsound.dll.
Still, either they're using some private interface, or I can't see how they're doing format support checking, given that `IsFormatSupported()` is both documented and tested to always return false if channel count or sampling rate is different from the mix format, and it has no flags to alter its behavior.
I don't know, that obviously needs some exploration. But what if they don't use IsFormatSupported(), and instead just check things themselves and use audio client Initialize in a way which always supports necessary conversion?
On Fri Jul 25 21:19:34 2025 +0000, Paul Gofman wrote:
Still, either they're using some private interface, or I can't see how
they're doing format support checking, given that `IsFormatSupported()` is both documented and tested to always return false if channel count or sampling rate is different from the mix format, and it has no flags to alter its behavior. I don't know, that obviously needs some exploration. But what if they don't use IsFormatSupported(), and instead just check things themselves and use audio client Initialize in a way which always supports necessary conversion?
Or, otherwise, do all the conversion on their side.
On Fri Jul 25 21:20:00 2025 +0000, Paul Gofman wrote:
Or, otherwise, do all the conversion on their side.
Note that it would make certain sense if IsFormatSupported reports natively supported format by device / driver (maybe just one configuration in the shared mode, with some specifics on multichannel system), while audio client may be still performing conversion under certain circumstances. So when using it you might be interested in IsFormatSupported() but additionally apply some knowledge what can actually be converted on the WASAPI side.