gTranslatorに韓国語と中国語の読み上げ機能を追加してみた(準備編)の続きです。
年をまたいで、かなり間が空いてしまいましたが、前回は、Windows Liveへのサインアップと、Microsoft® Translator ツールの各種APIコール時に必要なAppIDの作成までが完了しました。それでは、実装の部分に入りたいと思います。
APIについて
韓国語と中国語の読み上げには、Speak Methodを利用しています。
このAPIのURIは
http://api.microsofttranslator.com/V2/Http.svc/Speak
です。
音声にしたい文字列や、それが何国語なのか、などはパラメータで渡します。
- appId:gTranslatorに韓国語と中国語の読み上げ機能を追加してみた(準備編)で作成したAppIDを指定します。これが正しく指定されていないと、正常な結果は得られません。
- text:音声に変換したい文章です。gTranslatorでは翻訳結果をここに指定します。
- language:textパラメータに指定した文章が何国語かを指定します。
- format:オプションで音声ファイルの形式を指定します。デフォルトではwav形式となります。
3番目のlanguageパラメータには、サポートされている言語を指定します。サポートされている言語は、GetLanguagesForSpeak Methodで取得することができます。
ブラウザで、以下のようにURLを入力してAPIを実行すると、
http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForSpeak?appId=取得したAppID
以下のような結果が得られました。
<ArrayOfstring> <string>en</string> <string>de</string> <string>es</string> <string>fr</string> <string>it</string> <string>pt</string> <string>ru</string> <string>ja</string> <string>ko</string> <string>zh-chs</string> <string>zh-cht</string> </ArrayOfstring>
韓国語はkoです。中国語は2種類あります。繁体字中国語(Traditional Chinese)と簡体字中国語(Simplified Chinese)です。繁体字中国語はzh-chtで、簡体字中国語はzh-chsになります。
まとめると、、、
以下のように実行します。
http://api.microsofttranslator.com/v2/Http.svc/Speak?appId=取得したAppID&text=%EC%95%88%EB%85%95%ED%95%98%EC%84%B8%EC%9A%94&language=ko
APIコールの実装
翻訳結果読み上げに使うAPIの概要がだいたいわかったところで、そのAPIをコールする部分を実装していきたいと思います。
処理は、大きく分けると以下の2点が中心になります。
- Speak Methodをコールし、取得したwavファイルを保存する。
- 保存したwavファイルを再生する。
どちらも、サーバやネットワークの状態、端末の状態によっては処理に時間がかかる可能性がありますので、Threadクラスで実装することにしました。
※ 実装には、日本語の読み上げに利用させていただいているJaTTSのソースコードを参考にさせていただきました。
gimite/android-jatts – GitHub
では、まず、APIをコールしてwavファイルを保存するThreadクラスです。
package jp.flashcast.translator.android.thread; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import jp.flashcast.translator.android.manager.TTSManager.TTSState; import jp.flashcast.translator.android.model.TTSModel; import android.content.Context; import android.os.Handler; public class TTSThread extends Thread { private final Context context; private final Handler handler; private final Runnable runnable; private final TTSModel model; private final HttpClient client; private FileOutputStream file; public TTSThread(Context context, Handler handler, Runnable runnable, TTSModel model) { this.context = context; this.handler = handler; this.runnable = runnable; this.model = model; this.client = new DefaultHttpClient(); try { this.context.deleteFile(TTSModel.MEDIA_FILE_NAME); this.file = context.openFileOutput(TTSModel.MEDIA_FILE_NAME, Context.MODE_WORLD_READABLE); } catch (FileNotFoundException e) { model.setDownloaded(false); } } @Override public void start() { model.setStatus(TTSState.LOADING); super.start(); } @Override public void run() { String url = model.getURL(); if (!url.equals("")) { model.setDownloaded(false); HttpGet get = new HttpGet(url); try { final HttpResponse response = client.execute(get); if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { InputStream input = response.getEntity().getContent(); int read; byte[] buffer = new byte[4096]; while ((read = input.read(buffer)) > 0) { file.write(buffer, 0, read); } file.close(); input.close(); model.setDownloaded(true); } } catch (ClientProtocolException e) { model.setDownloaded(false); } catch (IOException e) { model.setDownloaded(false); } } else { model.setDownloaded(false); } handler.post(runnable); } }
APIコールに必要な情報は、すべて1つのモデルクラスにまとめるよう実装しました。
※ 12行目のAppIDは適宜変更してください。
package jp.flashcast.translator.android.model; import jp.flashcast.translator.android.manager.TTSManager.TTSState; import android.net.Uri; public class TTSModel { private String text; private String language; private boolean downloaded; private TTSState state; private final static String G_TRANSLATE_BING_TRANSLATE_API_URL = "http://api.microsofttranslator.com/V2/Http.svc/Speak"; private final static String G_TRANSLATE_BING_TRANSLATE_API_APPID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; public final static String MEDIA_FILE_NAME = "temp.wav"; public TTSModel() { this.text = ""; this.language = ""; this.downloaded = false; this.state = TTSState.IDLE; } public void setText(String text) { this.text = text; } public String getText() { return text; } public void setLanguage(String language) { this.language = language; } public String getLanguage() { return language; } public void setDownloaded(boolean downloaded) { this.downloaded = downloaded; } public boolean isDownloaded() { return downloaded; } public void setStatus(TTSState state) { this.state = state; } public TTSState getStatus() { return state; } public String getURL() { String url =""; if (!text.equals("") && !language.equals("")) { url = G_TRANSLATE_BING_TRANSLATE_API_URL + "?appid=" + G_TRANSLATE_BING_TRANSLATE_API_APPID + "&text=" + Uri.encode(text) + "&language=" + language; } return url; } }
次に、保存したwavファイルを再生するThreadクラスです。
package jp.flashcast.translator.android.thread; import java.io.IOException; import jp.flashcast.translator.android.manager.TTSManager.TTSState; import jp.flashcast.translator.android.model.TTSModel; import android.content.Context; import android.media.MediaPlayer; import android.speech.tts.TextToSpeech; public class PlayerThread extends Thread { private final Context context; private final MediaPlayer player; private final TTSModel model; public PlayerThread(Context context, MediaPlayer player, TTSModel model) { this.context = context; this.player = player; this.model = model; } @Override public void start() { model.setStatus(TTSState.SPEAKING); super.start(); } @Override public void run() { if (player != null) { try { player.reset(); player.setAudioStreamType(TextToSpeech.Engine.DEFAULT_STREAM); player.setDataSource(context.getFilesDir() + "/" + TTSModel.MEDIA_FILE_NAME); player.prepare(); player.start(); } catch (IllegalArgumentException e) { model.setStatus(TTSState.IDLE); } catch (IllegalStateException e) { model.setStatus(TTSState.IDLE); } catch (IOException e) { model.setStatus(TTSState.IDLE); } } } }
この2つのThreadをコントロールするクラスを、以下のようにしました。
package jp.flashcast.translator.android.manager; import android.content.Context; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.os.Handler; import jp.flashcast.translator.android.model.TTSModel; import jp.flashcast.translator.android.thread.PlayerThread; import jp.flashcast.translator.android.thread.TTSThread; public class TTSManager implements Runnable { private final Context context; private final MediaPlayer player; private final TTSModel model; private TTSThread thread; public enum TTSState { IDLE, LOADING, SPEAKING } public TTSManager(Context context) { this.context = context; this.player = new MediaPlayer(); this.player.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { model.setStatus(TTSState.IDLE); } }); this.model = new TTSModel(); } public synchronized void speak(String text, String language) { model.setText(text); model.setLanguage(language); if (!isSpeaking()) { if (thread != null) { thread.interrupt(); } player.stop(); } thread = new TTSThread(context, new Handler(), this, model); thread.start(); } public void run() { if (model.isDownloaded()) { new PlayerThread(context, player, model).start(); } } public boolean isSpeaking() { return model.getStatus() != TTSState.IDLE; } public void stop() { if (thread != null) { thread.interrupt(); } if (player != null) { player.stop(); } } }
画面は以下のようにし、このTTSManagerクラスのみを呼び出すようにしました。
操作は簡単です。EditTextに音声にしたい文章を入力し、Spinnerコントロールから何国語を入力したか選択します。「Play」ボタンを押すと入力した文章が再生されるという仕組みです。
読み上げを中断するための「Stop」ボタンも用意してみました。
「Play」ボタンのクリックイベントハンドラでTTSManagerクラスのspeakメソッドを、「Stop」ボタンのクリックイベントハンドラでTTSManagerのstopメソッドをコールします。
Button btnPlay = (Button)findViewById(R.id.play); btnPlay.setOnClickListener(new OnClickListener() { public void onClick(View view) { String text = txtTTS.getText().toString(); if (!text.equals("")) { manager.speak(text, (String)language.getSelectedItem()); } } }); Button btnStop = (Button)findViewById(R.id.stop); btnStop.setOnClickListener(new OnClickListener() { public void onClick(View view) { manager.stop(); } });
一応、4カ国語に対応しました。
Spinnerコントロールを押すと、以下のように表示されます。
ということで、韓国語と中国語の読み上げに対応したgTranslator、是非、試してみてください!
サンプルソース
- TranslatorSample14:ダウンロード