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   
GeneralSolved Compile - microphone vol does not change...sussAnonymous13 Feb '04 - 16:40 
I resolved the compile errors by putting the following lines into the VolumeInXXX.h file.
#include "windows.h"
#include "mmSystem.h"
 
I verified that the Microphone hardware was detected and that the value I set is actually changed. However, the Mic volume does not change.
 
This Is XP with SP1. Any thoughts?
 
The code I used to check the response, then change the volume is shown below:
pMicrophoneVolume->Enable();
pMicrophoneVolume->GetVolumeMetric();
pMicrophoneVolume->GetMinimalVolume();
pMicrophoneVolume->GetMaximalVolume();
pMicrophoneVolume->GetCurrentVolume();
pMicrophoneVolume->SetCurrentVolume((DWORD) (655 * volume)); //where volume is an int between 0 and 100
 
~Bob
Generalline-in playbacksussAnonymous12 Feb '04 - 10:53 
Finally got the code working... Smile | :)
 
But I want to be able to control the volume of the playback of the line-in (not recording)
 
CVolumeOutWave does the wave... how can I make it work for line-in (or microphone?)
GeneralRe: line-in playbackmemberSaber00119 Jul '06 - 0:13 
class SoundMan
{
protected:
MMRESULT result;
HMIXER hMixer;
MIXERLINE Line_In;
MIXERLINECONTROLS Line_In_cont;
MIXERCONTROL LineC;
MIXERCONTROLDETAILS LineCd;
MIXERCONTROLDETAILS_UNSIGNED LineVol;
public:
void initLine();
void initWav();
void setLineVol(int);
void setWave(int);
};
void SoundMan::initLine()
{
result = mixerOpen(&hMixer, MIXER_OBJECTF_MIXER, 0, 0, 0);
Line_In.cbStruct = sizeof(MIXERLINE);
Line_In.dwComponentType = 0x000010001L ;
result = mixerGetLineInfo((HMIXEROBJ) hMixer,
&Line_In, MIXER_GETLINEINFOF_COMPONENTTYPE);
 
Line_In_cont.cbStruct = sizeof(MIXERLINECONTROLS);
Line_In_cont.dwLineID = Line_In.dwLineID;
Line_In_cont.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
Line_In_cont.cControls = 1;
Line_In_cont.pamxctrl = &LineC;
Line_In_cont.cbmxctrl = sizeof(MIXERCONTROL);
result = mixerGetLineControls((HMIXEROBJ) hMixer,
&Line_In_cont, MIXER_GETLINECONTROLSF_ONEBYTYPE);
}
void SoundMan::initWav()
{
result = mixerOpen(&hMixer, MIXER_OBJECTF_MIXER, 0, 0, 0);
Line_In.cbStruct = sizeof(MIXERLINE);
Line_In.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS ;
result = mixerGetLineInfo((HMIXEROBJ) hMixer,
&Line_In, MIXER_GETLINEINFOF_COMPONENTTYPE);
 


Line_In_cont.cbStruct = sizeof(MIXERLINECONTROLS);
Line_In_cont.dwLineID = Line_In.dwLineID;
Line_In_cont.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
Line_In_cont.cControls = 1;
Line_In_cont.pamxctrl = &LineC;
Line_In_cont.cbmxctrl = sizeof(MIXERCONTROL);
result = mixerGetLineControls((HMIXEROBJ) hMixer,
&Line_In_cont, MIXER_GETLINECONTROLSF_ONEBYTYPE);
}
void SoundMan::setWave(int perc)
{
LineVol.dwValue = (65535/100)*perc; // the volume is a number between 0 and 65535
LineCd.cbStruct = sizeof(MIXERCONTROLDETAILS);
LineCd.hwndOwner = 0;
LineCd.dwControlID = LineC.dwControlID;
LineCd.paDetails = &LineVol;
LineCd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
LineCd.cChannels = 1;
result = mixerSetControlDetails((HMIXEROBJ) hMixer,
&LineCd, MIXER_SETCONTROLDETAILSF_VALUE);
}
 
void SoundMan::setLineVol(int perc)
{
LineVol.dwValue = (65535/100)*perc; // the volume is a number between 0 and 65535
LineCd.cbStruct = sizeof(MIXERCONTROLDETAILS);
LineCd.hwndOwner = 0;
LineCd.dwControlID = LineC.dwControlID;
LineCd.paDetails = &LineVol;
LineCd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
LineCd.cChannels = 1;
result = mixerSetControlDetails((HMIXEROBJ) hMixer,
&LineCd, MIXER_SETCONTROLDETAILSF_VALUE);
}>

 
works perfectly with ac'97 cards
won't work on CMedia cards still working on it...

GeneralRe: line-in playbackmemberwanhaven23 Feb '07 - 1:38 
Hi,
 
I have tried it in Windows XP with service pack 2 and I think it doesn't work, I can compile and execute it, but then I can't see in the volume settings if the volume of the microphone has changed. Maybe I am doing something wrong, have you got the same problem?
 
I hope you can help me.
 
Thanks.
GeneralDisplaying input audio level in progress barmemberHridaynath Musale10 Feb '04 - 13:24 
Hi,
 
I need to display the volume meter of current audio level coming from input devices like Microphone, Sound Card. The volume level will be displayed between 1 to 100 %. I am little new to MFC and I want to use your VolumeInXXX classes.
 
Can you please provide any sample application on How to use your classes to get the audio level from microphone at regular timer interval say 1 second and display the current volume level in Progress bar?
 
Thanks

GeneralFound bug. It should called the destructor.membersaintstone1 Feb '04 - 17:28 
Hi. I'm pleased for your article. Thanks Alex. Smile | :)
I have made AVWizard to ActiveX with your classes, But when AVWizard host IE is closed, it occur unexpected error. So I found that the CVolumeOutMaster's destructor didn't called. Because IVolume interface hasn't the virtual destructor. So I modified the IVolume interface.
 
insert this line into the IVolume interface.
 
virtual ~IVolume() {}
 


GeneralThanks!membersparky90923 Oct '03 - 1:05 
Thanks for the code, all I wanted to write was a couple of line command line EXE that set my main volume to 5% of the maximum (as my sound-card drivers default to 100% for some reason - and until new ones come out I'm in danger of getting 'BLASTED' in my ears when I forget!!
 
This is it:
 
int _tmain(int argc, _TCHAR* argv[])
{
// set the mixer master volume to 5% of max
IVolume* pMasterVolume = (IVolume*)new CVolumeOutMaster();
pMasterVolume->SetCurrentVolume( (DWORD)65535 * 0.05f );
 
return 0;
}
 
Thanks very much! Smile | :)
GeneralRe: Thanks!sussAnonymous8 Aug '04 - 4:33 
Do not forget to delete the IVolume instance before returning. Wink | ;)
GeneralmmSystem.hmemberdietercools20 Sep '03 - 13:02 
Hi, first thing to say: i'm a newbie so don't know a lot. I would like to use your program, and followed your instructions. But when I import mmsystem.h he gives 89 errors when trying to build. I use .NET
GeneralRe: mmSystem.hmembersparky90923 Oct '03 - 0:57 
try to include the main windows header before mmsystem.h, as in my stdafx.h i have:
 
#include "windows.h"
#include "mmsystem.h"

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

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