SATCHのコンテンツ自体がしゃべるわけじゃないです。

KDDIのN2 TTSを使います。



概要

SATCHのサンプルTOUCH MEを使います。

使用方法は、
このページ(SATCHのTOUCH MEを使ってみる) 参照

サンプルの中で、スクリーンタップに対応しているのは、TouchscreenWake.luaの中のtapFingerView()関数です。

これが機能すると、TouchMeSample.Javaコードの中の、openSatch()が呼ばれます。

このコードは、Lua --> java です。
java --> Luaは別のサンプルで...。

サンプルでは、他のサイトがオープンするようなコードになっていますが、ここを書き換て、N2 TTSにしゃべらせる文字列を渡しています。

New
文字列は、サーバーから取得するようにしました。

タップしてからしゃべるまで、1~2秒ほどタイムラグがあります。

サーバーとの通信は、Luaに書くか、java側に書くか、2とおりありますが、今回はjava(javascript)に書いています。

というわけで、SATCHのコンテンツ自体は、新規でもオリジナルのままでもいいです。



TOP

マーカー画像をタップしてしゃべらせる



Android用サンプル



ダウンロードが完了後、通知バーに表示されるので、apkファイルをクリックしてインストール。

検出用画像のサンプル


KDDIの「N2 TTS」が必要です。インストールしておいてください。

アンドロイドで日本語音声出力(TextToSpeech):音声読み上げ
を参照。

assets¥Senarioに格納するシナリオファイルは、ご自分で作成。
プロジェクトファイル


src¥com¥kddi¥satch¥gambaemblemactivity¥GambaemblemActivity_simple.java

assets¥www¥index.html

以下のようにコードの「追加4xx」部分を修正してください。

「要修正」のところは、ご自分のdpdファイル名。

【GambaemblemspeechActivity_simple.java】

package com.kddi.satch.gambaemblemspeechActivity;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyEvent;
import android.view.View;
import android.widget.FrameLayout;

import com.kddi.satch.LoadScenarioStatus;
import com.kddi.satch.ARViewer;

import android.speech.tts.TextToSpeech;//追加4tts
import android.speech.tts.TextToSpeech.OnInitListener;//追加4tts


public abstract class GambaemblemspeechActivity_simple extends Activity implements OnInitListener {//追加4tts implements
    protected abstract String getSampleScenarioName();
    protected abstract String getSampleLogTag();
    
    //add
    private static final String     THIS_CLASS_SHORT_NAME = GambaemblemspeechActivity_simple.class.getName().replace("com.kddi.satch.gambaemblemspeech.", "");
    private static final String   THIS_LOGTAG  = THIS_CLASS_SHORT_NAME;
    
    public TextToSpeech tts;//追加4tts
    
    protected boolean _isInitializedCorrectly;
    protected ARViewer _kddiComponent;
    protected FrameLayout _frameLayout;
    
    public void resetMembers(){
        _isInitializedCorrectly = false;
        _frameLayout = null;
        _kddiComponent = null;
    }
    private static final int DIALOG_EXIT = 0;
    
