"Google Text To Speech" speak Japanese.
Google Text To Speechに日本語を喋ってもらいました。
/**
* Copyright ikekou ( http://wonderfl.net/user/ikekou )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/faUk
*/
//googleTTSで無理やり日本語を喋らせてみる。カタコト。
//実際に喋ってるのはスペイン語。
//スペイン語はアルファベットをローマ字に非常に近く発音してくれることを学んだ!
//
//paqさんのVoiceChat(http://wonderfl.net/c/p2KD)とくっつけてみました
//http://wonderfl.net/c/w08s
//
package
{
import com.bit101.components.PushButton;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.ui.Keyboard;
[SWF(backgroundColor=0xeeeeee, width=465, height=465, frameRate=30)]
public class Main extends Sprite
{
private var tf:TextField;
private var isChanged:Boolean;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
tf = new TextField();
tf.border = true;
tf.borderColor = 0x999999;
tf.background = true;
tf.backgroundColor = 0xffffff;
tf.multiline = false;
tf.width = 300;
tf.height = 19;
tf.type = TextFieldType.INPUT;
tf.x = 33;
tf.y = (stage.stageHeight - tf.height) / 2;
tf.multiline = false;
tf.restrict = "^ア-ン";
tf.text = "今日は、いい天気ですね。焼肉が食べたいですね。";
addChild(tf);
isChanged = false;
tf.addEventListener(KeyboardEvent.KEY_DOWN, _onKeyDown);
tf.addEventListener(Event.CHANGE, _onChange);
new PushButton(this, 333, tf.y, "speak", _onSpeak);
}
private function _onChange(e:Event):void
{
isChanged = true;
}
private function _onKeyDown(e:KeyboardEvent):void
{
if (e.keyCode == Keyboard.ENTER && isChanged)
{
isChanged = false;
_onSpeak(null);
}
}
private function _onSpeak(e:Event):void
{
if (!tf.text) return;
var p:Parser = new Parser();
p.addEventListener(Event.COMPLETE, _onComplete);
p.parse(tf.text);
tf.text = "";
isChanged = false;
stage.focus = tf;
}
private function _onComplete(e:Event):void
{
var c:ParsedXMLConverter = new ParsedXMLConverter();
var result:String = c.convert(Parser(e.target).data);
var s:Speaker = new Speaker();
s.speak(result);
}
}
}
internal class Config
{
public static const URL_PROXY:String = "http://api.ikekou.jp/proxy.php";
}
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLVariables;
internal class Parser extends EventDispatcher
{
public var data:XML;
public function parse(target:String, useProxy:Boolean = false ):void
{
if (!target) return;
var req:URLRequest = new URLRequest(Config.URL_PROXY);
var vars:URLVariables = new URLVariables();
req.data = vars;
vars.url = "http://jlp.yahooapis.jp/MAService/V1/parse?appid=0GIwpCyxg67plKeaS4vB4tJQVU8hQ9RTaAC4AlZibwtGU0tYbRB7SbLkFvgEs2Db&sentence=" + encodeURI(target);
var ul:URLLoader = new URLLoader();
ul.addEventListener(Event.COMPLETE, _onComplete);
ul.load(req);
}
private function _onComplete(e:Event):void
{
data = new XML(XMLUtil.deleteNameSpace(URLLoader(e.target).data));
dispatchEvent(new Event(Event.COMPLETE));
}
}
internal class XMLUtil
{
/**
*
* clockmakerさんのを拝借
*
* 名前空間を削除します
* @param オリジナルストリング(XML形式)
* @return namespace宣言を取り去ったXML
*/
public static function deleteNameSpace(xmlText:String):XML
{
// remove the namespaces from the string representation of the XML
xmlText = xmlText.replace(new RegExp("xmlns[^\"]*\"[^\"]*\"", "gi"), "");
xmlText = xmlText.replace(new RegExp("xsi[^\"]*\"[^\"]*\"", "gi"), "");
// set the string rep. of the XML back to real XML
return new XML(xmlText);
}
}
internal class ParsedXMLConverter
{
public function convert(parsedXml:XML):String
{
var converter:Converter = new Converter();
var words:XMLList = parsedXml..word;
var result:String = "";
//var n:int = words.length();
//for (var i:int = 0; i < n; i++)
//{
//result += converter.convert(words[i].reading, words[i].pos) + " ";
//}
//助詞以外は全部くっつけちゃったほうがいいかも
var buf:String = "";
var n:int = words.length();
for (var i:int = 0; i < n; i++)
{
if (words[i].pos == "助詞")
{
result += converter.convert(buf) + " ";
buf = "";
result += converter.convert(words[i].reading, words[i].pos) + " ";
}
else
{
buf += words[i].reading;
}
}
result += converter.convert(buf) + " ";
return result;
}
}
internal class Converter
{
public static const COLLECTION_BASIC:Array = [
["あ", "a"],
["い", "i"],
["う", "u"],
["え", "e"],
["お", "o"],
["ぁ", "a"],
["ぃ", "i"],
["ぅ", "u"],
["ぇ", "e"],
["ぉ", "o"],
["か", "ka"],
["き", "ki"],
["く", "ku"],
["け", "ke"],
["こ", "ko"],
["きゃ", "kya"],
["きゅ", "kyu"],
["きょ", "kyo"],
["さ", "sa"],
["し", "shi"],
["す", "su"],
["せ", "se"],
["そ", "so"],
["しゃ", "sha"],
["しゅ", "shu"],
["しょ", "sho"],
["た", "ta"],
["ち", "chi"],
["つ", "tsu"],
["て", "te"],
["と", "to"],
["ちゃ", "cha"],
["ちゅ", "chu"],
["ちょ", "cho"],
["な", "na"],
["に", "ni"],
["ぬ", "nu"],
["ね", "ne"],
["の", "no"],
["にゃ", "nya"],
["にゅ", "nyu"],
["にょ", "nyo"],
["は", "ha"],
["ひ", "hi"],
["ふ", "fu"],
["へ", "he"],
["ほ", "ho"],
["ひゃ", "hya"],
["ひゅ", "hyu"],
["ひょ", "hyo"],
["ま", "ma"],
["み", "mi"],
["む", "mu"],
["め", "me"],
["も", "mo"],
["みゃ", "mya"],
["みゅ", "myu"],
["みょ", "myo"],
["や", "ya"],
["ゆ", "yu"],
["よ", "yo"],
["ら", "ra"],
["り", "ri"],
["る", "ri"],
["れ", "re"],
["ろ", "ro"],
["りゃ", "rya"],
["りゅ", "ryu"],
["りょ", "ryo"],
["わ", "wa"],
["を", "o"],
["ん", "nn"],
["が", "ga"],
["ぎ", "gi"],
["ぐ", "gu"],
["げ", "ge"],
["ご", "go"],
["ぎゃ", "gya"],
["ぎゅ", "gyu"],
["ぎょ", "gyo"],
["ざ", "za"],
["じ", "zi"],
["ず", "zu"],
["ぜ", "ze"],
["ぞ", "zo"],
["じゃ", "zya"],
["じゅ", "zyu"],
["じょ", "zyo"],
["だ", "da"],
["じ", "ji"],
["ず", "zu"],
["で", "de"],
["ど", "do"],
["じゃ", "zya"],
["じゅ", "zyu"],
["じょ", "zyo"],
["ば", "ba"],
["び", "bi"],
["ぶ", "bu"],
["べ", "be"],
["ぼ", "bo"],
["びゃ", "bya"],
["びゅ", "byu"],
["びょ", "byo"],
["ぱ", "pa"],
["ぴ", "pi"],
["ぷ", "pu"],
["ぺ", "pe"],
["ぽ", "po"],
["ぴゃ", "pya"],
["ぴゅ", "pyu"],
["ぴょ", "pyo"],
["、", "."],
["。", "."]
];
public static const COLLECTION_PARTICLE:Array = [
["あ", "a"],
["い", "i"],
["う", "u"],
["え", "e"],
["お", "o"],
["か", "ka"],
["き", "ki"],
["く", "ku"],
["け", "ke"],
["こ", "ko"],
["きゃ", "kya"],
["きゅ", "kyu"],
["きょ", "kyo"],
["さ", "sa"],
["し", "shi"],
["す", "su"],
["せ", "se"],
["そ", "so"],
["しゃ", "sha"],
["しゅ", "shu"],
["しょ", "sho"],
["た", "ta"],
["ち", "chi"],
["つ", "tsu"],
["て", "te"],
["と", "to"],
["ちゃ", "cha"],
["ちゅ", "chu"],
["ちょ", "cho"],
["な", "na"],
["に", "ni"],
["ぬ", "nu"],
["ね", "ne"],
["の", "no"],
["にゃ", "nya"],
["にゅ", "nyu"],
["にょ", "nyo"],
["は", "wa"],
["ひ", "hi"],
["ふ", "fu"],
["へ", "he"],
["ほ", "ho"],
["ひゃ", "hya"],
["ひゅ", "hyu"],
["ひょ", "hyo"],
["ま", "ma"],
["み", "mi"],
["む", "mu"],
["め", "me"],
["も", "mo"],
["みゃ", "mya"],
["みゅ", "myu"],
["みょ", "myo"],
["や", "ya"],
["ゆ", "yu"],
["よ", "yo"],
["ら", "ra"],
["り", "ri"],
["る", "ri"],
["れ", "re"],
["ろ", "ro"],
["りゃ", "rya"],
["りゅ", "ryu"],
["りょ", "ryo"],
["わ", "wa"],
["を", "o"],
["ん", "nn"],
["が", "ga"],
["ぎ", "gi"],
["ぐ", "gu"],
["げ", "ge"],
["ご", "go"],
["ぎゃ", "gya"],
["ぎゅ", "gyu"],
["ぎょ", "gyo"],
["ざ", "za"],
["じ", "zi"],
["ず", "zu"],
["ぜ", "ze"],
["ぞ", "zo"],
["じゃ", "ja"],
["じゅ", "ju"],
["じょ", "jo"],
["だ", "da"],
["じ", "ji"],
["ず", "zu"],
["で", "de"],
["ど", "do"],
["じゃ", "ja"],
["じゅ", "ju"],
["じょ", "jo"],
["ば", "ba"],
["び", "bi"],
["ぶ", "bu"],
["べ", "be"],
["ぼ", "bo"],
["びゃ", "bya"],
["びゅ", "byu"],
["びょ", "byo"],
["ぱ", "pa"],
["ぴ", "pi"],
["ぷ", "pu"],
["ぺ", "pe"],
["ぽ", "po"],
["ぴゃ", "pya"],
["ぴゅ", "pyu"],
["ぴょ", "pyo"]
];
public function convert(target:String, pos:String="noun"):String
{
if ( !target) return "";
var collection:Array = COLLECTION_BASIC;
if (pos == POS.PARTICLE) collection = COLLECTION_PARTICLE;
var result:String = _convert(target, collection);
return result;
}
private function _convert(target:String, collection:Array):String
{
target = StringUtil.toHiragana(target);
var result:String = "";
var isPrevSmallTsu:Boolean = false;
var n:int = target.length;
for (var i:int = 0; i < n; i++)
{
result = __convert(
result,
target.charAt(i),
target.charAt(i - 1),
target.charAt(i + 1),
collection
);
}
return result;
}
private function __convert(result:String, current:String, prev:String, next:String, collection:Array):String
{
var tmp:String = current;
if (/[A-Za-z\.]/.test(current))
{
if (!/[a-zA-Z\.]/.test(prev))
{
result += " ";
}
result += tmp;
return result;
}
if (/[ゃゅょ]/.test(next))
{
tmp += next;
}
if (/[ゃゅょ]/.test(current) && result != "")
{
return result;
}
if (current == "っ")
{
return result;
}
if (result != "" &&
/ー/.test(current)
)
{
result = result + result.substr(result.length - 1, 1);
return result;
}
var converted:String = "";
var m:int = collection.length;
for (var j:int = 0; j < m; j++)
{
if (collection[j][0] == tmp)
{
converted = collection[j][1];
}
}
if (prev == "っ")
{
var fl:String = converted.charAt(0);
if (fl.indexOf("aiueo") == -1)
{
converted = fl + converted;
}
}
if (converted)
{
if (result) result += " ";
result += converted;
}
return result;
}
}
import flash.events.Event;
import flash.media.Sound;
import flash.net.URLRequest;
import flash.net.URLVariables;
internal class Speaker
{
public function speak(target:String):void
{
if (!target) return;
var req:URLRequest = new URLRequest(Config.URL_PROXY);
var vars:URLVariables = new URLVariables();
req.data = vars;
vars.url = "http://translate.google.com/translate_tts?tl=es&q=" + encodeURI(target);
var snd:Sound = new Sound();
snd.addEventListener(Event.COMPLETE, _onComplete);
snd.load(req);
}
private function _onComplete(e:Event):void
{
Sound(e.target).play();
}
}
internal class POS
{
public static const PARTICLE:String = "助詞";
}
internal class StringUtil
{
public static function toHiragana(target:String):String
{
var result:String = "";
var n:int = target.length;
for (var i:int = 0; i < n; i++)
{
var char:String = target.charAt(i);
if (/[ア-ン]/.test(char))
{
result += String.fromCharCode(char.charCodeAt() + (12354 - 12450));
}
//あとで
//else if (/[ア-ン]/.test(char))
//{
//result += String.fromCharCode(char.charCodeAt() + (12354 - 65393));
//}
else
{
result += char;
}
}
return result;
}
}