任意の日本語テキストをAquesTalk Pico用の文字列に変換してみる

July 04, 2016

これまではAquestalkで音声合成するためのテキストデータを直接記述していましたが、これではtwitter等からとってきた任意のテキストを喋らせられませんので、自動的に通常の日本語テキストをAquestalk用のテキストに変換する方法を考えてみました。本家のサイトには日本語テキストを変換してくれるWebサービスがありますが、これはブラウザからしか使ってはいけない、と書いてありましたので、自前で作ることにしました。

ざっくりとした流れとしては、mecabで形態素解析(辞書はnaist-jdicを使用)して、得られた各単語の読み仮名(カタカナ)をAquestalk用の文字に変換(ローマ字に変換する処理に近い)、です。下記がそのモジュールのソースです。mecabはサーバ上にインストールされているものを使用しますが、coffeescriptから利用するためにmecab-asyncというモジュールを使わせていただきました(同期実行しています)。Aquestalk picoのローマ字に変換する部分はここを参考に実装しました。アクセント句とかアクセントの位置とかはどう付ければいいのかまだわかっていないので、対応できていません。あと、数字とかアルファベットで書かれた名称等はmecabで変換できないので、現在のところは「ピー」で置き換えてます。

 

Mecab = require 'mecab-async'
 
class RomanGenerator
  constructor: ->
    @dmap = {}
    @maxlen = 0
  put: (kana, alpha) ->
    @dmap[kana] = alpha.toLowerCase()
    if kana.length > @maxlen
      @maxlen = kana.length
  setup: ->
    @dmap = {}
    @put("ア", "A")
    @put("イ", "I")
    @put("ウ", "U")
    @put("エ", "E")
    @put("オ", "O")
    @put("カ", "KA")
    @put("キ", "KI")
    @put("ク", "KU")
    @put("ケ", "KE")
    @put("コ", "KO")
    @put("サ", "SA")
    @put("シ", "SHI")
    @put("ス", "SU")
    @put("セ", "SE")
    @put("ソ", "SO")
    @put("タ", "TA")
    @put("チ", "CHI")
    @put("ツ", "TU")
    @put("テ", "TE")
    @put("ト", "TO")
    @put("ナ", "NA")
    @put("ニ", "NI")
    @put("ヌ", "NU")
    @put("ネ", "NE")
    @put("ノ", "NO")
    @put("ハ", "HA")
    @put("ヒ", "HI")
    @put("フ", "FU")
    @put("ヘ", "HE")
    @put("ホ", "HO")
    @put("マ", "MA")
    @put("ミ", "MI")
    @put("ム", "MU")
    @put("メ", "ME")
    @put("モ", "MO")
    @put("ヤ", "YA")
    @put("ユ", "YU")
    @put("ヨ", "YO")
    @put("ラ", "RA")
    @put("リ", "RI")
    @put("ル", "RU")
    @put("レ", "RE")
    @put("ロ", "RO")
    @put("ワ", "WA")
    @put("ヲ", "WO")
    @put("ン", "NN")
    @put("ガ", "GA")
    @put("ギ", "GI")
    @put("グ", "GU")
    @put("ゲ", "GE")
    @put("ゴ", "GO")
    @put("ザ", "ZA")
    @put("ジ", "ZI")
    @put("ズ", "ZU")
    @put("ゼ", "ZE")
    @put("ゾ", "ZO")
    @put("ダ", "DA")
    @put("ヂ", "DI")
    @put("ヅ", "DU")
    @put("デ", "DE")
    @put("ド", "DO")
    @put("バ", "BA")
    @put("ビ", "BI")
    @put("ブ", "BU")
    @put("ベ", "BE")
    @put("ボ", "BO")
    @put("パ", "PA")
    @put("ピ", "PI")
    @put("プ", "PU")
    @put("ペ", "PE")
    @put("ポ", "PO")
    @put("キャ", "KYA")
    @put("キュ", "KYU")
    @put("キョ", "KYO")
    @put("シャ", "SYA")
    @put("シュ", "SYU")
    @put("ショ", "SYO")
    @put("チャ", "TYA")
    @put("チュ", "TYU")
    @put("チョ", "TYO")
    @put("ニャ", "NYA")
    @put("ニュ", "NYU")
    @put("ニョ", "NYO")
    @put("ヒャ", "HYA")
    @put("ヒュ", "HYU")
    @put("ヒョ", "HYO")
    @put("リャ", "RYA")
    @put("リュ", "RYU")
    @put("リョ", "RYO")
    @put("ギャ", "GYA")
    @put("ギュ", "GYU")
    @put("ギョ", "GYO")
    @put("ジャ", "ZYA")
    @put("ジュ", "ZYU")
    @put("ジョ", "ZYO")
    @put("ヂャ", "DYA")
    @put("ヂュ", "DYU")
    @put("ヂョ", "DYO")
    @put("ビャ", "BYA")
    @put("ビュ", "BYU")
    @put("ビョ", "BYO")
    @put("ピャ", "PYA")
    @put("ピュ", "PYU")
    @put("ピョ", "PYO")
    @put("ッカ", "KKA")
    @put("ッキ", "KKI")
    @put("ック", "KKU")
    @put("ッケ", "KKE")
    @put("ッコ", "KKO")
    @put("ッサ", "SSA")
    @put("ッシ", "SSI")
    @put("ッス", "SSU")
    @put("ッセ", "SSE")
    @put("ッソ", "SSO")
    @put("ッタ", "TTA")
    @put("ッチ", "CCHI")
    @put("ッツ", "TTSU")
    @put("ッテ", "TTE")
    @put("ット", "TTO")
    @put("ッパ", "PPA")
    @put("ッピ", "PPI")
    @put("ップ", "PPU")
    @put("ッペ", "PPE")
    @put("ッポ", "PPO")
    @put("ッキャ", "KKYA")
    @put("ッキュ", "KKYU")
    @put("ッキョ", "KKYO")
    @put("ッシャ", "SSHA")
    @put("ッシュ", "SSHU")
    @put("ッショ", "SSHO")
    @put("ッチャ", "CCHA")
    @put("ッチュ", "CCHU")
    @put("ッチョ", "CCHO")
    @put("ー", "-")
    @put("?", "?")
    @put("。", ".")
    @put("、", ",")
    @put("-", ",")
  trans: (str) ->
    out_str = ""
    in_str = str
    while in_str.length > 0
      l = Math.min in_str.length, @maxlen
      while l > 0
        k = in_str.substring 0, l
        if k in Object.keys(@dmap)
          out_str = out_str + @dmap[k]
          in_str = in_str.substr l
          break
        l--
      if l == 0
        console.log "変換不可:" + in_str
        return false
    out_str
 
rgen = new RomanGenerator()
rgen.setup()
 
mecab = new Mecab()
 
Mecab.command = "mecab -d /usr/local/lib/mecab/dic/naist-jdic/"
 
module.exports.textToAques = (str) ->
  res = mecab.parseSync str
  sentence = ""
  for token in res
    if typeof token[9] != "undefined"
      sentence += token[9]
    else
      sentence += "ピー"
  rgen.trans sentence