OpenJtalkを並列化したいので既存コードを書き直したが,動かない
解決済
回答 1
投稿
- 評価
- クリップ 0
- VIEW 1,781

退会済みユーザー
前提・実現したいこと
Raspberry PiでOpenJtalkを動かしたのですが,おそすぎるため,調べたところ,文を句読点ごとに区切り,それぞれ処理を並列化し
最終的に再生することで高速化を図るというものがありました
ただし,いちいちテキストファイルにしなければならないので
これをPythonのモジュールにしようと思い,
テキストを受取るようクラスにしてみましたが,なぜかずっと"音声合成の完了待ちです"となってしまい,
処理が終わりません(Python3.5)
少し調査すると,下の""正常に動くコード""はPython2.7ならばうまくいくようで,python3.5だと音声合成の完了待ちです,がずっと続き同じようになります
おそらく互換性がないのか,それ以上はわたしの知識ではわかりませんでした
""ずっと"音声合成の完了待ちです"""のコードはpython2.7,python3.5でもどちらも動きませんでした
該当のソースコード
######################ずっと"音声合成の完了待ちです"となる(Python2.7,Python3.5どちらも音声合成中になる)
class OutSpeak():
# デバッグフラグ
DEBUG = True
# 音声合成の同時処理数
MAX_PROCESS = 3
# open_jtalkコマンドの場所
JTALK = '/usr/bin/open_jtalk'
# aplayコマンドの場所
APLAY = '/usr/bin/aplay'
# 辞書ディレクトリ
DICDIR = "/var/lib/mecab/dic/open-jtalk/naist-jdic"
# 音声ファイル
# VOICE = '/home/pi/openjtalk/hts_voice/nitech_jp_atr503_m001.htsvoice'
# VOICE = '/home/pi/openjtalk/hts_voice/omoine_ikuru.htsvoice'
# VOICE = '/home/pi/openjtalk/hts_voice/syane_homu.htsvoice'
# VOICE = '/home/pi/openjtalk/hts_voice/suranki.htsvoice'
VOICE = '/home/pi/OpenJtalk/mei_normal.htsvoice'
# VOICE = '/home/pi/openjtalk/hts_voice/mei_happy.htsvoice'
# 話す速度(標準 1.0。0.0以上の値を指定)
SPEED = 1.0
# additional half-tone
TONE = 2.0
# ボリューム
VOLUME = 10.0
alltext=""
# 作業ディレクトリ
WORKDIR = "/home/pi/"
def _print(self,message):
if self.DEBUG:
print(message)
# 音声合成を実行する
#
def create_wav(self,t):
import subprocess
self._print("DEBUG: 音声作成開始[%d:%s]" % (t[0], t[1]))
# サブプロセス呼び出し
outfile = self.WORKDIR + "talk%02d.wav" % t[0]
# -g という引数はエラーが出るので削除
c = subprocess.Popen(
[self.JTALK, '-x', self.DICDIR, '-m', self.VOICE, '-ow', outfile, '-r', str(self.SPEED), '-fm', str(self.TONE)], stdin=subprocess.PIPE)
# 音声合成するテキストを入力
c.stdin.write(t[1])
# 終了を待つ
c.stdin.close()
c.wait()
self._print("DEBUG: 音声作成終了[%d:%s]" % (t[0], t[1]))
t[2].put(t[0])
return c.returncode
def play_wav(self,listindex):
import subprocess
command = list()
command.append(self.APLAY)
command.append('-q')
for index in listindex:
command.append(self.WORKDIR + "talk%02d.wav" % index)
return subprocess.Popen(command)
def Speak(self,text):
self.alltext=text
import re,time
from multiprocessing import Pool, Manager
queue = Manager().Queue()
# テキストを短文に分割。テキストに連番を振ってリストにしておく。
index = 0
arg = list()
#alltextに "." ","が含まれていたらそれを日本語の句読点に変換
self.alltext.replace(".","。")
self.alltext.replace(",","、")
shorttext = re.split(r'、|。', self.alltext)
for s in shorttext:
if s != "":
arg.append((index, s, queue))
index += 1
self._print("DEBUG: %d個に分割しました。" % len(arg))
# プロセスプールを作成
p = Pool(self.MAX_PROCESS)
# 音声合成を開始
r = p.map_async(self.create_wav, arg)
# 音声合成の進捗リスト。0は未完了、1は音声合成済み。
l = [0 for i in range(len(arg))]
# 終端の判断のため-1を付加。
l.append(-1)
nowplaying = -1
# 現在再生中のaplayコマンドのProcess
playing = None
# 進捗を確認しつつ音声を読み上げる
while (not r.ready()) or (nowplaying != len(arg)) or (playing is not None):
time.sleep(0.5)
# 音声合成の終了報告があるかキューを確認する。
for _ in range(queue.qsize()):
compiled_index = queue.get()
l[compiled_index] = 1
# 再生できるならしてみる?
if nowplaying < len(arg):
if playing is None:
if l[nowplaying + 1] == 1:
# まとめてWAVファイルを指定できるときはする
listindex = list()
while l[nowplaying + 1] == 1:
nowplaying += 1
listindex.append(nowplaying)
self._print("DEBUG: しゃべるよ![%s]" % str(listindex))
playing = self.play_wav(listindex)
elif l[nowplaying + 1] == 0:
self._print("DEBUG: 音声合成の完了待ちです!")
else:
exit()
else:
if playing.poll() is not None:
playing = None
#######################正常に動くコード(Python3.5ではずっと音声合成中になる)
# coding: UTF-8
import sys
import subprocess
import re
import time
from multiprocessing import Pool, Manager
# デバッグフラグ
DEBUG = True
# 音声合成の同時処理数
MAX_PROCESS = 3
# open_jtalkコマンドの場所
JTALK = '/usr/bin/open_jtalk'
# aplayコマンドの場所
APLAY = '/usr/bin/aplay'
# 辞書ディレクトリ
DICDIR = "/var/lib/mecab/dic/open-jtalk/naist-jdic"
# 音声ファイル
# VOICE = '/home/pi/openjtalk/hts_voice/nitech_jp_atr503_m001.htsvoice'
# VOICE = '/home/pi/openjtalk/hts_voice/omoine_ikuru.htsvoice'
# VOICE = '/home/pi/openjtalk/hts_voice/syane_homu.htsvoice'
# VOICE = '/home/pi/openjtalk/hts_voice/suranki.htsvoice'
VOICE = '/home/pi/OpenJtalk/mei_normal.htsvoice'
# VOICE = '/home/pi/openjtalk/hts_voice/mei_happy.htsvoice'
# 話す速度(標準 1.0。0.0以上の値を指定)
SPEED = 1.0
# additional half-tone
TONE = 2.0
# ボリューム
VOLUME = 10.0
# 作業ディレクトリ
WORKDIR = "/home/pi/"
def _print(message):
if DEBUG:
print(message)
#
# 音声合成を実行する
#
def create_wav(t):
_print("DEBUG: 音声作成開始[%d:%s]" % (t[0], t[1]))
# サブプロセス呼び出し
outfile = WORKDIR + "talk%02d.wav" % t[0]
c = subprocess.Popen(
[JTALK, '-x', DICDIR, '-m', VOICE, '-ow', outfile, '-r', str(SPEED), '-fm', str(TONE)], stdin=subprocess.PIPE)
# 音声合成するテキストを入力
c.stdin.write(t[1])
# 終了を待つ
c.stdin.close()
c.wait()
_print("DEBUG: 音声作成終了[%d:%s]" % (t[0], t[1]))
t[2].put(t[0])
return c.returncode
#
# 音声を(複数まとめて)再生する
#
def play_wav(listindex):
command = list()
command.append(APLAY)
command.append('-q')
for index in listindex:
command.append(WORKDIR + "talk%02d.wav" % index)
return subprocess.Popen(command)
#######################################
# 入力
#######################################
# 引数を取り込み
argvs = sys.argv
# 簡単な引数チェック
if (len(argvs) != 2):
print
'Usage: # %s textfile' % argvs[0]
exit(1)
# 指定されたファイルを読み込み
alltext = ""
for line in open(argvs[1]):
alltext += line.rstrip()
# 音声合成の進捗を確認するためのキューを作成
queue = Manager().Queue()
# テキストを短文に分割。テキストに連番を振ってリストにしておく。
index = 0
arg = list()
shorttext = re.split(r'、|。', alltext)
for s in shorttext:
if s != "":
arg.append((index, s, queue))
index += 1
_print("DEBUG: %d個に分割しました。" % len(arg))
#######################################
# 合成
#######################################
# プロセスプールを作成
p = Pool(MAX_PROCESS)
# 音声合成を開始
r = p.map_async(create_wav, arg)
# 音声合成の進捗リスト。0は未完了、1は音声合成済み。
l = [0 for i in range(len(arg))]
# 終端の判断のため-1を付加。
l.append(-1)
#######################################
# 再生
#######################################
# 現在再生中の音声のインデックス
nowplaying = -1
# 現在再生中のaplayコマンドのProcess
playing = None
# 進捗を確認しつつ音声を読み上げる
while (not r.ready()) or (nowplaying != len(arg)) or (playing is not None):
time.sleep(0.5)
# 音声合成の終了報告があるかキューを確認する。
for _ in range(queue.qsize()):
compiled_index = queue.get()
l[compiled_index] = 1
# 再生できるならしてみる?
if nowplaying < len(arg):
if playing is None:
if l[nowplaying + 1] == 1:
# まとめてWAVファイルを指定できるときはする
listindex = list()
while l[nowplaying + 1] == 1:
nowplaying += 1
listindex.append(nowplaying)
_print("DEBUG: しゃべるよ![%s]" % str(listindex))
playing = play_wav(listindex)
elif l[nowplaying + 1] == 0:
_print("DEBUG: 音声合成の完了待ちです!")
else:
exit()
else:
if playing.poll() is not None:
playing = None
補足情報(言語/FW/ツール等のバージョンなど)
参考サイト:
http://windvoice.hatenablog.jp/entry/2015/02/22/165457
Raspberry Pi 2 stretch
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
check解決した方法
0
引数に文字列を入れることで"擬似的に"(システムコールによって)
呼び出すことにしました
IOアクセスによるオーバヘッドは少なくなると思います
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.19%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる