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
1867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
1967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport android.util.Log;
2067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
2167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport java.util.LinkedList;
2267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport java.util.concurrent.locks.Condition;
2367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport java.util.concurrent.locks.Lock;
2467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport java.util.concurrent.locks.ReentrantLock;
2567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
2667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath/**
2767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * Manages the playback of a list of byte arrays representing audio data
2867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * that are queued by the engine to an audio track.
2967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath */
3067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathfinal class SynthesisPlaybackQueueItem extends PlaybackQueueItem {
3167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private static final String TAG = "TTS.SynthQueueItem";
3267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private static final boolean DBG = false;
3367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
3467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    /**
3567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * Maximum length of audio we leave unconsumed by the audio track.
3667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * Calls to {@link #put(byte[])} will block until we have less than
3767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * this amount of audio left to play back.
3867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     */
3967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private static final long MAX_UNCONSUMED_AUDIO_MS = 500;
4067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
4167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    /**
4267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * Guards accesses to mDataBufferList and mUnconsumedBytes.
4367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     */
4467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private final Lock mListLock = new ReentrantLock();
4567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private final Condition mReadReady = mListLock.newCondition();
4667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private final Condition mNotFull = mListLock.newCondition();
4767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
4867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    // Guarded by mListLock.
4967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>();
5067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    // Guarded by mListLock.
5167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private int mUnconsumedBytes;
5267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
5367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    /*
5467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * While mStopped and mIsError can be written from any thread, mDone is written
5567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * only from the synthesis thread. All three variables are read from the
5667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     * audio playback thread.
5767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath     */
5867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private volatile boolean mStopped;
5967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private volatile boolean mDone;
6067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private volatile boolean mIsError;
6167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
6267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private final BlockingAudioTrack mAudioTrack;
6367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private final EventLogger mLogger;
6467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
6567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
6667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    SynthesisPlaybackQueueItem(int streamType, int sampleRate,
6767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            int audioFormat, int channelCount,
6867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            float volume, float pan, UtteranceProgressDispatcher dispatcher,
6967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            Object callerIdentity, EventLogger logger) {
7067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        super(dispatcher, callerIdentity);
7167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
7267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mUnconsumedBytes = 0;
7367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
7467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mStopped = false;
7567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mDone = false;
7667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mIsError = false;
7767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
7867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mAudioTrack = new BlockingAudioTrack(streamType, sampleRate, audioFormat,
7967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                channelCount, volume, pan);
8067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mLogger = logger;
8167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
8267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
8367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
8467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    @Override
8567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    public void run() {
8667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        final UtteranceProgressDispatcher dispatcher = getDispatcher();
8767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        dispatcher.dispatchOnStart();
8867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
8967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
9067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mAudioTrack.init();
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
11267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        if (mIsError) {
11367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            dispatcher.dispatchOnError();
11467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        } else {
11567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            dispatcher.dispatchOnDone();
11667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
11767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
11867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mLogger.onWriteData();
11967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
12067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
12167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    @Override
12267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    void stop(boolean isError) {
12367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        try {
12467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.lock();
12567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
12667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Update our internal state.
12767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mStopped = true;
12867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mIsError = isError;
12967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
13067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Wake up the audio playback thread if it was waiting on take().
13167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // take() will return null since mStopped was true, and will then
13267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // break out of the data write loop.
13367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mReadReady.signal();
13467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
13567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Wake up the synthesis thread if it was waiting on put(). Its
13667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // buffers will no longer be copied since mStopped is true. The
13767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // PlaybackSynthesisCallback that this synthesis corresponds to
13867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // would also have been stopped, and so all calls to
13967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Callback.onDataAvailable( ) will return errors too.
14067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mNotFull.signal();
14167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        } finally {
14267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.unlock();
14367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
14467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
14567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        // Stop the underlying audio track. This will stop sending
14667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        // data to the mixer and discard any pending buffers that the
14767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        // track holds.
14867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        mAudioTrack.stop();
14967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
15067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
15167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    void done() {
15267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        try {
15367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.lock();
15467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
15567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Update state.
15667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mDone = true;
15767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
15867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Unblocks the audio playback thread if it was waiting on take()
15967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // after having consumed all available buffers. It will then return
16067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // null and leave the write loop.
16167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mReadReady.signal();
16267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
16367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Just so that engines that try to queue buffers after
16467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // calling done() don't block the synthesis thread forever. Ideally
16567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // this should be called from the same thread as put() is, and hence
16667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // this call should be pointless.
16767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mNotFull.signal();
16867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        } finally {
16967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.unlock();
17067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
17167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
17267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
17367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
17467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    void put(byte[] buffer) throws InterruptedException {
17567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        try {
17667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.lock();
17767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            long unconsumedAudioMs = 0;
17867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
17967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            while ((unconsumedAudioMs = mAudioTrack.getAudioLengthMs(mUnconsumedBytes)) >
18067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                    MAX_UNCONSUMED_AUDIO_MS && !mStopped) {
18167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                mNotFull.await();
18267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            }
18367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
18467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Don't bother queueing the buffer if we've stopped. The playback thread
18567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // would have woken up when stop() is called (if it was blocked) and will
18667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // proceed to leave the write loop since take() will return null when
18767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // stopped.
18867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            if (mStopped) {
18967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                return;
19067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            }
19167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
19267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mDataBufferList.add(new ListEntry(buffer));
19367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mUnconsumedBytes += buffer.length;
19467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mReadReady.signal();
19567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        } finally {
19667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.unlock();
19767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
19867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
19967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
20067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    private byte[] take() throws InterruptedException {
20167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        try {
20267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.lock();
20367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
20467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Block if there are no available buffers, and stop() has not
20567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // been called and done() has not been called.
20667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            while (mDataBufferList.size() == 0 && !mStopped && !mDone) {
20767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                mReadReady.await();
20867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            }
20967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
21067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // If stopped, return null so that we can exit the playback loop
21167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // as soon as possible.
21267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            if (mStopped) {
21367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                return null;
21467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            }
21567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
21667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Remove the first entry from the queue.
21767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            ListEntry entry = mDataBufferList.poll();
21867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
21967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // This is the normal playback loop exit case, when done() was
22067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // called. (mDone will be true at this point).
22167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            if (entry == null) {
22267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath                return null;
22367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            }
22467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
22567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mUnconsumedBytes -= entry.mBytes.length;
22667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Unblock the waiting writer. We use signal() and not signalAll()
22767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // because there will only be one thread waiting on this (the
22867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            // Synthesis thread).
22967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mNotFull.signal();
23067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
23167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            return entry.mBytes;
23267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        } finally {
23367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mListLock.unlock();
23467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
23567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
23667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
23767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    static final class ListEntry {
23867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        final byte[] mBytes;
23967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
24067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        ListEntry(byte[] bytes) {
24167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath            mBytes = bytes;
24267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath        }
24367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath    }
24467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath}
24567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath
246