• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
EventLogger(SynthesisRequest request, String callingApp, String serviceApp)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      */
onRequestProcessingStart()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      */
onEngineDataReceived()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      */
onEngineComplete()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      */
onPlaybackStart()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      */
onStopped()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      */
onError()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      */
onWriteData()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      */
getUtteranceLength()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      */
getLocaleString()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