Changing Master Volume Level

Changing master volume level

Okay, here goes:

const int MAXPNAMELEN            = 32;
const int MIXER_SHORT_NAME_CHARS = 16;
const int MIXER_LONG_NAME_CHARS = 64;

[Flags] enum MIXERLINE_LINEF : uint{
ACTIVE = 0x00000001,
DISCONNECTED = 0x00008000,
SOURCE = 0x80000000
}
[Flags] enum MIXER : uint{
GETLINEINFOF_DESTINATION = 0x00000000,
GETLINEINFOF_SOURCE = 0x00000001,
GETLINEINFOF_LINEID = 0x00000002,
GETLINEINFOF_COMPONENTTYPE = 0x00000003,
GETLINEINFOF_TARGETTYPE = 0x00000004,
GETLINEINFOF_QUERYMASK = 0x0000000F,

GETLINECONTROLSF_ALL = 0x00000000,
GETLINECONTROLSF_ONEBYID = 0x00000001,
GETLINECONTROLSF_ONEBYTYPE = 0x00000002,
GETLINECONTROLSF_QUERYMASK = 0x0000000F,

GETCONTROLDETAILSF_VALUE = 0x00000000,
GETCONTROLDETAILSF_LISTTEXT = 0x00000001,
GETCONTROLDETAILSF_QUERYMASK = 0x0000000F,

OBJECTF_MIXER = 0x00000000,
OBJECTF_WAVEOUT = 0x10000000,
OBJECTF_WAVEIN = 0x20000000,
OBJECTF_MIDIOUT = 0x30000000,
OBJECTF_MIDIIN = 0x40000000,
OBJECTF_AUX = 0x50000000,
OBJECTF_HANDLE = 0x80000000,
OBJECTF_HMIXER = OBJECTF_HANDLE | OBJECTF_MIXER,
OBJECTF_HWAVEOUT = OBJECTF_HANDLE | OBJECTF_WAVEOUT,
OBJECTF_HWAVEIN = OBJECTF_HANDLE | OBJECTF_WAVEIN,
OBJECTF_HMIDIOUT = OBJECTF_HANDLE | OBJECTF_MIDIOUT,
OBJECTF_HMIDIIN = OBJECTF_HANDLE | OBJECTF_MIDIIN
}
[Flags] enum MIXERCONTROL_CT : uint{
CLASS_MASK = 0xF0000000,
CLASS_CUSTOM = 0x00000000,
CLASS_METER = 0x10000000,
CLASS_SWITCH = 0x20000000,
CLASS_NUMBER = 0x30000000,
CLASS_SLIDER = 0x40000000,
CLASS_FADER = 0x50000000,
CLASS_TIME = 0x60000000,
CLASS_LIST = 0x70000000,

SUBCLASS_MASK = 0x0F000000,

SC_SWITCH_BOOLEAN = 0x00000000,
SC_SWITCH_BUTTON = 0x01000000,

SC_METER_POLLED = 0x00000000,

SC_TIME_MICROSECS = 0x00000000,
SC_TIME_MILLISECS = 0x01000000,

SC_LIST_SINGLE = 0x00000000,
SC_LIST_MULTIPLE = 0x01000000,

UNITS_MASK = 0x00FF0000,
UNITS_CUSTOM = 0x00000000,
UNITS_BOOLEAN = 0x00010000,
UNITS_SIGNED = 0x00020000,
UNITS_UNSIGNED = 0x00030000,
UNITS_DECIBELS = 0x00040000, /* in 10ths */
UNITS_PERCENT = 0x00050000, /* in 10ths */
}
[Flags] enum MIXERCONTROL_CONTROLTYPE : uint{
CUSTOM = MIXERCONTROL_CT.CLASS_CUSTOM | MIXERCONTROL_CT.UNITS_CUSTOM,
BOOLEANMETER = MIXERCONTROL_CT.CLASS_METER | MIXERCONTROL_CT.SC_METER_POLLED | MIXERCONTROL_CT.UNITS_BOOLEAN,
SIGNEDMETER = MIXERCONTROL_CT.CLASS_METER | MIXERCONTROL_CT.SC_METER_POLLED | MIXERCONTROL_CT.UNITS_SIGNED,
PEAKMETER = SIGNEDMETER + 1,
UNSIGNEDMETER = MIXERCONTROL_CT.CLASS_METER | MIXERCONTROL_CT.SC_METER_POLLED | MIXERCONTROL_CT.UNITS_UNSIGNED,
BOOLEAN = MIXERCONTROL_CT.CLASS_SWITCH | MIXERCONTROL_CT.SC_SWITCH_BOOLEAN | MIXERCONTROL_CT.UNITS_BOOLEAN,
ONOFF = BOOLEAN + 1,
MUTE = BOOLEAN + 2,
MONO = BOOLEAN + 3,
LOUDNESS = BOOLEAN + 4,
STEREOENH = BOOLEAN + 5,
BASS_BOOST = BOOLEAN + 0x00002277,
BUTTON = MIXERCONTROL_CT.CLASS_SWITCH | MIXERCONTROL_CT.SC_SWITCH_BUTTON | MIXERCONTROL_CT.UNITS_BOOLEAN,
DECIBELS = MIXERCONTROL_CT.CLASS_NUMBER | MIXERCONTROL_CT.UNITS_DECIBELS,
SIGNED = MIXERCONTROL_CT.CLASS_NUMBER | MIXERCONTROL_CT.UNITS_SIGNED,
UNSIGNED = MIXERCONTROL_CT.CLASS_NUMBER | MIXERCONTROL_CT.UNITS_UNSIGNED,
PERCENT = MIXERCONTROL_CT.CLASS_NUMBER | MIXERCONTROL_CT.UNITS_PERCENT,
SLIDER = MIXERCONTROL_CT.CLASS_SLIDER | MIXERCONTROL_CT.UNITS_SIGNED,
PAN = SLIDER + 1,
QSOUNDPAN = SLIDER + 2,
FADER = MIXERCONTROL_CT.CLASS_FADER | MIXERCONTROL_CT.UNITS_UNSIGNED,
VOLUME = FADER + 1,
BASS = FADER + 2,
TREBLE = FADER + 3,
EQUALIZER = FADER + 4,
SINGLESELECT = MIXERCONTROL_CT.CLASS_LIST | MIXERCONTROL_CT.SC_LIST_SINGLE | MIXERCONTROL_CT.UNITS_BOOLEAN,
MUX = SINGLESELECT + 1,
MULTIPLESELECT = MIXERCONTROL_CT.CLASS_LIST | MIXERCONTROL_CT.SC_LIST_MULTIPLE | MIXERCONTROL_CT.UNITS_BOOLEAN,
MIXER = MULTIPLESELECT + 1,
MICROTIME = MIXERCONTROL_CT.CLASS_TIME | MIXERCONTROL_CT.SC_TIME_MICROSECS | MIXERCONTROL_CT.UNITS_UNSIGNED,
MILLITIME = MIXERCONTROL_CT.CLASS_TIME | MIXERCONTROL_CT.SC_TIME_MILLISECS | MIXERCONTROL_CT.UNITS_UNSIGNED
}

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
struct MIXERLINE{
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct TargetInfo{
public uint dwType;
public uint dwDeviceID;
public ushort wMid;
public ushort wPid;
public uint vDriverVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAXPNAMELEN)]
public string szPname;
}

