🎤

discordでつくよみちゃんの読み上げbotを作ってみる [Python]

2021/12/14に公開約2,300字47件のコメント

法政大学 Advent Calendar 2021 10日目穴埋めのほっとここあです。

discordのボイスチャットに入っているけど夜中で静かにしなきゃいけなく、テキストで通話に参加したいみたいな場面があると思います。
そういう時に使うのがdiscordの読み上げbotなのですが、無料で使えるもののほとんどは人工的な声が多く、発音が若干悪いため何を言ってるか分からないときがあります。

というわけで誰でも無料で使えそうなつくよみちゃんというテキスト読み上げモデルを使って自然に喋る読み上げbotを作成してみようと思います。

簡単に作ってみる

つくよみちゃん(TsukuyomichanTalksoft)をインストールします。
以下の記事の「Bot用のトークンを手に入れる」を参考にdiscordのbotのトークンを取得します。
https://qiita.com/PinappleHunter/items/af4ccdbb04727437477f
ひとまず形だけつくよみちゃんができました。

ローカルで以下のpythonコードを書いて実行します。

app.py
import discord
from discord.channel import VoiceChannel
from discord.player import FFmpegPCMAudio
import pydub
from tsukuyomichan_talksoft import TsukuyomichanTalksoft
tsukuyomichan_talksoft = TsukuyomichanTalksoft(model_version='v.1.2.0')
fs = 24000

TOKEN = '作ったbotのTokenをここに貼ってください'
client = discord.Client()

voiceChannel: VoiceChannel 

@client.event
async def on_ready():
    print('つくよみちゃんがサーバーにログインしました!')

@client.event
async def on_message(message):
    global voiceChannel

    if message.author.bot:
        return
    if message.content == '!tukuyomi':
        voiceChannel = await VoiceChannel.connect(message.author.voice.channel)
        await message.channel.send('つくよみちゃんが参加したよ!')
        return
    elif message.content == '!dtukuyomi':
        voiceChannel.stop()
        await message.channel.send('つくよみちゃんが退出したよ!')
        await voiceChannel.disconnect()
        return

    play_voice(message.content)

def play_voice(text):
    wav = tsukuyomichan_talksoft.generate_voice(text, 0)
    sound = pydub.AudioSegment.from_wav("out.wav")
    sound.export("out.mp3", format="mp3")
    voiceChannel.play(FFmpegPCMAudio("out.mp3"))

client.run(TOKEN)
$ python3 app.py

discordのボイスチャンネルに入って!tukuyomiと打つとdiscordでつくよみちゃんが起動します。適当な文章を書くと可愛い声で読み上げてくれると思います。

ちなみに!dtukuyomiでbotを切断できます。

感想

とてもいい感じだな~と思ったのですが、GPUでないとつくよみちゃんの反応が遅く、タイムアウトしてしまう時があったのでGPUでの使用をおすすめします。他にも Cloud Text-to-Speechのような高性能なものもありますが、お金がかかるため少し悩んでしまうなーという感じもします。

その他

つくよみちゃんのイラスト描きました。botを作るときなど好きに使ってください。

Discussion

ほっとここあ様のご協力を元に、少し補足させて頂きます。

  1. Python 3.7とGitをインストール
    3.8以上だと動かないっぽいです。3.7.9がおすすめです。
  2. discord.py[voice]とpydubをインストール
pip install discord.py[voice]
pip install pydub
  1. FFmpegをインストール
    こちらのサイトを参考にしてインストールしてください。

https://rikoubou.hatenablog.com/entry/2019/11/07/144533

  1. pyopenjtalk 0.1.3をインストール
    pip install pyopenjtalkがうまく行かないので、ソースコードをダウンロードして少し編集しないといけないようです。
    この2つのサイトが参考になりました。

https://note.com/fms_moriselab/n/nc689d98abbf0
https://teitoku-window.hatenablog.com/entry/2021/09/17/004040

  1. tsukuyomichan-talksoftをインストール
pip install git+https://github.com/shirowanisan/tsukuyomichan-talksoft
  1. espnet2を0.10.4にアップグレード
pip install espnet==0.10.4

