Skip to content

Commit b59fb16

Browse files
author
hongqingwan
committed
win-wasapi: Add audio stream category selection to microphone capture properties
1 parent 32ba0c3 commit b59fb16

File tree

3 files changed

+155
-7
lines changed

3 files changed

+155
-7
lines changed

plugins/win-wasapi/data/locale/en-US.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,10 @@ Priority="Window Match Priority"
99
Priority.Title="Window title must match"
1010
Priority.Class="Match title, otherwise find window of same type"
1111
Priority.Exe="Match title, otherwise find window of same executable"
12+
StreamOrigin="Origin Stream[no audio effect]"
13+
StreamCategory="Audio category"
14+
Communications="Communications"
15+
Speech="Speech recognition"
16+
VoiceTyping="VoiceTyping"
17+
GameChat="GameChat"
18+
HardwareOffloaded="HardwareOffloaded(Hardware-offloaded audio processing allows the main audio processing tasks to be performed outside the computer's main CPU)"

plugins/win-wasapi/data/locale/zh-CN.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,10 @@ Priority="窗口匹配优先级"
99
Priority.Title="窗口标题必须匹配"
1010
Priority.Class="匹配标题,否则查找相同类型的窗口"
1111
Priority.Exe="匹配标题,否则查找相同可执行程序的窗口"
12+
StreamOrigin="原始流【无任何系统处理】"
13+
StreamCategory="音频流类型"
14+
Communications="实时通信"
15+
Speech="语音识别"
16+
VoiceTyping="语音听写"
17+
GameChat="游戏聊天"
18+
HardwareOffloaded="硬件卸载音频处理(硬件卸载音频处理允许在计算机主 CPU 之外执行主要的音频处理任务)"

plugins/win-wasapi/win-wasapi.cpp

Lines changed: 141 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ using namespace std;
2626

2727
#define OPT_DEVICE_ID "device_id"
2828
#define OPT_USE_DEVICE_TIMING "use_device_timing"
29+
30+
#define OPT_STREAM_ORIGIN "stream_origin"
31+
#define OPT_STREAM_CATEGORY "stream_category"
32+
33+
#define OPT_HARDWARE_OFFLOADED "hardware_offloaded"
34+
2935
#define OPT_WINDOW "window"
3036
#define OPT_PRIORITY "priority"
3137

@@ -162,6 +168,9 @@ class WASAPISource {
162168
string window_class;
163169
string title;
164170
string executable;
171+
string streamCategory;
172+
bool isOriginStream;
173+
bool isHardwareOffStream;
165174
HWND hwnd = NULL;
166175
DWORD process_id = 0;
167176
const SourceType sourceType;
@@ -241,6 +250,8 @@ class WASAPISource {
241250
static ComPtr<IMMDevice> InitDevice(IMMDeviceEnumerator *enumerator, bool isDefaultDevice, SourceType type,
242251
const string device_id);
243252
static ComPtr<IAudioClient> InitClient(IMMDevice *device, SourceType type, DWORD process_id,
253+
const string &streamCategory, bool isOriginStream,
254+
bool isHardwareOffStream,
244255
PFN_ActivateAudioInterfaceAsync activate_audio_interface_async,
245256
speaker_layout &speakers, audio_format &format, uint32_t &sampleRate);
246257
static void InitFormat(const WAVEFORMATEX *wfex, enum speaker_layout &speakers, enum audio_format &format,
@@ -259,6 +270,10 @@ class WASAPISource {
259270
string window_class;
260271
string title;
261272
string executable;
273+
274+
string streamCategory;
275+
bool isOriginStream;
276+
bool isHardwareOffStream;
262277
};
263278

264279
UpdateParams BuildUpdateParams(obs_data_t *settings);
@@ -496,6 +511,10 @@ WASAPISource::UpdateParams WASAPISource::BuildUpdateParams(obs_data_t *settings)
496511
params.executable = executable;
497512
bfree(executable);
498513
}
514+
} else if (sourceType == SourceType::Input) {
515+
params.streamCategory = obs_data_get_string(settings, OPT_STREAM_CATEGORY);
516+
params.isOriginStream = obs_data_get_bool(settings, OPT_STREAM_ORIGIN);
517+
params.isHardwareOffStream = obs_data_get_bool(settings, OPT_HARDWARE_OFFLOADED);
499518
}
500519

501520
return params;
@@ -510,6 +529,9 @@ void WASAPISource::UpdateSettings(UpdateParams &&params)
510529
window_class = std::move(params.window_class);
511530
title = std::move(params.title);
512531
executable = std::move(params.executable);
532+
streamCategory = std::move(params.streamCategory);
533+
isOriginStream = params.isOriginStream;
534+
isHardwareOffStream = params.isHardwareOffStream;
513535
}
514536

