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
185cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniakimport android.speech.tts.TextToSpeechService.AudioOutputParams;
19754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamathimport android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
2050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.util.Log;
2150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
2250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert/**
2350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Speech synthesis request that plays the audio as it is received.
2450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */
25e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamathclass PlaybackSynthesisCallback extends AbstractSynthesisCallback {
2650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
2750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final String TAG = "PlaybackSynthesisRequest";
2850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final boolean DBG = false;
2950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
3050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
3150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
325cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak    private final AudioOutputParams mAudioParams;
3350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
348d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    /**
3567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * Guards {@link #mAudioTrackHandler}, {@link #mItem} and {@link #mStopped}.
368d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath     */
3750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private final Object mStateLock = new Object();
388d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
398d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // Handler associated with a thread that plays back audio requests.
408d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private final AudioPlaybackHandler mAudioTrackHandler;
41c3da8818f0598b3ab2cd6f4168349da6d0f72cb1Narayan Kamath    // A request "token", which will be non null after start() has been called.
4267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private SynthesisPlaybackQueueItem mItem = null;
4350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
448d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private volatile boolean mDone = false;
458d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4690d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    /** Status code of synthesis */
4790d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    protected int mStatusCode;
4890d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak
49754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath    private final UtteranceProgressDispatcher mDispatcher;
50492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath    private final Object mCallerIdentity;
5190d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    private final AbstractEventLogger mLogger;
528d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
535cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak    PlaybackSynthesisCallback(AudioOutputParams audioParams, AudioPlaybackHandler audioTrackHandler,
545cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak            UtteranceProgressDispatcher dispatcher, Object callerIdentity,
555cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak            AbstractEventLogger logger, boolean clientIsUsingV2) {
5690d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        super(clientIsUsingV2);
575cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak        mAudioParams = audioParams;
58c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath        mAudioTrackHandler = audioTrackHandler;
598d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        mDispatcher = dispatcher;
60492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath        mCallerIdentity = callerIdentity;
616dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        mLogger = logger;
62fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak        mStatusCode = TextToSpeech.SUCCESS;
6350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
6450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
6550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
6650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    void stop() {
6750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) Log.d(TAG, "stop()");
688d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
6967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        SynthesisPlaybackQueueItem item;
7050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
7190d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            if (mDone) {
7290d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                return;
7390d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            }
74fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak            if (mStatusCode == TextToSpeech.STOPPED) {
756dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                Log.w(TAG, "stop() called twice");
768d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                return;
778d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            }
78be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath
7967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            item = mItem;
80fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak            mStatusCode = TextToSpeech.STOPPED;
8150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
822a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath
8367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        if (item != null) {
842a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath            // This might result in the synthesis thread being woken up, at which
8567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // point it will write an additional buffer to the item - but we
862a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath            // won't worry about that because the audio playback queue will be cleared
872a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath            // soon after (see SynthHandler#stop(String).
88fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak            item.stop(TextToSpeech.STOPPED);
8940f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath        } else {
9040f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            // This happens when stop() or error() were called before start() was.
9140f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath
9240f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            // In all other cases, mAudioTrackHandler.stop() will
9340f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            // result in onSynthesisDone being called, and we will
9440f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath            // write data there.
95fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak            mLogger.onCompleted(TextToSpeech.STOPPED);
9690d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            mDispatcher.dispatchOnStop();
972a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        }
9850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
9950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
10071e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    @Override
10171e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    public int getMaxBufferSize() {
10271e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        // The AudioTrack buffer will be at least MIN_AUDIO_BUFFER_SIZE, so that should always be
10371e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        // a safe buffer size to pass in.
10471e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        return MIN_AUDIO_BUFFER_SIZE;
10571e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    }
10671e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert
107360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    @Override
10890d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    public boolean hasStarted() {
10990d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        synchronized (mStateLock) {
11090d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            return mItem != null;
11190d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        }
112360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    }
113360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert
11450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
11590d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    public boolean hasFinished() {
11690d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        synchronized (mStateLock) {
11790d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            return mDone;
11850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
11990d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    }
12090d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak
12190d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    @Override
12290d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    public int start(int sampleRateInHz, int audioFormat, int channelCount) {
12390d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        if (DBG) Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat + "," + channelCount
12490d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                + ")");
12550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
12667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        int channelConfig = BlockingAudioTrack.getChannelConfig(channelCount);
1278d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
12850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
12990d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            if (channelConfig == 0) {
13090d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                Log.e(TAG, "Unsupported number of channels :" + channelCount);
131fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak                mStatusCode = TextToSpeech.ERROR_OUTPUT;
13290d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                return TextToSpeech.ERROR;
13390d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            }
134fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak            if (mStatusCode == TextToSpeech.STOPPED) {
1358d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                if (DBG) Log.d(TAG, "stop() called before start(), returning.");
13690d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                return errorCodeOnStop();
13790d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            }
138fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak            if (mStatusCode != TextToSpeech.SUCCESS) {
13990d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                if (DBG) Log.d(TAG, "Error was raised");
14090d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                return TextToSpeech.ERROR;
14190d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            }
14290d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            if (mItem != null) {
14390d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                Log.e(TAG, "Start called twice");
14450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
14550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
14667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            SynthesisPlaybackQueueItem item = new SynthesisPlaybackQueueItem(
1475cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak                    mAudioParams, sampleRateInHz, audioFormat, channelCount,
148492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath                    mDispatcher, mCallerIdentity, mLogger);
14967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mAudioTrackHandler.enqueue(item);
15067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mItem = item;
15150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
15250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
15350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        return TextToSpeech.SUCCESS;
15450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
15550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
15650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
15750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int audioAvailable(byte[] buffer, int offset, int length) {
15890d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        if (DBG) Log.d(TAG, "audioAvailable(byte[" + buffer.length + "]," + offset + "," + length
15990d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                + ")");
16090d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak
161c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath        if (length > getMaxBufferSize() || length <= 0) {
162c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath            throw new IllegalArgumentException("buffer is too large or of zero length (" +
163c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath                    + length + " bytes)");
16450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
1658d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
16667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        SynthesisPlaybackQueueItem item = null;
16750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
16890d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            if (mItem == null) {
169fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak                mStatusCode = TextToSpeech.ERROR_OUTPUT;
17050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
17150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
172fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak            if (mStatusCode != TextToSpeech.SUCCESS) {
17390d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                if (DBG) Log.d(TAG, "Error was raised");
17490d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                return TextToSpeech.ERROR;
17590d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            }
176fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak            if (mStatusCode == TextToSpeech.STOPPED) {
17790d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                return errorCodeOnStop();
17890d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            }
17967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            item = mItem;
18050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
1818d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1822a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        // Sigh, another copy.
1832a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        final byte[] bufferCopy = new byte[length];
1842a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        System.arraycopy(buffer, offset, bufferCopy, 0, length);
18567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
18667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        // Might block on mItem.this, if there are too many buffers waiting to
1872a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        // be consumed.
18867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        try {
18967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            item.put(bufferCopy);
19067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        } catch (InterruptedException ie) {
19190d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            synchronized (mStateLock) {
192fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak                mStatusCode = TextToSpeech.ERROR_OUTPUT;
19390d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                return TextToSpeech.ERROR;
19490d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            }
19567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
1962a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath
1976dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        mLogger.onEngineDataReceived();
1988d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        return TextToSpeech.SUCCESS;
19950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
20050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
20150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
20250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int done() {
20350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) Log.d(TAG, "done()");
2048d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
20590d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        int statusCode = 0;
20667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        SynthesisPlaybackQueueItem item = null;
20750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
2088d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (mDone) {
2098d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                Log.w(TAG, "Duplicate call to done()");
21090d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                // Not an error that would prevent synthesis. Hence no
21190d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                // setStatusCode
21250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
21350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
214fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak            if (mStatusCode == TextToSpeech.STOPPED) {
21590d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                if (DBG) Log.d(TAG, "Request has been aborted.");
21690d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                return errorCodeOnStop();
21790d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            }
2188d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mDone = true;
2198d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
22067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            if (mItem == null) {
22190d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                // .done() was called before .start. Treat it as successful synthesis
22290d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                // for a client, despite service bad implementation.
22390d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                Log.w(TAG, "done() was called before start() call");
224fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak                if (mStatusCode == TextToSpeech.SUCCESS) {
22590d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                    mDispatcher.dispatchOnSuccess();
22690d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                } else {
22790d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                    mDispatcher.dispatchOnError(mStatusCode);
22890d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                }
22990d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                mLogger.onEngineComplete();
23050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
23150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
2328d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
23367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            item = mItem;
23490d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            statusCode = mStatusCode;
23550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
2362a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath
23790d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        // Signal done or error to item
238fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak        if (statusCode == TextToSpeech.SUCCESS) {
23990d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            item.done();
24090d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        } else {
24190d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            item.stop(statusCode);
24290d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        }
2432a0518cd1d18dc50ffa063a71133e69de2e866abNarayan Kamath        mLogger.onEngineComplete();
24450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        return TextToSpeech.SUCCESS;
24550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
24671e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert
24771e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    @Override
248360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    public void error() {
249fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak        error(TextToSpeech.ERROR_SYNTHESIS);
25090d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    }
25190d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak
25290d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    @Override
25390d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    public void error(int errorCode) {
2548d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (DBG) Log.d(TAG, "error() [will call stop]");
25590d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        synchronized (mStateLock) {
25690d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            if (mDone) {
25790d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak                return;
25890d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            }
25990d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            mStatusCode = errorCode;
26090d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        }
261360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    }
26253f6f95308854e7da1fcd15081030d7de2fc5a7fNarayan Kamath}
263