詩と創作・思索のひろば

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

ts-nodeで実行中かどうか確認する

TypeScript でユーティリティ的なスクリプトを開発しているとき、毎回 tsc でコンパイルして node で実行するのはあまりに重たいのでやってられない。

ts-node というのでコンパイル作業なしに直接 .ts ファイルを実行できるんだけど、.ts ファイルとコンパイル後の .js ファイルの位置が異なっているので微妙に困ることがある。ファイルからの相対位置で何かを指定するような場合ですね。

なので ts-node 下での実行かどうかを判定したい。process.argv[0] らへんを見ると素の node との違いはわかるんだけど、本質的な情報ではないのでちょっと気持ち悪いところがある。

と思って調べていたらちょうどそういう話があって、結局、以下のようにして調べられるらしい。process を拡張してるんですね。なるほど。

import { REGISTER_INSTANCE } from "ts-node";

const tsService = process[REGISTER_INSTANCE]; // これがあれば ts-node で実行中

ただ、これだと ts-node が成果物の依存に入ってしまうので、REGISTER_INSTANCE の代わりに Symbol.for('ts-node.register.instance') を使ったほうがいいかもしれない。どっちも微妙。

ちなみに process[REGISTER_INSTANCE] には TypeScript コンパイラの設定が入ってるようなので、ここからさらに詳細な設定を知ることも可能そうではある。

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

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