AudioPlaybackHandler.java revision 4924fe38675f0bf69bb0c16fc059ffa1606807ce
18d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath/*
28d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * Copyright (C) 2011 The Android Open Source Project
38d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath *
48d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * Licensed under the Apache License, Version 2.0 (the "License"); you may not
58d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * use this file except in compliance with the License. You may obtain a copy of
68d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * the License at
78d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath *
88d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * http://www.apache.org/licenses/LICENSE-2.0
98d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath *
108d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * Unless required by applicable law or agreed to in writing, software
118d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
128d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
138d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * License for the specific language governing permissions and limitations under
148d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * the License.
158d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath */
168d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamathpackage android.speech.tts;
178d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
188d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamathimport android.media.AudioFormat;
198d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamathimport android.media.AudioTrack;
208d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamathimport android.util.Log;
218d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
224924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamathimport java.util.Iterator;
234924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamathimport java.util.concurrent.PriorityBlockingQueue;
244924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamathimport java.util.concurrent.atomic.AtomicLong;
254924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
264924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamathclass AudioPlaybackHandler {
278d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static final String TAG = "TTS.AudioPlaybackHandler";
288d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static final boolean DBG = false;
298d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
308d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
318d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
328d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static final int SYNTHESIS_START = 1;
338d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static final int SYNTHESIS_DATA_AVAILABLE = 2;
348d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static final int SYNTHESIS_COMPLETE_DATA_AVAILABLE = 3;
358d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static final int SYNTHESIS_DONE = 4;
368d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
378d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static final int PLAY_AUDIO = 5;
388d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static final int PLAY_SILENCE = 6;
398d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
404924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private static final int SHUTDOWN = -1;
414924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
424924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private static final int DEFAULT_PRIORITY = 1;
434924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private static final int HIGH_PRIORITY = 0;
444924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
454924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private final PriorityBlockingQueue<ListEntry> mQueue =
464924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            new PriorityBlockingQueue<ListEntry>();
474924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private final Thread mHandlerThread;
484924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
494924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private final Object mStateLock = new Object();
504924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
514924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    // Accessed by multiple threads, synchronized by "mStateLock".
524924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private MessageParams mCurrentParams = null;
538d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // Used only for book keeping and error detection.
544924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private volatile SynthesisMessageParams mLastSynthesisRequest = null;
554924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    // Used to order incoming messages in our priority queue.
564924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private final AtomicLong mSequenceIdCtr = new AtomicLong(0);
578d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
588d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
594924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    AudioPlaybackHandler() {
604924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        mHandlerThread = new Thread(new MessageLoop(), "TTS.AudioPlaybackThread");
614924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    }
628d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
634924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    public void start() {
644924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        mHandlerThread.start();
658d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
668d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
678d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    /**
688d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath     * Stops all synthesis for a given {@code token}. If the current token
698d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath     * is currently being processed, an effort will be made to stop it but
708d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath     * that is not guaranteed.
718d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath     */
728d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    synchronized public void stop(MessageParams token) {
734924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        if (token == null) {
744924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            return;
754924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        }
764924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
774924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        removeMessages(token);
788d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
798d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (token.getType() == MessageParams.TYPE_SYNTHESIS) {
804924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            mQueue.add(new ListEntry(SYNTHESIS_DONE, token, HIGH_PRIORITY));
814924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        } else  {
824924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            MessageParams current = getCurrentParams();
834924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
844924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            if (current != null) {
854924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                if (token.getType() == MessageParams.TYPE_AUDIO) {
864924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                    ((AudioMessageParams) current).getPlayer().stop();
874924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                } else if (token.getType() == MessageParams.TYPE_SILENCE) {
884924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                    ((SilenceMessageParams) current).getConditionVariable().open();
894924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                }
908d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            }
918d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
928d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
938d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
944924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    synchronized public void removePlaybackItems(String callingApp) {
954924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        removeMessages(callingApp);
964924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        stop(getCurrentParams());
974924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    }
984924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
994924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    synchronized public void removeAllItems() {
1004924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        removeAllMessages();
1014924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        stop(getCurrentParams());
1024924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    }
1034924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
1048d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    /**
1058d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath     * Shut down the audio playback thread.
1068d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath     */
1078d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    synchronized public void quit() {
1084924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        stop(getCurrentParams());
1094924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        mQueue.add(new ListEntry(SHUTDOWN, null, HIGH_PRIORITY));
1108d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
1118d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1128d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    void enqueueSynthesisStart(SynthesisMessageParams token) {
1134924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        mQueue.add(new ListEntry(SYNTHESIS_START, token));
1148d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
1158d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1168d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    void enqueueSynthesisDataAvailable(SynthesisMessageParams token) {
1174924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        mQueue.add(new ListEntry(SYNTHESIS_DATA_AVAILABLE, token));
1188d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
1198d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1208d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    void enqueueSynthesisCompleteDataAvailable(SynthesisMessageParams token) {
1214924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        mQueue.add(new ListEntry(SYNTHESIS_COMPLETE_DATA_AVAILABLE, token));
1228d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
1238d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1248d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    void enqueueSynthesisDone(SynthesisMessageParams token) {
1254924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        mQueue.add(new ListEntry(SYNTHESIS_DONE, token));
1268d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
1278d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1288d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    void enqueueAudio(AudioMessageParams token) {
1294924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        mQueue.add(new ListEntry(PLAY_AUDIO, token));
1308d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
1318d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1328d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    void enqueueSilence(SilenceMessageParams token) {
1334924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        mQueue.add(new ListEntry(PLAY_SILENCE, token));
1348d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
1358d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1368d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // -----------------------------------------
1378d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // End of public API methods.
1388d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // -----------------------------------------
1398d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
1404924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    // -----------------------------------------
1414924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    // Methods for managing the message queue.
1424924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    // -----------------------------------------
1434924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
1444924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    /*
1454924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     * The MessageLoop is a handler like implementation that
1464924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     * processes messages from a priority queue.
1474924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     */
1484924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private final class MessageLoop implements Runnable {
1494924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        @Override
1504924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        public void run() {
1514924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            while (true) {
1524924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                ListEntry entry = null;
1534924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                try {
1544924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                    entry = mQueue.take();
1554924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                } catch (InterruptedException ie) {
1564924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                    return;
1574924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                }
1584924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
1594924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                if (entry.mWhat == SHUTDOWN) {
1604924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                    if (DBG) Log.d(TAG, "MessageLoop : Shutting down");
1614924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                    return;
1624924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                }
1634924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
1644924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                if (DBG) {
1654924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                    Log.d(TAG, "MessageLoop : Handling message :" + entry.mWhat
1664924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                            + " ,seqId : " + entry.mSequenceId);
1674924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                }
1684924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
1694924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                setCurrentParams(entry.mMessage);
1704924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                handleMessage(entry);
1714924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                setCurrentParams(null);
1724924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            }
1734924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        }
1744924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    }
1754924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
1764924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    /*
1774924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     * Remove all messages from the queue that contain the supplied token.
1784924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     * Note that the Iterator is thread safe, and other methods can safely
1794924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     * continue adding to the queue at this point.
1804924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     */
1814924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    synchronized private void removeMessages(MessageParams token) {
1824924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        if (token == null) {
1834924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            return;
1844924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        }
1854924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
1864924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        Iterator<ListEntry> it = mQueue.iterator();
1874924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
1884924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        while (it.hasNext()) {
1894924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            final ListEntry current = it.next();
1904924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            if (current.mMessage == token) {
1914924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                it.remove();
1924924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            }
1934924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        }
1944924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    }
1954924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
1964924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    /*
1974924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     * Atomically clear the queue of all messages.
1984924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     */
1994924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    synchronized private void removeAllMessages() {
2004924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        mQueue.clear();
2014924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    }
2024924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2034924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    /*
2044924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     * Remove all messages that originate from a given calling app.
2054924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     */
2064924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    synchronized private void removeMessages(String callingApp) {
2074924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        Iterator<ListEntry> it = mQueue.iterator();
2084924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2094924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        while (it.hasNext()) {
2104924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            final ListEntry current = it.next();
2114924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            // The null check is to prevent us from removing control messages,
2124924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            // such as a shutdown message.
2134924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            if (current.mMessage != null &&
2144924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                    callingApp.equals(current.mMessage.getCallingApp())) {
2154924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                it.remove();
2164924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            }
2174924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        }
2184924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    }
2194924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2204924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    /*
2214924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     * An element of our priority queue of messages. Each message has a priority,
2224924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     * and a sequence id (defined by the order of enqueue calls). Among messages
2234924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     * with the same priority, messages that were received earlier win out.
2244924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath     */
2254924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private final class ListEntry implements Comparable<ListEntry> {
2264924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        final int mWhat;
2274924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        final MessageParams mMessage;
2284924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        final int mPriority;
2294924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        final long mSequenceId;
2304924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2314924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        private ListEntry(int what, MessageParams message) {
2324924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            this(what, message, DEFAULT_PRIORITY);
2334924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        }
2344924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2354924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        private ListEntry(int what, MessageParams message, int priority) {
2364924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            mWhat = what;
2374924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            mMessage = message;
2384924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            mPriority = priority;
2394924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            mSequenceId = mSequenceIdCtr.incrementAndGet();
2404924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        }
2414924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2424924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        @Override
2434924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        public int compareTo(ListEntry that) {
2444924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            if (that == this) {
2454924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                return 0;
2464924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            }
2474924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2484924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            // Note that this is always 0, 1 or -1.
2494924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            int priorityDiff = mPriority - that.mPriority;
2504924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            if (priorityDiff == 0) {
2514924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                // The == case cannot occur.
2524924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                return (mSequenceId < that.mSequenceId) ? -1 : 1;
2534924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            }
2544924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2554924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            return priorityDiff;
2564924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        }
2574924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    }
2584924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2594924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private void setCurrentParams(MessageParams p) {
2604924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        synchronized (mStateLock) {
2614924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            mCurrentParams = p;
2624924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        }
2634924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    }
2644924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2654924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private MessageParams getCurrentParams() {
2664924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        synchronized (mStateLock) {
2674924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            return mCurrentParams;
2684924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        }
2694924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    }
2704924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2714924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    // -----------------------------------------
2724924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    // Methods for dealing with individual messages, the methods
2734924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    // below do the actual work.
2744924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    // -----------------------------------------
2754924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2764924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private void handleMessage(ListEntry entry) {
2774924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        final MessageParams msg = entry.mMessage;
2784924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        if (entry.mWhat == SYNTHESIS_START) {
2794924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            handleSynthesisStart(msg);
2804924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        } else if (entry.mWhat == SYNTHESIS_DATA_AVAILABLE) {
2814924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            handleSynthesisDataAvailable(msg);
2824924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        } else if (entry.mWhat == SYNTHESIS_DONE) {
2834924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            handleSynthesisDone(msg);
2844924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        } else if (entry.mWhat == SYNTHESIS_COMPLETE_DATA_AVAILABLE) {
2854924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            handleSynthesisCompleteDataAvailable(msg);
2864924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        } else if (entry.mWhat == PLAY_AUDIO) {
2874924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            handleAudio(msg);
2884924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        } else if (entry.mWhat == PLAY_SILENCE) {
2894924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath            handleSilence(msg);
2904924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        }
2914924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    }
2924924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
2938d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // Currently implemented as blocking the audio playback thread for the
2948d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // specified duration. If a call to stop() is made, the thread
2958d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // unblocks.
2964924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private void handleSilence(MessageParams msg) {
2978d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (DBG) Log.d(TAG, "handleSilence()");
2984924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        SilenceMessageParams params = (SilenceMessageParams) msg;
2998d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (params.getSilenceDurationMs() > 0) {
3008d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            params.getConditionVariable().block(params.getSilenceDurationMs());
3018d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
3028d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        params.getDispatcher().dispatchUtteranceCompleted();
3038d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (DBG) Log.d(TAG, "handleSilence() done.");
3048d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
3058d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3068d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // Plays back audio from a given URI. No TTS engine involvement here.
3074924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private void handleAudio(MessageParams msg) {
3088d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (DBG) Log.d(TAG, "handleAudio()");
3094924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        AudioMessageParams params = (AudioMessageParams) msg;
3108d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        // Note that the BlockingMediaPlayer spawns a separate thread.
3118d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        //
3128d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        // TODO: This can be avoided.
3138d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        params.getPlayer().startAndWait();
3148d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        params.getDispatcher().dispatchUtteranceCompleted();
3158d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (DBG) Log.d(TAG, "handleAudio() done.");
3168d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
3178d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3188d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // Denotes the start of a new synthesis request. We create a new
3198d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // audio track, and prepare it for incoming data.
3208d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    //
3218d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // Note that since all TTS synthesis happens on a single thread, we
3228d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // should ALWAYS see the following order :
3238d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    //
3248d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // handleSynthesisStart -> handleSynthesisDataAvailable(*) -> handleSynthesisDone
3258d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // OR
3268d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // handleSynthesisCompleteDataAvailable.
3274924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private void handleSynthesisStart(MessageParams msg) {
3288d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (DBG) Log.d(TAG, "handleSynthesisStart()");
3294924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        final SynthesisMessageParams param = (SynthesisMessageParams) msg;
3308d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3318d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        // Oops, looks like the engine forgot to call done(). We go through
3328d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        // extra trouble to clean the data to prevent the AudioTrack resources
3338d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        // from being leaked.
3348d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (mLastSynthesisRequest != null) {
3358d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            Log.w(TAG, "Error : Missing call to done() for request : " +
3368d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                    mLastSynthesisRequest);
3378d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            handleSynthesisDone(mLastSynthesisRequest);
3388d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
3398d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3408d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        mLastSynthesisRequest = param;
3418d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3428d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        // Create the audio track.
3438d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        final AudioTrack audioTrack = createStreamingAudioTrack(
3448d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                param.mStreamType, param.mSampleRateInHz, param.mAudioFormat,
3458d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                param.mChannelCount, param.mVolume, param.mPan);
3468d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3474924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        if (DBG) Log.d(TAG, "Created audio track [" + audioTrack.hashCode() + "]");
3484924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath
3498d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        param.setAudioTrack(audioTrack);
3508d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
3518d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3528d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // More data available to be flushed to the audio track.
3534924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private void handleSynthesisDataAvailable(MessageParams msg) {
3544924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        final SynthesisMessageParams param = (SynthesisMessageParams) msg;
3558d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (param.getAudioTrack() == null) {
3568d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            Log.w(TAG, "Error : null audio track in handleDataAvailable.");
3578d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            return;
3588d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
3598d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3608d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (param != mLastSynthesisRequest) {
3618d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            Log.e(TAG, "Call to dataAvailable without done() / start()");
3628d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            return;
3638d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
3648d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3658d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        final AudioTrack audioTrack = param.getAudioTrack();
3664924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        final SynthesisMessageParams.ListEntry bufferCopy = param.getNextBuffer();
3678d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3688d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (bufferCopy == null) {
3698d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            Log.e(TAG, "No buffers available to play.");
3708d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            return;
3718d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
3728d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3738d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        int playState = audioTrack.getPlayState();
3748d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (playState == AudioTrack.PLAYSTATE_STOPPED) {
3758d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (DBG) Log.d(TAG, "AudioTrack stopped, restarting : " + audioTrack.hashCode());
3768d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            audioTrack.play();
3778d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
3788d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        int count = 0;
3798d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        while (count < bufferCopy.mLength) {
3808d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            // Note that we don't take bufferCopy.mOffset into account because
3818d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            // it is guaranteed to be 0.
3828d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mLength);
3838d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (written <= 0) {
3848d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                break;
3858d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            }
3868d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            count += written;
3878d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
3888d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
3898d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3904924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private void handleSynthesisDone(MessageParams msg) {
3914924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        final SynthesisMessageParams params = (SynthesisMessageParams) msg;
3928d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        handleSynthesisDone(params);
3938d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
3948d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
3958d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // Flush all remaining data to the audio track, stop it and release
3968d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    // all it's resources.
3978d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private void handleSynthesisDone(SynthesisMessageParams params) {
3988d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (DBG) Log.d(TAG, "handleSynthesisDone()");
3998d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        final AudioTrack audioTrack = params.getAudioTrack();
4008d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4018d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        try {
4028d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (audioTrack != null) {
4038d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                audioTrack.flush();
4048d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                audioTrack.stop();
4054924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath                if (DBG) Log.d(TAG, "Releasing audio track [" + audioTrack.hashCode() + "]");
4068d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                audioTrack.release();
4078d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            }
4088d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        } finally {
4098d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            params.setAudioTrack(null);
4108d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            params.getDispatcher().dispatchUtteranceCompleted();
4118d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            mLastSynthesisRequest = null;
4128d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
4138d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
4148d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4154924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath    private void handleSynthesisCompleteDataAvailable(MessageParams msg) {
4164924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        final SynthesisMessageParams params = (SynthesisMessageParams) msg;
4178d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (DBG) Log.d(TAG, "completeAudioAvailable(" + params + ")");
4188d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4198d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        // Channel config and bytes per frame are checked before
4208d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        // this message is sent.
4218d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        int channelConfig = AudioPlaybackHandler.getChannelConfig(params.mChannelCount);
4228d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(params.mAudioFormat);
4238d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4244924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath        SynthesisMessageParams.ListEntry entry = params.getNextBuffer();
4258d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4268d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (entry == null) {
4278d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            Log.w(TAG, "completeDataAvailable : No buffers available to play.");
4288d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            return;
4298d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
4308d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4318d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        final AudioTrack audioTrack = new AudioTrack(params.mStreamType, params.mSampleRateInHz,
4328d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                channelConfig, params.mAudioFormat, entry.mLength, AudioTrack.MODE_STATIC);
4338d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4348d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        // So that handleDone can access this correctly.
4358d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        params.mAudioTrack = audioTrack;
4368d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4378d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        try {
4388d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            audioTrack.write(entry.mBytes, entry.mOffset, entry.mLength);
4398d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            setupVolume(audioTrack, params.mVolume, params.mPan);
4408d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            audioTrack.play();
4418d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            blockUntilDone(audioTrack, bytesPerFrame, entry.mLength);
4428d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (DBG) Log.d(TAG, "Wrote data to audio track successfully : " + entry.mLength);
4438d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        } catch (IllegalStateException ex) {
4448d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            Log.e(TAG, "Playback error", ex);
4458d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        } finally {
4468d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            handleSynthesisDone(msg);
4478d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
4488d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
4498d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4508d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4518d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) {
4528d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        int lengthInFrames = length / bytesPerFrame;
4538d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        int currentPosition = 0;
4548d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
4558d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) /
4568d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                    audioTrack.getSampleRate();
4578d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            audioTrack.getPlayState();
4588d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," +
4598d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                    " Playback position : " + currentPosition);
4608d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            try {
4618d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                Thread.sleep(estimatedTimeMs);
4628d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            } catch (InterruptedException ie) {
4638d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                break;
4648d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            }
4658d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
4668d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
4678d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4688d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static AudioTrack createStreamingAudioTrack(int streamType, int sampleRateInHz,
4698d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            int audioFormat, int channelCount, float volume, float pan) {
4708d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        int channelConfig = getChannelConfig(channelCount);
4718d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4728d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        int minBufferSizeInBytes
4738d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
4748d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
4758d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4768d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        AudioTrack audioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig,
4778d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath                audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
4788d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
4798d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            Log.w(TAG, "Unable to create audio track.");
4808d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            audioTrack.release();
4818d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            return null;
4828d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
4838d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4848d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        setupVolume(audioTrack, volume, pan);
4858d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        return audioTrack;
4868d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
4878d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4888d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    static int getChannelConfig(int channelCount) {
4898d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (channelCount == 1) {
4908d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            return AudioFormat.CHANNEL_OUT_MONO;
4918d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        } else if (channelCount == 2){
4928d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            return AudioFormat.CHANNEL_OUT_STEREO;
4938d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
4948d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4958d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        return 0;
4968d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
4978d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
4988d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    static int getBytesPerFrame(int audioFormat) {
4998d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) {
5008d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            return 1;
5018d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
5028d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            return 2;
5038d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
5048d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
5058d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        return -1;
5068d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
5078d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
5088d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static void setupVolume(AudioTrack audioTrack, float volume, float pan) {
5098d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        float vol = clip(volume, 0.0f, 1.0f);
5108d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        float panning = clip(pan, -1.0f, 1.0f);
5118d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        float volLeft = vol;
5128d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        float volRight = vol;
5138d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (panning > 0.0f) {
5148d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            volLeft *= (1.0f - panning);
5158d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        } else if (panning < 0.0f) {
5168d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            volRight *= (1.0f + panning);
5178d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
5188d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight);
5198d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        if (audioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) {
5208d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath            Log.e(TAG, "Failed to set volume");
5218d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        }
5228d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
5238d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
5248d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    private static float clip(float value, float min, float max) {
5258d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath        return value > max ? max : (value < min ? min : value);
5268d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath    }
5278d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath
5288d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath}
529