PlaybackSynthesisCallback.java revision be4ad4ac66d6b4b878ed052975f7fb09af92c6d6
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            }
93be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath
946dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            // mToken will be null if the engine encounters
956dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            // an error before it called start().
96be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath            if (mToken == null) {
976dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                // In all other cases, mAudioTrackHandler.stop() will
986dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                // result in onComplete being called.
996dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                mLogger.onWriteData();
1006dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            }
10150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            mStopped = true;
10250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
10350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
10450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
10571e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    @Override
10671e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    public int getMaxBufferSize() {
10771e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        // The AudioTrack buffer will be at least MIN_AUDIO_BUFFER_SIZE, so that should always be
10871e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        // a safe buffer size to pass in.
10971e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        return MIN_AUDIO_BUFFER_SIZE;
11071e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    }
11171e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert
112360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    @Override
113360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    boolean isDone() {
114360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert        return mDone;
115360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    }
116360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert
11750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
11850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int start(int sampleRateInHz, int audioFormat, int channelCount) {
11950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) {
12050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat
12150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                    + "," + channelCount + ")");
12250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
12350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
1248d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount);
1258d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (channelConfig == 0) {
1268d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            Log.e(TAG, "Unsupported number of channels :" + channelCount);
1278d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            return TextToSpeech.ERROR;
1288d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
1298d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
13050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
13150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            if (mStopped) {
1328d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                if (DBG) Log.d(TAG, "stop() called before start(), returning.");
13350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
13450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1358d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            SynthesisMessageParams params = new SynthesisMessageParams(
1368d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                    mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
1376dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath                    mDispatcher, mCallingApp, mLogger);
1388d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mAudioTrackHandler.enqueueSynthesisStart(params);
13950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
1408d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mToken = params;
14150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
14250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
14350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        return TextToSpeech.SUCCESS;
14450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
14550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
14650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
14750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
14850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int audioAvailable(byte[] buffer, int offset, int length) {
14950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) {
15050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
15171e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert                    + offset + "," + length + ")");
15271e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        }
153c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath        if (length > getMaxBufferSize() || length <= 0) {
154c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath            throw new IllegalArgumentException("buffer is too large or of zero length (" +
155c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath                    + length + " bytes)");
15650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
1578d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
15850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
159be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath            if (mToken == null || mStopped) {
16050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
16150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1628d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
163c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath            // Sigh, another copy.
164c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath            final byte[] bufferCopy = new byte[length];
165c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath            System.arraycopy(buffer, offset, bufferCopy, 0, length);
1668d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mToken.addBuffer(bufferCopy);
1678d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken);
16850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
1698d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1706dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        mLogger.onEngineDataReceived();
1716dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath
1728d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        return TextToSpeech.SUCCESS;
17350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
17450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
17550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
17650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int done() {
17750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) Log.d(TAG, "done()");
1788d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
17950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
1808d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (mDone) {
1818d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                Log.w(TAG, "Duplicate call to done()");
18250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
18350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1848d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1858d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mDone = true;
1868d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1878d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (mToken == null) {
18850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
18950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1908d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1918d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mAudioTrackHandler.enqueueSynthesisDone(mToken);
1926dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath            mLogger.onEngineComplete();
19350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
19450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        return TextToSpeech.SUCCESS;
19550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
19671e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert
19771e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    @Override
198360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    public void error() {
1998d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (DBG) Log.d(TAG, "error() [will call stop]");
2006dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        // Currently, this call will not be logged if error( ) is called
2016dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        // before start.
2026dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath        mLogger.onError();
2038d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        stop();
204360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    }
205360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert
20653f6f95308854e7da1fcd15081030d7de2fc5a7fNarayan Kamath}
207