🎙️

簡単に原稿を読み上げて録音できるウェブアプリケーションをStreamlitで作った

2022/03/07に公開


Tojiが動作している様子

概要

YouTubeなどの動画サイトを開くと、最近流行りのVTuberやゲーム実況などで、音声合成や声質変換を活用している様子を見かけることが多くなりました。私はゆっくり実況を見すぎて逆にあれが自然に思えてくるくらいの人間なのですが、自然な音声合成や声質変換の技術発展には目を見張るものがあります。

私も興味があってそうした音声情報処理の世界に片足を突っ込み始めているのですが、ディープニューラルネットを利用した各種タスクでは、ドメインが変わったとしてもデータ量が物を言う世界には違いがないようです。音声に関する代表的なデータセットとしては、Common Voice声優統計コーパスJUSTコーパスJVSコーパスなどがあります。こうしたコーパスや音源を利用することで気軽に各種タスクに適用することができますが、既存製品と肩を並べたり実用に耐えうるものを作ったり、または自分の声で音声合成をするといった場合では、何かしら独自にデータを集める必要が出てきます。その際にはマイクに向かってひたすら喋るだけで必要なデータを集められるというわけではなく、録音した音声の切り出しや頭出しをしたり、音声から文字起こしをしたりと、色々と面倒な処理が必要になってきます。

そこで、今回は原稿を読み上げて音声データを収集するという部分にフォーカスして、簡単に原稿を読み上げて録音できるウェブアプリケーションを作成しました。テキストファイルの原稿を用意し、ウェブアプリのテキストエリアに貼り付けるだけで画面に原稿が表示され、録音開始ボタンを押して話し始めて録音終了を押すだけで、その原稿単体の読み上げ音声ファイルを手軽に得ることができます。

この記事では、作成したウェブアプリケーションの主な使い方を紹介します。また、PythonのStreamlitというフレームワークを利用して作成していますので、実装の裏側も紹介できればと思います。ただし、自分のPCでDockerなどを動かすことが必要であり、まだウェブアプリ自体は開発中でありバグ等が含まれている可能性もありますので、自身で環境を準備できたり予期せぬエラー等に対応できる方を対象としている点をご容赦下さい。

ソースコード

https://github.com/yagays/toji

レポジトリ名の由来である「東寺」は、音声コーパスを触ったことがある人ならおそらくピンと来たと思いますが、声優統計コーパスの1文目の最初に出てくる象徴的な単語から来ています。不動明王が主要な明王の中央に配されていることで知られるお寺です(詳しくは知りません……)。

使い方

Tojiは現在開発中であり、本記事では執筆時点のものであることをご承知ください。

1. Tojiを起動する

TojiはDockerで起動することができます。

$ git clone https://github.com/yagays/toji
$ cd toji
$ docker build -t toji .
$ docker run -p 8501:8501 --rm -t -i toji

起動したことを確認したら、 http://localhost:8501/ にアクセスします。すると以下のような画面が表示されます。


起動直後の画面

ここではサンプルとして以下のような読み上げ文を用意しました。

吾輩は猫である。名前はまだ無い。
どこで生れたかとんと<ruby><rb>見当</rb><rt>けんとう</rt></ruby>がつかぬ。
何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。

夏目漱石 吾輩は猫であるより

それでは始めましょう。

2. テキストを表示して読み上げを録音する

まず左上のテキストエリアに先ほどの読み上げ文を入力し確定します。そうすると、右側に1行目の読み上げ文が表示されます。


読み上げ画面

この状態で、画面中央にあるSTARTという赤いボタンを押すと録音が開始されます。読み上げが完了したら、STOPボタンを押して録音を終了します。右側のSELECT DEVICEを押すと、マイクなど入力デバイスの種類を変更することができます。


録音中の画面


録音完了の画面

録音が完了すると画面下部に音声再生ウィジェットが表示され、先ほど録音した音声を確認することが出来ます。