    public void initComponent(){
        _isInitializedCorrectly = false;
        _kddiComponent = new ARViewer(this);
        // This FrameLayout must be empty (but initialized) when you pass it to the kddiComponent.initialize() method.
        _frameLayout = new FrameLayout(this);
        _kddiComponent.initialize(_frameLayout);
        _isInitializedCorrectly = true;
    }
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Add null to AR Viewer Library compornent's reference.
        resetMembers();
    }
    
    @Override
    public void onRestart() {
        super.onRestart();
    }
    
    @Override
    public void onStart() {
        super.onStart();
        // Create AR Viewer Library compornent.
        initComponent();
        postInitComponent();
        initContentView();
        if (_isInitializedCorrectly) {
            // Do authorize and madia is loaded.
            // You must call loadScenario() method.
            loadScenario();
        }
    }
    
    @Override
    public void onResume(){
        super.onResume();
        if (_isInitializedCorrectly) {
            // GL context is recreated and media is reloaded.
            _kddiComponent.onResume();
            reservePlayScenario();
        }
    }
    
    @Override
    public void onPause() {
        // When the activity is paused the GL context is destroyed, so all media is unloaded.
        if (_isInitializedCorrectly) {
            cancelReservePlayScenario();
        if (_kddiComponent.checkLoadScenarioStatus() == LoadScenarioStatus.COMPLETE) {
            _kddiComponent.pauseScenario();
        }
            _kddiComponent.onPause();
        }
        super.onPause();
    }
    
    @Override
    public void onStop() {
        releaseContentView();
        // Destroy AR Viewer Library Objects.
        if (_isInitializedCorrectly){
            _kddiComponent.terminate();
            _kddiComponent = null;
            _frameLayout = null;
        }
        super.onStop();
    }
    
  //追加4tts
    @Override
    public void onInit(int status) {
        //空
        //
    }
    
    @Override
    public void onDestroy() {
        // Destroy AR Viewer Library Objects.
        if (_isInitializedCorrectly){
            _frameLayout = null;
            _kddiComponent = null;
        }
        super.onDestroy();
        resetMembers(); // forced clean
        
        //追加4tts
        tts.shutdown();
    }
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent msg){
        switch(keyCode){
            case android.view.KeyEvent.KEYCODE_BACK :
            showDialog( DIALOG_EXIT );
            return true;
        }
        return false;
    }
    
    public void postInitComponent(){
        // override this if you need to do some special handling on the component after standard initialization
        if (_isInitializedCorrectly) {
            _kddiComponent.activateAutoFocusOnDownEvent(true);
        }
    }
    
    public void initContentView(){
        // override this if you need to do some special handling on the component after standard initialization
        if (_isInitializedCorrectly) {
            // you'll probably use some other UI object as the content view that itself will embed the component's frame layout -- here you can change all this
            // by default, the frame layout containing DFusion will be the activity content view
            setContentView(_frameLayout);
        }
    }
    
    public void releaseContentView(){
        // override this if you need to do some special handling on the component after standard initialization
        if (_isInitializedCorrectly) {
            // do here the release of the your UI instances (if customized)
        }
    }
    
    public void loadScenario(){
        ApplicationInfo appInfo = null;
        PackageManager packMgmr = getApplicationContext().getPackageManager();
        try {
            appInfo = packMgmr.getApplicationInfo(getPackageName(), 0);
        } catch (NameNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException("Unable to locate assets, aborting...");
        }
        String dpdfile = appInfo.sourceDir + getSampleScenarioName();
        _kddiComponent.loadScenario(dpdfile);
    }
    
    // Set polling rate for loading the media.
    private final int REPEAT_INTERVAL = 100;
    private Handler handler = new Handler();
    private Runnable runnable = null;
    
    
    //
    private void reservePlayScenario(){
        if (runnable == null){
            runnable = new Runnable(){
                @Override
                public void run(){
                    LoadScenarioStatus status = _kddiComponent.checkLoadScenarioStatus();
                    if (status == LoadScenarioStatus.CANCEL){
                        // cancel(appli suspend)
                    }else if (status == LoadScenarioStatus.COMPLETE){
                        // Ready to play scenario
                        _frameLayout.setVisibility(View.VISIBLE);
                        _kddiComponent.playScenario();
                    }else if (
                        status == LoadScenarioStatus.ERROR_NETWORK_UNUSABLE ||
                        // faild to load a media becase of no network connection.
                        status == LoadScenarioStatus.ERROR_NETWORK ||
                        // faild to load a media becase of network error.
                        status == LoadScenarioStatus.ERROR_SOFTWAREKEY ||
                        // faild to load a media becase software key has not be found on server.
                        status == LoadScenarioStatus.ERROR_CONTENT_STOPPED ||
                        // faild to load a media becase content has stopped.
                        status == LoadScenarioStatus.ERROR_SERVER ||
                        // faild to load a media becase of server error.
                        status == LoadScenarioStatus.ERROR_ETC
                        // faild to load a media becase of another error.
                        ){
                    // error
                    }else{
                        handler.postDelayed(this, REPEAT_INTERVAL);
                    }
                }
            };
            handler.postDelayed(runnable, REPEAT_INTERVAL);
        }
    }
    
    //
    
    private void cancelReservePlayScenario(){
        if (handler != null && runnable != null){
            handler.removeCallbacks(runnable);
            runnable = null;
        }
    }
    
    protected Dialog onCreateDialog(int id) {
        Dialog dialog;
        switch(id) {
            case DIALOG_EXIT:
            {
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage(
                    "Really want to quit the sample?"
                )
                .setCancelable(true)
                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        finish();
                    }})
                    .setNegativeButton("No", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }});
                    dialog = builder.create();
                    break;
            }
            default:
            dialog = null;
        }
        return dialog;
    }
}

【GemblemspeechActivity.java】

package com.kddi.satch.gambaemblemspeech;

import com.kddi.satch.gambaemblemspeechActivity.GambaemblemspeechActivity_simple;
import android.os.Bundle;
//add
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

import android.speech.tts.TextToSpeech;//追加4tts

//追加4web
import android.content.Context;
import android.webkit.WebChromeClient; 
import android.webkit.WebView;

public class GemblemspeechActivity extends GambaemblemspeechActivity_simple {
    private static final String THIS_CLASS_SHORT_NAME = GemblemspeechActivity.class.getName().replace("com.kddi.satch.gambaemblemspeech.", "");
    private static final String THIS_LOGTAG  = THIS_CLASS_SHORT_NAME;
    // Set AR scenario file path.
    private static final String SCENARIO_NAME = "/assets/Scenario/Scenario_a/gambaemblemspeech.dpd";//要修正
    protected String getSampleScenarioName() { return SCENARIO_NAME; }
    protected String getSampleLogTag() { return THIS_LOGTAG;   }
    
