Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Windows] enable XAudio2 sink in Windows desktop and code improvements #25068

Open
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

thexai
Copy link
Member

@thexai thexai commented Apr 26, 2024

Description

[Windows] enable XAudio2 sink in Windows desktop + code improvements

Now that Windows minimum version is Windows 8.1 is possible enable this sink also for Windows desktop as it's supported on Windows 8, 10 and 11.

Motivation and context

See: #25046 (comment)

Initially it was just a quick test to see what "would happen" when enabled XAudio on Windows desktop. It has turned out to work quite well but a multitude of things to improve have also been detected and hidden bugs have appeared in the enumeration of devices that were hidden since Xbox only uses one device.

It is not yet intended to be an alternative to DirectSound and does not replace it, it only adds XAudio as an additional option. I think that with these changes it is in a usable state although more issues will probably appear when tested on a large scale. The code improvements and general improvements are also valid for Xbox as the code is common.

Many of the changes are just code improvements with no changes in functionality. The only notable change is in the device enumeration in commit 4c40160. Also fixed issues on channels layout enumeration.

XAudio wants device Id in the from: {0.0.0.00000000}.{4f5f9f01-868c-4a8b-b96c-d1e12e05cc92} while the form {4F5F9F01-868C-4A8B-B96C-D1E12E05CC92} was being used. This causes code always fallback to default device here:

