AudioPlaybackHandler.java revision 963719869967cc257e666809aeb9bff3f25117ed
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 49abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath private volatile MessageParams mCurrentParams = null; 508d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // Used only for book keeping and error detection. 514924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private volatile SynthesisMessageParams mLastSynthesisRequest = null; 524924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // Used to order incoming messages in our priority queue. 534924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private final AtomicLong mSequenceIdCtr = new AtomicLong(0); 548d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 558d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 564924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath AudioPlaybackHandler() { 574924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mHandlerThread = new Thread(new MessageLoop(), "TTS.AudioPlaybackThread"); 584924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 598d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 604924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath public void start() { 614924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mHandlerThread.start(); 628d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 638d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 648d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath /** 658d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * Stops all synthesis for a given {@code token}. If the current token 668d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * is currently being processed, an effort will be made to stop it but 678d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * that is not guaranteed. 688d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath */ 698d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath synchronized public void stop(MessageParams token) { 704924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (token == null) { 714924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath return; 724924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 734924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 744924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath removeMessages(token); 758d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 768d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (token.getType() == MessageParams.TYPE_SYNTHESIS) { 77963719869967cc257e666809aeb9bff3f25117edNarayan Kamath AudioTrack current = ((SynthesisMessageParams) token).getAudioTrack(); 78963719869967cc257e666809aeb9bff3f25117edNarayan Kamath if (current != null) { 79963719869967cc257e666809aeb9bff3f25117edNarayan Kamath // Stop the current audio track if it's still playing. 80963719869967cc257e666809aeb9bff3f25117edNarayan Kamath // The audio track is thread safe in this regard. 81963719869967cc257e666809aeb9bff3f25117edNarayan Kamath current.stop(); 82963719869967cc257e666809aeb9bff3f25117edNarayan Kamath } 834924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mQueue.add(new ListEntry(SYNTHESIS_DONE, token, HIGH_PRIORITY)); 844924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } else { 85abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath final MessageParams current = getCurrentParams(); 864924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 874924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (current != null) { 884924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (token.getType() == MessageParams.TYPE_AUDIO) { 894924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath ((AudioMessageParams) current).getPlayer().stop(); 904924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } else if (token.getType() == MessageParams.TYPE_SILENCE) { 914924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath ((SilenceMessageParams) current).getConditionVariable().open(); 924924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 938d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 948d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 958d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 968d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 974924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath synchronized public void removePlaybackItems(String callingApp) { 984924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath removeMessages(callingApp); 994924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath stop(getCurrentParams()); 1004924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1014924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 1024924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath synchronized public void removeAllItems() { 1034924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath removeAllMessages(); 1044924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath stop(getCurrentParams()); 1054924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1064924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 1078d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath /** 1088d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * Shut down the audio playback thread. 1098d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath */ 1108d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath synchronized public void quit() { 1114924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath stop(getCurrentParams()); 1124924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mQueue.add(new ListEntry(SHUTDOWN, null, HIGH_PRIORITY)); 1138d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 1148d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 1158d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath void enqueueSynthesisStart(SynthesisMessageParams token) { 1164924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mQueue.add(new ListEntry(SYNTHESIS_START, token)); 1178d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 1188d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 1198d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath void enqueueSynthesisDataAvailable(SynthesisMessageParams token) { 1204924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mQueue.add(new ListEntry(SYNTHESIS_DATA_AVAILABLE, token)); 1218d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 1228d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 1238d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath void enqueueSynthesisCompleteDataAvailable(SynthesisMessageParams token) { 1244924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mQueue.add(new ListEntry(SYNTHESIS_COMPLETE_DATA_AVAILABLE, token)); 1258d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 1268d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 1278d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath void enqueueSynthesisDone(SynthesisMessageParams token) { 1284924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mQueue.add(new ListEntry(SYNTHESIS_DONE, token)); 1298d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 1308d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 1318d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath void enqueueAudio(AudioMessageParams token) { 1324924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mQueue.add(new ListEntry(PLAY_AUDIO, token)); 1338d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 1348d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 1358d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath void enqueueSilence(SilenceMessageParams token) { 1364924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mQueue.add(new ListEntry(PLAY_SILENCE, token)); 1378d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 1388d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 1398d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // ----------------------------------------- 1408d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // End of public API methods. 1418d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // ----------------------------------------- 1428d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 1434924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // ----------------------------------------- 1444924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // Methods for managing the message queue. 1454924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // ----------------------------------------- 1464924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 1474924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath /* 1484924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath * The MessageLoop is a handler like implementation that 1494924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath * processes messages from a priority queue. 1504924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath */ 1514924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private final class MessageLoop implements Runnable { 1524924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath @Override 1534924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath public void run() { 1544924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath while (true) { 1554924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath ListEntry entry = null; 1564924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath try { 1574924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath entry = mQueue.take(); 1584924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } catch (InterruptedException ie) { 1594924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath return; 1604924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1614924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 1624924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (entry.mWhat == SHUTDOWN) { 1634924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (DBG) Log.d(TAG, "MessageLoop : Shutting down"); 1644924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath return; 1654924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1664924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 1674924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (DBG) { 1684924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath Log.d(TAG, "MessageLoop : Handling message :" + entry.mWhat 1694924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath + " ,seqId : " + entry.mSequenceId); 1704924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1714924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 1724924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath setCurrentParams(entry.mMessage); 1734924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath handleMessage(entry); 1744924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath setCurrentParams(null); 1754924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1764924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1774924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1784924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 1794924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath /* 1804924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath * Remove all messages from the queue that contain the supplied token. 1814924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath * Note that the Iterator is thread safe, and other methods can safely 1824924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath * continue adding to the queue at this point. 1834924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath */ 1844924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath synchronized private void removeMessages(MessageParams token) { 1854924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (token == null) { 1864924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath return; 1874924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1884924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 1894924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath Iterator<ListEntry> it = mQueue.iterator(); 1904924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 1914924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath while (it.hasNext()) { 1924924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath final ListEntry current = it.next(); 1934924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (current.mMessage == token) { 1944924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath it.remove(); 1954924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1964924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1974924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1984924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 1994924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath /* 2004924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath * Atomically clear the queue of all messages. 2014924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath */ 2024924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath synchronized private void removeAllMessages() { 2034924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mQueue.clear(); 2044924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2054924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2064924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath /* 2074924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath * Remove all messages that originate from a given calling app. 2084924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath */ 2094924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath synchronized private void removeMessages(String callingApp) { 2104924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath Iterator<ListEntry> it = mQueue.iterator(); 2114924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2124924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath while (it.hasNext()) { 2134924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath final ListEntry current = it.next(); 2144924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // The null check is to prevent us from removing control messages, 2154924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // such as a shutdown message. 2164924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (current.mMessage != null && 2174924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath callingApp.equals(current.mMessage.getCallingApp())) { 2184924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath it.remove(); 2194924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2204924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2214924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2224924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2234924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath /* 2244924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath * An element of our priority queue of messages. Each message has a priority, 2254924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath * and a sequence id (defined by the order of enqueue calls). Among messages 2264924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath * with the same priority, messages that were received earlier win out. 2274924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath */ 2284924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private final class ListEntry implements Comparable<ListEntry> { 2294924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath final int mWhat; 2304924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath final MessageParams mMessage; 2314924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath final int mPriority; 2324924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath final long mSequenceId; 2334924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2344924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private ListEntry(int what, MessageParams message) { 2354924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath this(what, message, DEFAULT_PRIORITY); 2364924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2374924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2384924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private ListEntry(int what, MessageParams message, int priority) { 2394924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mWhat = what; 2404924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mMessage = message; 2414924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mPriority = priority; 2424924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mSequenceId = mSequenceIdCtr.incrementAndGet(); 2434924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2444924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2454924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath @Override 2464924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath public int compareTo(ListEntry that) { 2474924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (that == this) { 2484924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath return 0; 2494924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2504924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2514924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // Note that this is always 0, 1 or -1. 2524924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath int priorityDiff = mPriority - that.mPriority; 2534924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (priorityDiff == 0) { 2544924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // The == case cannot occur. 2554924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath return (mSequenceId < that.mSequenceId) ? -1 : 1; 2564924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2574924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2584924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath return priorityDiff; 2594924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2604924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2614924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2624924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private void setCurrentParams(MessageParams p) { 263abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath mCurrentParams = p; 2644924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2654924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2664924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private MessageParams getCurrentParams() { 267abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return mCurrentParams; 2684924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2694924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2704924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // ----------------------------------------- 2714924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // Methods for dealing with individual messages, the methods 2724924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // below do the actual work. 2734924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // ----------------------------------------- 2744924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2754924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private void handleMessage(ListEntry entry) { 2764924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath final MessageParams msg = entry.mMessage; 2774924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (entry.mWhat == SYNTHESIS_START) { 2784924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath handleSynthesisStart(msg); 2794924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } else if (entry.mWhat == SYNTHESIS_DATA_AVAILABLE) { 2804924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath handleSynthesisDataAvailable(msg); 2814924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } else if (entry.mWhat == SYNTHESIS_DONE) { 2824924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath handleSynthesisDone(msg); 2834924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } else if (entry.mWhat == SYNTHESIS_COMPLETE_DATA_AVAILABLE) { 2844924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath handleSynthesisCompleteDataAvailable(msg); 2854924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } else if (entry.mWhat == PLAY_AUDIO) { 2864924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath handleAudio(msg); 2874924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } else if (entry.mWhat == PLAY_SILENCE) { 2884924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath handleSilence(msg); 2894924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2904924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 2914924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 2928d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // Currently implemented as blocking the audio playback thread for the 2938d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // specified duration. If a call to stop() is made, the thread 2948d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // unblocks. 2954924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private void handleSilence(MessageParams msg) { 2968d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (DBG) Log.d(TAG, "handleSilence()"); 2974924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath SilenceMessageParams params = (SilenceMessageParams) msg; 2988d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (params.getSilenceDurationMs() > 0) { 2998d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath params.getConditionVariable().block(params.getSilenceDurationMs()); 3008d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3018d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath params.getDispatcher().dispatchUtteranceCompleted(); 3028d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (DBG) Log.d(TAG, "handleSilence() done."); 3038d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3048d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3058d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // Plays back audio from a given URI. No TTS engine involvement here. 3064924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private void handleAudio(MessageParams msg) { 3078d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (DBG) Log.d(TAG, "handleAudio()"); 3084924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath AudioMessageParams params = (AudioMessageParams) msg; 3098d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // Note that the BlockingMediaPlayer spawns a separate thread. 3108d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // 3118d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // TODO: This can be avoided. 3128d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath params.getPlayer().startAndWait(); 3138d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath params.getDispatcher().dispatchUtteranceCompleted(); 3148d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (DBG) Log.d(TAG, "handleAudio() done."); 3158d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3168d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3178d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // Denotes the start of a new synthesis request. We create a new 3188d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // audio track, and prepare it for incoming data. 3198d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // 3208d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // Note that since all TTS synthesis happens on a single thread, we 3218d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // should ALWAYS see the following order : 3228d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // 3238d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // handleSynthesisStart -> handleSynthesisDataAvailable(*) -> handleSynthesisDone 3248d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // OR 3258d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // handleSynthesisCompleteDataAvailable. 3264924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private void handleSynthesisStart(MessageParams msg) { 3278d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (DBG) Log.d(TAG, "handleSynthesisStart()"); 3284924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath final SynthesisMessageParams param = (SynthesisMessageParams) msg; 3298d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3308d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // Oops, looks like the engine forgot to call done(). We go through 3318d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // extra trouble to clean the data to prevent the AudioTrack resources 3328d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // from being leaked. 3338d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (mLastSynthesisRequest != null) { 3348d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath Log.w(TAG, "Error : Missing call to done() for request : " + 3358d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath mLastSynthesisRequest); 3368d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath handleSynthesisDone(mLastSynthesisRequest); 3378d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3388d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3398d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath mLastSynthesisRequest = param; 3408d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3418d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // Create the audio track. 3428d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath final AudioTrack audioTrack = createStreamingAudioTrack( 3438d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath param.mStreamType, param.mSampleRateInHz, param.mAudioFormat, 3448d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath param.mChannelCount, param.mVolume, param.mPan); 3458d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3464924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (DBG) Log.d(TAG, "Created audio track [" + audioTrack.hashCode() + "]"); 3474924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 3488d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath param.setAudioTrack(audioTrack); 3498d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3508d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3518d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // More data available to be flushed to the audio track. 3524924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private void handleSynthesisDataAvailable(MessageParams msg) { 3534924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath final SynthesisMessageParams param = (SynthesisMessageParams) msg; 3548d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (param.getAudioTrack() == null) { 3558d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath Log.w(TAG, "Error : null audio track in handleDataAvailable."); 3568d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath return; 3578d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3588d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3598d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (param != mLastSynthesisRequest) { 3608d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath Log.e(TAG, "Call to dataAvailable without done() / start()"); 3618d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath return; 3628d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3638d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3648d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath final AudioTrack audioTrack = param.getAudioTrack(); 3654924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath final SynthesisMessageParams.ListEntry bufferCopy = param.getNextBuffer(); 3668d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3678d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (bufferCopy == null) { 3688d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath Log.e(TAG, "No buffers available to play."); 3698d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath return; 3708d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3718d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3728d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath int playState = audioTrack.getPlayState(); 3738d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (playState == AudioTrack.PLAYSTATE_STOPPED) { 3748d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (DBG) Log.d(TAG, "AudioTrack stopped, restarting : " + audioTrack.hashCode()); 3758d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath audioTrack.play(); 3768d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3778d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath int count = 0; 3788d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath while (count < bufferCopy.mLength) { 3798d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // Note that we don't take bufferCopy.mOffset into account because 3808d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // it is guaranteed to be 0. 3818d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mLength); 3828d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (written <= 0) { 3838d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath break; 3848d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3858d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath count += written; 3868d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3878d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3888d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3894924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private void handleSynthesisDone(MessageParams msg) { 3904924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath final SynthesisMessageParams params = (SynthesisMessageParams) msg; 3918d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath handleSynthesisDone(params); 3928d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3938d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3948d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // Flush all remaining data to the audio track, stop it and release 3958d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // all it's resources. 3968d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath private void handleSynthesisDone(SynthesisMessageParams params) { 3978d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (DBG) Log.d(TAG, "handleSynthesisDone()"); 3988d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath final AudioTrack audioTrack = params.getAudioTrack(); 3998d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 4008d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath try { 4018d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (audioTrack != null) { 4024924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath if (DBG) Log.d(TAG, "Releasing audio track [" + audioTrack.hashCode() + "]"); 403963719869967cc257e666809aeb9bff3f25117edNarayan Kamath // The last call to AudioTrack.write( ) will return only after 404963719869967cc257e666809aeb9bff3f25117edNarayan Kamath // all data from the audioTrack has been sent to the mixer, so 405963719869967cc257e666809aeb9bff3f25117edNarayan Kamath // it's safe to release at this point. 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