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)
@@ -546,6 +554,12 @@ void WASAPISource::Update(obs_data_t *settings)
546554
547555 if (restart)
548556 SetEvent (restartSignal);
557+
558+ IMMDevice *device = GetMMDeviceById (params.isDefaultDevice , params.device_id , sourceType == SourceType::Input);
559+ WASAPISource::DeviceTopologyTraversal (device, nullptr , settings, true );
560+ if (device) {
561+ device->Release ();
562+ }
549563}
550564
551565void WASAPISource::OnWindowChanged (obs_data_t *settings)
@@ -630,6 +644,201 @@ static DWORD GetSpeakerChannelMask(speaker_layout layout)
630644 return (DWORD)layout;
631645}
632646
647+ void WASAPISource::DeviceTopologyTraversal (IPart *part, obs_properties_t *props, obs_data_t *settings,
648+ bool modifyBySettins)
649+ {
650+ if (part == nullptr ) {
651+ return ;
652+ }
653+ IPartsList *partList = nullptr ;
654+ part->EnumPartsOutgoing (&partList);
655+ if (partList == nullptr ) {
656+ return ;
657+ }
658+ IPart *pPartNext = NULL ;
659+ partList->GetPart (0 , &pPartNext);
660+ if (pPartNext == nullptr ) {
661+ partList->Release ();
662+ return ;
663+ }
664+ PartType partType;
665+ HRESULT hr = pPartNext->GetPartType (&partType);
666+ if (FAILED (hr)) {
667+ partList->Release ();
668+ pPartNext->Release ();
669+ return ;
670+ }
671+
672+ if (partType == PartType::Subunit) {
673+ IKsJackDescription **Jack = nullptr ;
674+ hr = pPartNext->Activate (CLSCTX_INPROC_SERVER, __uuidof (IKsJackDescription), (void **)&Jack);
675+ GUID SubType = {};
676+ pPartNext->GetSubType (&SubType);
677+ LPWSTR name = nullptr ;
678+ pPartNext->GetName (&name);
679+ if (SubType == KSNODETYPE_LOUDNESS) {
680+ // TODO
681+ } else if (SubType == KSNODETYPE_ADC) {
682+ // TODO
683+ } else if (SubType == KSNODETYPE_VOLUME) {
684+ IAudioVolumeLevel *audioVolumeLevel = nullptr ;
685+ pPartNext->Activate (CLSCTX_ALL, __uuidof (IAudioVolumeLevel), (void **)&audioVolumeLevel);
686+ if (audioVolumeLevel) {
687+ float pfMinLevelDB = 0 .0f ;
688+ float pfMaxLevelDB = 0 .0f ;
689+ float pfStepping = 0 .0f ;
690+ hr = audioVolumeLevel->GetLevelRange (0 , &pfMinLevelDB, &pfMaxLevelDB, &pfStepping);
691+ if (SUCCEEDED (hr)) {
692+ size_t len = wcslen (name);
693+ size_t size = os_wcs_to_utf8 (name, len, nullptr , 0 ) + 1 ;
694+ std::string utf8_name;
695+ utf8_name.resize (size);
696+ os_wcs_to_utf8 (name, len, &utf8_name[0 ], size);
697+ if (modifyBySettins) {
698+ float pfLevelDB =
699+ (float )obs_data_get_double (settings, utf8_name.c_str ());
700+ audioVolumeLevel->SetLevel (0 , pfLevelDB, nullptr );
701+ } else {
702+ float pfLevelDB = 0 .0f ;
703+ audioVolumeLevel->GetLevel (0 , &pfLevelDB);
704+ obs_property_t *p = obs_properties_add_float_slider (
705+ props, utf8_name.c_str (), utf8_name.c_str (), pfMinLevelDB,
706+ pfMaxLevelDB, pfStepping);
707+ obs_data_set_double (settings, utf8_name.c_str (), pfLevelDB);
708+ }
709+ }
710+ audioVolumeLevel->Release ();
711+ }
712+ } else if (SubType == KSNODETYPE_MUTE) {
713+ IAudioMute *audioMute = nullptr ;
714+ pPartNext->Activate (CLSCTX_ALL, __uuidof (IAudioMute), (void **)&audioMute);
715+ if (audioMute != nullptr ) {
716+ size_t len = wcslen (name);
717+ size_t size = os_wcs_to_utf8 (name, len, nullptr , 0 ) + 1 ;
718+ std::string utf8_name;
719+ utf8_name.resize (size);
720+ os_wcs_to_utf8 (name, len, &utf8_name[0 ], size);
721+ if (modifyBySettins) {
722+ bool mute = obs_data_get_bool (settings, utf8_name.c_str ());
723+ audioMute->SetMute (mute, nullptr );
724+ } else {
725+ obs_property_t *p =
726+ obs_properties_add_bool (props, utf8_name.c_str (), utf8_name.c_str ());
727+ BOOL isMute = FALSE ;
728+ audioMute->GetMute (&isMute);
729+ obs_data_set_bool (settings, utf8_name.c_str (), !!isMute);
730+ }
731+ audioMute->Release ();
732+ }
733+ } else if (SubType == KSNODETYPE_AGC) {
734+ IAudioAutoGainControl *audioAutoGainControl = nullptr ;
735+ pPartNext->Activate (CLSCTX_ALL, __uuidof (IAudioAutoGainControl),
736+ (void **)&audioAutoGainControl);
737+
738+ if (audioAutoGainControl != nullptr ) {
739+ size_t len = wcslen (name);
740+ size_t size = os_wcs_to_utf8 (name, len, nullptr , 0 ) + 1 ;
741+ std::string utf8_name;
742+ utf8_name.resize (size);
743+ os_wcs_to_utf8 (name, len, &utf8_name[0 ], size);
744+ if (modifyBySettins) {
745+ bool enable = obs_data_get_bool (settings, utf8_name.c_str ());
746+ audioAutoGainControl->SetEnabled (enable, nullptr );
747+ } else {
748+ obs_property_t *p =
749+ obs_properties_add_bool (props, utf8_name.c_str (), utf8_name.c_str ());
750+ BOOL isEnabled = FALSE ;
751+ audioAutoGainControl->GetEnabled (&isEnabled);
752+ obs_data_set_bool (settings, utf8_name.c_str (), !!isEnabled);
753+ }
754+ audioAutoGainControl->Release ();
755+ }
756+ } else if (SubType == KSNODETYPE_TONE) {
757+ // TODO
758+ }
759+
760+ if (!name) {
761+ CoTaskMemFree (name);
762+ }
763+
764+ DeviceTopologyTraversal (pPartNext, props, settings, modifyBySettins);
765+ pPartNext->Release ();
766+ }
767+
768+ if (partType == PartType::Connector) {
769+ IConnector *connect = nullptr ;
770+ hr = pPartNext->QueryInterface (__uuidof (IConnector), (void **)&connect);
771+ DeviceTopologyTraversal (connect, props, settings, modifyBySettins);
772+ }
773+ }
774+
775+ void WASAPISource::DeviceTopologyTraversal (IConnector *connect, obs_properties_t *props, obs_data_t *settings,
776+ bool modifyBySettins)
777+ {
778+ if (connect == nullptr ) {
779+ return ;
780+ }
781+
782+ BOOL IsConnected = FALSE ;
783+ HRESULT hr = connect->IsConnected (&IsConnected);
784+ if (FAILED (hr)) {
785+ return ;
786+ }
787+
788+ if (IsConnected == FALSE ) {
789+ ConnectorType connType;
790+ hr = connect->GetType (&connType);
791+
792+ if (FAILED (hr)) {
793+ return ;
794+ }
795+ if (connType == Software_IO) {
796+ return ;
797+ }
798+ }
799+
800+ IConnector *connectedTo = nullptr ;
801+ hr = connect->GetConnectedTo (&connectedTo);
802+ if (FAILED (hr) || connectedTo == nullptr ) {
803+ return ;
804+ }
805+
806+ IPart *Part = nullptr ;
807+
808+ connectedTo->QueryInterface (__uuidof (IPart), (void **)&Part);
809+ DeviceTopologyTraversal (Part, props, settings, modifyBySettins);
810+ if (Part != nullptr ) {
811+ Part->Release ();
812+ }
813+
814+ connectedTo->Release ();
815+ }
816+
817+ void WASAPISource::DeviceTopologyTraversal (IMMDevice *device, obs_properties_t *props, obs_data_t *settings,
818+ bool modifyBySettins)
819+ {
820+ if (device == nullptr ) {
821+ return ;
822+ }
823+ IDeviceTopology *pDeviceTopology = nullptr ;
824+ HRESULT hr = device->Activate (__uuidof (IDeviceTopology), CLSCTX_ALL, nullptr , (void **)&pDeviceTopology);
825+ if (FAILED (hr))
826+ return ;
827+ UINT nConnectorCount = 0 ;
828+ hr = pDeviceTopology->GetConnectorCount (&nConnectorCount);
829+ if (FAILED (hr))
830+ return ;
831+ for (UINT i = 0 ; i < nConnectorCount; i++) {
832+ IConnector *pConnector = nullptr ;
833+ pDeviceTopology->GetConnector (i, &pConnector);
834+ DeviceTopologyTraversal (pConnector, props, settings, modifyBySettins);
835+ if (pConnector) {
836+ pConnector->Release ();
837+ }
838+ }
839+ pDeviceTopology->Release ();
840+ }
841+
633842ComPtr<IAudioClient> WASAPISource::InitClient (IMMDevice *device, SourceType type, DWORD process_id,
634843 PFN_ActivateAudioInterfaceAsync activate_audio_interface_async,
635844 speaker_layout &speakers, audio_format &format, uint32_t &samples_per_sec)
@@ -707,6 +916,7 @@ ComPtr<IAudioClient> WASAPISource::InitClient(IMMDevice *device, SourceType type
707916 DWORD flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
708917 if (type != SourceType::Input)
709918 flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
919+
710920 res = client->Initialize (AUDCLNT_SHAREMODE_SHARED, flags, BUFFER_TIME_100NS, 0 , pFormat, nullptr );
711921 if (FAILED (res))
712922 throw HRError (" Failed to initialize audio client" , res);
@@ -1454,8 +1664,29 @@ static bool UpdateWASAPIMethod(obs_properties_t *props, obs_property_t *, obs_da
14541664 return true ;
14551665}
14561666
1457- static obs_properties_t *GetWASAPIPropertiesInput ( void * )
1667+ static bool DeviceSelectionChanged ( obs_properties_t *props, obs_property_t *p, obs_data_t *settings )
14581668{
1669+ string id = obs_data_get_string (settings, OPT_DEVICE_ID);
1670+ obs_property_t *property = obs_properties_first (props);
1671+
1672+ while (property) {
1673+ std::string name = obs_property_name (property);
1674+ obs_property_next (&property);
1675+ if (name != OPT_DEVICE_ID && name != OPT_USE_DEVICE_TIMING) {
1676+ obs_properties_remove_by_name (props, name.c_str ());
1677+ }
1678+ }
1679+ IMMDevice *device = GetMMDeviceById (id == " default" ? true : false , id, true );
1680+ WASAPISource::DeviceTopologyTraversal (device, props, settings, false );
1681+ if (device) {
1682+ device->Release ();
1683+ }
1684+ return true ;
1685+ }
1686+
1687+ static obs_properties_t *GetWASAPIPropertiesInput (void *data)
1688+ {
1689+ WASAPISource *source = (WASAPISource *)data;
14591690 obs_properties_t *props = obs_properties_create ();
14601691 vector<AudioDeviceInfo> devices;
14611692
@@ -1473,7 +1704,7 @@ static obs_properties_t *GetWASAPIPropertiesInput(void *)
14731704 }
14741705
14751706 obs_properties_add_bool (props, OPT_USE_DEVICE_TIMING, obs_module_text (" UseDeviceTiming" ));
1476-
1707+ obs_property_set_modified_callback (device_prop, DeviceSelectionChanged);
14771708 return props;
14781709}
14791710
0 commit comments