Windowsで音声ボリュームをスクリプト(PowerShell)で制御

2020/12/21

Windows

t f B! P L


Windows限定の話なんだけど、
PCを持ち運びする際に自宅だと音声ONにして、スタバでドヤるときは音声OFFにしたい、
といった、PCを触る状況に応じて気軽に音声ON/OFFを切り替えたいときはないかい?

いや、手動で音声ON/OFFすればいいやん

と思った方、正解ですwwwwwwwwwwwwww


それだとつまらないので、プログラミングっぽくスクリプトで音声を制御できたらいいなー
というお話。
WiFiもBluetoothもコマンドで制御できるので、音声も制御できるやろ~と安易な考えでいたけど、かなりハードルが高かった・・・。

PowerShellなら実現可能
コマンドプロンプトで音声コントロールができそうな感じがしたんだけど、どんだけ調べてもヒットせず。
PowerShellなら実現可能っぽい。
具体的には以下のコードをPowerShell実行ファイルとして用意する。

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)); }
    }
}
'@

[audio]::Mute = $false

この最終行の[audio]::Mute = $falseでミュートを無効、つまり音声ONとなり、
[audio]::Mute = $trueに変えると音声OFFになる。
他にも音声ボリュームをXX%といった細かい大きさで指定することも可能。

音声をONにするスクリプト、OFFにするスクリプトをそれぞれ作ってもいいんだけど、
あまりファイルを増やしたくない俺としてはPowerShellの引数でON/OFFを指定するようにしてみた。
以下のような感じ。

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)); }
    }
}
'@

if($Args[0] -eq "Mute"){
    [audio]::Mute = $true
}elseif($Args[0] -eq "UnMute"){
    [audio]::Mute = $false
}

引数に"Mute"をつけると音声OFF、"UnMute"をつけると音声ONとなる。
実際のコマンド実行イメージは以下のような感じ。

# 音声OFFの場合
powershell -executionpolicy RemoteSigned <PowerShellスクリプト>.ps1 Mute

#音声ONの場合
powershell -executionpolicy RemoteSigned <PowerShellスクリプト>.ps1 UnMute

上記のコマンドを適宜実行するだけで手軽に音声ON/OFFを切り替えることができる。
これで間違っていかがわしい動画を音声付きで流さなくて済むようになるぞ!w

俺の場合は、PC起動時にWiFiの状態を確認して、
特定のSSIDに接続していたら音声ON・OFFを実行するようにしている。
その辺りの手順についてはまた後ほど記事にしよう。


参考
以下を参考にしますた。
スクリプト部分はほぼ丸パクリw

検索

Blog Archive

Popular Posts

About Me

自分の写真
性別:男
年齢:ついに40over
趣味:Snowboard、パソコン、iPhone、子育て

仕事:ユー子の社内SEとしてサーバ、NW等のインフラ全般をやってます

日々生活していく中で思ったことなどをつらつらと書いていきます。

どうぞよろしく!

ブログランキング

ブログランキング・にほんブログ村へ

QooQ