Merge change 6925 into donut

* changes:
  Propagate optional audio stream type to the creation of the audio resources to enable the playback of TTS content on various stream types.
This commit is contained in:
Android (Google) Code Review
2009-07-13 13:36:37 -07:00
3 changed files with 67 additions and 26 deletions

View File

@@ -33,6 +33,8 @@
#define DEFAULT_TTS_FORMAT AudioSystem::PCM_16_BIT #define DEFAULT_TTS_FORMAT AudioSystem::PCM_16_BIT
#define DEFAULT_TTS_NB_CHANNELS 1 #define DEFAULT_TTS_NB_CHANNELS 1
#define DEFAULT_TTS_BUFFERSIZE 1024 #define DEFAULT_TTS_BUFFERSIZE 1024
// TODO use the TTS stream type when available
#define DEFAULT_TTS_STREAM_TYPE AudioSystem::MUSIC
#define USAGEMODE_PLAY_IMMEDIATELY 0 #define USAGEMODE_PLAY_IMMEDIATELY 0
#define USAGEMODE_WRITE_TO_FILE 1 #define USAGEMODE_WRITE_TO_FILE 1
@@ -46,10 +48,12 @@ struct fields_t {
jmethodID synthProxyMethodPost; jmethodID synthProxyMethodPost;
}; };
// structure to hold the data that is used each time the TTS engine has synthesized more data
struct afterSynthData_t { struct afterSynthData_t {
jint jniStorage; jint jniStorage;
int usageMode; int usageMode;
FILE* outputFile; FILE* outputFile;
AudioSystem::stream_type streamType;
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -62,6 +66,7 @@ class SynthProxyJniStorage {
jobject tts_ref; jobject tts_ref;
TtsEngine* mNativeSynthInterface; TtsEngine* mNativeSynthInterface;
AudioTrack* mAudioOut; AudioTrack* mAudioOut;
AudioSystem::stream_type mStreamType;
uint32_t mSampleRate; uint32_t mSampleRate;
AudioSystem::audio_format mAudFormat; AudioSystem::audio_format mAudFormat;
int mNbChannels; int mNbChannels;
@@ -73,6 +78,7 @@ class SynthProxyJniStorage {
tts_ref = NULL; tts_ref = NULL;
mNativeSynthInterface = NULL; mNativeSynthInterface = NULL;
mAudioOut = NULL; mAudioOut = NULL;
mStreamType = DEFAULT_TTS_STREAM_TYPE;
mSampleRate = DEFAULT_TTS_RATE; mSampleRate = DEFAULT_TTS_RATE;
mAudFormat = DEFAULT_TTS_FORMAT; mAudFormat = DEFAULT_TTS_FORMAT;
mNbChannels = DEFAULT_TTS_NB_CHANNELS; mNbChannels = DEFAULT_TTS_NB_CHANNELS;
@@ -97,34 +103,33 @@ class SynthProxyJniStorage {
} }
} }
void createAudioOut(uint32_t rate, AudioSystem::audio_format format, void createAudioOut(AudioSystem::stream_type streamType, uint32_t rate,
int channel) { AudioSystem::audio_format format, int channel) {
mSampleRate = rate; mSampleRate = rate;
mAudFormat = format; mAudFormat = format;
mNbChannels = channel; mNbChannels = channel;
// TODO use the TTS stream type mStreamType = streamType;
int streamType = AudioSystem::MUSIC;
// retrieve system properties to ensure successful creation of the // retrieve system properties to ensure successful creation of the
// AudioTrack object for playback // AudioTrack object for playback
int afSampleRate; int afSampleRate;
if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
afSampleRate = 44100; afSampleRate = 44100;
} }
int afFrameCount; int afFrameCount;
if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
afFrameCount = 2048; afFrameCount = 2048;
} }
uint32_t afLatency; uint32_t afLatency;
if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) { if (AudioSystem::getOutputLatency(&afLatency, mStreamType) != NO_ERROR) {
afLatency = 500; afLatency = 500;
} }
uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate); uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
if (minBufCount < 2) minBufCount = 2; if (minBufCount < 2) minBufCount = 2;
int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate; int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
mAudioOut = new AudioTrack(streamType, rate, format, channel, mAudioOut = new AudioTrack(mStreamType, rate, format, channel,
minFrameCount > 4096 ? minFrameCount : 4096, minFrameCount > 4096 ? minFrameCount : 4096,
0, 0, 0, 0); // not using an AudioTrack callback 0, 0, 0, 0); // not using an AudioTrack callback
@@ -142,21 +147,21 @@ class SynthProxyJniStorage {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void prepAudioTrack(SynthProxyJniStorage* pJniData, void prepAudioTrack(SynthProxyJniStorage* pJniData, AudioSystem::stream_type streamType,
uint32_t rate, AudioSystem::audio_format format, int channel) uint32_t rate, AudioSystem::audio_format format, int channel) {
{
// Don't bother creating a new audiotrack object if the current // Don't bother creating a new audiotrack object if the current
// object is already set. // object is already initialized with the same audio parameters.
if ( pJniData->mAudioOut && if ( pJniData->mAudioOut &&
(rate == pJniData->mSampleRate) && (rate == pJniData->mSampleRate) &&
(format == pJniData->mAudFormat) && (format == pJniData->mAudFormat) &&
(channel == pJniData->mNbChannels) ){ (channel == pJniData->mNbChannels) &&
(streamType == pJniData->mStreamType) ){
return; return;
} }
if (pJniData->mAudioOut){ if (pJniData->mAudioOut){
pJniData->killAudio(); pJniData->killAudio();
} }
pJniData->createAudioOut(rate, format, channel); pJniData->createAudioOut(streamType, rate, format, channel);
} }
@@ -186,7 +191,7 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
} }
if (bufferSize > 0) { if (bufferSize > 0) {
prepAudioTrack(pJniData, rate, format, channel); prepAudioTrack(pJniData, pForAfter->streamType, rate, format, channel);
if (pJniData->mAudioOut) { if (pJniData->mAudioOut) {
pJniData->mAudioOut->write(wav, bufferSize); pJniData->mAudioOut->write(wav, bufferSize);
//LOGV("AudioTrack wrote: %d bytes", bufferSize); //LOGV("AudioTrack wrote: %d bytes", bufferSize);
@@ -241,7 +246,7 @@ android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage(); SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();
prepAudioTrack(pJniStorage, prepAudioTrack(pJniStorage,
DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS); DEFAULT_TTS_STREAM_TYPE, DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
const char *nativeSoLibNativeString = const char *nativeSoLibNativeString =
env->GetStringUTFChars(nativeSoLib, 0); env->GetStringUTFChars(nativeSoLib, 0);
@@ -526,7 +531,7 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
static int static int
android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData, android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
jstring textJavaString) jstring textJavaString, jint javaStreamType)
{ {
int result = TTS_FAILURE; int result = TTS_FAILURE;
@@ -545,6 +550,7 @@ android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
afterSynthData_t* pForAfter = new (afterSynthData_t); afterSynthData_t* pForAfter = new (afterSynthData_t);
pForAfter->jniStorage = jniData; pForAfter->jniStorage = jniData;
pForAfter->usageMode = USAGEMODE_PLAY_IMMEDIATELY; pForAfter->usageMode = USAGEMODE_PLAY_IMMEDIATELY;
pForAfter->streamType = (AudioSystem::stream_type) javaStreamType;
if (pSynthData->mNativeSynthInterface) { if (pSynthData->mNativeSynthInterface) {
const char *textNativeString = env->GetStringUTFChars(textJavaString, 0); const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
@@ -672,7 +678,7 @@ static JNINativeMethod gMethods[] = {
(void*)android_tts_SynthProxy_stop (void*)android_tts_SynthProxy_stop
}, },
{ "native_speak", { "native_speak",
"(ILjava/lang/String;)I", "(ILjava/lang/String;I)I",
(void*)android_tts_SynthProxy_speak (void*)android_tts_SynthProxy_speak
}, },
{ "native_synthesizeToFile", { "native_synthesizeToFile",

View File

@@ -15,6 +15,8 @@
*/ */
package android.tts; package android.tts;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.util.Log; import android.util.Log;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@@ -52,8 +54,13 @@ public class SynthProxy {
/** /**
* Synthesize speech and speak it directly using AudioTrack. * Synthesize speech and speak it directly using AudioTrack.
*/ */
public int speak(String text) { public int speak(String text, int streamType) {
return native_speak(mJniData, text); if ((streamType > -1) && (streamType < AudioSystem.getNumStreamTypes())) {
return native_speak(mJniData, text, streamType);
} else {
Log.e("SynthProxy", "Trying to speak with invalid stream type " + streamType);
return native_speak(mJniData, text, AudioManager.STREAM_MUSIC);
}
} }
/** /**
@@ -156,7 +163,7 @@ public class SynthProxy {
private native final int native_stop(int jniData); private native final int native_stop(int jniData);
private native final int native_speak(int jniData, String text); private native final int native_speak(int jniData, String text, int streamType);
private native final int native_synthesizeToFile(int jniData, String text, String filename); private native final int native_synthesizeToFile(int jniData, String text, String filename);

View File

@@ -22,6 +22,7 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.media.AudioManager;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri; import android.net.Uri;
@@ -109,6 +110,8 @@ public class TtsService extends Service implements OnCompletionListener {
private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000; private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
private static final int MAX_FILENAME_LENGTH = 250; private static final int MAX_FILENAME_LENGTH = 250;
// TODO use the TTS stream type when available
private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC;
private static final String ACTION = "android.intent.action.START_TTS_SERVICE"; private static final String ACTION = "android.intent.action.START_TTS_SERVICE";
private static final String CATEGORY = "android.intent.category.TTS"; private static final String CATEGORY = "android.intent.category.TTS";
@@ -450,14 +453,15 @@ public class TtsService extends Service implements OnCompletionListener {
synth.start(); synth.start();
return; return;
} }
int streamType = DEFAULT_STREAM_TYPE;
if (params != null){ if (params != null){
String language = ""; String language = "";
String country = ""; String country = "";
String variant = ""; String variant = "";
for (int i = 0; i < params.size() - 1; i = i + 2){ for (int i = 0; i < params.size() - 1; i = i + 2){
String param = params.get(i); String param = params.get(i);
if (param != null){ if (param != null) {
if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){ if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)) {
setSpeechRate("", Integer.parseInt(params.get(i+1))); setSpeechRate("", Integer.parseInt(params.get(i+1)));
} else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
language = params.get(i+1); language = params.get(i+1);
@@ -465,6 +469,12 @@ public class TtsService extends Service implements OnCompletionListener {
country = params.get(i+1); country = params.get(i+1);
} else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
variant = params.get(i+1); variant = params.get(i+1);
} else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_STREAM)) {
try {
streamType = Integer.parseInt(params.get(i + 1));
} catch (NumberFormatException e) {
streamType = DEFAULT_STREAM_TYPE;
}
} }
} }
} }
@@ -472,7 +482,7 @@ public class TtsService extends Service implements OnCompletionListener {
setLanguage("", language, country, variant); setLanguage("", language, country, variant);
} }
} }
nativeSynth.speak(text); nativeSynth.speak(text, streamType);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Log.e("TTS speakInternalOnly", "tryLock interrupted"); Log.e("TTS speakInternalOnly", "tryLock interrupted");
e.printStackTrace(); e.printStackTrace();
@@ -651,8 +661,7 @@ public class TtsService extends Service implements OnCompletionListener {
// Utterance is part of the app calling the library // Utterance is part of the app calling the library
Context ctx; Context ctx;
try { try {
ctx = this.createPackageContext(sr.mSourcePackageName, ctx = this.createPackageContext(sr.mSourcePackageName, 0);
0);
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
mSpeechQueue.remove(0); // Remove it from the queue and mSpeechQueue.remove(0); // Remove it from the queue and
@@ -675,6 +684,7 @@ public class TtsService extends Service implements OnCompletionListener {
} }
mPlayer.setOnCompletionListener(this); mPlayer.setOnCompletionListener(this);
try { try {
mPlayer.setAudioStreamType(getStreamTypeFromParams(currentSpeechItem.mParams));
mPlayer.start(); mPlayer.start();
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
mSpeechQueue.clear(); mSpeechQueue.clear();
@@ -695,6 +705,24 @@ public class TtsService extends Service implements OnCompletionListener {
} }
} }
private int getStreamTypeFromParams(ArrayList<String> paramList) {
int streamType = DEFAULT_STREAM_TYPE;
if (paramList == null) {
return streamType;
}
for (int i = 0; i < paramList.size() - 1; i = i + 2) {
String param = paramList.get(i);
if ((param != null) && (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_STREAM))) {
try {
streamType = Integer.parseInt(paramList.get(i + 1));
} catch (NumberFormatException e) {
streamType = DEFAULT_STREAM_TYPE;
}
}
}
return streamType;
}
private void cleanUpPlayer() { private void cleanUpPlayer() {
if (mPlayer != null) { if (mPlayer != null) {
mPlayer.release(); mPlayer.release();