日程Fit|「いつ空いてますか?」の往復はもう不要。候補日を選んでURLを送るだけ|登録不要|今すぐ無料で使う →

PowerShellでシステム音量を簡単に取得する方法

PowerShellでシステム音量を簡単に取得する方法を安全に進める結論は「標準PowerShellだけの短い公式cmdletはありません。既存の社内署名済みhelperまたはCore Audio APIのIAudioEndpointVolume.GetMasterVolumeLevelScalarを呼ぶ小さなwrapperで、default render endpointの0.0~1.0を読みます。Gallery moduleを無検証でinstallしません。」です。PowerShell標準にはGet-AudioDevice/Get-SystemVolume cmdletがなく、IAudioEndpointVolumeを使う承認済みwrapperが必要という前提を外すと同じ入力でも結果が変わります。まず読み取り確認で現状を固定し、その後に必要最小限の操作、検証、復元を分けて行います。 確認ポイント:取得対象は既定render endpointのmaster scalarであり、application別volumeや物理knobの値ではありません。

日程Fit。無料・登録不要。「いつ空いてる?」を、ひとつのリンクで。リンクを送って、○△×でかんたん日程調整。無料で日程を作る。
目次

PowerShell標準cmdletだけでは音量値を取得できない

Windowsのシステム音量を読む処理では、現在の既定レンダーエンドポイントが存在することと、取得APIが0から1のスカラー値を返すことを確認します。結果を0から100パーセントへ変換して表示するだけに限定し、音量やミュート状態は変更しません。リモートや非対話セッションでは取得不能を0パーセントと扱いません。

  • Get-Command Get-AudioDeviceで非標準moduleの有無とSourceを確認する
  • default playback endpointがspeaker/headsetのどれか確認する
  • wrapperの署名、source、bitness、対応OSを確認する
  • 取得だけか音量変更も許可するか権限を分ける

既定render endpointと取得値の範囲を理解する

Core Audioのmaster volumeはdefault endpointごとで、application session volume、hardware knob、communication deviceとは別です。0%とmuteも別状態です。Remote DesktopやBluetooth接続でdefault endpointが切り替わるため、値だけでなくendpoint ID/nameと取得時刻を記録します。Win32_SoundDeviceのStatusはvolumeではありません。

Core Audio読取ラッパーを段階的に組み立てる

Audio系commandの有無を確認する

Get-Command '*Audio*' -CommandType Cmdlet,Function -All -ErrorAction SilentlyContinue | Select-Object Name,CommandType,Source,Version

結果に出るcommandは導入済みmodule由来か確認します。Get-AudioDeviceはWindows標準ではありません。

Win32_SoundDeviceは音量値ではない

Get-CimInstance Win32_SoundDevice | Select-Object Name,Manufacturer,Status,DeviceID

これはmaster volume値を返しません。device認識の確認だけです。

IMMDeviceEnumeratorから既定端点を選ぶ

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

[ComImport]
[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
internal class MMDeviceEnumeratorComObject { }

[ComImport]
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IMMDeviceEnumerator {
    [PreserveSig] int EnumAudioEndpoints(int dataFlow, int stateMask, out IntPtr devices);
    [PreserveSig] int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice endpoint);
    [PreserveSig] int GetDevice([MarshalAs(UnmanagedType.LPWStr)] string id, out IMMDevice device);
    [PreserveSig] int RegisterEndpointNotificationCallback(IntPtr client);
    [PreserveSig] int UnregisterEndpointNotificationCallback(IntPtr client);
}

