dohanin

Set Default Playback Device to next one

2 posts in this topic

I have been searching for a while for a script to toggle the default sound device, so that i can easily switch between Speakers and Headphones.  Some added a tray icon and keep running on background, some will call up the control panel briefly, while some need to call nircmd externally.

I ended up gathered some cool scripts (sorry, i failed to recall the original source) and modified for my own taste.  It will get the current default playback device, and then set to the next one.  It will print an icon on the screen, and then will just exit.  Good to use with shortcut key on keyboards, etc.

Feel free to try.

I was able to retrieve the actual icons info but found it not satisfactory to draw the icon using GUI.  So, I simply use SplashText with Webdings font.  :)

;==========================================================
; Set Default Playback Device
; to the next available one
;==========================================================

#NoTrayIcon

#include <WinAPI.au3>
#include <Constants.au3>

Global Const $sCLSID_MMDeviceEnumerator = "{BCDE0395-E52F-467C-8E3D-C4579291692E}"
Global Const $sIID_IMMDeviceEnumerator = "{A95664D2-9614-4F35-A746-DE8DB63617E6}"

Global Const $tagIMMDeviceEnumerator = "EnumAudioEndpoints hresult(dword;dword;ptr*);" & _
"GetDefaultAudioEndpoint hresult(dword;dword;ptr*);" & _
"GetDevice hresult(wstr;ptr*);" & _
"RegisterEndpointNotificationCallback hresult(ptr);" & _
"UnregisterEndpointNotificationCallback hresult(ptr);"

Global Const $sCLSID_CPolicyConfigClient = "{870af99c-171d-4f9e-af0d-e63df40c2bc9}"
Global Const $sIID_IPolicyConfig = "{f8679f50-850a-41cf-9c72-430f290290c8}"

Global Const $tagIPolicyConfig = "GetMixFormat hresult(wstr;ptr*);" & _
"GetDeviceFormat hresult(wstr;int;ptr*);" & _
"ResetDeviceFormat hresult(wstr);" & _
"SetDeviceFormat hresult(wstr;ptr;ptr);" & _
"GetProcessingPeriod hresult(wstr;int;int64*;int64*);" & _
"SetProcessingPeriod hresult(wstr;int64*);" & _
"GetShareMode hresult(wstr;ptr);" & _
"SetShareMode hresult(wstr;ptr);" & _
"GetPropertyValue hresult(wstr;struct;variant*);" & _
"SetPropertyValue hresult(wstr;struct;variant*);" & _
"SetDefaultEndpoint hresult(wstr;int);" & _
"SetEndpointVisibility hresult(wstr;int);"

Global Const $IID_IMMDevice = "{D666063F-1587-4E43-81F1-B948E807363F}"
Global Const $tagIMMDevice = "Activate hresult(ptr;dword;variant*;ptr*);" & _
"OpenPropertyStore hresult(dword;ptr*);" & _
"GetId hresult(ptr*);" & _
"GetState hresult(dword*);"

Global Const $IID_IMMDeviceCollection = "{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}"
Global Const $tagIMMDeviceCollection = "GetCount hresult(uint*);" & _
"Item hresult(uint;ptr*)"

Global Const $IID_IPropertyStore = "{886d8eeb-8cf2-4446-8d02-cdba1dbdcf99}"
Global Const $tagIPropertyStore = "GetCount hresult(dword*);" & _
"GetAt hresult(dword;ptr*);" & _
"GetValue hresult(ptr;variant*);" & _
"SetValue hresult(ptr;variant);" & _
"Commit hresult();"

Global Const $sPKEY_Device_FriendlyName = "{a45c254e-df1c-4efd-8020-67d146a850e0} 2"
Global Const $sPKEY_Device_Icon = "{259abffc-50a7-47ce-af08-68c9a7d73366} 12"

Global Const $STGM_READ = 0
Global Const $DEVICE_STATE_ACTIVE = 0x00000001

Global Const $S_OK = 0
Global Const $E_INVALIDARG = 0x80070057

Global Const $eRender = 0
Global Const $eCapture = 1
Global Const $eAll = 2
Global Const $EDataFlow_enum_count = 3

Global Const $eConsole = 0
Global Const $eMultimedia = 1
Global Const $eCommunications = 2
Global Const $ERole_enum_count = 3
;==========================================================

If @OSVersion <> "WIN_7" Then Exit ; God knows what $tagIPolicyConfig is for other systems

; First generate array filled with devices data
Global $aArray = _EnumerateDevices()

; FY: Get default device id
Global $sDefID = _GetDefaultDevice()

; Set next device as default device
$nDev = UBound($aArray, 2) ; no. of devices

For $i = 0 to $nDev -1
   If $aArray[0][$i]=$sDefID Then $sDev=$i+1
Next
If $sDev >= $nDev Then $sDev = 0
_SwitchToDevice($aArray[0][$sDev])

$logo = "U"
if $aArray[1][$sDev] = "Headphones" Then $logo=chr(178) 
   
$xTT = @DesktopWidth/2-50
$yTT = @DesktopHeight/2-70
ToolTip(" ", $xTT, $yTT, $aArray[1][$sDev],0) 
SplashTextOn("",$logo, 100, 100, -1, -1, 33, "Webdings", 80) 

Sleep(2000) 
SplashOff() 

Exit
; THE END

Func _GetDefaultDevice()
; Error monitoring
Local $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc")
    #forceref $oErrorHandler