if (!bdefault)
{
hr = m_xAudio2->CreateMasteringVoice(&pMasterVoice, wfxex.Format.nChannels, wfxex.Format.nSamplesPerSec,
0, device.c_str(), nullptr, AudioCategory_Media);
}
if (!pMasterVoice)
{
if (!bdefault)
{
CLog::Log(LOGINFO,
__FUNCTION__ ": Could not locate the device named \"{}\" in the list of Xaudio "
"endpoint devices. Trying the default device...",
KODI::PLATFORM::WINDOWS::FromW(device));
}
// smartphone issue: providing device ID (even default ID) causes E_NOINTERFACE result
// workaround: device = nullptr will initialize default audio endpoint
hr = m_xAudio2->CreateMasteringVoice(&pMasterVoice, wfxex.Format.nChannels, wfxex.Format.nSamplesPerSec,
0, 0, nullptr, AudioCategory_Media);
if (FAILED(hr) || !pMasterVoice)
{
CLog::Log(LOGINFO,
__FUNCTION__ ": Could not retrieve the default XAudio audio endpoint ({}).",
WASAPIErrToStr(hr));
return false;
}

That was a known issue but not very important on Xbox since there is only one device anyway. For Windows Desktop this would not be acceptable because the user expects to open the device selected in settings and not always the default one i.e: HDMI - DENON-AVR (NVIDIA High Definition Audio).

How has this been tested?

Runtime Windows 11 x64 and Xbox Series S

All devices are enumerated correctly, channels layouts, sample rates, formats, etc.
Sound plays...

2024-04-28 11:10:45.632 T:4928     info <general>: Found 3 Lists of Devices
2024-04-28 11:10:45.632 T:4928     info <general>: Enumerated DIRECTSOUND devices:
2024-04-28 11:10:45.632 T:4928     info <general>:     Device 1
2024-04-28 11:10:45.632 T:4928     info <general>:         m_deviceName      : {4F5F9F01-868C-4A8B-B96C-D1E12E05CC92}
2024-04-28 11:10:45.632 T:4928     info <general>:         m_displayName     : SPDIF - SPDIF Interface (Realtek USB2.0 Audio)
2024-04-28 11:10:45.632 T:4928     info <general>:         m_displayNameExtra: DIRECTSOUND: SPDIF Interface (Realtek USB2.0 Audio)
2024-04-28 11:10:45.632 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_IEC958
2024-04-28 11:10:45.632 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.632 T:4928     info <general>:         m_sampleRates     : 48000
2024-04-28 11:10:45.632 T:4928     info <general>:         m_dataFormats     : AE_FMT_FLOAT,AE_FMT_RAW
2024-04-28 11:10:45.633 T:4928     info <general>:         m_streamTypes     : STREAM_TYPE_AC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_512
2024-04-28 11:10:45.633 T:4928     info <general>:     Device 2
2024-04-28 11:10:45.633 T:4928     info <general>:         m_deviceName      : {6A8744BE-9C7F-4E4E-AB1D-BC40FEFEE1E5}
2024-04-28 11:10:45.633 T:4928     info <general>:         m_displayName     : Speakers - Altavoces (Realtek USB2.0 Audio)
2024-04-28 11:10:45.633 T:4928     info <general>:         m_displayNameExtra: DIRECTSOUND: Altavoces (Realtek USB2.0 Audio)
2024-04-28 11:10:45.633 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_PCM
2024-04-28 11:10:45.633 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.633 T:4928     info <general>:         m_sampleRates     : 48000
2024-04-28 11:10:45.633 T:4928     info <general>:         m_dataFormats     : AE_FMT_FLOAT
2024-04-28 11:10:45.633 T:4928     info <general>:         m_streamTypes     : STREAM_TYPE_AC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_512
2024-04-28 11:10:45.633 T:4928     info <general>:     Device 3
2024-04-28 11:10:45.633 T:4928     info <general>:         m_deviceName      : default
2024-04-28 11:10:45.633 T:4928     info <general>:         m_displayName     : default
2024-04-28 11:10:45.633 T:4928     info <general>:         m_displayNameExtra: 
2024-04-28 11:10:45.633 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_PCM
2024-04-28 11:10:45.633 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.633 T:4928     info <general>:         m_sampleRates     : 48000
2024-04-28 11:10:45.634 T:4928     info <general>:         m_dataFormats     : AE_FMT_FLOAT
2024-04-28 11:10:45.634 T:4928     info <general>:         m_streamTypes     : STREAM_TYPE_AC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_512
2024-04-28 11:10:45.634 T:4928     info <general>:     Device 4
2024-04-28 11:10:45.634 T:4928     info <general>:         m_deviceName      : {B2BF28FD-954F-4BE6-931C-F58821B5F236}
2024-04-28 11:10:45.634 T:4928     info <general>:         m_displayName     : HDMI - DENON-AVR (NVIDIA High Definition Audio)
2024-04-28 11:10:45.634 T:4928     info <general>:         m_displayNameExtra: DIRECTSOUND: DENON-AVR (NVIDIA High Definition Audio)
2024-04-28 11:10:45.634 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_HDMI
2024-04-28 11:10:45.634 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.634 T:4928     info <general>:         m_sampleRates     : 48000
2024-04-28 11:10:45.634 T:4928     info <general>:         m_dataFormats     : AE_FMT_FLOAT,AE_FMT_RAW
2024-04-28 11:10:45.634 T:4928     info <general>:         m_streamTypes     : STREAM_TYPE_AC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_512
2024-04-28 11:10:45.634 T:4928     info <general>:     Device 5
2024-04-28 11:10:45.634 T:4928     info <general>:         m_deviceName      : {DC8229C7-9334-4344-8AC9-37D81DF4D15C}
2024-04-28 11:10:45.634 T:4928     info <general>:         m_displayName     : HDMI - PA249 (NVIDIA High Definition Audio)
2024-04-28 11:10:45.634 T:4928     info <general>:         m_displayNameExtra: DIRECTSOUND: PA249 (NVIDIA High Definition Audio)
2024-04-28 11:10:45.634 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_HDMI
2024-04-28 11:10:45.634 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.634 T:4928     info <general>:         m_sampleRates     : 48000
2024-04-28 11:10:45.635 T:4928     info <general>:         m_dataFormats     : AE_FMT_FLOAT,AE_FMT_RAW
2024-04-28 11:10:45.635 T:4928     info <general>:         m_streamTypes     : STREAM_TYPE_AC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_512
2024-04-28 11:10:45.635 T:4928     info <general>: Enumerated WASAPI devices:
2024-04-28 11:10:45.635 T:4928     info <general>:     Device 1
2024-04-28 11:10:45.635 T:4928     info <general>:         m_deviceName      : {4F5F9F01-868C-4A8B-B96C-D1E12E05CC92}
2024-04-28 11:10:45.635 T:4928     info <general>:         m_displayName     : SPDIF - SPDIF Interface (Realtek USB2.0 Audio)
2024-04-28 11:10:45.635 T:4928     info <general>:         m_displayNameExtra: WASAPI: SPDIF Interface (Realtek USB2.0 Audio)
2024-04-28 11:10:45.635 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_IEC958
2024-04-28 11:10:45.635 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.635 T:4928     info <general>:         m_sampleRates     : 192000,96000,88200,48000,44100
2024-04-28 11:10:45.635 T:4928     info <general>:         m_dataFormats     : AE_FMT_S24NE3,AE_FMT_S24LE3,AE_FMT_S24BE3,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_RAW
2024-04-28 11:10:45.635 T:4928     info <general>:         m_streamTypes     : STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3
2024-04-28 11:10:45.635 T:4928     info <general>:     Device 2
2024-04-28 11:10:45.635 T:4928     info <general>:         m_deviceName      : {6A8744BE-9C7F-4E4E-AB1D-BC40FEFEE1E5}
2024-04-28 11:10:45.635 T:4928     info <general>:         m_displayName     : Speakers - Altavoces (Realtek USB2.0 Audio)
2024-04-28 11:10:45.635 T:4928     info <general>:         m_displayNameExtra: WASAPI: Altavoces (Realtek USB2.0 Audio)
2024-04-28 11:10:45.635 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_PCM
2024-04-28 11:10:45.636 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.636 T:4928     info <general>:         m_sampleRates     : 192000,176400,96000,88200,48000,44100
2024-04-28 11:10:45.636 T:4928     info <general>:         m_dataFormats     : AE_FMT_S24NE3,AE_FMT_S24LE3,AE_FMT_S24BE3,AE_FMT_S32NE,AE_FMT_S32LE,AE_FMT_S32BE,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_RAW
2024-04-28 11:10:45.636 T:4928     info <general>:         m_streamTypes     : STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3
2024-04-28 11:10:45.636 T:4928     info <general>:     Device 3
2024-04-28 11:10:45.636 T:4928     info <general>:         m_deviceName      : default
2024-04-28 11:10:45.636 T:4928     info <general>:         m_displayName     : default
2024-04-28 11:10:45.636 T:4928     info <general>:         m_displayNameExtra: 
2024-04-28 11:10:45.636 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_PCM
2024-04-28 11:10:45.636 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.636 T:4928     info <general>:         m_sampleRates     : 192000,176400,96000,88200,48000,44100
2024-04-28 11:10:45.636 T:4928     info <general>:         m_dataFormats     : AE_FMT_S24NE3,AE_FMT_S24LE3,AE_FMT_S24BE3,AE_FMT_S32NE,AE_FMT_S32LE,AE_FMT_S32BE,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_RAW
2024-04-28 11:10:45.636 T:4928     info <general>:         m_streamTypes     : STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3
2024-04-28 11:10:45.636 T:4928     info <general>:     Device 4
2024-04-28 11:10:45.636 T:4928     info <general>:         m_deviceName      : {B2BF28FD-954F-4BE6-931C-F58821B5F236}
2024-04-28 11:10:45.636 T:4928     info <general>:         m_displayName     : HDMI - DENON-AVR (NVIDIA High Definition Audio)
2024-04-28 11:10:45.636 T:4928     info <general>:         m_displayNameExtra: WASAPI: DENON-AVR (NVIDIA High Definition Audio)
2024-04-28 11:10:45.636 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_HDMI
2024-04-28 11:10:45.637 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.637 T:4928     info <general>:         m_sampleRates     : 192000,176400,96000,88200,48000,44100,32000
2024-04-28 11:10:45.637 T:4928     info <general>:         m_dataFormats     : AE_FMT_S24NE4MSB,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_RAW
2024-04-28 11:10:45.637 T:4928     info <general>:         m_streamTypes     : STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3,STREAM_TYPE_DTSHD,STREAM_TYPE_DTSHD_MA,STREAM_TYPE_TRUEHD,STREAM_TYPE_EAC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3
2024-04-28 11:10:45.637 T:4928     info <general>:     Device 5
2024-04-28 11:10:45.637 T:4928     info <general>:         m_deviceName      : {DC8229C7-9334-4344-8AC9-37D81DF4D15C}
2024-04-28 11:10:45.637 T:4928     info <general>:         m_displayName     : HDMI - PA249 (NVIDIA High Definition Audio)
2024-04-28 11:10:45.637 T:4928     info <general>:         m_displayNameExtra: WASAPI: PA249 (NVIDIA High Definition Audio)
2024-04-28 11:10:45.637 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_HDMI
2024-04-28 11:10:45.637 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.637 T:4928     info <general>:         m_sampleRates     : 192000,48000
2024-04-28 11:10:45.637 T:4928     info <general>:         m_dataFormats     : AE_FMT_S24NE4MSB,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_RAW
2024-04-28 11:10:45.637 T:4928     info <general>:         m_streamTypes     : STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3,STREAM_TYPE_DTSHD,STREAM_TYPE_DTSHD_MA,STREAM_TYPE_TRUEHD,STREAM_TYPE_EAC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3,STREAM_TYPE_DTSHD,STREAM_TYPE_DTSHD_MA,STREAM_TYPE_TRUEHD,STREAM_TYPE_EAC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3
2024-04-28 11:10:45.637 T:4928     info <general>: Enumerated XAUDIO devices:
2024-04-28 11:10:45.637 T:4928     info <general>:     Device 1
2024-04-28 11:10:45.637 T:4928     info <general>:         m_deviceName      : {0.0.0.00000000}.{4f5f9f01-868c-4a8b-b96c-d1e12e05cc92}
2024-04-28 11:10:45.637 T:4928     info <general>:         m_displayName     : SPDIF - SPDIF Interface (Realtek USB2.0 Audio)
2024-04-28 11:10:45.637 T:4928     info <general>:         m_displayNameExtra: XAudio: SPDIF Interface (Realtek USB2.0 Audio)
2024-04-28 11:10:45.637 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_IEC958
2024-04-28 11:10:45.638 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.638 T:4928     info <general>:         m_sampleRates     : 192000,176400,96000,88200,48000,44100,32000,22050,11025
2024-04-28 11:10:45.638 T:4928     info <general>:         m_dataFormats     : AE_FMT_FLOAT,AE_FMT_S24NE3,AE_FMT_S24LE3,AE_FMT_S24BE3,AE_FMT_S24NE4MSB,AE_FMT_S32NE,AE_FMT_S32LE,AE_FMT_S32BE,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_U8
2024-04-28 11:10:45.638 T:4928     info <general>:         m_streamTypes     : No passthrough capabilities
2024-04-28 11:10:45.638 T:4928     info <general>:     Device 2
2024-04-28 11:10:45.638 T:4928     info <general>:         m_deviceName      : {0.0.0.00000000}.{6a8744be-9c7f-4e4e-ab1d-bc40fefee1e5}
2024-04-28 11:10:45.638 T:4928     info <general>:         m_displayName     : Speakers - Altavoces (Realtek USB2.0 Audio)
2024-04-28 11:10:45.638 T:4928     info <general>:         m_displayNameExtra: XAudio: Altavoces (Realtek USB2.0 Audio)
2024-04-28 11:10:45.638 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_PCM
2024-04-28 11:10:45.638 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.638 T:4928     info <general>:         m_sampleRates     : 192000,176400,96000,88200,48000,44100,32000,22050,11025
2024-04-28 11:10:45.638 T:4928     info <general>:         m_dataFormats     : AE_FMT_FLOAT,AE_FMT_S24NE3,AE_FMT_S24LE3,AE_FMT_S24BE3,AE_FMT_S24NE4MSB,AE_FMT_S32NE,AE_FMT_S32LE,AE_FMT_S32BE,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_U8
2024-04-28 11:10:45.638 T:4928     info <general>:         m_streamTypes     : No passthrough capabilities
2024-04-28 11:10:45.638 T:4928     info <general>:     Device 3
2024-04-28 11:10:45.638 T:4928     info <general>:         m_deviceName      : default
2024-04-28 11:10:45.638 T:4928     info <general>:         m_displayName     : default
2024-04-28 11:10:45.638 T:4928     info <general>:         m_displayNameExtra: 
2024-04-28 11:10:45.638 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_PCM
2024-04-28 11:10:45.639 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.639 T:4928     info <general>:         m_sampleRates     : 192000,176400,96000,88200,48000,44100,32000,22050,11025
2024-04-28 11:10:45.639 T:4928     info <general>:         m_dataFormats     : AE_FMT_FLOAT,AE_FMT_S24NE3,AE_FMT_S24LE3,AE_FMT_S24BE3,AE_FMT_S24NE4MSB,AE_FMT_S32NE,AE_FMT_S32LE,AE_FMT_S32BE,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_U8
2024-04-28 11:10:45.639 T:4928     info <general>:         m_streamTypes     : No passthrough capabilities
2024-04-28 11:10:45.639 T:4928     info <general>:     Device 4
2024-04-28 11:10:45.639 T:4928     info <general>:         m_deviceName      : {0.0.0.00000000}.{b2bf28fd-954f-4be6-931c-f58821b5f236}
2024-04-28 11:10:45.639 T:4928     info <general>:         m_displayName     : HDMI - DENON-AVR (NVIDIA High Definition Audio)
2024-04-28 11:10:45.639 T:4928     info <general>:         m_displayNameExtra: XAudio: DENON-AVR (NVIDIA High Definition Audio)
2024-04-28 11:10:45.639 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_HDMI
2024-04-28 11:10:45.639 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.639 T:4928     info <general>:         m_sampleRates     : 192000,176400,96000,88200,48000,44100,32000,22050,11025
2024-04-28 11:10:45.639 T:4928     info <general>:         m_dataFormats     : AE_FMT_FLOAT,AE_FMT_S24NE3,AE_FMT_S24LE3,AE_FMT_S24BE3,AE_FMT_S24NE4MSB,AE_FMT_S32NE,AE_FMT_S32LE,AE_FMT_S32BE,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_U8
2024-04-28 11:10:45.639 T:4928     info <general>:         m_streamTypes     : No passthrough capabilities
2024-04-28 11:10:45.639 T:4928     info <general>:     Device 5
2024-04-28 11:10:45.639 T:4928     info <general>:         m_deviceName      : {0.0.0.00000000}.{dc8229c7-9334-4344-8ac9-37d81df4d15c}
2024-04-28 11:10:45.639 T:4928     info <general>:         m_displayName     : HDMI - PA249 (NVIDIA High Definition Audio)
2024-04-28 11:10:45.639 T:4928     info <general>:         m_displayNameExtra: XAudio: PA249 (NVIDIA High Definition Audio)
2024-04-28 11:10:45.639 T:4928     info <general>:         m_deviceType      : AE_DEVTYPE_HDMI
2024-04-28 11:10:45.639 T:4928     info <general>:         m_channels        : FL, FR
2024-04-28 11:10:45.640 T:4928     info <general>:         m_sampleRates     : 192000,176400,96000,88200,48000,44100,32000,22050,11025
2024-04-28 11:10:45.640 T:4928     info <general>:         m_dataFormats     : AE_FMT_FLOAT,AE_FMT_S24NE3,AE_FMT_S24LE3,AE_FMT_S24BE3,AE_FMT_S24NE4MSB,AE_FMT_S32NE,AE_FMT_S32LE,AE_FMT_S32BE,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_U8
2024-04-28 11:10:45.640 T:4928     info <general>:         m_streamTypes     : No passthrough capabilities
2024-04-28 11:10:45.640 T:16920    info <general>: CActiveAESink::OpenSink - initialize sink
2024-04-28 11:10:45.972 T:16920    info <general>: CAESinkXAudio::InitializeInternal: Format is Supported - will attempt to Initialize
2024-04-28 11:10:45.972 T:16920    info <general>: CAESinkXAudio::InitializeInternal: XAudio Sink Initialized using: AE_FMT_FLOAT, 44100, 2

What is the effect on users?

Users have another type of sink to choose from besides DirectSound and WASAPI.

Pros:

  • Very low latency.
  • No possibility of buffer underruns since it's not a circular buffer but a queue based.
  • Modern API.
  • Like DirectSound does not take devices for exclusive use. Other applications can play audio at the same time.

Cons:

  • No passthrough support.

Screenshots (if appropriate):

XAudio

Types of change

  • Bug fix (non-breaking change which fixes an issue)
  • Clean up (non-breaking change which removes non-working, unmaintained functionality)
  • Improvement (non-breaking change which improves existing functionality)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that will cause existing functionality to change)
  • Cosmetic change (non-breaking change that doesn't touch code)
  • Student submission (PR was done for educational purposes and will be treated as such)
  • None of the above (please explain below)

Checklist:

  • My code follows the Code Guidelines of this project
  • My change requires a change to the documentation, either Doxygen or wiki
  • I have updated the documentation accordingly
  • I have read the Contributing document
  • I have added tests to cover my change
  • All new and existing tests passed

@thexai thexai force-pushed the XAudio2-test branch 2 times, most recently from 8d98ed4 to 4c40160 Compare April 27, 2024 18:10
@thexai thexai added this to the "P" 22.0 Alpha 1 milestone Apr 27, 2024
@thexai thexai marked this pull request as ready for review April 28, 2024 10:05
@thexai thexai added the Type: Improvement non-breaking change which improves existing functionality label Apr 28, 2024
@thexai
Copy link
Member Author

thexai commented Apr 29, 2024

Tested on Xbox:

2024-04-29 16:24:20.923 T:2596     info <general>: Found 2 Lists of Devices
2024-04-29 16:24:20.923 T:2596     info <general>: Enumerated WASAPI devices:
2024-04-29 16:24:20.923 T:2596     info <general>:     Device 1
2024-04-29 16:24:20.923 T:2596     info <general>:         m_deviceName      : \\?\SWD#MMDEVAPI#{0.0.0.00000000}.{62e9df40-8ca5-4339-b39d-e0c5b90c1795}#{e6327cad-dcec-4949-ae8a-991e976a79d2}
2024-04-29 16:24:20.923 T:2596     info <general>:         m_displayName     : HDMI - Digital Output (Virtual Audio Device)
2024-04-29 16:24:20.923 T:2596     info <general>:         m_displayNameExtra: WASAPI: Digital Output (Virtual Audio Device)
2024-04-29 16:24:20.923 T:2596     info <general>:         m_deviceType      : AE_DEVTYPE_HDMI
2024-04-29 16:24:20.923 T:2596     info <general>:         m_channels        : FL, FR, FC, LFE, SL, SR
2024-04-29 16:24:20.923 T:2596     info <general>:         m_sampleRates     : 192000,48000
2024-04-29 16:24:20.923 T:2596     info <general>:         m_dataFormats     : AE_FMT_S24NE4MSB,AE_FMT_RAW
2024-04-29 16:24:20.923 T:2596     info <general>:         m_streamTypes     : STREAM_TYPE_DTSHD,STREAM_TYPE_DTSHD_MA,STREAM_TYPE_TRUEHD,STREAM_TYPE_EAC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3
2024-04-29 16:24:20.923 T:2596     info <general>:     Device 2
2024-04-29 16:24:20.923 T:2596     info <general>:         m_deviceName      : default
2024-04-29 16:24:20.923 T:2596     info <general>:         m_displayName     : default
2024-04-29 16:24:20.923 T:2596     info <general>:         m_displayNameExtra: 
2024-04-29 16:24:20.923 T:2596     info <general>:         m_deviceType      : AE_DEVTYPE_HDMI
2024-04-29 16:24:20.923 T:2596     info <general>:         m_channels        : FL, FR, FC, LFE, SL, SR
2024-04-29 16:24:20.923 T:2596     info <general>:         m_sampleRates     : 192000,48000
2024-04-29 16:24:20.923 T:2596     info <general>:         m_dataFormats     : AE_FMT_S24NE4MSB,AE_FMT_RAW
2024-04-29 16:24:20.923 T:2596     info <general>:         m_streamTypes     : STREAM_TYPE_DTSHD,STREAM_TYPE_DTSHD_MA,STREAM_TYPE_TRUEHD,STREAM_TYPE_EAC3,STREAM_TYPE_DTSHD_CORE,STREAM_TYPE_DTS_2048,STREAM_TYPE_DTS_1024,STREAM_TYPE_DTS_512,STREAM_TYPE_AC3
2024-04-29 16:24:20.923 T:2596     info <general>: Enumerated XAUDIO devices:
2024-04-29 16:24:20.923 T:2596     info <general>:     Device 1
2024-04-29 16:24:20.923 T:2596     info <general>:         m_deviceName      : \\?\SWD#MMDEVAPI#{0.0.0.00000000}.{62e9df40-8ca5-4339-b39d-e0c5b90c1795}#{e6327cad-dcec-4949-ae8a-991e976a79d2}
2024-04-29 16:24:20.923 T:2596     info <general>:         m_displayName     : HDMI - Digital Output (Virtual Audio Device)
2024-04-29 16:24:20.924 T:2596     info <general>:         m_displayNameExtra: XAudio: Digital Output (Virtual Audio Device)
2024-04-29 16:24:20.924 T:2596     info <general>:         m_deviceType      : AE_DEVTYPE_HDMI
2024-04-29 16:24:20.924 T:2596     info <general>:         m_channels        : FL, FR, FC, LFE, SL, SR
2024-04-29 16:24:20.924 T:2596     info <general>:         m_sampleRates     : 192000,176400,96000,88200,48000,44100,32000,22050,11025
2024-04-29 16:24:20.924 T:2596     info <general>:         m_dataFormats     : AE_FMT_FLOAT,AE_FMT_S24NE3,AE_FMT_S24LE3,AE_FMT_S24BE3,AE_FMT_S24NE4MSB,AE_FMT_S32NE,AE_FMT_S32LE,AE_FMT_S32BE,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_U8
2024-04-29 16:24:20.924 T:2596     info <general>:         m_streamTypes     : No passthrough capabilities
2024-04-29 16:24:20.924 T:2596     info <general>:     Device 2
2024-04-29 16:24:20.924 T:2596     info <general>:         m_deviceName      : default
2024-04-29 16:24:20.924 T:2596     info <general>:         m_displayName     : default
2024-04-29 16:24:20.924 T:2596     info <general>:         m_displayNameExtra: 
2024-04-29 16:24:20.924 T:2596     info <general>:         m_deviceType      : AE_DEVTYPE_HDMI
2024-04-29 16:24:20.924 T:2596     info <general>:         m_channels        : FL, FR, FC, LFE, SL, SR
2024-04-29 16:24:20.924 T:2596     info <general>:         m_sampleRates     : 192000,176400,96000,88200,48000,44100,32000,22050,11025
2024-04-29 16:24:20.924 T:2596     info <general>:         m_dataFormats     : AE_FMT_FLOAT,AE_FMT_S24NE3,AE_FMT_S24LE3,AE_FMT_S24BE3,AE_FMT_S24NE4MSB,AE_FMT_S32NE,AE_FMT_S32LE,AE_FMT_S32BE,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_U8
2024-04-29 16:24:20.924 T:2596     info <general>:         m_streamTypes     : No passthrough capabilities
2024-04-29 16:24:20.924 T:2692     info <general>: CActiveAESink::OpenSink - initialize sink
2024-04-29 16:24:20.934 T:2692     info <general>: CAESinkXAudio::InitializeInternal: Format is Supported - will attempt to Initialize
2024-04-29 16:24:20.934 T:2692     info <general>: CAESinkXAudio::InitializeInternal: XAudio Sink Initialized using: AE_FMT_FLOAT, 44100, 2

Note hat fixes channel layout enumeration, before was:

2024-04-29 16:44:53.355 T:6708     info <general>:         m_deviceName      : default
2024-04-29 16:44:53.355 T:6708     info <general>:         m_displayName     : default
2024-04-29 16:44:53.355 T:6708     info <general>:         m_displayNameExtra: 
2024-04-29 16:44:53.355 T:6708     info <general>:         m_deviceType      : AE_DEVTYPE_HDMI
2024-04-29 16:44:53.355 T:6708     info <general>:         m_channels        : FL, FR, FC, BL, BR, LFE

And this is on Windows 11 PC when configured 5.1 speakers (same as Xbox with PR):

2024-04-29 17:45:12.951 T:14124    info <general>:         m_deviceName      : {0.0.0.00000000}.{803b913b-dc42-475b-85d2-faaf4045361b}
2024-04-29 17:45:12.951 T:14124    info <general>:         m_displayName     : HDMI - DENON-AVR (NVIDIA High Definition Audio)
2024-04-29 17:45:12.951 T:14124    info <general>:         m_displayNameExtra: XAudio: DENON-AVR (NVIDIA High Definition Audio)
2024-04-29 17:45:12.951 T:14124    info <general>:         m_deviceType      : AE_DEVTYPE_HDMI
2024-04-29 17:45:12.951 T:14124    info <general>:         m_channels        : FL, FR, FC, LFE, SL, SR
2024-04-29 17:45:12.951 T:14124    info <general>:         m_sampleRates     : 192000,176400,96000,88200,48000,44100,32000,22050,11025
2024-04-29 17:45:12.951 T:14124    info <general>:         m_dataFormats     : AE_FMT_FLOAT,AE_FMT_S24NE3,AE_FMT_S24LE3,AE_FMT_S24BE3,AE_FMT_S24NE4MSB,AE_FMT_S32NE,AE_FMT_S32LE,AE_FMT_S32BE,AE_FMT_S16NE,AE_FMT_S16LE,AE_FMT_S16BE,AE_FMT_U8
2024-04-29 17:45:12.951 T:14124    info <general>:         m_streamTypes     : No passthrough capabilities

@CrystalP
Copy link
Contributor

CrystalP commented Apr 30, 2024

It runs, I don't have the hardware to test multichannel, a/v sync is the other thing that needs a lot of testing. I'll try on Windows 8 as well.

One missing piece for desktop use is the handling of devices coming and going (plug or unplug headphones for example).
Windows 10 has "virtual audio endpoint" but it's a partial solution. More robust would be to handle XAUDIO2_E_DEVICE_INVALIDATED or add XAudio2 event handlers. Could be a future PR.
See MS recommendations https://learn.microsoft.com/en-us/windows/win32/xaudio2/xaudio2-redistributable#error-handling

It sounds like there is a way to query the processing quantum so maybe we don't have to guess the chunk size https://learn.microsoft.com/en-us/windows/win32/xaudio2/xaudio2-redistributable#duration-of-audio-processing-quantum
There doesn't seem to be online documentation but that interface can be cast from IXAudio2 and is defined in xaudio2.h

@thexai
Copy link
Member Author

thexai commented May 1, 2024

One missing piece for desktop use is the handling of devices coming and going (plug or unplug headphones for example).

This don't need to be handled by XAudio2 (or any other sink) directly. These system events are handled by AudioEngine (ActiveAE) and all this is already implemented and code is common for all Windows sinks:

https://github.com/xbmc/xbmc/blob/master/xbmc/platform/win32/IMMNotificationClient.h

HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
{
CLog::Log(LOGDEBUG, "{}: Added device: {}", __FUNCTION__, FromW(pwstrDeviceId));
NotifyAE();
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
{
CLog::Log(LOGDEBUG, "{}: Removed device: {}", __FUNCTION__, FromW(pwstrDeviceId));
NotifyAE();
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState)

Basically:

System Events ---> ActiveAE ---> Sinks

Sinks only needs obey what AE says. Every time that an audio device is plugged or unplugged AE is notified and enumerates all sinks and re-initializes same or other device/endpoint depending what is available.

It sounds like there is a way to query the processing quantum so maybe we don't have to guess the chunk size

chunk size is not guessed:

The duration we want use is known and predefined in code:

  const unsigned int bufferLen = static_cast<int>(format.m_sampleRate * 0.02); // 20 ms chunks
  m_dwFrameSize = wfxex.Format.nBlockAlign;
  m_dwChunkSize = m_dwFrameSize * bufferLen;
  m_dwBufferLen = m_dwChunkSize * 4; // 80 ms buffer
  m_AvgBytesPerSec = wfxex.Format.nAvgBytesPerSec;

  format.m_frames = bufferLen;

The value you say "quantum" is not very related and seems to be the value that the API uses internally as the minimum value. That is, if we use 20 ms in Audio Engine, the XAudio API will use 2 packets of 10 ms but that is done transparently. Obviously it is better that it be a multiple but this is already true.

a/v sync is the other thing that needs a lot of testing

Yes and no. Delay of sink is perfectly know and code it's implemented in CAESinkXAudio::GetDelay:

  XAUDIO2_VOICE_STATE state;
  m_sourceVoice->GetState(&state, 0);

  double delay = (double)(m_sinkFrames - state.SamplesPlayed) / m_format.m_sampleRate;
  status.SetDelay(delay);
  return;

m_sinkFrames is number of audio samples pushed to sink and state.SamplesPlayed are number of samples played. The difference is number of samples pending to play (queued samples) and with sample rate the time (delay) of audio in queue.

This is typically 40 ms because are 2 packets in queue and each packet is 20 ms.

#define XAUDIO_BUFFERS_IN_QUEUE 2

delay

@thexai thexai requested a review from fritsch May 1, 2024 06:23
@thexai thexai changed the title [Windows] enable XAudio2 sink in Win32 [Windows] enable XAudio2 sink in Windows desktop and code improvements May 1, 2024
@CrystalP
Copy link
Contributor

CrystalP commented May 6, 2024

Tested plugging/unplugging audio interface during playback and it works but has some rough edges on Windows 10. Since I haven't really tested something similar with DS or Wasapi, I'll dig more.
Windows 8: doesn't enumerate audio devices with
CAESinkXAudio::EnumerateDevicesEx: failed to activate XAudio for capability testin (Undefined)
I haven't had much time with this and will try to find out more. It would be best if we didn't have to ship the redistributable package.

Yes I was suggesting maybe making the chunk size a multiple of the quantum, not by coincidence but by properly querying XAudio. Unless AE prefers to buffer a different way.

@fritsch
Copy link
Member

fritsch commented May 6, 2024

For quantum and chunksize, the Linux setup nicely documents how AE works:

Short summary: https://github.com/xbmc/xbmc/blob/master/xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp#L739
Aim at 200 ms buffer, make sure to have at least 3 periods, then AE does not have to come back too often per second. As you see ALSA calculates in frames, so we have nicely aligned values (see integer division, based on samplerate).

@CrystalP
Copy link
Contributor

CrystalP commented May 6, 2024

Windows 10: noticed a couple times an issue with no sound after video pause. It's not clear if pause length is a factor or not. That wasn't on dev PC so couldn't just break into code. Will try to find more.

@CrystalP
Copy link
Contributor

CrystalP commented May 10, 2024

Windows 8: patch below allows enumeration and activation of output.

There were 3 problems:

  • XAudio2Create was always loading xaudio2_9.dll, which doesn't exist in Windows 8. Lookup the definition of the function in the Windows SDK. The change will try a few names instead. Works fine with Windows 8 and 10.
  • category parameter of CreateMasteringVoice. Windows 8 rejects the value AudioCategory_Media. Only 0 through 8 are allowed. Maybe 2 would be a little better than 0, but it looks mostly like things that matter for UWP apps.
  • device id parameter of CreateMasteringVoice. null is accepted but wrong format for specifying a device. The addition of strDeviceRawId may work in Windows 10 but that may be just luck, or is there documentation somewhere for that?
    Instead the device id should come from WinRT device enumeration (see https://walbourn.github.io/xaudio2-and-windows-8/, https://communityforums.atmeta.com/t5/PCVR-Development/Xaudio2-and-ovr-GetAudioDeviceOutGuidStr-create-mastering-voice/td-p/616396) and that format works with Windows 10 as well.

The catch is that the WinRT enumeration has less information than MMDevice enumeration used so far. It's a generic device enumeration and only provides device id and friendly name,The isdefault property doesn't work as we need it to for audio devices. Maybe there is some other WinRT API to connect the info with the information retrieved by MMDevice and grab the rest of the info, I didn't look too deep. It's possible to test that MMDevice GetId value is contained in a WinRT device id. Not documented but doesn't seem very risky.

As a possible workaround, I found a registry value that contains what we need and it fits well in the MMDevice enumeration, though I couldn't find an official name in the Windows SDK for it. That's the solution of the patch below.

Another way would be to allow only the default device (null device id), which works fine.

@CrystalP
Copy link
Contributor

patch on top of your last commit 93405197a6fd1aed70639f3d2880e098c9435e89

diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp
index 318dec1ac4..22063380b8 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp
@@ -13,6 +13,7 @@
 #include "cores/AudioEngine/Sinks/windows/AESinkFactoryWin.h"
 #include "cores/AudioEngine/Utils/AEDeviceInfo.h"
 #include "cores/AudioEngine/Utils/AEUtil.h"
+#include "utils/SystemInfo.h"
 #include "utils/log.h"
 
 #ifdef TARGET_WINDOWS_STORE
@@ -34,6 +35,47 @@ using namespace Microsoft::WRL;
 namespace
 {
 constexpr int XAUDIO_BUFFERS_IN_QUEUE = 2;
+
+HRESULT KXAudio2Create(IXAudio2** ppXAudio2,
+                       UINT32 Flags X2DEFAULT(0),
+                       XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR))
+{
+  typedef HRESULT(__stdcall * XAudio2CreateInfoFunc)(_Outptr_ IXAudio2**, UINT32,
+                                                     XAUDIO2_PROCESSOR);
+  static HMODULE dll = NULL;
+  static XAudio2CreateInfoFunc XAudio2CreateFn = nullptr;
+
+  if (dll == NULL)
+  {
+    dll = LoadLibraryEx(L"xaudio2_9redist.dll", NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
+
+    if (dll == NULL)
+    {
+      dll = LoadLibraryEx(L"xaudio2_9.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+
+      if (dll == NULL)
+      {
+        // Windows 8 compatibility
+        dll = LoadLibraryEx(L"xaudio2_8.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+
+        if (dll == NULL)
+          return HRESULT_FROM_WIN32(GetLastError());
+      }
+    }
+
+    XAudio2CreateFn = (XAudio2CreateInfoFunc)(void*)GetProcAddress(dll, "XAudio2Create");
+    if (XAudio2CreateFn == nullptr)
+    {
+      return HRESULT_FROM_WIN32(GetLastError());
+    }
+  }
+
+  if (XAudio2CreateFn != NULL)
+    return (*XAudio2CreateFn)(ppXAudio2, Flags, XAudio2Processor);
+  else
+    return E_FAIL;
+}
+
 } // namespace
 
 extern const char* WASAPIErrToStr(HRESULT err);
@@ -50,7 +92,7 @@ inline void SafeDestroyVoice(TVoice **ppVoice)
 
 CAESinkXAudio::CAESinkXAudio()
 {
-  HRESULT hr = XAudio2Create(m_xAudio2.ReleaseAndGetAddressOf(), 0);
+  HRESULT hr = KXAudio2Create(m_xAudio2.ReleaseAndGetAddressOf(), 0);
   if (FAILED(hr))
   {
     CLog::LogF(LOGERROR, "XAudio initialization failed.");
@@ -279,7 +321,12 @@ void CAESinkXAudio::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo
   IXAudio2SourceVoice* mSourceVoice = nullptr;
   Microsoft::WRL::ComPtr<IXAudio2> xaudio2;
 
-  hr = XAudio2Create(xaudio2.ReleaseAndGetAddressOf(), eflags);
+  // AudioCategory_Media is not accepted by Windows 8
+  const AUDIO_STREAM_CATEGORY streamCategory{
+      CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10) ? AudioCategory_Media
+                                                                       : AudioCategory_Other};
+
+  hr = KXAudio2Create(xaudio2.ReleaseAndGetAddressOf(), eflags);
   if (FAILED(hr))
   {
     CLog::LogF(LOGERROR, "failed to activate XAudio for capability testing ({})",
@@ -310,9 +357,15 @@ void CAESinkXAudio::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo
     wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
     wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
 
-    xaudio2->CreateMasteringVoice(&mMasterVoice, wfxex.Format.nChannels,
-                                  wfxex.Format.nSamplesPerSec, 0, deviceId.c_str(), nullptr,
-                                  AudioCategory_Media);
+    hr = xaudio2->CreateMasteringVoice(&mMasterVoice, wfxex.Format.nChannels,
+                                       wfxex.Format.nSamplesPerSec, 0, deviceId.c_str(), nullptr,
+                                       streamCategory);
+
+    if (FAILED(hr))
+    {
+      CLog::LogF(LOGERROR, "failed to create mastering voice ({})", WASAPIErrToStr(hr));
+      return;
+    }
 
     for (int p = AE_FMT_FLOAT; p > AE_FMT_INVALID; p--)
     {
@@ -363,11 +416,14 @@ void CAESinkXAudio::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool fo
 
       xaudio2->CreateMasteringVoice(&mMasterVoice, wfxex.Format.nChannels,
                                     wfxex.Format.nSamplesPerSec, 0, deviceId.c_str(), nullptr,
-                                    AudioCategory_Media);
-      hr = xaudio2->CreateSourceVoice(&mSourceVoice, &wfxex.Format);
-
+                                    streamCategory);
       if (SUCCEEDED(hr))
-        deviceInfo.m_sampleRates.push_back(WASAPISampleRates[j]);
+      {
+        hr = xaudio2->CreateSourceVoice(&mSourceVoice, &wfxex.Format);
+
+        if (SUCCEEDED(hr))
+          deviceInfo.m_sampleRates.push_back(WASAPISampleRates[j]);
+      }
     }
 
     SafeDestroyVoice(&mSourceVoice);
@@ -438,11 +494,16 @@ bool CAESinkXAudio::InitializeInternal(std::string deviceId, AEAudioFormat &form
 
   HRESULT hr;
   IXAudio2MasteringVoice* pMasterVoice = nullptr;
+  // AudioCategory_Media is not accepted by Windows 8
+  const AUDIO_STREAM_CATEGORY streamCategory{
+      CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10) ? AudioCategory_Media
+                                                                       : AudioCategory_Other};
 
   if (!bdefault)
   {
-    hr = m_xAudio2->CreateMasteringVoice(&pMasterVoice, wfxex.Format.nChannels, wfxex.Format.nSamplesPerSec,
-                                          0, device.c_str(), nullptr, AudioCategory_Media);
+    hr = m_xAudio2->CreateMasteringVoice(&pMasterVoice, wfxex.Format.nChannels,
+                                         wfxex.Format.nSamplesPerSec, 0, device.c_str(), nullptr,
+                                         streamCategory);
   }
 
   if (!pMasterVoice)
@@ -457,8 +518,9 @@ bool CAESinkXAudio::InitializeInternal(std::string deviceId, AEAudioFormat &form
 
     // smartphone issue: providing device ID (even default ID) causes E_NOINTERFACE result
     // workaround: device = nullptr will initialize default audio endpoint
-    hr = m_xAudio2->CreateMasteringVoice(&pMasterVoice, wfxex.Format.nChannels, wfxex.Format.nSamplesPerSec,
-                                          0, 0, nullptr, AudioCategory_Media);
+    hr = m_xAudio2->CreateMasteringVoice(&pMasterVoice, wfxex.Format.nChannels,
+                                         wfxex.Format.nSamplesPerSec, 0, nullptr, nullptr,
+                                         streamCategory);
     if (FAILED(hr) || !pMasterVoice)
     {
       CLog::LogF(LOGINFO, "Could not retrieve the default XAudio audio endpoint ({}).",
@@ -524,8 +586,9 @@ bool CAESinkXAudio::InitializeInternal(std::string deviceId, AEAudioFormat &form
         wfxex.Format.nSamplesPerSec    = WASAPISampleRates[i];
         wfxex.Format.nAvgBytesPerSec   = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
 
-        hr = m_xAudio2->CreateMasteringVoice(&m_masterVoice, wfxex.Format.nChannels, wfxex.Format.nSamplesPerSec,
-                                             0, device.c_str(), nullptr, AudioCategory_Media);
+        hr = m_xAudio2->CreateMasteringVoice(&m_masterVoice, wfxex.Format.nChannels,
+                                             wfxex.Format.nSamplesPerSec, 0, device.c_str(),
+                                             nullptr, streamCategory);
         if (SUCCEEDED(hr))
         {
           hr = m_xAudio2->CreateSourceVoice(&m_sourceVoice, &wfxex.Format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &m_voiceCallback);
diff --git a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp
index 3334b9ba27..d082effd1c 100644
--- a/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/windows/AESinkFactoryWin32.cpp
@@ -22,6 +22,8 @@ const IID IID_IAudioClient = __uuidof(IAudioClient);
 
 DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
 DEFINE_PROPERTYKEY(PKEY_Device_EnumeratorName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 24);
+const PROPERTYKEY PKEY_AudioEndpoint_Path{
+    {0x9c119480, 0xddc2, 0x4954, {0xa1, 0x50, 0x5b, 0xd2, 0x40, 0xd4, 0x54, 0xad}}, 1};
 
 extern const char *WASAPIErrToStr(HRESULT err);
 #define EXIT_ON_FAILURE(hr, reason) \
@@ -129,12 +131,21 @@ std::vector<RendererDetail> CAESinkFactoryWin::GetRendererDetails()
     details.uiChannelMask = std::max(varName.uintVal, (unsigned int)KSAUDIO_SPEAKER_STEREO);
     PropVariantClear(&varName);
 
+    hr = pProperty->GetValue(PKEY_AudioEndpoint_Path, &varName);
+    if (FAILED(hr))
+    {
+      CLog::LogF(LOGERROR, "Retrieval of endpoint path failed.");
+      goto failed;
+    }
+
+    details.strDeviceRawId = KODI::PLATFORM::WINDOWS::FromW(varName.pwszVal);
+    PropVariantClear(&varName);
+
     if (pDevice->GetId(&pwszID) == S_OK)
     {
       if (wstrDDID.compare(pwszID) == 0)
         details.bDefault = true;
 
-      details.strDeviceRawId = KODI::PLATFORM::WINDOWS::FromW(pwszID);
       CoTaskMemFree(pwszID);
     }

@CrystalP
Copy link
Contributor

Other thing noticed, both Windows 8 and 10: there seems to be a different in the delay/latency with the other sink types. It's easy to notice with GUI sounds turned on. With DirectSound in particular you hear the click much later than with XAudio2 (feels instant). WASAPI sounded similar to DS to me, but not always for some reason.
I tried an av sync test video (the bbchdsynctest) and think there is a difference between sinks as well. A sync delay is fine because of varying processing time by receiver or tv, but it should be the same for ds, wasapi and xaudio2, so it can be corrected once in audio settings.
Maybe it has something to do with the number of chunks used to prime the buffers?

@thexai
Copy link
Member Author

thexai commented May 11, 2024

Thank you very much for the diff. Added as is.

Tested and works fine also in Win11 and UWP.

I've changed the name from deviceRawId to devicePath which better reflects what it is.

Seems Microsoft has migrated at some point from use device GUID for identify audio devices to use Path. But in Windows desktop is need continue use GUID for compatibility with DirectSound and WASAPI although XAudio want Path to open devices.

While in UWP deviceId propriety already returns Path (Id and Path is the same). =>a0193dd

in Xbox deviceId is:
\\?\SWD#MMDEVAPI#{0.0.0.00000000}.{62e9df40-8ca5-4339-b39d-e0c5b90c1795}#{e6327cad-dcec-4949-ae8a-991e976a79d2}

Since device enumeration code is common for XAudio and WASAPI we can't replace Id by Path. As it is now, it is fine: both values are saved and it's used the one necessary in each case.

@thexai
Copy link
Member Author

thexai commented May 11, 2024

Other thing noticed, both Windows 8 and 10: there seems to be a different in the delay/latency with the other sink types. It's easy to notice with GUI sounds turned on. With DirectSound in particular you hear the click much later than with XAudio2 (feels instant).

Latency and a/v delay are different concepts.

Low latency is already mentioned:

  • Very low latency.
  • No possibility of buffer underruns since it's not a circular buffer but a queue based.
  • Modern API.
  • Like DirectSound does not take devices for exclusive use. Other applications can play audio at the same time.

Latency is time from click in GUI to hear sound is low in XAudio because only 2 packets of 20 ms in queue (latency = 40 ms)

On DirectSound now is 400 ms (it was less before Bluetooth fix).

EDIT:
It is not actually 400 but ~350 ms because the circular buffer is never completely filled (you would not be able to know if it is full or empty).

8 * 50 = 400 ms but as only it is filled with 7 packets = 350 ms

Even with 400 350 ms a/v may be perfect in sync (zero delay) because is not real time stream and Audio Engine knows sink delay and compensates that difference. Same for XAudio with 40 ms.

GUI sounds behaves different because is "realtime", video playback should be the same with DirectSound and XAudio.

@fritsch
Copy link
Member

fritsch commented May 11, 2024

One thing to keep in mind with this very low buffer: While AE has absolutely no problems by design to provide samples in time. A slow computer might not.

For menu sounds that is especially relevant when user did not activate keep audio alive. That way one might not hear menu sounds at all.

Also when enabled AEs activeaesink will output 1000/period size samples per second. With normal audio periods being around 32 ms for AC3 or PCM it makes not much sense to go below 64 ms. I would suggest to always have at least two periods buffer as a minimum to avoid designed underrun.

@thexai
Copy link
Member Author

thexai commented May 11, 2024

One point to note is that this has been working fine on Xbox since the beginning (and XAudio was the only sink available on Xbox). So there is no reason to change it now unless there emerge new issues on Windows desktop.

Any changes to this could cause regressions on Xbox. If this had to be changed, it could be done later, this PR only aims to enable XAudio on Windows desktop with minimal functional changes: most of the changes are just code improvements that do not change the functionality, except for the differences in device enumeration that are necessary for it to work.

@fritsch
Copy link
Member

fritsch commented May 11, 2024

No need to change anything. Just explained from a real-world scenario how often the activeaesink thread adds data. If it has no back buffer, e.g. two periods buffer, it will immediately run dry after adding the first packet.

All good.

@CrystalP
Copy link
Contributor

I wasn't very satisfied with the previous solution of using an undocumented registry value.
Instead, after moving some code around, it's possible to run the Xbox winrt enumerator on desktop and it works for Windows 8 and 10.
Here is a branch based on your last force-push, from which you can cherry-pick the last commit,
https://github.com/CrystalP/xbmc/tree/pr25068v3
also as a gist https://gist.github.com/CrystalP/63326c10f0af04079fc968baab0466c0

@thexai
Copy link
Member Author

thexai commented May 12, 2024

Maybe we can merge this and you continue later in a different PR.

This way at least we would have a check point in case there are regressions.

@thexai
Copy link
Member Author

thexai commented May 12, 2024

I've taken a look and I like the changes, I'm just saying that it would be easier to continue in a separate PR.

@CrystalP
Copy link
Contributor

That would be fine.

Last thing I noticed, with an old computer I sometimes get "clicks" or "cracks" in the audio when playback resumes after skipping. Maybe it has something to do with the small buffer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: Audio Platform: Windows Type: Improvement non-breaking change which improves existing functionality v22 "P"
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants