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()内での再帰処理の部分は、わりと使える気がします。
このたどたどしさを逆に活かしたコンテンツを作る機会をいつまでも待っています。

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
AS3習得本
AS3の全容を学習できる本。この中でどれか1冊自分に合ったものを。
Adobe Flash CS4 詳細!ActionScript3.0入門ノート ActionScript3.0 プロフェッショナルガイド 初めてのActionScript 3.0 Flashユーザーのためのステップアップガイド 詳説 ActionScript 3.0 Actionscript 3.0 Cookbook
AS3発展本
ASでアニメーションさせる面白さを知るための本。
Flash Math & Physics Design:ActionScript 3.0による数学・物理学表現[入門編] ActionScript 3.0 アニメーション AdvancED ActionScript 3.0 Animation (Advanced)
AS2
"Flash"ではなく"ActionScript2.0"学習のための良著。他にもいろいろ読んだけど、この4冊を読んだ後、自分が成長できた感じがしました。
FLASH ActionScript 2.0入門完全ガイド+実践サンプル集 [CD-ROM付] Essential Actionscript 2.0 Flash 8 Essentials Foundation Actionscript Animation: Making Things Move (Foundation)

あわせて読みたいブログパーツ

相互リンク

hi-posiさん
携帯Flashといえばhi-posiの岡田昇三さん。FlashLiteの有益な記事もたくさん書かれていていつもお世話になってます。ついにご挨拶させていただきました。面白すぎる人でしたw。

Return to page top