Add support for installing the Steam Streaming Speakers driver (#1262)
Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
This commit is contained in:
@@ -483,7 +483,8 @@ virtual_sink
|
|||||||
|
|
||||||
- Stream Streaming Speakers (Linux, macOS, Windows)
|
- Stream Streaming Speakers (Linux, macOS, Windows)
|
||||||
|
|
||||||
- To use this option, you must have Steam installed and have used Stream remote play at least once.
|
- Steam must be installed.
|
||||||
|
- Enable `install_steam_audio_drivers`_ or use Steam Remote Play at least once to install the drivers.
|
||||||
|
|
||||||
- `Virtual Audio Cable <https://vb-audio.com/Cable/>`_ (macOS, Windows)
|
- `Virtual Audio Cable <https://vb-audio.com/Cable/>`_ (macOS, Windows)
|
||||||
|
|
||||||
@@ -492,6 +493,22 @@ virtual_sink
|
|||||||
|
|
||||||
virtual_sink = Steam Streaming Speakers
|
virtual_sink = Steam Streaming Speakers
|
||||||
|
|
||||||
|
install_steam_audio_drivers
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
**Description**
|
||||||
|
Installs the Steam Streaming Speakers driver (if Steam is installed) to support surround sound and muting host audio.
|
||||||
|
|
||||||
|
.. Tip:: This option is only supported on Windows.
|
||||||
|
|
||||||
|
**Default**
|
||||||
|
``enabled``
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
install_steam_audio_drivers = enabled
|
||||||
|
|
||||||
Network
|
Network
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|||||||
@@ -382,7 +382,11 @@ namespace config {
|
|||||||
true // dwmflush
|
true // dwmflush
|
||||||
};
|
};
|
||||||
|
|
||||||
audio_t audio {};
|
audio_t audio {
|
||||||
|
{}, // audio_sink
|
||||||
|
{}, // virtual_sink
|
||||||
|
true, // install_steam_drivers
|
||||||
|
};
|
||||||
|
|
||||||
stream_t stream {
|
stream_t stream {
|
||||||
10s, // ping_timeout
|
10s, // ping_timeout
|
||||||
@@ -985,6 +989,7 @@ namespace config {
|
|||||||
|
|
||||||
string_f(vars, "audio_sink", audio.sink);
|
string_f(vars, "audio_sink", audio.sink);
|
||||||
string_f(vars, "virtual_sink", audio.virtual_sink);
|
string_f(vars, "virtual_sink", audio.virtual_sink);
|
||||||
|
bool_f(vars, "install_steam_audio_drivers", audio.install_steam_drivers);
|
||||||
|
|
||||||
string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, { "pc"sv, "lan"sv, "wan"sv });
|
string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, { "pc"sv, "lan"sv, "wan"sv });
|
||||||
string_restricted_f(vars, "origin_web_ui_allowed", nvhttp.origin_web_ui_allowed, { "pc"sv, "lan"sv, "wan"sv });
|
string_restricted_f(vars, "origin_web_ui_allowed", nvhttp.origin_web_ui_allowed, { "pc"sv, "lan"sv, "wan"sv });
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ namespace config {
|
|||||||
struct audio_t {
|
struct audio_t {
|
||||||
std::string sink;
|
std::string sink;
|
||||||
std::string virtual_sink;
|
std::string virtual_sink;
|
||||||
|
bool install_steam_drivers;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct stream_t {
|
struct stream_t {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include <synchapi.h>
|
#include <synchapi.h>
|
||||||
|
|
||||||
|
#include <newdev.h>
|
||||||
|
|
||||||
#define INITGUID
|
#define INITGUID
|
||||||
#include <propkeydef.h>
|
#include <propkeydef.h>
|
||||||
#undef INITGUID
|
#undef INITGUID
|
||||||
@@ -32,10 +34,20 @@ const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
|
|||||||
const IID IID_IAudioClient = __uuidof(IAudioClient);
|
const IID IID_IAudioClient = __uuidof(IAudioClient);
|
||||||
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
|
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
|
||||||
|
|
||||||
|
#if defined(__x86_64) || defined(_M_AMD64)
|
||||||
|
#define STEAM_DRIVER_SUBDIR L"x64"
|
||||||
|
#elif defined(__i386) || defined(_M_IX86)
|
||||||
|
#define STEAM_DRIVER_SUBDIR L"x86"
|
||||||
|
#else
|
||||||
|
#warning No known Steam audio driver for this architecture
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
namespace platf::audio {
|
namespace platf::audio {
|
||||||
constexpr auto SAMPLE_RATE = 48000;
|
constexpr auto SAMPLE_RATE = 48000;
|
||||||
|
|
||||||
|
constexpr auto STEAM_AUDIO_DRIVER_PATH = L"%CommonProgramFiles(x86)%\\Steam\\drivers\\Windows10\\" STEAM_DRIVER_SUBDIR L"\\SteamStreamingSpeakers.inf";
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void
|
void
|
||||||
Release(T *p) {
|
Release(T *p) {
|
||||||
@@ -254,7 +266,7 @@ namespace platf::audio {
|
|||||||
&device);
|
&device);
|
||||||
|
|
||||||
if (FAILED(status)) {
|
if (FAILED(status)) {
|
||||||
BOOST_LOG(error) << "Couldn't create audio Device [0x"sv << util::hex(status).to_string_view() << ']';
|
BOOST_LOG(error) << "Couldn't get default audio endpoint [0x"sv << util::hex(status).to_string_view() << ']';
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -577,20 +589,6 @@ namespace platf::audio {
|
|||||||
|
|
||||||
sink_t sink;
|
sink_t sink;
|
||||||
|
|
||||||
audio::device_enum_t device_enum;
|
|
||||||
auto status = CoCreateInstance(
|
|
||||||
CLSID_MMDeviceEnumerator,
|
|
||||||
nullptr,
|
|
||||||
CLSCTX_ALL,
|
|
||||||
IID_IMMDeviceEnumerator,
|
|
||||||
(void **) &device_enum);
|
|
||||||
|
|
||||||
if (FAILED(status)) {
|
|
||||||
BOOST_LOG(error) << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']';
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto device = default_device(device_enum);
|
auto device = default_device(device_enum);
|
||||||
if (!device) {
|
if (!device) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -602,7 +600,7 @@ namespace platf::audio {
|
|||||||
sink.host = converter.to_bytes(wstring.get());
|
sink.host = converter.to_bytes(wstring.get());
|
||||||
|
|
||||||
collection_t collection;
|
collection_t collection;
|
||||||
status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection);
|
auto status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection);
|
||||||
if (FAILED(status)) {
|
if (FAILED(status)) {
|
||||||
BOOST_LOG(error) << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']';
|
BOOST_LOG(error) << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||||
|
|
||||||
@@ -814,22 +812,8 @@ namespace platf::audio {
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio::device_enum_t device_enum;
|
|
||||||
auto status = CoCreateInstance(
|
|
||||||
CLSID_MMDeviceEnumerator,
|
|
||||||
nullptr,
|
|
||||||
CLSCTX_ALL,
|
|
||||||
IID_IMMDeviceEnumerator,
|
|
||||||
(void **) &device_enum);
|
|
||||||
|
|
||||||
if (FAILED(status)) {
|
|
||||||
BOOST_LOG(error) << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']';
|
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
collection_t collection;
|
collection_t collection;
|
||||||
status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection);
|
auto status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection);
|
||||||
if (FAILED(status)) {
|
if (FAILED(status)) {
|
||||||
BOOST_LOG(error) << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']';
|
BOOST_LOG(error) << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||||
|
|
||||||
@@ -878,6 +862,79 @@ namespace platf::audio {
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Installs the Steam Streaming Speakers driver, if present.
|
||||||
|
* @return `true` if installation was successful.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
install_steam_audio_drivers() {
|
||||||
|
#ifdef STEAM_DRIVER_SUBDIR
|
||||||
|
// MinGW's libnewdev.a is missing DiInstallDriverW() even though the headers have it,
|
||||||
|
// so we have to load it at runtime. It's Vista or later, so it will always be available.
|
||||||
|
auto newdev = LoadLibraryExW(L"newdev.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||||
|
if (!newdev) {
|
||||||
|
BOOST_LOG(error) << "newdev.dll failed to load"sv;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto fg = util::fail_guard([newdev]() {
|
||||||
|
FreeLibrary(newdev);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto fn_DiInstallDriverW = (decltype(DiInstallDriverW) *) GetProcAddress(newdev, "DiInstallDriverW");
|
||||||
|
if (!fn_DiInstallDriverW) {
|
||||||
|
BOOST_LOG(error) << "DiInstallDriverW() is missing"sv;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current default audio device (if present)
|
||||||
|
auto old_default_dev = default_device(device_enum);
|
||||||
|
|
||||||
|
// Install the Steam Streaming Speakers driver
|
||||||
|
WCHAR driver_path[MAX_PATH] = {};
|
||||||
|
ExpandEnvironmentStringsW(STEAM_AUDIO_DRIVER_PATH, driver_path, ARRAYSIZE(driver_path));
|
||||||
|
if (fn_DiInstallDriverW(nullptr, driver_path, 0, nullptr)) {
|
||||||
|
BOOST_LOG(info) << "Successfully installed Steam Streaming Speakers"sv;
|
||||||
|
|
||||||
|
// Wait for 5 seconds to allow the audio subsystem to reconfigure things before
|
||||||
|
// modifying the default audio device or enumerating devices again.
|
||||||
|
Sleep(5000);
|
||||||
|
|
||||||
|
// If there was a previous default device, restore that original device as the
|
||||||
|
// default output device just in case installing the new one changed it.
|
||||||
|
if (old_default_dev) {
|
||||||
|
audio::wstring_t old_default_id;
|
||||||
|
old_default_dev->GetId(&old_default_id);
|
||||||
|
|
||||||
|
for (int x = 0; x < (int) ERole_enum_count; ++x) {
|
||||||
|
policy->SetDefaultEndpoint(old_default_id.get(), (ERole) x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto err = GetLastError();
|
||||||
|
switch (err) {
|
||||||
|
case ERROR_ACCESS_DENIED:
|
||||||
|
BOOST_LOG(warning) << "Administrator privileges are required to install Steam Streaming Speakers"sv;
|
||||||
|
break;
|
||||||
|
case ERROR_FILE_NOT_FOUND:
|
||||||
|
case ERROR_PATH_NOT_FOUND:
|
||||||
|
BOOST_LOG(info) << "Steam audio drivers not found. This is expected if you don't have Steam installed."sv;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BOOST_LOG(warning) << "Failed to install Steam audio drivers: "sv << err;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
BOOST_LOG(warning) << "Unable to install Steam Streaming Speakers on unknown architecture"sv;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
init() {
|
init() {
|
||||||
auto status = CoCreateInstance(
|
auto status = CoCreateInstance(
|
||||||
@@ -893,12 +950,32 @@ namespace platf::audio {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status = CoCreateInstance(
|
||||||
|
CLSID_MMDeviceEnumerator,
|
||||||
|
nullptr,
|
||||||
|
CLSCTX_ALL,
|
||||||
|
IID_IMMDeviceEnumerator,
|
||||||
|
(void **) &device_enum);
|
||||||
|
|
||||||
|
if (FAILED(status)) {
|
||||||
|
BOOST_LOG(error) << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install Steam Streaming Speakers if needed. We do this during init() to ensure
|
||||||
|
// the sink information returned includes the new Steam Streaming Speakers device.
|
||||||
|
if (config::audio.install_steam_drivers && !find_device_id_by_name("Steam Streaming Speakers"s)) {
|
||||||
|
// This is best effort. Don't fail if it doesn't work.
|
||||||
|
install_steam_audio_drivers();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
~audio_control_t() override {}
|
~audio_control_t() override {}
|
||||||
|
|
||||||
policy_t policy;
|
policy_t policy;
|
||||||
|
audio::device_enum_t device_enum;
|
||||||
std::string assigned_sink;
|
std::string assigned_sink;
|
||||||
};
|
};
|
||||||
} // namespace platf::audio
|
} // namespace platf::audio
|
||||||
|
|||||||
@@ -515,6 +515,18 @@
|
|||||||
stream audio, while muting the host PC speakers.
|
stream audio, while muting the host PC speakers.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--Install Steam Audio Drivers-->
|
||||||
|
<div class="mb-3" v-if="platform === 'windows'">
|
||||||
|
<label for="install_steam_audio_drivers" class="form-label">Install Steam Audio Drivers</label>
|
||||||
|
<select id="install_steam_audio_drivers" class="form-select" v-model="config.install_steam_audio_drivers">
|
||||||
|
<option value="disabled">Disabled</option>
|
||||||
|
<option value="enabled">Enabled</option>
|
||||||
|
</select>
|
||||||
|
<div class="form-text">
|
||||||
|
If Steam is installed, this will automatically install the Steam Streaming Speakers driver to support
|
||||||
|
5.1/7.1 surround sound and muting host audio.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!--Adapter Name -->
|
<!--Adapter Name -->
|
||||||
<div class="mb-3" v-if="platform === 'windows'">
|
<div class="mb-3" v-if="platform === 'windows'">
|
||||||
<label for="adapter_name" class="form-label">Adapter Name</label>
|
<label for="adapter_name" class="form-label">Adapter Name</label>
|
||||||
@@ -992,6 +1004,7 @@
|
|||||||
"amd_vbaq": "enabled",
|
"amd_vbaq": "enabled",
|
||||||
"capture": "",
|
"capture": "",
|
||||||
"controller": "enabled",
|
"controller": "enabled",
|
||||||
|
"install_steam_audio_drivers": "enabled",
|
||||||
"dwmflush": "enabled",
|
"dwmflush": "enabled",
|
||||||
"encoder": "",
|
"encoder": "",
|
||||||
"fps": "[10,30,60,90,120]",
|
"fps": "[10,30,60,90,120]",
|
||||||
|
|||||||
Reference in New Issue
Block a user