2121#include < avrt.h>
2222#include < RTWorkQ.h>
2323#include < wrl/implements.h>
24+ #include < devicetopology.h>
2425
2526using namespace std ;
2627
@@ -289,6 +290,13 @@ class WASAPISource {
289290 obs_weak_source_release (reroute_target);
290291 reroute_target = obs_source_get_weak_source (target);
291292 }
293+
294+ static void DeviceTopologyTraversal (IMMDevice *device, obs_properties_t *props, obs_data_t *settings,
295+ bool modifyBySettins);
296+ static void DeviceTopologyTraversal (IConnector *connect, obs_properties_t *props, obs_data_t *settings,
297+ bool modifyBySettins);
298+ static void DeviceTopologyTraversal (IPart *part, obs_properties_t *props, obs_data_t *settings,
299+ bool modifyBySettins);
292300};
293301
294302WASAPISource::WASAPISource (obs_data_t *settings, obs_source_t *source_, SourceType type)
@@ -541,11 +549,19 @@ void WASAPISource::Update(obs_data_t *settings)
541549 (title != params.title ) || (executable != params.executable ))
542550 : (device_id.compare (params.device_id ) != 0 );
543551
552+ std::string deviceId = params.device_id ;
553+ bool isDefault = params.isDefaultDevice ;
544554 UpdateSettings (std::move (params));
545555 LogSettings ();
546556
547557 if (restart)
548558 SetEvent (restartSignal);
559+
560+ IMMDevice *device = GetMMDeviceById (isDefault, deviceId, sourceType == SourceType::Input);
561+ WASAPISource::DeviceTopologyTraversal (device, nullptr , settings, true );
562+ if (device) {
563+ device->Release ();
564+ }
549565}
550566
551567void WASAPISource::OnWindowChanged (obs_data_t *settings)
@@ -630,6 +646,201 @@ static DWORD GetSpeakerChannelMask(speaker_layout layout)
630646 return (DWORD)layout;
631647}
632648
649+ void WASAPISource::DeviceTopologyTraversal (IPart *part, obs_properties_t *props, obs_data_t *settings,
650+ bool modifyBySettins)
651+ {
652+ if (part == nullptr ) {
653+ return ;
654+ }
655+ IPartsList *partList = nullptr ;
656+ part->EnumPartsOutgoing (&partList);
657+ if (partList == nullptr ) {
658+ return ;
659+ }
660+ IPart *pPartNext = NULL ;
661+ partList->GetPart (0 , &pPartNext);
662+ if (pPartNext == nullptr ) {
663+ partList->Release ();
664+ return ;
665+ }
666+ PartType partType;
667+ HRESULT hr = pPartNext->GetPartType (&partType);
668+ if (FAILED (hr)) {
669+ partList->Release ();
670+ pPartNext->Release ();
671+ return ;
672+ }
673+
674+ if (partType == PartType::Subunit) {
675+ IKsJackDescription **Jack = nullptr ;
676+ hr = pPartNext->Activate (CLSCTX_INPROC_SERVER, __uuidof (IKsJackDescription), (void **)&Jack);
677+ GUID SubType = {};
678+ pPartNext->GetSubType (&SubType);
679+ LPWSTR name = nullptr ;
680+ pPartNext->GetName (&name);
681+ if (SubType == KSNODETYPE_LOUDNESS) {
682+ // TODO
683+ } else if (SubType == KSNODETYPE_ADC) {
684+ // TODO
685+ } else if (SubType == KSNODETYPE_VOLUME) {
686+ IAudioVolumeLevel *audioVolumeLevel = nullptr ;
687+ pPartNext->Activate (CLSCTX_ALL, __uuidof (IAudioVolumeLevel), (void **)&audioVolumeLevel);
688+ if (audioVolumeLevel) {
689+ float pfMinLevelDB = 0 .0f ;
690+ float pfMaxLevelDB = 0 .0f ;
691+ float pfStepping = 0 .0f ;
692+ hr = audioVolumeLevel->GetLevelRange (0 , &pfMinLevelDB, &pfMaxLevelDB, &pfStepping);
693+ if (SUCCEEDED (hr)) {
694+ size_t len = wcslen (name);
695+ size_t size = os_wcs_to_utf8 (name, len, nullptr , 0 ) + 1 ;
696+ std::string utf8_name;
697+ utf8_name.resize (size);
698+ os_wcs_to_utf8 (name, len, &utf8_name[0 ], size);
699+ if (modifyBySettins) {
700+ float pfLevelDB =
701+ (float )obs_data_get_double (settings, utf8_name.c_str ());
702+ audioVolumeLevel->SetLevel (0 , pfLevelDB, nullptr );
703+ } else {
704+ float pfLevelDB = 0 .0f ;
705+ audioVolumeLevel->GetLevel (0 , &pfLevelDB);
706+ obs_property_t *p = obs_properties_add_float_slider (
707+ props, utf8_name.c_str (), utf8_name.c_str (), pfMinLevelDB,
708+ pfMaxLevelDB, pfStepping);
709+ obs_data_set_double (settings, utf8_name.c_str (), pfLevelDB);
710+ }
711+ }
712+ audioVolumeLevel->Release ();
713+ }
714+ } else if (SubType == KSNODETYPE_MUTE) {
715+ IAudioMute *audioMute = nullptr ;
716+ pPartNext->Activate (CLSCTX_ALL, __uuidof (IAudioMute), (void **)&audioMute);
717+ if (audioMute != nullptr ) {
718+ size_t len = wcslen (name);
719+ size_t size = os_wcs_to_utf8 (name, len, nullptr , 0 ) + 1 ;
720+ std::string utf8_name;
721+ utf8_name.resize (size);
722+ os_wcs_to_utf8 (name, len, &utf8_name[0 ], size);
723+ if (modifyBySettins) {
724+ bool mute = obs_data_get_bool (settings, utf8_name.c_str ());
725+ audioMute->SetMute (mute, nullptr );
726+ } else {
727+ obs_property_t *p =
728+ obs_properties_add_bool (props, utf8_name.c_str (), utf8_name.c_str ());
729+ BOOL isMute = FALSE ;
730+ audioMute->GetMute (&isMute);
731+ obs_data_set_bool (settings, utf8_name.c_str (), !!isMute);
732+ }
733+ audioMute->Release ();
734+ }
735+ } else if (SubType == KSNODETYPE_AGC) {
736+ IAudioAutoGainControl *audioAutoGainControl = nullptr ;
737+ pPartNext->Activate (CLSCTX_ALL, __uuidof (IAudioAutoGainControl),
738+ (void **)&audioAutoGainControl);
739+
740+ if (audioAutoGainControl != nullptr ) {
741+ size_t len = wcslen (name);
742+ size_t size = os_wcs_to_utf8 (name, len, nullptr , 0 ) + 1 ;
743+ std::string utf8_name;
744+ utf8_name.resize (size);
745+ os_wcs_to_utf8 (name, len, &utf8_name[0 ], size);
746+ if (modifyBySettins) {
747+ bool enable = obs_data_get_bool (settings, utf8_name.c_str ());
748+ audioAutoGainControl->SetEnabled (enable, nullptr );
749+ } else {
750+ obs_property_t *p =
751+ obs_properties_add_bool (props, utf8_name.c_str (), utf8_name.c_str ());
752+ BOOL isEnabled = FALSE ;
753+ audioAutoGainControl->GetEnabled (&isEnabled);
754+ obs_data_set_bool (settings, utf8_name.c_str (), !!isEnabled);
755+ }
756+ audioAutoGainControl->Release ();
757+ }
758+ } else if (SubType == KSNODETYPE_TONE) {
759+ // TODO
760+ }
761+
762+ if (!name) {
763+ CoTaskMemFree (name);
764+ }
765+
766+ DeviceTopologyTraversal (pPartNext, props, settings, modifyBySettins);
767+ pPartNext->Release ();
768+ }
769+
770+ if (partType == PartType::Connector) {
771+ IConnector *connect = nullptr ;
772+ hr = pPartNext->QueryInterface (__uuidof (IConnector), (void **)&connect);
773+ DeviceTopologyTraversal (connect, props, settings, modifyBySettins);
774+ }
775+ }
776+
777+ void WASAPISource::DeviceTopologyTraversal (IConnector *connect, obs_properties_t *props, obs_data_t *settings,
778+ bool modifyBySettins)
779+ {
780+ if (connect == nullptr ) {
781+ return ;
782+ }
783+
784+ BOOL IsConnected = FALSE ;
785+ HRESULT hr = connect->IsConnected (&IsConnected);
786+ if (FAILED (hr)) {
787+ return ;
788+ }
789+
790+ if (IsConnected == FALSE ) {
791+ ConnectorType connType;
792+ hr = connect->GetType (&connType);
793+
794+ if (FAILED (hr)) {
795+ return ;
796+ }
797+ if (connType == Software_IO) {
798+ return ;
799+ }
800+ }
801+
802+ IConnector *connectedTo = nullptr ;
803+ hr = connect->GetConnectedTo (&connectedTo);
804+ if (FAILED (hr) || connectedTo == nullptr ) {
805+ return ;
806+ }
807+
808+ IPart *Part = nullptr ;
809+
810+ connectedTo->QueryInterface (__uuidof (IPart), (void **)&Part);
811+ DeviceTopologyTraversal (Part, props, settings, modifyBySettins);
812+ if (Part != nullptr ) {
813+ Part->Release ();
814+ }
815+
816+ connectedTo->Release ();
817+ }
818+
819+ void WASAPISource::DeviceTopologyTraversal (IMMDevice *device, obs_properties_t *props, obs_data_t *settings,
820+ bool modifyBySettins)
821+ {
822+ if (device == nullptr ) {
823+ return ;
824+ }
825+ IDeviceTopology *pDeviceTopology = nullptr ;
826+ HRESULT hr = device->Activate (__uuidof (IDeviceTopology), CLSCTX_ALL, nullptr , (void **)&pDeviceTopology);
827+ if (FAILED (hr))
828+ return ;
829+ UINT nConnectorCount = 0 ;
830+ hr = pDeviceTopology->GetConnectorCount (&nConnectorCount);
831+ if (FAILED (hr))
832+ return ;
833+ for (UINT i = 0 ; i < nConnectorCount; i++) {
834+ IConnector *pConnector = nullptr ;
835+ pDeviceTopology->GetConnector (i, &pConnector);
836+ DeviceTopologyTraversal (pConnector, props, settings, modifyBySettins);
837+ if (pConnector) {
838+ pConnector->Release ();
839+ }
840+ }
841+ pDeviceTopology->Release ();
842+ }
843+
633844ComPtr<IAudioClient> WASAPISource::InitClient (IMMDevice *device, SourceType type, DWORD process_id,
634845 PFN_ActivateAudioInterfaceAsync activate_audio_interface_async,
635846 speaker_layout &speakers, audio_format &format, uint32_t &samples_per_sec)
@@ -707,6 +918,7 @@ ComPtr<IAudioClient> WASAPISource::InitClient(IMMDevice *device, SourceType type
707918 DWORD flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
708919 if (type != SourceType::Input)
709920 flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
921+
710922 res = client->Initialize (AUDCLNT_SHAREMODE_SHARED, flags, BUFFER_TIME_100NS, 0 , pFormat, nullptr );
711923 if (FAILED (res))
712924 throw HRError (" Failed to initialize audio client" , res);
@@ -1454,8 +1666,29 @@ static bool UpdateWASAPIMethod(obs_properties_t *props, obs_property_t *, obs_da
14541666 return true ;
14551667}
14561668
1457- static obs_properties_t *GetWASAPIPropertiesInput ( void * )
1669+ static bool DeviceSelectionChanged ( obs_properties_t *props, obs_property_t *p, obs_data_t *settings )
14581670{
1671+ string id = obs_data_get_string (settings, OPT_DEVICE_ID);
1672+ obs_property_t *property = obs_properties_first (props);
1673+
1674+ while (property) {
1675+ std::string name = obs_property_name (property);
1676+ obs_property_next (&property);
1677+ if (name != OPT_DEVICE_ID && name != OPT_USE_DEVICE_TIMING) {
1678+ obs_properties_remove_by_name (props, name.c_str ());
1679+ }
1680+ }
1681+ IMMDevice *device = GetMMDeviceById (id == " default" ? true : false , id, true );
1682+ WASAPISource::DeviceTopologyTraversal (device, props, settings, false );
1683+ if (device) {
1684+ device->Release ();
1685+ }
1686+ return true ;
1687+ }
1688+
1689+ static obs_properties_t *GetWASAPIPropertiesInput (void *data)
1690+ {
1691+ WASAPISource *source = (WASAPISource *)data;
14591692 obs_properties_t *props = obs_properties_create ();
14601693 vector<AudioDeviceInfo> devices;
14611694
@@ -1473,7 +1706,7 @@ static obs_properties_t *GetWASAPIPropertiesInput(void *)
14731706 }
14741707
14751708 obs_properties_add_bool (props, OPT_USE_DEVICE_TIMING, obs_module_text (" UseDeviceTiming" ));
1476-
1709+ obs_property_set_modified_callback (device_prop, DeviceSelectionChanged);
14771710 return props;
14781711}
14791712
0 commit comments