FreeTTSを使ってJavaプログラムからしゃべらせる¶
はじめに¶
Java Speech APIの実装系の1つFreeTTSを使って、Javaプログラムからしゃべらせてみます。
環境の準備¶
NetBeans IDE(Ant)でビルドする場合のFreeTTSの入手、展開、設定¶
入手¶
FreeTTSのWebサイト から、最新版のバイナリをダウンロードします。
ページ内の[download]リンクをクリックし、バイナリのアーカイブファイルをダウンロードします。
- freetts-1.2.2-bin.zip
展開¶
ダウンロードしたファイルを展開します。Windows OSの場合、JDKをインストールした場所の並びがよいかと思います。Windows OSの場合、たいていは、C:\Program Files\Java
フォルダになります。展開し次のように配置します。
C:\Program Files\Java\ +-- freetts-1.2\ +-- bin\ +-- docs\ +-- javadoc\ +-- lib\ +-- mbrola\
アーカイブを展開しただけではjsapi.jarが生成されません。展開後、lib\jsapi.exeを管理者権限で実行します。実行するとライセンスに同意するか聞かれるダイアログが表示されるので[I Agree]を押します。するとjsapi.jarファイルが生成されます。この場所にファイルを作成するには管理者権限が必要です。
設定¶
NetBeans IDEを起動し、[ツール]メニュー > [ライブラリ]で「Antライブラリ・マネージャ」を開き、[新規ライブラリ]ボタンを押し、ライブラリ名に"FreeTTS"と入力し[OK]を押します。
「Antライブラリ・マネージャ」画面で、左側ペインでFreeTTSを選択、右側ペインで[クラスパス]タブを選択、[JAR/フォルダの追加]ボタンを押し、先にFreeTTSを展開したlibフォルダ下のjarファイルをすべて選択します。
続いて、[Javadoc]タブを選択、[ZIP/フォルダの追加]ボタンを押し、先にFreeTTSを展開したjavadocフォルダを選択します。
maven、gradleでビルドする場合ノ設定¶
FreeTTSはmavenリポジトリにあります。(設定省略)
プログラムの作成(HelloTts)¶
しゃべらせたいメッセージをテキストで入力し、ボタンを押すと入力したテキストを読み上げる、という簡単なプログラムを作成します。
NetBeans IDEでJavaFX FXMLアプリケーションとして作成¶
プロジェクトの作成(JavaFX FXMLアプリケーション)¶
NetBeans IDEの[ファイル]メニュー > [新規プロジェクト]で「新規プロジェクト」画面を開き、カテゴリ欄で[JavaFX]を選択、プロジェクト欄で[JavaFX FXMLアプリケーション]を選択し、[次へ]をクリックします。
プロジェクト名は、HelloTtsとし、FXML名はHelloTtsViewとし、アプリケーション・クラスの作成はチェックが付いたまま、名前をHelloTtsAppに(パッケージ名は何でも)変更します。
生成されるソースファイルは次のようになります。
hellotts +-- HelloTtsApp.java +-- HelloTtsView.fxml +-- HelloTtsViewController.java
画面レイアウトの作成¶
HelloTtsView.fxmlをScene Builderで開き編集します。Scenen Builderがインストールされていれば、NetBeans IDEのプロジェクトビューからHelloTtsView.fxmlをダブルクリックすればScenen Builderが立ち上がり、HelloTtsView.fxmlのレイアウトが表示されます。
中央のテキスト入力領域にTextArea、しゃべるトリガーとなるButtonを右下に、エラー発生などステータスを表示するLabelを下端に配置しています。
画面コントローラの編集¶
FXMLと対になるJavaソースファイルがHelloTtsViewController.javaです。
FXMLでレイアウトしたコントロールのうちプログラムからアクセスするものについて、インジェクション定義を記述します。
public class HelloTtsViewController implements Initializable {
@FXML
private Label statusLabel;
@FXML
private TextArea speechTextArea;
@FXML
private void handleSpeechAction(ActionEvent event) {
}
音声合成の実装¶
JavaFXプログラムにおいて、MVCの構造をとる場合、V(View)はFXML、C(Controller)はFXMLに対応づけるコントローラ、M(Model)は独自に作成するクラスとなります。
そこで、SpeechModel.javaを作成し、ここにSpeech APIを呼び出すコードを記述することにします。
比較的シンプルなJavaFXプログラムであれば、Modelはシングルトンで実装してしまうのが手っ取り早いです。
NetBeans IDEの[ファイル]メニュー > [新規ファイル]で「新規ファイル」画面を開き、カテゴリ欄で[Java]を選択、ファイル・タイプ欄で[Javaシングルトン・クラス]を選択し、[次へ]ボタンをクリックします。
クラス名に、"SpeechModel"と入れます。これで、SpeechModel.javaが生成されます。
initメソッド、doSpeechメソッド、terminateメソッドを定義します。
public class SpeechModel {
private SynthesizerModeDesc desc;
private Synthesizer synthesizer;
public void init(String voiceName) throws EngineException, AudioException, PropertyVetoException {
if (desc != null) {
return;
}
System.setProperty("freetts.voices", "com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory");
desc = new SynthesizerModeDesc(Locale.US);
Central.registerEngineCentral("com.sun.speech.freetts.jsapi.FreeTTSEngineCentral");
synthesizer = Central.createSynthesizer(desc);
synthesizer.allocate();
synthesizer.resume();
SynthesizerModeDesc smd = (SynthesizerModeDesc) synthesizer.getEngineModeDesc();
Optional<Voice> voice = Arrays.stream(smd.getVoices())
.filter(v -> v.getName().equals(voiceName))
.findFirst();
synthesizer.getSynthesizerProperties().setVoice(voice.get());
}
public void doSpeech(String text) throws InterruptedException {
synthesizer.speakPlainText(text, null);
synthesizer.waitEngineState(Synthesizer.QUEUE_EMPTY);
}
public void terminate() {
try {
synthesizer.deallocate();
} catch (EngineException ex) {
// do nothing
}
}
画面コントローラの実装¶
初期化メソッド
@Override
public void initialize(URL url, ResourceBundle rb) {
speechModel = SpeechModel.getInstance();
try {
speechModel.init("kevin16");
} catch (EngineException | AudioException | PropertyVetoException ex) {
Logger.getLogger(HelloTtsViewController.class.getName()).log(Level.SEVERE, null, ex);
statusLabel.setText("ERROR: SpeechEngine initialization:" + ex.getLocalizedMessage());
}
}
ボタンが押された時の処理
@FXML
private void handleSpeechAction(ActionEvent event) {
String textToSpeech = speechTextArea.getText();
try {
speechModel.doSpeech(textToSpeech);
} catch (InterruptedException ex) {
statusLabel.setText("ERROR: speech error" + ex.getLocalizedMessage());
}
}