515537
void WASAPISource::LogSettings()
@@ -536,11 +558,16 @@ void WASAPISource::Update(obs_data_t *settings)
536558
{
537559
UpdateParams params = BuildUpdateParams(settings);
538560

539-
const bool restart = (sourceType == SourceType::ProcessOutput)
540-
? ((priority != params.priority) || (window_class != params.window_class) ||
541-
(title != params.title) || (executable != params.executable))
542-
: (device_id.compare(params.device_id) != 0);
543-
561+
bool restart = false;
562+
if (sourceType == SourceType::ProcessOutput) {
563+
restart = ((priority != params.priority) || (window_class != params.window_class) ||
564+
(title != params.title) || (executable != params.executable));
565+
} else if (sourceType == SourceType::DeviceOutput) {
566+
restart = (device_id.compare(params.device_id) != 0);
567+
} else if (sourceType == SourceType::Input) {
568+
restart = (device_id.compare(params.device_id) != 0) || isOriginStream != params.isOriginStream ||
569+
streamCategory != params.streamCategory || isHardwareOffStream != params.isHardwareOffStream;
570+
}
544571
UpdateSettings(std::move(params));
545572
LogSettings();
546573

@@ -630,7 +657,33 @@ static DWORD GetSpeakerChannelMask(speaker_layout layout)
630657
return (DWORD)layout;
631658
}
632659

660+
static inline AUDIO_STREAM_CATEGORY GetAudioStreamCategory(const string &category)
661+
{
662+
if (category == "Communications")
663+
return AudioCategory_Communications;
664+
else if (category == "Alerts")
665+
return AudioCategory_Alerts;
666+
else if (category == "SoundEffects")
667+
return AudioCategory_SoundEffects;
668+
else if (category == "GameEffects")
669+
return AudioCategory_GameEffects;
670+
else if (category == "GameMedia")
671+
return AudioCategory_GameMedia;
672+
else if (category == "GameChat")
673+
return AudioCategory_GameChat;
674+
else if (category == "Speech")
675+
return AudioCategory_Speech;
676+
else if (category == "Movie")
677+
return AudioCategory_Movie;
678+
else if (category == "Media")
679+
return AudioCategory_Media;
680+
else
681+
return AudioCategory_Other;
682+
}
683+
633684
ComPtr<IAudioClient> WASAPISource::InitClient(IMMDevice *device, SourceType type, DWORD process_id,
685+
const string &streamCategory, bool isOriginStream,
686+
bool isHardwareOffStream,
634687
PFN_ActivateAudioInterfaceAsync activate_audio_interface_async,
635688
speaker_layout &speakers, audio_format &format, uint32_t &samples_per_sec)
636689
{
@@ -707,10 +760,52 @@ ComPtr<IAudioClient> WASAPISource::InitClient(IMMDevice *device, SourceType type
707760
DWORD flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
708761
if (type != SourceType::Input)
709762
flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
763+
764+
if (type == SourceType::Input && streamCategory != "default") {
765+
ComPtr<IAudioClient2> audioClient2 = nullptr;
766+
client->QueryInterface(__uuidof(IAudioClient2), (void **)(&audioClient2));
767+
if (audioClient2) {
768+
AudioClientProperties properties = {};
769+
properties.cbSize = sizeof(AudioClientProperties);
770+
BOOL isSupportOffloadStream = FALSE;
771+
properties.Options = isOriginStream ? AUDCLNT_STREAMOPTIONS_RAW : AUDCLNT_STREAMOPTIONS_NONE;
772+
audioClient2->IsOffloadCapable(properties.eCategory, &isSupportOffloadStream);
773+
flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
774+
properties.eCategory = GetAudioStreamCategory(streamCategory);
775+
776+
if (isSupportOffloadStream) {
777+
properties.bIsOffload = isHardwareOffStream ? TRUE : FALSE;
778+
}
779+
audioClient2->SetClientProperties(&properties);
780+
}
781+
}
782+
710783
res = client->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, BUFFER_TIME_100NS, 0, pFormat, nullptr);
711784
if (FAILED(res))
712785
throw HRError("Failed to initialize audio client", res);
713786

787+
if (type == SourceType::Input) {
788+
ComPtr<IAudioEffectsManager> audioEffectsManager;
789+
HRESULT hr = client->GetService(IID_PPV_ARGS(audioEffectsManager.Assign()));
790+
if (audioEffectsManager) {
791+
CoTaskMemPtr<AUDIO_EFFECT> audioEffects = nullptr;
792+
UINT32 numEffects = 0;
793+
audioEffectsManager->GetAudioEffects(&audioEffects, &numEffects);
794+
795+
for (UINT32 i = 0; i < numEffects; ++i) {
796+
if (audioEffects[i].id == AUDIO_EFFECT_TYPE_DEEP_NOISE_SUPPRESSION) {
797+
blog(LOG_INFO, "DEEP_NOISE_SUPPRESSION STATE:%d, Can Set:%d",
798+
audioEffects[i].state, audioEffects[i].canSetState);
799+
} else if (audioEffects[i].id == AUDIO_EFFECT_TYPE_AUTOMATIC_GAIN_CONTROL) {
800+
blog(LOG_INFO, "AUTOMATIC_GAIN_CONTROL STATE:%d, Can Set:%d",
801+
audioEffects[i].state, audioEffects[i].canSetState);
802+
} else if (audioEffects[i].id == AUDIO_EFFECT_TYPE_ACOUSTIC_ECHO_CANCELLATION) {
803+
blog(LOG_INFO, "ACOUSTIC_ECHO_CANCELLATION STATE:%d, Can Set:%d",
804+
audioEffects[i].state, audioEffects[i].canSetState);
805+
}
806+
}
807+
}
808+
}
714809
return client;
715810
}
716811

