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(¤tStep, &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).
Comments
Anonymous
March 20, 2007
There are still someone who can code in a proper language at Microsoft ;-)Anonymous
March 20, 2007
I code in the language that best fits my task at hand, usually it's C++.Anonymous
March 20, 2007
Too bad it won't compile in ASCII builds. No use of the _T* types or macros? For shame, Larry. For shame.Anonymous
March 20, 2007
John, why on earth would want this to compile in 8 bit character mode? The compiler's default character set is now Unicode, and 8bit character sets carry with them a mountainous set of baggage. If you want it to work in non unicode, the conversion is simple.Anonymous
March 20, 2007
Just for the sake of completeness. Will the Windows SDK headers ever ship without definitions for ASCII structs / functions? As you say there is not really any compelling use for ASCII these days aside from backwards compatibility, but how long is that going to be maintained?Anonymous
March 20, 2007
John, the A functions won't go away, but all new APIs are unicode only.Anonymous
March 20, 2007
Many of the new APIs are Vista-only and 9x is officially way way past its lifetime. (I'm ok with that, some things need to die.) So, if you want to use these new APIs in actual products, you'ld better hope your customers have all upgraded. ;)Anonymous
March 20, 2007
Larry – Could you please show an example of implementing IAudioEndpointVolumeCallback? Not a full app, just the lines of code to create the interface and supply the OnNotify function, so I’ve got something to hand to RegisterControlChangeNotify? I will praise your name fulsomely. Thanks, Michael (mrgriffin@gmail.com if you want to comment without posting in your space)Anonymous
March 21, 2007
Yesterday I posted a quick&dirty OSD (complete with cheesy text graphics). Today I'm going to addAnonymous
May 15, 2007
Nice to see your code is color highlighted.Anonymous
May 30, 2007
Hi Larry,i want to change the Mute status. if (inputChar == '+') { BOOL *mute_status=FALSE; endpointVolume->GetMute(mute_status); if (mute_status) endpointVolume->SetMute(true,NULL); else endpointVolume->SetMute(false,NULL); } No matter the Mute is on or off,the mute_status is 0,what is worry with this? Thanks!Anonymous
May 30, 2007
Larry,oh,it is my worry. I can set the muting state .