public uint cbStruct;
public uint dwDestination;
public uint dwSource;
public uint dwLineID;
public MIXERLINE_LINEF fdwLine;
public uint dwUser;
public uint dwComponentType;
public uint cChannels;
public uint cConnection;
public uint cControls;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MIXER_SHORT_NAME_CHARS)]
public string szShortName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MIXER_LONG_NAME_CHARS)]
public string szName;
public TargetInfo Target;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
struct MIXERCONTROL{
[StructLayout(LayoutKind.Explicit)]
public struct BoundsInfo{
[FieldOffset(0)]
public int lMinimum;
[FieldOffset(4)]
public int lMaximum;
[FieldOffset(0)]
public uint dwMinimum;
[FieldOffset(4)]
public uint dwMaximum;
[FieldOffset(8), MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
public uint[] dwReserved;
}
[StructLayout(LayoutKind.Explicit)]
public struct MetricsInfo{
[FieldOffset(0)]
public uint cSteps;
[FieldOffset(0)]
public uint cbCustomData;
[FieldOffset(4), MarshalAs(UnmanagedType.ByValArray, SizeConst=5)]
public uint[] dwReserved;
}

public uint cbStruct;
public uint dwControlID;
public MIXERCONTROL_CONTROLTYPE dwControlType;
public uint fdwControl;
public uint cMultipleItems;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MIXER_SHORT_NAME_CHARS)]
public string szShortName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MIXER_LONG_NAME_CHARS)]
public string szName;
public BoundsInfo Bounds;
public MetricsInfo Metrics;
}
[StructLayout(LayoutKind.Explicit)]
struct MIXERLINECONTROLS{
[FieldOffset(0)]
public uint cbStruct;
[FieldOffset(4)]
public uint dwLineID;
[FieldOffset(8)]
public uint dwControlID;
[FieldOffset(8)] // not a typo! overlaps previous field
public uint dwControlType;
[FieldOffset(12)]
public uint cControls;
[FieldOffset(16)]
public uint cbmxctrl;
[FieldOffset(20)]
public IntPtr pamxctrl;
}
[StructLayout(LayoutKind.Explicit)]
struct MIXERCONTROLDETAILS{
[FieldOffset(0)]
public uint cbStruct;
[FieldOffset(4)]
public uint dwControlID;
[FieldOffset(8)]
public uint cChannels;
[FieldOffset(12)]
public IntPtr hwndOwner;
[FieldOffset(12)] // not a typo!
public uint cMultipleItems;
[FieldOffset(16)]
public uint cbDetails;
[FieldOffset(20)]
public IntPtr paDetails;
}
[StructLayout(LayoutKind.Sequential)]
struct VOLUME{
public int left;
public int right;
}
struct MixerInfo{
public uint volumeCtl;
public uint muteCtl;
public int minVolume;
public int maxVolume;
}

