nikkie-ftnextの日記

イベントレポートや読書メモを発信

Pythonの読み上げを聴かせて

はじめに

吉浦康裕スペース、楽しかったー!(配信ありがとうございます) nikkieです。

この記事は、アイの歌声を聴かせてに関する一技術者なりのファン活動の一幕です。

実は私、大好きなアイうたに出てくる"ポンコツAI"シオンを実装しようとしています。
今回はテキストを読み上げるやり方について、これまでに分かったことを書き留めます。
これまではmacOS限定の say コマンドで実装していましたが、機械学習モデルを使った音声合成に入門しました(レベル感としては「やってみた系」です)。

目次

背景:技書博にシオン v0.0.1実装で技術同人誌出すぞー!

放っておいたらC99通っていたけれど、書きたいものが変わっていて参加を断念した2021年末1
晦日に見た『魔女見習いをさがして』がめっちゃよく2、「アイうたファン活動として、2月の技書博6にアイうたの技術同人誌だそう」と決意。😤

ファン活動に邁進するnikkie

しかし、昨今の情勢もあって技書博は中止に😢(仕方ないけれど、残念)

私の中での気持ちの整理として、以下に落ち着きました。

  • 同人誌は無理して書かない
  • ただ、いま作りたいと思っているシオン v0.0.1は3月末までに作る(目標として掲げる)
    • 作りたいものを作らないと、自分が後悔する

そういうわけで、シオン v0.0.1実装中のアウトプットを(多分あと数回)お送りします。
今回は、テキストの読み上げ機能です。

現在のシオン v0.0.1の実装

上のツイートにあるように、スクリプトを実行すると以下のように動きます:

  1. 人が音声で入力
  2. 音声を文字に起こす
  3. 文字列を処理(今はオウム返し)
  4. 処理された文字列を読み上げる

例えば私が「こんにちは」と言うと、「こんにちは」と返します3

処理された文字列を読み上げる

これまでの実装:sayコマンド

「処理された文字列を読み上げる」のはsubprocesssayコマンドを呼び出して実装していました。

import subprocess


def call_say_command(sentence: str):  # 処理された文字列がsentenceに渡されます
    subprocess.run(["say", sentence])

これだとmacOSでは動きますが、他のOSでは動きません。
これを解決したく、機械学習モデルを使ってみることにしました。
上記を解決した先に、Dockerイメージとしてシオンを配布してどんな環境でも動かせたらと思うと、ワクワクしますね。

機械学習モデルを使った現在の実装

import sounddevice as sd
from ttslearn.dnntts import DNNTTS

dnntts_engine = DNNTTS()


def play_text_audio(sentence: str):
    audio_array, sampling_rate = dnntts_engine.tts(sentence)
    sd.play(audio_array, sampling_rate)
    sd.wait()

テキストから音声データを作る部分はttslearnというライブラリを使い、音声データの再生はsounddeviceを使っています。

ttslearn:『Pythonで学ぶ音声合成

テキストの読み上げ(Text-To-Speech:TTS)はすでに使っているsayコマンド以外にも、クラウドベンダーが提供するAPIもあります。
ですが、シオンはスタンドアローンなので、機械学習モデルを選択しました。

TTSの機械学習モデルもWeb上に大量に情報がありますが、今回扱いたいのは日本語の読み上げです。
書名だけ知っていた『Pythonで学ぶ音声合成』が最初に当たるのにぴったりではないかと思い付き、今回参考にしました。

体系だった説明もあるため、音声の扱いの経験が全然ない身には、この本に当たってよかったです。

上記のコードは、Quick startの「DNN音声合成」そのままです。
手元で動かせたので「完全に理解した!」(←分かってない)って感じです。
音声合成の理論面のキャッチアップは長い道のりですね。

sounddevice

ttslearnのコードはJupyter Notebookです。
音声データはNumPyのarray4で表されますが、Notebookではそれを再生するUIを埋め込んで対応しています。
シオンはNotebookではなく、Pythonスクリプトで実装したく、音声データ(NumPy array)の再生方法を調べました。
そこで見つかったのがsounddeviceです。

短いコードで使えるので好印象!
音声データを表すNumPyのarrayをsd.playメソッドに渡すと、音声が再生されます。
続く行でsd.wait()を呼び出すことで、音声の再生が終わるまで待ちます。

sounddeviceを知ったきっかけはRealPythonの Playing and Recording Sound in Python – Real Python です。
simpleaudioもよさそうでしたが、こちらは2021年11月にアーカイブが宣言されていました5

終わりに

シオン v0.0.1の読み上げ機能は、macOS以外の環境でも動作するようになりました!

Pythonで学ぶ音声合成』、これはとてもよさそうな本です。
この本で音声合成を学んで、まだまだシオンを改良できそうです。

アイうたの劇中では、音声合成機能がサラッと出てくるのですが、手を動かした今なら分かります、この実装は相当大変ですよ!
しかもシオンの読み上げには息の音が入りますからね。
フィクションに「魔法」っぽい部分はあっていいと思うんですが、どれくらい魔法か、手を動かして思い知りました。

変更履歴

  • 2022/07/11: 「機械学習モデルを使った現在の実装」にてFix typo(dnntts_enging → dnntts_engine)

  1. Everlasting Diary、#C99A サークル参加断念のお知らせ - nikkie-ftnextの日記

  2. 大晦日に見た『魔女見習いをさがして』が響きました - nikkie-ftnextの日記

  3. 一アイうたファンとしては、これだけでも胸熱です

  4. 今後の鑑賞でシオンが何か言うたびに、「いまNumPyのarray的なデータが裏で処理されているんだ」と一人でエモくなってそうです

  5. https://github.com/hamiltron/py-simple-audio のREADME参照