tsukuyomichan-talksoft 0.0.1.dev1 requires espnet==0.10.0, but you have espnet 0.10.4 which is incompatible.
と言われますがスルーして大丈夫です。

  1. tsukuyomichan_talksoft.pyを編集
    このコマンドでファイルの場所を調べてください。
pip show tsukuyomichan-talksoft
Name: tsukuyomichan-talksoft

Location: c:\users\user\appdata\local\programs\python\python37\lib\site-packages

ファイルの内容を以下に書き換えてください。

import numpy as np
import torch
from espnet2.bin.tts_inference import Text2Speech
from parallel_wavegan.utils import load_model
import soundfile

from tts_config import TTSConfig

class TsukuyomichanTalksoft:
    def __init__(self, model_version='v.1.2.0'):
        self.config: TTSConfig = TTSConfig.get_config_from_version(model_version)
        self.acoustic_model = self.get_acoustic_model()
        self.vocoder = self.get_vocoder()

    def get_acoustic_model(self):
        acoustic_model = Text2Speech(
            self.config.acoustic_model_config_path,
            self.config.acoustic_model_path,
            device=self.config.device,
            threshold=0.5,
            minlenratio=0.0,
            maxlenratio=10.0,
            use_att_constraint=False,
            backward_window=1,
            forward_window=3
        )
        acoustic_model.spc2wav = None
        return acoustic_model

    def get_vocoder(self):
        vocoder = load_model(self.config.vocoder_model_path).to(self.config.device).eval()
        vocoder.remove_weight_norm()
        return vocoder

    def generate_voice(self, text, seed):
        np.random.seed(seed)
        torch.manual_seed(seed)
        with torch.no_grad():
            speech = self.acoustic_model(text)
            if self.config.use_vocoder_stats_flag:
                mel = self.config.scaler.transform(speech["feat_gen_denorm"].cpu())
            wav = self.vocoder.inference(mel)
        wav = wav.view(-1).cpu().numpy()
        soundfile.write("out.wav", wav, self.acoustic_model.fs, "PCM_16")
        return wav

Hayate様のコメントを参考に現在Botを開発しているものです
窓を作っては壊し~様のブログを元にpyopenjtalkをダウンロードし、
pip show pyopenjtalk でダウンロードできていることも確認できました
しかしつくよみちゃんのダウンロードを行う際に
note: This error originates from a subprocess, and is likely not a problem with pip.
ERROR: Failed building wheel for pyopenjtalk
Failed to build pyopenjtalk
ERROR: Could not build wheels for pyopenjtalk, which is required to install pyproject.toml-based projects
との形でエラーを吐いてしまいました
プログラミングへの理解が足りず、原因を特定できずにいます
もし何かわかることがあれば教えていただきたいです

sugar_oさん、こんにちは。コメントありがとうございます。
OSは何を使用していますか?

Windows10を使用しています
Hayate様の書いた通りPythonも3.7を使用しました

仮想環境に新規でWindows 11をインストールして試してみましたが、問題なくインストール出来ました。
Python 3.7.9では正常に動作することを確認しましたので、もし3.7.9でなければ3.7.9に変更してみるといいかもしれないです。
ほっとここあさんのお力も借りたいところです...

3.7.0を使用していたため、3.7.9に変更してみましたが結果は変わらずでした
setup.py:26: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
_CYTHON_INSTALLED = ver >= LooseVersion(min_cython_ver)
error: command 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\HostX86\x64\cl.exe' failed with exit code 2
[end of output]
以前張らせていただいたエラー文の前の文章をコピペしてきました
こちらの文章から何かわかったりはするでしょうか

1箇所PATHが通ってないかもしれないです!
Visual Studioが2019の場合は下のPATHを追加してみてください。

「C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64」

