Skip to content

Instantly share code, notes, and snippets.

@getify
Last active June 18, 2020 15:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save getify/34b69f81430ff6e87e3deac27fd5ad54 to your computer and use it in GitHub Desktop.
Save getify/34b69f81430ff6e87e3deac27fd5ad54 to your computer and use it in GitHub Desktop.
AutoHotKeys (AHK) script for toggling my mic's muting Demo: https://gyazo.com/07ac569f5e5225f7cd32c34701ca00db Icons: https://jumpshare.com/b/5xatZXpaIdIV1BaP9aET
#include VA.ahk
SwitchMicDevice(micName) {
return VA_SetDefaultEndpoint(micName,1)
}
GetMicDeviceID(micName) {
Loop
{
deviceIDPtr := GetMicDevice(A_Index)
if (deviceIDPtr = 0) {
return -1
}
deviceName:= VA_GetDeviceName(deviceIDPtr)
ObjRelease(devideIDPtr)
if (deviceName = micName) {
return A_Index
}
}
return -1
}
; adapted from: https://autohotkey.com/board/topic/21984-vista-audio-control-functions/
; REFERENCE: https://www.autohotkey.com/boards/viewtopic.php?style=1&t=70346
GetMicDevice(device_desc="playback")
{
static CLSID_MMDeviceEnumerator := "{BCDE0395-E52F-467C-8E3D-C4579291692E}"
, IID_IMMDeviceEnumerator := "{A95664D2-9614-4F35-A746-DE8DB63617E6}"
if !(deviceEnumerator := ComObjCreate(CLSID_MMDeviceEnumerator, IID_IMMDeviceEnumerator))
return 0
device := 0
if VA_IMMDeviceEnumerator_GetDevice(deviceEnumerator, device_desc, device) = 0
goto GetMicDevice_Return
if device_desc is integer
{
m2 := device_desc
if m2 >= 4096 ; Probably a device pointer, passed here indirectly via VA_GetAudioMeter or such.
{
ObjAddRef(device := m2)
goto GetMicDevice_Return
}
}
else
RegExMatch(device_desc, "(.*?)\s*(?::(\d+))?$", m)
if m1 in playback,p
m1 := "", flow := 0 ; eRender
else if m1 in capture,c
m1 := "", flow := 1 ; eCapture
else if (m1 . m2) = "" ; no name or number specified
m1 := "", flow := 0 ; eRender (default)
else
flow := 2 ; eAll
if (m1 . m2) = "" ; no name or number (maybe "playback" or "capture")
{
VA_IMMDeviceEnumerator_GetDefaultAudioEndpoint(deviceEnumerator, flow, 0, device)
goto GetMicDevice_Return
}
VA_IMMDeviceEnumerator_EnumAudioEndpoints(deviceEnumerator, 2, 9, devices)
if m1 =
{
VA_IMMDeviceCollection_Item(devices, m2-1, device)
goto GetMicDevice_Return
}
VA_IMMDeviceCollection_GetCount(devices, count)
index := 0
Loop % count
if VA_IMMDeviceCollection_Item(devices, A_Index-1, device) = 0
if InStr(VA_GetDeviceName(device), m1) && (m2 = "" || ++index = m2)
goto GetMicDevice_Return
else
ObjRelease(device), device:=0
GetMicDevice_Return:
ObjRelease(deviceEnumerator)
if devices
ObjRelease(devices)
return device ; may be 0
}
#include MicDevice.ahk
#SingleInstance force
; **************************************************
; You need to configure the `mics` array below.
; open your windows control panel and find the
; Sound "Input" devices list. Whichever of those
; that you want to control with the app, you need
; to add entries in the array below, as shown with
; the "{{ mic name 1 }}" example strings, where your
; text is *IDENTICAL* to what shows in your control
; panel (not with the leading/trailing spaces or
; the "{{ }}"). Leave the `deviceID` as -1, since
; that value will be auto-detected by the app. Leave
; the `enabled` as 0, as that will be auto-detected.
; Set the `label` to some friendly label that you
; you want to show in the taskbar popup menu.
;
; For example, my first entry says:
; { "name": "Blue Yeti (Yeti Nano)", "label": "Yeti", "deviceID": -1, "enabled": 0 }
;
; You can have as many entries as you want want,
; and you can list them in any order you want. If
; you only specify one entry, then the app will still
; work, but you won't be able to switch devices.
;
; Note: AHK syntax requires this array/object list
; to be all on one line, which sucks for readability.
; **************************************************
mics := [ { "name": " {{ mic name 1 }} ", "label": "My Favorite Mic", "deviceID": -1, "enabled": 0 }, { "name": " {{ mic name 2 }} ", "label": "My Backup Mic", "deviceID": -1, "enabled": 0 } ]
currentMic := -1
InitMicDeviceIDs()
SetupMenu()
UpdateTrayIcon()
; my chosen hotkey is win+shift+m
#+m::
ToggleMic(mics[currentMic].deviceID)
ShowMuteTooltip(mics[currentMic].deviceID)
return
; cycle through mic inputs
#+<::
CycleMic(-1)
return
#+>::
CycleMic(1)
return
; ***************************************
InitMicDeviceIDs() {
global mics
global currentMic
Loop % (mics.Length())
{
deviceID := GetMicDeviceID(mics[A_Index].name)
if (deviceID != -1) {
mics[A_Index].deviceID := deviceID
; check if mic is actually able to be selected
mics[A_Index].enabled := SwitchMicDevice(mics[A_Index].name)
; default mic to first enabled one found
if (mics[A_Index].enabled and currentMic == -1) {
currentMic := A_Index
}
}
}
; no enabled mics were found?
if (currentMic == -1) {
ErrorAndQuit("No microphones found")
}
else {
; switch back to the default current mic
SwitchMicDevice(mics[currentMic].name)
}
}
SetupMenu() {
Menu, Tray, UseErrorLevel
Menu, Tray, NoMainWindow
; remove all standard menu items
Menu, Tray, NoStandard
; add the toggle-mute menu item
Menu, Tray, Add, Mute Mic, ToggleCurrentMic
SetupMicrophonesSubMenu()
UpdateMicrophonesSubMenu()
; add a refresh/reload menu item
Menu, Tray, Add, Refresh Mic Status, ReloadApp
; add a quit menu item
Menu, Tray, Add, Quit, Quit
; set the toggle-mute menu item to be the "default" menu item
Menu, Tray, Default, Mute Mic
; set single clicks on the tray icon to activate the default menu item
Menu, Tray, Click, 1
}
SetupMicrophonesSubMenu() {
global mics
Loop, % (mics.Length())
{
Menu, Microphones, Add, % (mics[A_Index].label), SelectMic
}
Menu, Tray, Add, Microphones, :Microphones
}
UpdateMicrophonesSubMenu() {
global mics
global currentMic
Loop, % (mics.Length())
{
Menu, Microphones, % (A_Index = currentMic ? "Check" : "Uncheck"), % (mics[A_Index].label)
Menu, Microphones, % (mics[A_Index].enabled ? "Enable" : "Disable"), % (mics[A_Index].label)
}
}
SelectMic(micLabel,selectedMic) {
global mics
global currentMic
micSwitched := SwitchMicDevice(mics[selectedMic].name)
if (!micSwitched) {
msgBox 16, Error!, Mic input switch failed
return 0
}
; switch the mic
if (selectedMic != currentMic) {
currentMic := selectedMic
UpdateMicrophonesSubMenu()
UpdateTrayIcon()
}
return 1
}
ToggleCurrentMic() {
global mics
global currentMic
ToggleMic(mics[currentMic].deviceID)
}
ToggleMic(deviceID) {
ToggleMuteStatus(deviceID)
UpdateTrayIcon()
}
CycleMic(cycleDir) {
global mics
global currentMic
numMics := mics.Length()
nextIdx := currentMic
; find next/previous enabled mic
Loop, % (numMics)
{
nextIdx := Mod((nextIdx + cycleDir + numMics - 1),numMics) + 1
if (mics[nextIdx].enabled) {
break
}
}
micSwitched := SelectMic(mics[currentMic].label,nextIdx)
if (micSwitched) {
ShowMicTooltip(nextIdx)
}
}
UpdateTrayIcon() {
global mics
global currentMic
label := mics[currentMic].label
deviceID := mics[currentMic].deviceID
isMuted := checkMuteStatus(deviceID)
; update tray icon's tooltip text
Menu, Tray, Tip, % (label ": " (isMuted ? "Muted" : "Unmuted"))
; toggle tray icon
Menu, Tray, Icon, ToggleMic.icl, % (isMuted ? 1 : 2), 1
; toggle menu item label text
prevLabel := % (isMuted ? "Mute Mic" : "Unmute Mic")
newLabel := % (isMuted ? "Unmute Mic" : "Mute Mic")
Menu, Tray, Rename, %prevLabel%, %newLabel%
}
ShowMuteTooltip(deviceID) {
isFullScreen := CheckFullScreenActive()
isMuted := checkMuteStatus(deviceID)
; only show tooltip while in full-screen and when unmuting
if (isFullScreen and !isMuted) {
ToolTip, Mic Unmuted!
; hide the tooltip (once) 250ms from now
SetTimer, HideToolTip, -250
}
else {
; cancel previous timer in case it's still running
SetTimer,, Off
HideToolTip()
}
}
ShowMicTooltip(whichMic) {
global mics
isMuted := checkMuteStatus(mics[whichMic].deviceID)
; cancel previous timer in case it's still running
SetTimer,, Off
HideToolTip()
ToolTip, % (mics[whichMic].label ": " (isMuted ? "Muted" : "Unmuted"))
; hide the tooltip (once) 750ms from now
SetTimer, HideToolTip, -750
}
HideToolTip() {
ToolTip
}
ToggleMuteStatus(deviceID) {
SoundSet, +1,, MUTE, %deviceID%
}
CheckMuteStatus(deviceID) {
; pull out current mute state ("On" or "Off")
SoundGet, micMuteState,, MUTE, %deviceID%
return (micMuteState = "On")
}
; Adapted from: https://autohotkey.com/board/topic/32659-how-to-detect-if-ingame-or-windows/://autohotkey.com/board/topic/32659-how-to-detect-if-ingame-or-windows/page-2?&#entry208939
CheckFullScreenActive() {
wingetpos,,,w,h,A
wwwh=%w%%h%
swsh=%a_screenwidth%%a_screenheight%
WinGet, Style, Style, A
if !(Style & 0xC00000) {
if(wwwh = swsh) {
return true
}
}
return false
}
ErrorAndQuit(errMsg) {
msgBox 16, Error!, %errMsg%
Quit()
}
ReloadApp() {
Reload
}
Quit() {
ExitApp
}
; get VA.ahk from here: https://autohotkey.com/board/topic/21984-vista-audio-control-functions/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment