1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16package com.android.tts.compat;
17
18import android.speech.tts.SynthesisCallback;
19import android.speech.tts.SynthesisRequest;
20import android.util.Log;
21
22/**
23 * The SpeechSynthesis class provides a high-level api to create and play
24 * synthesized speech. This class is used internally to talk to a native
25 * TTS library that implements the interface defined in
26 * frameworks/base/include/tts/TtsEngine.h
27 *
28 */
29public class SynthProxy {
30
31    static {
32        System.loadLibrary("ttscompat");
33    }
34
35    private final static String TAG = "SynthProxy";
36
37    // Default parameters of a filter to be applied when using the Pico engine.
38    // Such a huge filter gain is justified by how much energy in the low frequencies is "wasted" at
39    // the output of the synthesis. The low shelving filter removes it, leaving room for
40    // amplification.
41    private final static float PICO_FILTER_GAIN = 5.0f; // linear gain
42    private final static float PICO_FILTER_LOWSHELF_ATTENUATION = -18.0f; // in dB
43    private final static float PICO_FILTER_TRANSITION_FREQ = 1100.0f;     // in Hz
44    private final static float PICO_FILTER_SHELF_SLOPE = 1.0f;            // Q
45
46    private int mJniData = 0;
47
48    /**
49     * Constructor; pass the location of the native TTS .so to use.
50     */
51    public SynthProxy(String nativeSoLib, String engineConfig) {
52        boolean applyFilter = shouldApplyAudioFilter(nativeSoLib);
53        Log.v(TAG, "About to load "+ nativeSoLib + ", applyFilter=" + applyFilter);
54        mJniData = native_setup(nativeSoLib, engineConfig);
55        if (mJniData == 0) {
56            throw new RuntimeException("Failed to load " + nativeSoLib);
57        }
58        native_setLowShelf(applyFilter, PICO_FILTER_GAIN, PICO_FILTER_LOWSHELF_ATTENUATION,
59                PICO_FILTER_TRANSITION_FREQ, PICO_FILTER_SHELF_SLOPE);
60    }
61
62    // HACK: Apply audio filter if the engine is pico
63    private boolean shouldApplyAudioFilter(String nativeSoLib) {
64        return nativeSoLib.toLowerCase().contains("pico");
65    }
66
67    /**
68     * Stops and clears the AudioTrack.
69     */
70    public int stop() {
71        return native_stop(mJniData);
72    }
73
74    /**
75     * Synchronous stop of the synthesizer. This method returns when the synth
76     * has completed the stop procedure and doesn't use any of the resources it
77     * was using while synthesizing.
78     *
79     * @return {@link android.speech.tts.TextToSpeech#SUCCESS} or
80     *         {@link android.speech.tts.TextToSpeech#ERROR}
81     */
82    public int stopSync() {
83        return native_stopSync(mJniData);
84    }
85
86    public int speak(SynthesisRequest request, SynthesisCallback callback) {
87        return native_speak(mJniData, request.getText(), callback);
88    }
89
90    /**
91     * Queries for language support.
92     * Return codes are defined in android.speech.tts.TextToSpeech
93     */
94    public int isLanguageAvailable(String language, String country, String variant) {
95        return native_isLanguageAvailable(mJniData, language, country, variant);
96    }
97
98    /**
99     * Updates the engine configuration.
100     */
101    public int setConfig(String engineConfig) {
102        return native_setProperty(mJniData, "engineConfig", engineConfig);
103    }
104
105    /**
106     * Sets the language.
107     */
108    public int setLanguage(String language, String country, String variant) {
109        return native_setLanguage(mJniData, language, country, variant);
110    }
111
112    /**
113     * Loads the language: it's not set, but prepared for use later.
114     */
115    public int loadLanguage(String language, String country, String variant) {
116        return native_loadLanguage(mJniData, language, country, variant);
117    }
118
119    /**
120     * Sets the speech rate.
121     */
122    public final int setSpeechRate(int speechRate) {
123        return native_setProperty(mJniData, "rate", String.valueOf(speechRate));
124    }
125
126    /**
127     * Sets the pitch of the synthesized voice.
128     */
129    public final int setPitch(int pitch) {
130        return native_setProperty(mJniData, "pitch", String.valueOf(pitch));
131    }
132
133    /**
134     * Returns the currently set language, country and variant information.
135     */
136    public String[] getLanguage() {
137        return native_getLanguage(mJniData);
138    }
139
140    /**
141     * Shuts down the native synthesizer.
142     */
143    public void shutdown() {
144        native_shutdown(mJniData);
145        mJniData = 0;
146    }
147
148    @Override
149    protected void finalize() {
150        if (mJniData != 0) {
151            Log.w(TAG, "SynthProxy finalized without being shutdown");
152            native_finalize(mJniData);
153            mJniData = 0;
154        }
155    }
156
157    private native final int native_setup(String nativeSoLib, String engineConfig);
158
159    private native final int native_setLowShelf(boolean applyFilter, float filterGain,
160            float attenuationInDb, float freqInHz, float slope);
161
162    private native final void native_finalize(int jniData);
163
164    private native final int native_stop(int jniData);
165
166    private native final int native_stopSync(int jniData);
167
168    private native final int native_speak(int jniData, String text, SynthesisCallback request);
169
170    private native final int  native_isLanguageAvailable(int jniData, String language,
171            String country, String variant);
172
173    private native final int native_setLanguage(int jniData, String language, String country,
174            String variant);
175
176    private native final int native_loadLanguage(int jniData, String language, String country,
177            String variant);
178
179    private native final int native_setProperty(int jniData, String name, String value);
180
181    private native final String[] native_getLanguage(int jniData);
182
183    private native final void native_shutdown(int jniData);
184
185}
186