PlaybackSynthesisCallback.java revision c3da8818f0598b3ab2cd6f4168349da6d0f72cb1
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
188d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamathimport android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
1950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.util.Log;
2050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
2150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert/**
2250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Speech synthesis request that plays the audio as it is received.
2350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */
24e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamathclass PlaybackSynthesisCallback extends AbstractSynthesisCallback {
2550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
2650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final String TAG = "PlaybackSynthesisRequest";
2750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final boolean DBG = false;
2850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
2950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
3050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
3150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    /**
3250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * Audio stream type. Must be one of the STREAM_ contants defined in
3350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * {@link android.media.AudioManager}.
3450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     */
3550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private final int mStreamType;
3650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
3750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    /**
3850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * Volume, in the range [0.0f, 1.0f]. The default value is
3950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
4050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     */
4150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private final float mVolume;
4250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
4350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    /**
4450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * Left/right position of the audio, in the range [-1.0f, 1.0f].
4550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
4650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     */
4750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private final float mPan;
4850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
498d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    /**
508d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath     * Guards {@link #mAudioTrackHandler}, {@link #mToken} and {@link #mStopped}.
518d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath     */
5250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private final Object mStateLock = new Object();
538d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
548d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // Handler associated with a thread that plays back audio requests.
558d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private final AudioPlaybackHandler mAudioTrackHandler;
56c3da8818f0598b3ab2cd6f4168349da6d0f72cb1Narayan Kamath    // A request "token", which will be non null after start() has been called.
578d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private SynthesisMessageParams mToken = null;
588d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // Whether this request has been stopped. This is useful for keeping
598d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // track whether stop() has been called before start(). In all other cases,
608d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // a non-null value of mToken will provide the same information.
6150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private boolean mStopped = false;
6250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
638d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private volatile boolean mDone = false;
648d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
658d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private final UtteranceCompletedDispatcher mDispatcher;
664924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private final String mCallingApp;
676dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath    private final EventLogger mLogger;
688d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
69e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath    PlaybackSynthesisCallback(int streamType, float volume, float pan,
704924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            AudioPlaybackHandler audioTrackHandler, UtteranceCompletedDispatcher dispatcher,
716dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            String callingApp, EventLogger logger) {
7250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        mStreamType = streamType;
7350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        mVolume = volume;
7450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        mPan = pan;
75c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath        mAudioTrackHandler = audioTrackHandler;
768d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        mDispatcher = dispatcher;
774924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        mCallingApp = callingApp;
786dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        mLogger = logger;
7950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
8050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
8150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
8250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    void stop() {
8350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) Log.d(TAG, "stop()");
848d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
856dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        // Note that mLogger.mError might be true too at this point.
866dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        mLogger.onStopped();
876dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath
8850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
896dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            if (mStopped) {
906dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                Log.w(TAG, "stop() called twice");
918d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                return;
928d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            }
936dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            // mToken will be null if the engine encounters
946dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            // an error before it called start().
956dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            if (mToken != null) {
966dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                mAudioTrackHandler.stop(mToken);
976dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                mToken = null;
986dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            } else {
996dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                // In all other cases, mAudioTrackHandler.stop() will
1006dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                // result in onComplete being called.
1016dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                mLogger.onWriteData();
1026dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            }
10350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            mStopped = true;
10450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
10550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
10650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
10771e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    @Override
10871e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    public int getMaxBufferSize() {
10971e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        // The AudioTrack buffer will be at least MIN_AUDIO_BUFFER_SIZE, so that should always be
11071e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        // a safe buffer size to pass in.
11171e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        return MIN_AUDIO_BUFFER_SIZE;
11271e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    }
11371e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert
114360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    @Override
115360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    boolean isDone() {
116360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert        return mDone;
117360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    }
118360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert
11950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
12050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int start(int sampleRateInHz, int audioFormat, int channelCount) {
12150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) {
12250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat
12350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                    + "," + channelCount + ")");
12450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
12550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
1268d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount);
1278d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (channelConfig == 0) {
1288d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            Log.e(TAG, "Unsupported number of channels :" + channelCount);
1298d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            return TextToSpeech.ERROR;
1308d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
1318d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
13250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
13350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            if (mStopped) {
1348d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                if (DBG) Log.d(TAG, "stop() called before start(), returning.");
13550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
13650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1378d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            SynthesisMessageParams params = new SynthesisMessageParams(
1388d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                    mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
1396dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                    mDispatcher, mCallingApp, mLogger);
1408d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mAudioTrackHandler.enqueueSynthesisStart(params);
14150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
1428d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mToken = params;
14350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
14450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
14550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        return TextToSpeech.SUCCESS;
14650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
14750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
14850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
14950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
15050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int audioAvailable(byte[] buffer, int offset, int length) {
15150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) {
15250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
15371e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert                    + offset + "," + length + ")");
15471e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        }
155c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath        if (length > getMaxBufferSize() || length <= 0) {
156c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath            throw new IllegalArgumentException("buffer is too large or of zero length (" +
157c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath                    + length + " bytes)");
15850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
1598d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
16050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
1618d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (mToken == null) {
16250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
16350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1648d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
165c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath            // Sigh, another copy.
166c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath            final byte[] bufferCopy = new byte[length];
167c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath            System.arraycopy(buffer, offset, bufferCopy, 0, length);
1688d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mToken.addBuffer(bufferCopy);
1698d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken);
17050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
1718d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1726dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        mLogger.onEngineDataReceived();
1736dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath
1748d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        return TextToSpeech.SUCCESS;
17550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
17650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
17750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
17850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int done() {
17950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) Log.d(TAG, "done()");
1808d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
18150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
1828d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (mDone) {
1838d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                Log.w(TAG, "Duplicate call to done()");
18450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
18550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1868d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1878d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mDone = true;
1888d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1898d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (mToken == null) {
19050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
19150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1928d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1938d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mAudioTrackHandler.enqueueSynthesisDone(mToken);
1946dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            mLogger.onEngineComplete();
19550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
19650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        return TextToSpeech.SUCCESS;
19750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
19871e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert
19971e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    @Override
200360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    public void error() {
2018d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (DBG) Log.d(TAG, "error() [will call stop]");
2026dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        // Currently, this call will not be logged if error( ) is called
2036dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        // before start.
2046dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        mLogger.onError();
2058d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        stop();
206360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    }
207360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert
20853f6f95308854e7da1fcd15081030d7de2fc5a7fNarayan Kamath}
209