Get Master Sound Volume in C#

Get Master Sound Volume in c#

You can get at these values using IAudioMeterInformation in the CoreAudio APIs in Vista and Win 7.

Managed wrappers are available in NAudio (get at the AudioMeterInformation from the MMDevice).

C# get master volume level/precent

I could not do it for all Windows versions (xp, vista & 7).

Though, I achieved it by used external programs, such as NirCmd, and sent the command I needed.

not so good solution but it did solve my problem.

How to Get and Set System Volume in Windows

This requires a plugin. Since this question is for Windows, you can use IAudioEndpointVolume to build a C++ plugin then call it from C#. This post has a working C++ example of how to change volume with IAudioEndpointVolume and you can use it as the base source to create the C++ plugin.


I've gone ahead and cleaned that code up then converted into a dll plugin and placed the DLL at Assets/Plugins folder. You can see how to build the C++ plugin here.

The C++ code:

#include "stdafx.h"
#include <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>

#define DLLExport __declspec(dllexport)

extern "C"
{
enum class VolumeUnit {
Decibel,
Scalar
};

//Gets volume
DLLExport float GetSystemVolume(VolumeUnit vUnit) {
HRESULT hr;

// -------------------------
CoInitialize(NULL);
IMMDeviceEnumerator *deviceEnumerator = NULL;
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
IMMDevice *defaultDevice = NULL;

hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
deviceEnumerator->Release();
deviceEnumerator = NULL;

IAudioEndpointVolume *endpointVolume = NULL;
hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
defaultDevice->Release();
defaultDevice = NULL;

float currentVolume = 0;
if (vUnit == VolumeUnit::Decibel) {
//Current volume in dB
hr = endpointVolume->GetMasterVolumeLevel(¤tVolume);
}

else if (vUnit == VolumeUnit::Scalar) {
//Current volume as a scalar
hr = endpointVolume->GetMasterVolumeLevelScalar(¤tVolume);
}
endpointVolume->Release();
CoUninitialize();

return currentVolume;
}

//Sets volume
DLLExport void SetSystemVolume(double newVolume, VolumeUnit vUnit) {
HRESULT hr;

// -------------------------
CoInitialize(NULL);
IMMDeviceEnumerator *deviceEnumerator = NULL;
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
IMMDevice *defaultDevice = NULL;

hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
deviceEnumerator->Release();
deviceEnumerator = NULL;

IAudioEndpointVolume *endpointVolume = NULL;
hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
defaultDevice->Release();
defaultDevice = NULL;

if (vUnit == VolumeUnit::Decibel)
hr = endpointVolume->SetMasterVolumeLevel((float)newVolume, NULL);

else if (vUnit == VolumeUnit::Scalar)
hr = endpointVolume->SetMasterVolumeLevelScalar((float)newVolume, NULL);

endpointVolume->Release();

CoUninitialize();
}
}

The C# code:

using System.Runtime.InteropServices;
using UnityEngine;

public class VolumeManager : MonoBehaviour
{
//The Unit to use when getting and setting the volume
public enum VolumeUnit
{
//Perform volume action in decibels</param>
Decibel,
//Perform volume action in scalar
Scalar
}

/// <summary>
/// Gets the current volume
/// </summary>
/// <param name="vUnit">The unit to report the current volume in</param>
[DllImport("ChangeVolumeWindows")]
public static extern float GetSystemVolume(VolumeUnit vUnit);
/// <summary>
/// sets the current volume
/// </summary>
/// <param name="newVolume">The new volume to set</param>
/// <param name="vUnit">The unit to set the current volume in</param>
[DllImport("ChangeVolumeWindows")]
public static extern void SetSystemVolume(double newVolume, VolumeUnit vUnit);

// Use this for initialization
void Start()
{
//Get volume in Decibel
float volumeDecibel = GetSystemVolume(VolumeUnit.Decibel);
Debug.Log("Volume in Decibel: " + volumeDecibel);

//Get volume in Scalar
float volumeScalar = GetSystemVolume(VolumeUnit.Scalar);
Debug.Log("Volume in Scalar: " + volumeScalar);

//Set volume in Decibel
SetSystemVolume(-16f, VolumeUnit.Decibel);

//Set volume in Scalar
SetSystemVolume(0.70f, VolumeUnit.Scalar);
}
}

