blob: 3cf60dd8f9920ff1822bc1f08f0d6abd3ef4ca8e [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
18import android.content.Context;
19import android.media.MediaPlayer;
20import android.net.Uri;
21import android.os.ConditionVariable;
22import android.os.Handler;
23import android.os.HandlerThread;
24import android.os.Looper;
25import android.util.Log;
26
27/**
28 * A media player that allows blocking to wait for it to finish.
29 */
30class BlockingMediaPlayer {
31
32 private static final String TAG = "BlockMediaPlayer";
33
34 private static final String MEDIA_PLAYER_THREAD_NAME = "TTS-MediaPlayer";
35
36 private final Context mContext;
37 private final Uri mUri;
38 private final int mStreamType;
39 private final ConditionVariable mDone;
40 // Only accessed on the Handler thread
41 private MediaPlayer mPlayer;
42 private volatile boolean mFinished;
43
44 /**
45 * Creates a new blocking media player.
46 * Creating a blocking media player is a cheap operation.
47 *
48 * @param context
49 * @param uri
50 * @param streamType
51 */
52 public BlockingMediaPlayer(Context context, Uri uri, int streamType) {
53 mContext = context;
54 mUri = uri;
55 mStreamType = streamType;
56 mDone = new ConditionVariable();
57
58 }
59
60 /**
61 * Starts playback and waits for it to finish.
62 * Can be called from any thread.
63 *
64 * @return {@code true} if the playback finished normally, {@code false} if the playback
65 * failed or {@link #stop} was called before the playback finished.
66 */
67 public boolean startAndWait() {
68 HandlerThread thread = new HandlerThread(MEDIA_PLAYER_THREAD_NAME);
69 thread.start();
70 Handler handler = new Handler(thread.getLooper());
71 mFinished = false;
72 handler.post(new Runnable() {
73 @Override
74 public void run() {
75 startPlaying();
76 }
77 });
78 mDone.block();
79 handler.post(new Runnable() {
80 @Override
81 public void run() {
82 finish();
83 // No new messages should get posted to the handler thread after this
84 Looper.myLooper().quit();
85 }
86 });
87 return mFinished;
88 }
89
90 /**
91 * Stops playback. Can be called multiple times.
92 * Can be called from any thread.
93 */
94 public void stop() {
95 mDone.open();
96 }
97
98 /**
99 * Starts playback.
100 * Called on the handler thread.
101 */
102 private void startPlaying() {
103 mPlayer = MediaPlayer.create(mContext, mUri);
104 if (mPlayer == null) {
105 Log.w(TAG, "Failed to play " + mUri);
106 mDone.open();
107 return;
108 }
109 try {
110 mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
111 @Override
112 public boolean onError(MediaPlayer mp, int what, int extra) {
113 Log.w(TAG, "Audio playback error: " + what + ", " + extra);
114 mDone.open();
115 return true;
116 }
117 });
118 mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
119 @Override
120 public void onCompletion(MediaPlayer mp) {
121 mFinished = true;
122 mDone.open();
123 }
124 });
125 mPlayer.setAudioStreamType(mStreamType);
126 mPlayer.start();
127 } catch (IllegalArgumentException ex) {
128 Log.w(TAG, "MediaPlayer failed", ex);
129 mDone.open();
130 }
131 }
132
133 /**
134 * Stops playback and release the media player.
135 * Called on the handler thread.
136 */
137 private void finish() {
138 try {
139 mPlayer.stop();
140 } catch (IllegalStateException ex) {
141 // Do nothing, the player is already stopped
142 }
143 mPlayer.release();
144 }
145
146}