0

I am trying to make a hotkey using AutoHotKey v2 that toggles through the audio output options using nircmd setdefaultsound. The following program is what I have: it does not give errors, and it gives the MsgBox but it always says "Headphones" and it does not actually change the device.

#Requires AutoHotkey v2.0
;Everything until the first return autoruns
device := "Speakers"
return

; Audio Output Device Toggle
#+a::
{
if (device = "Speakers") 
    device := "Headphones"
else if (device = "Headphones") 
    device := "Speakers"
MsgBox Format("Selected device: {}", device)
run "nircmd.exe setdefaultsounddevice %device%"
}

What is causing this not to work?

Max
  • 99
  • 6

2 Answers2

1

Your variable device is not in that hotkey's scope. You'd have to explicitly state that you're using a variable from the global scope. Or in this case what you really want is a static variable in the hotkey's scope.(about local and global)

And also, you're trying to use the legacy AHK syntax for referring to a variable. And even while inside a string. There's absolutely no way that could work. Here's a fixed script, although I can't comment on what you're doing with nircmd. I don't use it, so I won't know if it will work.

#Requires AutoHotkey v2.0
;Everything until the first return autoruns
;device := "Speakers"
return

; Audio Output Device Toggle
#+a::
{
    ;global device ;to refer from global scope
    static device := "Speakers" ;use a static variable

    if (device = "Speakers")
        device := "Headphones"
    else if (device = "Headphones")
        device := "Speakers"

    MsgBox("Selected device: " device)
    Run("nircmd.exe setdefaultsounddevice " device)
}
0x464e
  • 5,948
  • 1
  • 12
  • 17
  • This works! Thank you so much! :D Only change I had to make was to give the full/path/to/nircmd.exe – Max Mar 05 '23 at 14:38
0

For one thing, you are always starting out by setting device to "Speakers" and then checking that it is in the If statement and thus setting it to "Headphones" wherein the MsgBox says so.

You never get to the else if because the device never starts out as Headphones.

And even after you moved that line to above the hotkey in your edits, the problem is, if you change the device without the hot key the variable will no longer be correct.

Maybe replace with some command that would return the actual "in-use" device? I use VA.ahk at https://www.autohotkey.com/board/topic/21984-vista-audio-control-functions/ for this kind of thing.

You could also do a quick python code to get the info (as referenced here Get default audio/video device):

import winreg as wr
import platform
from contextlib import suppress

def is_os_64bit():
    return platform.machine().endswith('64')

def get_default_output_device():
    """ returns the PyAudio formatted name of the default output device """
    import winreg as wr
    read_access = wr.KEY_READ | wr.KEY_WOW64_64KEY if is_os_64bit() else wr.KEY_READ
    audio_path = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Render'
    audio_key = wr.OpenKeyEx(wr.HKEY_LOCAL_MACHINE, audio_path, 0, read_access)
    num_devices = wr.QueryInfoKey(audio_key)[0]
    active_last_used, active_device_name = -1, None
    for i in range(num_devices):
        device_key_path = f'{audio_path}\\{wr.EnumKey(audio_key, i)}'
        device_key = wr.OpenKeyEx(wr.HKEY_LOCAL_MACHINE, device_key_path, 0, read_access)
        if wr.QueryValueEx(device_key, 'DeviceState')[0] == 1:  # if enabled
            properties_path = f'{device_key_path}\\Properties'
            properties = wr.OpenKeyEx(wr.HKEY_LOCAL_MACHINE, properties_path, 0, read_access)
            device_name = wr.QueryValueEx(properties, '{b3f8fa53-0004-438e-9003-51a46e139bfc},6')[0]
            device_type = wr.QueryValueEx(properties, '{a45c254e-df1c-4efd-8020-67d146a850e0},2')[0]
            pa_name = f'{device_type} ({device_name})'  # name shown in PyAudio
            with suppress(FileNotFoundError):
                last_used = wr.QueryValueEx(device_key, 'Level:0')[0]
                if last_used > active_last_used:  # the bigger the number, the more recent it was used
                    active_last_used = last_used
                    active_device_name = pa_name
    return active_device_name
# print (get_default_output_device())  # uncomment to test in console

hth

PGilm
  • 2,262
  • 1
  • 14
  • 26
  • I changed the code a tiny bit, right now AHK does not need to return the in-use device, it just get it from `device`. ([Credit](https://stackoverflow.com/a/34501729/8739121)) But when I use the hotkey, it gives: `Error: This variable has not been assigned a value. Specifically: local device (same name as a global)` and points to line 011 – Max Mar 03 '23 at 16:35
  • Problem is, if you change the device without the hot key the variable will no longer be correct? Plus, maybe name it as Global and maybe put in an `end if` to close out the `else if` or better yet, since it is just two, you can use the ternary operator? Or even treat it as boolean `speaker` and `!speaker`. But better just see my edited answer. – PGilm Mar 03 '23 at 16:50
  • Maybe try making marking `device` as `GLOBAL` then before the first if `if` add a line setting `x = device` and then change the expressions to `x = "Speakers"` and so forth? – PGilm Mar 03 '23 at 18:55
  • Thank you for your effort! I really wanted to not have to download yet another application to make this work (AHK, nircmd and then _another_ app), and 0x464e now gave a working AHK script, so I accepted his as the answer. But thank you for your effort, greatly appreciated! :) – Max Mar 05 '23 at 14:46