PATHを追加してみたところ、同じエラー文が現れました
pyopenjtalkの再インストールも行ってみましたが結果は変わりませんでした
しかしエラー文が増えていた?ようでこちらも手掛かりになるかと考えます
少々長い分になってしまうのですがご了承ください
char_property.cpp
C:\Users\mcfnh\AppData\Local\Temp\pip-install-w2is4psw\pyopenjtalk_3b4ebcb247f04e89a28bf27c845467e0\lib\open_jtalk\src\mecab\src\utils.h(47): warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
C:\Users\mcfnh\AppData\Local\Temp\pip-install-w2is4psw\pyopenjtalk_3b4ebcb247f04e89a28bf27c845467e0\lib\open_jtalk\src\mecab\src\mmap.h(67): error C3646: 'hFile': 不明なオーバーライド指定子です
C:\Users\mcfnh\AppData\Local\Temp\pip-install-w2is4psw\pyopenjtalk_3b4ebcb247f04e89a28bf27c845467e0\lib\open_jtalk\src\mecab\src\mmap.h(214): note: コンパイル対象の クラス テンプレート インスタンス化 'MeCab::Mmap<T>' のリファレンスを 確認してください
C:\Users\mcfnh\AppData\Local\Temp\pip-install-w2is4psw\pyopenjtalk_3b4ebcb247f04e89a28bf27c845467e0\lib\open_jtalk\src\mecab\src\mmap.h(67): error C4430: 型指定子がありません - int と仮定しました。メモ: C++ は int を既定値としてサポートしていません
C:\Users\mcfnh\AppData\Local\Temp\pip-install-w2is4psw\pyopenjtalk_3b4ebcb247f04e89a28bf27c845467e0\lib\open_jtalk\src\mecab\src\mmap.h(68): error C3646: 'hMap': 不明なオーバーライド指定子です
C:\Users\mcfnh\AppData\Local\Temp\pip-install-w2is4psw\pyopenjtalk_3b4ebcb247f04e89a28bf27c845467e0\lib\open_jtalk\src\mecab\src\mmap.h(68): error C4430: 型指定子がありません - int と仮定しました。メモ: C++ は int を既定値としてサポートしていません
C:\Users\mcfnh\AppData\Local\Temp\pip-install-w2is4psw\pyopenjtalk_3b4ebcb247f04e89a28bf27c845467e0\lib\open_jtalk\src\mecab\src\scoped_ptr.h(84): warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
C:\Users\mcfnh\AppData\Local\Temp\pip-install-w2is4psw\pyopenjtalk_3b4ebcb247f04e89a28bf27c845467e0\lib\open_jtalk\src\mecab\src\scoped_ptr.h(90): warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
lib\open_jtalk\src\mecab/src\char_property.cpp(269): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details
mcfnhは私のユーザー名になります
頼りきりで申し訳ございません

ちょっと試してみましたが、「pip install pyopenjtalk」でpyopenjtalkをインストールをした後、tsukuyomichan-talksoftをインストールしようとするとそのエラーが出るようです。

こちらのサイトの方法でpyopenjtalkのインストールを行うと解決するかもしれないです。(Anacondaは飛ばしてOKです)
https://note.com/fms_moriselab/n/nc689d98abbf0#4Wh4y

何度も申し訳ございません 記事内に「最後にanacondaを開き~」
と書いてあるのですがanacondaを飛ばす場合どのように
pyopenjtalkのインストールを行えばよいのでしょうか
コマンドプロンプトを使用するべきですか?

説明が漏れていて申し訳ないです。
コマンドプロンプトかPowerShellでpyopenjtalkのフォルダに移動して「pip install -e .」で出来ると思います!

pyopenjtalkのインストールを行い、再度つくよみちゃんのインストールを行ったのですが
以前と同じエラーを吐いてしまいました
ffmpeg、pyopenjtalkともにダウンロード確認済みです…

pyopenjtalkはバージョン0.1.3で動作確認したので0.1.3でなければ変更してみてください!

インストール成功しました!本当にありがとうございました!
良ければなのですがインストール後の手順について詳しく教えていただきたいです
Pythonへの理解が足らず、ネットで調べてもコマンドプロンプトにどのように打てばいいのかが
分かりません…

良かったです!

botを動かす方法は、
最初にbot用のフォルダを作成して、その中にテキストドキュメントを作成し、そこにこの記事のコードをコピーしてください。9行目のTOKENだけ書き換えてください。

