FileDocCategorySizeDatePackage
PlaybackSynthesisCallback.javaAPI DocAndroid 5.1 API8920Thu Mar 12 22:22:10 GMT 2015android.speech.tts

PlaybackSynthesisCallback

public class PlaybackSynthesisCallback extends AbstractSynthesisCallback
Speech synthesis request that plays the audio as it is received.

Fields Summary
private static final String
TAG
private static final boolean
DBG
private static final int
MIN_AUDIO_BUFFER_SIZE
private final android.speech.tts.TextToSpeechService.AudioOutputParams
mAudioParams
private final Object
mStateLock
Guards {@link #mAudioTrackHandler}, {@link #mItem} and {@link #mStopped}.
private final AudioPlaybackHandler
mAudioTrackHandler
private SynthesisPlaybackQueueItem
mItem
private volatile boolean
mDone
protected int
mStatusCode
Status code of synthesis
private final android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher
mDispatcher
private final Object
mCallerIdentity
private final AbstractEventLogger
mLogger
Constructors Summary
PlaybackSynthesisCallback(android.speech.tts.TextToSpeechService.AudioOutputParams audioParams, AudioPlaybackHandler audioTrackHandler, android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher dispatcher, Object callerIdentity, AbstractEventLogger logger, boolean clientIsUsingV2)


       
               
                
        super(clientIsUsingV2);
        mAudioParams = audioParams;
        mAudioTrackHandler = audioTrackHandler;
        mDispatcher = dispatcher;
        mCallerIdentity = callerIdentity;
        mLogger = logger;
        mStatusCode = TextToSpeech.SUCCESS;
    
Methods Summary
public intaudioAvailable(byte[] buffer, int offset, int length)

        if (DBG) Log.d(TAG, "audioAvailable(byte[" + buffer.length + "]," + offset + "," + length
                + ")");

        if (length > getMaxBufferSize() || length <= 0) {
            throw new IllegalArgumentException("buffer is too large or of zero length (" +
                    + length + " bytes)");
        }

        SynthesisPlaybackQueueItem item = null;
        synchronized (mStateLock) {
            if (mItem == null) {
                mStatusCode = TextToSpeech.ERROR_OUTPUT;
                return TextToSpeech.ERROR;
            }
            if (mStatusCode != TextToSpeech.SUCCESS) {
                if (DBG) Log.d(TAG, "Error was raised");
                return TextToSpeech.ERROR;
            }
            if (mStatusCode == TextToSpeech.STOPPED) {
                return errorCodeOnStop();
            }
            item = mItem;
        }

        // Sigh, another copy.
        final byte[] bufferCopy = new byte[length];
        System.arraycopy(buffer, offset, bufferCopy, 0, length);

        // Might block on mItem.this, if there are too many buffers waiting to
        // be consumed.
        try {
            item.put(bufferCopy);
        } catch (InterruptedException ie) {
            synchronized (mStateLock) {
                mStatusCode = TextToSpeech.ERROR_OUTPUT;
                return TextToSpeech.ERROR;
            }
        }

        mLogger.onEngineDataReceived();
        return TextToSpeech.SUCCESS;
    
public intdone()

        if (DBG) Log.d(TAG, "done()");

        int statusCode = 0;
        SynthesisPlaybackQueueItem item = null;
        synchronized (mStateLock) {
            if (mDone) {
                Log.w(TAG, "Duplicate call to done()");
                // Not an error that would prevent synthesis. Hence no
                // setStatusCode
                return TextToSpeech.ERROR;
            }
            if (mStatusCode == TextToSpeech.STOPPED) {
                if (DBG) Log.d(TAG, "Request has been aborted.");
                return errorCodeOnStop();
            }
            mDone = true;

            if (mItem == null) {
                // .done() was called before .start. Treat it as successful synthesis
                // for a client, despite service bad implementation.
                Log.w(TAG, "done() was called before start() call");
                if (mStatusCode == TextToSpeech.SUCCESS) {
                    mDispatcher.dispatchOnSuccess();
                } else {
                    mDispatcher.dispatchOnError(mStatusCode);
                }
                mLogger.onEngineComplete();
                return TextToSpeech.ERROR;
            }

            item = mItem;
            statusCode = mStatusCode;
        }

        // Signal done or error to item
        if (statusCode == TextToSpeech.SUCCESS) {
            item.done();
        } else {
            item.stop(statusCode);
        }
        mLogger.onEngineComplete();
        return TextToSpeech.SUCCESS;
    
public voiderror(int errorCode)

        if (DBG) Log.d(TAG, "error() [will call stop]");
        synchronized (mStateLock) {
            if (mDone) {
                return;
            }
            mStatusCode = errorCode;
        }
    
public voiderror()

        error(TextToSpeech.ERROR_SYNTHESIS);
    
public intgetMaxBufferSize()

        // The AudioTrack buffer will be at least MIN_AUDIO_BUFFER_SIZE, so that should always be
        // a safe buffer size to pass in.
        return MIN_AUDIO_BUFFER_SIZE;
    
public booleanhasFinished()

        synchronized (mStateLock) {
            return mDone;
        }
    
public booleanhasStarted()

        synchronized (mStateLock) {
            return mItem != null;
        }
    
public intstart(int sampleRateInHz, int audioFormat, int channelCount)

        if (DBG) Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat + "," + channelCount
                + ")");

        int channelConfig = BlockingAudioTrack.getChannelConfig(channelCount);

        synchronized (mStateLock) {
            if (channelConfig == 0) {
                Log.e(TAG, "Unsupported number of channels :" + channelCount);
                mStatusCode = TextToSpeech.ERROR_OUTPUT;
                return TextToSpeech.ERROR;
            }
            if (mStatusCode == TextToSpeech.STOPPED) {
                if (DBG) Log.d(TAG, "stop() called before start(), returning.");
                return errorCodeOnStop();
            }
            if (mStatusCode != TextToSpeech.SUCCESS) {
                if (DBG) Log.d(TAG, "Error was raised");
                return TextToSpeech.ERROR;
            }
            if (mItem != null) {
                Log.e(TAG, "Start called twice");
                return TextToSpeech.ERROR;
            }
            SynthesisPlaybackQueueItem item = new SynthesisPlaybackQueueItem(
                    mAudioParams, sampleRateInHz, audioFormat, channelCount,
                    mDispatcher, mCallerIdentity, mLogger);
            mAudioTrackHandler.enqueue(item);
            mItem = item;
        }

        return TextToSpeech.SUCCESS;
    
voidstop()

        if (DBG) Log.d(TAG, "stop()");

        SynthesisPlaybackQueueItem item;
        synchronized (mStateLock) {
            if (mDone) {
                return;
            }
            if (mStatusCode == TextToSpeech.STOPPED) {
                Log.w(TAG, "stop() called twice");
                return;
            }

            item = mItem;
            mStatusCode = TextToSpeech.STOPPED;
        }

        if (item != null) {
            // This might result in the synthesis thread being woken up, at which
            // point it will write an additional buffer to the item - but we
            // won't worry about that because the audio playback queue will be cleared
            // soon after (see SynthHandler#stop(String).
            item.stop(TextToSpeech.STOPPED);
        } else {
            // This happens when stop() or error() were called before start() was.

            // In all other cases, mAudioTrackHandler.stop() will
            // result in onSynthesisDone being called, and we will
            // write data there.
            mLogger.onCompleted(TextToSpeech.STOPPED);
            mDispatcher.dispatchOnStop();
        }