これで一つの読み上げ文の作業は終了です。画面上部のPrevious,Nextボタンを押すことで、次の原稿や一つ前の原稿に移動することができます。原稿の遷移によって録音した音声は消えることはありませんが、ブラウザのリロードやアプリ自体の再起動をすると録音音声や原稿は消えてしまいます。また、再度STARTボタンを押して録音を開始すると、この読み上げ文で前回録音した音声は上書きされてしまいますので注意下さい。

また、Tojiではルビの表示に対応しています。読みが難しい漢字にふりがなを振ることで、話者による読み間違いを防ぐことができます。記法はHTMLの<ruby>タグと同じです。主要なブラウザでは<ruby>タグに対応しているので、下記のような記法が可能です。

どこで生れたかとんと<ruby><rb>見当</rb><rt>けんとう</rt></ruby>がつかぬ。


漢字にルビを付与した表示

3. 音声ファイルを保存する

最後に左下のProceed to DownloadからDownloadボタンをクリックして、音声ファイルをZip圧縮のファイルとして保存できます。この中には音声ファイルに加えて、音声ファイルと読み上げ文を紐付けるmeta.jsonというファイルが含まれています。


左下のDownloadからZip圧縮ファイルをダウンロードする


保存されたファイルの中身

基本的な機能は以上です。あとは原稿を用意して録音するだけです。ここから先は根気が必要で泥臭い作業ですが、頑張りましょう!

実装の詳細

さて、ここからはTojiの実装について少し解説していきます。

TojiはPythonのみで実装しており、Streamlitというデータの可視化や機械学習のGUIインターフェイスとしてよく使われる人気のウェブフレームワークを利用しています。また、音声録音にはstreamlit-webrtcを使っています。

機能単位で見ていくと、以下のような感じです。

  • サイドバー
  • メインウィンドウ
    • Previous/Nextボタン & 状態管理: streamlit (st.button)
    • 原稿表示: streamlit (st.markdown)
    • 音声録音: streamlit-webrtc
    • 音声再生: streamlit (st.audio)

ほぼすべての機能がStreamlitで完結していることがわかります。プログレスバーや音声ファイルの再生も標準の機能として完備されており、とても便利です。

Session State

さて、このようなアプリを作成する上で必須となるのが、同一セッション内での情報の保存と更新です。今回のアプリケーションの場合、原稿のうち何番目を表示しているかという情報ですね。Previous/Nextのボタンでこの番号を増やしたり減らしたりして、画面に表示する文字の制御や録音ファイルのID付与などを行っています。

Session State - Streamlit Docs

ちなみにSession Stateは昔は野良実装があってそれを使うことで実現できていましたが、現在では公式で実装されました。

streamlit-webrtc

そしてもう一つ大事なのが音声の録音をひとえに担うstreamlit-webrtcです。WebRTCの機能をStreamlitから使えるようにしてくれる3rd partyのパッケージで、カメラやマイクといったインターフェイスの入力をStreamlit上で容易に扱うことができます。GitHubのレポジトリ内で色々な使い方が紹介されているので、ぜひ参考にしてみてください。

https://github.com/whitphx/streamlit-webrtc

Tojiでは、以下のデモアプリの実装を参考にさせていただきました。

https://github.com/whitphx/streamlit-stt-app

実際にやってみた

このウェブアプリケーションを使って、私が実際に読み上げをしてみた体験記事を書きましたので、こちらもぜひ御覧ください。実際の発声や読み上げの難しさがわかるかもしれません。

https://zenn.dev/yag_ays/articles/8d2fc207975953

まとめ

今回は音声認識や音声合成などの音声情報処理に利用する学習データを収集するためのウェブアプリケーションを、PythonとStreamlitで作成しました。シンプルなアプリケーションではありますが、こうしたちょっとした工夫で音声切り出しや頭出しといった面倒な処理から開放されます。やはりデータセットを大量に集める上では、いかに煩雑な作業から開放されて本質的なデータ収集部分に注力するかが大事です。

あまり本格的なアプリケーションにする予定はなく、あくまで自分や他の個人の人が使うくらいのシンプルかつ単純な作りを維持しようと思っています。Pythonを書ける人ならば容易に改造ができると思いますので、ぜひご自身の環境に合うようにカスタマイズしてみてください。

Discussion