Other fun things to do with the EndpointVolume interfaces

Last week I posted a code snippet that showed how to change the master volume in Vista.  That snippet doesn't really show the coolness that exists within the IAudioEndpointVolume interface.

One of my favorite features is the support for OSD's (On Screen Displays).  To show it off, I write a tiny little OSD program that shows off just two of the APIs in question - VolumeStepUp and VolumeStepDown. 

If you hit '+' and '-' it'll increase and decrease the volume, I also included a cheesy OSD so you can see the effects of the APIs.

Before people complain about how much code is here, this is the complete source file, and there's a lot more code to deal with the console than there is to deal with the volume. 

#include "stdafx.h"
#include <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <strsafe.h>

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr;

    CoInitialize(NULL);
    IMMDeviceEnumerator *deviceEnumerator = NULL;;
    HANDLE      handle;

    //
    // Clear the screen. Code stolen from: https://support.microsoft.com/kb/319257.
    //
    handle = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD writtenChars = 0;
    CONSOLE_SCREEN_BUFFER_INFO consoleInfo;            
    COORD home;
    home.X = home.Y = 0;
    GetConsoleScreenBufferInfo(handle, &consoleInfo);
    FillConsoleOutputCharacter(handle, L' ', consoleInfo.dwSize.X * consoleInfo.dwSize.Y, home, &writtenChars);
    SetConsoleCursorPosition(handle, home);

    //
    // Set the console to raw mode. 
    //
    DWORD oldMode;
    GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &oldMode);
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldMode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));

    //
// Instantiate an endpoint volume object.
//

    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;

    wchar_t inputChar = '\0';
    while (inputChar != '\r')
    {
        UINT currentStep, stepCount;
        DWORD read, written;
        COORD writeCoord;
        wchar_t outputString[256];
        StringCbCopy(outputString, sizeof(outputString), L"Volume: ");

        //
// Calculate the cheesy OSD.
//

        endpointVolume->GetVolumeStepInfo(&currentStep, &stepCount);
        for (size_t i = 0 ; i < stepCount ; i += 1)
        {
            if (i <= currentStep)
            {
                StringCbCat(outputString, sizeof(outputString), L"=");
            }
            else
            {
                StringCbCat(outputString, sizeof(outputString), L"-");
            }
        }
        writeCoord.X = 0;
        writeCoord.Y = 0;
        WriteConsoleOutputCharacter(handle, outputString, (DWORD)wcslen(outputString), writeCoord, &written);

        ReadConsole(GetStdHandle(STD_INPUT_HANDLE), &inputChar, 1, &read, NULL);
        if (inputChar == '+')
        {
            endpointVolume->VolumeStepUp(NULL);
        }
        else if (inputChar == '-')
        {
            endpointVolume->VolumeStepDown(NULL);
        }
    }

    endpointVolume->Release();
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldMode);

    CoUninitialize();
    return 0;
}

This program's pretty simple right now, the next step is to add a bit of magic to it (metering).