Setting volume levels with IAudioVolumeLevel
Hello,
I've been trying to set the level of a particular part by using the IAudioVolumeLevel interface of the API. I can Activate a valid instance of the interface, and when I call GetLevelRange on it, I get a min value of -34.5, a max value of 12, and a step value of 1.5. However if I loop through the valid step values and set them with SetLevelUniform, while comparing the value of the volume level slider in the Control Panel (which goes from 1-100), I get unexpected results. The number on the slider (and volume from the speaker) remains at 0 for the first 8 iterations of the loop until I get to a level of -24 for the SetLevelUniform call, at which point the slider value moves to 1, where it remains until the 8th iteration in the loop when the level parameter is at -19.5, and the slider moves to 2. As the loop progresses the jumps in the volume start happening on every iteration, and before long the volume slider is moving up by 10 or more units at a time, the size of the jump growing exponentially. If I manually move the slider from 0 to 100 however, you can tell that there are far more nuances in the scale than I am able to achieve by calling SetLevel or SetLevelUniform. Is there another call I can use to get these settings, similar to the GetMasterVolumeLevelScalar function?
Here is a short excerpt of code from my test:
IAudioVolumeLevel* pVolControl=NULL;
... // Code that gets pVolControl
UINT channelCount=0;
hr=pVolControl->GetChannelCount(&channelCount);
if ((hr==S_OK) && (channelCount>0))
{
float min=0,max=0,step=0;
hr=pVolControl->GetLevelRange(0,&min,&max,&step);
if (hr==S_OK)
{
for (float level=min;level<=max;level+=step)
hr=pVolControl->SetLevelUniform(level,NULL);
}
}
Thanks for any help you can offer,
Scott
[1912 byte] By [
Sc0tty] at [2008-1-9]
Two things: First off: why are you using the IAudioVolumeLevel interface? Why doesn't the IAudioEndointVolume interface suffice?
Secondly: The IAudioVolumeLevel API rounds dB values to the nearest .5 (because of some issues with certain devices).
If you're comparing the results of the IAudioVolumeLevel with the master volume slider (in the volume app), you're seeing the effect of Vista's built-in volume taper - the values used by the slider aren't true dB values, they go through a taper to make the perceived loudness more linear. If, on the other hand, you're comparing with the levels tab in mmsys.cpl, I'm surprised.
Hi Larry,
Thanks for the reply. I'm using IAudioVolumeLevel interface because it seemed my only option. I'm trying to set the value for the loopback volume of various input sources as described in the "Enumerating Audio Source Lines in Vista ?" thread. The only way I could get to the controls was by enumerating the incoming parts of the "Speakers" endpoint until I found a part with the name of the desired input source. If I call Activate on that part, I can get a valid IAudioVolumeLevel interface that does effect the sound I hear through the speakers of the input source (a tuner on Line In for instance), but without much precision as I described. I'm comparing what I can set through that interface with how I can manually adjust the source input volume level on the sliders in the Control Panel->Sound->Playback Tab->Speakers->Properties->Levels Tab, where there is a master volume slider as well as sliders for each of the input sources. If I enumerate all the endpoints looking for the input and activate a IAudioEndpointVolume interface, I am adjusting the wrong volume -- recording volume level instead of playback. I know I have the right IPart -- is there another interface I can activate?
Thanks,
Scott
Ok - I just wanted to make sure you weren't trying to do that on the render output or capture input (it won't work on many new machines, including the laptop on which I'm typing this response because they don't have hardware volume controls).
There are two possibilities: #1 is that the driver is reporting invalid information (we know of several that do). The volume range you see is exactly what the driver reported to Windows, if the driver is lying, that's what you get.
The other possibility is that you're seeing the effect of the .5dB rounding that is done in Vista by the IAudioVolumeLevel interface (the rounding was added to compensate for drivers that misreported the actual dB levels that were allowed by the drivers).
Btw, the CPL you're using also uses the exact same interface, so it should report essentially the same values.
Hmmm. I'm guessing CPL is control panel? If the sliders in the control panel are using the IAudioVolumeLevel interface then I should be able to achieve any level I get by manually positioning the slider by using the function calls from the interface, right? I do not have nearly as fine of control programmatically with this interface as I do with the slider. Could it be there is another IPart I should be activating the interface on? As I've stated, I thought I had the right one just because I can change the volume of the appropriate input with it, just not nearly precise enough.
Thanks,
Scott
Yes, CPL is control panel.
The IAudioVolumeLevel interface directly controls the volume control on the audio device. Many audio devices only support a limited number of steps on their volume control so it's entirely possible that you don't have a very fine grained control - you can use the GetLevelRange to determine the range and step count that's supported by the audio device.
Note that some devices support rather "unique" dB ranges - I have one in my office that claims to support -10dB to 40dB in 10dB increments for example.
Hi Larry,
Thanks for the response. I guess what I'm not understanding is why I can achieve a more finely grained control of the device through the CPL than I can with IAudioVolumeLevel interface. It seems you are telling me that the IAudioVolumeLevel interface should be able to set the volume to all possible levels for the device, yet as I've described, when I use GetLevelRange to determine the valid range and steps, and then use SetLevelUniform to set the volume to each level, the slider value in the CPL and the perceived volume jumps exponentially, skipping many steps that can be set using the CPL slider manually. Any thoughts?
Thanks,
Scott
The volume slider on the levels tab interacts with the IAudioVolumeLevel.
If you're thinking about the master volume slider in sndvol (not the levels tab in mmsys.cpl), that's expected - the master volume slider does not represent the actual volume control, instead the position is run through a volume taper to provide a more linear volume experience. In addition, the master volume slider in sndvol remembers the last position set and doesn't have the "snap-to" behavior that you see with the IAudioVolumeLevel API.
Hi Larry,
I'm not sure where to go from here. I am talking about the slider on the levels tab. Incrementing the volume level one step at a time (each step with a value of 1.5 according to the GetLevelRange call) causes the slider on the levels tab to jump significantly when I reach the upper end of the scale. For example, I can manually set the slider to a value of 80 (of 100), but calling SetUniformLevel to one particular level will set the slider to 72, and calling it again with the a level one step higher jumps the slider up to a level of 90. If it is true that the slider is interacting with the same interface calls, it must be operating on a different part, right?
-Scott
Here's something to try - right click on the volume value (in the edit control) and change it from linear to decibels. The IAudioVolumeLevel API takes its input in decibels, it might be that this is part of the problem you're seeing.
Hi Larry,
Changing the unit display does make the problem easier to describe. On the lower end of the volume scale the decible increments on the slider are large. For instance, a slider setting of 2 percent is equal to to a slider setting of -20.1 dB, and a slider setting of 3% = -17.2 dB, for a delta of 2.9 dB. As the volume goes up, the delta becomes much smaller (83%=10.4dB, 84%=10.5dB, delta=.1dB). The step value that I can use according to GetLevelRange is 1.5 dB, so I have no way to get to these finer settings at the high end. When I move the slider across these settings that I cannot reach via the function call, I can discern changes in the volume from the speaker, so I know that they are valid settings for the device. If I ignore the reported 1.5dB step value and use a parameter in between, the volume is just set to the nearest 1.5dB mark.
Thanks,
Scott
Hi Scott.
Now that you have the CPL in "dB mode", do the values there agree with what your program is setting? i.e. when you programatically sweep from -34.5dB to +12dB does the slider in the CPL sweep monotonically (maybe put a Sleep(200) in your program to see or step through in a debugger).
Unless I'm missing something here you are just seeing the effects of conversion between dB and amplitude scalar values. There's really no way around this because the device only gives us 1.5dB resolution. 1.5dB resolution is really fine near -34.5dB and really coarse at +12dB. Maybe it would be nice if hardware volumes were stepped with respect to amplitude scalar values rather than dB since volume controls are usually tapered to something closer to a scalar space. Unfortunately this is what we're stuck with.
Regards,
Mitch Rundle
Microsoft
Hi Mitch,
Yes, my program is setting the volumes properly and the CPL display agrees with what I think I am setting. I understand that the 1.5dB resolution will cause the behavior that I'm seeing, what I don't understand is why I have a resolution of .1dB from the CPL, and the device reacts audibly to changes of much less than 1.5dB (ie I can hear several differences in the audio level when changing the level in the CPL 1.5dB, .1dB at a time.)
-Scott
Scott,
Are you running the final RTM build of Vista? If I recall correctly, this sounds similar to a bug that was fixed fairly late before RTM.
Frank Yerrace MS
Hi Frank,
I believe I'm running the final RTM build -- I pulled it from the MSDN site last month. How can I be sure it is the final build?
Thanks,
Scott