Home | History | Annotate | Download | only in tts
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 package android.speech.tts;
     17 
     18 import android.util.Log;
     19 
     20 import java.util.Iterator;
     21 import java.util.concurrent.LinkedBlockingQueue;
     22 
     23 class AudioPlaybackHandler {
     24     private static final String TAG = "TTS.AudioPlaybackHandler";
     25     private static final boolean DBG = false;
     26 
     27     private final LinkedBlockingQueue<PlaybackQueueItem> mQueue =
     28             new LinkedBlockingQueue<PlaybackQueueItem>();
     29     private final Thread mHandlerThread;
     30 
     31     private volatile PlaybackQueueItem mCurrentWorkItem = null;
     32 
     33     AudioPlaybackHandler() {
     34         mHandlerThread = new Thread(new MessageLoop(), "TTS.AudioPlaybackThread");
     35     }
     36 
     37     public void start() {
     38         mHandlerThread.start();
     39     }
     40 
     41     private void stop(PlaybackQueueItem item) {
     42         if (item == null) {
     43             return;
     44         }
     45 
     46         item.stop(TextToSpeech.STOPPED);
     47     }
     48 
     49     public void enqueue(PlaybackQueueItem item) {
     50         try {
     51             mQueue.put(item);
     52         } catch (InterruptedException ie) {
     53             // This exception will never be thrown, since we allow our queue
     54             // to be have an unbounded size. put() will therefore never block.
     55         }
     56     }
     57 
     58     public void stopForApp(Object callerIdentity) {
     59         if (DBG) Log.d(TAG, "Removing all callback items for : " + callerIdentity);
     60         removeWorkItemsFor(callerIdentity);
     61 
     62         final PlaybackQueueItem current = mCurrentWorkItem;
     63         if (current != null && (current.getCallerIdentity() == callerIdentity)) {
     64             stop(current);
     65         }
     66     }
     67 
     68     public void stop() {
     69         if (DBG) Log.d(TAG, "Stopping all items");
     70         removeAllMessages();
     71 
     72         stop(mCurrentWorkItem);
     73     }
     74 
     75     /**
     76      * @return false iff the queue is empty and no queue item is currently
     77      *        being handled, true otherwise.
     78      */
     79     public boolean isSpeaking() {
     80         return (mQueue.peek() != null) || (mCurrentWorkItem != null);
     81     }
     82 
     83     /**
     84      * Shut down the audio playback thread.
     85      */
     86     public void quit() {
     87         removeAllMessages();
     88         stop(mCurrentWorkItem);
     89         mHandlerThread.interrupt();
     90     }
     91 
     92     /*
     93      * Atomically clear the queue of all messages.
     94      */
     95     private void removeAllMessages() {
     96         mQueue.clear();
     97     }
     98 
     99     /*
    100      * Remove all messages that originate from a given calling app.
    101      */
    102     private void removeWorkItemsFor(Object callerIdentity) {
    103         Iterator<PlaybackQueueItem> it = mQueue.iterator();
    104 
    105         while (it.hasNext()) {
    106             final PlaybackQueueItem item = it.next();
    107             if (item.getCallerIdentity() == callerIdentity) {
    108                 it.remove();
    109                 stop(item);
    110             }
    111         }
    112     }
    113 
    114     /*
    115      * The MessageLoop is a handler like implementation that
    116      * processes messages from a priority queue.
    117      */
    118     private final class MessageLoop implements Runnable {
    119         @Override
    120         public void run() {
    121             while (true) {
    122                 PlaybackQueueItem item = null;
    123                 try {
    124                     item = mQueue.take();
    125                 } catch (InterruptedException ie) {
    126                     if (DBG) Log.d(TAG, "MessageLoop : Shutting down (interrupted)");
    127                     return;
    128                 }
    129 
    130                 // If stop() or stopForApp() are called between mQueue.take()
    131                 // returning and mCurrentWorkItem being set, the current work item
    132                 // will be run anyway.
    133 
    134                 mCurrentWorkItem = item;
    135                 item.run();
    136                 mCurrentWorkItem = null;
    137             }
    138         }
    139     }
    140 
    141 }
    142