Open3

Stream Deckプラグイン「Volume Controller」をOSの音量ミキサーのWebScoket APIとして使う

Yuichiroh AraiYuichiroh Arai

Elgatoが提供しているStream DeckのVolume ControllerはOSやアプリの音量を操作できる公式プラグインです。
Windows 11の場合、設定 > システム > サウンド > 音量ミキサーの画面を物理デバイスで操作できるようになり、私もとても重宝しています。

そんな中、勉強がてらプラグインのソースコード(minifyされている)を覗いていたんですが、どうやらElgatoAudioControlServerというプログラムがバックグラウンドで実行されていて、プラグインはそのプログラムとWebSocketで通信しているだけでした。
要するにOSの音量ミキサーのWebSocket APIが使える状態なわけです。

Volume Controllerでは実現できない細かい要望があったのですが、最近はこのAPIを利用して自分で開発したプラグインも併用しています。

Yuichiroh AraiYuichiroh Arai

WebScoket API (独自調べ)

接続先

ws://127.0.0.1:1844

グローバルメッセージ

すべてのクライアントに対して送信されるメッセージです。
OSやアプリの音量に関連する状態が変化した時に発生します。
WebSocketのaddEventListenerメソッドでmessageイベントを監視しておきましょう。

フォーマット

{
  "jsonrpc": "2.0", // 以下省略
  "method": "ここに通信の種類を識別する文字列が入る",
  "params": {
    ...
  }
}

以降、抜粋して記載します

ボリューム変更

アプリのプロセスIDからアプリ名などを取得する方法は後ほど紹介します。

// 全体
{
  "method": "currentSystemDefaultDeviceVolumeChanged",
  "params": {
    "volume": number // [0-1]
  }
}

// アプリ
{
  "method": "preferredSessionInstanceVolumeChanged",
  "params": {
    "processID": number,
    "volume": number // [0-1]
  }
}

ミュート変更

// 全体
{
  "method": "currentSystemDefaultDeviceMuteChanged",
  "params": {
    "mute": true
  }
}

// アプリ
{
  "method": "preferredSessionInstanceMuteChanged",
  "params": {
    "mute": boolean,
    "processID": number
  }
}

アプリの追加・削除

アプリの起動・終了に伴って、アプリ一覧にアプリが追加・削除された時に発生します。

{
  "method": "appInstanceAddRemove",
  "params": {
    "appAddedRemoved": "Added" | "Removed",
    "processID": number
  }
}

アクティビティ変更

アプリの音声出力の状態が変更された時に発生します。

  • 2 (Active 音が鳴っている)
  • 3 (InactiveShort 音が止まって短い時間が経過している)
  • 4 (InactiveLong 音が止まって長い時間が経過している)
{
  "method": "appInstanceActivityChanged",
  "params": {
    "activity": 2 | 3 | 4,
    "processID": number
  }
}
Yuichiroh AraiYuichiroh Arai

個別メッセージ

特定のフォーマットでデータを送信すると、そのクライアントにだけメッセージが返信されます。
クライアントから能動的にデータを取得したい場合に使用します。

送信フォーマット

WebSocketのsendメソッドで送信する際のフォーマットです。
JSON.stringify()で文字列にしてから渡しましょう。

{
  "jsonrpc": "2.0", // 以下省略
  "id": 任意の整数値, // 以下省略
  "method": "ここに通信の種類を識別する文字列が入る",
  "params": {
    ...
  }
}

返信フォーマット

{
  "id": 送信時に設定した任意の整数値, // 以下省略
  "result:": {
    ...
  }
}

どの送信に対応する返信かを識別するためにidを設定しておきます。
文字列ではなく整数値なので注意してください。

ちなみに、Volume Controllerプラグインでは下記の方法でランダムに生成していました。

Math.floor(1e9 * Math.random())

執筆中