[ComImport]
[Guid("D666063F-1587-4E43-81F1-B948E807363F")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IMMDevice {
    [PreserveSig] int Activate(ref Guid iid, int clsctx, IntPtr activationParams,
        [MarshalAs(UnmanagedType.IUnknown)] out object interfacePointer);
    [PreserveSig] int OpenPropertyStore(int access, out IntPtr properties);
    [PreserveSig] int GetId([MarshalAs(UnmanagedType.LPWStr)] out string id);
    [PreserveSig] int GetState(out int state);
}

[ComImport]
[Guid("5CDF2C82-841E-4546-9722-0CF74078229A")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAudioEndpointVolume {
    [PreserveSig] int RegisterControlChangeNotify(IntPtr notify);
    [PreserveSig] int UnregisterControlChangeNotify(IntPtr notify);
    [PreserveSig] int GetChannelCount(out uint count);
    [PreserveSig] int SetMasterVolumeLevel(float levelDb, Guid context);
    [PreserveSig] int SetMasterVolumeLevelScalar(float level, Guid context);
    [PreserveSig] int GetMasterVolumeLevel(out float levelDb);
    [PreserveSig] int GetMasterVolumeLevelScalar(out float level);
    [PreserveSig] int SetChannelVolumeLevel(uint channel, float levelDb, Guid context);
    [PreserveSig] int SetChannelVolumeLevelScalar(uint channel, float level, Guid context);
    [PreserveSig] int GetChannelVolumeLevel(uint channel, out float levelDb);
    [PreserveSig] int GetChannelVolumeLevelScalar(uint channel, out float level);
    [PreserveSig] int SetMute([MarshalAs(UnmanagedType.Bool)] bool muted, Guid context);
    [PreserveSig] int GetMute([MarshalAs(UnmanagedType.Bool)] out bool muted);
    [PreserveSig] int GetVolumeStepInfo(out uint step, out uint stepCount);
    [PreserveSig] int VolumeStepUp(Guid context);
    [PreserveSig] int VolumeStepDown(Guid context);
    [PreserveSig] int QueryHardwareSupport(out uint mask);
    [PreserveSig] int GetVolumeRange(out float minDb, out float maxDb, out float incrementDb);
}

public static class AudioLevelReader {
    public static float GetMasterVolumeScalar() {
        IMMDeviceEnumerator enumerator = null;
        IMMDevice device = null;
        IAudioEndpointVolume endpoint = null;
        try {
            enumerator = (IMMDeviceEnumerator)new MMDeviceEnumeratorComObject();
            Marshal.ThrowExceptionForHR(enumerator.GetDefaultAudioEndpoint(0, 1, out device));
            Guid iid = typeof(IAudioEndpointVolume).GUID;
            object rawEndpoint;
            Marshal.ThrowExceptionForHR(device.Activate(ref iid, 23, IntPtr.Zero, out rawEndpoint));
            endpoint = (IAudioEndpointVolume)rawEndpoint;
            float level;
            Marshal.ThrowExceptionForHR(endpoint.GetMasterVolumeLevelScalar(out level));
            return level;
        }
        finally {
            if (endpoint != null) Marshal.ReleaseComObject(endpoint);
            if (device != null) Marshal.ReleaseComObject(device);
            if (enumerator != null) Marshal.ReleaseComObject(enumerator);
        }
    }
}
'@

$scalar = [AudioLevelReader]::GetMasterVolumeScalar()
if ($scalar -lt 0.0 -or $scalar -gt 1.0) { throw "Core Audio returned an out-of-range value: $scalar" }
[pscustomobject]@{
  Scalar  = $scalar
  Percent = [math]::Round($scalar * 100, 1)
}

以下の完成例はCore Audioの必要interface、HRESULT検査、COM object解放を一つのAdd-Type定義に含めます。取得methodだけを公開し、SetMasterVolumeやSetMuteは呼びません。実行sessionの既定render endpointを読む点を確認してください。

読み込んだ.NET型とmethodを確認する

[AudioLevelReader].Assembly.FullName
[AudioLevelReader].GetMethod('GetMasterVolumeScalar').ToString()

Add-Type後はAudioLevelReader typeのassemblyとGetMasterVolumeScalar methodを表示し、想定したtypeが現在sessionへ読み込まれたことを確認します。別sourceの同名typeが既にある場合は新しいsessionで内容を照合します。

0.0〜1.0を百分率へ変換する

$volume=[AudioLevelReader]::GetMasterVolumeScalar()
if($volume -lt 0.0 -or $volume -gt 1.0){ throw "Core Audio returned out-of-range value: $volume" }
[pscustomobject]@{Scalar=$volume;Percent=[math]::Round($volume*100,1);SessionId=[Diagnostics.Process]::GetCurrentProcess().SessionId}

GetMasterVolumeScalarの戻り値を0.0〜1.0で検証してからpercentへ変換します。endpointなしやCOM errorを0へ置換せず例外として扱い、0%と取得失敗を明確に分けます。

WMIのStatusを音量と誤認しない

  • Get-AudioDeviceを標準cmdletと説明する
  • Win32_SoundDevice Statusをvolumeとする
  • endpoint切替を見落とす
  • muteと0%を同一視する
  • 未審査Gallery moduleをinstallする

読取専用処理で復元が不要な範囲

取得だけなら影響を抑えられます。旧記事のInstall-Module AudioDeviceCmdletsをそのまま実行せず、repository、publisher、source code、署名、versionをsecurity reviewします。Set系で夜間に自動変更すると会議・alert音を消す恐れがあるため、本記事では変更しません。変更が必要なら元endpoint、volume、muteを保存し、失敗時にその値へ戻します。

session差を含めて検証記録を残す

Windows SettingsのVolume mixerと同時刻に比較し、speaker/headset切替、mute、0/50/100%の境界をtestします。helperはHRESULT error、endpointなし、Remote Desktop、複数deviceを明示statusへし、推測値を返しません。

既定音量取得の合格基準

標準cmdletにsystem master volume取得がないため、Windows Core AudioのIMMDeviceEnumeratorで既定render/multimedia endpointを選び、IAudioEndpointVolume.GetMasterVolumeLevelScalarを呼びます。戻り値0.0〜1.0をpercentへ変換し、音量を変更するmethodは実装しません。

完成したCore Audio読取コード

本文中の完成codeが主手順です。ここでは同じcodeを重複掲載せず、次の三状態と境界値を使って結果を判定します。期待する通常状態は「既定render endpointがありscalar 0〜1を返し、0〜100%へ変換できる」です。

正常値・endpoint不在・実行失敗を分ける

  • 期待どおり:既定render endpointがありscalar 0〜1を返し、0〜100%へ変換できる
  • 0件・非適用:audio endpointなしはNoDefaultEndpointとして0%と区別する
  • 実行error:COM activation、session、device無効、API HRESULT errorは例外として取得失敗にする

Win32_SoundDeviceのStatusはhardware状態でmaster volumeではありません。未定義のGet-SystemMasterVolumeを呼ぶだけの例を避け、Core Audio wrapperのsource、bitness、署名/内容を確認します。remote/noninteractive sessionではendpointが異なる場合があります。

無音と範囲外値を確認するfixture

$volume=[AudioLevelReader]::GetMasterVolumeScalar()
if($volume -lt 0.0 -or $volume -gt 1.0){ throw "Core Audio returned out-of-range value: $volume" }
[pscustomobject]@{Scalar=$volume;Percent=[math]::Round($volume*100,1);SessionId=[Diagnostics.Process]::GetCurrentProcess().SessionId}

volume 0/中間/100%、mute、endpointなし、RDP/local session、device切替をtestします。endpoint role、session、scalar、percent、HRESULT/exceptionを保存し、取得前後で音量が変わらないことを確認します。

PowerShellでシステム音量を簡単に取得する方法の証跡には、実行対象と取得時刻に加え、通常・0件・errorのどれへ分類したかを残します。通常系は「既定render endpointがありscalar 0〜1を返し、0〜100%へ変換できる」、停止系は「COM activation、session、device無効、API HRESULT errorは例外として取得失敗にする」を判断文としてそのまま作業票へ写し、担当者ごとの言い換えで意味が変わらないようにします。

Core Audioの音量取得を定期実行する場合は、変更APIを呼ばない読取専用processとして固定し、同時起動をlockで防ぎます。実行hostとsession種別、選択されたdefault endpoint、開始・終了時刻を一つのrun IDへ結び、COM初期化失敗とendpoint未検出を別の状態で通知します。前回値との差は傾向監視に使いますが、音量を自動補正する契機にはしません。

公式情報・参考資料

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次