@@ -834,8 +929,9 @@ void WASAPISource::Initialize()
834929

835930
ResetEvent(receiveSignal);
836931

837-
ComPtr<IAudioClient> temp_client = InitClient(device, sourceType, process_id, activate_audio_interface_async,
838-
speakers, format, sampleRate);
932+
ComPtr<IAudioClient> temp_client = InitClient(device, sourceType, process_id, streamCategory, isOriginStream,
933+
isHardwareOffStream, activate_audio_interface_async, speakers,
934+
format, sampleRate);
839935
if (sourceType == SourceType::DeviceOutput)
840936
ClearBuffer(device);
841937
ComPtr<IAudioCaptureClient> temp_capture = InitCapture(temp_client, receiveSignal);
@@ -1324,6 +1420,9 @@ static void GetWASAPIDefaultsInput(obs_data_t *settings)
13241420
{
13251421
obs_data_set_default_string(settings, OPT_DEVICE_ID, "default");
13261422
obs_data_set_default_bool(settings, OPT_USE_DEVICE_TIMING, false);
1423+
obs_data_set_default_bool(settings, OPT_STREAM_ORIGIN, false);
1424+
obs_data_set_default_string(settings, OPT_STREAM_CATEGORY, "default");
1425+
obs_data_set_default_bool(settings, OPT_HARDWARE_OFFLOADED, false);
13271426
}
13281427

13291428
static void GetWASAPIDefaultsDeviceOutput(obs_data_t *settings)
@@ -1454,6 +1553,24 @@ static bool UpdateWASAPIMethod(obs_properties_t *props, obs_property_t *, obs_da
14541553
return true;
14551554
}
14561555

1556+
static bool StreamCategoryChanged(obs_properties_t *props, obs_property_t *p, obs_data_t *settings)
1557+
{
1558+
std::string category = obs_data_get_string(settings, OPT_STREAM_CATEGORY);
1559+
1560+
obs_property_t *streamOrigin = obs_properties_get(props, OPT_STREAM_ORIGIN);
1561+
obs_property_t *streamHardwareOffload = obs_properties_get(props, OPT_HARDWARE_OFFLOADED);
1562+
1563+
if (category != "default") {
1564+
obs_property_set_enabled(streamOrigin, true);
1565+
obs_property_set_enabled(streamHardwareOffload, true);
1566+
} else {
1567+
obs_property_set_enabled(streamOrigin, false);
1568+
obs_property_set_enabled(streamHardwareOffload, false);
1569+
}
1570+
1571+
return true;
1572+
}
1573+
14571574
static obs_properties_t *GetWASAPIPropertiesInput(void *)
14581575
{
14591576
obs_properties_t *props = obs_properties_create();
@@ -1474,6 +1591,23 @@ static obs_properties_t *GetWASAPIPropertiesInput(void *)
14741591

14751592
obs_properties_add_bool(props, OPT_USE_DEVICE_TIMING, obs_module_text("UseDeviceTiming"));
14761593

1594+
obs_property_t *stream_category = obs_properties_add_list(props, OPT_STREAM_CATEGORY,
1595+
obs_module_text("StreamCategory"),
1596+
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
1597+
obs_property_set_modified_callback(stream_category, StreamCategoryChanged);
1598+
1599+
obs_property_list_add_string(stream_category, obs_module_text("Default"), "default");
1600+
obs_property_list_add_string(stream_category, obs_module_text("Communications"), "Communications");
1601+
obs_property_list_add_string(stream_category, obs_module_text("Speech"), "Speech");
1602+
obs_property_list_add_string(stream_category, obs_module_text("VoiceTyping"), "VoiceTyping");
1603+
obs_property_list_add_string(stream_category, obs_module_text("GameChat"), "GameChat");
1604+
1605+
obs_property_t *streamOrigin =
1606+
obs_properties_add_bool(props, OPT_STREAM_ORIGIN, obs_module_text("StreamOrigin"));
1607+
obs_property_set_enabled(streamOrigin, false);
1608+
obs_property_t *streamHardwareOffload =
1609+
obs_properties_add_bool(props, OPT_HARDWARE_OFFLOADED, obs_module_text("HardwareOffloaded"));
1610+
obs_property_set_enabled(streamHardwareOffload, false);
14771611
return props;
14781612
}
14791613

0 commit comments

Comments
 (0)