Question

Trying to write a PowerShell cmdlet that will mute the sound at start, unless already muted, and un-mute it at the end (only if it wasn't muted to begin with). Couldn't find any PoweShell or WMI object I could use. I was toying with using Win32 functions like auxGetVolume or auxSetVolume, but couldn't quite get it to work (how to read the values from an IntPtr?).

I'm using V2 CTP2. Any ideas folks?

Thanks!

Was it helpful?

Solution

There does not seem to be a quick and easy way to adjust the volume.. If you have c++ experience, you could do something with this blog post, where Larry Osterman describes how to call the IAudioEndpointVolume interface from the platform api(for Vista, XP might be more difficult from what I've found in a few searches).

V2 does allow you to compile inline code (via Add-Type), so that might be an option.

OTHER TIPS

Starting with Vista you have to use the Core Audio API to control the system volume. It's a COM API that doesn't support automation and thus requires a lot of boilerplate to use from .NET and PowerShell.

Anyways the code bellow let you access the [Audio]::Volume and [Audio]::Mute properties from PowerShell. This also work on a remote computer which could be useful. Just copy-paste the code in your PowerShell window.

Add-Type -TypeDefinition @'
using System.Runtime.InteropServices;

[Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAudioEndpointVolume {
  // f(), g(), ... are unused COM method slots. Define these if you care
  int f(); int g(); int h(); int i();
  int SetMasterVolumeLevelScalar(float fLevel, System.Guid pguidEventContext);
  int j();
  int GetMasterVolumeLevelScalar(out float pfLevel);
  int k(); int l(); int m(); int n();
  int SetMute([MarshalAs(UnmanagedType.Bool)] bool bMute, System.Guid pguidEventContext);
  int GetMute(out bool pbMute);
}
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDevice {
  int Activate(ref System.Guid id, int clsCtx, int activationParams, out IAudioEndpointVolume aev);
}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDeviceEnumerator {
  int f(); // Unused
  int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice endpoint);
}
[ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] class MMDeviceEnumeratorComObject { }

public class Audio {
  static IAudioEndpointVolume Vol() {
    var enumerator = new MMDeviceEnumeratorComObject() as IMMDeviceEnumerator;
    IMMDevice dev = null;
    Marshal.ThrowExceptionForHR(enumerator.GetDefaultAudioEndpoint(/*eRender*/ 0, /*eMultimedia*/ 1, out dev));
    IAudioEndpointVolume epv = null;
    var epvid = typeof(IAudioEndpointVolume).GUID;
    Marshal.ThrowExceptionForHR(dev.Activate(ref epvid, /*CLSCTX_ALL*/ 23, 0, out epv));
    return epv;
  }
  public static float Volume {
    get {float v = -1; Marshal.ThrowExceptionForHR(Vol().GetMasterVolumeLevelScalar(out v)); return v;}
    set {Marshal.ThrowExceptionForHR(Vol().SetMasterVolumeLevelScalar(value, System.Guid.Empty));}
  }
  public static bool Mute {
    get { bool mute; Marshal.ThrowExceptionForHR(Vol().GetMute(out mute)); return mute; }
    set { Marshal.ThrowExceptionForHR(Vol().SetMute(value, System.Guid.Empty)); }
  }
}
'@

Usage sample:

PS C:\> [Audio]::Volume         # Check current volume (now about 10%)
0,09999999
PS C:\> [Audio]::Mute           # See if speaker is muted
False
PS C:\> [Audio]::Mute = $true   # Mute speaker
PS C:\> [Audio]::Volume = 0.75  # Set volume to 75%
PS C:\> [Audio]::Volume         # Check that the changes are applied
0,75
PS C:\> [Audio]::Mute
True
PS C:\>

There are more comprehensive .NET wrappers out there for the Core Audio API if you need one but I'm not aware of a set of PowerShell friendly cmdlets.

P.S. Diogo answer seems clever but it doesn't work for me.

Use the following commands on a ps1 powershell script:

$obj = new-object -com wscript.shell 
$obj.SendKeys([char]173)

Alexandre's answer fit my situation, but his example does not work due to compilation errors regarding the namespace of 'var'. It seems that newer/different versions of .net may cause the example not to work. If you found that you received compilation errors, this is an alternative version to try for those cases:

Add-Type -Language CsharpVersion3 -TypeDefinition @'
using System.Runtime.InteropServices;

[Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAudioEndpointVolume {
  // f(), g(), ... are unused COM method slots. Define these if you care
  int f(); int g(); int h(); int i();
  int SetMasterVolumeLevelScalar(float fLevel, System.Guid pguidEventContext);
  int j();
  int GetMasterVolumeLevelScalar(out float pfLevel);
  int k(); int l(); int m(); int n();
  int SetMute([MarshalAs(UnmanagedType.Bool)] bool bMute, System.Guid pguidEventContext);
  int GetMute(out bool pbMute);
}
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDevice {
  int Activate(ref System.Guid id, int clsCtx, int activationParams, out IAudioEndpointVolume aev);
}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDeviceEnumerator {
  int f(); // Unused
  int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice endpoint);
}
[ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] class MMDeviceEnumeratorComObject { }

public class Audio {
  static IAudioEndpointVolume Vol() {
    var enumerator = new MMDeviceEnumeratorComObject() as IMMDeviceEnumerator;
    IMMDevice dev = null;
    Marshal.ThrowExceptionForHR(enumerator.GetDefaultAudioEndpoint(/*eRender*/ 0, /*eMultimedia*/ 1, out dev));
    IAudioEndpointVolume epv = null;
    var epvid = typeof(IAudioEndpointVolume).GUID;
    Marshal.ThrowExceptionForHR(dev.Activate(ref epvid, /*CLSCTX_ALL*/ 23, 0, out epv));
    return epv;
  }
  public static float Volume {
    get {float v = -1; Marshal.ThrowExceptionForHR(Vol().GetMasterVolumeLevelScalar(out v)); return v;}
    set {Marshal.ThrowExceptionForHR(Vol().SetMasterVolumeLevelScalar(value, System.Guid.Empty));}
  }
  public static bool Mute {
    get { bool mute; Marshal.ThrowExceptionForHR(Vol().GetMute(out mute)); return mute; }
    set { Marshal.ThrowExceptionForHR(Vol().SetMute(value, System.Guid.Empty)); }
  }
}
'@

Usage is the same:

PS C:\> [Audio]::Volume         # Check current volume (now about 10%)
0,09999999
PS C:\> [Audio]::Mute           # See if speaker is muted
False
PS C:\> [Audio]::Mute = $true   # Mute speaker
PS C:\> [Audio]::Volume = 0.75  # Set volume to 75%
PS C:\> [Audio]::Volume         # Check that the changes are applied
0,75
PS C:\> [Audio]::Mute
True
PS C:\>

If you can do it in C#, you can do it in PowerShell.

You could skin the cat another way by simply managing the Windows Audio Service. Stop it to mute, start it to unmute.

I didn't find how to do this in PowerShell, but there is a commandline utility called NirCmd that will do the trick by running this command:

C:\utils\nircmd.exe mutesysvolume 2

NirCmd is available for free here: http://www.nirsoft.net/utils/nircmd.html

Solution in vbscript:

Set WshShell = CreateObject("WScript.Shell")
For i = 0 To 50
  WshShell.SendKeys(chr(174))
  WScript.Sleep 100
Next

The keys reduce the volume by 2 each time.

I know it isn't PowerShell, but combining the answers from Michael and Diogo gives a one-line VBScript solution:

CreateObject("WScript.Shell").SendKeys(chr(173))

Slap this in mute.vbs, and you can just double-click to toggle mute

  • still works in Windows 10 (10586.104)
  • no need to Set-ExecutionPolicy as you might with PowerShell

Check out my answer to Change audio level from powershell?

Set-DefaultAudioDeviceMute
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top