[DllImport("WinMM.dll", CharSet=CharSet.Auto)]
static extern uint mixerGetLineInfo (IntPtr hmxobj, ref MIXERLINE pmxl, MIXER flags);

[DllImport("WinMM.dll", CharSet=CharSet.Auto)]
static extern uint mixerGetLineControls (IntPtr hmxobj, ref MIXERLINECONTROLS pmxlc, MIXER flags);

[DllImport("WinMM.dll", CharSet=CharSet.Auto)]
static extern uint mixerGetControlDetails(IntPtr hmxobj, ref MIXERCONTROLDETAILS pmxcd, MIXER flags);

[DllImport("WinMM.dll", CharSet=CharSet.Auto)]
static extern uint mixerSetControlDetails(IntPtr hmxobj, ref MIXERCONTROLDETAILS pmxcd, MIXER flags);

static MixerInfo GetMixerControls(){
MIXERLINE mxl = new MIXERLINE();
MIXERLINECONTROLS mlc = new MIXERLINECONTROLS();
mxl.cbStruct = (uint)Marshal.SizeOf(typeof(MIXERLINE));
mlc.cbStruct = (uint)Marshal.SizeOf(typeof(MIXERLINECONTROLS));

mixerGetLineInfo(IntPtr.Zero, ref mxl, MIXER.OBJECTF_MIXER | MIXER.GETLINEINFOF_DESTINATION);

mlc.dwLineID = mxl.dwLineID;
mlc.cControls = mxl.cControls;
mlc.cbmxctrl = (uint)Marshal.SizeOf(typeof(MIXERCONTROL));
mlc.pamxctrl = Marshal.AllocHGlobal((int)(mlc.cbmxctrl * mlc.cControls));

mixerGetLineControls(IntPtr.Zero, ref mlc, MIXER.OBJECTF_MIXER | MIXER.GETLINECONTROLSF_ALL);

MixerInfo rtn = new MixerInfo();

for(int i = 0; i < mlc.cControls; i++){
MIXERCONTROL mxc = (MIXERCONTROL)Marshal.PtrToStructure((IntPtr)((int)mlc.pamxctrl + (int)mlc.cbmxctrl * i), typeof(MIXERCONTROL));
switch(mxc.dwControlType){
case MIXERCONTROL_CONTROLTYPE.VOLUME:
rtn.volumeCtl = mxc.dwControlID;
rtn.minVolume = mxc.Bounds.lMinimum;
rtn.maxVolume = mxc.Bounds.lMaximum;
break;
case MIXERCONTROL_CONTROLTYPE.MUTE:
rtn.muteCtl = mxc.dwControlID;
break;
}
}

Marshal.FreeHGlobal(mlc.pamxctrl);

return rtn;
}
static VOLUME GetVolume(MixerInfo mi){
MIXERCONTROLDETAILS mcd = new MIXERCONTROLDETAILS();
mcd.cbStruct = (uint)Marshal.SizeOf(typeof(MIXERCONTROLDETAILS));
mcd.dwControlID = mi.volumeCtl;
mcd.cMultipleItems = 0;
mcd.cChannels = 2;
mcd.cbDetails = (uint)Marshal.SizeOf(typeof(int));
mcd.paDetails = Marshal.AllocHGlobal((int)mcd.cbDetails);

mixerGetControlDetails(IntPtr.Zero, ref mcd, MIXER.GETCONTROLDETAILSF_VALUE | MIXER.OBJECTF_MIXER);

VOLUME rtn = (VOLUME)Marshal.PtrToStructure(mcd.paDetails, typeof(VOLUME));

Marshal.FreeHGlobal(mcd.paDetails);

return rtn;
}
static bool IsMuted(MixerInfo mi){
MIXERCONTROLDETAILS mcd = new MIXERCONTROLDETAILS();
mcd.cbStruct = (uint)Marshal.SizeOf(typeof(MIXERCONTROLDETAILS));
mcd.dwControlID = mi.muteCtl;
mcd.cMultipleItems = 0;
mcd.cChannels = 1;
mcd.cbDetails = 4;
mcd.paDetails = Marshal.AllocHGlobal((int)mcd.cbDetails);

mixerGetControlDetails(IntPtr.Zero, ref mcd, MIXER.GETCONTROLDETAILSF_VALUE | MIXER.OBJECTF_MIXER);

int rtn = Marshal.ReadInt32(mcd.paDetails);

Marshal.FreeHGlobal(mcd.paDetails);

return rtn != 0;
}
static void AdjustVolume(MixerInfo mi, int delta){
VOLUME volume = GetVolume(mi);

if(delta > 0){
volume.left = Math.Min(mi.maxVolume, volume.left + delta);
volume.right = Math.Min(mi.maxVolume, volume.right + delta);
}else{
volume.left = Math.Max(mi.minVolume, volume.left + delta);
volume.right = Math.Max(mi.minVolume, volume.right + delta);
}

SetVolume(mi, volume);
}
static void SetVolume(MixerInfo mi, VOLUME volume){
MIXERCONTROLDETAILS mcd = new MIXERCONTROLDETAILS();
mcd.cbStruct = (uint)Marshal.SizeOf(typeof(MIXERCONTROLDETAILS));
mcd.dwControlID = mi.volumeCtl;
mcd.cMultipleItems = 0;
mcd.cChannels = 2;
mcd.cbDetails = (uint)Marshal.SizeOf(typeof(int));
mcd.paDetails = Marshal.AllocHGlobal((int)mcd.cbDetails);

Marshal.StructureToPtr(volume, mcd.paDetails, false);

mixerSetControlDetails(IntPtr.Zero, ref mcd, MIXER.GETCONTROLDETAILSF_VALUE | MIXER.OBJECTF_MIXER);

Marshal.FreeHGlobal(mcd.paDetails);
}
static void SetMute(MixerInfo mi, bool mute){
MIXERCONTROLDETAILS mcd = new MIXERCONTROLDETAILS();
mcd.cbStruct = (uint)Marshal.SizeOf(typeof(MIXERCONTROLDETAILS));
mcd.dwControlID = mi.muteCtl;
mcd.cMultipleItems = 0;
mcd.cChannels = 1;
mcd.cbDetails = 4;
mcd.paDetails = Marshal.AllocHGlobal((int)mcd.cbDetails);

Marshal.WriteInt32(mcd.paDetails, mute ? 1 : 0);

mixerSetControlDetails(IntPtr.Zero, ref mcd, MIXER.GETCONTROLDETAILSF_VALUE | MIXER.OBJECTF_MIXER);

Marshal.FreeHGlobal(mcd.paDetails);
}

