blob: c99f2011f303b17b3347f6d41fd18267c19c8b2f [file] [log] [blame]
Bjorn Bringert50e657b2011-03-08 16:00:40 +00001/*
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 */
16package android.speech.tts;
17
Narayan Kamath754c72e2011-11-09 14:22:32 +000018import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
Bjorn Bringert50e657b2011-03-08 16:00:40 +000019import android.util.Log;
20
21/**
22 * Speech synthesis request that plays the audio as it is received.
23 */
Narayan Kamathe22b69a2011-06-08 11:41:47 +010024class PlaybackSynthesisCallback extends AbstractSynthesisCallback {
Bjorn Bringert50e657b2011-03-08 16:00:40 +000025
26 private static final String TAG = "PlaybackSynthesisRequest";
27 private static final boolean DBG = false;
28
29 private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
30
31 /**
32 * Audio stream type. Must be one of the STREAM_ contants defined in
33 * {@link android.media.AudioManager}.
34 */
35 private final int mStreamType;
36
37 /**
38 * Volume, in the range [0.0f, 1.0f]. The default value is
39 * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
40 */
41 private final float mVolume;
42
43 /**
44 * Left/right position of the audio, in the range [-1.0f, 1.0f].
45 * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
46 */
47 private final float mPan;
48
Narayan Kamath8d1fc242011-06-03 18:11:54 +010049 /**
Narayan Kamath67ae6bc2011-11-30 14:51:00 +000050 * Guards {@link #mAudioTrackHandler}, {@link #mItem} and {@link #mStopped}.
Narayan Kamath8d1fc242011-06-03 18:11:54 +010051 */
Bjorn Bringert50e657b2011-03-08 16:00:40 +000052 private final Object mStateLock = new Object();
Bjorn Bringert50e657b2011-03-08 16:00:40 +000053
Narayan Kamath8d1fc242011-06-03 18:11:54 +010054 // Handler associated with a thread that plays back audio requests.
55 private final AudioPlaybackHandler mAudioTrackHandler;
Narayan Kamathc3da8812011-07-01 10:13:54 +010056 // A request "token", which will be non null after start() has been called.
Narayan Kamath67ae6bc2011-11-30 14:51:00 +000057 private SynthesisPlaybackQueueItem mItem = null;
Narayan Kamath8d1fc242011-06-03 18:11:54 +010058 // Whether this request has been stopped. This is useful for keeping
59 // track whether stop() has been called before start(). In all other cases,
Narayan Kamath67ae6bc2011-11-30 14:51:00 +000060 // a non-null value of mItem will provide the same information.
Narayan Kamath8d1fc242011-06-03 18:11:54 +010061 private boolean mStopped = false;
62
63 private volatile boolean mDone = false;
64
Narayan Kamath754c72e2011-11-09 14:22:32 +000065 private final UtteranceProgressDispatcher mDispatcher;
Narayan Kamath492b7f02011-11-29 17:02:06 +000066 private final Object mCallerIdentity;
Narayan Kamath6dabb632011-07-08 12:13:03 +010067 private final EventLogger mLogger;
Narayan Kamath8d1fc242011-06-03 18:11:54 +010068
Narayan Kamathe22b69a2011-06-08 11:41:47 +010069 PlaybackSynthesisCallback(int streamType, float volume, float pan,
Narayan Kamath754c72e2011-11-09 14:22:32 +000070 AudioPlaybackHandler audioTrackHandler, UtteranceProgressDispatcher dispatcher,
Narayan Kamath492b7f02011-11-29 17:02:06 +000071 Object callerIdentity, EventLogger logger) {
Bjorn Bringert50e657b2011-03-08 16:00:40 +000072 mStreamType = streamType;
73 mVolume = volume;
74 mPan = pan;
Narayan Kamathc90f1c82011-05-24 11:39:43 +010075 mAudioTrackHandler = audioTrackHandler;
Narayan Kamath8d1fc242011-06-03 18:11:54 +010076 mDispatcher = dispatcher;
Narayan Kamath492b7f02011-11-29 17:02:06 +000077 mCallerIdentity = callerIdentity;
Narayan Kamath6dabb632011-07-08 12:13:03 +010078 mLogger = logger;
Bjorn Bringert50e657b2011-03-08 16:00:40 +000079 }
80
81 @Override
82 void stop() {
Narayan Kamath40f71f02011-11-23 16:42:53 +000083 stopImpl(false);
84 }
85
86 void stopImpl(boolean wasError) {
Bjorn Bringert50e657b2011-03-08 16:00:40 +000087 if (DBG) Log.d(TAG, "stop()");
Narayan Kamath8d1fc242011-06-03 18:11:54 +010088
Narayan Kamath6dabb632011-07-08 12:13:03 +010089 // Note that mLogger.mError might be true too at this point.
90 mLogger.onStopped();
91
Narayan Kamath67ae6bc2011-11-30 14:51:00 +000092 SynthesisPlaybackQueueItem item;
Bjorn Bringert50e657b2011-03-08 16:00:40 +000093 synchronized (mStateLock) {
Narayan Kamath6dabb632011-07-08 12:13:03 +010094 if (mStopped) {
95 Log.w(TAG, "stop() called twice");
Narayan Kamath8d1fc242011-06-03 18:11:54 +010096 return;
Narayan Kamathc90f1c82011-05-24 11:39:43 +010097 }
Narayan Kamathbe4ad4a2011-07-15 13:01:09 +010098
Narayan Kamath67ae6bc2011-11-30 14:51:00 +000099 item = mItem;
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100100 mStopped = true;
101 }
Narayan Kamath2a0518c2011-08-26 11:43:03 +0100102
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000103 if (item != null) {
Narayan Kamath2a0518c2011-08-26 11:43:03 +0100104 // This might result in the synthesis thread being woken up, at which
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000105 // point it will write an additional buffer to the item - but we
Narayan Kamath2a0518c2011-08-26 11:43:03 +0100106 // won't worry about that because the audio playback queue will be cleared
107 // soon after (see SynthHandler#stop(String).
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000108 item.stop(wasError);
Narayan Kamath40f71f02011-11-23 16:42:53 +0000109 } else {
110 // This happens when stop() or error() were called before start() was.
111
112 // In all other cases, mAudioTrackHandler.stop() will
113 // result in onSynthesisDone being called, and we will
114 // write data there.
115 mLogger.onWriteData();
116
117 if (wasError) {
118 // We have to dispatch the error ourselves.
119 mDispatcher.dispatchOnError();
120 }
Narayan Kamath2a0518c2011-08-26 11:43:03 +0100121 }
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000122 }
123
Bjorn Bringert71e0b482011-04-15 14:37:05 +0100124 @Override
125 public int getMaxBufferSize() {
126 // The AudioTrack buffer will be at least MIN_AUDIO_BUFFER_SIZE, so that should always be
127 // a safe buffer size to pass in.
128 return MIN_AUDIO_BUFFER_SIZE;
129 }
130
Bjorn Bringert360eb162011-04-19 09:20:35 +0100131 @Override
132 boolean isDone() {
133 return mDone;
134 }
135
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000136 @Override
137 public int start(int sampleRateInHz, int audioFormat, int channelCount) {
138 if (DBG) {
139 Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat
140 + "," + channelCount + ")");
141 }
142
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000143 int channelConfig = BlockingAudioTrack.getChannelConfig(channelCount);
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100144 if (channelConfig == 0) {
145 Log.e(TAG, "Unsupported number of channels :" + channelCount);
146 return TextToSpeech.ERROR;
147 }
148
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000149 synchronized (mStateLock) {
150 if (mStopped) {
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100151 if (DBG) Log.d(TAG, "stop() called before start(), returning.");
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000152 return TextToSpeech.ERROR;
153 }
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000154 SynthesisPlaybackQueueItem item = new SynthesisPlaybackQueueItem(
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100155 mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
Narayan Kamath492b7f02011-11-29 17:02:06 +0000156 mDispatcher, mCallerIdentity, mLogger);
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000157 mAudioTrackHandler.enqueue(item);
158 mItem = item;
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000159 }
160
161 return TextToSpeech.SUCCESS;
162 }
163
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000164
165 @Override
166 public int audioAvailable(byte[] buffer, int offset, int length) {
167 if (DBG) {
168 Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
Bjorn Bringert71e0b482011-04-15 14:37:05 +0100169 + offset + "," + length + ")");
170 }
Narayan Kamathc90f1c82011-05-24 11:39:43 +0100171 if (length > getMaxBufferSize() || length <= 0) {
172 throw new IllegalArgumentException("buffer is too large or of zero length (" +
173 + length + " bytes)");
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000174 }
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100175
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000176 SynthesisPlaybackQueueItem item = null;
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000177 synchronized (mStateLock) {
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000178 if (mItem == null || mStopped) {
Narayan Kamathc90f1c82011-05-24 11:39:43 +0100179 return TextToSpeech.ERROR;
180 }
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000181 item = mItem;
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000182 }
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100183
Narayan Kamath2a0518c2011-08-26 11:43:03 +0100184 // Sigh, another copy.
185 final byte[] bufferCopy = new byte[length];
186 System.arraycopy(buffer, offset, bufferCopy, 0, length);
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000187
188 // Might block on mItem.this, if there are too many buffers waiting to
Narayan Kamath2a0518c2011-08-26 11:43:03 +0100189 // be consumed.
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000190 try {
191 item.put(bufferCopy);
192 } catch (InterruptedException ie) {
193 return TextToSpeech.ERROR;
194 }
Narayan Kamath2a0518c2011-08-26 11:43:03 +0100195
Narayan Kamath6dabb632011-07-08 12:13:03 +0100196 mLogger.onEngineDataReceived();
197
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100198 return TextToSpeech.SUCCESS;
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000199 }
200
201 @Override
202 public int done() {
203 if (DBG) Log.d(TAG, "done()");
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100204
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000205 SynthesisPlaybackQueueItem item = null;
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000206 synchronized (mStateLock) {
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100207 if (mDone) {
208 Log.w(TAG, "Duplicate call to done()");
Narayan Kamathc90f1c82011-05-24 11:39:43 +0100209 return TextToSpeech.ERROR;
210 }
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100211
Bjorn Bringert360eb162011-04-19 09:20:35 +0100212 mDone = true;
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100213
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000214 if (mItem == null) {
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100215 return TextToSpeech.ERROR;
216 }
217
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000218 item = mItem;
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000219 }
Narayan Kamath2a0518c2011-08-26 11:43:03 +0100220
Narayan Kamath67ae6bc2011-11-30 14:51:00 +0000221 item.done();
Narayan Kamath2a0518c2011-08-26 11:43:03 +0100222 mLogger.onEngineComplete();
223
Bjorn Bringert50e657b2011-03-08 16:00:40 +0000224 return TextToSpeech.SUCCESS;
225 }
Bjorn Bringert71e0b482011-04-15 14:37:05 +0100226
227 @Override
Bjorn Bringert360eb162011-04-19 09:20:35 +0100228 public void error() {
Narayan Kamath8d1fc242011-06-03 18:11:54 +0100229 if (DBG) Log.d(TAG, "error() [will call stop]");
Narayan Kamath6dabb632011-07-08 12:13:03 +0100230 // Currently, this call will not be logged if error( ) is called
231 // before start.
232 mLogger.onError();
Narayan Kamath40f71f02011-11-23 16:42:53 +0000233 stopImpl(true);
Bjorn Bringert360eb162011-04-19 09:20:35 +0100234 }
235
Narayan Kamath53f6f952011-04-19 16:39:20 +0100236}