音声合成(AquesTalk)をPythonから…に今さらながらチャレンジ



クイックジャパンを立ち読みしたところ(すんません)、「モヤモヤさまぁ~ず2」のナレーション「ショウくん」は音声合成ソフト、とのこと。
てっきり人間だと思ってましたよ(汗

というわけで(?)Pythonからフリーの音声合成ライブラリ AquesTalkを使って遊んでみました。ついでに、MeCabをつかって読み仮名の取得にもチャレンジ。

使ってみて。。。大量に読み上げさせたい場合は、ちゃんとしたソフトを買ったほうが楽だと思います(汗

でも、ほんのちょっとだけ遊びたいなら、結構便利かも、です。

以下 ソースコード


つかいみち

  • ちょっとだけ合成音声が欲しい
  • プログラムにかかるまえに、AquesTalkの性能がどんなだか調べたい

などなど。

つかいかた

windows版 Python2.5用

  • kanaボタンで、漢字かな混じり文の読み仮名を取得
  • saveボタンで、wavファイルとして保存。
  • talkボタンで音声再生
    (AquesTalkで認識できない記号が文に混じっていると音が出ません。)
    (基本的には仮名のみでないとダメ)
  • マウスで選択した部分のみ読み上げます。
    • 選択しないと全文。
  • 読み上げが終わるまでソフトがストップしますので注意。

使用したライブラリ

  • AquesTalk Windows用
    独特の表記法により、イントネーションを指定する必要あり。主として

    • Windows版は無料で使えます
    • Python用のスクリプトと同じディレクトリにDLLを置く(もしくはDLLにパスを通す)
    • シングルコーテーション記号を、イントネーションが高い場所に挿入する
    • 単語の区切りにスラッシュを入れる。
    • 女声、男声、ロボットっぽい声、などなど追加データも。(これも無料)
  • AquesTalkをPythonから(音声合成)
    感謝
  • MeCab

ソースコード

# -*- coding: utf8 -*-
from  AquesTalk import AquesTalk
import time
from Tkinter import *
import re
import MeCab

TXTWIDTH = 60
TXTHEIGHT = 20

def check(s,
         pat= re.compile(r"([^" u"、。.,.ぁ-ん,ァ-ン,ー,0-9,a-z,A-Z,A-Z" r"])",re.UNICODE)
         ) :
   s2=pat.sub(r"", yomi)
   return s==s2 

def cnvkana(s,biaskata=ord(u"ァ"),biashira=ord(u"ぁ"),
        pat= re.compile(r"([" u"ァ-ン"  r"])",re.UNICODE)):
   return pat.sub(lambda x: unichr(ord(x.group(0))-biaskata+biashira),s)

def cnvnum(s,biaszen=ord(u"0"),biashan=ord("0"),
            pat1= re.compile(r"([" u"0-9" r"])"),
            pat2=re.compile(r"([0-9]+)"),
            f=lambda x: unichr(ord(x.group(0))-biaszen+biashan)
          ):
   s=pat1.sub(f,s)
   return pat2.sub( r"<NUMK VAL=1>",s)
def cnvalpha(s,biashan=ord("a"),biaszen=ord(u"a"),
        pat= re.compile(r"([" u"a-z" r"])")
       ):
   return pat.sub(lambda x: unichr(ord(x.group(0))-biashan+biaszen),s)

def timestamp(last=[0]):
   t=time.strftime("%Y%m%d%H%M%S",time.localtime())
   if last[0]==t :
       time.sleep(1)
       t=time.strftime("%Y%m%d%H%M%S",time.localtime())
   last[0]=t
   return t

class App(Frame):
 name = ""
 text = unicode(
       u"""モヤモヤさ'まーず。こんしゅうわ、あな'たの/まち'に,おじゃましちゃいま'す。う'そです。

あくえす'/と'ーーくわ/、/てきすと'/じょ'ーーほーーを,おんせい'/は'けいに/へんかん',しゅつ'りょくする,うぃんどうず'/じょーーで/ど'ーーさする,きそく'/お'んせい/ごーーせい'/ら'いぶらりです。

なめ'らかな,ききとりやす'い/おんしつと,しぜんな',イ'ントネ'ーション。"""

)

 def init(self):
   self.t = MeCab.Tagger (" ".join(sys.argv))
   self.at=AquesTalk()
   self.master.title("Talk Test")
   self.d = Text(self,width=TXTWIDTH,height=TXTHEIGHT)
   f = Button(text="Save",command=self.cmd_clicked_save)
   e = Button(text="Kana",command=self.cmd_clicked_kana)
   g = Button(text="Talk",command=self.cmd_clicked_talk)
   self.d.pack(padx=5, pady=0)
   self.d.insert(END,self.text)

   g.pack(side=RIGHT, padx=5, pady=5)
   f.pack(side=RIGHT, padx=5, pady=5)
   e.pack(side=RIGHT, padx=5, pady=5)
   self.d.focus()
   self.pack()

 def cmd_clicked_kana(self):
   t=self.txt()
   t
   self.getyomi(t)
   self.stat = 2

 def cmd_clicked_save(self):
   fn=timestamp()
   print >>file(fn+".txt","wb"),self.txt().encode("utf8","ignore")
   self.at.SyntheFile(self.txt(fsjis=True).replace("n",""),100,fn+".wav")

   self.stat = 2

 def cmd_clicked_talk(self):
   self.at.PlaySync(self.txt(fsjis=True),100)
   self.stat = 2

 def getyomi(self,s):
   txt=cnvalpha(s)
   txt = txt.encode("sjis","ignore")
   m = self.t.parseToNode (txt)
   l=[]
   while m:
       y=m.feature.split(",")[-1]
       if  y=="*" or not y:
           y=m.surface
       l.append(y)
       m = m.next

   yomi="/".join(l)
   yomi=cnvkana(yomi)
   yomi=cnvnum(yomi)
   yomi="nn"+yomi+"nn"
   yomi=yomi.decode("sjis","ignore")
   yomi=re.sub(r"(b|" u" "  r")","",yomi)
   yomi=yomi.replace(u"ー",u"ーー")
   yomi=cnvkana(yomi)
   self.d.insert(INSERT,yomi)
   return yomi

 def txt(self, fsjis=False ):
   try:
     text=self.d.get(SEL_FIRST, SEL_LAST)
   except TclError,e :
     text = self.d.get("1.0",END)
   text=text.replace("n","")
   if fsjis :
       text=text.encode("sjis","ignore")
   return text

 def __init__(self, master=None):
   Frame.__init__(self, master)
   self.pack(); self.init()

 def mainloop(self,objname,objtext,mode):

   Frame.mainloop(self)
app=App()

今後の改良点

イントネーション記号を自動で振ってくれるようにするには。。。
私じゃムリっぽいです(汗 というか、本格的に読ませるには、多分、市販品買ったほうが安いですし。
(AquesTalkは、インタラクティブなソフト内から使いたいとき、には便利ですけれども)

Tags:

Related posts

タグ:

コメントは受け付けていません。