次にPythonで実行するために拡張子をpyに変更します。名前を「○○.py」に変更してください。app.pyとかbot.pyで大丈夫です。
標準では拡張子が表示されない設定になっているので、設定も併せて確認してください。

最後にコマンドプロンプトかPowerShellでこのフォルダを開き、「python3 ○○.py」と打てば実行できると思います。

ごめんなさい、今気づきました...Hayateさんの言う通りで新しくapp.pyというファイルを作成して
以下をファイルに書いてください。TOKEN = '作ったbotのTokenをここに貼ってください'には自分のTOKENを貼り付けてください。

import discord
from discord.channel import VoiceChannel
from discord.player import FFmpegPCMAudio
import pydub
from tsukuyomichan_talksoft import TsukuyomichanTalksoft
tsukuyomichan_talksoft = TsukuyomichanTalksoft(model_version='v.1.2.0')
fs = 24000

TOKEN = '作ったbotのTokenをここに貼ってください'
client = discord.Client()

voiceChannel: VoiceChannel 

@client.event
async def on_ready():
    print('つくよみちゃんがサーバーにログインしました!')

@client.event
async def on_message(message):
    global voiceChannel

    if message.author.bot:
        return
    if message.content == '!tukuyomi':
        voiceChannel = await VoiceChannel.connect(message.author.voice.channel)
        await message.channel.send('つくよみちゃんが参加したよ!')
        return
    elif message.content == '!dtukuyomi':
        voiceChannel.stop()
        await message.channel.send('つくよみちゃんが退出したよ!')
        await voiceChannel.disconnect()
        return

    play_voice(message.content)

def play_voice(text):
    wav = tsukuyomichan_talksoft.generate_voice(text, 0)
    sound = pydub.AudioSegment.from_wav("out.wav")
    sound.export("out.mp3", format="mp3")
    voiceChannel.play(FFmpegPCMAudio("out.mp3"))

client.run(TOKEN)

そのあとそのファイルを以下のように実行してください。

python3 app.py

誰も見ないだろうと思って書いた記事なのですが結構頻繁にお問い合わせがくるので初心者向けに書き直そうと思います。

ボイスチャンネルに参加し、!tukuyomi と入力しても反応がありませんでした

コマンドプロンプトで

python3 app.py

を実行した時どんな表示が出ていますか?

Python と表示されました

Windowsだとpython3ではなくpythonじゃないとダメだと思います。

pythonに変更してみたところ
Traceback (most recent call last):
File "app.py", line 4, in <module>
import pydub
ModuleNotFoundError: No module named 'pydub'
という表記がされました

あ~そういえばそれもありましたね!pip install pydubを試してみてください。

pip install pydub でpydubをインストール後、再びpython app.pyを試したところ
Access denied with the following error:

    Cannot retrieve the public link of the file. You may need to change
    the permission to 'Anyone with the link', or have had many accesses.

You may still be able to access the file from the browser:

     https://drive.google.com/uc?id=1scfGUohN2QTT4w6XTrKX2FPvm8yuhA1f

Traceback (most recent call last):
File "app.py", line 6, in <module>
tsukuyomichan_talksoft = TsukuyomichanTalksoft(model_version='v.1.2.0')
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\site-packages\tsukuyomichan_talksoft.py", line 11, in init
self.config: TTSConfig = TTSConfig.get_config_from_version(model_version)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\site-packages\tts_config.py", line 55, in get_config_from_version
cls.download_model(download_path, model_path, model_url)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\site-packages\tts_config.py", line 78, in download_model
with zipfile.ZipFile(zip_path) as model_zip:
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 1240, in init
self.fp = io.open(file, filemode)
FileNotFoundError: [Errno 2] No such file or directory: './models/TSUKUYOMICHAN_MODEL_v.1.2.0.zip'
と帰ってきました