The GetSystemVolume function is used to get the current volume and SetSystemVolume is used to set it. This question is for Windows but you can do a similar thing with the API for other platforms.

To set it to 70% when A key is pressed:

void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
//Set Windows Volume 70%
SetSystemVolume(0.70f, VolumeUnit.Scalar);
}
}

Change master audio volume from XP to Windows 8 in C#

So my solutions is to combine 2 projects:

  1. Mute/unmute, Change master volume in Windows 7 x64 with C#

  2. http://www.geekpedia.com/tutorial176_Get-and-set-the-wave-sound-volume.html

    The final code should be like (It uses NAudio framework)

        static class NativeMethods
    {

    [DllImport("winmm.dll", EntryPoint = "waveOutSetVolume")]
    public static extern int WaveOutSetVolume(IntPtr hwo, uint dwVolume);

    [DllImport("winmm.dll", SetLastError = true)]
    public static extern bool PlaySound(string pszSound, IntPtr hmod, uint fdwSound);
    }

    public static class MSWindowsFriendlyNames
    {
    public static string WindowsXP { get { return "Windows XP"; } }
    public static string WindowsVista { get { return "Windows Vista"; } }
    public static string Windows7 { get { return "Windows 7"; } }
    public static string Windows8 { get { return "Windows 8"; } }
    }

    public static class SistemVolumChanger
    {
    public static void SetVolume(int value)
    {
    if (value < 0)
    value = 0;

    if (value > 100)
    value = 100;

    var osFriendlyName = GetOSFriendlyName();

    if (osFriendlyName.Contains(MSWindowsFriendlyNames.WindowsXP))
    {
    SetVolumeForWIndowsXP(value);
    }
    else if (osFriendlyName.Contains(MSWindowsFriendlyNames.WindowsVista) || osFriendlyName.Contains(MSWindowsFriendlyNames.Windows7) || osFriendlyName.Contains(MSWindowsFriendlyNames.Windows8))
    {
    SetVolumeForWIndowsVista78(value);
    }
    else
    {
    SetVolumeForWIndowsVista78(value);
    }
    }

    public static int GetVolume()
    {
    int result = 100;
    try
    {
    MMDeviceEnumerator DevEnum = new MMDeviceEnumerator();
    MMDevice device = DevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
    result = (int)(device.AudioEndpointVolume.MasterVolumeLevelScalar * 100);
    }
    catch (Exception)
    {
    }

    return result;
    }

    private static void SetVolumeForWIndowsVista78(int value)
    {
    try
    {
    MMDeviceEnumerator DevEnum = new MMDeviceEnumerator();
    MMDevice device = DevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);

    device.AudioEndpointVolume.MasterVolumeLevelScalar = (float)value / 100.0f;
    }
    catch (Exception)
    {
    }
    }

    private static void SetVolumeForWIndowsXP(int value)
    {
    try
    {
    // Calculate the volume that's being set
    double newVolume = ushort.MaxValue * value / 10.0;

    uint v = ((uint)newVolume) & 0xffff;
    uint vAll = v | (v << 16);

    // Set the volume
    int retVal = NativeMethods.WaveOutSetVolume(IntPtr.Zero, vAll);
    }
    catch (Exception)
    {
    }
    }

    private static string GetOSFriendlyName()
    {
    string result = string.Empty;
    ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem");
    foreach (ManagementObject os in searcher.Get())
    {
    result = os["Caption"].ToString();
    break;
    }
    return result;
    }
    }

Update #1. Year 2015
Basically it uses NAudio framework. So nowdays some methods and properties of NAudio have other names.

For instance

eDataFlow.eRender is now DataFlow.Render

and

eRole.eMultimedia is Role.Multimedia

getting Master audio volume

No, current API does not allow you to do it.



Related Topics



Leave a reply



Submit