FileDocCategorySizeDatePackage
SynthesisPlaybackQueueItem.javaAPI DocAndroid 5.1 API8346Thu Mar 12 22:22:10 GMT 2015android.speech.tts

SynthesisPlaybackQueueItem

public final class SynthesisPlaybackQueueItem extends PlaybackQueueItem
Manages the playback of a list of byte arrays representing audio data that are queued by the engine to an audio track.

Fields Summary
private static final String
TAG
private static final boolean
DBG
private static final long
MAX_UNCONSUMED_AUDIO_MS
Maximum length of audio we leave unconsumed by the audio track. Calls to {@link #put(byte[])} will block until we have less than this amount of audio left to play back.
private final Lock
mListLock
Guards accesses to mDataBufferList and mUnconsumedBytes.
private final Condition
mReadReady
private final Condition
mNotFull
private final LinkedList
mDataBufferList
private int
mUnconsumedBytes
private volatile boolean
mStopped
private volatile boolean
mDone
private volatile int
mStatusCode
private final BlockingAudioTrack
mAudioTrack
private final AbstractEventLogger
mLogger
Constructors Summary
SynthesisPlaybackQueueItem(android.speech.tts.TextToSpeechService.AudioOutputParams audioParams, int sampleRate, int audioFormat, int channelCount, android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher dispatcher, Object callerIdentity, AbstractEventLogger logger)


       
                 
                
        super(dispatcher, callerIdentity);

        mUnconsumedBytes = 0;

        mStopped = false;
        mDone = false;
        mStatusCode = TextToSpeech.SUCCESS;

        mAudioTrack = new BlockingAudioTrack(audioParams, sampleRate, audioFormat, channelCount);
        mLogger = logger;
    
Methods Summary
voiddone()

        try {
            mListLock.lock();

            // Update state.
            mDone = true;

            // Unblocks the audio playback thread if it was waiting on take()
            // after having consumed all available buffers. It will then return
            // null and leave the write loop.
            mReadReady.signal();

            // Just so that engines that try to queue buffers after
            // calling done() don't block the synthesis thread forever. Ideally
            // this should be called from the same thread as put() is, and hence
            // this call should be pointless.
            mNotFull.signal();
        } finally {
            mListLock.unlock();
        }
    
voidput(byte[] buffer)

        try {
            mListLock.lock();
            long unconsumedAudioMs = 0;

            while ((unconsumedAudioMs = mAudioTrack.getAudioLengthMs(mUnconsumedBytes)) >
                    MAX_UNCONSUMED_AUDIO_MS && !mStopped) {
                mNotFull.await();
            }

            // Don't bother queueing the buffer if we've stopped. The playback thread
            // would have woken up when stop() is called (if it was blocked) and will
            // proceed to leave the write loop since take() will return null when
            // stopped.
            if (mStopped) {
                return;
            }

            mDataBufferList.add(new ListEntry(buffer));
            mUnconsumedBytes += buffer.length;
            mReadReady.signal();
        } finally {
            mListLock.unlock();
        }
    
public voidrun()

        final UtteranceProgressDispatcher dispatcher = getDispatcher();
        dispatcher.dispatchOnStart();

        if (!mAudioTrack.init()) {
            dispatcher.dispatchOnError(TextToSpeech.ERROR_OUTPUT);
            return;
        }

        try {
            byte[] buffer = null;

            // take() will block until:
            //
            // (a) there is a buffer available to tread. In which case
            // a non null value is returned.
            // OR (b) stop() is called in which case it will return null.
            // OR (c) done() is called in which case it will return null.
            while ((buffer = take()) != null) {
                mAudioTrack.write(buffer);
                mLogger.onAudioDataWritten();
            }

        } catch (InterruptedException ie) {
            if (DBG) Log.d(TAG, "Interrupted waiting for buffers, cleaning up.");
        }

        mAudioTrack.waitAndRelease();

        if (mStatusCode == TextToSpeech.SUCCESS) {
            dispatcher.dispatchOnSuccess();
        } else if(mStatusCode == TextToSpeech.STOPPED) {
            dispatcher.dispatchOnStop();
        } else {
            dispatcher.dispatchOnError(mStatusCode);
        }

        mLogger.onCompleted(mStatusCode);
    
voidstop(int statusCode)

        try {
            mListLock.lock();

            // Update our internal state.
            mStopped = true;
            mStatusCode = statusCode;

            // Wake up the audio playback thread if it was waiting on take().
            // take() will return null since mStopped was true, and will then
            // break out of the data write loop.
            mReadReady.signal();

            // Wake up the synthesis thread if it was waiting on put(). Its
            // buffers will no longer be copied since mStopped is true. The
            // PlaybackSynthesisCallback that this synthesis corresponds to
            // would also have been stopped, and so all calls to
            // Callback.onDataAvailable( ) will return errors too.
            mNotFull.signal();
        } finally {
            mListLock.unlock();
        }

        // Stop the underlying audio track. This will stop sending
        // data to the mixer and discard any pending buffers that the
        // track holds.
        mAudioTrack.stop();
    
private byte[]take()

        try {
            mListLock.lock();

            // Block if there are no available buffers, and stop() has not
            // been called and done() has not been called.
            while (mDataBufferList.size() == 0 && !mStopped && !mDone) {
                mReadReady.await();
            }

            // If stopped, return null so that we can exit the playback loop
            // as soon as possible.
            if (mStopped) {
                return null;
            }

            // Remove the first entry from the queue.
            ListEntry entry = mDataBufferList.poll();

            // This is the normal playback loop exit case, when done() was
            // called. (mDone will be true at this point).
            if (entry == null) {
                return null;
            }

            mUnconsumedBytes -= entry.mBytes.length;
            // Unblock the waiting writer. We use signal() and not signalAll()
            // because there will only be one thread waiting on this (the
            // Synthesis thread).
            mNotFull.signal();

            return entry.mBytes;
        } finally {
            mListLock.unlock();
        }