This code is huge and ugly. It's a translation of some C++ code, and with having to define all the P/Invoke stuff, it's a lot more code. But I've tested it, and it works. To use it, you simply need something like:

MixerInfo mi = GetMixerControls();
AdjustVolume(mi, 100); // add 100 to the current volume

or

MixerInfo mi = GetMixerControls();
AdjustVolume(mi, (mi.maxVolume - mi.minVolume) / 10); // increase the volume by 10% of total range

or

MixerInfo mi = GetMixerControls();
SetVolume(mi, mi.maxVolume); // let's get this party crunk'd!

or

MixerInfo mi = GetMixerControls();
SetMute(mi, true); // shhhh!!!!!!

WARNING

Due to the use of fixed-sized ints and field offsets, this may fail fantastically on 64-bit Windows. I don't know, I haven't tested it and haven't paid enough attention to know if these field sizes expand to 64 bits. caveat codor

EDIT

For the sake of simplicity (relatively speaking), I've left out any error handling. You should really check the return codes of all the mixerXXX functions, but I'll leave that as an exercise for the reader (read as: I was too lazy to do this).

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

How to get system volume level as a scalar from 0 to 100?

Ok guys, I've found the solution for my problem!

I had to call QueryInterface on the SessionControl to gain access to ISimpleAudioVolume where you can use the functions GetMasterVolume and SetMasterVolume. It's a 0-1 scalar, but you can just multiply it with 100 to get a 0-100 scalar. If the system's master volume is on 50%, you get a output of 1 if the program volume is on 50% too, so the output is based on the system's master volume!