    //追加4web
    private WebView webView = null;
    
    @Override
    public void postInitComponent(){
        super.postInitComponent();
        
        //add
        if (_isInitializedCorrectly) {
            _kddiComponent.activateAutoFocusOnDownEvent(true);
            _kddiComponent.registerCommunicationCallback("openSatch", this, "openSatch");
        }
    }
    
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //追加4tts
        tts = new TextToSpeech(getApplicationContext(), this);
        
        //追加4web
        webView = new WebView(this);
        webView.setWebChromeClient(new WebChromeClient());
        webView.getSettings().setJavaScriptEnabled(true);
        JavascriptAdapter jsobj = new JavascriptAdapter(this);
        webView.addJavascriptInterface(jsobj, "android_ar");
        webView.loadUrl("file:///android_asset/www/index.html");
    }
    
    //追加4tts
    public void onInit(int status) {
        if (status == TextToSpeech.SUCCESS) {
            //空
        } else {
            //
            
        }
    }
    
    //add
    public void openSatch(String[] arrayOfString) {
        
        //追加4tts
        webView.loadUrl("javascript:ajax_job()");
        
    }
    
    //add
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(Menu.NONE, Menu.FIRST, Menu.NONE, getString(R.string.setting));
        return super.onCreateOptionsMenu(menu);
    }
    
    //add
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        boolean ret = true;
        switch (item.getItemId()) {
        default:
            ret = super.onOptionsItemSelected(item);
            break;
        case Menu.FIRST:
            ret = true;
            Intent intent = new Intent(GemblemspeechActivity.this.getApplicationContext(), GambaemblemSetting.class);
            startActivity(intent);
            break;
        }
        return ret;
    }
    
    //JavaScript -> Java
    class JavascriptAdapter {
        
        private Context con;
        
        public JavascriptAdapter(Context con) {
            this.con = con;
         }
        //
        public void start_tts(String speec_content){
            tts.speak(speec_content, TextToSpeech.QUEUE_FLUSH, null);
        }
        
    }
}

【index.html】

<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="http://code.jquery.com/jquery-1.6.1.min.js"></script>

<script type="text/javascript" charset="utf-8">
    
    function onSuccess_ajax(data, status){
        information = data;
        android_ar.start_tts(information);
        
    }

    function onError_ajax(data, status){
        // handle an error
        alert("err");
    }   
    
    function ajax_job(){
        
        var formData = "data=" + "dummy";
        var url_ajax = "http://wisteriahill.sakura.ne.jp/satch/call_message.php";
        
        $.ajax({
            type: "POST",
            url: url_ajax,
            cache: false,
            data: formData,
            success: onSuccess_ajax,
            error: onError_ajax
        });
        return false;
        
    }
    
    
</script>

</head>
<body
</body>
</html>


TOP






N2 TTSのスピーチ終了を検知するコード



N2 TTSがインストールされているかどうかをチェックするコードも追加。

//使用するパッケージ
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
import java.util.HashMap;
import java.util.Map;
import android.widget.Toast;
    ・
    ・
    ・
    ・
private TextToSpeech tts;
    ・
    ・
    ・
public void onCreate(Bundle savedInstanceState) {
    tts = new TextToSpeech(getApplicationContext(), new OnInitListener() {
            public void onInit(int status) {
                //スピーチ終了のリスナー
                tts.setOnUtteranceCompletedListener(new OnUtteranceCompletedListener() {
                        public void onUtteranceCompleted(String utteranceId) {
                //To Do
                        }
                    });
            }
        });

}


@Override
public void onInit(int status) {
    if (status == TextToSpeech.SUCCESS) {
        tts.speak("TTS準備OKです", TextToSpeech.QUEUE_FLUSH, null);
    } else {
        //
        
    }
}

@Override
public void onDestroy() {
    super.onDestroy();
    resetMembers(); // forced clean
    
    tts.shutdown();


}


private void tts_speak(){
    //N2 TTSが入っているかチェック
    String res = package_check("jp.kddilabs.n2tts");
        if (res.equals("ok")){
            String UTTERANCE_ID = "SPEECH";//ここは何でもいいです
            HashMap<String, String> ttsparam = new HashMap<String, String>();
       ttsparam.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, UTTERANCE_ID);
       
            tts.speak("テキスト", TextToSpeech.QUEUE_FLUSH, ttsparam);
        }else if (res.equals("no")){
            Toast.makeText(satchActivity_simple.this, 
                    "音声出力エンジンのN2 TTSがみあたりません", 
                    Toast.LENGTH_LONG).show();
        }


}


private String package_check(String package_name){
        String exist_stat = "ok";
        //
        try  {  
          PackageManager packageManager = this.getPackageManager();  
          ApplicationInfo applicationInfo = packageManager.getApplicationInfo(package_name,PackageManager.GET_META_DATA);  
        
        
        }catch(NameNotFoundException exception){ 
            exist_stat = "no";
            
        }  
        
        return exist_stat;
}