つくよみちゃんがVCへの入室、退出は行えるようになったのですが
肝心の読み上げ機能が機能しませんでした
Downloading: "https://downloads.sourceforge.net/open-jtalk/open_jtalk_dic_utf_8-1.11.tar.gz"
Ignoring exception in on_message
Traceback (most recent call last):
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\urllib\request.py", line 1350, in do_open
encode_chunked=req.has_header('Transfer-encoding'))
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\http\client.py", line 1277, in request
self._send_request(method, url, body, headers, encode_chunked)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\http\client.py", line 1323, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\http\client.py", line 1272, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\http\client.py", line 1032, in _send_output
self.send(msg)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\http\client.py", line 972, in send
self.connect()
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\http\client.py", line 1447, in connect
server_hostname=server_hostname)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\ssl.py", line 423, in wrap_socket
session=session
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\ssl.py", line 870, in _create
self.do_handshake()
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\ssl.py", line 1139, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1091)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\client.py", line 343, in run_event
await coro(*args, **kwargs)
File "app.py", line 34, in on_message
play_voice(message.content)
File "app.py", line 37, in play_voice
wav = tsukuyomichan_talksoft.generate_voice(text, 0)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\site-packages\tsukuyomichan_talksoft.py", line 39, in generate_voice
speech = self.acoustic_model(text)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\site-packages\torch\autograd\grad_mode.py", line 28, in decorate_context
return func(*args, **kwargs)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\site-packages\espnet2\bin\tts_inference.py", line 180, in call
text = self.preprocess_fn("<dummy>", dict(text=text))["text"]
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\site-packages\espnet2\train\preprocessor.py", line 306, in call
tokens = self.tokenizer.text2tokens(text)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\site-packages\espnet2\text\phoneme_tokenizer.py", line 512, in text2tokens
tokens = self.g2p(line)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\site-packages\espnet2\text\phoneme_tokenizer.py", line 79, in pyopenjtalk_g2p_accent_with_pause
for labels in pyopenjtalk.run_frontend(text)[1]:
File "c:\users\mcfnh\desktop\pyopenjtalk-0.1.3.tar\dist\pyopenjtalk-0.1.3\pyopenjtalk-0.1.3\pyopenjtalk_init.py", line 151, in run_frontend
lazy_init()
File "c:\users\mcfnh\desktop\pyopenjtalk-0.1.3.tar\dist\pyopenjtalk-0.1.3\pyopenjtalk-0.1.3\pyopenjtalk_init.py", line 60, in _lazy_init
extract_dic()
File "c:\users\mcfnh\desktop\pyopenjtalk-0.1.3.tar\dist\pyopenjtalk-0.1.3\pyopenjtalk-0.1.3\pyopenjtalk_init.py", line 48, in _extract_dic
urlretrieve(_DICT_URL, filename)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\urllib\request.py", line 247, in urlretrieve
with contextlib.closing(urlopen(url, data)) as fp:
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\urllib\request.py", line 222, in urlopen
return opener.open(url, data, timeout)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\urllib\request.py", line 525, in open
response = self._open(req, data)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\urllib\request.py", line 543, in _open
'_open', req)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\urllib\request.py", line 503, in _call_chain
result = func(*args)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\urllib\request.py", line 1393, in https_open
context=self._context, check_hostname=self._check_hostname)
File "C:\Users\mcfnh\AppData\Local\Programs\Python\Python37\lib\urllib\request.py", line 1352, in do_open
raise URLError(err)
urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1091)>
こちらのメッセージがコマンドプロンプトに書かれていました

解決になるかは分からないですが以下のapp.pyでもう一度試してもらってもいいでしょうか(TOKENを貼るのを忘れずに)

import ssl
ssl._create_default_https_context = ssl._create_unverified_context
import discord
from discord.channel import VoiceChannel
from discord.player import FFmpegPCMAudio
import pydub
from tsukuyomichan_talksoft import TsukuyomichanTalksoft
tsukuyomichan_talksoft = TsukuyomichanTalksoft(model_version='v.1.2.0')
fs = 24000

TOKEN = '作ったbotのTokenをここに貼ってください'
client = discord.Client()

voiceChannel: VoiceChannel 

@client.event
async def on_ready():
    print('つくよみちゃんがサーバーにログインしました!')

