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