詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

Fork me on GitHub

Goのswitch文を生成するツール

GitHub - motemen/go-switchgen

昔作ったソフトウェアをちょっとメンテしたシリーズ。Go で switch 文を自動生成したいよーってときに使えるやつです。 case 文が exhaustive になるようにいちいち書いてくのが面倒なので、機械にやらせる。

こんなやつ欲しいんだが……ってなったときに、オレならこういう名前のツールを作る! って思った名前で過去の自分が作ってることに一貫性を感じる。まあ世の中にすでに3つくらい実装のありそうなやつではある。

% go install github.com/motemen/go-switchgen/cmd/goswitchgen@latest

使い方

fully-qualified な型を指定すると、その型を持つ値に対する switch 文を生成します。case に挙げられるのは、同じパッケージに定義されている値や型。

% goswitchgen <package>.<type>

reflect.Kind みたいに enum っぽい定数なら、

% goswitchgen reflect.Kind
switch _ {
case reflect.Array:
case reflect.Bool:
[...]
case reflect.Uintptr:
case reflect.UnsafePointer:
}

みたいに生成してくれる。

go/ast.Node みたいなインタフェースの場合は型switchを生成してくれる。インタフェースを実装する型の一覧は go doc からわかんないので便利ですね。

% goswitchgen go/ast.Node
switch _.(type) {
case *ast.ArrayType:
case *ast.AssignStmt:
[...]
case *ast.UnaryExpr:
case *ast.ValueSpec:
}

となります。

go.mod に挙げられていれば、標準モジュール以外についても switch 文を生成できます。

% cat go.mod | grep github.com/slack-go/slack
    github.com/slack-go/slack v0.8.2
% goswitchgen github.com/slack-go/slack.MessageBlockType
switch _ {
case slack.MBTAction:
[...]
case slack.MBTSection:
}

今回、x/tools/go/packages を使ったんだけどすでに Go の関数を context 対応するツール - 詩と創作・思索のひろば で書いてた。いろいろ文脈のあるモジュールのようなので、いつかまとめたい。

PCを離れたらマイク音量を下げるmacOSアプリを作った

この記事は、はてなエンジニア Advent Calendar 2020の21日目です。昨日は id:tarao による Scalaの依存ライブラリ更新はRenovateでもけっこうイケる でした。明日は id:Krouton です。


みなさん在宅勤務してますか? 私もしています。

仕事も雑談も、とにかくオンラインで話すことが多いので在宅勤務中は AfterShokz という骨伝導ヘッドホンを使っている。ずっと装着してても疲れにくいので、大変いい買い物です。

いちいち外すのも面倒なのでほとんど一日中付けっぱなしにしているんだけど、これが事故を呼ぶこともある。マイクをオフにすることを忘れて離席してしまうと、オフのときの会話が筒抜けになってしまうので、同僚の前ではおとなしいのに家族の前では豹変するとか……。あとおしっこしてる音が聞こえちゃってないとか。気になりますよね。油断できない。

そういうわけで、現実のメンタルモデルにあわせて「パソコンからの距離が遠くなったらマイクの音量を下げる」ってアプリを作ってみることにした。ちょうど macOS アプリを作ってみたかったというのもある。そうだったかな。かもしれない。距離は Bluetooth の信号強度(RSSI とかいうやつ)を使えば十分だろう。

できたもの

GitHub - motemen/macos-BluetoothAudioDistancer

初めてだったけど、SwiftUI のおかげでわりと簡単に作ることができた。

f:id:motemen:20201221210613p:plain
現在値が最大値を超えてますがあまり気にしないでください

設定をどんな風にさせるかが悩ましく、信号強度の時系列グラフを描画して、インタラクティブに閾値を選択できるといいかと考えていたが、何も知らん状態からだと難しそうだし、さっさと完成させたいので楽をすることにした。Calibration mode のチェックをオンにすると信号強度を記録するモードになるので、その間に家の中をうろついて最高強度と最低強度を覚えさせておく。あとは現在の信号強度が最低~最高のレンジのどこにいるかを見て、ボリュームに反映させる。こんな感じの式にした。

これで、コーヒーを飲んだりトイレに行ったりしているときには入力ボリュームが小さくなり、なんならゼロになり、Meet や Discord のマイクをオフにしたかどうかを気にせず独り言が言えるようになったわけです。

わかったこと・わからなかったこと

  • 数日間使ってみて思ったけど、別に音量を小さくする必要はあまりなくて、一定距離離れたらオフになってくれたらいい。
  • macOS で、Bluetooth デバイスとオーディオデバイスを突き合わせる方法がわからない。今はとりあえず AudioObjectGetPropertyDatakAudioDevicePropertyDeviceUID を使用した際に取れる UID が IOBluetoothDeviceaddressString に対応するらしいと想定しているけど、UID は本来ブラックボックスなので、正しいやり方ではなさそう。というか自分の環境でしか試してないので、偶然に頼ってるかもしれない。
  • メンタルモデルができてないからか、Apple Developer Documentation がどうも読みづらかった。API の使用例とかあんま載せてくれないのかな。Stack Overflow が役に立った。
  • 最初 Swift Playground で試して、よさそうだったのでプロジェクトを作ったんだけど、権限の関係で挙動が変わって難儀した(エラーになるとかではなく、0 が取得される、というぐあい)。
  • SwiftUI で作ったウィンドウの最大化や最小化を禁止する方法は、SwiftUI の世界にはなさそうな気がする。NSApplicationDelegateAdaptor というのを使うと NSWindow とか触れる感じ。でたよデリゲート! って気持ちになった。
    • ホントは Dock から隠してメニューバーに常駐させるなどできればカッコイイと思っていたのだった。
  • プロジェクトの設定で Team を設定しないと PreviewProvider による Xcode でのプレビューが失敗する? 気がする。最初設定してなかったので困った。
  • GitHub にソースコード push してるけど、これみんなビルドできるの?

Big Sur の見た目はちょっと気に入ってるので、小さなアプリでも作れると楽しいですね。もしビルドできそうだったら、どうぞご利用ください。

Google Meet のミュートをツールバーから切り替える拡張を作った

Google Meet Mute Toggler - Chrome ウェブストア

こんなやつです。

Google Meet で仕事をすることが多くなった昨今、咳払いや生活音といったよんどころない事情によりマイクをミュートにしたいこともあるわけですが、ミュートボタンは普段隠れていて急に出てこないし、切断ボタンの隣りにあってちょっと怖い。正確なエイムが求められるのは小さなストレスになってるわけですね。この切り替えボタンをツールバーの常に同じ位置に置くことで、それを軽減させようとするものです。

Chrome 拡張のストアって決まった大きさの画像をアップロードしないといけないので面倒だったんだけど、画面の録画を YouTube に上げたらいいってことがわかりました。

Google Meet Mute Toggler Demo - YouTube

ソースコードはこれ。

GitHub - motemen/chrome-meet-mute-toggler: Easily toggle your microphone mutes on Google Meet meetings, by clicking toolbar button.

最近のウェブアプリは外部からいじりやすいクラスがついてなくて困るので、aria-label 属性へのマッチで要素を探してるのだが、これがまた言語によってスペースが入ってたり入ってなかったり大文字小文字だったりで大変だ……。

  document.querySelector<HTMLElement>(
    [
      '[role=button][aria-label*="⌘+D" i]',
      '[role=button][aria-label*="⌘ + D" i]',
      '[role=button][aria-label*="Ctrl+D" i]',
      '[role=button][aria-label*="Ctrl + D" i]',
    ].join(",")
  );

はてなで一緒に働きませんか?