Flashでテキストtoスピーチ

speechSystem.jpg

TFM – HondaSweetMission」のコメントを音声出力してくれるシステムに感動して、2ヶ月ほど前に作ったFlashでText to Speechするサンプル。
10分ぐらいで作った程度のサンプルです。

あまりにもショボくて、封印していたのだけれど、最近、尊敬するロリポの社長さんの「教えてメカロリポおじさん」を見て、多少公開する勇気をもらったので(といってもメカロリポおじさんのように裏でナウい処理をしているわけではないのだけれど)、ソースごと公開します。

再生ボタンを押すとテキストを読み上げます。テキストを自由にタイプし直して、その通りに喋らせることができますが、ひらがなと句読点以外は読めず、すっとばします。(要FlashPlayer7以上)

speechSystem.swf

ソースも一応載せておきますが、適当に書き始めて整頓する前に飽きてしまったので汚いです。すいません。

var text_Arr:Array = new Array();
var str:String = textBox.text;
btn.onRelease = function() {
text_Arr = new Array();
str = enterText.text;
setArray();
setSound();
playSound(0);
};
function setArray() {
for (var i = 0; i<str.length; i++) {
text_Arr.push({sound:str.substr(i, 1), mouth:0});
text_Arr[i].sound = changeKanaToRome(text_Arr[i].sound);
if (text_Arr[i].sound == "bar") {
var lastBoin = text_Arr[i-1].sound.substr(-1, 1);
text_Arr[i].sound=lastBoin;
}
text_Arr[i].mouth = animateMouth(text_Arr[i].sound);
}
}
function setSound() {
for (var i = 0; i<text_Arr.length; i++) {
this["s_"+i] = new Sound();
var sound = this["s_"+i];
sound.attachSound("s_"+text_Arr[i].sound);
}
}
//サウンドオブジェクトを0から順に再帰的に再生していく関数
function playSound(num:Number) {
if (num<text_Arr.length) {
this["s_"+num].start();
chara.head.mouth.gotoAndStop(text_Arr[num].mouth);
this["s_"+num].onSoundComplete = function() {
playSound(num+1);
};
} else {
chara.head.mouth.gotoAndStop(1);
}
}
function animateMouth(soundChara:String) {
var lastChar = soundChara.substr(-1, 1);
if (lastChar == "a") {
return 2;
} else if (lastChar == "i") {
return 3;
} else if (lastChar == "u") {
return 4;
} else if (lastChar == "e") {
return 5;
} else if (lastChar == "o") {
return 6;
} else {
return 1;
}
}
//text_Arrのひらがなをローマ字に変換する関数
function changeKanaToRome(kana:String) {
var tmp:String = kana;
if (tmp == "あ") {
tmp = "a";
return tmp;
} else if (tmp == "い") {
tmp = "i";
return tmp;
} else if (tmp == "う") {
tmp = "u";
return tmp;
} else if (tmp == "え") {
tmp = "e";
return tmp;
} else if (tmp == "お") {
tmp = "o";
return tmp;
} else if (tmp == "か") {
tmp = "ka";
return tmp;
} else if (tmp == "き") {
tmp = "ki";
return tmp;
} else if (tmp == "く") {
tmp = "ku";
return tmp;
} else if (tmp == "け") {
tmp = "ke";
return tmp;
} else if (tmp == "こ") {
tmp = "ko";
return tmp;
} else if (tmp == "さ") {
tmp = "sa";
return tmp;
} else if (tmp == "し") {
tmp = "si";
return tmp;
} else if (tmp == "す") {
tmp = "su";
return tmp;
} else if (tmp == "せ") {
tmp = "se";
return tmp;
} else if (tmp == "そ") {
tmp = "so";
return tmp;
} else if (tmp == "た") {
tmp = "ta";
return tmp;
} else if (tmp == "ち") {
tmp = "ti";
return tmp;
} else if (tmp == "つ") {
tmp = "tu";
return tmp;
} else if (tmp == "て") {
tmp = "te";
return tmp;
} else if (tmp == "と") {
tmp = "to";
return tmp;
} else if (tmp == "な") {
tmp = "na";
return tmp;
} else if (tmp == "に") {
tmp = "ni";
return tmp;
} else if (tmp == "ぬ") {
tmp = "nu";
return tmp;
} else if (tmp == "ね") {
tmp = "ne";
return tmp;
} else if (tmp == "の") {
tmp = "no";
return tmp;
} else if (tmp == "は") {
tmp = "ha";
return tmp;
} else if (tmp == "ひ") {
tmp = "hi";
return tmp;
} else if (tmp == "ふ") {
tmp = "hu";
return tmp;
} else if (tmp == "へ") {
tmp = "he";
return tmp;
} else if (tmp == "ほ") {
tmp = "ho";
return tmp;
} else if (tmp == "ま") {
tmp = "ma";
return tmp;
} else if (tmp == "み") {
tmp = "mi";
return tmp;
} else if (tmp == "む") {
tmp = "mu";
return tmp;
} else if (tmp == "め") {
tmp = "me";
return tmp;
} else if (tmp == "も") {
tmp = "mo";
return tmp;
} else if (tmp == "や") {
tmp = "ya";
return tmp;
} else if (tmp == "ゆ") {
tmp = "yu";
return tmp;
} else if (tmp == "よ") {
tmp = "yo";
return tmp;
} else if (tmp == "ら") {
tmp = "ra";
return tmp;
} else if (tmp == "り") {
tmp = "ri";
return tmp;
} else if (tmp == "る") {
tmp = "ru";
return tmp;
} else if (tmp == "れ") {
tmp = "re";
return tmp;
} else if (tmp == "ろ") {
tmp = "ro";
return tmp;
} else if (tmp == "わ") {
tmp = "wa";
return tmp;
} else if (tmp == "を") {
tmp = "wo";
return tmp;
} else if (tmp == "ん") {
tmp = "nn";
return tmp;
} else if (tmp == "が") {
tmp = "ga";
return tmp;
} else if (tmp == "ぎ") {
tmp = "gi";
return tmp;
} else if (tmp == "ぐ") {
tmp = "gu";
return tmp;
} else if (tmp == "げ") {
tmp = "ge";
return tmp;
} else if (tmp == "ご") {
tmp = "go";
return tmp;
} else if (tmp == "ざ") {
tmp = "za";
return tmp;
} else if (tmp == "じ") {
tmp = "zi";
return tmp;
} else if (tmp == "ず") {
tmp = "zu";
return tmp;
} else if (tmp == "ぜ") {
tmp = "ze";
return tmp;
} else if (tmp == "ぞ") {
tmp = "zo";
return tmp;
} else if (tmp == "だ") {
tmp = "da";
return tmp;
} else if (tmp == "ぢ") {
tmp = "di";
return tmp;
} else if (tmp == "づ") {
tmp = "du";
return tmp;
} else if (tmp == "で") {
tmp = "de";
return tmp;
} else if (tmp == "ど") {
tmp = "do";
return tmp;
} else if (tmp == "ば") {
tmp = "ba";
return tmp;
} else if (tmp == "び") {
tmp = "bi";
return tmp;
} else if (tmp == "ぶ") {
tmp = "bu";
return tmp;
} else if (tmp == "べ") {
tmp = "be";
return tmp;
} else if (tmp == "ぼ") {
tmp = "bo";
return tmp;
} else if (tmp == "ぱ") {
tmp = "pa";
return tmp;
} else if (tmp == "ぴ") {
tmp = "pi";
return tmp;
} else if (tmp == "ぷ") {
tmp = "pu";
return tmp;
} else if (tmp == "ぺ") {
tmp = "pe";
return tmp;
} else if (tmp == "ぽ") {
tmp = "po";
return tmp;
} else if (tmp == "ー") {
tmp = "bar";
return tmp;
} else if (tmp == "ぁ") {
tmp = "a";
return tmp;
} else if (tmp == "ぃ") {
tmp = "i";
return tmp;
} else if (tmp == "ぅ") {
tmp = "u";
return tmp;
} else if (tmp == "ぇ") {
tmp = "e";
return tmp;
} else if (tmp == "ぉ") {
tmp = "o";
return tmp;
} else if (tmp == "っ") {
tmp = "tt";
return tmp;
} else if (tmp == "ゃ") {
tmp = "ya";
return tmp;
} else if (tmp == "ゅ") {
tmp = "yu";
return tmp;
} else if (tmp == "ょ") {
tmp = "yo";
return tmp;
} else {
tmp = "ttt";
return tmp;
}
}

