PlaybackSynthesisCallback.java revision 40f71f0be3cefabde9dc066d7707a1e5ebaec820
150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert/*
250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Copyright (C) 2011 The Android Open Source Project
350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert *
450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License"); you may not
550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * use this file except in compliance with the License. You may obtain a copy of
650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * the License at
750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert *
850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0
950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert *
1050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Unless required by applicable law or agreed to in writing, software
1150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * License for the specific language governing permissions and limitations under
1450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * the License.
1550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */
1650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertpackage android.speech.tts;
1750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
18754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamathimport android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
1950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.util.Log;
2050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
2150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert/**
2250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Speech synthesis request that plays the audio as it is received.
2350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */
24e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamathclass PlaybackSynthesisCallback extends AbstractSynthesisCallback {
2550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
2650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final String TAG = "PlaybackSynthesisRequest";
2750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final boolean DBG = false;
2850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
2950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
3050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
3150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    /**
3250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * Audio stream type. Must be one of the STREAM_ contants defined in
3350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * {@link android.media.AudioManager}.
3450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     */
3550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private final int mStreamType;
3650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
3750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    /**
3850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * Volume, in the range [0.0f, 1.0f]. The default value is
3950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
4050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     */
4150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private final float mVolume;
4250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
4350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    /**
4450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * Left/right position of the audio, in the range [-1.0f, 1.0f].
4550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
4650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     */
4750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private final float mPan;
4850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
498d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    /**
508d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath     * Guards {@link #mAudioTrackHandler}, {@link #mToken} and {@link #mStopped}.
518d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath     */
5250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private final Object mStateLock = new Object();
538d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
548d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // Handler associated with a thread that plays back audio requests.
558d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private final AudioPlaybackHandler mAudioTrackHandler;
56c3da8818f0598b3ab2cd6f4168349da6d0f72cb1Narayan Kamath    // A request "token", which will be non null after start() has been called.
578d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private SynthesisMessageParams mToken = null;
588d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // Whether this request has been stopped. This is useful for keeping
598d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // track whether stop() has been called before start(). In all other cases,
608d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // a non-null value of mToken will provide the same information.
6150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private boolean mStopped = false;
6250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
638d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private volatile boolean mDone = false;
648d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
65754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath    private final UtteranceProgressDispatcher mDispatcher;
664924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private final String mCallingApp;
676dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath    private final EventLogger mLogger;
688d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
69e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath    PlaybackSynthesisCallback(int streamType, float volume, float pan,
70754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath            AudioPlaybackHandler audioTrackHandler, UtteranceProgressDispatcher dispatcher,
716dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            String callingApp, EventLogger logger) {
7250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        mStreamType = streamType;
7350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        mVolume = volume;
7450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        mPan = pan;
75c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath        mAudioTrackHandler = audioTrackHandler;
768d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        mDispatcher = dispatcher;
774924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        mCallingApp = callingApp;
786dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        mLogger = logger;
7950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
8050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
8150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
8250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    void stop() {
8340f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath        stopImpl(false);
8440f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath    }
8540f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath
8640f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath    void stopImpl(boolean wasError) {
8750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) Log.d(TAG, "stop()");
888d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
896dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        // Note that mLogger.mError might be true too at this point.
906dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        mLogger.onStopped();
916dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath
9240f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath        SynthesisMessageParams token;
9350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
946dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            if (mStopped) {
956dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                Log.w(TAG, "stop() called twice");
968d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                return;
978d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            }
98be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath
9940f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            token = mToken;
10050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            mStopped = true;
10150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
1022a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath
1032a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        if (token != null) {
1042a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath            // This might result in the synthesis thread being woken up, at which
1052a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath            // point it will write an additional buffer to the token - but we
1062a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath            // won't worry about that because the audio playback queue will be cleared
1072a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath            // soon after (see SynthHandler#stop(String).
10840f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            token.setIsError(wasError);
1092a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath            token.clearBuffers();
11040f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            if (wasError) {
11140f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath                // Also clean up the audio track if an error occurs.
11240f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath                mAudioTrackHandler.enqueueSynthesisDone(token);
11340f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            }
11440f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath        } else {
11540f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            // This happens when stop() or error() were called before start() was.
11640f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath
11740f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            // In all other cases, mAudioTrackHandler.stop() will
11840f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            // result in onSynthesisDone being called, and we will
11940f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            // write data there.
12040f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            mLogger.onWriteData();
12140f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath
12240f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            if (wasError) {
12340f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath                // We have to dispatch the error ourselves.
12440f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath                mDispatcher.dispatchOnError();
12540f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            }
1262a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        }
12750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
12850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
12971e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    @Override
13071e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    public int getMaxBufferSize() {
13171e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        // The AudioTrack buffer will be at least MIN_AUDIO_BUFFER_SIZE, so that should always be
13271e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        // a safe buffer size to pass in.
13371e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        return MIN_AUDIO_BUFFER_SIZE;
13471e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    }
13571e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert
136360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    @Override
137360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    boolean isDone() {
138360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert        return mDone;
139360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    }
140360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert
14150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
14250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int start(int sampleRateInHz, int audioFormat, int channelCount) {
14350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) {
14450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat
14550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                    + "," + channelCount + ")");
14650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
14750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
1488d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount);
1498d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (channelConfig == 0) {
1508d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            Log.e(TAG, "Unsupported number of channels :" + channelCount);
1518d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            return TextToSpeech.ERROR;
1528d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
1538d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
15450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
15550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            if (mStopped) {
1568d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                if (DBG) Log.d(TAG, "stop() called before start(), returning.");
15750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
15850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1598d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            SynthesisMessageParams params = new SynthesisMessageParams(
1608d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                    mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
1616dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                    mDispatcher, mCallingApp, mLogger);
1628d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mAudioTrackHandler.enqueueSynthesisStart(params);
16350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
1648d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mToken = params;
16550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
16650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
16750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        return TextToSpeech.SUCCESS;
16850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
16950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
17050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
17150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
17250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int audioAvailable(byte[] buffer, int offset, int length) {
17350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) {
17450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
17571e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert                    + offset + "," + length + ")");
17671e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        }
177c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath        if (length > getMaxBufferSize() || length <= 0) {
178c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath            throw new IllegalArgumentException("buffer is too large or of zero length (" +
179c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath                    + length + " bytes)");
18050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
1818d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1822a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        SynthesisMessageParams token = null;
18350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
184be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath            if (mToken == null || mStopped) {
18550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
18650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1872a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath            token = mToken;
18850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
1898d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1902a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        // Sigh, another copy.
1912a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        final byte[] bufferCopy = new byte[length];
1922a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        System.arraycopy(buffer, offset, bufferCopy, 0, length);
1932a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        // Might block on mToken.this, if there are too many buffers waiting to
1942a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        // be consumed.
1952a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        token.addBuffer(bufferCopy);
1962a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        mAudioTrackHandler.enqueueSynthesisDataAvailable(token);
1972a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath
1986dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        mLogger.onEngineDataReceived();
1996dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath
2008d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        return TextToSpeech.SUCCESS;
20150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
20250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
20350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
20450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int done() {
20550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) Log.d(TAG, "done()");
2068d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
2072a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        SynthesisMessageParams token = null;
20850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
2098d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (mDone) {
2108d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                Log.w(TAG, "Duplicate call to done()");
21150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
21250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
2138d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
2148d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mDone = true;
2158d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
2168d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (mToken == null) {
21750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
21850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
2198d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
2202a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath            token = mToken;
22150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
2222a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath
2232a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        mAudioTrackHandler.enqueueSynthesisDone(token);
2242a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        mLogger.onEngineComplete();
2252a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath
22650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        return TextToSpeech.SUCCESS;
22750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
22871e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert
22971e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    @Override
230360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    public void error() {
2318d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (DBG) Log.d(TAG, "error() [will call stop]");
2326dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        // Currently, this call will not be logged if error( ) is called
2336dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        // before start.
2346dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        mLogger.onError();
23540f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath        stopImpl(true);
236360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    }
237360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert
23853f6f95308854e7da1fcd15081030d7de2fc5a7fNarayan Kamath}
239