8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

横国ゲーム制作部Advent Calendar 2022

Day 16

Discordの読み上げにVOICEROIDとVOICEVOXを使う

Last updated at Posted at 2022-12-16

始めに

ゲームサークルで使うためにVOICEROIDとVOICEVOXを使ったbotを作った。それぞれを使ったbotについては既に記事がたくさんあるので今回は話者を途中で切り替えるための実装について書いていく。製作したものについてはここに公開している。

動作確認済み環境

  • windows10 64bit
  • python 3.11.0
  • pyvcroid2
  • VOICEROID2 64bit
  • VOICEVOX

VOICEROIDをpythonで動かす

pyvcroid2というライブラリを見つけたためこれを利用した。

使用するVOICEROIDのキャラを変更する

pyvcroidにはloadVoiceというメソッドが用意されており、これを使うと話者を設定できる。ただ、これをそのまま繰り返し用いても話者を切り替えられないのでので268行目から270行目のコメントアウトを外す必要がある。

        #result = self.__dll.AITalkAPI_VoiceClear()
        #if (result != aitalk.ResultCode.SUCCESS) and (result != aitalk.ResultCode.NOT_LOADED):
        #    raise Exception(result)

これを

        result = self.__dll.AITalkAPI_VoiceClear()
        if (result != aitalk.ResultCode.SUCCESS) and (result != aitalk.ResultCode.NOT_LOADED):
            raise Exception(result)

こうすると初回設定だけでなく話者の変更にもloadVoiceメソッドを使える。

VOICEVOXをpythonで動かす

VOICEVOXはhttpサーバ経由で動かせるようなので少し調べるだけでとりあえず動くものはできた。VOICEVOXではspeakerを変更してクエリを取得することで簡単に話者を変更できる。

import requests
import json

class vcvox():
    def __init__(self):
        self.speaker=3
        self.text=None
        self.query=None
        self.sound=None
    
    def get_query(self):
        self.query=requests.post("http://localhost:50021/audio_query",params=(("text",self.text),("speaker",self.speaker)))

    def get_synthesis(self):
        self.sound=requests.post("http://localhost:50021/synthesis",params={"speaker":self.speaker},data=json.dumps(self.query.json()))
    
    def texttosound(self,text):
        self.text=text
        self.get_query()
        self.get_synthesis()

        return self.sound.content

使用するVOICEVOXのキャラを変更する

VOICEVOXについてはリクエストを送る際に載せるspeakerを変更することで簡単に話者を変更できる。(例:四国めたん→2 ずんだもん→3)

VOICEROIDとVOICEVOXで実際に音声を合成していく

使うソフトが2つになって面倒なのでソフトの切り替えや話者の切り替え等をするためのcontrolクラスを作った。
使っているソフトを記録するための変数と呼び出されたテキストチャンネルを記録するための変数に加えて各ソフトによる読み上げ用のクラス(VcRoid2,vcvox)の継承をした。

class control(pyvcroid2.VcRoid2,pyvcvox.vcvox):
    def __init__(self):
        self.speakertype="roid"
        self.ch=None
        pyvcroid2.VcRoid2.__init__(self)
        pyvcvox.vcvox.__init__(self)

使用しているソフトを管理するspeakertypeは"roid"と"vox"で切り替えるとしてあとはこのspeakertypeに応じて実際に音声ファイルを作るメソッドを追加していく。

class control(pyvcroid2.VcRoid2,pyvcvox.vcvox):
    def __init__(self):
        self.speakertype="roid"
        self.ch=None
        pyvcroid2.VcRoid2.__init__(self)
        pyvcvox.vcvox.__init__(self)


    def speak(self,text):
        if self.speakertype=="roid":
            lang_list = ctrl.listLanguages()
            if "standard" in lang_list:
                ctrl.loadLanguage("standard")
            elif 0 < len(lang_list):
                ctrl.loadLanguage(lang_list[0])
            self.param.volume = 1.00
            self.param.speed = 1.2
            self.param.pitch = 1.1
            self.param.emphasis = 0.95
            self.param.pauseMiddle = 80
            self.param.pauseLong = 100
            self.param.pauseSentence = 100
            self.param.masterVolume = 1.123
            filename = "temp.wav"
            speech, tts_events = self.textToSpeech(text)

            with open(filename, mode="wb") as f:
                f.write(speech)
        elif self.speakertype=="vox":
            with open("temp.wav", mode="wb") as f:
                f.write(self.texttosound(text))
        
        return "temp.wav"

これで簡単にwavファイルを取得できるようになった。

コマンドによるソフトや話者の切り替え

ここまででそれぞれのソフトを使った音声合成ができるようになったのでいくつかコマンドを用意していく

使用するソフトの変更

ただ変数を書き換えるだけ

            #ソフトの切り替え
            elif message.content=="!vcchange":
                if ctrl.speakertype=="roid":
                    ctrl.speakertype="vox"
                else:
                    ctrl.speakertype="roid"

話者IDの表示

話者を変更するコマンドを用意する前にそれぞれに割り振られたIDを表示させるコマンドを用意した。pyvcroidではlistVoiceメソッドで取得できるがVOICEVOXではそうはいかなさそうなのでいくつかピックアップして表示させた。

elif "!vclist" in message.content:
                if ctrl.speakertype=="roid":
                    voice_list = ctrl.listVoices()
                    if 0 < len(voice_list):
                        for i,j in enumerate(voice_list):
                            await message.channel.send("{}:{}".format(i,j))
                    else:
                        raise Exception("No voice library")
                
                else:
                    await message.channel.send("2:四国めたん\n3:ずんだもん\n8:春日つむぎ")

話者の切り替え

手に入れた話者IDをコマンドのあとに入力して使う。
VOICEROIDを使っているならloadVoiceで使用するキャラを変更し、VOICEVOXを使っているならクエリの取得に使用するspeaker変数を変更する。

#話者の切り替え
            elif "!vcset" in message.content:
                    
                if ctrl.speakertype=="roid":
                    voice_list = ctrl.listVoices()
                    if 0 < len(voice_list):
                        number=message.content.split()
                        number=number[1]
                        ctrl.loadVoice(voice_list[int(number)])
                    else:
                        raise Exception("No voice library")

                else:
                    number=message.content.split()
                    number=number[1]
                    ctrl.speaker=number

実際に読んでもらう

ここまで来れば入力したテキストで音声を合成し、wavファイルを流すだけなので先ほど作った読み上げメソッドを使って

            #テキストは読み上げる
            else:
                while message.guild.voice_client.is_playing():
                            await asyncio.sleep(0.1)
                
                source=discord.FFmpegPCMAudio(ctrl.speak(message.content))
                message.guild.voice_client.play(source)

最後に

とりあえず目当ての動作をするbotを作ることができたが、まだまだ機能面や安定性に不十分な点があるので更新を加えていきたい。次は読み上げ速度をいじれるようにしたいと思う。

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?