作ってみると、ショボいなりにもいろいろ見えてくるものです。

問題点が山積みです。

  • 「は」の区別ができない
    「私は〜」の場合も「は」と読み上げてしまいます。こういうのって文脈から判別するのでしょうか?判別処理ライブラリとかあればいいのですが。
  • 声の抑揚が気持ち悪すぎる
    今回は自分でマイクに向かって「あいうえおかきく〜わをん」まで一連で録音し、Windowsフリーソフト「SoundEngine Free」で、一語づつ分割してます。抑揚のないようにPC音声で作るとか、抑揚を整えてくれるフリーソフトがあれば、もう少しましになるでしょう。
  • ちっちゃい「ゃ」とかが「や」になってる。
    面倒だったのでやってないだけで、条件分岐すればもうすこしましに仕上がると思います。
  • 「ま行」とか「ぱ行」とかは発声前に唇を一旦閉じないといけません。
    これも条件分岐すればいいです。さぼってしまいました。

ほどんど見所はありませんが、onSoundComplete()内での再帰処理の部分は、わりと使える気がします。
このたどたどしさを逆に活かしたコンテンツを作る機会をいつまでも待っています。

このエントリーをはてなブックマークに追加
はてなブックマーク - Flashでテキストtoスピーチ

Comments:2

TK 07-11-15 (木) 17:59

単語・文脈解析がIMEをそのまま使えると、”私「は」”の問題も解決できそうです。
また、抑揚の問題は、日本語が50音だけでないということで、辞書などを拡張していくことによって解決できそうですが…。
遠いな。

tera 07-11-15 (木) 19:55

>TK様
情報ありがとうございます。
僕の方では yahoo の形態素解析を使えば、ちょっといい感じにできるのではないかなと考えたことがあります。助詞なら「は(ha)」にするとかで部分的にはできるかなぁと思ったのですが、根本的解決はできそうにない気がして、それ以来放置していました。
僕の方が遠いです(笑)

Comment Form
Remember personal info

Trackbacks:0

Trackback URL for this entry
http://www.trick7.com/blog/2006/07/14-135315.php/trackback
Listed below are links to weblogs that reference
Flashでテキストtoスピーチ from trick7

Return to page top