@client.event
async def on_message(message):
    global voiceChannel

    if message.author.bot:
        return
    if message.content == '!tukuyomi':
        voiceChannel = await VoiceChannel.connect(message.author.voice.channel)
        await message.channel.send('つくよみちゃんが参加したよ!')
        return
    elif message.content == '!dtukuyomi':
        voiceChannel.stop()
        await message.channel.send('つくよみちゃんが退出したよ!')
        await voiceChannel.disconnect()
        return

    play_voice(message.content)

def play_voice(text):
    wav = tsukuyomichan_talksoft.generate_voice(text, 0)
    sound = pydub.AudioSegment.from_wav("out.wav")
    sound.export("out.mp3", format="mp3")
    voiceChannel.play(FFmpegPCMAudio("out.mp3"))

client.run(TOKEN)

もう一度試したら治りました!ご協力本当にありがとうございました
最後に一つお聞きしたいことがありまして、GPUでの使用方法を教えていただきたいです

CUDAとcuDNNが入っていないのではないでしょうか?
以下の記事などを参考にして環境構築する必要があります。
https://rupic.hatenablog.com/entry/2020/03/24/021455

もし正しく環境構築できていれば、同じようにpython app.pyを実行するだけで勝手にGPUをつかんでくれると思います。

何から何まで本当にありがとうございました!
Pythonを履修していない私だけでは完成させることが出来なかったと思います

こちらこそありがとうございます!また何かあればお話しましょう!

ほっとここあさん> もし自分に何かお手伝い出来ることがあれば言ってください!

こちらで,Hayate様のコメントを参考に,Windows10で読み上げBOTを開発しています。
どうにか完成にはこぎ着けたのですが,特定のチャンネルのみの発言を反応させたいと思っており,どうにか修正したいと考えています。
つきましては,方法がありましたら教えて頂きたく存じます。

discordのユーザー設定で開発者モードをオンにしておきます。

オンにするとチャンネルを右クリックした際にチャンネルIDをコピーできるようになるので、発言に反応させたいチャンネルIDをコピーします。

以下は特定のチャンネルのみ反応するように書き加えたapp.pyで
channel_id = 01234567890123456789
と書いてある部分の右辺を消して、代わりに先ほどコピーしたIDを貼り付けてください。

import discord
from discord.channel import VoiceChannel
from discord.player import FFmpegPCMAudio
import pydub
from tsukuyomichan_talksoft import TsukuyomichanTalksoft
tsukuyomichan_talksoft = TsukuyomichanTalksoft(model_version='v.1.2.0')
fs = 24000

TOKEN = '作ったbotのTokenをここに貼ってください'
client = discord.Client()

voiceChannel: VoiceChannel 

@client.event
async def on_ready():
    print('つくよみちゃんがサーバーにログインしました!')

@client.event
async def on_message(message):
    global voiceChannel
    channel_id = 01234567890123456789
    if message.channel.id != channel_id:
        return

    if message.author.bot:
        return
    if message.content == '!tukuyomi':
        voiceChannel = await VoiceChannel.connect(message.author.voice.channel)
        await message.channel.send('つくよみちゃんが参加したよ!')
        return
    elif message.content == '!dtukuyomi':
        voiceChannel.stop()
        await message.channel.send('つくよみちゃんが退出したよ!')
        await voiceChannel.disconnect()
        return

    play_voice(message.content)

def play_voice(text):
    wav = tsukuyomichan_talksoft.generate_voice(text, 0)
    sound = pydub.AudioSegment.from_wav("out.wav")
    sound.export("out.mp3", format="mp3")
    voiceChannel.play(FFmpegPCMAudio("out.mp3"))

そのあとは同様に

python disbot.py

を実行すれば指定した特定のチャンネルのみに反応すると思います。

できました!ありがとうございました。

すみません、2度目の質問となってしまうのですが、URLや絵文字・メンションなどの特定の要件を満たす文字列を無視して読み上げることって可能なんでしょうか…?

すみません、返信遅くなりました。
URLを無視して読み上げる方法は以下の通りです。

  • 一番上の当たりにこれを追加
import re
  • def play_voiceの前をこのように少し編集
msg = re.sub(r"https?://[\w/:%#\$&\?\(\)~\.=\+\-]+", "", message.content)
play_voice(msg)

絵文字とメンションについては方法が分かりませんでしたが多分出来ると思います。また調べてみます。

