NK2Tray icon indicating copy to clipboard operation
NK2Tray copied to clipboard

[feature] Change default audio output device with hotkey

Open clex25 opened this issue 4 years ago • 5 comments

Is your feature request related to a problem? Please describe. I have multiple audio devices including headphones and speakers. I want to be able to quickly switch between them (ie, going from gaming to music listening)

Describe the solution you'd like Assign a button to toggle between default audio devices. Stretch goal: make the button configurable (user controllable).

Describe alternatives you've considered Could also assign a fader to switch between devices, but I assume many people will not have that many audio devices. Maybe from 1-5 is average, so a toggle button is faster.

Additional context Small discussion in discord

I am planning on investigating/working on this. I am a windows development noob but attempting this as a first project.

clex25 avatar May 17 '20 19:05 clex25

Some suggestions from AtticJack

  • A C# library called NAudio (https://github.com/naudio/NAudio) is used for a lot of talking to Windows for details about devices and mixer entries. I'm assuming somewhere in their API you can set a default device.
  • Once you've found how, whack that logic somewhere (for current master I think that'd be best placed in AudioDevice.cs; after the refactor in PR #67, I think AudioDeviceWatcher.cs)
  • I think Button.cs holds some definitions for what buttons are able to do. You'd have to add in the trigger for the code you whacked in above

clex25 avatar May 17 '20 19:05 clex25

After looking into this further, this issue is much more complex than it would seem at first glance.

There are several challenges here:

  1. NAudio does not provide any API for setting the default audio device, as far as I can tell.
  2. Windows does not offer a documented API for setting the default audio device. This article has a great general overview of how windows sets the default audio device.
  3. Even if you use the undocumented COM interface IPolicyConfig, you still have to take care of the following issues, for a comprehensive solution:
    1. setting GUID based on windows version [no one size fits all solution]
    2. If a process is already running, it will need to be updated to use the new default device (new processes launched will use the new default)
    3. There are different windows defaults for console, audio and multimedia devices. This could be simplified by setting the default the same for both, but not all users may want to change both defaults simultaneously (increasing complexity)

The good news is that the quite-popular SoundSwitch project has already taken care of this and it is open source GPL-2.0. However, it is beyond my skills to integrate this level of code into the existing project, nor am I certain if the maintainers want to add this level of complexity (although it seems to be a nice extensible class structure with well handled errors and other good coding practices). I have downloaded and tested this application and it works flawlessly. It also includes a ton of other features, such as letting the user select the device(s) they wish to toggle between in a GUI.

I also found another alternative project audio-switcher, which uses the same IPolicyConfig undocumented API, but this time with much lower complexity (doesn't add any new classes except audio device role). However, it seems like it could be less robust. I have not tested this one since there is no release, and the project seems a little stale.

At this point, before moving forward, I'm looking for thoughts/opinions on what direction to take, or alternative suggestions.

clex25 avatar May 24 '20 21:05 clex25

Hi,

AtticJack did some work on Issue #33 and provided me with a beta build, so I build a little batch script to change my default sound devices, the only thing you need is nircmd (https://www.nirsoft.net/utils/nircmd.html)

@ECHO OFF
if exist C:\NK2\toggle.txt goto :2

C:\NK2\NIRCMD\NIRCMDC setdefaultsounddevice “Speakers” 2
C:\NK2\NIRCMD\NIRCMDC setdefaultsounddevice “Speakers” 1
echo toggle>>C:\NK2\toggle.txt

exit
:2
C:\NK2\NIRCMD\NIRCMDC setdefaultsounddevice “Headset” 2
C:\NK2\NIRCMD\NIRCMDC setdefaultsounddevice “Headset” 1
del C:\NK2\toggle.txt /q

exit

and a little explanation: With the nircmd command setdefaultsounddevice you can change the default device, the parameter 1 (or 2) is there to determine the audio device (1) and the communication device (2), if you chose to use your default device in discord for example it uses your communication device, spotify for example uses the "normal" audio device. If you don't want to change the communication device you can delete the rows with the parameter 2. IMPORTANT: the name of your sound devices in windows have to match the name in the script (here "Headset" and "Speakers"). You can change the names via the windows control panel (the old and good one) => sound, there you'll find a list of your devices and you can rename them.

You'll also find this here: https://github.com/ho0ber/NK2Tray/issues/33#issuecomment-633471239

Haze1707 avatar May 25 '20 09:05 Haze1707

Fantastic work on this, @clex25, and thanks loads for the work-around with the latest alpha build, @Haze1707. Fantastic to see people pooling their efforts to make this better for everyone!

Great find on the audio device watching info, @clex25. It's a shame and an oddity that Windows seems to hide the functionality away from developers so it's difficult to leverage it. I wonder why!

In fact, the comment inside one of the functions you linked to in davkean/audio-switcher explains it well:

// BADNESS: The following code uses undocumented interfaces provided by the Audio SDK. This is completely
// unsupported, and should be used for amusement purposes only. This is *extremely likely* to be broken 
// in future updates and/or versions of Windows. If Larry Osterman was dead, he would be rolling over 
// in his grave if he knew you were using this for nefarious purposes.

My inclination when it comes to things like this is to leave applications to do what they do best and not bloat one application with a half-baked solution when another does the job (and only that job) exceedingly well. In this situation, Belphemur/SoundSwitch does its job exceedingly well and could, perhaps, even be sensibly used alongside NK2Tray as they should both be able to be used in harmony.

Ultimately, this is a decision for @ho0ber, the creator of this application, as to whether it's something we should continue investigating. To be clear, if this were a few lines of code that we could whack in to a "custom button action" then it wouldn't be a concern, but it's obviously a large enough problem that supporting its use may take up a disproportionate amount of time in the future.

Given the effort (and future support) required for this, I'd advise against implementing it. To be able to support the functionality of switching default audio devices while ensuring that NK2Tray stays in its "doing one job well" domain, we could:

  1. Use a script utilising nircmd (as @Haze1707 has implemented) triggered by a MIDI button press. This isn't available in the latest release yet, but I can provide a build for this and hopefully we can push it through as soon as possible!
  2. Use Belphemur/SoundSwitch with a keyboard shortcut triggered by a MIDI button press. @ReapersDragon has been doing some work on this I believe, and the custom button actions utilised in the point above would enable this nicely.

I think those are more realistic solutions for this, but we'll wait for @ho0ber's word to see what he thinks is best here. Sorry for inflicting these essays on you, @ho0ber!

jpwilliams avatar May 25 '20 11:05 jpwilliams

I agree with needing input from @ho0ber on this as it is more of a philosophical issue as to what the program ought to do, as well as a big design change if we moved further in this direction.

If we did move forward, though, I would caution against using the nircmd route (specifically for the audio device switching). As mentioned, it requires the user to configure the audio device precisely by name (including formatting changes), rather than being able to look up devices and toggle between them. It also uses the extended device name rather than the title (eg. "audioengine hd2" vs "Speakers"). Also, audio devices will change names when moved to different usb ports (for example, my speakers go from something like "AudioEngine 2+" to "AudioEngine 2+ (1)" if I move it around). This makes it a little frustrating to keep up to date (for example, this would require me to update my config daily as I move headset from one computer to another and thus change the name when it is replugged).

Plus, using nircmd would introduce a static dependency. I wonder if the nircmd author would share info on how the sound switching is done, under the hood?

Soundswitch is open source, allowing us to integrate as much (or little) as we choose, and is actively maintained.

clex25 avatar May 26 '20 21:05 clex25