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.os.SystemClock;
     19 import android.text.TextUtils;
     20 
     21 /**
     22  * Writes data about a given speech synthesis request to the event logs.
     23  * The data that is logged includes the calling app, length of the utterance,
     24  * speech rate / pitch and the latency and overall time taken.
     25  *
     26  * Note that {@link EventLogger#onStopped()} and {@link EventLogger#onError()}
     27  * might be called from any thread, but on {@link EventLogger#onPlaybackStart()} and
     28  * {@link EventLogger#onComplete()} must be called from a single thread
     29  * (usually the audio playback thread}
     30  */
     31 class EventLogger {
     32     private final SynthesisRequest mRequest;
     33     private final String mCallingApp;
     34     private final String mServiceApp;
     35     private final long mReceivedTime;
     36     private long mPlaybackStartTime = -1;
     37     private volatile long mRequestProcessingStartTime = -1;
     38     private volatile long mEngineStartTime = -1;
     39     private volatile long mEngineCompleteTime = -1;
     40 
     41     private volatile boolean mError = false;
     42     private volatile boolean mStopped = false;
     43     private boolean mLogWritten = false;
     44 
     45     EventLogger(SynthesisRequest request, String callingApp,
     46             String serviceApp) {
     47         mRequest = request;
     48         mCallingApp = callingApp;
     49         mServiceApp = serviceApp;
     50         mReceivedTime = SystemClock.elapsedRealtime();
     51     }
     52 
     53     /**
     54      * Notifies the logger that this request has been selected from
     55      * the processing queue for processing. Engine latency / total time
     56      * is measured from this baseline.
     57      */
     58     public void onRequestProcessingStart() {
     59         mRequestProcessingStartTime = SystemClock.elapsedRealtime();
     60     }
     61 
     62     /**
     63      * Notifies the logger that a chunk of data has been received from
     64      * the engine. Might be called multiple times.
     65      */
     66     public void onEngineDataReceived() {
     67         if (mEngineStartTime == -1) {
     68             mEngineStartTime = SystemClock.elapsedRealtime();
     69         }
     70     }
     71 
     72     /**
     73      * Notifies the logger that the engine has finished processing data.
     74      * Will be called exactly once.
     75      */
     76     public void onEngineComplete() {
     77         mEngineCompleteTime = SystemClock.elapsedRealtime();
     78     }
     79 
     80     /**
     81      * Notifies the logger that audio playback has started for some section
     82      * of the synthesis. This is normally some amount of time after the engine
     83      * has synthesized data and varides depending on utterances and
     84      * other audio currently in the queue.
     85      */
     86     public void onPlaybackStart() {
     87         // For now, keep track of only the first chunk of audio
     88         // that was played.
     89         if (mPlaybackStartTime == -1) {
     90             mPlaybackStartTime = SystemClock.elapsedRealtime();
     91         }
     92     }
     93 
     94     /**
     95      * Notifies the logger that the current synthesis was stopped.
     96      * Latency numbers are not reported for stopped syntheses.
     97      */
     98     public void onStopped() {
     99         mStopped = false;
    100     }
    101 
    102     /**
    103      * Notifies the logger that the current synthesis resulted in
    104      * an error. This is logged using {@link EventLogTags#writeTtsSpeakFailure}.
    105      */
    106     public void onError() {
    107         mError = true;
    108     }
    109 
    110     /**
    111      * Notifies the logger that the current synthesis has completed.
    112      * All available data is not logged.
    113      */
    114     public void onWriteData() {
    115         if (mLogWritten) {
    116             return;
    117         } else {
    118             mLogWritten = true;
    119         }
    120 
    121         long completionTime = SystemClock.elapsedRealtime();
    122         // onPlaybackStart() should normally always be called if an
    123         // error does not occur.
    124         if (mError || mPlaybackStartTime == -1 || mEngineCompleteTime == -1) {
    125             EventLogTags.writeTtsSpeakFailure(mServiceApp, mCallingApp,
    126                     getUtteranceLength(), getLocaleString(),
    127                     mRequest.getSpeechRate(), mRequest.getPitch());
    128             return;
    129         }
    130 
    131         // We don't report stopped syntheses because their overall
    132         // total time spent will be innacurate (will not correlate with
    133         // the length of the utterance).
    134         if (mStopped) {
    135             return;
    136         }
    137 
    138         final long audioLatency = mPlaybackStartTime - mReceivedTime;
    139         final long engineLatency = mEngineStartTime - mRequestProcessingStartTime;
    140         final long engineTotal = mEngineCompleteTime - mRequestProcessingStartTime;
    141         EventLogTags.writeTtsSpeakSuccess(mServiceApp, mCallingApp,
    142                 getUtteranceLength(), getLocaleString(),
    143                 mRequest.getSpeechRate(), mRequest.getPitch(),
    144                 engineLatency, engineTotal, audioLatency);
    145     }
    146 
    147     /**
    148      * @return the length of the utterance for the given synthesis, 0
    149      *          if the utterance was {@code null}.
    150      */
    151     private int getUtteranceLength() {
    152         final String utterance = mRequest.getText();
    153         return utterance == null ? 0 : utterance.length();
    154     }
    155 
    156     /**
    157      * Returns a formatted locale string from the synthesis params of the
    158      * form lang-country-variant.
    159      */
    160     private String getLocaleString() {
    161         StringBuilder sb = new StringBuilder(mRequest.getLanguage());
    162         if (!TextUtils.isEmpty(mRequest.getCountry())) {
    163             sb.append('-');
    164             sb.append(mRequest.getCountry());
    165 
    166             if (!TextUtils.isEmpty(mRequest.getVariant())) {
    167                 sb.append('-');
    168                 sb.append(mRequest.getVariant());
    169             }
    170         }
    171 
    172         return sb.toString();
    173     }
    174 
    175 }
    176