ありがとうございます!
私も絵文字のやり方を考えたのですがやはり分かりません。
あとは辞書機能なども追加できればと思っているところです。
また分からないところがあったら質問させていただくかもしれませんがよろしくお願いします。

力不足で申し訳ないです...
また質問お待ちしてます。

はじめまして。
ほっとここあさんとHayateさんの説明を参考にBOT開発に挑戦中のものです。
環境構築などを進めていきましたが、音声出力が出来ない状況です。
sugar_oさんとのやり取りを参考にしていましたがその後下記のFileNotFoundErrorが出てしまい動きませんでした。

つくよみちゃんがサーバーにログインしました!
Ignoring exception in on_message
Traceback (most recent call last):
  File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\client.py", line 343, in _run_event
    await coro(*args, **kwargs)
  File "C:\Users\user\Documents\tukuyomi.py", line 36, in on_message
    play_voice(message.content)
  File "C:\Users\user\Documents\tukuyomi.py", line 40, in play_voice
    sound = pydub.AudioSegment.from_wav("out.wav")
  File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\site-packages\pydub\audio_segment.py", line 808, in from_wav
    return cls.from_file(file, 'wav', parameters=parameters)
  File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\site-packages\pydub\audio_segment.py", line 651, in from_file
    file, close_file = _fd_or_path_or_tempfile(file, 'rb', tempfile=False)
  File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\site-packages\pydub\utils.py", line 60, in _fd_or_path_or_tempfile
    fd = open(fd, mode=mode)
FileNotFoundError: [Errno 2] No such file or directory: 'out.wav'

自身では解決出来ず、この場を借りて相談させて頂きました。
なにかご存知でしたら教えていただけないでしょうか。
よろしくお願いいたします。

以下の手順を実行してみてください~

  1. tsukuyomichan_talksoft.pyを編集
    このコマンドでファイルの場所を調べてください。
pip show tsukuyomichan-talksoft
Name: tsukuyomichan-talksoft

Location: c:\users\user\appdata\local\programs\python\python37\lib\site-packages

表示されたディレクトリに移動し、ファイルの内容を以下に書き換えてください。

import numpy as np
import torch
from espnet2.bin.tts_inference import Text2Speech
from parallel_wavegan.utils import load_model
import soundfile

from tts_config import TTSConfig

class TsukuyomichanTalksoft:
    def __init__(self, model_version='v.1.2.0'):
        self.config: TTSConfig = TTSConfig.get_config_from_version(model_version)
        self.acoustic_model = self.get_acoustic_model()
        self.vocoder = self.get_vocoder()

    def get_acoustic_model(self):
        acoustic_model = Text2Speech(
            self.config.acoustic_model_config_path,
            self.config.acoustic_model_path,
            device=self.config.device,
            threshold=0.5,
            minlenratio=0.0,
            maxlenratio=10.0,
            use_att_constraint=False,
            backward_window=1,
            forward_window=3
        )
        acoustic_model.spc2wav = None
        return acoustic_model

    def get_vocoder(self):
        vocoder = load_model(self.config.vocoder_model_path).to(self.config.device).eval()
        vocoder.remove_weight_norm()
        return vocoder

    def generate_voice(self, text, seed):
        np.random.seed(seed)
        torch.manual_seed(seed)
        with torch.no_grad():
            speech = self.acoustic_model(text)
            if self.config.use_vocoder_stats_flag:
                mel = self.config.scaler.transform(speech["feat_gen_denorm"].cpu())
            wav = self.vocoder.inference(mel)
        wav = wav.view(-1).cpu().numpy()
        soundfile.write("out.wav", wav, self.acoustic_model.fs, "PCM_16")
        return wav

Hayateさん
返信ありがとうございます!
tsukuyomichan_talksoft.pyの中身を貼ってくださったのに変えたのですが、変わらずエラーが出てしまいます。
読み上げbotのpyの39行目
wav = tsukuyomichan_talksoft.generate_voice(text, 0)
ここが怪しそうではあるのですが、、、pythonのデバッグモードが出来る環境構築をして自分でももっと調べてみます。。

