Click here to Skip to main content
Click here to Skip to main content

Volume Manipulation Classes

By , 16 Nov 1999
 

Introduction

Many times my applications required audio volume manipulation. To make the volume-enabled application development easier, I decided to create a few C++ classes that would allow me to easily regulate and track the changes of such volume controls as Output Master Volume, WaveOut Volume and Input (WaveIn) Volume. Here I provide such classes that share a common interface (defined in IVolume.h):

  • bool IsAvailable() - Says whether the volume controlling is possible.
  • void Enable() - Enables the line of the volume control.
  • void Disable() - Disables the line of the volume control.
  • DWORD GetVolumeMetric() - Retrieves the granularity of volume.
  • DWORD GetMinimalVolume() - Retrieves the minimal volume that can be set.
  • DWORD GetMaximalVolume() - Retrieves the maximal volume that can be set.
  • DWORD GetCurrentVolume() - Retrieves the current volume.
  • void SetCurrentVolume( DWORD dwValue ) - Sets the volume.

And the last function allows to register a user-implemented callback that will be called as a notification of volume changes:

void RegisterNotificationSink( PONMICVOULUMECHANGE, DWORD )

This interface is implemented by CVolumeOutMaster (VolumeOutMaster.h/cpp), CVolumeOutWave (VolumeOutWave.h/cpp) and CVolumeInXXX (VolumeInXXX.h/cpp) classes. The usage of the classes is very simple:

In your StdAfx.h, include "mmSystem.h" and make sure you link to the "winmm.lib" (#pragma comment(lib, "winmm.lib")). Then, if you are going to use Output Mater volume control, include "VolumeOutMaster.h", say, to the StdAfx.h.

The IVolume.h, VolumeInXXX.h, VolumeInXXX.cpp are to be inserted as your project files.

...
void CALLBACK MasterVolumeChanged( DWORD dwCurrentVolume, DWORD dwUserValue );
...
// Volume control Initialization
IVolume* pMasterVolume = (IVolume*)new CVolumeOutMaster();
if ( !pMasterVolume || !pMasterVolume->IsAvailable() )
{
    // handle error
}
pMasterVolume->Enable();
pMasterVolume->RegisterNotificationSink( MasterVolumeChanged, dwAnyUserValue );
...
pMasterVolume->SetCurrentVolume( dwVolumeToSet );
...
DWORD dwCurrentVolume = pMasterVolume->SetCurrentVolume();
...
void CALLBACK MasterVolumeChanged( DWORD dwCurrentVolume, DWORD dwUserValue )
{
   // handle the volume change
}
...

Very simple, isn't it? Yet, the CVolumeInXXX class requires more explanation. In order to manipulate the Input volume, the source line index is to be passed to the constructor. Confused? Please, be not. CVolumeInXXX class provides a static function to enumerate those lines:

bool EnumerateInputLines( PINPUTLINEPROC, DWORD dwUserValue );

This allows you to manipulate the volume of any WaveIn-based lines. Say, you want to manipulate the microphone volume:

...
bool CALLBACK EnumInputLineProc( UINT uLineIndex, 
              MIXERLINE* pLineInfo, DWORD dwUserValue );
...
// Initialization
UINT uMicrophoneLineIndex = (UINT)-1;
if ( !CVolumeInXXX::EnumerateInputLines( EnumInputLineProc, 
                           (DWORD)&uMicrophoneLineIndex ) )
{
   // handle error
}
if ( uMicrophoneLineIndex == (UINT)-1 )
{
        // Error: mic volume'ing is not available.
}
IVolume* pMicrophoneVolume = 
         (IVolume*)new CVolumeInXXX( uMicrophoneLineIndex );
if ( !pMicrophoneVolume || !pMicrophoneVolume->IsAvailable() )
{
   // handle error
}
// Go on and use pMicrophoneVolume to manipulate the volume
...
bool CALLBACK EnumInputLineProc( UINT uLineIndex, 
              MIXERLINE* pLineInfo, DWORD dwUserValue )
{
    if ( pLineInfo->dwComponentType == 
             MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE )
    {
        *((UINT*)dwUserValue) = uLineIndex;
        return false;
    }
    return true;
}
...

Be aware, that for performance reasons it is better to have a single instance of a given class per application. So don't rush to create lots of CVolumeInXXX objects, better share the only one through your code.

Conclusion

The proposed classes do not encapsulate all the abilities exposed by the mixers. However, working with a mixer just to add a pretty simple functionality is quite boring. That's why, as I think, the proposed classes might be of some help to you.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Alex Chmut
Web Developer
Ukraine Ukraine
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
SuggestionWindows 7 volume control samplememberM A V15 Dec '12 - 1:05 
Dear colleagues!
 
Searching the Internet found the relatively easy to understand and implement sample in volume manipulating under Windows 7:
 
http://blogs.msdn.com/b/larryosterman/archive/2007/03/06/how-do-i-change-the-master-volume-in-windows-vista.aspx[^]
 

Artem
QuestionLicense?memberLuisVilla16 Feb '12 - 4:48 
Hi, Alex- What is the license on this code? Some people I know are looking into distributing it for commercial purposes. Thanks!
AnswerRe: License?memberAlex Chmut16 Feb '12 - 10:15 
You can use it as you want, - it is a dev-shared experience.
GeneralControlling master volume in Win7 - IAudioEndpointVolume - Working sample code in MSDNmemberkormoe15 Jan '12 - 5:39 
here:
http://msdn.microsoft.com/en-us/library/bb331828.aspx[^]
Simple and works fine..
 
also found peak meter sample:
http://msdn.microsoft.com/en-us/library/bb736029.aspx[^]
 
I tested both examples and worked fine with win7 64bit
GeneralVolume Control in Windows7memberMember 264083617 Aug '10 - 20:41 
This article works fine for application level volume control in windows 7 but I need Master volume control in windows 7? any help
GeneralMaking compatiblememberBazKhan18 Jan '09 - 22:49 
I'm working on an application which was written in VB using MCI.OCX and winmm.dll library. It runs on Windows 98 successfully but cause problems on Windows XP. Do you know how I can make the application compatible for Windows XP?

 
Always looking for seniors help and guidance.

QuestionWindows Vista Anyone???member{bill}1 Sep '08 - 9:41 
I have been using this library under XP for years with great success. Thanks Alex. I have recently become aware that the volume control doesn't set the volume when running under Window Vista. I know that they rewrote the entire sound system in Vista, but all the calls look like they are working when you examine the results under a debugger.
 
Anyhow, has nyone been able to get this to go under Vista? If not, I would be interested in what code libraries people are using under vista to control volume. I need to control the volume for both the CD and wave devices independantly.
 
In order to support setting the balance, I also changed the SetVolume method to this (which has been working under XP for years):
 
void CVolumeOutWave::SetCurrentVolume( DWORD dwRightValue, DWORD dwLeftValue )
{
if ( !m_bAvailable || (dwRightValue<m_dwMinimalVolume) || (dwRightValue>m_dwMaximalVolume) ||
(dwLeftValue<m_dwMinimalVolume) || (dwLeftValue>m_dwMaximalVolume) )
return;
MIXERCONTROLDETAILS_UNSIGNED* aDetails = (MIXERCONTROLDETAILS_UNSIGNED*)malloc(m_nChannelCount*sizeof(MIXERCONTROLDETAILS_UNSIGNED));
if ( !aDetails )
return;
for ( int i = 0; i < m_nChannelCount; i++ )
{
if (i < (m_nChannelCount / 2))
{
aDetails[i].dwValue = dwLeftValue;
}
else
{
aDetails[i].dwValue = dwRightValue;
}
}
MIXERCONTROLDETAILS ControlDetails;
memset( &ControlDetails, 0, sizeof(MIXERCONTROLDETAILS) );
ControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
ControlDetails.dwControlID = m_dwVolumeControlID;
ControlDetails.cChannels = m_nChannelCount;
ControlDetails.cMultipleItems = 0;
ControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
ControlDetails.paDetails = &aDetails[0];
MMRESULT mmResult = mixerSetControlDetails( (HMIXEROBJ)m_dwMixerHandle, &ControlDetails, MIXER_SETCONTROLDETAILSF_VALUE );
free( aDetails );
if ( mmResult != MMSYSERR_NOERROR )
{
TRACE(".WaveOutputVolume: FAILURE: Could not set volume(%d) mmResult=%d\n", dwRightValue, dwLeftValue, mmResult );
}
}
AnswerRe: Windows Vista Anyone???memberSteven Dong9 Nov '08 - 4:37 
I have same question!
QuestionCallback Functionmemberpeene9 Aug '07 - 5:46 
How to use the callback function to update a control like a text field in a dialog? I am a beginner and hope somebody can help.

Questionhow can i control the sound of left and right speakermemberrajneshmalik1 Aug '07 - 21:49 
Confused | :confused: hi
how can i control the sound of left and right speaker by vc++/mfc.Actually i want to send different sound at same time on left and right speaker.
 
if anybody reply me i will obliged
 
hi
GeneralProblem in 2000memberlilesh26 Feb '07 - 22:56 
Your class work excellent with windows xp but there is some problem in 2000,
when i run program it give following messages which u have included.
 
error: CVolumeInXXX::EnumerateInputLines
 
error: uMicrophoneLineIndex = -1
 
error: !ob_MicroPhoneVolume || !ob_MicroPhoneVolume->IsAvailable()
 
plz tell me wht shall i do for this

QuestionBegginer's questionmemberlnenad12 Nov '06 - 8:12 
I want to make a dll which i can use with Liberty Basic.
I'm still a begginer so you might think this is easy but for me it isn't
 
Here is the code. It only reports one error but i can't fix that because i can't find the error?
PS: There is probably lots of errors in my code Confused | :confused:
 
#include iostream.h
#include stdlib.h
#include windows.h>
 
EXTERN_C __declspec(dllexport) LONG Jacina (LONG);
 
BOOL WINAPI __declspec(dllexport) LibMain (HINSTANCE hInst, DWORD Reason, LPVOID Reserved)
{
if(Reason==DLL_PROCESS_ATTACH)
{
return TRUE;
}
 
if(Reason==DLL_PROCESS_DETACH)
{
return TRUE;
}
 
return FALSE;
}
 

EXTERN_C __declspec(dllexport) LONG Jacina ( LONG jaci )
{
long lRet;
lRet = void SetCurrentVolume( DWord jaci );
return lRet;
}

QuestionHigh Definition AudiomemberSaber00119 Jul '06 - 0:09 
will this classes work for The Intel sigmatel and realtech
audio devices which does not use the AC'97 codec.
 
having problems manipulating the LINEIN volume control in my software...
 
any IDEAS?Confused | :confused: Confused | :confused:
QuestionHow to use Volume Manipulation Classesmembercastlezhen28 Apr '06 - 3:04 
I need your hand so much !Would you do me a favor to show me how to use those Classes above!Thank you very much!
Please mail me:guoliangzhen2005@163.com,if you have time .
GeneralDiable Microphonememberanakia18 Apr '06 - 17:05 
I want to Diable Microphone, But the code doesn't Support it! Could you Help me!
General[Q] Love your article and more...memberyoontet17 Aug '05 - 5:52 
I love your article and have another question.
 
I used Enable() method and It always chose the Aux Recording-in. For my general idea, It should choose MIC-in or Line-In. Is it right? If I am wrong, comment me. What's the purpose of Enable() in this class?
 
Thank you.

GeneralRe: [Q] Love your article and more...memberyoontet17 Aug '05 - 6:23 
I've tested your Enable() funtion and found some error in my computer. I don't know whether it is applicable to other computers.
 
Error is that somehow the Detail value in Enable() is counted in backward, which means it should accumulate the data from the end of buffer.
 
aDetails[nMultipleItems-nItem-1].fValue = lValue;
 
Then you can enable the selection that you have chosen input in enumeration().
 
Thanks.

GeneralProblems with Microphone controlmemberVirendra Sharma25 May '05 - 23:29 
Hi,
Your MasterVolume control works well. But microphone control doesn't work on Win-XP.
It shows following lines of error while debugging:
 
MasterOutputVolume: Initializing for Source MasterOut Line ..
.MasterOutputVolume: "Master Volume" Volume control for the Speakers Source Line adopted.
.MasterOutputVolume: Enabling Line: Speakers Line control "Master Mute"(0x20010002) has been set to 0.
 
.InputXxxVolume: Initializing for the Source Line (-1) ..
.InputXxxVolume: FAILURE: Invalid Source Line index passed.
Loaded 'C:\WINDOWS\wsec32hk.dll', no matching symbolic information found.
 
I am not getting what the problem is? The code works on WIN-2000. I am using CMedia audio hardware.
 
Please reply me soon. Smile | :)
Thanks again,
Virendra

