こどもの百人一首の宿題だけに特化したWebアプリを作ってみてハマったとことか解決策

# はじめに
こどもの小学校の宿題で百人一首の暗記読み上げが毎日あるのですが、問題を出題しなければならずこどもだけでは完結させられないため一緒に行う必要があります。
これが百人一首をまったく覚えていない私としては問題の出題や合っているかのチェックが地味に負荷が高く…

百人一首のランダム読み上げ自体はYouTubeにいくつか動画はあったりするものの宿題の形式に沿ったものがなかったため、宿題の要件にあわせたWebアプリをTTS、STTで実装してみました。



# 完成したアプリ

https://twitter.com/miso_develop/status/1602651118282149888

![image](https://user-images.githubusercontent.com/22117028/207875375-c0d6761f-036c-4c91-b4f3-fccf5267acd7.png)



# 宿題の要件
* 色札ごとに分けられた20の句の中からランダムに問題をだす
	* 五色百人一首っていうルールで色分けが決まってるらしいです
* 問題となった句の初句（最初の5文字ほど）を読み上げ、残りをこどもが読み上げる
* 間違っていないかと声の大きさを3段階で評価する



## 実装時に参考にした記事

### TTS/STT
TTS/STTの実装は以下の記事を参考にさせていただきました。

* [Webページでブラウザの音声合成機能を使おう - Web Speech API Speech Synthesis](https://qiita.com/hmmrjn/items/be29c62ba4e4a02d305c)
* [Webページでブラウザの音声認識機能を使おう - Web Speech API Speech Recognition](https://qiita.com/hmmrjn/items/4b77a86030ed0071f548)

### 百人一首データ
百人一首のデータは以下のJSONデータを使用させていただきました。
便利なデータを公開いただきありがとうございます！

[百人一首の JSON（Qiita）](https://qiita.com/wakaba@github/items/861500682eb414142938)
[JSONデータ（gist）](https://gist.githubusercontent.com/wakaba/8363dc27f4c54f76b4a7/raw/hyakunin.json)



# ハマったことと解決策

## 1. STTがHTTPサイトだと動かない
* VSCode拡張のLive Serverで静的サーバーを立ち上げてスマホからアクセスして試してみたら動かなかった
	* HTTPだと動かない仕様らしいです
		* https://stackoverflow.com/questions/64402850/web-speech-api-not-working-on-android-chrome

### 解決策：Live ServerをHTTPS化
* Live ServerをHTTPS化できるようです
	* 参考記事：[Live Server for Visual Studio Code で HTTPS を有効にする（Qiita）](https://qiita.com/ma2shita/items/297c812a6cd09b5e82c8)
		* オレオレ証明書のワンライナー作成コマンドもあってさくっとできちゃった



## 2. STTで認識した発話が漢字で表示される
* まだこどもは多くの漢字が読めず、百人一首自体もひらがなで覚えている
* 認識結果も画面表示させたいが、漢字が入ってしまうとこどもが読めない
* 百人一首DBをひらがなで保持しているので漢字変換されてしまうと突合できない

### 解決しなかった策：漢字→ひらがな変換API
* gooラボの[漢字→ひらがな変換API](https://labs.goo.ne.jp/api/jp/hiragana-translation/)を試してみたけど、短歌の言葉遣いが独特なため期待した変換がされない
	* 例：「夜に（よに）」→「よるに」
* 参考記事：[JavaScriptでひらなが⇄漢字へ変換するAPIを叩くスクリプトforサジェスト機能の辞書データ（Qiita）](https://qiita.com/akifumii/items/bf1511cb8bc53e12f503)

### 解決策：一度STTした結果を保持
* 一度STTした結果を保持し、それと認識結果を突合する
	* 一旦は解決したが次の問題発生
	* 結果に漢字が含まれてしまうのは諦め…😢



## 3. 端末や発話者、周辺ノイズによりSTTの認識結果が若干ブレる
* VUIあるある
* 漢字変換されたりされなかったり

### 解決策1：レーベンシュタイン距離を測定
* 一致度をとれればいいかなと思って「js 文字列 一致度」で検索、レーベンシュタイン距離というので類似度を算出できるらしい
	* 実装コードがぱっと見ソートっぽいけど、実際それっぽい動きで計算してるっぽい
* レーベンシュタイン距離のしきい値を設けて近ければ正解とするようにした
* 参考記事
	* [JavaScriptで文字の類似度を見る](https://temari.co.jp/blog/2020/10/06/levenshtein/)
	* [レーベンシュタイン距離で文字列の類似度を高速に取得する](https://www.bugbugnow.net/2020/02/levenshtein-distance.html)

### 解決策2：発話認識結果を蓄積
* 発話認識結果をログ蓄積して、合ってそうな結果はDBの突合ワードに追加していく
	* スマートスピーカースキルでいう、スロットの同義語に追加していくイメージ
	* 問題の結果画面に「せいかいにする」ボタンを設置して人判断
* ログ蓄積にはSpreadSheetsに対しdoPost（Webhook）で記録していく実装
	* アプリを開いたときにdoGetで突合ワードを一括取得



## 4. デプロイ環境
* ハマったというわけではないけど、いつも静的ホスティングなら[Firebase](https://firebase.google.com/)を使ってたのにプロジェクトが増えすぎて作れなかったので代替を探してみました
	* 申請すれば増やせるらしいけどすぐ対応してくれるかなんともいえなかったので代替を

### 解決策：[Vercel](https://vercel.com/)にしてみた
* 名前をちょいちょい聞いて気になってた[Vercel](https://vercel.com/)にしてみた
	* GitHubと連携することでPushしたら自動でデプロイしてくれてとてもいい感じ
* 公開リポジトリだったらGitHub Pagesでもいいかも



# おわりに
こどもにはまだスマホを持たせていないため私や妻のスマホでアプリを開いて隣で宿題をやってもらっています。
前は宿題を後回しにしがちでしたがこのアプリならゲーム感覚でできるからか、こどもも喜んですぐに宿題に取り組むようになりました。
私自身もデバッグやユーザーフィードバックを兼ねることで楽しくこどもの宿題に付き合うことができ一石二鳥です😊。