blob: 8dc88db886f98306e3f0d34e91aa77d0e10e5b55 [file] [log] [blame]
Jean-Michel Trivi700ec652009-05-27 15:01:59 -07001/*
David 'Digit' Turner01f2f962010-05-20 16:57:34 -07002 * Copyright (C) 2009-2010 Google Inc.
Jean-Michel Trivi700ec652009-05-27 15:01:59 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Jean-Michel Trivi700ec652009-05-27 15:01:59 -070016
17#include <stdio.h>
18#include <unistd.h>
19
20#define LOG_TAG "SynthProxy"
21
22#include <utils/Log.h>
23#include <nativehelper/jni.h>
24#include <nativehelper/JNIHelp.h>
25#include <android_runtime/AndroidRuntime.h>
David 'Digit' Turner01f2f962010-05-20 16:57:34 -070026#include <android/tts.h>
Jean-Michel Trivi700ec652009-05-27 15:01:59 -070027#include <media/AudioTrack.h>
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -070028#include <math.h>
Jean-Michel Trivi700ec652009-05-27 15:01:59 -070029
30#include <dlfcn.h>
31
32#define DEFAULT_TTS_RATE 16000
33#define DEFAULT_TTS_FORMAT AudioSystem::PCM_16_BIT
34#define DEFAULT_TTS_NB_CHANNELS 1
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -070035#define DEFAULT_TTS_BUFFERSIZE 2048
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -070036// TODO use the TTS stream type when available
37#define DEFAULT_TTS_STREAM_TYPE AudioSystem::MUSIC
Jean-Michel Trivi700ec652009-05-27 15:01:59 -070038
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -070039// EQ + BOOST parameters
40#define FILTER_LOWSHELF_ATTENUATION -18.0f // in dB
41#define FILTER_TRANSITION_FREQ 1100.0f // in Hz
42#define FILTER_SHELF_SLOPE 1.0f // Q
Jean-Michel Trivi32184712009-09-01 10:22:51 -070043#define FILTER_GAIN 5.5f // linear gain
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -070044
Jean-Michel Trivi700ec652009-05-27 15:01:59 -070045#define USAGEMODE_PLAY_IMMEDIATELY 0
46#define USAGEMODE_WRITE_TO_FILE 1
47
Jean-Michel Trivie3c18902010-02-24 18:52:39 -080048#define SYNTHPLAYSTATE_IS_STOPPED 0
49#define SYNTHPLAYSTATE_IS_PLAYING 1
50
Jean-Michel Trivi700ec652009-05-27 15:01:59 -070051using namespace android;
52
53// ----------------------------------------------------------------------------
54struct fields_t {
55 jfieldID synthProxyFieldJniData;
56 jclass synthProxyClass;
57 jmethodID synthProxyMethodPost;
58};
59
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -070060// structure to hold the data that is used each time the TTS engine has synthesized more data
Jean-Michel Trivi700ec652009-05-27 15:01:59 -070061struct afterSynthData_t {
62 jint jniStorage;
63 int usageMode;
64 FILE* outputFile;
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -070065 AudioSystem::stream_type streamType;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -070066};
67
68// ----------------------------------------------------------------------------
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -070069// EQ data
70double amp;
71double w;
72double sinw;
73double cosw;
74double beta;
75double a0, a1, a2, b0, b1, b2;
76double m_fa, m_fb, m_fc, m_fd, m_fe;
77double x0; // x[n]
78double x1; // x[n-1]
79double x2; // x[n-2]
80double out0;// y[n]
81double out1;// y[n-1]
82double out2;// y[n-2]
83
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -080084static float fFilterLowshelfAttenuation = FILTER_LOWSHELF_ATTENUATION;
85static float fFilterTransitionFreq = FILTER_TRANSITION_FREQ;
86static float fFilterShelfSlope = FILTER_SHELF_SLOPE;
87static float fFilterGain = FILTER_GAIN;
88static bool bUseFilter = false;
89
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -070090void initializeEQ() {
91
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -080092 amp = float(pow(10.0, fFilterLowshelfAttenuation / 40.0));
93 w = 2.0 * M_PI * (fFilterTransitionFreq / DEFAULT_TTS_RATE);
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -070094 sinw = float(sin(w));
95 cosw = float(cos(w));
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -080096 beta = float(sqrt(amp)/fFilterShelfSlope);
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -070097
98 // initialize low-shelf parameters
99 b0 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) + (beta*sinw));
100 b1 = 2.0F * amp * ((amp-1.0F) - ((amp+1.0F)*cosw));
101 b2 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) - (beta*sinw));
102 a0 = (amp+1.0F) + ((amp-1.0F)*cosw) + (beta*sinw);
103 a1 = 2.0F * ((amp-1.0F) + ((amp+1.0F)*cosw));
104 a2 = -((amp+1.0F) + ((amp-1.0F)*cosw) - (beta*sinw));
105
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800106 m_fa = fFilterGain * b0/a0;
107 m_fb = fFilterGain * b1/a0;
108 m_fc = fFilterGain * b2/a0;
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -0700109 m_fd = a1/a0;
110 m_fe = a2/a0;
111}
112
113void initializeFilter() {
114 x0 = 0.0f;
115 x1 = 0.0f;
116 x2 = 0.0f;
117 out0 = 0.0f;
118 out1 = 0.0f;
119 out2 = 0.0f;
120}
121
122void applyFilter(int16_t* buffer, size_t sampleCount) {
123
124 for (size_t i=0 ; i<sampleCount ; i++) {
125
126 x0 = (double) buffer[i];
127
128 out0 = (m_fa*x0) + (m_fb*x1) + (m_fc*x2) + (m_fd*out1) + (m_fe*out2);
129
130 x2 = x1;
131 x1 = x0;
132
133 out2 = out1;
134 out1 = out0;
135
136 if (out0 > 32767.0f) {
137 buffer[i] = 32767;
138 } else if (out0 < -32768.0f) {
139 buffer[i] = -32768;
140 } else {
141 buffer[i] = (int16_t) out0;
142 }
143 }
144}
145
146
147// ----------------------------------------------------------------------------
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700148static fields_t javaTTSFields;
149
Jean-Michel Trivi5e11a6a2009-07-20 14:05:33 -0700150// TODO move to synth member once we have multiple simultaneous engines running
151static Mutex engineMutex;
152
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700153// ----------------------------------------------------------------------------
154class SynthProxyJniStorage {
155 public :
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700156 jobject tts_ref;
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700157 android_tts_engine_t* mEngine;
Jean-Michel Trivicee3bd42009-07-21 14:12:47 -0700158 void* mEngineLibHandle;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700159 AudioTrack* mAudioOut;
Jean-Michel Trivie3c18902010-02-24 18:52:39 -0800160 int8_t mPlayState;
161 Mutex mPlayLock;
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700162 AudioSystem::stream_type mStreamType;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700163 uint32_t mSampleRate;
Eric Laurenta553c252009-07-17 12:17:14 -0700164 uint32_t mAudFormat;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700165 int mNbChannels;
Charles Chen83e712a2009-06-05 13:58:33 -0700166 int8_t * mBuffer;
167 size_t mBufferSize;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700168
169 SynthProxyJniStorage() {
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700170 tts_ref = NULL;
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700171 mEngine = NULL;
Jean-Michel Trivicee3bd42009-07-21 14:12:47 -0700172 mEngineLibHandle = NULL;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700173 mAudioOut = NULL;
Jean-Michel Trivie3c18902010-02-24 18:52:39 -0800174 mPlayState = SYNTHPLAYSTATE_IS_STOPPED;
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700175 mStreamType = DEFAULT_TTS_STREAM_TYPE;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700176 mSampleRate = DEFAULT_TTS_RATE;
177 mAudFormat = DEFAULT_TTS_FORMAT;
178 mNbChannels = DEFAULT_TTS_NB_CHANNELS;
Charles Chen83e712a2009-06-05 13:58:33 -0700179 mBufferSize = DEFAULT_TTS_BUFFERSIZE;
180 mBuffer = new int8_t[mBufferSize];
Charles Chen4a3368f2009-07-14 17:11:44 -0700181 memset(mBuffer, 0, mBufferSize);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700182 }
183
184 ~SynthProxyJniStorage() {
Jean-Michel Trivicee3bd42009-07-21 14:12:47 -0700185 //LOGV("entering ~SynthProxyJniStorage()");
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700186 killAudio();
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700187 if (mEngine) {
188 mEngine->funcs->shutdown(mEngine);
189 mEngine = NULL;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700190 }
Jean-Michel Trivicee3bd42009-07-21 14:12:47 -0700191 if (mEngineLibHandle) {
192 //LOGE("~SynthProxyJniStorage(): before close library");
193 int res = dlclose(mEngineLibHandle);
194 LOGE_IF( res != 0, "~SynthProxyJniStorage(): dlclose returned %d", res);
195 }
Charles Chen83e712a2009-06-05 13:58:33 -0700196 delete mBuffer;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700197 }
198
199 void killAudio() {
200 if (mAudioOut) {
201 mAudioOut->stop();
202 delete mAudioOut;
203 mAudioOut = NULL;
204 }
205 }
206
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700207 void createAudioOut(AudioSystem::stream_type streamType, uint32_t rate,
208 AudioSystem::audio_format format, int channel) {
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700209 mSampleRate = rate;
210 mAudFormat = format;
211 mNbChannels = channel;
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700212 mStreamType = streamType;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700213
214 // retrieve system properties to ensure successful creation of the
215 // AudioTrack object for playback
216 int afSampleRate;
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700217 if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700218 afSampleRate = 44100;
219 }
220 int afFrameCount;
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700221 if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700222 afFrameCount = 2048;
223 }
224 uint32_t afLatency;
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700225 if (AudioSystem::getOutputLatency(&afLatency, mStreamType) != NO_ERROR) {
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700226 afLatency = 500;
227 }
228 uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
229 if (minBufCount < 2) minBufCount = 2;
230 int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
231
Jean-Michel Trivie3c18902010-02-24 18:52:39 -0800232 mPlayLock.lock();
Eric Laurenta553c252009-07-17 12:17:14 -0700233 mAudioOut = new AudioTrack(mStreamType, rate, format,
234 (channel == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700235 minFrameCount > 4096 ? minFrameCount : 4096,
236 0, 0, 0, 0); // not using an AudioTrack callback
237
238 if (mAudioOut->initCheck() != NO_ERROR) {
Jean-Michel Trivicee3bd42009-07-21 14:12:47 -0700239 LOGE("createAudioOut(): AudioTrack error");
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700240 delete mAudioOut;
241 mAudioOut = NULL;
242 } else {
Charles Chenb02ced72009-07-07 14:33:52 -0700243 //LOGI("AudioTrack OK");
Jean-Michel Trividdc63ad2010-01-08 14:06:21 -0800244 mAudioOut->setVolume(1.0f, 1.0f);
245 LOGV("AudioTrack ready");
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700246 }
Jean-Michel Trivie3c18902010-02-24 18:52:39 -0800247 mPlayLock.unlock();
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700248 }
249};
250
251
252// ----------------------------------------------------------------------------
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700253void prepAudioTrack(SynthProxyJniStorage* pJniData, AudioSystem::stream_type streamType,
254 uint32_t rate, AudioSystem::audio_format format, int channel) {
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700255 // Don't bother creating a new audiotrack object if the current
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700256 // object is already initialized with the same audio parameters.
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700257 if ( pJniData->mAudioOut &&
258 (rate == pJniData->mSampleRate) &&
259 (format == pJniData->mAudFormat) &&
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700260 (channel == pJniData->mNbChannels) &&
261 (streamType == pJniData->mStreamType) ){
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700262 return;
263 }
264 if (pJniData->mAudioOut){
265 pJniData->killAudio();
266 }
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700267 pJniData->createAudioOut(streamType, rate, format, channel);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700268}
269
270
271// ----------------------------------------------------------------------------
272/*
273 * Callback from TTS engine.
274 * Directly speaks using AudioTrack or write to file
275 */
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700276extern "C" android_tts_callback_status_t
277__ttsSynthDoneCB(void ** pUserdata, uint32_t rate,
278 android_tts_audio_format_t format, int channel,
279 int8_t **pWav, size_t *pBufferSize,
280 android_tts_synth_status_t status)
281{
Charles Chen2a8a2d72009-07-07 16:24:02 -0700282 //LOGV("ttsSynthDoneCallback: %d bytes", bufferSize);
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700283 AudioSystem::audio_format encoding;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700284
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700285 if (*pUserdata == NULL){
Charles Chen83e712a2009-06-05 13:58:33 -0700286 LOGE("userdata == NULL");
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700287 return ANDROID_TTS_CALLBACK_HALT;
Charles Chen83e712a2009-06-05 13:58:33 -0700288 }
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700289 switch (format) {
290 case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
291 encoding = AudioSystem::PCM_8_BIT;
292 break;
293 case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
294 encoding = AudioSystem::PCM_16_BIT;
295 break;
296 default:
297 LOGE("Can't play, bad format");
298 return ANDROID_TTS_CALLBACK_HALT;
299 }
300 afterSynthData_t* pForAfter = (afterSynthData_t*) *pUserdata;
Charles Chen83e712a2009-06-05 13:58:33 -0700301 SynthProxyJniStorage* pJniData = (SynthProxyJniStorage*)(pForAfter->jniStorage);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700302
303 if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY){
Charles Chenb02ced72009-07-07 14:33:52 -0700304 //LOGV("Direct speech");
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700305
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700306 if (*pWav == NULL) {
Charles Chen83e712a2009-06-05 13:58:33 -0700307 delete pForAfter;
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700308 pForAfter = NULL;
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -0700309 LOGV("Null: speech has completed");
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700310 return ANDROID_TTS_CALLBACK_HALT;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700311 }
312
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700313 if (*pBufferSize > 0) {
314 prepAudioTrack(pJniData, pForAfter->streamType, rate, encoding, channel);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700315 if (pJniData->mAudioOut) {
Jean-Michel Trivie3c18902010-02-24 18:52:39 -0800316 pJniData->mPlayLock.lock();
317 if(pJniData->mAudioOut->stopped()
318 && (pJniData->mPlayState == SYNTHPLAYSTATE_IS_PLAYING)) {
319 pJniData->mAudioOut->start();
320 }
321 pJniData->mPlayLock.unlock();
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800322 if (bUseFilter) {
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700323 applyFilter((int16_t*)*pWav, *pBufferSize/2);
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800324 }
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700325 pJniData->mAudioOut->write(*pWav, *pBufferSize);
326 memset(*pWav, 0, *pBufferSize);
Jean-Michel Trivi6a0e2932009-06-24 11:32:06 -0700327 //LOGV("AudioTrack wrote: %d bytes", bufferSize);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700328 } else {
Jean-Michel Trivi6a0e2932009-06-24 11:32:06 -0700329 LOGE("Can't play, null audiotrack");
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700330 delete pForAfter;
331 pForAfter = NULL;
332 return ANDROID_TTS_CALLBACK_HALT;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700333 }
334 }
335 } else if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) {
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -0700336 //LOGV("Save to file");
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700337 if (*pWav == NULL) {
Charles Chen83e712a2009-06-05 13:58:33 -0700338 delete pForAfter;
Jean-Michel Trivi6a0e2932009-06-24 11:32:06 -0700339 LOGV("Null: speech has completed");
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700340 return ANDROID_TTS_CALLBACK_HALT;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700341 }
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700342 if (*pBufferSize > 0){
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800343 if (bUseFilter) {
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700344 applyFilter((int16_t*)*pWav, *pBufferSize/2);
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800345 }
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700346 fwrite(*pWav, 1, *pBufferSize, pForAfter->outputFile);
347 memset(*pWav, 0, *pBufferSize);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700348 }
349 }
Jean-Michel Trivi6c24f242009-06-25 17:03:51 -0700350 // Future update:
351 // For sync points in the speech, call back into the SynthProxy class through the
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700352 // javaTTSFields.synthProxyMethodPost methode to notify
Jean-Michel Trivi6c24f242009-06-25 17:03:51 -0700353 // playback has completed if the synthesis is done or if a marker has been reached.
354
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700355 if (status == ANDROID_TTS_SYNTH_DONE) {
Jean-Michel Trivi6c24f242009-06-25 17:03:51 -0700356 // this struct was allocated in the original android_tts_SynthProxy_speak call,
357 // all processing matching this call is now done.
358 LOGV("Speech synthesis done.");
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700359 if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY) {
360 // only delete for direct playback. When writing to a file, we still have work to do
361 // in android_tts_SynthProxy_synthesizeToFile. The struct will be deleted there.
362 delete pForAfter;
363 pForAfter = NULL;
364 }
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700365 return ANDROID_TTS_CALLBACK_HALT;
Jean-Michel Trivi6c24f242009-06-25 17:03:51 -0700366 }
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700367
Charles Chen83e712a2009-06-05 13:58:33 -0700368 // we don't update the wav (output) parameter as we'll let the next callback
369 // write at the same location, we've consumed the data already, but we need
370 // to update bufferSize to let the TTS engine know how much it can write the
371 // next time it calls this function.
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700372 *pBufferSize = pJniData->mBufferSize;
Charles Chen83e712a2009-06-05 13:58:33 -0700373
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700374 return ANDROID_TTS_CALLBACK_CONTINUE;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700375}
376
377
378// ----------------------------------------------------------------------------
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800379static int
380android_tts_SynthProxy_setLowShelf(JNIEnv *env, jobject thiz, jboolean applyFilter,
381 jfloat filterGain, jfloat attenuationInDb, jfloat freqInHz, jfloat slope)
382{
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700383 int result = ANDROID_TTS_SUCCESS;
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800384
385 bUseFilter = applyFilter;
386 if (applyFilter) {
387 fFilterLowshelfAttenuation = attenuationInDb;
388 fFilterTransitionFreq = freqInHz;
389 fFilterShelfSlope = slope;
390 fFilterGain = filterGain;
391
392 if (fFilterShelfSlope != 0.0f) {
393 initializeEQ();
394 } else {
395 LOGE("Invalid slope, can't be null");
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700396 result = ANDROID_TTS_FAILURE;
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800397 }
398 }
399
400 return result;
401}
402
403// ----------------------------------------------------------------------------
404static int
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700405android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
Jean-Michel Trivi900e0d02010-03-18 11:07:45 -0700406 jobject weak_this, jstring nativeSoLib, jstring engConfig)
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700407{
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700408 int result = ANDROID_TTS_FAILURE;
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800409
410 bUseFilter = false;
411
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700412 SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();
413
414 prepAudioTrack(pJniStorage,
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700415 DEFAULT_TTS_STREAM_TYPE, DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700416
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800417 const char *nativeSoLibNativeString = env->GetStringUTFChars(nativeSoLib, 0);
Jean-Michel Trivi900e0d02010-03-18 11:07:45 -0700418 const char *engConfigString = env->GetStringUTFChars(engConfig, 0);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700419
420 void *engine_lib_handle = dlopen(nativeSoLibNativeString,
421 RTLD_NOW | RTLD_LOCAL);
Jean-Michel Trivicee3bd42009-07-21 14:12:47 -0700422 if (engine_lib_handle == NULL) {
423 LOGE("android_tts_SynthProxy_native_setup(): engine_lib_handle == NULL");
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700424 } else {
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700425 android_tts_engine_t * (*get_TtsEngine)() =
426 reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "android_getTtsEngine"));
Charles Chen83e712a2009-06-05 13:58:33 -0700427
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700428 // Support obsolete/legacy binary modules
429 if (get_TtsEngine == NULL) {
430 get_TtsEngine =
431 reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "getTtsEngine"));
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700432 }
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800433
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700434 pJniStorage->mEngine = (*get_TtsEngine)();
435 pJniStorage->mEngineLibHandle = engine_lib_handle;
436
437 android_tts_engine_t *engine = pJniStorage->mEngine;
438 if (engine) {
439 Mutex::Autolock l(engineMutex);
440 engine->funcs->init(
441 engine,
442 __ttsSynthDoneCB,
443 engConfigString);
444 }
445
446 result = ANDROID_TTS_SUCCESS;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700447 }
448
449 // we use a weak reference so the SynthProxy object can be garbage collected.
450 pJniStorage->tts_ref = env->NewGlobalRef(weak_this);
451
452 // save the JNI resources so we can use them (and free them) later
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800453 env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, (int)pJniStorage);
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -0700454
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700455 env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
Jean-Michel Trivi900e0d02010-03-18 11:07:45 -0700456 env->ReleaseStringUTFChars(engConfig, engConfigString);
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800457
458 return result;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700459}
460
461
462static void
463android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData)
464{
Jean-Michel Trivicee3bd42009-07-21 14:12:47 -0700465 //LOGV("entering android_tts_SynthProxy_finalize()");
466 if (jniData == 0) {
467 //LOGE("android_tts_SynthProxy_native_finalize(): invalid JNI data");
468 return;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700469 }
Jean-Michel Trivicee3bd42009-07-21 14:12:47 -0700470
471 Mutex::Autolock l(engineMutex);
472
473 SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
474 env->DeleteGlobalRef(pSynthData->tts_ref);
475 delete pSynthData;
476
477 env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, 0);
478}
479
480
481static void
482android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
483{
484 //LOGV("entering android_tts_SynthProxy_shutdown()");
485
486 // do everything a call to finalize would
487 android_tts_SynthProxy_native_finalize(env, thiz, jniData);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700488}
489
Jean-Michel Trivi679d7282009-06-16 15:36:28 -0700490
Jean-Michel Trivibee1c7e2009-06-29 15:55:05 -0700491static int
492android_tts_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData,
493 jstring language, jstring country, jstring variant)
494{
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700495 int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
Jean-Michel Trivibee1c7e2009-06-29 15:55:05 -0700496
497 if (jniData == 0) {
498 LOGE("android_tts_SynthProxy_isLanguageAvailable(): invalid JNI data");
499 return result;
500 }
501
502 SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
503 const char *langNativeString = env->GetStringUTFChars(language, 0);
504 const char *countryNativeString = env->GetStringUTFChars(country, 0);
505 const char *variantNativeString = env->GetStringUTFChars(variant, 0);
Charles Chen35b86c22009-07-06 10:51:48 -0700506
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700507 android_tts_engine_t *engine = pSynthData->mEngine;
508
509 if (engine) {
510 result = engine->funcs->isLanguageAvailable(engine,langNativeString,
Jean-Michel Trivibee1c7e2009-06-29 15:55:05 -0700511 countryNativeString, variantNativeString);
512 }
513 env->ReleaseStringUTFChars(language, langNativeString);
514 env->ReleaseStringUTFChars(country, countryNativeString);
515 env->ReleaseStringUTFChars(variant, variantNativeString);
516 return result;
517}
518
Jean-Michel Trivi900e0d02010-03-18 11:07:45 -0700519static int
520android_tts_SynthProxy_setConfig(JNIEnv *env, jobject thiz, jint jniData, jstring engineConfig)
521{
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700522 int result = ANDROID_TTS_FAILURE;
Jean-Michel Trivi900e0d02010-03-18 11:07:45 -0700523
524 if (jniData == 0) {
525 LOGE("android_tts_SynthProxy_setConfig(): invalid JNI data");
526 return result;
527 }
528
529 Mutex::Autolock l(engineMutex);
530
531 SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
532 const char *engineConfigNativeString = env->GetStringUTFChars(engineConfig, 0);
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700533 android_tts_engine_t *engine = pSynthData->mEngine;
Jean-Michel Trivi900e0d02010-03-18 11:07:45 -0700534
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700535 if (engine) {
536 result = engine->funcs->setProperty(engine,ANDROID_TTS_ENGINE_PROPERTY_CONFIG,
Jean-Michel Trivi900e0d02010-03-18 11:07:45 -0700537 engineConfigNativeString, strlen(engineConfigNativeString));
538 }
539 env->ReleaseStringUTFChars(engineConfig, engineConfigNativeString);
540
541 return result;
542}
Jean-Michel Trivibee1c7e2009-06-29 15:55:05 -0700543
Charles Chen35b86c22009-07-06 10:51:48 -0700544static int
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700545android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
Jean-Michel Trivi679d7282009-06-16 15:36:28 -0700546 jstring language, jstring country, jstring variant)
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700547{
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700548 int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
Charles Chen35b86c22009-07-06 10:51:48 -0700549
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700550 if (jniData == 0) {
551 LOGE("android_tts_SynthProxy_setLanguage(): invalid JNI data");
Charles Chen35b86c22009-07-06 10:51:48 -0700552 return result;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700553 }
554
Jean-Michel Trivi5e11a6a2009-07-20 14:05:33 -0700555 Mutex::Autolock l(engineMutex);
556
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700557 SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
558 const char *langNativeString = env->GetStringUTFChars(language, 0);
Jean-Michel Trivi679d7282009-06-16 15:36:28 -0700559 const char *countryNativeString = env->GetStringUTFChars(country, 0);
560 const char *variantNativeString = env->GetStringUTFChars(variant, 0);
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700561 android_tts_engine_t *engine = pSynthData->mEngine;
Charles Chen35b86c22009-07-06 10:51:48 -0700562
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700563 if (engine) {
564 result = engine->funcs->setLanguage(engine, langNativeString,
Charles Chen35b86c22009-07-06 10:51:48 -0700565 countryNativeString, variantNativeString);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700566 }
567 env->ReleaseStringUTFChars(language, langNativeString);
Jean-Michel Trivid6d03e02009-06-25 18:37:55 -0700568 env->ReleaseStringUTFChars(country, countryNativeString);
569 env->ReleaseStringUTFChars(variant, variantNativeString);
Charles Chen35b86c22009-07-06 10:51:48 -0700570 return result;
Jean-Michel Trivid6d03e02009-06-25 18:37:55 -0700571}
572
573
Charles Chen35b86c22009-07-06 10:51:48 -0700574static int
Jean-Michel Trivid6d03e02009-06-25 18:37:55 -0700575android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData,
576 jstring language, jstring country, jstring variant)
577{
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700578 int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
Charles Chen35b86c22009-07-06 10:51:48 -0700579
Jean-Michel Trivid6d03e02009-06-25 18:37:55 -0700580 if (jniData == 0) {
581 LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data");
Charles Chen35b86c22009-07-06 10:51:48 -0700582 return result;
Jean-Michel Trivid6d03e02009-06-25 18:37:55 -0700583 }
584
585 SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
586 const char *langNativeString = env->GetStringUTFChars(language, 0);
587 const char *countryNativeString = env->GetStringUTFChars(country, 0);
588 const char *variantNativeString = env->GetStringUTFChars(variant, 0);
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700589 android_tts_engine_t *engine = pSynthData->mEngine;
Charles Chen35b86c22009-07-06 10:51:48 -0700590
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700591 if (engine) {
592 result = engine->funcs->loadLanguage(engine, langNativeString,
Charles Chen35b86c22009-07-06 10:51:48 -0700593 countryNativeString, variantNativeString);
Jean-Michel Trivid6d03e02009-06-25 18:37:55 -0700594 }
595 env->ReleaseStringUTFChars(language, langNativeString);
596 env->ReleaseStringUTFChars(country, countryNativeString);
597 env->ReleaseStringUTFChars(variant, variantNativeString);
Charles Chen35b86c22009-07-06 10:51:48 -0700598
599 return result;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700600}
601
602
Charles Chen35b86c22009-07-06 10:51:48 -0700603static int
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700604android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData,
Jean-Michel Trivi2ea53492009-06-23 13:44:40 -0700605 jint speechRate)
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700606{
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700607 int result = ANDROID_TTS_FAILURE;
Charles Chen35b86c22009-07-06 10:51:48 -0700608
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700609 if (jniData == 0) {
610 LOGE("android_tts_SynthProxy_setSpeechRate(): invalid JNI data");
Charles Chen35b86c22009-07-06 10:51:48 -0700611 return result;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700612 }
613
Kenny Root1ead4f02010-02-18 10:35:17 -0800614 int bufSize = 12;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700615 char buffer [bufSize];
616 sprintf(buffer, "%d", speechRate);
617
Jean-Michel Trivi5e11a6a2009-07-20 14:05:33 -0700618 Mutex::Autolock l(engineMutex);
619
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700620 SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
621 LOGI("setting speech rate to %d", speechRate);
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700622 android_tts_engine_t *engine = pSynthData->mEngine;
Charles Chen35b86c22009-07-06 10:51:48 -0700623
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700624 if (engine) {
625 result = engine->funcs->setProperty(engine, "rate", buffer, bufSize);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700626 }
Charles Chen35b86c22009-07-06 10:51:48 -0700627
628 return result;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700629}
630
631
Charles Chen35b86c22009-07-06 10:51:48 -0700632static int
Jean-Michel Trivi2ea53492009-06-23 13:44:40 -0700633android_tts_SynthProxy_setPitch(JNIEnv *env, jobject thiz, jint jniData,
634 jint pitch)
635{
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700636 int result = ANDROID_TTS_FAILURE;
Charles Chen35b86c22009-07-06 10:51:48 -0700637
Jean-Michel Trivi2ea53492009-06-23 13:44:40 -0700638 if (jniData == 0) {
639 LOGE("android_tts_SynthProxy_setPitch(): invalid JNI data");
Charles Chen35b86c22009-07-06 10:51:48 -0700640 return result;
Jean-Michel Trivi2ea53492009-06-23 13:44:40 -0700641 }
642
Jean-Michel Trivi5e11a6a2009-07-20 14:05:33 -0700643 Mutex::Autolock l(engineMutex);
644
Kenny Root1ead4f02010-02-18 10:35:17 -0800645 int bufSize = 12;
Jean-Michel Trivi2ea53492009-06-23 13:44:40 -0700646 char buffer [bufSize];
647 sprintf(buffer, "%d", pitch);
648
649 SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
650 LOGI("setting pitch to %d", pitch);
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700651 android_tts_engine_t *engine = pSynthData->mEngine;
Charles Chen35b86c22009-07-06 10:51:48 -0700652
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700653 if (engine) {
654 result = engine->funcs->setProperty(engine, "pitch", buffer, bufSize);
Jean-Michel Trivi2ea53492009-06-23 13:44:40 -0700655 }
Charles Chen35b86c22009-07-06 10:51:48 -0700656
657 return result;
Jean-Michel Trivi2ea53492009-06-23 13:44:40 -0700658}
659
660
Charles Chen35b86c22009-07-06 10:51:48 -0700661static int
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700662android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
663 jstring textJavaString, jstring filenameJavaString)
664{
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700665 int result = ANDROID_TTS_FAILURE;
Charles Chen35b86c22009-07-06 10:51:48 -0700666
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700667 if (jniData == 0) {
668 LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid JNI data");
Charles Chen35b86c22009-07-06 10:51:48 -0700669 return result;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700670 }
671
672 SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700673 if (!pSynthData->mEngine) {
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700674 LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle");
Charles Chen35b86c22009-07-06 10:51:48 -0700675 return result;
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700676 }
677
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -0700678 initializeFilter();
679
Jean-Michel Trivi5e11a6a2009-07-20 14:05:33 -0700680 Mutex::Autolock l(engineMutex);
681
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700682 // Retrieve audio parameters before writing the file header
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700683 AudioSystem::audio_format encoding;
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700684 uint32_t rate = DEFAULT_TTS_RATE;
685 int channels = DEFAULT_TTS_NB_CHANNELS;
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700686 android_tts_engine_t *engine = pSynthData->mEngine;
687 android_tts_audio_format_t format = ANDROID_TTS_AUDIO_FORMAT_DEFAULT;
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700688
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700689 engine->funcs->setAudioFormat(engine, &format, &rate, &channels);
690
691 switch (format) {
692 case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
693 encoding = AudioSystem::PCM_16_BIT;
694 break;
695 case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
696 encoding = AudioSystem::PCM_8_BIT;
697 break;
698 default:
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700699 LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format");
Charles Chen35b86c22009-07-06 10:51:48 -0700700 return result;
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700701 }
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700702
703 const char *filenameNativeString =
704 env->GetStringUTFChars(filenameJavaString, 0);
705 const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
706
707 afterSynthData_t* pForAfter = new (afterSynthData_t);
708 pForAfter->jniStorage = jniData;
709 pForAfter->usageMode = USAGEMODE_WRITE_TO_FILE;
710
711 pForAfter->outputFile = fopen(filenameNativeString, "wb");
712
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700713 if (pForAfter->outputFile == NULL) {
714 LOGE("android_tts_SynthProxy_synthesizeToFile(): error creating output file");
715 delete pForAfter;
Charles Chen35b86c22009-07-06 10:51:48 -0700716 return result;
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700717 }
718
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700719 // Write 44 blank bytes for WAV header, then come back and fill them in
720 // after we've written the audio data
721 char header[44];
722 fwrite(header, 1, 44, pForAfter->outputFile);
723
724 unsigned int unique_identifier;
725
Charles Chen4a3368f2009-07-14 17:11:44 -0700726 memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700727
728 result = engine->funcs->synthesizeText(engine, textNativeString,
Charles Chen35b86c22009-07-06 10:51:48 -0700729 pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700730
731 long filelen = ftell(pForAfter->outputFile);
732
733 int samples = (((int)filelen) - 44) / 2;
734 header[0] = 'R';
735 header[1] = 'I';
736 header[2] = 'F';
737 header[3] = 'F';
738 ((uint32_t *)(&header[4]))[0] = filelen - 8;
739 header[8] = 'W';
740 header[9] = 'A';
741 header[10] = 'V';
742 header[11] = 'E';
743
744 header[12] = 'f';
745 header[13] = 'm';
746 header[14] = 't';
747 header[15] = ' ';
748
749 ((uint32_t *)(&header[16]))[0] = 16; // size of fmt
750
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700751 int sampleSizeInByte = (encoding == AudioSystem::PCM_16_BIT ? 2 : 1);
752
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700753 ((unsigned short *)(&header[20]))[0] = 1; // format
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700754 ((unsigned short *)(&header[22]))[0] = channels; // channels
755 ((uint32_t *)(&header[24]))[0] = rate; // samplerate
756 ((uint32_t *)(&header[28]))[0] = rate * sampleSizeInByte * channels;// byterate
757 ((unsigned short *)(&header[32]))[0] = sampleSizeInByte * channels; // block align
758 ((unsigned short *)(&header[34]))[0] = sampleSizeInByte * 8; // bits per sample
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700759
760 header[36] = 'd';
761 header[37] = 'a';
762 header[38] = 't';
763 header[39] = 'a';
764
765 ((uint32_t *)(&header[40]))[0] = samples * 2; // size of data
766
767 // Skip back to the beginning and rewrite the header
768 fseek(pForAfter->outputFile, 0, SEEK_SET);
769 fwrite(header, 1, 44, pForAfter->outputFile);
770
771 fflush(pForAfter->outputFile);
772 fclose(pForAfter->outputFile);
773
Jean-Michel Trivif07d8242009-06-30 09:36:08 -0700774 delete pForAfter;
775 pForAfter = NULL;
776
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700777 env->ReleaseStringUTFChars(textJavaString, textNativeString);
778 env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
Charles Chen35b86c22009-07-06 10:51:48 -0700779
780 return result;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700781}
782
783
Charles Chen35b86c22009-07-06 10:51:48 -0700784static int
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700785android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700786 jstring textJavaString, jint javaStreamType)
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700787{
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700788 int result = ANDROID_TTS_FAILURE;
Charles Chen35b86c22009-07-06 10:51:48 -0700789
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700790 if (jniData == 0) {
791 LOGE("android_tts_SynthProxy_speak(): invalid JNI data");
Charles Chen35b86c22009-07-06 10:51:48 -0700792 return result;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700793 }
794
Jean-Michel Trivi4fb7d882009-08-13 19:05:45 -0700795 initializeFilter();
796
Jean-Michel Trivi5e11a6a2009-07-20 14:05:33 -0700797 Mutex::Autolock l(engineMutex);
798
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700799 SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
800
Jean-Michel Trivie3c18902010-02-24 18:52:39 -0800801 pSynthData->mPlayLock.lock();
802 pSynthData->mPlayState = SYNTHPLAYSTATE_IS_PLAYING;
803 pSynthData->mPlayLock.unlock();
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700804
805 afterSynthData_t* pForAfter = new (afterSynthData_t);
806 pForAfter->jniStorage = jniData;
807 pForAfter->usageMode = USAGEMODE_PLAY_IMMEDIATELY;
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700808 pForAfter->streamType = (AudioSystem::stream_type) javaStreamType;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700809
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700810 if (pSynthData->mEngine) {
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700811 const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
Charles Chen4a3368f2009-07-14 17:11:44 -0700812 memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700813 android_tts_engine_t *engine = pSynthData->mEngine;
814
815 result = engine->funcs->synthesizeText(engine, textNativeString,
Charles Chen35b86c22009-07-06 10:51:48 -0700816 pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700817 env->ReleaseStringUTFChars(textJavaString, textNativeString);
818 }
Charles Chen35b86c22009-07-06 10:51:48 -0700819
820 return result;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700821}
822
823
Charles Chen35b86c22009-07-06 10:51:48 -0700824static int
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700825android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
826{
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700827 int result = ANDROID_TTS_FAILURE;
Charles Chen35b86c22009-07-06 10:51:48 -0700828
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700829 if (jniData == 0) {
830 LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
Charles Chen35b86c22009-07-06 10:51:48 -0700831 return result;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700832 }
833
834 SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
835
Jean-Michel Trivie3c18902010-02-24 18:52:39 -0800836 pSynthData->mPlayLock.lock();
837 pSynthData->mPlayState = SYNTHPLAYSTATE_IS_STOPPED;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700838 if (pSynthData->mAudioOut) {
839 pSynthData->mAudioOut->stop();
840 }
Jean-Michel Trivie3c18902010-02-24 18:52:39 -0800841 pSynthData->mPlayLock.unlock();
842
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700843 android_tts_engine_t *engine = pSynthData->mEngine;
844 if (engine) {
845 result = engine->funcs->stop(engine);
Charles Chen4a3368f2009-07-14 17:11:44 -0700846 }
Charles Chen35b86c22009-07-06 10:51:48 -0700847
848 return result;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700849}
850
851
Jean-Michel Trivi09f8db72009-08-31 10:41:55 -0700852static int
853android_tts_SynthProxy_stopSync(JNIEnv *env, jobject thiz, jint jniData)
854{
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700855 int result = ANDROID_TTS_FAILURE;
Jean-Michel Trivi09f8db72009-08-31 10:41:55 -0700856
857 if (jniData == 0) {
858 LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
859 return result;
860 }
861
862 // perform a regular stop
863 result = android_tts_SynthProxy_stop(env, thiz, jniData);
864 // but wait on the engine having released the engine mutex which protects
865 // the synthesizer resources.
866 engineMutex.lock();
867 engineMutex.unlock();
868
869 return result;
870}
871
872
Jean-Michel Trivibee1c7e2009-06-29 15:55:05 -0700873static jobjectArray
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700874android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
875{
876 if (jniData == 0) {
877 LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data");
Jean-Michel Trivibee1c7e2009-06-29 15:55:05 -0700878 return NULL;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700879 }
880
881 SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
Jean-Michel Trivibee1c7e2009-06-29 15:55:05 -0700882
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700883 if (pSynthData->mEngine) {
Jean-Michel Trivibee1c7e2009-06-29 15:55:05 -0700884 size_t bufSize = 100;
885 char lang[bufSize];
886 char country[bufSize];
887 char variant[bufSize];
888 memset(lang, 0, bufSize);
889 memset(country, 0, bufSize);
890 memset(variant, 0, bufSize);
891 jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3,
892 env->FindClass("java/lang/String"), env->NewStringUTF(""));
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700893
894 android_tts_engine_t *engine = pSynthData->mEngine;
895 engine->funcs->getLanguage(engine, lang, country, variant);
Jean-Michel Trivibee1c7e2009-06-29 15:55:05 -0700896 env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang));
897 env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country));
898 env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant));
899 return retLocale;
900 } else {
901 return NULL;
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700902 }
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700903}
904
Jean-Michel Trivi679d7282009-06-16 15:36:28 -0700905
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700906JNIEXPORT int JNICALL
907android_tts_SynthProxy_getRate(JNIEnv *env, jobject thiz, jint jniData)
908{
909 if (jniData == 0) {
910 LOGE("android_tts_SynthProxy_getRate(): invalid JNI data");
911 return 0;
912 }
913
914 SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
915 size_t bufSize = 100;
916
917 char buf[bufSize];
918 memset(buf, 0, bufSize);
919 // TODO check return codes
David 'Digit' Turner01f2f962010-05-20 16:57:34 -0700920 android_tts_engine_t *engine = pSynthData->mEngine;
921 if (engine) {
922 engine->funcs->getProperty(engine,"rate", buf, &bufSize);
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700923 }
924 return atoi(buf);
925}
926
927// Dalvik VM type signatures
928static JNINativeMethod gMethods[] = {
929 { "native_stop",
Charles Chen35b86c22009-07-06 10:51:48 -0700930 "(I)I",
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700931 (void*)android_tts_SynthProxy_stop
932 },
Jean-Michel Trivi09f8db72009-08-31 10:41:55 -0700933 { "native_stopSync",
934 "(I)I",
935 (void*)android_tts_SynthProxy_stopSync
936 },
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700937 { "native_speak",
Jean-Michel Trivi9440bce2009-07-13 10:12:37 -0700938 "(ILjava/lang/String;I)I",
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700939 (void*)android_tts_SynthProxy_speak
940 },
941 { "native_synthesizeToFile",
Charles Chen35b86c22009-07-06 10:51:48 -0700942 "(ILjava/lang/String;Ljava/lang/String;)I",
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700943 (void*)android_tts_SynthProxy_synthesizeToFile
944 },
Jean-Michel Trivibee1c7e2009-06-29 15:55:05 -0700945 { "native_isLanguageAvailable",
946 "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
947 (void*)android_tts_SynthProxy_isLanguageAvailable
948 },
Jean-Michel Trivi900e0d02010-03-18 11:07:45 -0700949 { "native_setConfig",
950 "(ILjava/lang/String;)I",
951 (void*)android_tts_SynthProxy_setConfig
952 },
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700953 { "native_setLanguage",
Charles Chen35b86c22009-07-06 10:51:48 -0700954 "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700955 (void*)android_tts_SynthProxy_setLanguage
956 },
Jean-Michel Trivid6d03e02009-06-25 18:37:55 -0700957 { "native_loadLanguage",
Charles Chen35b86c22009-07-06 10:51:48 -0700958 "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
Jean-Michel Trivid6d03e02009-06-25 18:37:55 -0700959 (void*)android_tts_SynthProxy_loadLanguage
960 },
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700961 { "native_setSpeechRate",
Charles Chen35b86c22009-07-06 10:51:48 -0700962 "(II)I",
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700963 (void*)android_tts_SynthProxy_setSpeechRate
964 },
Jean-Michel Trivi2ea53492009-06-23 13:44:40 -0700965 { "native_setPitch",
Charles Chen35b86c22009-07-06 10:51:48 -0700966 "(II)I",
Jean-Michel Trivi2ea53492009-06-23 13:44:40 -0700967 (void*)android_tts_SynthProxy_setPitch
968 },
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700969 { "native_getLanguage",
Jean-Michel Trivibee1c7e2009-06-29 15:55:05 -0700970 "(I)[Ljava/lang/String;",
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700971 (void*)android_tts_SynthProxy_getLanguage
972 },
973 { "native_getRate",
974 "(I)I",
975 (void*)android_tts_SynthProxy_getRate
976 },
977 { "native_shutdown",
978 "(I)V",
979 (void*)android_tts_SynthProxy_shutdown
980 },
981 { "native_setup",
Jean-Michel Trivi900e0d02010-03-18 11:07:45 -0700982 "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)I",
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700983 (void*)android_tts_SynthProxy_native_setup
984 },
Jean-Michel Trivi0320f8b2010-01-11 14:04:50 -0800985 { "native_setLowShelf",
986 "(ZFFFF)I",
987 (void*)android_tts_SynthProxy_setLowShelf
988 },
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700989 { "native_finalize",
990 "(I)V",
991 (void*)android_tts_SynthProxy_native_finalize
992 }
993};
994
995#define SP_JNIDATA_FIELD_NAME "mJniData"
996#define SP_POSTSPEECHSYNTHESIZED_METHOD_NAME "postNativeSpeechSynthesizedInJava"
997
Jean-Michel Trivi700ec652009-05-27 15:01:59 -0700998static const char* const kClassPathName = "android/tts/SynthProxy";
999
1000jint JNI_OnLoad(JavaVM* vm, void* reserved)
1001{
1002 JNIEnv* env = NULL;
1003 jint result = -1;
1004 jclass clazz;
1005
1006 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
1007 LOGE("ERROR: GetEnv failed\n");
1008 goto bail;
1009 }
1010 assert(env != NULL);
1011
1012 clazz = env->FindClass(kClassPathName);
1013 if (clazz == NULL) {
1014 LOGE("Can't find %s", kClassPathName);
1015 goto bail;
1016 }
1017
1018 javaTTSFields.synthProxyClass = clazz;
1019 javaTTSFields.synthProxyFieldJniData = NULL;
1020 javaTTSFields.synthProxyMethodPost = NULL;
1021
1022 javaTTSFields.synthProxyFieldJniData = env->GetFieldID(clazz,
1023 SP_JNIDATA_FIELD_NAME, "I");
1024 if (javaTTSFields.synthProxyFieldJniData == NULL) {
1025 LOGE("Can't find %s.%s field", kClassPathName, SP_JNIDATA_FIELD_NAME);
1026 goto bail;
1027 }
1028
1029 javaTTSFields.synthProxyMethodPost = env->GetStaticMethodID(clazz,
1030 SP_POSTSPEECHSYNTHESIZED_METHOD_NAME, "(Ljava/lang/Object;II)V");
1031 if (javaTTSFields.synthProxyMethodPost == NULL) {
1032 LOGE("Can't find %s.%s method", kClassPathName, SP_POSTSPEECHSYNTHESIZED_METHOD_NAME);
1033 goto bail;
1034 }
1035
1036 if (jniRegisterNativeMethods(
1037 env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
1038 goto bail;
1039
1040 /* success -- return valid version number */
1041 result = JNI_VERSION_1_4;
1042
1043 bail:
1044 return result;
1045}