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.content.Context;
19 import android.media.AudioManager;
20 import android.media.MediaPlayer;
21 import android.net.Uri;
22 import android.os.ConditionVariable;
23 import android.speech.tts.TextToSpeechService.AudioOutputParams;
24 import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
25 import android.util.Log;
26 
27 class AudioPlaybackQueueItem extends PlaybackQueueItem {
28     private static final String TAG = "TTS.AudioQueueItem";
29 
30     private final Context mContext;
31     private final Uri mUri;
32     private final AudioOutputParams mAudioParams;
33 
34     private final ConditionVariable mDone;
35     private MediaPlayer mPlayer;
36     private volatile boolean mFinished;
37 
AudioPlaybackQueueItem(UtteranceProgressDispatcher dispatcher, Object callerIdentity, Context context, Uri uri, AudioOutputParams audioParams)38     AudioPlaybackQueueItem(UtteranceProgressDispatcher dispatcher,
39             Object callerIdentity,
40             Context context, Uri uri, AudioOutputParams audioParams) {
41         super(dispatcher, callerIdentity);
42 
43         mContext = context;
44         mUri = uri;
45         mAudioParams = audioParams;
46 
47         mDone = new ConditionVariable();
48         mPlayer = null;
49         mFinished = false;
50     }
51     @Override
run()52     public void run() {
53         final UtteranceProgressDispatcher dispatcher = getDispatcher();
54 
55         dispatcher.dispatchOnStart();
56 
57         int sessionId = mAudioParams.mSessionId;
58         mPlayer = MediaPlayer.create(
59                 mContext, mUri, null, mAudioParams.mAudioAttributes,
60                 sessionId > 0 ? sessionId : AudioManager.AUDIO_SESSION_ID_GENERATE);
61         if (mPlayer == null) {
62             dispatcher.dispatchOnError(TextToSpeech.ERROR_OUTPUT);
63             return;
64         }
65 
66         try {
67             mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
68                 @Override
69                 public boolean onError(MediaPlayer mp, int what, int extra) {
70                     Log.w(TAG, "Audio playback error: " + what + ", " + extra);
71                     mDone.open();
72                     return true;
73                 }
74             });
75             mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
76                 @Override
77                 public void onCompletion(MediaPlayer mp) {
78                     mFinished = true;
79                     mDone.open();
80                 }
81             });
82 
83             setupVolume(mPlayer, mAudioParams.mVolume, mAudioParams.mPan);
84             mPlayer.start();
85             mDone.block();
86             finish();
87         } catch (IllegalArgumentException ex) {
88             Log.w(TAG, "MediaPlayer failed", ex);
89             mDone.open();
90         }
91 
92         if (mFinished) {
93             dispatcher.dispatchOnSuccess();
94         } else {
95             dispatcher.dispatchOnStop();
96         }
97     }
98 
setupVolume(MediaPlayer player, float volume, float pan)99     private static void setupVolume(MediaPlayer player, float volume, float pan) {
100         final float vol = clip(volume, 0.0f, 1.0f);
101         final float panning = clip(pan, -1.0f, 1.0f);
102 
103         float volLeft = vol, volRight = vol;
104         if (panning > 0.0f) {
105             volLeft *= (1.0f - panning);
106         } else if (panning < 0.0f) {
107             volRight *= (1.0f + panning);
108         }
109         player.setVolume(volLeft, volRight);
110     }
111 
clip(float value, float min, float max)112     private static final float clip(float value, float min, float max) {
113         return value < min ? min : (value < max ? value : max);
114     }
115 
finish()116     private void finish() {
117         try {
118             mPlayer.stop();
119         } catch (IllegalStateException ex) {
120             // Do nothing, the player is already stopped
121         }
122         mPlayer.release();
123     }
124 
125     @Override
stop(int errorCode)126     void stop(int errorCode) {
127         mDone.open();
128     }
129 }
130