GeneralRe: Problems with Microphone controlmemberFco Javier Lama3 May '06 - 23:24 
Hi everybody. I discovered a problem selecting the desired input line under XP(for example, you want to select microphone, but the software selects another input line). I found that Windows XP enumerates the different input lines in inverse order. So, in XP you have to invert the order of input lines, and your new code will work!
Good luck ! Smile | :)
FJLama.
 
Voice Over Ip
Generaldoesn t work with a USB headsetmemberbartouze22 Apr '05 - 3:09 
Hi,
 
This progem is working fine with a classic sound card,
but it doesn t work with a usb headset , plantronics DSP 400,
 
If I change the input, then the CD audio output is changed, and the Master volume is muted !!!
 

any idea ?
 

GeneralProblem passing callback function in...memberel davo4 Mar '04 - 13:48 
Hi,
 
I implemented a simple MFC dialog, and tried to incorporate this volume manipulation source into the program. When I tried to pass the callback function in, as per the example,
pMasterVolume->RegisterNotificationSink(MasterVolChanged, (DWORD) 0);
 
I get the following error:
error C2664: 'RegisterNotificationSink' : cannot convert parameter 1 from 'void (unsigned long,unsigned long)' to 'void (__stdcall *)
(unsigned long,unsigned long)'
None of the functions with this name in scope match the target type
 
