Skip to content

Commit 754a825

Browse files
committed
fix(audio/windows): refactor logic for Steam Streaming Speakers audio format
* Match the virtual device's bits-per-sample to the default audio device, as a mismatch could cause audio glitches. * Add back f32 and s32 stereo formats which are now needed to support devices set to 32-bit. They are now lower priority than 24 and 16-bit to avoid regressing the fix for spatial audio settings. * Add more detailed logging around the virtual audio settings. Some users may be having problems caused by the internal mixer format, so that is now logged as well.
1 parent 7cda5fa commit 754a825

File tree

1 file changed

+36
-3
lines changed

1 file changed

+36
-3
lines changed

src/platform/windows/audio.cpp

+36-3
Original file line numberDiff line numberDiff line change
@@ -117,19 +117,24 @@ namespace {
117117

118118
using virtual_sink_waveformats_t = std::vector<WAVEFORMATEXTENSIBLE>;
119119

120+
// The list of virtual formats are sorted in preference order and the first valid format will be used.
121+
// All bits-per-sample options are listed because we try to match this to the default audio device.
120122
template<WORD channel_count>
121123
virtual_sink_waveformats_t create_virtual_sink_waveformats() {
122124
if constexpr (channel_count == 2) {
123125
auto channel_mask = waveformat_mask_stereo;
124-
// only choose 24 or 16-bit formats to avoid clobbering existing Dolby/DTS spatial audio settings
126+
// The 32-bit formats are a lower priority for stereo because using one will disable Dolby/DTS
127+
// spatial audio mode if the user enabled it on the Steam speaker.
125128
return {
126129
create_waveformat(sample_format_e::s24in32, channel_count, channel_mask),
127130
create_waveformat(sample_format_e::s24, channel_count, channel_mask),
128131
create_waveformat(sample_format_e::s16, channel_count, channel_mask),
132+
create_waveformat(sample_format_e::f32, channel_count, channel_mask),
133+
create_waveformat(sample_format_e::s32, channel_count, channel_mask),
129134
};
130135
} else if (channel_count == 6) {
131136
auto channel_mask1 = waveformat_mask_surround51_with_backspeakers;
132-
auto channel_mask2 = waveformat_mask_surround51_with_sidespeakers;
137+
auto channel_mask2 = waveformat_mask_surround51_with_sidespeakers; // XXX will never be used, but is probably the better 5.1 layout
133138
return {
134139
create_waveformat(sample_format_e::f32, channel_count, channel_mask1),
135140
create_waveformat(sample_format_e::f32, channel_count, channel_mask2),
@@ -298,6 +303,10 @@ namespace platf::audio {
298303
auto waveformatext_pointer = reinterpret_cast<const WAVEFORMATEXTENSIBLE *>(mixer_waveformat.get());
299304
capture_waveformat.dwChannelMask = waveformatext_pointer->dwChannelMask;
300305
}
306+
307+
BOOST_LOG(info) << "Audio mixer format is "sv << mixer_waveformat->wBitsPerSample << "-bit, "sv
308+
<< mixer_waveformat->nSamplesPerSec << " Hz, "sv
309+
<< ((mixer_waveformat->nSamplesPerSec != 48000) ? "will be resampled to 48000 by Windows"sv : "no resampling needed"sv);
301310
}
302311

303312
status = audio_client->Initialize(
@@ -315,7 +324,7 @@ namespace platf::audio {
315324
return nullptr;
316325
}
317326

318-
BOOST_LOG(info) << "Audio capture format is " << logging::bracket(waveformat_to_pretty_string(capture_waveformat));
327+
BOOST_LOG(info) << "Audio capture format is "sv << logging::bracket(waveformat_to_pretty_string(capture_waveformat));
319328

320329
return audio_client;
321330
}
@@ -793,6 +802,26 @@ namespace platf::audio {
793802
}
794803
}
795804

805+
// When switching to a Steam virtual speaker device, try to retain the bit depth of the
806+
// default audio device. Switching from a 16-bit device to a 24-bit one has been known to
807+
// cause glitches for some users.
808+
int wanted_bits_per_sample = 32;
809+
auto current_default_dev = default_device(device_enum);
810+
if (current_default_dev) {
811+
audio::prop_t prop;
812+
prop_var_t current_device_format;
813+
814+
// clang-format off: easier to read this split over 2 lines
815+
if ( SUCCEEDED(current_default_dev->OpenPropertyStore(STGM_READ, &prop))
816+
&& SUCCEEDED(prop->GetValue(PKEY_AudioEngine_DeviceFormat, &current_device_format.prop)) )
817+
{
818+
auto *format = (WAVEFORMATEXTENSIBLE *)current_device_format.prop.blob.pBlobData;
819+
wanted_bits_per_sample = format->Samples.wValidBitsPerSample;
820+
BOOST_LOG(info) << "Virtual audio device will use "sv << wanted_bits_per_sample << "-bit to match default device"sv;
821+
}
822+
// clang-format on
823+
}
824+
796825
auto &device_id = virtual_sink_info->first;
797826
auto &waveformats = virtual_sink_info->second.get().virtual_sink_waveformats;
798827
for (const auto &waveformat : waveformats) {
@@ -802,6 +831,10 @@ namespace platf::audio {
802831
auto waveformat_copy = waveformat;
803832
auto waveformat_copy_pointer = reinterpret_cast<WAVEFORMATEX *>(&waveformat_copy);
804833

834+
if (wanted_bits_per_sample != waveformat.Samples.wValidBitsPerSample) {
835+
continue;
836+
}
837+
805838
WAVEFORMATEXTENSIBLE p {};
806839
if (SUCCEEDED(policy->SetDeviceFormat(device_id_copy.c_str(), waveformat_copy_pointer, (WAVEFORMATEX *) &p))) {
807840
BOOST_LOG(info) << "Changed virtual audio sink format to " << logging::bracket(waveformat_to_pretty_string(waveformat));

0 commit comments

Comments
 (0)