rch850 の上澄み

技術的な話題とか、雑談とか。タイトルを上澄みに変えました @ 2020/09/02

耳掃除したら耳が聞こえなくなって、耳鼻科に行って治してもらった

ここ1週間ほど、鼻水がひどくて、無限に鼻をかんでティッシュの山を築き上げる日々だった。どうも右耳がすっきりしない感じがしてきて、小指で耳をほじってみたら、少し耳垢が取れた。耳垢の一部が削れて取れた感じだった。これがたしか土曜夜ぐらいの話。


そして月曜の朝、改めて綿棒で耳垢を取ろうとしたところ、少ししか取れない代わりに、どうも聞こえが悪くなってきた感じがしてきた。

耳かきしすぎで耳が聞こえなくなるとかいう情報をどこかで見た気がするんだけど、今がそれなのでは??と思って、左耳を閉じてみたら、両耳を閉じたぐらいの聞こえになった。つまり、右耳を閉じていないのに、閉じたと同じ状態。これは聞こえなくなったか?ちょうど新しい朝ドラが話題になっていたのと、雨が降っていたのとで、「半分、青い。」を思い出した。外に出て雨音を聞いてみたけど、あんまり半分青い感じはしなかった。あの話では傘を差していたんだっけか。

いろいろ試してみようと思って、静かな環境でスマホを正面に構えて、音を流してみた。そうしたら、左耳だけイヤホンをさした状態としか表現できない感覚だった。ワイヤレスイヤホンを右耳だけ付けるってことをしょっしゅうしているんだけど、それの左耳バージョンで、実際、左耳にイヤホンがささってないか確認したぐらいだった。スマホを左右に動かしてみたけど、左耳イヤホンの感覚は続いた。

雨の音のときに極端に左耳だけって感じがしなかったのは、人体のいろいろな感覚が補完していたからかもしれない。でもスマホはダメだった。人体の不思議。


とりあえず耳鼻科に向かったはいいものの、運転中はかなり不安だった。いつもより対向車の音が小さい気がする。いや、小さいどころじゃなくて、聞こえてないんじゃないか?人体がいい感じに補完して聞こえている気がしてるだけなんじゃないか?

そうこうしているうちに、耳鼻科に着いた。受付の人がなにを言っているか聞き取りにくくて、左耳を寄せて聴くようにした。そして、耳鼻科の先生に状況を説明して、軽く耳の中を見てもらったあと、ベッドに横になるように言われた。

ここからプロの耳かき。「触るから動かないでねー」と言われ、何かしらの機械が耳の中のどこかに触れて耳垢を取っているようだった。この感じ、去年初めて胃カメラを飲んだ、いや、鼻から入れたときに近くて、苦手だ。別にお医者さんが悪いわけではなくて、個人的に苦手だ。早く終わってくれーと思いながら、体を固くして待っていた。体感5分、おそらく実時間1分程度で処置は終わった。測っておけばよかった。こんな ASMR があったら嫌だなって思った。

と思ったのも束の間。左耳も見てくれるとのこと。さすがにこっちは浅いところを少し見るだけだろう、と祈っていたら、そのとおり終わった。

最後に、取れた耳垢を見せてもらったけど、よく耳に入ってたなと思うと同時に、これは綿棒ではどう頑張っても取れないし、押し込まれるに決まってるわっていうサイズだった。そりゃ聞こえなくなるわ。ひとまず無事に治ってよかったです。

f:id:rch850:20210518001700j:plain

「耳垢栓塞除去」というらしい。どんだけ耳垢溜まってるんだって思われそうだけど、鼻水の影響で急激に溜まったものだと思いたい。

Mac で Soundcore Liberty 2 Pro のマイク入力がバリバリになった話

Bluetooth イヤホンマイクの Soundcore Liberty 2 Pro をよく使っているのだけど、何かをきっかけに「音がバリバリしてますよ」と言われるようになってしまった。つないでたのは macOS で、もしかしたら OS アップデートで相性が悪くなってしまったのかもしれない。詳細は忘れたんだけど、確か iPad Air(第4世代、iOS 14.5)のボイスメモで試したらバリバリしてなかった気がする。

ファームウェアアップデートで 01.28 に更新したらあっさり直った。

