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:ダウンロード

