From 40f71f0be3cefabde9dc066d7707a1e5ebaec820 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Wed, 23 Nov 2011 16:42:53 +0000 Subject: Fix regressions in TTS completion callbacks. (a) onUtteranceCompleted should be called on errors too. Also, fix up the error handling so that onUtteranceCompleted is always called. (b) Don't treat empty utterances as errors, and let the engine synthesize them, as before. bug:5662598 Change-Id: I9223592bc6fe5f47d71103f4f02f046b54a655a8 --- .../android/speech/tts/AudioPlaybackHandler.java | 24 +++++++++++++-- .../speech/tts/PlaybackSynthesisCallback.java | 35 +++++++++++++++------- .../android/speech/tts/SynthesisMessageParams.java | 10 +++++++ .../android/speech/tts/TextToSpeechService.java | 5 ++-- .../speech/tts/UtteranceProgressListener.java | 10 +++++-- 5 files changed, 65 insertions(+), 19 deletions(-) diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java index 0194240..fd00dce 100644 --- a/core/java/android/speech/tts/AudioPlaybackHandler.java +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -118,12 +118,26 @@ class AudioPlaybackHandler { if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) { stop(current); } + + final MessageParams lastSynthesis = mLastSynthesisRequest; + + if (lastSynthesis != null && lastSynthesis != current && + TextUtils.equals(callingApp, lastSynthesis.getCallingApp())) { + stop(lastSynthesis); + } } synchronized public void removeAllItems() { if (DBG_THREADING) Log.d(TAG, "Removing all items"); removeAllMessages(); - stop(getCurrentParams()); + + final MessageParams current = getCurrentParams(); + final MessageParams lastSynthesis = mLastSynthesisRequest; + stop(current); + + if (lastSynthesis != null && lastSynthesis != current) { + stop(lastSynthesis); + } } /** @@ -350,7 +364,7 @@ class AudioPlaybackHandler { // extra trouble to clean the data to prevent the AudioTrack resources // from being leaked. if (mLastSynthesisRequest != null) { - Log.w(TAG, "Error : Missing call to done() for request : " + + Log.e(TAG, "Error : Missing call to done() for request : " + mLastSynthesisRequest); handleSynthesisDone(mLastSynthesisRequest); } @@ -443,7 +457,11 @@ class AudioPlaybackHandler { audioTrack.release(); params.setAudioTrack(null); } - params.getDispatcher().dispatchOnDone(); + if (params.isError()) { + params.getDispatcher().dispatchOnError(); + } else { + params.getDispatcher().dispatchOnDone(); + } mLastSynthesisRequest = null; params.mLogger.onWriteData(); } diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java index ce3522b..91a3452 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java +++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java @@ -80,27 +80,23 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { @Override void stop() { + stopImpl(false); + } + + void stopImpl(boolean wasError) { if (DBG) Log.d(TAG, "stop()"); // Note that mLogger.mError might be true too at this point. mLogger.onStopped(); - SynthesisMessageParams token = null; + SynthesisMessageParams token; synchronized (mStateLock) { if (mStopped) { Log.w(TAG, "stop() called twice"); return; } - // mToken will be null if the engine encounters - // an error before it called start(). - if (mToken == null) { - // In all other cases, mAudioTrackHandler.stop() will - // result in onComplete being called. - mLogger.onWriteData(); - } else { - token = mToken; - } + token = mToken; mStopped = true; } @@ -109,7 +105,24 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { // point it will write an additional buffer to the token - but we // won't worry about that because the audio playback queue will be cleared // soon after (see SynthHandler#stop(String). + token.setIsError(wasError); token.clearBuffers(); + if (wasError) { + // Also clean up the audio track if an error occurs. + mAudioTrackHandler.enqueueSynthesisDone(token); + } + } else { + // This happens when stop() or error() were called before start() was. + + // In all other cases, mAudioTrackHandler.stop() will + // result in onSynthesisDone being called, and we will + // write data there. + mLogger.onWriteData(); + + if (wasError) { + // We have to dispatch the error ourselves. + mDispatcher.dispatchOnError(); + } } } @@ -219,7 +232,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { // Currently, this call will not be logged if error( ) is called // before start. mLogger.onError(); - stop(); + stopImpl(true); } } diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java index 0c0f033..ed66420 100644 --- a/core/java/android/speech/tts/SynthesisMessageParams.java +++ b/core/java/android/speech/tts/SynthesisMessageParams.java @@ -51,6 +51,7 @@ final class SynthesisMessageParams extends MessageParams { int mAudioBufferSize; // Always synchronized on "this". int mUnconsumedBytes; + volatile boolean mIsError; private final LinkedList mDataBufferList = new LinkedList(); @@ -74,6 +75,7 @@ final class SynthesisMessageParams extends MessageParams { mAudioTrack = null; mBytesWritten = 0; mAudioBufferSize = 0; + mIsError = false; } @Override @@ -120,6 +122,14 @@ final class SynthesisMessageParams extends MessageParams { return mAudioTrack; } + void setIsError(boolean isError) { + mIsError = isError; + } + + boolean isError() { + return mIsError; + } + // Must be called synchronized on this. private long getUnconsumedAudioLengthMs() { final int unconsumedFrames = mUnconsumedBytes / mBytesPerFrame; diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 39922da..f82a659 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -509,6 +509,7 @@ public abstract class TextToSpeechService extends Service { } class SynthesisSpeechItem extends SpeechItem { + // Never null. private final String mText; private final SynthesisRequest mSynthesisRequest; private final String[] mDefaultLocale; @@ -532,8 +533,8 @@ public abstract class TextToSpeechService extends Service { @Override public boolean isValid() { - if (TextUtils.isEmpty(mText)) { - Log.w(TAG, "Got empty text"); + if (mText == null) { + Log.wtf(TAG, "Got null text"); return false; } if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH) { diff --git a/core/java/android/speech/tts/UtteranceProgressListener.java b/core/java/android/speech/tts/UtteranceProgressListener.java index a04458a..cf0d22c 100644 --- a/core/java/android/speech/tts/UtteranceProgressListener.java +++ b/core/java/android/speech/tts/UtteranceProgressListener.java @@ -57,12 +57,16 @@ public abstract class UtteranceProgressListener { listener.onUtteranceCompleted(utteranceId); } - // The following methods are left unimplemented. @Override - public void onStart(String utteranceId) { } + public void onError(String utteranceId) { + listener.onUtteranceCompleted(utteranceId); + } @Override - public void onError(String utteranceId) { } + public void onStart(String utteranceId) { + // Left unimplemented, has no equivalent in the old + // API. + } }; } } -- cgit v1.1