167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath/*
267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * Copyright (C) 2011 The Android Open Source Project
367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath *
467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * Licensed under the Apache License, Version 2.0 (the "License"); you may not
567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * use this file except in compliance with the License. You may obtain a copy of
667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * the License at
767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath *
867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * http://www.apache.org/licenses/LICENSE-2.0
967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath *
1067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * Unless required by applicable law or agreed to in writing, software
1167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * License for the specific language governing permissions and limitations under
1467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * the License.
1567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath */
1667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathpackage android.speech.tts;
1767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
185cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniakimport android.speech.tts.TextToSpeechService.AudioOutputParams;
1967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
2067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport android.util.Log;
2167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
2267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport java.util.LinkedList;
2367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport java.util.concurrent.locks.Condition;
2467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport java.util.concurrent.locks.Lock;
2567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport java.util.concurrent.locks.ReentrantLock;
2667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
2767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath/**
2867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * Manages the playback of a list of byte arrays representing audio data
2967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * that are queued by the engine to an audio track.
3067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath */
3167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathfinal class SynthesisPlaybackQueueItem extends PlaybackQueueItem {
3267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private static final String TAG = "TTS.SynthQueueItem";
3367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private static final boolean DBG = false;
3467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
3567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    /**
3667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * Maximum length of audio we leave unconsumed by the audio track.
3767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * Calls to {@link #put(byte[])} will block until we have less than
3867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * this amount of audio left to play back.
3967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     */
4067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private static final long MAX_UNCONSUMED_AUDIO_MS = 500;
4167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
4267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    /**
4367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * Guards accesses to mDataBufferList and mUnconsumedBytes.
4467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     */
4567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private final Lock mListLock = new ReentrantLock();
4667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private final Condition mReadReady = mListLock.newCondition();
4767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private final Condition mNotFull = mListLock.newCondition();
4867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
4967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    // Guarded by mListLock.
5067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>();
5167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    // Guarded by mListLock.
5267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private int mUnconsumedBytes;
5367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
5467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    /*
5567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * While mStopped and mIsError can be written from any thread, mDone is written
5667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * only from the synthesis thread. All three variables are read from the
5767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * audio playback thread.
5867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     */
5967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private volatile boolean mStopped;
6067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private volatile boolean mDone;
6190d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    private volatile int mStatusCode;
6267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
6367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private final BlockingAudioTrack mAudioTrack;
6490d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    private final AbstractEventLogger mLogger;
6567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
665cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak    SynthesisPlaybackQueueItem(AudioOutputParams audioParams, int sampleRate,
675cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak            int audioFormat, int channelCount, UtteranceProgressDispatcher dispatcher,
6890d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            Object callerIdentity, AbstractEventLogger logger) {
6967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        super(dispatcher, callerIdentity);
7067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
7167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mUnconsumedBytes = 0;
7267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
7367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mStopped = false;
7467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mDone = false;
75fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak        mStatusCode = TextToSpeech.SUCCESS;
7667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
775cbf17ca053b09beadd0b031a46ce193ab27a0f8Przemyslaw Szczepaniak        mAudioTrack = new BlockingAudioTrack(audioParams, sampleRate, audioFormat, channelCount);
7867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mLogger = logger;
7967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
8067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
8167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
8267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    @Override
8367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    public void run() {
8467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        final UtteranceProgressDispatcher dispatcher = getDispatcher();
8567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        dispatcher.dispatchOnStart();
8667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
87ed4e541a20cc662b8399844684d18ad0060bd1cbNarayan Kamath        if (!mAudioTrack.init()) {
88fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak            dispatcher.dispatchOnError(TextToSpeech.ERROR_OUTPUT);
89ed4e541a20cc662b8399844684d18ad0060bd1cbNarayan Kamath            return;
90ed4e541a20cc662b8399844684d18ad0060bd1cbNarayan Kamath        }
9167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
9267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        try {
9367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            byte[] buffer = null;
9467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
9567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // take() will block until:
9667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            //
9767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // (a) there is a buffer available to tread. In which case
9867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // a non null value is returned.
9967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // OR (b) stop() is called in which case it will return null.
10067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // OR (c) done() is called in which case it will return null.
10167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            while ((buffer = take()) != null) {
10267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                mAudioTrack.write(buffer);
10367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                mLogger.onAudioDataWritten();
10467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            }
10567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
10667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        } catch (InterruptedException ie) {
10767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            if (DBG) Log.d(TAG, "Interrupted waiting for buffers, cleaning up.");
10867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
10967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
11067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mAudioTrack.waitAndRelease();
11167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
112fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak        if (mStatusCode == TextToSpeech.SUCCESS) {
11390d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            dispatcher.dispatchOnSuccess();
114fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak        } else if(mStatusCode == TextToSpeech.STOPPED) {
11590d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            dispatcher.dispatchOnStop();
11667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        } else {
11790d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            dispatcher.dispatchOnError(mStatusCode);
11867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
11967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
12090d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak        mLogger.onCompleted(mStatusCode);
12167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
12267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
12367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    @Override
12490d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak    void stop(int statusCode) {
12567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        try {
12667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.lock();
12767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
12867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Update our internal state.
12967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mStopped = true;
13090d15d2371ad85f22254be6985455aa2baa5d15dPrzemyslaw Szczepaniak            mStatusCode = statusCode;
13167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
13267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Wake up the audio playback thread if it was waiting on take().
13367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // take() will return null since mStopped was true, and will then
13467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // break out of the data write loop.
13567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mReadReady.signal();
13667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
13767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Wake up the synthesis thread if it was waiting on put(). Its
13867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // buffers will no longer be copied since mStopped is true. The
13967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // PlaybackSynthesisCallback that this synthesis corresponds to
14067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // would also have been stopped, and so all calls to
14167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Callback.onDataAvailable( ) will return errors too.
14267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mNotFull.signal();
14367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        } finally {
14467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.unlock();
14567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
14667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
14767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        // Stop the underlying audio track. This will stop sending
14867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        // data to the mixer and discard any pending buffers that the
14967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        // track holds.
15067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mAudioTrack.stop();
15167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
15267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
15367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    void done() {
15467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        try {
15567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.lock();
15667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
15767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Update state.
15867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mDone = true;
15967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
16067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Unblocks the audio playback thread if it was waiting on take()
16167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // after having consumed all available buffers. It will then return
16267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // null and leave the write loop.
16367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mReadReady.signal();
16467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
16567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Just so that engines that try to queue buffers after
16667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // calling done() don't block the synthesis thread forever. Ideally
16767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // this should be called from the same thread as put() is, and hence
16867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // this call should be pointless.
16967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mNotFull.signal();
17067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        } finally {
17167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.unlock();
17267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
17367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
17467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
17567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
17667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    void put(byte[] buffer) throws InterruptedException {
17767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        try {
17867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.lock();
17967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            long unconsumedAudioMs = 0;
18067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
18167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            while ((unconsumedAudioMs = mAudioTrack.getAudioLengthMs(mUnconsumedBytes)) >
18267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                    MAX_UNCONSUMED_AUDIO_MS && !mStopped) {
18367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                mNotFull.await();
18467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            }
18567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
18667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Don't bother queueing the buffer if we've stopped. The playback thread
18767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // would have woken up when stop() is called (if it was blocked) and will
18867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // proceed to leave the write loop since take() will return null when
18967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // stopped.
19067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            if (mStopped) {
19167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                return;
19267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            }
19367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
19467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mDataBufferList.add(new ListEntry(buffer));
19567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mUnconsumedBytes += buffer.length;
19667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mReadReady.signal();
19767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        } finally {
19867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.unlock();
19967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
20067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
20167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
20267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private byte[] take() throws InterruptedException {
20367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        try {
20467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.lock();
20567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
20667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Block if there are no available buffers, and stop() has not
20767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // been called and done() has not been called.
20867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            while (mDataBufferList.size() == 0 && !mStopped && !mDone) {
20967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                mReadReady.await();
21067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            }
21167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
21267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // If stopped, return null so that we can exit the playback loop
21367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // as soon as possible.
21467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            if (mStopped) {
21567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                return null;
21667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            }
21767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
21867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Remove the first entry from the queue.
21967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            ListEntry entry = mDataBufferList.poll();
22067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
22167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // This is the normal playback loop exit case, when done() was
22267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // called. (mDone will be true at this point).
22367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            if (entry == null) {
22467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                return null;
22567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            }
22667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
22767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mUnconsumedBytes -= entry.mBytes.length;
22867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Unblock the waiting writer. We use signal() and not signalAll()
22967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // because there will only be one thread waiting on this (the
23067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Synthesis thread).
23167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mNotFull.signal();
23267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
23367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            return entry.mBytes;
23467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        } finally {
23567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.unlock();
23667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
23767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
23867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
23967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    static final class ListEntry {
24067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        final byte[] mBytes;
24167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
24267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        ListEntry(byte[] bytes) {
24367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mBytes = bytes;
24467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
24567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
24667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath}
247