新春シェル乗り換えショー 〜 fish から zsh prezto へ

長いこと fish shell を使ってきたんですが、この春から zsh prezto に乗り換えました。

いつから fish 使っていたかはっきり覚えてないんですが、Twitter で最古の言及が約5年前。それぐらい長い付き合いだったんだと思います。

この手の環境は、あまりヘビーにカスタマイズせず使いたいなと考えていて、その結果が fish という選択でした。

fish のよかったところ

  • 素の状態でいい感じだった。
  • 補完がいい感じだった。
  • コマンド履歴を雑に上キーで遡れた。bash だと Ctrl+R を駆使するところ。

自分では使いこなせなかったところ

しばらく時が経って、macOS でのデフォルトシェルが zsh になりました。さらにこの春、prezto を使えばかなり fish っぽくなると同僚から教わりました。これがとどめでした。

実際に使ってみると、fish のよかったところが満たされていて、気持ちよく使えています。カスタマイズ具合はぼちぼちです。

  • 追加モジュール。各モジュールの説明は prezto/modules
    • autosuggestions
      • 補完が fish っぽくなる。この薄いやつ。
      • f:id:rch850:20210415014941p:plain
    • git
      • アグレッシブなエイリアスzstyle ':prezto:module:git:alias' skip 'yes' でオフ
    • history-substring-search
      • コマンドを遡るのが fish っぽくなる。
  • しばらくいろいろなテーマで遊びたいので zstyle ':prezto:module:prompt' theme 'random'

あ、あとこの typo 修正もデフォルトである機能みたいで素敵ですね。

% vrew
zsh: correct 'vrew' to 'brew' [nyae]? n
zsh: command not found: vrew

以上 fish から prezto に乗り換えた記録でした。

参考記事:

Android の ML Kit とスクリーンキャプチャのメモ書き

Android で画面をキャプチャしながらいい感じにテキスト抽出してみたいな、と考えて、色々試してみました。Android 開発の基礎力がないので、つぎはぎなんとかならないかと調べてみた、雑多なメモ書きだと思ってください。

ML Kit でのテキスト検出

developers.google.com

この ML Kit のドキュメントを見ながら、自力で空のプロジェクトに追加してみようとしたのですが、Android 開発の基礎力がなさすぎて断念。

冒頭に codelab へのリンクがあったので、そちらに頼ることにしました。

codelabs.developers.google.com

こちらはコードをダウンロードして starter に少しコードを書き足すだけだったので、問題なく動いてくれました。やさしいですね。コードは Java なので、Kotlin プロジェクトに適用するときは色々と変わるところがありそうです。

画像を選択するところに選択肢を増やして、読み取らせることもできました。

Media Projection API でのスクリーンキャプチャ

キャプチャについては公式のサンプルに頼ってみました。動くことが分かっているものに手を入れていくスタイルのほうが、初心者にとってはやさしいと思うので。

github.com

この ScreenCapture というサンプルが、 Media Projection API を使ってキャプチャするサンプルです。難なく動きました。

組み合わせ

このサンプルだけでは、キャプチャして画面に表示するだけなので、ML Kit に流し込むため Bitmap にする方法を調べました。

techbooster.org

TechBooster さんの記事、もう6年前ですが、これで大体の流れは分かりました。結局 Codelab のサンプルに、この記事を参考にしながらスクリーンキャプチャを組み込む実装となりました。

Bitmap にするときに次のようなエラーが出てしまったのですが、PixelFormat.RGBA_8888 にすればよいという記事があったので、その通りやってみたらエラーは出なくなりました。

E/ImageReader_JNI: Producer output buffer format: 0x1, ImageReader configured format: 0x4
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.android.screencapture, PID: 11064
    java.lang.UnsupportedOperationException: The producer output buffer format 0x1 doesn't match the ImageReader's configured buffer format 0x4.
        at android.media.ImageReader.nativeImageSetup(Native Method)

ここまでの実装だと、他のアプリがフォアグラウンドになっているときのキャプチャができませんでした。フォアグラウンドサービス化などを試してみたいと思います。

TypeScript 4.2 の Template Literal Expressions Have Template Literal Types の例

TypeScript 4.2 の Template Literal Expressions Have Template Literal Types について。これは `/topics/${id}` が TypeScript 4.1 までは string 型だったのが TypeScript 4.2 から `/topics/${string}`型になったという話です。idnumber であれば `/topics/${number}` 型にもなります。

