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 (32)

  1. Anonymous says:

    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?? 🙂

  2. DanT says:

    Thanks Larry.  I’ll have to send a link to this to the people at Griffin so they can fix my PowerMate.  I loved this volume knob on XP.  On Vista it only changes the volume for itself which is somewhat less than useful.

  3. 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.

  4. David says:

    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?

  5. David: Those are two unrelated issues.  The functionality you’re describing was provided by your audio driver, on Vista, the same functionality exists – go to the playback devices, select your default playback device, and look at the "levels" tab – there should be a "Mic" or "Line In" slider, unmute that and it should work.

  6. bw says:

    and that’s the easy way????????? Then we must have different views on the word "EASY"

  7. bw, IMHO, it’s easy.  Especially compared to the amount of work it was to do it on every version of Windows before Vista.

  8. Cheong says:

    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?

  9. Bob says:

    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?"

  10. Dean Harding says:

    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.

  11. Jonathan says:

    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

  12. 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)).

  13. 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)

  14. Phaeron says:

    Having had to write code to activate and change the volume on line-in, this is definitely MUCH easier than mucking through the source lines, destination lines, and control types in the mixer API.

    I agree with Dean Harding’s comment, though — apps generally should not be mucking with the global playback volume, or really any volume settings, unless they’re providing UI for the user to do so. It’s annoying to have a game automatically shove MIDI to maximum volume because that was the fix the programmers came up with for no-music bugs.

  15. Anony Moose says:

    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.

  16. After I posted the " How do I set the master volume in Vista ", DanT commented : Thanks Larry. I’ll have

  17. Gabe says:

    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.

  18. Sad Michael says:

    Larry –

        Thanks for the great article, it covers exactly what I need to adapt some XP code. I hate to reward your generosity by tossing you a low-level support question, but I’m betting/hoping this is a quickie for you.

        I compiled your sample code as a Win32 project in VS2005 under Vista. It got 32 syntax errors in ocidl.h complaining about __RPC__in and other such constructs. After some research, I added an include for rpcsal.h. That cleared the original problems, but released a slew of syntax errors from propsys.h about REFPROPVARIANT and a complaint from shtypes.h about the undefined identifier PROPERTYKEY.

       Since nobody else mentioned any such problems, I assume it’s something basic in my setup or understanding. I’ve been working in VC6/MFC/XP exclusively until the past month, so there’s much basic infrastructure in the brave new world that I’m running into for the first time, and no newbie mistake is beneath me. Is there some project choice or setting that I got wrong? If it isn’t obvious from the above description, I don’t want to waste your time over my lack of experience. But I’m prepared to slap my forehead in chagrin if you can point out my error.

       Many thanks….

  19. Sad Michael says:

    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>.

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

  21. Hans Jakobsen says:

    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?

  22. 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.

  23. Nike says:

    Hi Larry:

    This snippet  code can not run at vs2005(c++)/MFC/Vista SDK,

    what is worry with this?

    Thanks!!

  24. Nike, that’s what I used to write the code.  Did you remember to add the Vista SDK to your visual studio environment?

  25. Nike says:

    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!

  26. Nike: You haven’t set NTDDI to Vista.

  27. Nike says:

    Larry, what is the NTDDI ?

    How can i set the NTDDI to Vista?

  28. 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.

  29. Windows is rather famous for its ability to run applications that were written for previous versions

  30. Nike says:

    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!

  31. Nike, I’m not sure – I’d go looking for the KSJACK_DESCRIPTION definition and see what’s up.

  32. Nike says:

    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.