こんにちは。 学生なので夏休みに入りました。 皆様いかがお過ごしでしょうか。
今回はDiscordの発言内容を自動で文字起こしするDiscord botを書いたので、それについて技術的にはまった点などを備忘録的に書いていきたいと思います。
そもそも何故そんな需要があったかというと、今の私のインターン先でチームで開発をしているのですが、その会議を文字起こししたいという話があったからでした。
今回のbotはNode.js + TypeScriptで記述しました。 普段の私であれば使用言語にPythonを選択するのですが、discord.pyよりもdiscord.jsの方が音声受信周りが充実していたのと、TypeScriptの方が型周りの開発体験が良かった1ので、TypeScriptを書くことにしました。
実際に作ったものはこちらです。
では詳細を見ていきましょう。
Discordの音声を受信してWAVファイルにする
Discordの音声受信はユーザごとに音声を受信します。
今回はユーザが一通りしゃべり終わったタイミングでしゃべった内容を文字起こしにかけ、リアルタイムにその内容をテキストチャンネルに投稿したいので、VoiceConnectionのspeakingイベントを監視して、speakingイベント発火時に録音を開始、createStreamの結果得られたReadableStreamがendの時に取得した録音データを文字起こしにかけ、その結果を発言ユーザの名前と共に開始コマンドが入力されたテキストチャンネルに投稿する、という処理を行っています。
詳しくはこのあたりを参照していただければわかるかと思います。
Google Cloud Speech-to-Text APIを使って文字起こし
文字起こしには、精度がそれなりによいGoogle Cloud Speech-to-Textを利用しました。 と書きましたが、AWSのTranscribeも結構良さげですね。 今まさに悩んでいます2。
まぁとりあえずGoogle Cloud Speech-to-Textで組んだのでその辺の説明をしようと思います。
Google Cloud Speech-to-Textでは、いくつかのファイル形式に対応していますが、WAVが一番扱いやすいと思います。 discord.js側が用意してくれるのは32bitステレオなWAVファイルですが(これについては後述)、Google Cloud Speech-to-Textに渡すためには16bitモノラルなWAVファイルを用意してあげる必要があり、fluent-ffmpegというパッケージを利用してNode.js側からffmpegを叩いて変換をしました。
詳細はsrc/speech.tsを参照してください。
遭遇した問題
ここからは実際に遭遇して、解決が難しかった問題について書いていきます。
discord.jsが実際に吐き出すWAVファイルがガイドの形式と異なる
このことはどのドキュメント、Webサイトにも載っていなかったので確かではないのですが、このガイドに「Signed 16-bit PCM」と書いてあるのに対し、手元で実行した結果得られたファイルをAudacityにかけてみると、32bitにしたところ正常に再生されました。
何らかの仕様が変わってそうなった可能性が高そうな気がしますが、これはどこが悪いのかわからない(そもそもDiscord側からはopus形式で来ていて、それを手元でWAVに変換しているっぽい)ので、何とも言えません。
とにかく、fluent-ffmpegの読み込み形式を s32le
にしたところ正常に音声認識されるようになったので、そのようにしました。
一定時間が経過するとVoiceConnectionのspeakingイベントが発火しなくなる現象への対処
このこともどのドキュメント、Webサイトにも載っていなかったので確かではないのですが、VoiceConnectionを張って一定時間経過すると、speakingイベントが発火しなくなる現象がこちらの手元で起きました。 これへの対処としては、一定時間毎に無音をplayしてやることで回避することができました。
これらの問題は二つともなんか腑に落ちませんが、まぁこの対処で動いているので良しとしました。 動くことこそ正義3!
おわりに
今回は簡単にですが、自動で発言内容を文字起こしするDiscord botについて説明しました。
Google Cloud Speech-to-TextやAmazon Transcribeの精度は実用には厳しいですが、非常に使いやすいものにはなってきているかと思います。 今回はDiscord botとして実装しましたが、ほかにも活用法を検討中です。 文字起こしを使ってこういうサービスが欲しい!などあればぜひお話をお聞かせください。