Thanks for every comment and help!

Here the working code:

void getSessions() {
CoInitialize(NULL);

IMMDeviceEnumerator *pDeviceEnumerator;
IMMDevice *pDevice;
IAudioSessionManager2 *pAudioSessionManager2;
IAudioSessionEnumerator *pAudioSessionEnumerator;

CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&pDeviceEnumerator);
pDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);

pDevice->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, (void **) &pAudioSessionManager2);
pAudioSessionManager2->GetSessionEnumerator(&pAudioSessionEnumerator);

int nSessionCount;
pAudioSessionEnumerator->GetCount(&nSessionCount);

while (true) {
for (int nSessionIndex = 0; nSessionIndex < nSessionCount; nSessionIndex++) {
IAudioSessionControl *pSessionControl;
if (FAILED(pAudioSessionEnumerator->GetSession(nSessionIndex, &pSessionControl)))
continue;

ISimpleAudioVolume *pSimpleAudioVolume;
pSessionControl->QueryInterface(&pSimpleAudioVolume);

float fLevel;
pSimpleAudioVolume->GetMasterVolume(&fLevel);

std::cout << "fLevel Value: " << fLevel << std::endl;
}

Sleep(1000);
}

CoUninitialize();
}

change volume win32 c++

Two options:

  1. There's an answer to that question here on SO (changing the master volume from C++, which also includes SetMute, etc.)

  2. Have you considered showing the Volume controls and letting the user?
    If so, I can post some code for that. (You basically just shell out to the volume control applet.

getting Master audio volume

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



Related Topics



Leave a reply



Submit