/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package android.speech.tts; import android.content.Context; import android.media.MediaPlayer; import android.net.Uri; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.util.Log; /** * A media player that allows blocking to wait for it to finish. */ class BlockingMediaPlayer { private static final String TAG = "BlockMediaPlayer"; private static final String MEDIA_PLAYER_THREAD_NAME = "TTS-MediaPlayer"; private final Context mContext; private final Uri mUri; private final int mStreamType; private final ConditionVariable mDone; // Only accessed on the Handler thread private MediaPlayer mPlayer; private volatile boolean mFinished; /** * Creates a new blocking media player. * Creating a blocking media player is a cheap operation. * * @param context * @param uri * @param streamType */ public BlockingMediaPlayer(Context context, Uri uri, int streamType) { mContext = context; mUri = uri; mStreamType = streamType; mDone = new ConditionVariable(); } /** * Starts playback and waits for it to finish. * Can be called from any thread. * * @return {@code true} if the playback finished normally, {@code false} if the playback * failed or {@link #stop} was called before the playback finished. */ public boolean startAndWait() { HandlerThread thread = new HandlerThread(MEDIA_PLAYER_THREAD_NAME); thread.start(); Handler handler = new Handler(thread.getLooper()); mFinished = false; handler.post(new Runnable() { @Override public void run() { startPlaying(); } }); mDone.block(); handler.post(new Runnable() { @Override public void run() { finish(); // No new messages should get posted to the handler thread after this Looper.myLooper().quit(); } }); return mFinished; } /** * Stops playback. Can be called multiple times. * Can be called from any thread. */ public void stop() { mDone.open(); } /** * Starts playback. * Called on the handler thread. */ private void startPlaying() { mPlayer = MediaPlayer.create(mContext, mUri); if (mPlayer == null) { Log.w(TAG, "Failed to play " + mUri); mDone.open(); return; } try { mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { Log.w(TAG, "Audio playback error: " + what + ", " + extra); mDone.open(); return true; } }); mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { mFinished = true; mDone.open(); } }); mPlayer.setAudioStreamType(mStreamType); mPlayer.start(); } catch (IllegalArgumentException ex) { Log.w(TAG, "MediaPlayer failed", ex); mDone.open(); } } /** * Stops playback and release the media player. * Called on the handler thread. */ private void finish() { try { mPlayer.stop(); } catch (IllegalStateException ex) { // Do nothing, the player is already stopped } mPlayer.release(); } }