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.util.Log; 198d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 204924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamathimport java.util.Iterator; 2167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamathimport java.util.concurrent.LinkedBlockingQueue; 224924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 234924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamathclass AudioPlaybackHandler { 248d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath private static final String TAG = "TTS.AudioPlaybackHandler"; 258d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath private static final boolean DBG = false; 268d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 2767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath private final LinkedBlockingQueue<PlaybackQueueItem> mQueue = 2867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath new LinkedBlockingQueue<PlaybackQueueItem>(); 294924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath private final Thread mHandlerThread; 304924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 3167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath private volatile PlaybackQueueItem mCurrentWorkItem = null; 328d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 334924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath AudioPlaybackHandler() { 344924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mHandlerThread = new Thread(new MessageLoop(), "TTS.AudioPlaybackThread"); 354924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 368d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 374924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath public void start() { 384924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mHandlerThread.start(); 398d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 408d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 4167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath private void stop(PlaybackQueueItem item) { 4267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath if (item == null) { 434924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath return; 444924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 454924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 46fc4b2890378eb1b6e0b11d60d703eb6854268064Przemyslaw Szczepaniak item.stop(TextToSpeech.STOPPED); 4767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath } 488d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 4967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath public void enqueue(PlaybackQueueItem item) { 5067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath try { 5167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath mQueue.put(item); 5267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath } catch (InterruptedException ie) { 5367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath // This exception will never be thrown, since we allow our queue 5467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath // to be have an unbounded size. put() will therefore never block. 558d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 568d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 578d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 5867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath public void stopForApp(Object callerIdentity) { 5967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath if (DBG) Log.d(TAG, "Removing all callback items for : " + callerIdentity); 6067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath removeWorkItemsFor(callerIdentity); 61be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath 6267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath final PlaybackQueueItem current = mCurrentWorkItem; 63492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (current != null && (current.getCallerIdentity() == callerIdentity)) { 64be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath stop(current); 65be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath } 664924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 674924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 6867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath public void stop() { 6967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath if (DBG) Log.d(TAG, "Stopping all items"); 704924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath removeAllMessages(); 7140f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath 7267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath stop(mCurrentWorkItem); 734924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 744924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 758d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath /** 76c34f76fe89b5a31d01d63067c2f24b9a6a76df18Narayan Kamath * @return false iff the queue is empty and no queue item is currently 77c34f76fe89b5a31d01d63067c2f24b9a6a76df18Narayan Kamath * being handled, true otherwise. 78c34f76fe89b5a31d01d63067c2f24b9a6a76df18Narayan Kamath */ 79c34f76fe89b5a31d01d63067c2f24b9a6a76df18Narayan Kamath public boolean isSpeaking() { 8067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath return (mQueue.peek() != null) || (mCurrentWorkItem != null); 81c34f76fe89b5a31d01d63067c2f24b9a6a76df18Narayan Kamath } 82c34f76fe89b5a31d01d63067c2f24b9a6a76df18Narayan Kamath 83c34f76fe89b5a31d01d63067c2f24b9a6a76df18Narayan Kamath /** 848d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath * Shut down the audio playback thread. 858d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath */ 8667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath public void quit() { 87be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath removeAllMessages(); 8867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath stop(mCurrentWorkItem); 8967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath mHandlerThread.interrupt(); 904924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 914924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 924924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath /* 934924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath * Atomically clear the queue of all messages. 944924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath */ 9567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath private void removeAllMessages() { 964924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mQueue.clear(); 974924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 984924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 994924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath /* 1004924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath * Remove all messages that originate from a given calling app. 1014924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath */ 10267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath private void removeWorkItemsFor(Object callerIdentity) { 10367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath Iterator<PlaybackQueueItem> it = mQueue.iterator(); 1044924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 1054924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath while (it.hasNext()) { 10667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath final PlaybackQueueItem item = it.next(); 10767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath if (item.getCallerIdentity() == callerIdentity) { 1084924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath it.remove(); 109a81d17b7959a67cbf12b389938dd2cd731893475Kazuhiro Inaba stop(item); 1104924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1114924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1124924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath } 1134924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 1144924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath /* 11567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * The MessageLoop is a handler like implementation that 11667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath * processes messages from a priority queue. 1174924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath */ 11867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath private final class MessageLoop implements Runnable { 1194924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath @Override 12067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath public void run() { 12167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath while (true) { 12267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath PlaybackQueueItem item = null; 12367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath try { 12467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath item = mQueue.take(); 12567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath } catch (InterruptedException ie) { 12667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath if (DBG) Log.d(TAG, "MessageLoop : Shutting down (interrupted)"); 12767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath return; 12869bc1b2696dde849102f0ac8071999843d01b8d1Narayan Kamath } 129673f360b0e22a8591f515cba7a90d5cfcfad81a7Narayan Kamath 13067ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath // If stop() or stopForApp() are called between mQueue.take() 13167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath // returning and mCurrentWorkItem being set, the current work item 13267ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath // will be run anyway. 1338d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 13467ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath mCurrentWorkItem = item; 13567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath item.run(); 13667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath mCurrentWorkItem = null; 13767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath } 1388d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 1398d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 1408d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 1418d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath} 142