Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

How do I change the master volume in Windows Vista

How do I change the master volume in Windows Vista

  • Comments 32

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.

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

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

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

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

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

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

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

  • Hi Larry:

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

    what is worry with this?

    Thanks!!

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

  • 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 files\microsoft SDKs\Windows\v6.0\include).

    c:\program files\microsoft sdks\windows\v6.0\include\structuredquery.h(372) : error C2061: syntax error : identifier '__RPC__out'

    c:\program files\microsoft sdks\windows\v6.0\include\structuredquery.h(376) : error C2061: syntax error : identifier '__RPC__in'

    c:\program files\microsoft sdks\windows\v6.0\include\structuredquery.h(380) : error C2061: syntax error : identifier '__RPC__deref_out_opt'

    c:\program files\microsoft sdks\windows\v6.0\include\propsys.h(1699) : error C2061: syntax error : identifier '__RPC__in'

    c:\program files\microsoft sdks\windows\v6.0\include\propsys.h(1705) : error C2061: syntax error : identifier '__RPC__out'

    c:\program files\microsoft sdks\windows\v6.0\include\propsys.h(1709) : error C2061: syntax error : identifier '__RPC__deref_out_opt_string'

    Build log was saved at "file://d:\zzyun\Vista(volumn)\MFC\MFC\Debug\BuildLog.htm"

    MFC - 103 error(s), 0 warning(s)

    What is worry with this ?

    Thanks!

  • Nike: You haven't set NTDDI to Vista.

  • Larry, what is the NTDDI ?

    How can i set the NTDDI to Vista?

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

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

  • Larry,i do at this in my stdafx.h file.

    #define NTDDI_VERSION  NTDDI_LONGHORN

    c:\program files\microsoft sdks\windows\v6.0\include\sdkddkver.h(213) : fatal error C1189: #error :  NTDDI_VERSION setting conflicts with _WIN32_WINNT setting.

    Or

    #define NTDDI_VERSION  NTDDI_VISTA

    c:\program files\microsoft sdks\windows\v6.0\include\devicetopology.h(2237) : error C2061: syntax error : identifier 'KSJACK_DESCRIPTION'.

    What is worry with this ? Thanks!

Page 2 of 3 (32 items) 123