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 */
16package android.speech.tts;
17
18import android.content.Context;
19import android.media.AudioManager;
20import android.media.MediaPlayer;
21import android.net.Uri;
22import android.os.ConditionVariable;
23import android.speech.tts.TextToSpeechService.AudioOutputParams;
24import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
25import android.util.Log;
26
27class 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
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
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
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
112    private static final float clip(float value, float min, float max) {
113        return value < min ? min : (value < max ? value : max);
114    }
115
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
126    void stop(int errorCode) {
127        mDone.open();
128    }
129}
130