[PowerShell]再生デバイスの「既定のデバイス」をワンアクションでトグルする

## はじめに
友人がゲーマーばかりなもので、ボイス チャットには [Discord](https://discord.com/) を利用しています。
デスクトップ アプリ版でマイク テストしてみると、おかしいことに気付きました。
**ボイス以外、聞こえない**……裏でBGMを流しているはずなのに。
ボイス チャット中は他の音量を下げる設定を無効にしても、**やっぱり聞こえない**。
設定をイジることで対応できないか探ってみると、〔サウンド コントロール パネル〕から再生デバイスの「既定のデバイス」を変更すれば、"可" ではありそう。

しかし、あくまで "可" なのです。
なぜなら、〈音質がまあまあな録音できない再生デバイス〉から〈音質は悪いが録音もできる再生デバイス〉に切り替えたためです。
この設定のままにしておくと、非通話時の音質まで悪いままになってしまいます。

- **通話時は〈音質は悪いが録音もできる再生デバイス〉にする必要がある**
- **非通話時は〈音質がまあまあな録音できない再生デバイス〉を使いたい**
- Discord起動はトリガーにならない（Discord用途はボイス チャットに限定しない）
- ボイス チャットの頻度はそこまで多くない
- ~~使ってるヘッドフォンが悪いなんて言わない~~

ということで、**任意のタイミングで切り替えられること**が望ましいです。
うまいことイイカンジに恒久対応できないかと抗いましたが、簡単にはできませんでした。
というか、基本的に脳筋（IT的な意味で）のため、すぐに「ツクレバ イイジャン」になってしまうんですよね。

え。フリーソフト使えばいいって？
買ったばかりのパソコンに、余計なソフトは、あんまり、入れたくない……かな。
※個人の感想です。

というわけで、作っていきますね！
今回は PowerShell を使いました。

## PowerShellで再生デバイスの「既定のデバイス」を変更する

CUIによる変更方法の話をするより先に、GUIによる変更方法の話をします。

1．Windows設定の〔サウンド コントロール パネル〕を開きます。
Windows10だと、タスクトレイのサウンド アイコンを右クリックし、表示されたメニューから「サウンドの設定を開く」をクリックします。さらに `関連設定 ＞ サウンド コントロール パネル` をクリックします。

2．再生タブを開きます。

3．任意のデバイスを選択し、ウィンドウ下部の「規定値に設定」ボタンをクリックします。

---

いや～、メンドウですね。アクション数が多い。毎回やりたくないです。

さて、次からはこれをCUIで実現します。
残念ながら、素のPowerShellではできないです。

### 1．PowerShellモジュール『AudioDeviceCmdlets』をインポートする
世の中には存在しました。MITライセンス！

- [AudioDeviceCmdlets](https://github.com/frgnca/AudioDeviceCmdlets) | GitHub

1-1．上記GitHubから AudioDeviceCmdlets.dll ファイルをダウンロードします。

1-2．PowerShellを起動し、AudioDeviceCmdlets.dll ファイルを置いた場所に移動（`Set-Location`）します。

1-3．下記のPowerShellコマンドを実行します。
※何かが失敗して再実行する場合、1-2の移動からやり直してください。

```powershell
New-Item "$($profile | split-path)\Modules\AudioDeviceCmdlets" -Type directory -Force
Copy-Item ".\AudioDeviceCmdlets.dll" "$($profile | split-path)\Modules\AudioDeviceCmdlets\AudioDeviceCmdlets.dll"
Set-Location "$($profile | Split-Path)\Modules\AudioDeviceCmdlets"
Get-ChildItem | Unblock-File
Import-Module AudioDeviceCmdlets
```

`Import-Module`は成功しても無言です。不安なら`Get-Module`で確認しましょう。

```
PS C:\Windows\system32>Get-Module

ModuleType Version Name               ExportedCommands
---------- ------- ----               ----------------
Binary     3.0.0.0 AudioDeviceCmdlets {Get-AudioDevice, Set-AudioDevice, Write-AudioDevice}
```

### 2．「既定のデバイス」変更先デバイスのIndexを調べる
「変更先デバイスがどれであるか」、一意の情報が必要ですね。
さっきインポートしたコマンドレットがさっそく使えます。

```powershell
Get-AudioDevice -List | Where-Object { $_.Type -eq "Playback" }
```

`Get-AudioDevice -List`ですべてのオーディオが表示されます。
目的外の情報がたくさん表示されてしまいますので、`Where-Object`で絞り込みました。
再生デバイスは Type=Playback、録音デバイスは Type=Recording ですね。

```
PS C:\Windows\system32>Get-AudioDevice -List | Where-Object { $_.Type -eq "Playback" }

Index   : 3
Default : False
Type    : Playback
Name    : Realtek Digital Output (2- Realtek(R) Audio)
ID      : {0.0.0.00000000}.{8a1df0ae-681d-408a-9382-a2da4a39918f}
Device  : CoreAudioApi.MMDevice
```

上記は例ですが、これの Index が目的の項目で、これは「3」です。

### 3．PowerShellで「既定のデバイス」を変更する

さて、仕込みは終わりました。あとは実行するだけです。
さきほど調べた変更先デバイスの Index=3 を使います。

```powershell
Set-AudioDevice -Index 3
```

実行後、即時反映でデバイスが切り替わりました。
Index しか指定していませんが、Index=3 は Type=Playback だったので、再生デバイスの「既定のデバイス」が変更されています。BGMを流しながらやるとわかりやすいです。

再生デバイスの「既定のデバイス」変更が、CUIでできました！

## 再生デバイスの「既定のデバイス」をトグルする
目的まであと一歩。PowerShellを軽くコーディングします。
PowerShellスクリプト(.ps1)を実行したら「いま使われていない方のデバイスに既定のデバイスを変更」してくれたら、使うときに何も考えなくてよくて、イイカンジですよね～！

というわけで、こんなカンジになりました。
今回は「PlaybackToggle.ps1」というファイル名にしました。

```powershell:PlaybackToggle.ps1
Set-Variable -Name forSound -Value 3 -Option constant;
Set-Variable -Name forVoice -Value 4 -Option constant;
$before = (Get-AudioDevice -Playback).Index;

$after = $forSound;
switch ($before) {
	$forSound { $after = $forVoice; }
	$forVoice { $after = $forSound; }
}
Set-AudioDevice -Index $after;
```

上記の「3」は非通話時に使う〈音質がまあまあな録音できない再生デバイス〉で、
「4」は通話時に使う〈音質は悪いが録音もできる再生デバイス〉だとお考えください。

`Get-AudioDevice -Playback`で、現在の「再生デバイスの既定のデバイス」情報が取得できます。`.Index`により、情報から Index に指定して変数に代入しています。

---

このPowerShellスクリプト(.ps1)を右クリックして「PowerShell で実行」するだけで、再生デバイスの変更ができます。
もう一度ファイルを右クリックして「PowerShell で実行」すると、また再生デバイスが切り替わります。

「既定のデバイス」のトグル、できました！

しかしまあ、使うたびに "右クリックして「PowerShell で実行」する" って、メンドウくさくないですか……？
右クリックメニュー表示するまで時間がかかる環境もあるにはあるし……。

## PowerShellスクリプトをワンアクションで実行できるようにする
ショートカット(.lnk)って、ワンアクションで実行できてイイですよね。
そうしましょう。

1．ショートカット配置場所（デスクトップ等）で何もないところを右クリックし、`新規作成 ＞ ショートカット`をクリックします。

2．「項目の場所を入力してください」を、下記のようにします。
"D:\bat\PlaybackToggle.ps1" には、さきほど作ったPowerShellスクリプト(.ps1)のパスを指定してください。

```
powershell -ExecutionPolicy RemoteSigned "D:\bat\PlaybackToggle.ps1"
```

3．「このショートカットの名前を入力してください」に任意の名前を入力し、完了ボタンをクリックします。

---

作成されたショートカット(.lnk)をダブルクリックします。

すると！ 即時切り替わります。

また、ショートカットをダブルクリックします。

やっぱり、切り替わります。やったー！

以上で、**再生デバイスの「既定のデバイス」をワンアクションでトグルする**でした！

## 余談
AudioDeviceCmdlets について調べたら、日本語の記事があまりにも無いので、書きました……。まさかこんな機会でQiitaデビューするとは。
自分用のバッチ作るだけなら調査から実装まで10分程度で済んだのですが、それをまとめるとなると何倍もの時間が掛かりますね。
ブログ等で情報を公開してくださる方々には本当に頭が下がります。

### 先人の知恵（PowerShellがない場合）
PowerShellがない場合、または、何らかの理由でPowerShellモジュールをインポートできない場合、以下のサイト様のやり方になるかと思い、紹介させていただきます。

- [Windows でサウンドの再生デバイスを切り替えるためのスクリプト（バッチ処理）](https://jutememo.blogspot.com/2012/08/windows.html) | すぐに忘れる脳みそのためのメモ

検索して真っ先に見つけたのですが、
「これは、ショートカットを2つ用意し、どちらか選んで実行する必要があるのでは？」
そう思ってしまうと、度を越したメンドウくさがり屋のため、選べませんでした。

PowerShellのおかげで、だいぶ楽になりましたね。