diff --git a/src/ch/blinkenlights/android/vanilla/BastpUtil.java b/src/ch/blinkenlights/android/vanilla/BastpUtil.java index b7d7d157..de4597f2 100644 --- a/src/ch/blinkenlights/android/vanilla/BastpUtil.java +++ b/src/ch/blinkenlights/android/vanilla/BastpUtil.java @@ -26,13 +26,18 @@ public class BastpUtil { private RGLruCache rgCache; public BastpUtil() { - rgCache = new RGLruCache(16); /* Cache up to 16 entries */ + rgCache = new RGLruCache(64); /* Cache up to 64 entries */ } /** Returns the ReplayGain values of 'path' as */ public float[] getReplayGainValues(String path) { + if(path == null) { + // path must not be null + path = "//null\\"; + } + float[] cached = rgCache.get(path); if(cached == null) { diff --git a/src/ch/blinkenlights/android/vanilla/PlaybackService.java b/src/ch/blinkenlights/android/vanilla/PlaybackService.java index 1d6ceac0..cd4aebe5 100644 --- a/src/ch/blinkenlights/android/vanilla/PlaybackService.java +++ b/src/ch/blinkenlights/android/vanilla/PlaybackService.java @@ -43,7 +43,6 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; -import android.media.audiofx.AudioEffect; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Build; @@ -301,8 +300,8 @@ public final class PlaybackService extends Service private Looper mLooper; private Handler mHandler; - MediaPlayer mMediaPlayer; - MediaPlayer mPreparedMediaPlayer; + VanillaMediaPlayer mMediaPlayer; + VanillaMediaPlayer mPreparedMediaPlayer; private boolean mMediaPlayerInitialized; private PowerManager.WakeLock mWakeLock; private NotificationManager mNotificationManager; @@ -404,6 +403,7 @@ public final class PlaybackService extends Service mPlayCounts = new PlayCountsHelper(this); mMediaPlayer = getNewMediaPlayer(); + mPreparedMediaPlayer = getNewMediaPlayer(); mBastpUtil = new BastpUtil(); mReadahead = new ReadaheadThread(); mReadahead.start(); @@ -554,14 +554,15 @@ public final class PlaybackService extends Service if (mMediaPlayer != null) { saveState(mMediaPlayer.getCurrentPosition()); - Intent i = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); - i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mMediaPlayer.getAudioSessionId()); - i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName()); - sendBroadcast(i); mMediaPlayer.release(); mMediaPlayer = null; } + if (mPreparedMediaPlayer != null) { + mPreparedMediaPlayer.release(); + mPreparedMediaPlayer = null; + } + MediaButtonReceiver.unregisterMediaButton(this); try { @@ -582,24 +583,18 @@ public final class PlaybackService extends Service /** * Returns a new MediaPlayer object */ - private MediaPlayer getNewMediaPlayer() { - MediaPlayer mp = new MediaPlayer(); + private VanillaMediaPlayer getNewMediaPlayer() { + VanillaMediaPlayer mp = new VanillaMediaPlayer(this); mp.setAudioStreamType(AudioManager.STREAM_MUSIC); mp.setOnCompletionListener(this); mp.setOnErrorListener(this); return mp; } - public void prepareMediaPlayer(MediaPlayer mp, String path) throws IOException{ + public void prepareMediaPlayer(VanillaMediaPlayer mp, String path) throws IOException{ mp.setDataSource(path); mp.prepare(); - - Intent i = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION); - i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mp.getAudioSessionId()); - i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName()); - sendBroadcast(i); - - applyReplayGain(mp, path); + applyReplayGain(mp); } @@ -608,23 +603,17 @@ public final class PlaybackService extends Service * the (maybe just changed) user settings */ private void refreshReplayGainValues() { - Song curSong = getSong(0); - - if(mMediaPlayer == null) - return; - if(curSong == null) - return; - - applyReplayGain(mMediaPlayer, curSong.path); - if(mPreparedMediaPlayer != null) { - applyReplayGain(mPreparedMediaPlayer, getSong(1).path); - } + applyReplayGain(mMediaPlayer); + applyReplayGain(mPreparedMediaPlayer); } - - private void applyReplayGain(MediaPlayer mp, String path) { + /*** + * Reads the replay gain value of the media players data source + * and adjusts the volume + */ + private void applyReplayGain(VanillaMediaPlayer mp) { - float[] rg = getReplayGainValues(path); /* track, album */ + float[] rg = getReplayGainValues(mp.getDataSource()); /* track, album */ float adjust = 0f; if(mReplayGainAlbumEnabled) { @@ -674,22 +663,16 @@ public final class PlaybackService extends Service * re-creates a newone if needed. */ private void triggerGaplessUpdate() { - 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; - } - + + // Reset any preparations + mMediaPlayer.setNextMediaPlayer(null); + mPreparedMediaPlayer.reset(); + int fa = finishAction(mState); Song nextSong = getSong(1); if( nextSong != null @@ -697,7 +680,6 @@ public final class PlaybackService extends Service && fa != SongTimeline.FINISH_STOP_CURRENT && !mTimeline.isEndOfQueue() ) { try { - mPreparedMediaPlayer = getNewMediaPlayer(); prepareMediaPlayer(mPreparedMediaPlayer, nextSong.path); mMediaPlayer.setNextMediaPlayer(mPreparedMediaPlayer); Log.d("VanillaMusic", "New media player prepared with path "+nextSong.path); @@ -1197,12 +1179,12 @@ public final class PlaybackService extends Service mMediaPlayerInitialized = false; mMediaPlayer.reset(); - if(mPreparedMediaPlayer != null && - mPreparedMediaPlayer.isPlaying()) { - - mMediaPlayer.release(); + if(mPreparedMediaPlayer.isPlaying()) { + VanillaMediaPlayer tmpPlayer = mMediaPlayer; mMediaPlayer = mPreparedMediaPlayer; - mPreparedMediaPlayer = null; + mPreparedMediaPlayer = tmpPlayer; + mPreparedMediaPlayer.reset(); + Log.v("VanillaMusic", "Swapped media players"); } else if(song.path != null) { prepareMediaPlayer(mMediaPlayer, song.path); @@ -1210,10 +1192,9 @@ public final class PlaybackService extends Service mMediaPlayerInitialized = true; - - // Cancel any queued gapless triggers: we are updating it right now + // Cancel any pending gapless updates and re-send them mHandler.removeMessages(GAPLESS_UPDATE); - triggerGaplessUpdate(); + mHandler.sendEmptyMessage(GAPLESS_UPDATE); if (mPendingSeek != 0 && mPendingSeekSong == song.id) { mMediaPlayer.seekTo(mPendingSeek); @@ -1677,7 +1658,7 @@ public final class PlaybackService extends Service // This might get canceled if setCurrentSong() also fired a call // to processSong(); mHandler.removeMessages(GAPLESS_UPDATE); - mHandler.sendEmptyMessageDelayed(GAPLESS_UPDATE, 350); + mHandler.sendEmptyMessageDelayed(GAPLESS_UPDATE, 500); ArrayList list = sActivities; for (int i = list.size(); --i != -1; ) diff --git a/src/ch/blinkenlights/android/vanilla/VanillaMediaPlayer.java b/src/ch/blinkenlights/android/vanilla/VanillaMediaPlayer.java new file mode 100644 index 00000000..8e77972b --- /dev/null +++ b/src/ch/blinkenlights/android/vanilla/VanillaMediaPlayer.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 Adrian Ulrich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +package ch.blinkenlights.android.vanilla; + +import android.content.Context; +import android.content.Intent; +import android.media.MediaPlayer; +import android.media.audiofx.AudioEffect; + +import java.io.IOException; + + +import android.util.Log; + + +public class VanillaMediaPlayer extends MediaPlayer { + + private Context mContext; + private String mDataSource; + private int mClaimedAudioSessionId = 0; + + /** + * Constructs a new VanillaMediaPlayer class + */ + public VanillaMediaPlayer(Context context) { + super(); + mContext = context; + _claimAudioSession(); + } + + /** + * Resets the media player to an unconfigured state + */ + public void reset() { + mDataSource = null; + super.reset(); + } + + /** + * Releases the media player and frees any claimed AudioEffect + */ + public void release() { + _releaseAudioSession(); + mDataSource = null; + super.release(); + } + + /** + * Sets the data source to use + */ + public void setDataSource(String dataSource) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { + mDataSource = dataSource; + super.setDataSource(mDataSource); + _claimAudioSession(); + } + + /** + * Returns the configured data source, may be null + */ + public String getDataSource() { + return mDataSource; + } + + /** + * Creates a new AudioEffect for our AudioSession + */ + private void _claimAudioSession() { + // No active audio session -> claim one + Log.v("VanillaMusic", "_claimAudioSession() -> "+mClaimedAudioSessionId+" -> "+this); + if (mClaimedAudioSessionId == 0) { + mClaimedAudioSessionId = this.getAudioSessionId(); + Intent i = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION); + i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mClaimedAudioSessionId); + i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, mContext.getPackageName()); + mContext.sendBroadcast(i); + } + } + + /** + * Releases a previously claimed audio session id + */ + private void _releaseAudioSession() { + Log.v("VanillaMusic", "_releaseAudioSession() "+mClaimedAudioSessionId+" -> "+this); + + if (mClaimedAudioSessionId != 0) { + Intent i = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); + i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mClaimedAudioSessionId); + i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, mContext.getPackageName()); + mContext.sendBroadcast(i); + mClaimedAudioSessionId = 0; + } + } + +}