The issue started from there. I did not want to have to plug and unplug the headset every time I wanted to use it, nor did I want to go to the Control Panel and manually change the default playback and recording devices.
Therefore, I started searching online to find a method. It appears that Microsoft does not support this in any way in Windows Vista and 7. I did discover that one of the program managers for Windows might pass this issue along to the developers for consideration, so we may not be out of luck in the next version.
The searches ended up revealing a number of methods to perform the change. I took a number of the suggestions, code snippets, helper DLLs, and came up with a solution of my own. All the source websites are credited in the list below and I am very grateful for their detailed groundwork.
The main points are as follows:
- Enumerate a list of all rendering (output) and capture (input) audio devices in the system via the registry for easiest access
- Use the unsupported Microsoft interface IPolicyConfig to actually make the switch
- Develop a command-line application that can perform all the default audio device switching with a minimum of intervention and callable from AutoHotkey
I continued digging and eventually found an excellent set of references in this Microsoft forum post. In this post, I am deeply indebted to ghoster_e, albain, and EreTIk. albain got me started with his excellent reference on reading the writing the necessary registry entries to get and set the default audio device. However, this method did not notify any running applications that the default changed, necessitating restarting that application for it to recognize the change.
ghoster_e had some great information on the unsupported IPolicyConfig COM interface, along with some snippets of code. EreTIk also maintains a very useful website that includes this full C++ header file for inclusion in a C++ project.
This almost got me to where I needed to be. However, as I was writing this entire project in C#, I did not want to have to make the necessary COM conversions to use those IPolicyConfig interfaces. I searched some more and found Ray Molenkamp's CoreAudio project. This was missing the IPolicyConfig interfaces, but was almost feature-complete.
One more search landed me at MixerProNET; a full .NET-based library (CoreAudio.dll), with all the necessary functions to set the default audio device (both playback and recording) via the IPolicyConfig COM interface that sends out notifications to running applications notifying them of the change.
Once I had this, I was at last in position to finish off my project. I made the last tweaks, setup the necessary command-line parameters for the front-end command-line application and started testing. To my great and lasting enjoyment, all the tests worked. I now had the ability to setup AutoHotkey to trap any necessary keys and fire off my application with parameters indicating which devices I wanted to use as default.
I've included a reference of the application below and will be posting it in a few days.
AudioChanger.exe
Allows a command-line method of switching system default audio devices. Tested and found working on Windows 7 x64. Should also work on Windows 7 x86. In both cases, this program must be run as an administrator or with UAC disabled.
Parameters:
/? Brings up this help message
/list Display a list of currently enabled audio devices and the defaults
/cro Returns the ID of the current default output audio device
/cri Returns the ID of the current default input audio device
/so {guid} Sets the default output audio device based on a GUID
/so devicename busname Sets the default output audio device based on the device
name and device bus name (like Speakers, Creative SB X-Fi)
/si {guid} Sets the default input audio device based on a GUID
/si devicename busname Sets the default input audio device based on the device
name and device bus name (like Speakers, Creative SB X-Fi)
/so devicename busname [devicename] [busname] ... Switches between default output audio devices in the list of pairs; once it reaches the last one, it cycles back to the first item
/si devicename busname [devicename] [busname] ... Switches between default input audio devices in the list of pairs; once it reaches the last one, it cycles back to the first item
This project could not have been realized without the work of all those I've already mentioned in this post. However, to ensure that credit be given where credit is due, I've recapped all the people who've provided the groundwork (in no particular order):
- Dave Amenta, http://www.daveamenta.com, article http://www.daveamenta.com/2011/05/
- Ray Molenkamp, article http://www.codeproject.com/KB/vista/CoreAudio.aspx
- albain, article http://social.microsoft.com/Forums/en/Offtopic/thread/9ebd7ad6-a460-4a28-9de9-2af63fd4a13e
- EreTIk, http://eretik.omegahg.com, article http://social.microsoft.com/Forums/en/Offtopic/thread/9ebd7ad6-a460-4a28-9de9-2af63fd4a13e, and http://eretik.omegahg.com/download/PolicyConfig.h
- ghoster_e, article http://social.microsoft.com/Forums/en/Offtopic/thread/9ebd7ad6-a460-4a28-9de9-2af63fd4a13e
- MixerProNET, http://software.xfx.net/, article http://software.xfx.net/netcl/mxp/
Great job, is your project open to share? if so, how could we get it?
ReplyDeleteDeliang,
ReplyDeleteI'm glad you find this useful. I uploaded the source and the application to my GitHub repository, https://github.com/aifdsc/AudioChanger. Feel free to suggest any improvements that you can think of.
Thanks!
Stephan
Stephan,
ReplyDeleteThanks for sharing. It is compiled successfully. But when I run it, the following error is shown up:
=======================================
System.NullReferenceException: Object reference not set to an instance of an object.
at AudioEndpointManager.getAvailableAudioDevices(String regKey, Boolean includeNonEnabledStates) in C:\ESysAudioChang
er\AudioChanger\Audio.cs:line 152
at AudioEndpointManager.RefreshAvailableAudioDevices() in C:\ESysAudioChanger\AudioChanger\Audio.cs:line 156
at AudioEndpointManager..ctor() in C:\ESysAudioChanger\AudioChanger\Audio.cs:line 94
at AudioChanger.Program.Main(String[] args) in C:\ESysAudioChanger\AudioChanger\Program.cs:line 75
========================================
Do I need to do some presettings before using this utility?
I saw:
"SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render";
"SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Capture";
in your code, so do we need to do some registration settings first?
thank you for help.
Deliang
Deliang,
ReplyDeleteNormally, you should not have to do anything else with the program, but one thing does come to mind. That registry setting that you pointed out is, by default, protected by Windows 7 and Windows Vista.
If you're running with UAC turned on, that could be the source of the issue. I experienced best results with turning UAC off; otherwise this utility occasionally had issues with that protected registry key.
One other thing that I had to do for this to work on Windows 8 Beta, was to go the Registry Editor, navigate to HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices, right-click on the MMDevices entry, click Permissions, and add the Administrators group with full control. That resolved any remaining permission issues with that registry key.
**NOTE**: this does involve editing the registry, to please make sure to take any necessary precautions and backup beforehand :)
Let me know if this doesn't work for you and provide any assistance that I can.
Thanks!
Stephan
Stephan,
DeleteI turned off UAC, but I run into the following issue when I try to set another input device as default one.
Do you have any clue?
thanks for help.
-D.
==================================
C:\AudioChanger\bin\debug\AnyCPU>AudioChanger.exe /si 762cf124-1deb-4443-bf18-dce1b0c2942a
System.NullReferenceException: Object reference not set to an instance of an object.
at AudioEndpointManager.setDefaultAudioDevice(String regKey, String deviceId, String deviceIdPrefix) in C:\AudioChanger\Audio.cs:line 188
at AudioEndpointManager.SetDefaultInputDevice(String deviceId) in C:\AudioChanger\Audio.cs:line 197
at AudioChanger.Program.Main(String[] args) in C:\AudioChanger\Program.cs:line 112
C:\AudioChanger\bin\debug\AnyCPU>AudioChanger.exe /si {762cf124-1deb-4443-bf18-dce1b0c2942a}
System.Security.SecurityException: Requested registry access is not allowed.
at Microsoft.Win32.RegistryKey.OpenSubKey(String name, Boolean writable)
at AudioEndpointManager.setDefaultAudioDevice(String regKey, String deviceId, String deviceIdPrefix) in C:\AudioChanger\Audio.cs:line 188
at AudioEndpointManager.SetDefaultInputDevice(String deviceId) in C:\AudioChanger\Audio.cs:line 197
at AudioChanger.Program.Main(String[] args) in C:\AudioChanger\Program.cs:line 112
The Zone of the assembly that failed was:
MyComputer
Deliang,
ReplyDeleteMy apologies for the delay in answering. I believe that this is caused by the insufficient registry permissions, so you may need to apply the second fix I mentioned in my last post on editing the registry. I wasn't able to find too much else on this when I first created the utility, and the registry edit seemed to be the only way to allow the tool to change those protected registry entries.
If that still doesn't work, let me know and I'll see what else I can find.
Cheers!
Stephan
with uac turned off and the required access permissions this works great on win7 64 bit. However on win7 32 bit, even with UAC off and the registry permissions correctly set, the program generates an error, about a memory access violation.
ReplyDeleteI tracked it down to these lines of code. After commenting them out, it still seems to work correctly. Not sure how or why :)
public class CPolicyConfigClient
{
private IPolicyConfig _policyConfigClient = (new _CPolicyConfigClient() as IPolicyConfig);
public int SetDefaultDevice(string deviceID)
{
//this._policyConfigClient.SetDefaultEndpoint(deviceID, ERole.eConsole);
//this._policyConfigClient.SetDefaultEndpoint(deviceID, ERole.eMultimedia);
//this._policyConfigClient.SetDefaultEndpoint(deviceID, ERole.eCommunications);
return 0;
}
}
by the way, I forgot to mention, I am talking about when setting the default input device with /si
ReplyDeletejitterjames: these are some very interesting findings. I confess I had not tried on a 32-bit setup, so I might be off-base here. However, I do recall running into an issue on 64-bit computers where I had to compile without the AnyCPU setting. I believe, in my reference research, that x86 (32-bit) versions of the IPolicyConfig code required compiling with x86 explicitly specified, rather than AnyCPU. Something about how .NET compiles causes issues when using that code unless the bitness is perfectly matched.
ReplyDeleteIf you're willing to try, I would be curious to know if uncommenting those lines you commented and recompiling with x86 instead of AnyCPU resolves the issue as well.
Thanks!
sorry about the delay in responding. I did not know you had responded. I will look and see. thanks. I *think* I already tried that, but maybe not.
ReplyDeleteOK, I tried it and it makes no difference. Maybe what you are thinking about is this: compiling for x86 and then running on a 64 bit machine will not work, because it ends up getting the registry keys wrong (something to do with wow64). This is too bad because I have a program VoxCommando which I would like to add this code to. Because my program uses TTS I am forced to compile it for x86. That means that I am forced to call this as a commandline param.
ReplyDeletethe weird thing is that it seems to set the device correctly. It shows up in the windows device selection window immediately, without having to close and reopen it.
aifdsc:
ReplyDeleteI'm working on developing a script that does something very similar to yours, and I have just overcome most of the major hurdles to getting it working.
In reading your code, it looks like the Role:# value has something to do with which device is the default...could you perhaps explain to me how you were able to determine which device was default in the registry, and how to change the device?
Thanks,
Overkill
Overkill,
ReplyDeleteCertainly. It has been a bit, so I will review my notes to ensure that I'm giving you the correct information and I will post the answer here. It may be a day or two, but I'll be back in touch soon.
Thanks!
aifdsc
Dear Stephan,
DeleteI am trying to use your AudioChanger program to control my audio device on my Win 7 x64 PC.
I have downloaded the following .exe file
AudioChanger / AudioChanger / obj / x64 / Debug / AudioChanger.exe
When I execute it I have the following message regardless of the /parameter I am using
D:\Users\Regis\Downloads>audiochanger /?
System.NullReferenceException: Object reference not set to an instance of an object.
at AudioEndpoint.BytesToDate(Byte[] bytes)
at AudioEndpoint.GetAudioInputDevices()
at AudioChanger.Program.Main(String[] args)
AMD High Definition Audio Device
High Definition Audio Device
High Definition Audio Device
Default output device: Digital Audio (S/PDIF)
Default input device:
Press any key to continue . . .
Could you let me what I am doing wrong?
Thank you in advance
Régis