実例。よくある API アクセスで、パスから戻り値の型を自動判定します。

export interface Topic {}

async function fetchJson(path: `/topics`): Promise<Topic[]>;
// ${string} は ${number} でも構いません
async function fetchJson(path: `/topics/${string}`): Promise<Topic>;
async function fetchJson<T>(path: string): Promise<T>;
async function fetchJson<T>(path: string): Promise<T> {
    return (await fetch(path)).json() as Promise<T>;
}

// これは 4.1 でも 4.2 でも Topic[] 型
const topics = await fetchJson('/topics');

// これは 4.1 でも 4.2 でも Topic 型
const topic300 = await fetchJson('/topics/300');

// TypeScript 4.1 だと unknown 型なのが
// TypeScript 4.2 だと Topic 型になる
const n: number = 10
const topicN = await fetchJson(`/topics/${n}`);

表にするとこうなります。

4.1 4.2
'/topics/300' `/topics/${string}` `/topics/${string}`
`/topics/${n}` unknown `/topics/${string}`

便利そう〜

web.dev に PR を出して Eleventy と Alex について学んだ

TL;DR

  • web.dev の記事は Eleventy で生成していて {% Banner 'caution, 'body' %} といった shortcode を使える。
  • web.dev は inclusive な用語になるよう alexGitHub Actions で動かして自動チェックしている。

きっかけ

スマホで web.dev の How to use HTTPS for local development を読んでいたら、Caution と書いてある場所に何も書いてなくて、まさかと思って端末を横持ちにしたら内容が出てきた(現在は修正済み)。

ページ下部の "File a bug" から不具合報告することもできたけど、ちょっとコードを読んだら直せそうだったから、直して PR を出して、ありがたいことにマージされた(その後、別の PR でもっといい形にしてもらった)。

github.com

{% Banner %} と Eleventy の shortcode

どこをどう直せばいいかは、こういう流れで調べた。

  • このページのソースを見に行って Caution 周りのコードを見たら {% Banner 'caution' %} と書いてあった。
  • コード全体を Banner で検索したら、常に表示すべきところは {% Banner 'caution', 'body' %} となっていた。
  • Banner.js を見つけて大体の動きを察した。

というわけで {% Banner 'caution' %}{% Banner 'caution', 'body' %} にして PR を出した。

この書き方でなぜこういう動きをするのか、後から調べてみた。

  • ビルドを受け持っていると思われる continuous-integration-workflow.yml を見たところ、ビルドは npm run build を呼び出すシンプルな形。
  • package.json を見ると build は "npm-run-all clean sass gulp rollup eleventy sw-manifest" で、eleventy がそれっぽい。ここで初めて Eleventy という Static Site Generator (SSG) の存在を知った。なんとトップページの Build with Eleventy に web.dev が載っている。知ってれば知っていたやつですね。
  • .eleventy.js を読んだら config.addPairedShortcode('Banner', Banner); とあった。
  • さらに調べると Eleventy に shortcode という仕組みがあることが分かり、 {% Banner 'caution', 'body' %} を div に変換していることが分かった。

alex での文章チェック

PR を出した後、Netlify で自動的にステージング環境ができたのは、まぁよくある話だなと思ったんだけど、Alex Recommends Report というものが初見だった。

f:id:rch850:20210207165223p:plain

例えばこのような指摘が出ていたので、master を main にする的な話なんだなというのはなんとなく分かった。

special may be insensitive, use has a disability, person with a disability, people with disabilities instead

Alex と言われても macOSsay コマンドで指定する英語の男性しか思いつかなかったので、こちらもそれっぽい GitHub Actions を探したところ、alex-workflow.yml があった。ここで使っていたのが Alex Recommends という Action だった。この Action は alex というものを走らせるものだった。textlint は知っていたけど、これは特に inclusive を狙ったもののようだ。もしかして、と思って調べたら textlint-rule-alex があった。

以上、web.dev に PR を出したときに学んだことでした。

不要になったコードの削除

フロントエンドでの「この画面が不要になった」、バックエンドでの「このAPIが不要になった」といったケースでの、不要になったコードの削除について、考えをまとめてみた。

なぜ削除するのか

  • なぜ残っているのかという疑問が発生するのを防ぐため。
  • リファクタするときの邪魔にならないように。
  • 全文検索で無駄にヒットするのを防ぐため。
  • ビルド、テストの時間を短縮するため。
  • 総じて言えばメンテナンスコスト削減。

色々理由を挙げてみたなかで、疑問を防ぐことを1番目に持ってきた。この理由は、他の要素と違って我慢や努力で解決できないからだ。

会話例:

  • 「icon_plus.png ってもう使ってない?」
  • 「使ってなさそうだな。ファイル名で検索してもヒットしない」
  • 「じゃぁなんで残ってるんだろう」
  • tig -Sicon_plus してみたけど、このコミットでの削除漏れかなぁ。うーん、たぶん消して大丈夫でしょう」

icon_plus.png を使わなくなった時点で消していれば、このやり取り自体が発生しなかったはずだ。スムーズに、気持ちよく仕事をするために、不要なコードはなるべく削除されていたほうがいい。

根から断つか、葉から断つか

根とは、フロントエンド (SPA) やバックエンドではルーティング部分だと思ってもらえればいい。逆に、葉は、フロントエンドの場合はその画面でしか使ってなかった画像、バックエンドの場合はその API でしか使ってなかったユーティリティ関数といったものだ。

根から断つか、葉から断つか。多くの場合、削除しようと思いたつきっかけが根にあるので、できる限り根から断つほうがいいと考えている。このユーティリティ関数を消したい。と思い立った場合は、それを根とみなす。

根にハサミを入れる

この根を断つ、と決めたら、まずそこにハサミを入れる。ルーティング情報を削除するといった具合で。URL /hoge に対してこの画面、この関数といった連携を断つ。もし、その画面や関数が一気に消せるようなボリュームじゃない感じがするなら、そこに「TODO: これはもう使ってないので、あとで消します」のようにコメントしておく。そして、ここでコミットして一息入れる。

このコメントをさぼると、いろいろあって削除しきれなかったときに「なぜ残っているのかという疑問」が発生してしまう。このコメントとルーティングの削除が同じコミットに入っていれば、後から経緯を調べるのは格段に楽になるはずだ。

少しずつ削除していく

あとは、根を断って浮いた部分を削除していくだけだ。削除するときも、根の方から削除していくのがやりやすい事が多い。葉が明らかに見えていて、削除も簡単なら、そっちから削除してもいい。

根の方から少しずつ削除していくと、最終的に葉まで至る。フロントエンドなら、不要となったCSS、画像など。また、葉と言えるボリュームではないけど、その画面だけで使っていたライブラリなんかも末端の葉だ。バックエンドの場合は、データベースのテーブルやカラムが葉となりうる。こういった末端の葉ほど、削除し忘れたときに「なんで残ってるの?」という疑問が発生しやすいので、できるだけ削除する。

もしかしたら、削除しないほうがよさそうなコードなどがあるかもしれない。その場合は、「これはもう使ってないけど、近いうちに使う予定があるから残しておく」といった、削除してない理由を記録して、後日疑問が出ないようにする。とにかく「なぜ残っているのかという疑問」を残さないようにする。「なぜ」を調べるコストは高い。

PR やコミットの粒度

できれば、最初のハサミを入れた時点で PR にしておくといいと思う。というのも、万が一、その画面や API が使われていて、戻さなければならなくなった場合に、ハサミを入れただけなら復旧が簡単だからだ。その先についても、復旧のことを考えて PR の粒度を考えるといいだろう。

コミットの粒度は、それなりに小さめに留めておいたほうがいいように思う。よほどのことがない限りい、小さくて困ることはない。1コミットにつき、多くて数ファイル、数百行か。とはいえ、できるだけ意味のある単位、コンパイルが通る単位でまとめたほうがいいので、場合によっては数十ファイルなどに及ぶこともあるだろう。そのあたりはいい感じにしよう。

コミットを小さくするのと同じ理由で、PR を小さくしてもいいのでは?とも考えられるが、PR を小さくする場合は、その PR の先に控えている作業を記録したり記憶したりしておかないと、削除漏れが発生しやすいので注意が必要だ(理由はちょっと長くなりそうなので割愛)。