How do I change the master volume in Windows Vista

It's actually easier in Vista than it was in XP.  For Vista, we recognized that one of the key customer scenarios was going to be setting the master volume, and since we'd removed the old mechanism that was used to set the volume, we knew we had to provide an easier mechanism for Vista.

Just for grins,  I threw together a tiny app that demonstrates it.  To save space, all error checking was removed.

#include <stdio.h>#include <windows.h>#include <mmdeviceapi.h>#include <endpointvolume.h> void Usage(){  printf("Usage: \n");  printf(" SetVolume [Reports the current volume]\n");  printf(" SetVolume -d <new volume in decibels> [Sets the current default render device volume to the new volume]\n");  printf(" SetVolume -f <new volume as an amplitude scalar> [Sets the current default render device volume to the new volume]\n"); }int _tmain(int argc, _TCHAR* argv[]){  HRESULT hr;  bool decibels = false;  bool scalar = false;  double newVolume;  if (argc != 3 && argc != 1)  {    Usage();    return -1;  }  if (argc == 3)  {    if (argv[1][0] == '-')    {      if (argv[1][1] == 'f')      {         scalar = true;      }      else if (argv[1][1] == 'd')      {        decibels = true;      }    }    else    {      Usage();      return -1;    }     newVolume = _tstof(argv[2]);  }  // -------------------------  CoInitialize(NULL);  IMMDeviceEnumerator *deviceEnumerator = NULL;  hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);  IMMDevice *defaultDevice = NULL;   hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);  deviceEnumerator->Release();  deviceEnumerator = NULL;   IAudioEndpointVolume *endpointVolume = NULL;  hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);  defaultDevice->Release();  defaultDevice = NULL;   // -------------------------  float currentVolume = 0;  endpointVolume->GetMasterVolumeLevel(&currentVolume);  printf("Current volume in dB is: %f\n", currentVolume);   hr = endpointVolume->GetMasterVolumeLevelScalar(&currentVolume);  printf("Current volume as a scalar is: %f\n", currentVolume);  if (decibels)  {    hr = endpointVolume->SetMasterVolumeLevel((float)newVolume, NULL);  }  else if (scalar)  {    hr = endpointVolume->SetMasterVolumeLevelScalar((float)newVolume, NULL);  }  endpointVolume->Release();   CoUninitialize();  return 0;}

This program has essentially 3 parts.  The first parses the command line, the second retrieves an endpoint volume interface on the default endpoint, the third retrieves the current volume and sets the volume.

I'm going to ignore the first part, it's the same junk you'll see in any CS 101 class. 

The second part instantiates an MMDeviceEnumerator object which implements the IMMDeviceEnumerator interface.  The IMMDeviceEnumerator interface is the gateway object to the new audio subsystem - it can be used to enumerate audio endpoints and retrieve information about the various endpoints.  In this case, I'm only interested in the GetDefaultAudioEndpoint method, it returns an IMMDevice object that points to the current endpoint.

Again, there are a bunch of things I can do with an IMMDevice object, but I'm only really interested in the "Activate" method.  The idea is that each MMDevice object supports lots of different interfaces, you "Activate" the interface to access the functionality associated with that object.  Again, in this case, I'm only interested in the IAudioEndpointVolume interface - there are other interfaces, like IDeviceTopology, and IAudioClient that can be activated from the endpoint.

The IAudioEndpointVolume interface is where the good stuff lives, right now I'm only interested in four methods, which retrieve (and set) the current endpoint volume in either decibels or as a scalar value. 

The decibels version of the IAudioEndointVolume interface instructs the driver to set the desired master volume (input or output) to the decibel value specified, it's intended to be used for applications that want to have exact control over the output dB value of the audio solution.

The scalar version is a bit more complicated.  It's intended for use in applications that have volume sliders, and provides a linear volume taper (represented as a floating point value between 0.0 and 1.0).  In other words, the perceived volume when you set the scalar version of the API to .5 is twice as loud as when set to .25 and is half as loud as when set to 1.0.

