Merge "Make changes to the TTS api suggested by the API review."

This commit is contained in:
Narayan Kamath
2011-06-09 04:09:22 -07:00
committed by Android (Google) Code Review
7 changed files with 236 additions and 144 deletions

View File

@@ -17455,21 +17455,23 @@ package android.speech {
package android.speech.tts {
public abstract class SynthesisRequest {
ctor public SynthesisRequest(java.lang.String, android.os.Bundle);
public abstract interface SynthesisCallback {
method public abstract int audioAvailable(byte[], int, int);
method public abstract int completeAudioAvailable(int, int, int, byte[], int, int);
method public abstract int done();
method public abstract void error();
method public abstract int getMaxBufferSize();
method public abstract int start(int, int, int);
}
public final class SynthesisRequest {
method public java.lang.String getCountry();
method public java.lang.String getLanguage();
method public abstract int getMaxBufferSize();
method public android.os.Bundle getParams();
method public int getPitch();
method public int getSpeechRate();
method public java.lang.String getText();
method public java.lang.String getVariant();
method public abstract int start(int, int, int);
}
public class TextToSpeech {
@@ -17555,7 +17557,7 @@ package android.speech.tts {
method protected abstract int onIsLanguageAvailable(java.lang.String, java.lang.String, java.lang.String);
method protected abstract int onLoadLanguage(java.lang.String, java.lang.String, java.lang.String);
method protected abstract void onStop();
method protected abstract void onSynthesizeText(android.speech.tts.SynthesisRequest);
method protected abstract void onSynthesizeText(android.speech.tts.SynthesisRequest, android.speech.tts.SynthesisCallback);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package android.speech.tts;
/**
* Defines additional methods the synthesis callback must implement that
* are private to the TTS service implementation.
*/
abstract class AbstractSynthesisCallback implements SynthesisCallback {
/**
* Checks whether the synthesis request completed successfully.
*/
abstract boolean isDone();
/**
* Aborts the speech request.
*
* Can be called from multiple threads.
*/
abstract void stop();
}

View File

@@ -16,7 +16,6 @@
package android.speech.tts;
import android.media.AudioFormat;
import android.os.Bundle;
import android.util.Log;
import java.io.File;
@@ -29,7 +28,7 @@ import java.nio.ByteOrder;
/**
* Speech synthesis request that writes the audio to a WAV file.
*/
class FileSynthesisRequest extends SynthesisRequest {
class FileSynthesisCallback extends AbstractSynthesisCallback {
private static final String TAG = "FileSynthesisRequest";
private static final boolean DBG = false;
@@ -48,8 +47,7 @@ class FileSynthesisRequest extends SynthesisRequest {
private boolean mStopped = false;
private boolean mDone = false;
FileSynthesisRequest(String text, Bundle params, File fileName) {
super(text, params);
FileSynthesisCallback(File fileName) {
mFileName = fileName;
}

View File

@@ -15,14 +15,13 @@
*/
package android.speech.tts;
import android.os.Bundle;
import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
import android.util.Log;
/**
* Speech synthesis request that plays the audio as it is received.
*/
class PlaybackSynthesisRequest extends SynthesisRequest {
class PlaybackSynthesisCallback extends AbstractSynthesisCallback {
private static final String TAG = "PlaybackSynthesisRequest";
private static final boolean DBG = false;
@@ -66,9 +65,9 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
private final UtteranceCompletedDispatcher mDispatcher;
PlaybackSynthesisRequest(String text, Bundle params, int streamType, float volume, float pan,
AudioPlaybackHandler audioTrackHandler, UtteranceCompletedDispatcher dispatcher) {
super(text, params);
PlaybackSynthesisCallback(int streamType, float volume, float pan,
AudioPlaybackHandler audioTrackHandler,
UtteranceCompletedDispatcher dispatcher) {
mStreamType = streamType;
mVolume = volume;
mPan = pan;

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package android.speech.tts;
/**
* A callback to return speech data synthesized by a text to speech engine.
*
* The engine can provide streaming audio by calling
* {@link #start}, then {@link #audioAvailable} until all audio has been provided, then finally
* {@link #done}.
*
* Alternatively, the engine can provide all the audio at once, by using
* {@link #completeAudioAvailable}.
*
* {@link #error} can be called at any stage in the synthesis process to
* indicate that an error has occured, but if the call is made after a call
* to {@link #done} or {@link #completeAudioAvailable} it might be discarded.
*/
public interface SynthesisCallback {
/**
* @return the maximum number of bytes that the TTS engine can pass in a single call of
* {@link #audioAvailable}. This does not apply to {@link #completeAudioAvailable}.
* Calls to {@link #audioAvailable} with data lengths larger than this
* value will not succeed.
*/
public int getMaxBufferSize();
/**
* The service should call this when it starts to synthesize audio for this
* request.
*
* This method should only be called on the synthesis thread,
* while in {@link TextToSpeechService#onSynthesizeText}.
*
* @param sampleRateInHz Sample rate in HZ of the generated audio.
* @param audioFormat Audio format of the generated audio. Must be one of
* the ENCODING_ constants defined in {@link android.media.AudioFormat}.
* @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
* @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
*/
public int start(int sampleRateInHz, int audioFormat, int channelCount);
/**
* The service should call this method when synthesized audio is ready for consumption.
*
* This method should only be called on the synthesis thread,
* while in {@link TextToSpeechService#onSynthesizeText}.
*
* @param buffer The generated audio data. This method will not hold on to {@code buffer},
* so the caller is free to modify it after this method returns.
* @param offset The offset into {@code buffer} where the audio data starts.
* @param length The number of bytes of audio data in {@code buffer}. This must be
* less than or equal to the return value of {@link #getMaxBufferSize}.
* @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
*/
public int audioAvailable(byte[] buffer, int offset, int length);
/**
* The service can call this method instead of using {@link #start}, {@link #audioAvailable}
* and {@link #done} if all the audio data is available in a single buffer.
*
* @param sampleRateInHz Sample rate in HZ of the generated audio.
* @param audioFormat Audio format of the generated audio. Must be one of
* the ENCODING_ constants defined in {@link android.media.AudioFormat}.
* @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
* @param buffer The generated audio data. This method will not hold on to {@code buffer},
* so the caller is free to modify it after this method returns.
* @param offset The offset into {@code buffer} where the audio data starts.
* @param length The number of bytes of audio data in {@code buffer}.
* @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
*/
public int completeAudioAvailable(int sampleRateInHz, int audioFormat,
int channelCount, byte[] buffer, int offset, int length);
/**
* The service should call this method when all the synthesized audio for a request has
* been passed to {@link #audioAvailable}.
*
* This method should only be called on the synthesis thread,
* while in {@link TextToSpeechService#onSynthesizeText}.
*
* @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
*/
public int done();
/**
* The service should call this method if the speech synthesis fails.
*
* This method should only be called on the synthesis thread,
* while in {@link TextToSpeechService#onSynthesizeText}.
*/
public void error();
}

View File

@@ -18,17 +18,22 @@ package android.speech.tts;
import android.os.Bundle;
/**
* A request for speech synthesis given to a TTS engine for processing.
* Contains data required by engines to synthesize speech. This data is :
* <ul>
* <li>The text to synthesize</li>
* <li>The synthesis locale, represented as a language, country and a variant.
* The language is an ISO 639-3 letter language code, and the country is an
* ISO 3166 alpha 3 code. The variant is not specified.</li>
* <li>The synthesis speech rate, with 100 being the normal, and
* higher values representing higher speech rates.</li>
* <li>The voice pitch, with 100 being the default pitch.</li>
* </ul>
*
* The engine can provide streaming audio by calling
* {@link #start}, then {@link #audioAvailable} until all audio has been provided, then finally
* {@link #done}.
*
* Alternatively, the engine can provide all the audio at once, by using
* {@link #completeAudioAvailable}.
* Any additional parameters sent to the text to speech service are passed in
* uninterpreted, see the @code{params} argument in {@link TextToSpeech#speak}
* and {@link TextToSpeech#synthesizeToFile}.
*/
public abstract class SynthesisRequest {
public final class SynthesisRequest {
private final String mText;
private final Bundle mParams;
private String mLanguage;
@@ -37,34 +42,12 @@ public abstract class SynthesisRequest {
private int mSpeechRate;
private int mPitch;
public SynthesisRequest(String text, Bundle params) {
SynthesisRequest(String text, Bundle params) {
mText = text;
// Makes a copy of params.
mParams = new Bundle(params);
}
/**
* Sets the locale for the request.
*/
void setLanguage(String language, String country, String variant) {
mLanguage = language;
mCountry = country;
mVariant = variant;
}
/**
* Sets the speech rate.
*/
void setSpeechRate(int speechRate) {
mSpeechRate = speechRate;
}
/**
* Sets the pitch.
*/
void setPitch(int pitch) {
mPitch = pitch;
}
/**
* Gets the text which should be synthesized.
*/
@@ -115,86 +98,25 @@ public abstract class SynthesisRequest {
}
/**
* Gets the maximum number of bytes that the TTS engine can pass in a single call of
* {@link #audioAvailable}. This does not apply to {@link #completeAudioAvailable}.
* Sets the locale for the request.
*/
public abstract int getMaxBufferSize();
void setLanguage(String language, String country, String variant) {
mLanguage = language;
mCountry = country;
mVariant = variant;
}
/**
* Checks whether the synthesis request completed successfully.
* Sets the speech rate.
*/
abstract boolean isDone();
void setSpeechRate(int speechRate) {
mSpeechRate = speechRate;
}
/**
* Aborts the speech request.
*
* Can be called from multiple threads.
* Sets the pitch.
*/
abstract void stop();
/**
* The service should call this when it starts to synthesize audio for this
* request.
*
* This method should only be called on the synthesis thread,
* while in {@link TextToSpeechService#onSynthesizeText}.
*
* @param sampleRateInHz Sample rate in HZ of the generated audio.
* @param audioFormat Audio format of the generated audio. Must be one of
* the ENCODING_ constants defined in {@link android.media.AudioFormat}.
* @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
* @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
*/
public abstract int start(int sampleRateInHz, int audioFormat, int channelCount);
/**
* The service should call this method when synthesized audio is ready for consumption.
*
* This method should only be called on the synthesis thread,
* while in {@link TextToSpeechService#onSynthesizeText}.
*
* @param buffer The generated audio data. This method will not hold on to {@code buffer},
* so the caller is free to modify it after this method returns.
* @param offset The offset into {@code buffer} where the audio data starts.
* @param length The number of bytes of audio data in {@code buffer}. This must be
* less than or equal to the return value of {@link #getMaxBufferSize}.
* @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
*/
public abstract int audioAvailable(byte[] buffer, int offset, int length);
/**
* The service should call this method when all the synthesized audio for a request has
* been passed to {@link #audioAvailable}.
*
* This method should only be called on the synthesis thread,
* while in {@link TextToSpeechService#onSynthesizeText}.
*
* @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
*/
public abstract int done();
/**
* The service should call this method if the speech synthesis fails.
*
* This method should only be called on the synthesis thread,
* while in {@link TextToSpeechService#onSynthesizeText}.
*/
public abstract void error();
/**
* The service can call this method instead of using {@link #start}, {@link #audioAvailable}
* and {@link #done} if all the audio data is available in a single buffer.
*
* @param sampleRateInHz Sample rate in HZ of the generated audio.
* @param audioFormat Audio format of the generated audio. Must be one of
* the ENCODING_ constants defined in {@link android.media.AudioFormat}.
* @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
* @param buffer The generated audio data. This method will not hold on to {@code buffer},
* so the caller is free to modify it after this method returns.
* @param offset The offset into {@code buffer} where the audio data starts.
* @param length The number of bytes of audio data in {@code buffer}.
* @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
*/
public abstract int completeAudioAvailable(int sampleRateInHz, int audioFormat,
int channelCount, byte[] buffer, int offset, int length);
}
void setPitch(int pitch) {
mPitch = pitch;
}
}

View File

@@ -19,7 +19,6 @@ import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -40,8 +39,34 @@ import java.util.Locale;
/**
* Abstract base class for TTS engine implementations.
* Abstract base class for TTS engine implementations. The following methods
* need to be implemented.
*
* <ul>
* <li>{@link #onIsLanguageAvailable}</li>
* <li>{@link #onLoadLanguage}</li>
* <li>{@link #onGetLanguage}</li>
* <li>{@link #onSynthesizeText}</li>
* <li>{@link #onStop}</li>
* </ul>
*
* The first three deal primarily with language management, and are used to
* query the engine for it's support for a given language and indicate to it
* that requests in a given language are imminent.
*
* {@link #onSynthesizeText} is central to the engine implementation. The
* implementation should synthesize text as per the request parameters and
* return synthesized data via the supplied callback. This class and its helpers
* will then consume that data, which might mean queueing it for playback or writing
* it to a file or similar. All calls to this method will be on a single
* thread, which will be different from the main thread of the service. Synthesis
* must be synchronous which means the engine must NOT hold on the callback or call
* any methods on it after the method returns
*
* {@link #onStop} tells the engine that it should stop all ongoing synthesis, if
* any. Any pending data from the current synthesis will be discarded.
*/
// TODO: Add a link to the sample TTS engine once it's done.
public abstract class TextToSpeechService extends Service {
private static final boolean DBG = false;
@@ -160,10 +185,12 @@ public abstract class TextToSpeechService extends Service {
*
* Called on the synthesis thread.
*
* @param request The synthesis request. The method should use the methods in the request
* object to communicate the results of the synthesis.
* @param request The synthesis request.
* @param callback The callback the the engine must use to make data available for
* playback or for writing to a file.
*/
protected abstract void onSynthesizeText(SynthesisRequest request);
protected abstract void onSynthesizeText(SynthesisRequest request,
SynthesisCallback callback);
private boolean areDefaultsEnforced() {
return getSecureSettingInt(Settings.Secure.TTS_USE_DEFAULTS,
@@ -421,11 +448,16 @@ public abstract class TextToSpeechService extends Service {
class SynthesisSpeechItem extends SpeechItem {
private final String mText;
private SynthesisRequest mSynthesisRequest;
private final SynthesisRequest mSynthesisRequest;
// Non null after synthesis has started, and all accesses
// guarded by 'this'.
private AbstractSynthesisCallback mSynthesisCallback;
public SynthesisSpeechItem(String callingApp, Bundle params, String text) {
super(callingApp, params);
mText = text;
mSynthesisRequest = new SynthesisRequest(mText, mParams);
setRequestParams(mSynthesisRequest);
}
public String getText() {
@@ -447,20 +479,18 @@ public abstract class TextToSpeechService extends Service {
@Override
protected int playImpl() {
SynthesisRequest synthesisRequest;
AbstractSynthesisCallback synthesisCallback;
synchronized (this) {
mSynthesisRequest = createSynthesisRequest();
synthesisRequest = mSynthesisRequest;
mSynthesisCallback = createSynthesisCallback();
synthesisCallback = mSynthesisCallback;
}
setRequestParams(synthesisRequest);
TextToSpeechService.this.onSynthesizeText(synthesisRequest);
return synthesisRequest.isDone() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
TextToSpeechService.this.onSynthesizeText(mSynthesisRequest, synthesisCallback);
return synthesisCallback.isDone() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
}
protected SynthesisRequest createSynthesisRequest() {
return new PlaybackSynthesisRequest(mText, mParams,
getStreamType(), getVolume(), getPan(), mAudioPlaybackHandler,
this);
protected AbstractSynthesisCallback createSynthesisCallback() {
return new PlaybackSynthesisCallback(getStreamType(), getVolume(), getPan(),
mAudioPlaybackHandler, this);
}
private void setRequestParams(SynthesisRequest request) {
@@ -476,11 +506,11 @@ public abstract class TextToSpeechService extends Service {
@Override
protected void stopImpl() {
SynthesisRequest synthesisRequest;
AbstractSynthesisCallback synthesisCallback;
synchronized (this) {
synthesisRequest = mSynthesisRequest;
synthesisCallback = mSynthesisCallback;
}
synthesisRequest.stop();
synthesisCallback.stop();
TextToSpeechService.this.onStop();
}
@@ -529,8 +559,8 @@ public abstract class TextToSpeechService extends Service {
}
@Override
protected SynthesisRequest createSynthesisRequest() {
return new FileSynthesisRequest(getText(), mParams, mFile);
protected AbstractSynthesisCallback createSynthesisCallback() {
return new FileSynthesisCallback(mFile);
}
@Override