Introduction
This article is a brief explanation of how to use the mixerSetControlDetails
API for setting the master volume of the speakers. Sometimes it is not enough to merely set the volume of the waveform-audio (or MIDI) output device.
Setting the volume
MMRESULT result;
result = waveOutSetVolume(0, 0x48444844);
If only it was that easy! Sometimes, the easiest-sounding task does not always equate to the most straight-forward code. While this does alter the speaker's volume, it does so in the context of the waveform-audio output device. What we want is the ability to set the master volume regardless of the output device.
The first thing we need to do is obtain a handle to the mixer device.
MMRESULT result;
HMIXER hMixer;
result = mixerOpen(&hMixer, MIXER_OBJECTF_MIXER, 0, 0, 0);
Next, we need to get the speaker line of the mixer device.
MIXERLINE ml = {0};
ml.cbStruct = sizeof(MIXERLINE);
ml.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
result = mixerGetLineInfo((HMIXEROBJ) hMixer,
&ml, MIXER_GETLINEINFOF_COMPONENTTYPE);
Next, we need to get the volume control of the speaker line.
MIXERLINECONTROLS mlc = {0};
MIXERCONTROL mc = {0};
mlc.cbStruct = sizeof(MIXERLINECONTROLS);
mlc.dwLineID = ml.dwLineID;
mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
mlc.cControls = 1;
mlc.pamxctrl = &mc;
mlc.cbmxctrl = sizeof(MIXERCONTROL);
result = mixerGetLineControls((HMIXEROBJ) hMixer,
&mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE);
We now have everything identified. All that's left is to actually set the volume level.
MIXERCONTROLDETAILS mcd = {0};
MIXERCONTROLDETAILS_UNSIGNED mcdu = {0};
mcdu.dwValue = 18500;
mcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
mcd.hwndOwner = 0;
mcd.dwControlID = mc.dwControlID;
mcd.paDetails = &mcdu;
mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
mcd.cChannels = 1;
result = mixerSetControlDetails((HMIXEROBJ) hMixer,
&mcd, MIXER_SETCONTROLDETAILSF_VALUE);
That's it! If you want to mute the speakers, simply set the value to 0. If you want to test the elasticity of the speaker cones, set the value to 65535!
Obtaining the current speaker volume is just as easy, or at least it uses most of the same code! Simply call mixerGetControlDetails()
instead and do not assign a value to the dwValue
member of the MIXERCONTROLDETAILS_UNSIGNED
structure. It will contain the current speaker volume upon successful return from mixerGetControlDetails()
.
Mute me, baby!
Using the above code, you can mute the master speakers by using a value of 0 for mcdu.dwValue
. However, there is another method to do it correctly. The control type used is MIXERCONTROL_CONTROLTYPE_MUTE
instead of MIXERCONTROL_CONTROLTYPE_VOLUME
. The structure used will be MIXERCONTROLDETAILS_BOOLEAN
instead of MIXERCONTROLDETAILS_UNSIGNED
. The value of the fValue
member is 1 to mute the speakers and 0 to unmute them. The code looks like:
mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
result = mixerGetLineControls((HMIXEROBJ) hMixer,
&mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE);
MIXERCONTROLDETAILS_BOOLEAN mcb = {0};
mcb.fValue = true;
mcd.paDetails = &mcb;
mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
result = mixerSetControlDetails((HMIXEROBJ) hMixer,
&mcd, MIXER_SETCONTROLDETAILSF_VALUE);
Notes
The code was not tested for all possible audio combinations, but is merely showing how to use a particular API. My development machine has only the default sound card, and some $19 speakers! Not exactly an audio mixer's dream!
There are a few other CP articles that attack this problem differently, or that provide different levels of detail. I was not aware of them when I wrote this article, and simply searched CP for mixerSetControlDetails
only to find no matches.