Home | History | Annotate | Download | only in tts
      1 /*
      2  * Copyright (C) 2013 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.os.SystemClock;
     19 
     20 /**
     21  * Base class for storing data about a given speech synthesis request to the
     22  * event logs. The data that is logged depends on actual implementation. Note
     23  * that {@link AbstractEventLogger#onAudioDataWritten()} and
     24  * {@link AbstractEventLogger#onEngineComplete()} must be called from a single
     25  * thread (usually the audio playback thread}.
     26  */
     27 abstract class AbstractEventLogger {
     28     protected final String mServiceApp;
     29     protected final int mCallerUid;
     30     protected final int mCallerPid;
     31     protected final long mReceivedTime;
     32     protected long mPlaybackStartTime = -1;
     33 
     34     private volatile long mRequestProcessingStartTime = -1;
     35     private volatile long mEngineStartTime = -1;
     36     private volatile long mEngineCompleteTime = -1;
     37 
     38     private boolean mLogWritten = false;
     39 
     40     AbstractEventLogger(int callerUid, int callerPid, String serviceApp) {
     41         mCallerUid = callerUid;
     42         mCallerPid = callerPid;
     43         mServiceApp = serviceApp;
     44         mReceivedTime = SystemClock.elapsedRealtime();
     45     }
     46 
     47     /**
     48      * Notifies the logger that this request has been selected from
     49      * the processing queue for processing. Engine latency / total time
     50      * is measured from this baseline.
     51      */
     52     public void onRequestProcessingStart() {
     53         mRequestProcessingStartTime = SystemClock.elapsedRealtime();
     54     }
     55 
     56     /**
     57      * Notifies the logger that a chunk of data has been received from
     58      * the engine. Might be called multiple times.
     59      */
     60     public void onEngineDataReceived() {
     61         if (mEngineStartTime == -1) {
     62             mEngineStartTime = SystemClock.elapsedRealtime();
     63         }
     64     }
     65 
     66     /**
     67      * Notifies the logger that the engine has finished processing data.
     68      * Will be called exactly once.
     69      */
     70     public void onEngineComplete() {
     71         mEngineCompleteTime = SystemClock.elapsedRealtime();
     72     }
     73 
     74     /**
     75      * Notifies the logger that audio playback has started for some section
     76      * of the synthesis. This is normally some amount of time after the engine
     77      * has synthesized data and varies depending on utterances and
     78      * other audio currently in the queue.
     79      */
     80     public void onAudioDataWritten() {
     81         // For now, keep track of only the first chunk of audio
     82         // that was played.
     83         if (mPlaybackStartTime == -1) {
     84             mPlaybackStartTime = SystemClock.elapsedRealtime();
     85         }
     86     }
     87 
     88     /**
     89      * Notifies the logger that the current synthesis has completed.
     90      * All available data is not logged.
     91      */
     92     public void onCompleted(int statusCode) {
     93         if (mLogWritten) {
     94             return;
     95         } else {
     96             mLogWritten = true;
     97         }
     98 
     99         long completionTime = SystemClock.elapsedRealtime();
    100 
    101         // We don't report latency for stopped syntheses because their overall
    102         // total time spent will be inaccurate (will not correlate with
    103         // the length of the utterance).
    104 
    105         // onAudioDataWritten() should normally always be called, and hence mPlaybackStartTime
    106         // should be set, if an error does not occur.
    107         if (statusCode != TextToSpeech.SUCCESS
    108                 || mPlaybackStartTime == -1 || mEngineCompleteTime == -1) {
    109             logFailure(statusCode);
    110             return;
    111         }
    112 
    113         final long audioLatency = mPlaybackStartTime - mReceivedTime;
    114         final long engineLatency = mEngineStartTime - mRequestProcessingStartTime;
    115         final long engineTotal = mEngineCompleteTime - mRequestProcessingStartTime;
    116         logSuccess(audioLatency, engineLatency, engineTotal);
    117     }
    118 
    119     protected abstract void logFailure(int statusCode);
    120     protected abstract void logSuccess(long audioLatency, long engineLatency,
    121             long engineTotal);
    122 
    123 
    124 }
    125