ビデオチャット中に左シフトキーを押している間だけミュート解除し会話できるアプリをつくった


Google MeetやZOOMでビデオチャットをしているとき、マイクに雑音が入らないよう自分の発言中以外は基本的にミュートしているのですが、
ミュート解除を切り忘れたり、別画面を開いているとすぐにミュート解除できなかったりで、面倒だったのでキーボードの特定のキーを押している間だけミュートを解除してくれるアプリ「Cough Switch」を作成しました。

トランシーバーやレコーディングスタジオのトークバック機能のようなイメージです。

AppleScriptでマイクの音量を操作する

Macの場合、 システム環境設定 > サウンド > 入力 でマイク設定を操作することができます。
この入力音量を0にするとミュートされます。

applescript
tell application "System Events" to set volume input volume 100

このスクリプトをjsから実行するにはこんな感じ

javascript
const applescript = require('applescript');
applescript.execString('tell application "System Events" to set volume input volume 100', function);

iohookですべてのキーボードイベントを取得する

さくっと作りたいのでまたElectronで簡単にアプリ化していきます。

Webのいつも通りにキーボード操作を取得しようと、
document.addEventListener("keydown", function)
としてしまうと、Windowがアクティブになっていないとキーボードイベントを取得できません。

electron.globalShortcutではkeyupイベントを取得できないので却下。

他に探してみたところiohookってやつで取得できるみたいです。
(特定のバージョンのNodeとElectronしかサポートしていないらしくちょっとはまった)

javascript
const iohook = require('iohook');

const KEYCODE = 42; // left shift key

iohook.on('keydown', (msg) => {
  if (msg.keycode === KEYCODE) {
    talk();
  }
});

iohook.on('keyup', (msg) => {
  if (msg.keycode === KEYCODE) {
    mute();
  }
});

どのキーに割り当てるか悩みましたが2つあるので、左シフトキーにしました。

Electronの最前面の触れない透明なWindowを作成

ミュート解除中のアイコンを表示させるために、最前面に触れないWindowを作成します。
さらに邪魔にならないよう背景を透明にします。

javascript
const appWindow = new BrowserWindow({
  transparent: true,
  frame: false,
  resizable: false,
  alwaysOnTop: true,
});

appWindow.setIgnoreMouseEvents(true);

appWindow.loadURL(`file://${__dirname}/index.html`);

これで最前面にHTMLが表示されます。
あとはキーボードイベントに応じて、Windowへ状態を送り、CSSでアイコンを表示させます。

javascript
appWindow.webContents.send('talk');

おわり

あとは前にやった MacBook Proの充電器の情報をメニューバーに表示するElectronアプリをつくった と同様にアプリケーション化して完成です。

ソースコードはこちら(https://github.com/narikei/cough-switch)

おわりのおわり

デフォルトだとスピーカーの設定しかできないけど、
こういうときこそタッチバーの出番なのではないかな?
バックグラウンドのアプリでタッチバーを使えないのどうにかしてほしい。