forked from: VoiceChat (Japanese support)
日本語対応
/**
* Copyright ikekou ( http://wonderfl.net/user/ikekou )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/w08s
*/
// forked from paq's VoiceChat
package
{
import com.bit101.components.ComboBox;
import com.bit101.components.PushButton;
import com.bit101.components.Window;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.media.Sound;
import flash.net.URLRequest;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
import flash.net.URLVariables;
import flash.system.IME;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.ui.Keyboard;
import net.user1.reactor.IClient;
import net.user1.reactor.Reactor;
import net.user1.reactor.ReactorEvent;
import net.user1.reactor.Room;
import net.user1.reactor.RoomEvent;
/**
* Union と Google TTS を使ったチャット
* 日本語は使えない
*
* @author paq89
*/
/**
* 日本語を無理矢理しゃべらせる
* "Google Text To Speech" speak Japanese. (http://wonderfl.net/c/faUk)
* と組み合わせてみた
*
* @author ikekou
*/
[SWF(width="465", height="465", backgroundColor="0xFFFFFF", frameRate="60")]
public class Main extends Sprite
{
private var _reactor:Reactor;
private var _room:Room;
private var _sound:Sound;
private var _header:URLRequestHeader;
private var _request:URLRequest;
private var _inputText:TextField;
private var _outputText:TextField;
private var _variable:URLVariables;
//private var _languages:Array = ["en", "de", "fr", "es", "it"];
private var _languages:Array = ["ja", "en", "de", "af", "sq", "hy",
"ca", "hr", "cs", "da", "nl",
"fi", "fr", "el", "ht", "hi",
"hu", "is", "id", "it", "la",
"lv", "mk", "no", "pl", "pt",
"ro", "ru", "sr", "sk", "es",
"sw", "sv", "tr", "vi", "cy"]
private var _comboBox:ComboBox;
private var _window:Window;
private var _selectedLanguage:String;
private var _sendButton:PushButton;
private var isChanged:Boolean = false;
// Constructor
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
graphics.beginFill(0xFFFFFF);
graphics.drawRect(0, 0, 465, 465);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// init google tts
//_request = new URLRequest("http://paq-web.appspot.com/proxy");
_request.method=URLRequestMethod.POST;
_variable = new URLVariables();
_request.data = _variable;
// init union reactor
_reactor = new Reactor();
_reactor.addEventListener(ReactorEvent.READY, _onReady);
_reactor.connect("tryunion.com", 80);
// build ui
//_inputText = new InputText(this, 0, 465 - 20, "");
_inputText = new TextField();
addChild(_inputText);
_inputText.x = 0;
_inputText.y = 465 - 20;
_inputText.width = 465 - 100;
_inputText.height = 19;
_inputText.border = true;
_inputText.borderColor = 0x999999;
_inputText.multiline = false;
_inputText.type = TextFieldType.INPUT;
_inputText.addEventListener(KeyboardEvent.KEY_UP, _onKeyUp);
//_outputText = new Text(this, 0, 0);
_outputText = new TextField();
addChild(_outputText);
_outputText.width = 465 -1;
_outputText.height = 465 - 20;
_outputText.border = true;
_outputText.borderColor = 0x999999;
_sendButton = new PushButton(this, 465 - 100, 465 - 20, "SEND", _onSend);
//_comboBox = new ComboBox(this, 465 - 40, 0, "", _languages);
//_comboBox.width = 40;
//_comboBox.selectedIndex = 0;
_selectedLanguage = _languages[0];
_window = new Window(this, 465 - 100, 0, "Languages");
_window.width = 100;
_window.height = 18 * 20;
_window.minimized = true;
_window.shadow = false;
_window.draggable = false;
_window.hasMinimizeButton = true;
_window.title = "Languages (" + _selectedLanguage + ")";
var len:int = _languages.length;
var x:int = 0;
var y:int = 0;
for (var i:int = 0; i < len; i++ )
{
var button:PushButton = new PushButton(_window.content, x * 50, y * 20, _languages[i], _onClick);
button.width = 50;
y++;
if (y > 16) {
x++;
y = 0;
}
//default language japanese
if (i == 0)
{
button.dispatchEvent(new Event(MouseEvent.CLICK));
}
}
stage.focus = _inputText;
}
private function _onKeyUp(e:KeyboardEvent):void
{
if (e.keyCode == Keyboard.ENTER) {
_onSend(e);
}
}
private function speak(text:String):void
{
if (text.slice(0,2) != "ja")
{
_sound = new Sound();
_sound.addEventListener(Event.COMPLETE, _onComplete);
_variable.url = "http://translate.google.com/translate_tts?tl=" + text.slice(0,2) + "&q=" + encodeURI(text.slice(2));
// _request.url = "http://translate.google.com/translate_tts?tl=" + text.slice(0,2) + "&q=" + encodeURI(text.slice(2));
// _request.url = "http://paq-web.appspot.com/proxy?url=" + encodeURIComponent("http://translate.google.com/translate_tts?tl=" + text.slice(0, 2) + "&q=" + text.slice(2));
_sound.load(_request);
}
else
{
var p:Parser = new Parser();
p.addEventListener(Event.COMPLETE, _onParseComplete4Ja);
p.parse(text.slice(2));
stage.focus = _inputText;
}
}
private function displayMessage(text:String):void
{
_outputText.text = text + "\n" + _outputText.text;
}
private function _onReady(e:ReactorEvent):void
{
_reactor.self().setAttribute("USERNAME", this.root.loaderInfo.parameters["viewer.displayName"]);
_room = _reactor.getRoomManager().createRoom("wonderfl_paq_voice_chat");
_room.addEventListener(RoomEvent.ADD_OCCUPANT, _onAddOccupant)
_room.addEventListener(RoomEvent.REMOVE_OCCUPANT, _onRemoveOccupant);
_room.addMessageListener("CHAT_MESSAGE", _onChatMessage);
_room.join();
}
private function _onAddOccupant(e:RoomEvent):void
{
displayMessage(e.getClient().getAttribute("USERNAME") + " joined the chat.");
}
private function _onRemoveOccupant(e:RoomEvent):void
{
displayMessage(e.getClient().getAttribute("USERNAME") + " left the chat.");
}
private function _onChatMessage(client:IClient, message:String):void
{
speak(message);
displayMessage(client.getAttribute("USERNAME") + ": " + message.slice(2));
}
private function _onComplete(e:Event):void
{
_sound.play();
}
private function _onSend(e:Event):void
{
if (_inputText.text == "") return;
if (_inputText.text.length > 100)
{
displayMessage("!!error!!");
return;
}
_room.sendMessage("CHAT_MESSAGE", true, null, _selectedLanguage + _inputText.text);
//_room.sendMessage("CHAT_MESSAGE", true, null, _languages[_comboBox.selectedIndex] + _inputText.text);
_inputText.text = "";
}
private function _onClick(e:Event):void
{
_selectedLanguage = e.target.label;
_window.minimized = true;
_window.title = "Languages (" + _selectedLanguage + ")";
if (_selectedLanguage != "ja")
{
_inputText.removeEventListener(KeyboardEvent.KEY_DOWN, _onKeyUp4Ja);
_inputText.removeEventListener(Event.CHANGE, _onChange4Ja);
_inputText.addEventListener(KeyboardEvent.KEY_UP, _onKeyUp);
IME.enabled = false;
_inputText.restrict = "0-9A-Za-z,. !?"; //ここらへん適当
}
else
{
_inputText.removeEventListener(KeyboardEvent.KEY_UP, _onKeyUp);
_inputText.addEventListener(KeyboardEvent.KEY_DOWN, _onKeyUp4Ja);
_inputText.addEventListener(Event.CHANGE, _onChange4Ja);
IME.enabled = true;
_inputText.restrict = "0-9A-Za-z,.、-熙0-9A-z !?!?"; //ここらへん適当
}
}
private function _onChange4Ja(e:Event):void
{
isChanged = true;
}
private function _onKeyUp4Ja(e:KeyboardEvent):void
{
if (e.keyCode == Keyboard.ENTER && isChanged)
{
isChanged = false;
_onSend(null);
}
}
private function _onParseComplete4Ja(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.media.Sound;
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;
}
}
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
{
result += char;
}
}
return result;
}
}