; MMDeviceEnumerator
Local $oMMDeviceEnumerator = ObjCreateInterface($sCLSID_MMDeviceEnumerator, $sIID_IMMDeviceEnumerator, $tagIMMDeviceEnumerator)
Local $pDefaultDevice
$oMMDeviceEnumerator.GetDefaultAudioEndpoint($eRender, $eConsole, $pDefaultDevice)
Local $oDefaultDevice = ObjCreateInterface($pDefaultDevice, $IID_IMMDevice, $tagIMMDevice)
Local $pDefID, $sDefId
$oDefaultDevice.GetId($pDefID)
$sDefId = DllStructGetData(DllStructCreate("wchar ID[" & _WinAPI_PtrStringLenW($pDefID) & "]", $pDefID), "ID")
_WinAPI_CoTaskMemFree($pDefID)
Return $sDefID
EndFunc ;==>_GetDefaultDevice

Func _EnumerateDevices()
; Error monitoring
Local $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc")
    #forceref $oErrorHandler

    ; Creae MMDeviceEnumerator object
Local $oEnumerator = ObjCreateInterface($sCLSID_MMDeviceEnumerator, $sIID_IMMDeviceEnumerator, $tagIMMDeviceEnumerator)

    ; Get collection out of it
Local $pCollection
$oEnumerator.EnumAudioEndpoints($eRender, $DEVICE_STATE_ACTIVE, $pCollection)
    ; Turn it into object
Local $oCollection = ObjCreateInterface($pCollection, $IID_IMMDeviceCollection, $tagIMMDeviceCollection)

    ; Check the number of devices in collection
Local $iCount
$oCollection.GetCount($iCount)

; Array for out
Local $aArray[2][$iCount]

Local $pEndpoint, $oEndpoint
Local $pID, $sId, $pProps, $oProps
Local $sName
Local $tPKEY_Device_FriendlyName = _WinAPI_PKEYFromString($sPKEY_Device_FriendlyName)
Local $tPKEY_Device_Icon = _WinAPI_PKEYFromString($sPKEY_Device_Icon)

    ; Actual enumeration
For $i = 0 To $iCount - 1
; Item at "i" index is audio endpoint device
$oCollection.Item($i, $pEndpoint)
$oEndpoint = ObjCreateInterface($pEndpoint, $IID_IMMDevice, $tagIMMDevice)

; Collect ID
$oEndpoint.GetId($pID)
$sId = DllStructGetData(DllStructCreate("wchar ID[" & _WinAPI_PtrStringLenW($pID) & "]", $pID), "ID")
_WinAPI_CoTaskMemFree($pID)
$aArray[0][$i] = $sId

; PropertyStore stores properties, lol
$oEndpoint.OpenPropertyStore($STGM_READ, $pProps)
$oProps = ObjCreateInterface($pProps, $IID_IPropertyStore, $tagIPropertyStore)

; Collect FriendlyName property
$sName = 0
$sIcon = 0
$oProps.GetValue(DllStructGetPtr($tPKEY_Device_FriendlyName), $sName)
$oProps.GetValue(DllStructGetPtr($tPKEY_Device_Icon), $sIcon)

$aArray[1][$i] = $sName
Next

Return $aArray
EndFunc ;==>_EnumerateDevices

Func _SwitchToDevice($sId)
Local $oPolicyConfig = ObjCreateInterface($sCLSID_CPolicyConfigClient, $sIID_IPolicyConfig, $tagIPolicyConfig)

Local $hResult
$hResult = $oPolicyConfig.SetDefaultEndpoint($sId, $eConsole)
If $hResult = $S_OK Then $hResult = $oPolicyConfig.SetDefaultEndpoint($sId, $eCommunications)

Return $hResult = $S_OK
EndFunc ;==>_SwitchToDevice


Func _WinAPI_PKEYFromString($sPKEY, $pID = Default)
Local $tPKEY = DllStructCreate("byte GUID[16]; dword PID;")
DllCall("propsys.dll", "long", "PSPropertyKeyFromString", "wstr", $sPKEY, "ptr", DllStructGetPtr($tPKEY))
If @error Then ; alternative for unsupported systems
Local $tGUID = _WinAPI_GUIDFromString($sPKEY)
If Not @error Then
DllStructSetData($tPKEY, "GUID", DllStructGetData(DllStructCreate("byte Data[16]", DllStructGetPtr($tGUID)), 1))
EndIf
DllStructSetData($tPKEY, "PID", Number(StringRegExpReplace($sPKEY, ".*?}", "")))
EndIf
If $pID <> Default Then DllStructSetData($tPKEY, "PID", $pID)
Return $tPKEY
EndFunc ;==>_WinAPI_PKEYFromString

Func _WinAPI_PtrStringLenW($pString)
Local $aCall = DllCall("kernel32.dll", "dword", "lstrlenW", "ptr", $pString)
If @error Then Return SetError(1, 0, 0)
Return $aCall[0]
EndFunc ;==>_WinAPI_PtrStringLenW

Func _WinAPI_CoTaskMemFree($pMem)
DllCall("ole32.dll", "none", "CoTaskMemFree", "ptr", $pMem)
If @error Then Return SetError(1, 0, False)
Return True
EndFunc ;==>_WinAPI_CoTaskMemFree

Func _ErrFunc($oError)
ConsoleWrite("COM Error, ScriptLine(" & $oError.scriptline & ") : Number 0x" & Hex($oError.number, 8) & " - " & $oError.windescription & @CRLF)
EndFunc ;==>_ErrFunc
1 person likes this

Share this post


Link to post
Share on other sites



Thank you, dohanin, this script helps me a lot with my two devices: optical and speakers. Works great on Windows 8.1.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now