Flexで音声合成

Flex勉強会(まいど!〜)でお話しした内容の一部です。


Flexで、入力した文字を音声で読み上げる。


マイコミジャーナルの記事が面白そうだったのと、gainerでFlashからローカルjavaにアクセスするアイデアが意外で面白かったので、


組み合わせてやってまえ!ということで作りました。


つまり、Flexでローカルjavaにソケット接続して、FreeTTSで音声を読み上げる方法です。


Flex(SpeechClient.mxml)

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
	creationComplete="connect();">
	
	<mx:VBox horizontalAlign="center" horizontalCenter="0">
		<mx:Spacer width="100%" height="100"/>
		
		<mx:HBox width="100%" horizontalAlign="center">
			<mx:TextInput id="txtMessage" horizontalCenter="0" width="294"/>
			<mx:Button label="Speech" click="speech();"/>
		</mx:HBox>
	</mx:VBox>
	
	<mx:Script>
		<![CDATA[
			private var socket:Socket;
			
			private function speech():void{
				socket.writeUTFBytes(txtMessage.text);
				socket.writeUTFBytes('\n');
				socket.flush();
			}
			
			private function connect():void{
				socket = new Socket();
				socket.addEventListener("connect", socket_connect);
				socket.addEventListener("ioError", socket_ioError);
				socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
				socket.connect("localhost", uint(10164));
			}
			private function socket_connect(event:Event):void {}
			private function socket_ioError(event:IOErrorEvent):void {}
			private function socketDataHandler(event:ProgressEvent):void {}
		]]>
	</mx:Script>
</mx:Application>


java起動(SpeechServer.java)

package jp.co.genephics.example.speech;
public class SpeechServer {
	public static void main(String[] args){
		int arg = 10164;
		if( args.length > 0 ){
			arg = Integer.parseInt(args[0]);
		}
		SpeechSocketServer server = new SpeechSocketServer();
		try{
			server.startServer(arg);
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}


javaサーバーソケット(SpeechSocketServer.java)

package jp.co.genephics.example.speech;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class SpeechSocketServer {
	public void startServer(int port)throws Exception{
		ServerSocket server = null;
		try{
			server = new ServerSocket(port,200);
			while(true){
				Socket socket = server.accept();
				socket.setSoTimeout( 1000*60 );
				// スレッド生成
				SpeechThread thread = new SpeechThread(socket);
				thread.start();
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(server != null){
				try{
					server.close();
				}catch(IOException ioe){
					ioe.printStackTrace();
				}
			}
		}
	}
}


javaスレッド(SpeechThread.java)

package jp.co.genephics.example.speech;

import java.net.Socket;

public class SpeechThread extends Thread {

	private Socket socket;
	
	public SpeechThread(Socket socket){
		this.socket = socket;
	}
	
	@Override
	public void run(){
		SpeechChild child = new SpeechChild(this.socket);
		child.execute();
	}
}


■Socketのメッセージを読んでSpeechクラスにわたすjava(SpeechChild.java)

package jp.co.genephics.example.speech;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class SpeechChild {
	private Socket socket;
	
	public SpeechChild(Socket socket){
		this.socket = socket;
	}
	
	public void execute(){
		try{
			BufferedReader reader 
				= new BufferedReader( new InputStreamReader(socket.getInputStream()));
			StringBuffer sb = new StringBuffer();
// textAreaとか複数行を拾うならこうかな
//			while(true){
				String str = reader.readLine();
//				if(str.endsWith("\n"))break;
//				sb.append( str );
//			}
			// Speechの実行
//	        String str = sb.toString();
	        Speech speech = new Speech();
	        speech.speak(str);
	        speech.deallocateSynthesizer();
		}catch(IOException e){
			e.printStackTrace();
		}
	}
}

■FreeTTSでSpeechするjava(Speech.java)

// マイコミジャーナルの記事のままですよ
// あと、ユーザーディレクトリにspeech.propertiesを置く事。
package jp.co.genephics.example.speech;
import java.beans.PropertyVetoException;
import java.util.Locale;
import javax.speech.AudioException;
import javax.speech.Central;
import javax.speech.EngineException;
import javax.speech.synthesis.Synthesizer;
import javax.speech.synthesis.SynthesizerModeDesc;
import javax.speech.synthesis.Voice;

public class Speech {
    private Synthesizer synthesizer = null;
    public Speech() {
        // シンセザイザのモードを指定
        SynthesizerModeDesc desc = new SynthesizerModeDesc
            (null, "general", Locale.US, Boolean.FALSE, null);
        try {
            // シンセザイザを作成
            synthesizer = Central.createSynthesizer(desc);
            if (synthesizer == null) {
                System.err.println("ERROR! シンセザイザが見つかりません。");
                System.exit(1);
           }
            // ボイスを作成
            String voiceName = "kevin16";
            Voice voice = new Voice(voiceName, Voice.GENDER_DONT_CARE,
                            Voice.AGE_DONT_CARE, null);
            if (voice == null) {
                System.err.println(
                    "ERROR! シンセザイザがボイス "
                    + voiceName + " をサポートしていません。");
                System.exit(1);
            }
            // リソースの割り当て
            synthesizer.allocate();
            synthesizer.resume();
            // ボイスの設定
            synthesizer.getSynthesizerProperties().setVoice(voice);
        } 
        catch (EngineException ex) {
            ex.printStackTrace();
        } catch (PropertyVetoException ex) {
            ex.printStackTrace();
        } catch (AudioException ex) {
            ex.printStackTrace();
        }
    }
    public void speak(String message) {
       try {
            // テキストの読み上げ
            synthesizer.speakPlainText(message, null);
            synthesizer.waitEngineState(Synthesizer.QUEUE_EMPTY);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
    public void deallocateSynthesizer() {
        try {
            // リソースの開放
            synthesizer.deallocate();
        } catch (EngineException ex) {
            ex.printStackTrace();
        }
    }
}


こんなところでしょうか。

しっかり読み上げてくれますけど、ちゃんと作り込んでないんで、
もし参考にする場合は、ちゃんと書いて下さいね(笑)。


僕はここでやめましたが、
このノリで作ってけば、なんか面白いの作れます?