I seem to have followed your instructions, can anyone tell me where I might be going wrong?

 
Thanks,
 
Dave
GeneralRe: Problem passing callback function in...memberladuran27 Aug '04 - 12:55 
Make sure you declared the function as CALLBACK which boils down to __stdcall
GeneralRe: Problem passing callback function in...memberVinod Vijayan29 Jan '05 - 0:06 
Hi el davo,
This is a late post but i am sending this for the benefit of others who may
have the same problem as yours.
CALLBACK functions should never be member functions. They should be made
static or global.
You probably got the above error because yu made it a member function.

QuestionHow to determine whether volume is enabled?memberM A V22 Feb '04 - 9:54 
There are Enable() and Disable() methods.
But how to determine whether volume is currently enabled or disabled?
GeneralMany Compile Errors - microphone implementationmemberBobboots12 Feb '04 - 19:19 
I'm getting many (100+)compile Errors in VolumeInXXX
 
WAVEFORMATEX, HWAVEIN, MMRESULT, HMIXEROBJ, etc.
 
I saw an earlier post with this question, but no reply. Is there a library or a header file that I'm missing?
 
I put these in the VolumeInXXX.cpp file
#pragma comment(lib, "winmm.lib") //RB413 For MicVolume Function
#include "StdAfx.h"
#include "IVolume.h"
#include "VolumeInXXX.h"
 
And put these toward the end of stdafx.h
#include "windows.h"
#include "mmSystem.h" //RB413 Added for Mic Volume Routine
#pragma comment(lib, "winmm.lib")
 

Any help is appreciated.
----------------------------------------------------------------
 
The Microphone control code is as follows (yet to be tested)
 
#pragma comment(lib, "winmm.lib") //RB413 For MicVolume Function
#include "Ivolume.h"
#include "VolumeInXXX.h"
extern Ivolume;
extern CVolumeInXXX;
bool CALLBACK EnumInputLineProc( UINT uLineIndex, MIXERLINE* pLineInfo, DWORD dwUserValue );
 
void Class::SetMicVolume(int uLineIndex)
{
// set the microphone volume in 1% increments (Range 0 - 100)
// Initialization
UINT uMicrophoneLineIndex = (UINT)-1;
if ( !CVolumeInXXX::EnumerateInputLines( EnumInputLineProc, (DWORD)&uMicrophoneLineIndex ) )
{
// handle error
}
if ( uMicrophoneLineIndex == (UINT)-1 )
{
// Error: mic volume'ing is not available.
}
IVolume* pMicrophoneVolume = (IVolume*)new CVolumeInXXX( uMicrophoneLineIndex );
if ( !pMicrophoneVolume || !pMicrophoneVolume->IsAvailable() )
{
// handle error
}
// Go on and use pMicrophoneVolume to manipulate the volume

pMicrophoneVolume->Enable();
pMicrophoneVolume->SetCurrentVolume((DWORD) 655 * uLineIndex); //65535 = max
 

}
 
bool CALLBACK EnumInputLineProc( UINT uLineIndex, MIXERLINE* pLineInfo, DWORD dwUserValue )
{
if ( pLineInfo->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE )
{
*((UINT*)dwUserValue) = uLineIndex;
return false;
}
return true;
}

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 17 Nov 1999
Article Copyright 1999 by Alex Chmut
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid