Gapless playback

This patch adds support for gapless playback present in JellyBean.
It will do nothing on ICS devices.
This commit is contained in:
Adrian Ulrich 2012-10-18 22:35:44 +02:00
parent 9d10a15a7c
commit e60be1af0e
3 changed files with 70 additions and 8 deletions

View File

@ -26,7 +26,7 @@ THE SOFTWARE.
android:versionName="0.9.13"
android:versionCode="0913"
android:installLocation="auto">
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="15" />
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="16" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

View File

@ -10,7 +10,7 @@
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-15
target=android-16
## do not enable: breaks settings in SDK15
##proguard.config=proguard.config

View File

@ -296,6 +296,7 @@ public final class PlaybackService extends Service
private Looper mLooper;
private Handler mHandler;
MediaPlayer mMediaPlayer;
MediaPlayer mPreparedMediaPlayer;
private boolean mMediaPlayerInitialized;
private PowerManager.WakeLock mWakeLock;
private NotificationManager mNotificationManager;
@ -385,11 +386,8 @@ public final class PlaybackService extends Service
mTimeline.setCallback(this);
int state = loadState();
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnErrorListener(this);
mMediaPlayer = getNewMediaPlayer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
try {
mEqualizer = new CompatEq(mMediaPlayer);
@ -573,6 +571,58 @@ public final class PlaybackService extends Service
super.onDestroy();
}
/**
* Returns a new MediaPlayer object
*/
public MediaPlayer getNewMediaPlayer() {
MediaPlayer mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.setOnCompletionListener(this);
mp.setOnErrorListener(this);
return mp;
}
public void triggerGaplessUpdate() {
Log.d("VanillaMusic", "triggering gapless update");
if(mMediaPlayerInitialized != true)
return;
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
return; /* setNextMediaPlayer is supported since JB */
if(mPreparedMediaPlayer != null) {
/* an old prepared player exists and is
* most likely invalid -> destroy it now
*/
mMediaPlayer.setNextMediaPlayer(null);
mPreparedMediaPlayer.release();
mPreparedMediaPlayer = null;
Log.d("VanillaMusic", "old prepared player destroyed");
}
int fa = finishAction(mState);
Song nextSong = getSong(1);
if( nextSong != null
&& fa != SongTimeline.FINISH_REPEAT_CURRENT
&& fa != SongTimeline.FINISH_STOP_CURRENT
&& !mTimeline.isEndOfQueue() ) {
try {
mPreparedMediaPlayer = getNewMediaPlayer();
mPreparedMediaPlayer.setDataSource(nextSong.path);
mPreparedMediaPlayer.prepare();
mMediaPlayer.setNextMediaPlayer(mPreparedMediaPlayer);
Log.d("VanillaMusic", "New media player prepared as "+mPreparedMediaPlayer+" with path "+nextSong.path);
} catch (IOException e) {
Log.e("VanillaMusic", "IOException", e);
}
}
else {
Log.d("VanillaMusic", "Must not create new media player object");
}
}
/**
* Return the SharedPreferences instance containing the PlaybackService
* settings, creating it if necessary.
@ -796,6 +846,8 @@ public final class PlaybackService extends Service
mTimeline.setShuffleMode(shuffleMode(state));
if ((toggled & MASK_FINISH) != 0)
mTimeline.setFinishAction(finishAction(state));
triggerGaplessUpdate();
}
private void broadcastChange(int state, Song song, long uptime)
@ -1054,8 +1106,18 @@ public final class PlaybackService extends Service
mMediaPlayer.reset();
mMediaPlayer.setDataSource(song.path);
mMediaPlayer.prepare();
if(mPreparedMediaPlayer != null &&
mPreparedMediaPlayer.isPlaying()) {
Log.v("VanillaMusic", "Replacing existing mediaplayer object with prepared version");
mMediaPlayer.release();
mMediaPlayer = mPreparedMediaPlayer;
mPreparedMediaPlayer = null;
}
mMediaPlayerInitialized = true;
triggerGaplessUpdate();
if (mPendingSeek != 0 && mPendingSeekSong == song.id) {
mMediaPlayer.seekTo(mPendingSeek);
mPendingSeek = 0;