// Copyright 2011, Aocate, Inc.
//
// 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 com.aocate.media;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Handler.Callback;
import android.util.Log;
public class MediaPlayer { // implements IMediaPlayer {
public interface OnBufferingUpdateListener {
public abstract void onBufferingUpdate(MediaPlayer arg0, int percent);
}
public interface OnCompletionListener {
public abstract void onCompletion(MediaPlayer arg0);
}
public interface OnErrorListener {
public abstract boolean onError(MediaPlayer arg0, int what, int extra);
}
public interface OnInfoListener {
public abstract boolean onInfo(MediaPlayer arg0, int what, int extra);
}
public interface OnPitchAdjustmentAvailableChangedListener {
/**
*
* @param arg0
* The owning media player
* @param pitchAdjustmentAvailable
* True if pitch adjustment is available, false if not
*/
public abstract void onPitchAdjustmentAvailableChanged(
MediaPlayer arg0, boolean pitchAdjustmentAvailable);
}
public interface OnPreparedListener {
public abstract void onPrepared(MediaPlayer arg0);
}
public interface OnSeekCompleteListener {
public abstract void onSeekComplete(MediaPlayer arg0);
}
public interface OnSpeedAdjustmentAvailableChangedListener {
/**
*
* @param arg0
* The owning media player
* @param speedAdjustmentAvailable
* True if speed adjustment is available, false if not
*/
public abstract void onSpeedAdjustmentAvailableChanged(
MediaPlayer arg0, boolean speedAdjustmentAvailable);
}
public enum State {
IDLE, INITIALIZED, PREPARED, STARTED, PAUSED, STOPPED, PREPARING, PLAYBACK_COMPLETED, END, ERROR
}
private static Uri SPEED_ADJUSTMENT_MARKET_URI = Uri
.parse("market://details?id=com.aocate.presto");
private static Intent prestoMarketIntent = null;
public static final int MEDIA_ERROR_SERVER_DIED = android.media.MediaPlayer.MEDIA_ERROR_SERVER_DIED;
public static final int MEDIA_ERROR_UNKNOWN = android.media.MediaPlayer.MEDIA_ERROR_UNKNOWN;
public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = android.media.MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK;
/**
* Indicates whether the specified action can be used as an intent. This
* method queries the package manager for installed packages that can
* respond to an intent with the specified action. If no suitable package is
* found, this method returns false.
*
* @param context
* The application's environment.
* @param action
* The Intent action to check for availability.
*
* @return True if an Intent with the specified action can be sent and
* responded to, false otherwise.
*/
public static boolean isIntentAvailable(Context context, String action) {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(action);
Log.d(MP_TAG, "Looking for intent " + action);
List<ResolveInfo> list = packageManager.queryIntentServices(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
/**
* Indicates whether the Presto library is installed
*
* @param context
* The context to use to query the package manager.
* @return True if the Presto library is installed, false if not.
*/
public static boolean isPrestoLibraryInstalled(Context context) {
return isIntentAvailable(context, ServiceBackedMediaPlayer.INTENT_NAME);
}
/**
* Return an Intent that opens the Android Market page for the speed
* alteration library
*
* @return The Intent for the Presto library on the Android Market
*/
public static Intent getPrestoMarketIntent() {
if (prestoMarketIntent == null) {
prestoMarketIntent = new Intent(Intent.ACTION_VIEW,
SPEED_ADJUSTMENT_MARKET_URI);
}
return prestoMarketIntent;
}
/**
* Open the Android Market page for the Presto library
*
* @param context
* The context from which to open the Android Market page
*/
public static void openPrestoMarketIntent(Context context) {
context.startActivity(getPrestoMarketIntent());
}
private static final String MP_TAG = "AocateReplacementMediaPlayer";
private static final double PITCH_STEP_CONSTANT = 1.0594630943593;
private AndroidMediaPlayer amp = null;
// This is whether speed adjustment should be enabled (by the Service)
// To avoid the Service entirely, set useService to false
protected boolean enableSpeedAdjustment = true;
private int lastKnownPosition = 0;
// In some cases, we're going to have to replace the
// android.media.MediaPlayer on the fly, and we don't want to touch the
// wrong media player, so lock it way too much.
ReentrantLock lock = new ReentrantLock();
private int mAudioStreamType = AudioManager.STREAM_MUSIC;
private Context mContext;
private boolean mIsLooping = false;
private float mLeftVolume = 1f;
private float mPitchStepsAdjustment = 0f;
private float mRightVolume = 1f;
private float mSpeedMultiplier = 1f;
private int mWakeMode = 0;
MediaPlayerImpl mpi = null;
protected boolean pitchAdjustmentAvailable = false;
private ServiceBackedMediaPlayer sbmp = null;
protected boolean speedAdjustmentAvailable = false;
private Handler mServiceDisconnectedHandler = null;
// Some parts of state cannot be found by calling MediaPlayerImpl functions,
// so store our own state. This also helps copy state when changing
// implementations
State state = State.INITIALIZED;
String stringDataSource = null;
Uri uriDataSource = null;
private boolean useService = false;
// Naming Convention for Listeners
// Most listeners can both be set by clients and called by MediaPlayImpls
// There are a few that have to do things in this class as well as calling
// the function. In all cases, onX is what is called by MediaPlayerImpl
// If there is work to be done in this class, then the listener that is
// set by setX is X (with the first letter lowercase).
OnBufferingUpdateListener onBufferingUpdateListener = null;
OnCompletionListener onCompletionListener = null;
OnErrorListener onErrorListener = null;
OnInfoListener onInfoListener = null;
// Special case. Pitch adjustment ceases to be available when we switch
// to the android.media.MediaPlayer (though it is not guaranteed to be
// available when using the ServiceBackedMediaPlayer)
OnPitchAdjustmentAvailableChangedListener onPitchAdjustmentAvailableChangedListener = new OnPitchAdjustmentAvailableChangedListener() {
public void onPitchAdjustmentAvailableChanged(MediaPlayer arg0,
boolean pitchAdjustmentAvailable) {
lock.lock();
try {
Log
.d(
MP_TAG,
"onPitchAdjustmentAvailableChangedListener.onPitchAdjustmentAvailableChanged being called");
if (MediaPlayer.this.pitchAdjustmentAvailable != pitchAdjustmentAvailable) {
Log.d(MP_TAG, "Pitch adjustment state has changed from "
+ MediaPlayer.this.pitchAdjustmentAvailable
+ " to " + pitchAdjustmentAvailable);
MediaPlayer.this.pitchAdjustmentAvailable = pitchAdjustmentAvailable;
if (MediaPlayer.this.pitchAdjustmentAvailableChangedListener != null) {
MediaPlayer.this.pitchAdjustmentAvailableChangedListener
.onPitchAdjustmentAvailableChanged(arg0,
pitchAdjustmentAvailable);
}
}
} finally {
lock.unlock();
}
}
};
OnPitchAdjustmentAvailableChangedListener pitchAdjustmentAvailableChangedListener = null;
MediaPlayer.OnPreparedListener onPreparedListener = new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer arg0) {
Log.d(MP_TAG, "onPreparedListener 242 setting state to PREPARED");
MediaPlayer.this.state = State.PREPARED;
if (MediaPlayer.this.preparedListener != null) {
Log.d(MP_TAG, "Calling preparedListener");
MediaPlayer.this.preparedListener.onPrepared(arg0);
}
Log.d(MP_TAG, "Wrap up onPreparedListener");
}
};
OnPreparedListener preparedListener = null;
OnSeekCompleteListener onSeekCompleteListener = null;
// Special case. Speed adjustment ceases to be available when we switch
// to the android.media.MediaPlayer (though it is not guaranteed to be
// available when using the ServiceBackedMediaPlayer)
OnSpeedAdjustmentAvailableChangedListener onSpeedAdjustmentAvailableChangedListener = new OnSpeedAdjustmentAvailableChangedListener() {
public void onSpeedAdjustmentAvailableChanged(MediaPlayer arg0,
boolean speedAdjustmentAvailable) {
lock.lock();
try {
Log
.d(
MP_TAG,
"onSpeedAdjustmentAvailableChangedListener.onSpeedAdjustmentAvailableChanged being called");
if (MediaPlayer.this.speedAdjustmentAvailable != speedAdjustmentAvailable) {
Log.d(MP_TAG, "Speed adjustment state has changed from "
+ MediaPlayer.this.speedAdjustmentAvailable
+ " to " + speedAdjustmentAvailable);
MediaPlayer.this.speedAdjustmentAvailable = speedAdjustmentAvailable;
if (MediaPlayer.this.speedAdjustmentAvailableChangedListener != null) {
MediaPlayer.this.speedAdjustmentAvailableChangedListener
.onSpeedAdjustmentAvailableChanged(arg0,
speedAdjustmentAvailable);
}
}
} finally {
lock.unlock();
}
}
};
OnSpeedAdjustmentAvailableChangedListener speedAdjustmentAvailableChangedListener = null;
private int speedAdjustmentAlgorithm = SpeedAdjustmentAlgorithm.SONIC;
public MediaPlayer(final Context context) {
this(context, true);
}
public MediaPlayer(final Context context, boolean useService) {
Log.d(MP_TAG, "Started with context " + context + " use=" + useService);
this.mContext = context;
this.useService = useService;
// So here's the major problem
// Sometimes the service won't exist or won't be connected,
// so start with an android.media.MediaPlayer, and when
// the service is connected, use that from then on
this.mpi = this.amp = new AndroidMediaPlayer(this, context);
// setupMpi will go get the Service, if it can, then bring that
// implementation into sync
Log.d(MP_TAG, "setupMpi");
setupMpi(context);
}
private boolean invalidServiceConnectionConfiguration() {
if (!(this.mpi instanceof ServiceBackedMediaPlayer)) {
if (this.useService && isPrestoLibraryInstalled()) {
// In this case, the Presto library has been installed
// or something while playing sound
// We could be using the service, but we're not
Log.d(MP_TAG, "We could be using the service, but we're not 316");
return true;
}
// If useService is false, then we shouldn't be using the SBMP
// If the Presto library isn't installed, ditto
Log.d(MP_TAG, "this.mpi is not a ServiceBackedMediaPlayer, but we couldn't use it anyway 321");
return false;
} else {
assert (this.mpi instanceof ServiceBackedMediaPlayer);
if (this.useService && isPrestoLibraryInstalled()) {
// We should be using the service, and we are. Great!
Log.d(MP_TAG, "We could be using a ServiceBackedMediaPlayer and we are 327");
return false;
}
// We're trying to use the service when we shouldn't,
// that's an invalid configuration
Log.d(MP_TAG, "We're trying to use a ServiceBackedMediaPlayer but we shouldn't be 332");
return true;
}
}
private void setupMpi(final Context context) {
lock.lock();
try {
Log.d(MP_TAG, "setupMpi 336");
// Check if the client wants to use the service at all,
// then if we're already using the right kind of media player
if (this.useService && isPrestoLibraryInstalled()) {
if ((this.mpi != null)
&& (this.mpi instanceof ServiceBackedMediaPlayer)) {
Log.d(MP_TAG, "Already using ServiceBackedMediaPlayer");
return;
}
if (this.sbmp == null) {
Log.d(MP_TAG, "Instantiating new ServiceBackedMediaPlayer 346");
this.sbmp = new ServiceBackedMediaPlayer(this, context,
new ServiceConnection() {
public void onServiceConnected(
ComponentName className,
final IBinder service) {
Thread t = new Thread(new Runnable() {
public void run() {
// This lock probably isn't granular
// enough
MediaPlayer.this.lock.lock();
Log.d(MP_TAG,
"onServiceConnected 257");
try {
MediaPlayer.this
.switchMediaPlayerImpl(
MediaPlayer.this.amp,
MediaPlayer.this.sbmp);
Log.d(MP_TAG, "End onServiceConnected 362");
} finally {
MediaPlayer.this.lock.unlock();
}
}
});
t.start();
}
public void onServiceDisconnected(
ComponentName className) {
MediaPlayer.this.lock.lock();
try {
// Can't get any more useful information
// out of sbmp
if (MediaPlayer.this.sbmp != null) {
MediaPlayer.this.sbmp.release();
}
// Unlike most other cases, sbmp gets set
// to null since there's nothing useful
// backing it now
MediaPlayer.this.sbmp = null;
if (mServiceDisconnectedHandler == null) {
mServiceDisconnectedHandler = new Handler(new Callback() {
public boolean handleMessage(Message msg) {
// switchMediaPlayerImpl won't try to
// clone anything from null
lock.lock();
try {
if (MediaPlayer.this.amp == null) {
// This should never be in this state
MediaPlayer.this.amp = new AndroidMediaPlayer(
MediaPlayer.this,
MediaPlayer.this.mContext);
}
// Use sbmp instead of null in case by some miracle it's
// been restored in the meantime
MediaPlayer.this.switchMediaPlayerImpl(
MediaPlayer.this.sbmp,
MediaPlayer.this.amp);
return true;
}
finally {
lock.unlock();
}
}
});
}
// This code needs to execute on the
// original thread to instantiate
// the new object in the right place
mServiceDisconnectedHandler
.sendMessage(
mServiceDisconnectedHandler
.obtainMessage());
// Note that we do NOT want to set
// useService. useService is about
// what the user wants, not what they
// get
} finally {
MediaPlayer.this.lock.unlock();
}
}
}
);
}
switchMediaPlayerImpl(this.amp, this.sbmp);
} else {
if ((this.mpi != null)
&& (this.mpi instanceof AndroidMediaPlayer)) {
Log.d(MP_TAG, "Already using AndroidMediaPlayer");
return;
}
if (this.amp == null) {
Log.d(MP_TAG, "Instantiating new AndroidMediaPlayer (this should be impossible)");
this.amp = new AndroidMediaPlayer(this, context);
}
switchMediaPlayerImpl(this.sbmp, this.amp);
}
} finally {
lock.unlock();
}
}
private void switchMediaPlayerImpl(MediaPlayerImpl from, MediaPlayerImpl to) {
lock.lock();
try {
Log.d(MP_TAG, "switchMediaPlayerImpl");
if ((from == to)
// Same object, nothing to synchronize
|| (to == null)
// Nothing to copy to (maybe this should throw an error?)
|| ((to instanceof ServiceBackedMediaPlayer) && !((ServiceBackedMediaPlayer) to).isConnected())
// ServiceBackedMediaPlayer hasn't yet connected, onServiceConnected will take care of the transition
|| (MediaPlayer.this.state == State.END)) {
// State.END is after a release(), no further functions should
// be called on this class and from is likely to have problems
// retrieving state that won't be used anyway
return;
}
// Extract all that we can from the existing implementation
// and copy it to the new implementation
Log.d(MP_TAG, "switchMediaPlayerImpl(), current state is "
+ this.state.toString());
to.reset();
// Do this first so we don't have to prepare the same
// data file twice
to.setEnableSpeedAdjustment(MediaPlayer.this.enableSpeedAdjustment);
// This is a reasonable place to set all of these,
// none of them require prepare() or the like first
to.setAudioStreamType(this.mAudioStreamType);
to.setSpeedAdjustmentAlgorithm(this.speedAdjustmentAlgorithm);
to.setLooping(this.mIsLooping);
to.setPitchStepsAdjustment(this.mPitchStepsAdjustment);
Log.d(MP_TAG, "Setting playback speed to " + this.mSpeedMultiplier);
to.setPlaybackSpeed(this.mSpeedMultiplier);
to.setVolume(MediaPlayer.this.mLeftVolume,
MediaPlayer.this.mRightVolume);
to.setWakeMode(this.mContext, this.mWakeMode);
Log.d(MP_TAG, "asserting at least one data source is null");
assert ((MediaPlayer.this.stringDataSource == null) || (MediaPlayer.this.uriDataSource == null));
if (uriDataSource != null) {
Log.d(MP_TAG, "switchMediaPlayerImpl(): uriDataSource != null");
try {
to.setDataSource(this.mContext, uriDataSource);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (stringDataSource != null) {
Log.d(MP_TAG,
"switchMediaPlayerImpl(): stringDataSource != null");
try {
to.setDataSource(stringDataSource);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if ((this.state == State.PREPARED)
|| (this.state == State.PREPARING)
|| (this.state == State.PAUSED)
|| (this.state == State.STOPPED)
|| (this.state == State.STARTED)
|| (this.state == State.PLAYBACK_COMPLETED)) {
Log.d(MP_TAG, "switchMediaPlayerImpl(): prepare and seek");
// Use prepare here instead of prepareAsync so that
// we wait for it to be ready before we try to use it
try {
to.muteNextOnPrepare();
to.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int seekPos = 0;
if (from != null) {
seekPos = from.getCurrentPosition();
} else if (this.lastKnownPosition < to.getDuration()) {
// This can happen if the Service unexpectedly
// disconnected. Because it would result in too much
// information being passed around, we don't constantly
// poll for the lastKnownPosition, but we'll save it
// when getCurrentPosition is called
seekPos = this.lastKnownPosition;
}
to.muteNextSeek();
to.seekTo(seekPos);
}
if ((from != null)
&& from.isPlaying()) {
from.pause();
}
if ((this.state == State.STARTED)
|| (this.state == State.PAUSED)
|| (this.state == State.STOPPED)) {
Log.d(MP_TAG, "switchMediaPlayerImpl(): start");
if (to != null) {
to.start();
}
}
if (this.state == State.PAUSED) {
Log.d(MP_TAG, "switchMediaPlayerImpl(): paused");
if (to != null) {
to.pause();
}
} else if (this.state == State.STOPPED) {
Log.d(MP_TAG, "switchMediaPlayerImpl(): stopped");
if (to != null) {
to.stop();
}
}
this.mpi = to;
// Cheating here by relying on the side effect in
// on(Pitch|Speed)AdjustmentAvailableChanged
if ((to.canSetPitch() != this.pitchAdjustmentAvailable)
&& (this.onPitchAdjustmentAvailableChangedListener != null)) {
this.onPitchAdjustmentAvailableChangedListener
.onPitchAdjustmentAvailableChanged(this, to
.canSetPitch());
}
if ((to.canSetSpeed() != this.speedAdjustmentAvailable)
&& (this.onSpeedAdjustmentAvailableChangedListener != null)) {
this.onSpeedAdjustmentAvailableChangedListener
.onSpeedAdjustmentAvailableChanged(this, to
.canSetSpeed());
}
Log.d(MP_TAG, "switchMediaPlayerImpl() 625 " + this.state.toString());
} finally {
lock.unlock();
}
}
/**
* Returns true if pitch can be changed at this moment
*
* @return True if pitch can be changed
*/
public boolean canSetPitch() {
lock.lock();
try {
return this.mpi.canSetPitch();
} finally {
lock.unlock();
}
}
/**
* Returns true if speed can be changed at this moment
*
* @return True if speed can be changed
*/
public boolean canSetSpeed() {
lock.lock();
try {
return this.mpi.canSetSpeed();
} finally {
lock.unlock();
}
}
protected void finalize() throws Throwable {
lock.lock();
try {
Log.d(MP_TAG, "finalize() 626");
this.release();
} finally {
lock.unlock();
}
}
/**
* Returns the number of steps (in a musical scale) by which playback is
* currently shifted. When greater than zero, pitch is shifted up. When less
* than zero, pitch is shifted down.
*
* @return The number of steps pitch is currently shifted by
*/
public float getCurrentPitchStepsAdjustment() {
lock.lock();
try {
return this.mpi.getCurrentPitchStepsAdjustment();
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.getCurrentPosition()
* Accurate only to frame size of encoded data (26 ms for MP3s)
*
* @return Current position (in milliseconds)
*/
public int getCurrentPosition() {
lock.lock();
try {
return (this.lastKnownPosition = this.mpi.getCurrentPosition());
} finally {
lock.unlock();
}
}
/**
* Returns the current speed multiplier. Defaults to 1.0 (normal speed)
*
* @return The current speed multiplier
*/
public float getCurrentSpeedMultiplier() {
lock.lock();
try {
return this.mpi.getCurrentSpeedMultiplier();
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.getDuration()
*
* @return Length of the track (in milliseconds)
*/
public int getDuration() {
lock.lock();
try {
return this.mpi.getDuration();
} finally {
lock.unlock();
}
}
/**
* Get the maximum value that can be passed to setPlaybackSpeed
*
* @return The maximum speed multiplier
*/
public float getMaxSpeedMultiplier() {
lock.lock();
try {
return this.mpi.getMaxSpeedMultiplier();
} finally {
lock.unlock();
}
}
/**
* Get the minimum value that can be passed to setPlaybackSpeed
*
* @return The minimum speed multiplier
*/
public float getMinSpeedMultiplier() {
lock.lock();
try {
return this.mpi.getMinSpeedMultiplier();
} finally {
lock.unlock();
}
}
/**
* Gets the version code of the backing service
* @return -1 if ServiceBackedMediaPlayer is not used, 0 if the service is not
* connected, otherwise the version code retrieved from the service
*/
public int getServiceVersionCode() {
lock.lock();
try {
if (this.mpi instanceof ServiceBackedMediaPlayer) {
return ((ServiceBackedMediaPlayer) this.mpi).getServiceVersionCode();
}
else {
return -1;
}
}
finally {
lock.unlock();
}
}
/**
* Gets the version name of the backing service
* @return null if ServiceBackedMediaPlayer is not used, empty string if
* the service is not connected, otherwise the version name retrieved from
* the service
*/
public String getServiceVersionName() {
lock.lock();
try {
if (this.mpi instanceof ServiceBackedMediaPlayer) {
return ((ServiceBackedMediaPlayer) this.mpi).getServiceVersionName();
}
else {
return null;
}
}
finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.isLooping()
*
* @return True if the track is looping
*/
public boolean isLooping() {
lock.lock();
try {
return this.mpi.isLooping();
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.isPlaying()
*
* @return True if the track is playing
*/
public boolean isPlaying() {
lock.lock();
try {
return this.mpi.isPlaying();
} finally {
lock.unlock();
}
}
/**
* Returns true if this MediaPlayer has access to the Presto
* library
*
* @return True if the Presto library is installed
*/
public boolean isPrestoLibraryInstalled() {
if ((this.mpi == null) || (this.mpi.mContext == null)) {
return false;
}
return isPrestoLibraryInstalled(this.mpi.mContext);
}
/**
* Open the Android Market page in the same context as this MediaPlayer
*/
public void openPrestoMarketIntent() {
if ((this.mpi != null) && (this.mpi.mContext != null)) {
openPrestoMarketIntent(this.mpi.mContext);
}
}
/**
* Functions identically to android.media.MediaPlayer.pause() Pauses the
* track
*/
public void pause() {
lock.lock();
try {
if (invalidServiceConnectionConfiguration()) {
setupMpi(this.mpi.mContext);
}
this.state = State.PAUSED;
this.mpi.pause();
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.prepare() Prepares the
* track. This or prepareAsync must be called before start()
*/
public void prepare() throws IllegalStateException, IOException {
lock.lock();
try {
Log.d(MP_TAG, "prepare() 746 using " + ((this.mpi == null) ? "null (this shouldn't happen)" : this.mpi.getClass().toString()) + " state " + this.state.toString());
Log.d(MP_TAG, "onPreparedListener is: " + ((this.onPreparedListener == null) ? "null" : "non-null"));
Log.d(MP_TAG, "preparedListener is: " + ((this.preparedListener == null) ? "null" : "non-null"));
if (invalidServiceConnectionConfiguration()) {
setupMpi(this.mpi.mContext);
}
this.mpi.prepare();
this.state = State.PREPARED;
Log.d(MP_TAG, "prepare() finished 778");
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.prepareAsync()
* Prepares the track. This or prepare must be called before start()
*/
public void prepareAsync() {
lock.lock();
try {
Log.d(MP_TAG, "prepareAsync() 779");
if (invalidServiceConnectionConfiguration()) {
setupMpi(this.mpi.mContext);
}
this.state = State.PREPARING;
this.mpi.prepareAsync();
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.release() Releases the
* underlying resources used by the media player.
*/
public void release() {
lock.lock();
try {
Log.d(MP_TAG, "Releasing MediaPlayer 791");
this.state = State.END;
if (this.amp != null) {
this.amp.release();
}
if (this.sbmp != null) {
this.sbmp.release();
}
this.onBufferingUpdateListener = null;
this.onCompletionListener = null;
this.onErrorListener = null;
this.onInfoListener = null;
this.preparedListener = null;
this.onPitchAdjustmentAvailableChangedListener = null;
this.pitchAdjustmentAvailableChangedListener = null;
Log.d(MP_TAG, "Setting onSeekCompleteListener to null 871");
this.onSeekCompleteListener = null;
this.onSpeedAdjustmentAvailableChangedListener = null;
this.speedAdjustmentAvailableChangedListener = null;
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.reset() Resets the
* track to idle state
*/
public void reset() {
lock.lock();
try {
this.state = State.IDLE;
this.stringDataSource = null;
this.uriDataSource = null;
this.mpi.reset();
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.seekTo(int msec) Seeks
* to msec in the track
*/
public void seekTo(int msec) throws IllegalStateException {
lock.lock();
try {
this.mpi.seekTo(msec);
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.setAudioStreamType(int
* streamtype) Sets the audio stream type.
*/
public void setAudioStreamType(int streamtype) {
lock.lock();
try {
this.mAudioStreamType = streamtype;
this.mpi.setAudioStreamType(streamtype);
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.setDataSource(Context
* context, Uri uri) Sets uri as data source in the context given
*/
public void setDataSource(Context context, Uri uri)
throws IllegalArgumentException, IllegalStateException, IOException {
lock.lock();
try {
Log.d(MP_TAG, "In setDataSource(context, " + uri.toString() + "), using " + this.mpi.getClass().toString());
if (invalidServiceConnectionConfiguration()) {
setupMpi(this.mpi.mContext);
}
this.state = State.INITIALIZED;
this.stringDataSource = null;
this.uriDataSource = uri;
this.mpi.setDataSource(context, uri);
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.setDataSource(String
* path) Sets the data source of the track to a file given.
*/
public void setDataSource(String path) throws IllegalArgumentException,
IllegalStateException, IOException {
lock.lock();
try {
Log.d(MP_TAG, "In setDataSource(context, " + path + ")");
if (invalidServiceConnectionConfiguration()) {
setupMpi(this.mpi.mContext);
}
this.state = State.INITIALIZED;
this.stringDataSource = path;
this.uriDataSource = null;
this.mpi.setDataSource(path);
} finally {
lock.unlock();
}
}
public String getDataSource() {
return stringDataSource;
}
public Uri getDataSourceUri() {
return uriDataSource;
}
/**
* Sets whether to use speed adjustment or not. Speed adjustment on is more
* computation-intensive than with it off.
*
* @param enableSpeedAdjustment
* Whether speed adjustment should be supported.
*/
public void setEnableSpeedAdjustment(boolean enableSpeedAdjustment) {
lock.lock();
try {
this.enableSpeedAdjustment = enableSpeedAdjustment;
this.mpi.setEnableSpeedAdjustment(enableSpeedAdjustment);
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.setLooping(boolean
* loop) Sets the track to loop infinitely if loop is true, play once if
* loop is false
*/
public void setLooping(boolean loop) {
lock.lock();
try {
this.mIsLooping = loop;
this.mpi.setLooping(loop);
} finally {
lock.unlock();
}
}
/**
* Sets the number of steps (in a musical scale) by which playback is
* currently shifted. When greater than zero, pitch is shifted up. When less
* than zero, pitch is shifted down.
*
* @param pitchSteps
* The number of steps by which to shift playback
*/
public void setPitchStepsAdjustment(float pitchSteps) {
lock.lock();
try {
this.mPitchStepsAdjustment = pitchSteps;
this.mpi.setPitchStepsAdjustment(pitchSteps);
} finally {
lock.unlock();
}
}
/**
* Set the algorithm to use for changing the speed and pitch of audio
* See SpeedAdjustmentAlgorithm constants for more details
* @param algorithm The algorithm to use.
*/
public void setSpeedAdjustmentAlgorithm(int algorithm) {
lock.lock();
try {
this.speedAdjustmentAlgorithm = algorithm;
if (this.mpi != null) {
this.mpi.setSpeedAdjustmentAlgorithm(algorithm);
}
}
finally {
lock.unlock();
}
}
private static float getPitchStepsAdjustment(float pitch) {
return (float) (Math.log(pitch) / (2 * Math.log(PITCH_STEP_CONSTANT)));
}
/**
* Sets the percentage by which pitch is currently shifted. When greater
* than zero, pitch is shifted up. When less than zero, pitch is shifted
* down
*
* @param pitch
* The percentage to shift pitch
*/
public void setPlaybackPitch(float pitch) {
lock.lock();
try {
this.mPitchStepsAdjustment = getPitchStepsAdjustment(pitch);
this.mpi.setPlaybackPitch(pitch);
} finally {
lock.unlock();
}
}
/**
* Set playback speed. 1.0 is normal speed, 2.0 is double speed, and so on.
* Speed should never be set to 0 or below.
*
* @param f
* The speed multiplier to use for further playback
*/
public void setPlaybackSpeed(float f) {
lock.lock();
try {
this.mSpeedMultiplier = f;
this.mpi.setPlaybackSpeed(f);
} finally {
lock.unlock();
}
}
// @Override
public float getPlaybackSpeed() {
return this.mSpeedMultiplier;
}
/**
* Sets whether to use speed adjustment or not. Speed adjustment on is more
* computation-intensive than with it off.
*
* @param useService
* Whether speed adjustment should be supported.
*/
public void setUseService(boolean useService) {
lock.lock();
try {
this.useService = useService;
setupMpi(this.mpi.mContext);
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.setVolume(float
* leftVolume, float rightVolume) Sets the stereo volume
*/
public void setVolume(float leftVolume, float rightVolume) {
lock.lock();
try {
this.mLeftVolume = leftVolume;
this.mRightVolume = rightVolume;
this.mpi.setVolume(leftVolume, rightVolume);
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.setWakeMode(Context
* context, int mode) Acquires a wake lock in the context given. You must
* request the appropriate permissions in your AndroidManifest.xml file.
*/
public void setWakeMode(Context context, int mode) {
lock.lock();
try {
this.mWakeMode = mode;
this.mpi.setWakeMode(context, mode);
} finally {
lock.unlock();
}
}
/**
* Functions identically to
* android.media.MediaPlayer.setOnCompletionListener(OnCompletionListener
* listener) Sets a listener to be used when a track completes playing.
*/
public void setOnBufferingUpdateListener(OnBufferingUpdateListener listener) {
lock.lock();
try {
this.onBufferingUpdateListener = listener;
} finally {
lock.unlock();
}
}
/**
* Functions identically to
* android.media.MediaPlayer.setOnCompletionListener(OnCompletionListener
* listener) Sets a listener to be used when a track completes playing.
*/
public void setOnCompletionListener(OnCompletionListener listener) {
lock.lock();
try {
this.onCompletionListener = listener;
} finally {
lock.unlock();
}
}
// @Override
// public void setOnCompletionListener(final IMediaPlayer.OnCompletionListener contentService) {
// setOnCompletionListener(new OnCompletionListener() {
// @Override
// public void onCompletion(MediaPlayer arg0) {
// contentService.onCompletion(MediaPlayer.this);
// }
// });
// }
/**
* Functions identically to
* android.media.MediaPlayer.setOnErrorListener(OnErrorListener listener)
* Sets a listener to be used when a track encounters an error.
*/
public void setOnErrorListener(OnErrorListener listener) {
lock.lock();
try {
this.onErrorListener = listener;
} finally {
lock.unlock();
}
}
/**
* Functions identically to
* android.media.MediaPlayer.setOnInfoListener(OnInfoListener listener) Sets
* a listener to be used when a track has info.
*/
public void setOnInfoListener(OnInfoListener listener) {
lock.lock();
try {
this.onInfoListener = listener;
} finally {
lock.unlock();
}
}
/**
* Sets a listener that will fire when pitch adjustment becomes available or
* stops being available
*/
public void setOnPitchAdjustmentAvailableChangedListener(
OnPitchAdjustmentAvailableChangedListener listener) {
lock.lock();
try {
this.pitchAdjustmentAvailableChangedListener = listener;
} finally {
lock.unlock();
}
}
/**
* Functions identically to
* android.media.MediaPlayer.setOnPreparedListener(OnPreparedListener
* listener) Sets a listener to be used when a track finishes preparing.
*/
public void setOnPreparedListener(OnPreparedListener listener) {
lock.lock();
Log.d(MP_TAG, " ++++++++++++++++++++++++++++++++++++++++++++ setOnPreparedListener");
try {
this.preparedListener = listener;
// For this one, we do not explicitly set the MediaPlayer or the
// Service listener. This is because in addition to calling the
// listener provided by the client, it's necessary to change
// state to PREPARED. See prepareAsync for implementation details
} finally {
lock.unlock();
}
}
/**
* Functions identically to
* android.media.MediaPlayer.setOnSeekCompleteListener
* (OnSeekCompleteListener listener) Sets a listener to be used when a track
* finishes seeking.
*/
public void setOnSeekCompleteListener(OnSeekCompleteListener listener) {
lock.lock();
try {
this.onSeekCompleteListener = listener;
} finally {
lock.unlock();
}
}
/**
* Sets a listener that will fire when speed adjustment becomes available or
* stops being available
*/
public void setOnSpeedAdjustmentAvailableChangedListener(
OnSpeedAdjustmentAvailableChangedListener listener) {
lock.lock();
try {
this.speedAdjustmentAvailableChangedListener = listener;
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.start() Starts a track
* playing
*/
public void start() {
lock.lock();
try {
Log.d(MP_TAG, "start() 1149");
if (invalidServiceConnectionConfiguration()) {
setupMpi(this.mpi.mContext);
}
this.state = State.STARTED;
Log.d(MP_TAG, "start() 1154");
this.mpi.start();
} finally {
lock.unlock();
}
}
/**
* Functions identically to android.media.MediaPlayer.stop() Stops a track
* playing and resets its position to the start.
*/
public void stop() {
lock.lock();
try {
if (invalidServiceConnectionConfiguration()) {
setupMpi(this.mpi.mContext);
}
this.state = State.STOPPED;
this.mpi.stop();
} finally {
lock.unlock();
}
}
}