Search code examples
c++windowsaudiovolume

What logarithmic function do windows use for the audio volume slider?


I am currently struggling with the implementation of my audio volume slider in my c++ app.

The app is able to control the windows mixer volume level and the slider has the range 0.0f to 1.0f.

The problem I am facing is that my DB values aren't equal to the DB values windows is using.

Here are some values I set with my volume slider with the resulting DB values and the ones windows is using.

enter image description here

Below is the function I use for calculating the audio DB level. What am I doing wrong here?

Thank you in advance!

    if (this->m_useAudioEndpointVolume)
    {
        const float slider_min = 0.0f;
        const float slider_max = 1.0f;
        const float logBase = 10;

        m_ignoreAudioValue = TRUE;

        if (volume >= 1.0f) {
            volume = 1.0f;
        }

        if (volume <= 0.0f) {
            volume = 0.0f;
        }

        float pfLevelMinDB = 0;
        float pfLevelMaxDB = 0;
        float pfVolumeIncrementDB = 0;
        m_pEndpointVolume->GetVolumeRange(&pfLevelMinDB, &pfLevelMaxDB, &pfVolumeIncrementDB);

        // Logarithmic formula for audio volume
        // Volume = log(((Slider.Value-Slider.Min)*(B-1))/(Slider.Max-Slider.Min) + 1)/log(B) * (Volume.Max - Volume.Min) + Volume.Min
        float calcVolume = log(((volume - slider_min)*(logBase - 1)) / (slider_max - slider_min) + 1) / log(logBase) * (pfLevelMaxDB - pfLevelMinDB) + pfLevelMinDB;

        if (volume == 0.0f) {
            m_pEndpointVolume->SetMute(TRUE, NULL);
        }
        else
        {
            m_pEndpointVolume->SetMute(FALSE, NULL);
        }

        float currentValue = 0.0f;

        m_pEndpointVolume->GetMasterVolumeLevel(&currentValue);

        // Todo: The calculation has to be logarithmic
        m_pEndpointVolume->SetMasterVolumeLevel(calcVolume, NULL);
    }

Solution

  • I found the solution.

    The IAudioEndpointVolume has the function SetMasterVolumeLevelScalar. This function uses the range from 0.0 to 1.0 regarding to the MSDN documentation so you don't need to implement a logarithmic function yourself. Seems like I overlooked this one.

    Here's the current code sample I am using in case someone will need it in the future.

    float pLevel = 0.0f;
    m_pEndpointVolume->GetMasterVolumeLevelScalar(&pLevel);
    
    // We have to set this first to TRUE to an avoid unnecessary callback
    m_ignoreAudioValue = TRUE;
    // Set the scalar value
    // https://msdn.microsoft.com/de-de/library/windows/desktop/dd368062(v=vs.85).aspx
    m_pEndpointVolume->SetMasterVolumeLevelScalar(sliderValue, NULL);
    
    // We have to set this again to TRUE to avoid an unnecessary callback
    // because the SetMasterVolumeLevelScalar triggers the OnNotify event
    // and this causes the m_ignoreAudioValue to be FALSE again.
    m_ignoreAudioValue = TRUE;
    // If the value is higher the 0.0 unmute the master volume.
    m_pEndpointVolume->SetMute(sliderValue > 0.0f ? FALSE : TRUE, NULL);
    
    m_pEndpointVolume->GetMasterVolumeLevelScalar(&pLevel);
    

    Edit:

    It seems like Windows is using a linear volume slider. Thats the reason why 2% in Windows feels still too loud and everything above 50% isn't much of an increase anymore.

    Here's a really good article about it why you should avoid it.

    Volume Controls