Comments

  • Anonymous
    March 06, 2007
    I'm confused how 13 lines of non-error checked code is "easy". Surely it wouldn't have killed anyone to have a nice API such as: SetDefaultMasterVolume(byte volume); ...or am I completely missing the point?? :)

  • Anonymous
    March 06, 2007
    The comment has been removed

  • Anonymous
    March 06, 2007
    Anonymous: You've clearly never tried to do this on XP :). On XP, you need to first retrieve the default wave output device (or voice communications app), then you need to open a mixer handle that corresponds to that object. You next need to enumerate over the mixer destination lines associated with that mixer.  For each destination, you need to enumerate the controls on that destination looking for a master volume control. There are some shortcuts that will make the search for master volume easier, but even so.  Once you've retrieved the master volume control, you retrieve the volume using mixerGetControlDetails and set the volue using mixerSetControlDetails. I've written the code, it's a TON more than 13 lines. As to why there isn't a trivial API like SetDefaultMasterVolume, it's a trade-off.  You can have a bazilion APIs each of which wraps 13 lines of code or a couple of dozen APIs and push the 13 lines of code to the app.

  • Anonymous
    March 06, 2007
    This is great, but why in Vista is the pass through from mic to speakers no longer available. In XP I could just "unmute" the microphone, then I would hear the mic through the speakers...no option for that in Vista...or am I missing something?

  • Anonymous
    March 06, 2007
    The comment has been removed

  • Anonymous
    March 06, 2007
    and that's the easy way????????? Then we must have different views on the word "EASY"

  • Anonymous
    March 06, 2007
    bw, IMHO, it's easy.  Especially compared to the amount of work it was to do it on every version of Windows before Vista.

  • Anonymous
    March 06, 2007
    Easy enough indeed. I'd like to ask whether these functions will be ported back to at least WinXP? Or if I have a program that'll run in both WinXP and Vista, I have to keep both code path?

  • Anonymous
    March 06, 2007
    I don't know, Larry. As annoying as the old mixer functions were, they were at least straightforward to call from languages other than C++ and (classic) VB. I'm not sure adding yet another COM server is the way to make this sort of thing "easy." Other examples are rampant throughout the shell, including the new Task Dialogs and common file dialogs. Off topic: Since MCI is presumably kaput now, what's the officially sanctioned version of "set door open" and "set door closed?"

  • Anonymous
    March 06, 2007
    bw: It's as easy as it should be, in my opinion. There's really only ONE application that I want controlling the master volume (and that's the little speaker icon in the notification area). Of course, they need to provide an API for "special cases" like MCE as noted before, but unless you are one of these "special cases" I don't want you touching my master volume control anyway.

  • Anonymous
    March 06, 2007
    Can you do this in vbscript? (in other words, does this have proper automation interfaces?) I guess the it would be GetObject("something with MMDeviceEnumerator").GetDefaultAudioEndpoint.Activate("something with AudioEndpointVolume").GetMasterVolumeLevel

  • Anonymous
    March 06, 2007
    Jonathan, unfortunately no :(  A managed interop layer for these APIs didn't make the bar for Vista :( Cheong, sorry, no - they rely on a TON of infrastructure in Vista, which does stink. Bob, MCI isn't kaput, it's still the best way of opening and closing the CD tray door (I've not yet found a better one (short of issuing the raw IOCTLs)).

  • Anonymous
    March 06, 2007
    Heh, when I first read the article title in my RSS reader, I thouhg you meant changing the default boot device :-) Re: MCI & CD tray operations - Surely there must be a generic API for ejecting devices? How else would you eject USB drives, etc? (Closing the device is another matter, but the API should be almost identical)

  • Anonymous
    March 06, 2007
    The comment has been removed

  • Anonymous
    March 07, 2007
    New and easy is good, and I'm sure this is a wonderful improvement. And people do often complain that Microsoft introduces new APIs without removing the old crud, thus seemingly causing endless problems. There's a little known guy called Mr Chen who spends a great deal of time defending backwards compatability. But I do have a question that I would have assumed to be obvious: if the old way actually has been removed, will applications that did it the "old way" on XP (you know, that archaic and irrelevant old thing that noone uses any more) have problems running on Vista? And if the old way is still there, doesn't that mean VBscript can still do things that way?  And the phrase "and since we'd removed the old mechanism that was used to set the volume" would mean something other than what it seems to mean.

  • Anonymous
    March 07, 2007
    After I posted the " How do I set the master volume in Vista ", DanT commented : Thanks Larry. I'll have

  • Anonymous
    March 08, 2007
    Re: generic API for ejecting devices: You wouldn't want the same API that unmounts volumes to also eject CD trays. If you have a USB CD player, you don't want "open tray" and "prepare for removal" to be the same thing.

  • Anonymous
    March 08, 2007
    The comment has been removed

  • Anonymous
    March 12, 2007
    Just for closure: I had to rebuild my hard drive from scratch. Apparently my development setup was not quite current either for the SDK or the DDK. After I reinstalled fresh copies, your code sample compiled fine. In the words of Emily Latella, <never mind>.

  • Anonymous
    March 20, 2007
    Last week I posted a code snippet that showed how to change the master volume in Vista . That snippet

  • Anonymous
    May 21, 2007
    I am still searching for a solution to control the MIDI volume in Vista without changing the Wave volume. My application use both MIDI sound and Wave audio, and the user needs to adjust them individually. Currently my application opens the Windows mixerlines to control this. This works fine in XP and previous versions, but in Vista it can no longer open the mixerline for MIDI sound and instead the mixerline for the wave sound also controls the MIDI sound. This means the user cannot lower the volume of the Wave sound without also lowering the volume of the MIDI sound. I have googled this and searched MSDN and still no answers. What am I missing?

  • Anonymous
    May 23, 2007
    Hans, if you're using the software MIDI renderer, then there's no solution - the software MIDI renderer uses the WAVE APIs to render audio, so they're going to be using the same volume control. You should contact Microsoft PSS, and file an insident report, that'll get this issue on the official radar.

  • Anonymous
    May 28, 2007
    Hi Larry: This snippet  code can not run at vs2005(c++)/MFC/Vista SDK, what is worry with this? Thanks!!

  • Anonymous
    May 29, 2007
    Nike, that's what I used to write the code.  Did you remember to add the Vista SDK to your visual studio environment?

  • Anonymous
    May 29, 2007
    Larry,I add the Vista SDK to my visual studio environment(Tools/Options/Projects and Solutions/vc++ Directories/Show directories for/Include files,,add c:program filesmicrosoft SDKsWindowsv6.0include). c:program filesmicrosoft sdkswindowsv6.0includestructuredquery.h(372) : error C2061: syntax error : identifier '__RPC__out' c:program filesmicrosoft sdkswindowsv6.0includestructuredquery.h(376) : error C2061: syntax error : identifier '__RPC__in' c:program filesmicrosoft sdkswindowsv6.0includestructuredquery.h(380) : error C2061: syntax error : identifier '__RPC__deref_out_opt' c:program filesmicrosoft sdkswindowsv6.0includepropsys.h(1699) : error C2061: syntax error : identifier '__RPC__in' c:program filesmicrosoft sdkswindowsv6.0includepropsys.h(1705) : error C2061: syntax error : identifier '__RPC__out' c:program filesmicrosoft sdkswindowsv6.0includepropsys.h(1709) : error C2061: syntax error : identifier '__RPC__deref_out_opt_string' Build log was saved at "file://d:zzyunVista(volumn)MFCMFCDebugBuildLog.htm" MFC - 103 error(s), 0 warning(s) What is worry with this ? Thanks!

  • Anonymous
    May 29, 2007
    Nike: You haven't set NTDDI to Vista.

  • Anonymous
    May 29, 2007
    Larry, what is the NTDDI ? How can i set the NTDDI to Vista?

  • Anonymous
    May 30, 2007
    Nike, you need to tell the SDK that you're building a Vista application.  You do that by setting NTDDI_VERSION=NTDDI_VISTA (or something like that) in your stdafx.h file.

  • Anonymous
    May 30, 2007
    Windows is rather famous for its ability to run applications that were written for previous versions

  • Anonymous
    May 30, 2007
    Larry,i do at this in my stdafx.h file. #define NTDDI_VERSION  NTDDI_LONGHORN c:program filesmicrosoft sdkswindowsv6.0includesdkddkver.h(213) : fatal error C1189: #error :  NTDDI_VERSION setting conflicts with _WIN32_WINNT setting. Or #define NTDDI_VERSION  NTDDI_VISTA c:program filesmicrosoft sdkswindowsv6.0includedevicetopology.h(2237) : error C2061: syntax error : identifier 'KSJACK_DESCRIPTION'. What is worry with this ? Thanks!

  • Anonymous
    May 30, 2007
    Nike, I'm not sure - I'd go looking for the KSJACK_DESCRIPTION definition and see what's up.

  • Anonymous
    June 03, 2007
    Hi Larry, I add the Vista SDK to my visual studio environment(Tools/Options/Projects and Solutions/vc++ Directories/Show directories for/Include files,,add c:program filesmicrosoft SDKsWindowsv6.0include). i do at this in my stdafx.h file. #define NTDDI_VERSION  NTDDI_LONGHORN Or #define NTDDI_VERSION  NTDDI_VISTA but, c:program filesmicrosoft sdkswindowsv6.0includeshtypes.h(450) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int c:program filesmicrosoft sdkswindowsv6.0includepropsys.h(438) : error C2061: syntax error : identifier 'REFPROPVARIANT' VistaVolume - 22 error(s), 0 warning(s) Can you help me ?Thanks.