ここあさんとHayateさんのものをもとに、読み上げるところまで成功することができました!個人的に、名前の読み上げ機能を追加したくて、play_voice()に複数のmessage.authorを入れてみたり、play_voiceを複数使ってみたいしたんですけど、複数のものを入れられないと言われてしまいます、、どうすればいいでしょうか、、

こんにちは。
こちらの部分を、

play_voice(message.content)

このように書き換えてみてください。

msg = (message.author.display_name + 'さん。' + message.content)
play_voice(msg)

はじめまして、こちらのサイトを参考にwindowsで初めてdiscordのbotを作成している者です。
pythonも初めて触るため、いろいろ調べながら挑戦しているのですが、
tsukuyomichan_talksoft.pyを編集して、読み上げBOT用のフォルダ内のmodelsのなかに
zipファイルを入れるところまでできたので、実際にコマンドプロンプトで実行してみたところ
botがサーバーにログインはできるのですが、VCへ入室してもらうためのコマンドを打つと
下記のようなエラーが出てしまいVCへ入ってきてくれない状態です。

WARNING: JPCommonLabel_make() in jcomon_label.c: No phoneme.
C:\Users\ユーザ名\AppData\Local\Programs\Python\Python37\lib\site-packages\espnet2\utils\griffin_lim.py:48: FutureWarning: Pass sr=24000, n_fft=2048, n_mels=80, fmin=80, fmax=7600 as keyword args. From version 0.10 passing these as positional arguments will result in an error
mel_basis = librosa.filters.mel(fs, n_fft, n_mels, fmin, fmax)
Ignoring exception in on_message
Traceback (most recent call last):
File "C:\Users\ユーザ名\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "C:\読み上げbot用\read_bot.py", line 36, in on_message
play_voice(message.content)
File "C:\読み上げbot用\read_bot.py", line 42, in play_voice
voiceChannel.play(FFmpegPCMAudio("out.mp3"))
NameError: name 'voiceChannel' is not defined

色々なサイトを見て調べて試してみましたが、自力では解決できなかったため
ご相談させて頂きました。
何か解決方法がお分かりでしたらご教示頂けませんでしょうか?
お忙しいところ恐れ入りますがよろしくお願いいたします。

返事遅くなりまして申し訳ございません。
こちらの2行はコード内に含まれていますでしょうか。

↓一番上のところ

from discord.channel import VoiceChannel

↓async def on_readyの上

voiceChannel: VoiceChannel 

Hayateさん、お忙しいところご返信ありがとうございます。
上記のコードですがなぜか消えてしまっていたため追加してみて試してみましたが、
やはり下記のようなエラー文が出てしまい、BOTの方もログインはしてくれるものの
VCには上がってきてはくれませんでした。

------------以下エラー文---------------

WARNING: JPCommonLabel_make() in jcomon_label.c: No phoneme.
C:\Users\ユーザ名\AppData\Local\Programs\Python\Python37\lib\site-packages\espnet2\utils\griffin_lim.py:48: FutureWarning: Pass sr=24000, n_fft=2048, n_mels=80, fmin=80, fmax=7600 as keyword args. From version 0.10 passing these as positional arguments will result in an error
mel_basis = librosa.filters.mel(fs, n_fft, n_mels, fmin, fmax)
Ignoring exception in on_message
Traceback (most recent call last):
File "C:\Users\ユーザ名\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "app.py", line 39, in on_message
play_voice(message.content)
File "app.py", line 45, in play_voice
voiceChannel.play(FFmpegPCMAudio("out.mp3"))
AttributeError: type object 'VoiceChannel' has no attribute 'play'


何かインストールするものが足りなかったりするのでしょうか?
ゲーミング用とはいえ、ノートPCで挑戦しているためもともと必要最低限のものしか入っていないPCだったので、ゲーム用に必要なものを入れたりして使用しているもので…。
恐れ入りますが、お時間ある際で構いませんのでご教示いただけますと幸いです。
何卒宜しくお願い致します。

質問です。
TsukuyomichanTalksoft側が
windows非対応なのですが
Hayateの手順通りにやればできるのでしょうか?

ログインするとコメントできます