Readahead support

This commit is contained in:
Adrian Ulrich 2013-08-30 18:52:45 +02:00
parent 70c7957d0a
commit cdbe36056f
5 changed files with 180 additions and 7 deletions

View File

@ -163,7 +163,10 @@ THE SOFTWARE.
<string name="replaygain_bump_title">Replay Gain Pre-amp</string>
<string name="replaygain_untagged_debump_title">Songs without Replay Gain tag</string>
<string name="replaygain_untagged_debump_summary">Decrease volume by</string>
<string name="readahead">Enable readahead</string>
<string name="readahead_summary">Readahead the currently playing song. This option may solve \'audio dropout\' issues. (caused by a slow SD card)</string>
<string name="notifications">Notifications</string>
<string name="notification_mode_title">Notification Mode</string>
<string name="notification_action_title">Notification Action</string>

View File

@ -43,6 +43,11 @@ THE SOFTWARE.
android:title="@string/headset_pause_title"
android:defaultValue="true"
android:summary="@string/headset_pause_summary" />
<CheckBoxPreference
android:key="enable_readahead"
android:title="@string/readahead"
android:defaultValue="false"
android:summary="@string/readahead_summary" />
<PreferenceScreen
android:title="@string/replaygain"
android:summary="@string/replaygain_summary">

View File

@ -373,7 +373,17 @@ public final class PlaybackService extends Service
private boolean mReplayGainAlbumEnabled;
private int mReplayGainBump;
private int mReplayGainUntaggedDeBump;
/**
* TRUE if the readahead feature is enabled
*/
private boolean mReadaheadEnabled;
/**
* Reference to precreated ReadAhead thread
*/
private ReadaheadThread mReadahead;
/**
* Reference to precreated BASTP Object
*/
private BastpUtil mBastpUtil;
@Override
@ -388,6 +398,8 @@ public final class PlaybackService extends Service
mMediaPlayer = getNewMediaPlayer();
mBastpUtil = new BastpUtil();
mReadahead = new ReadaheadThread();
mReadahead.start();
mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
@ -412,7 +424,9 @@ public final class PlaybackService extends Service
mReplayGainAlbumEnabled = settings.getBoolean(PrefKeys.ENABLE_ALBUM_REPLAYGAIN, false);
mReplayGainBump = settings.getInt(PrefKeys.REPLAYGAIN_BUMP, 75); /* seek bar is 150 -> 75 == middle == 0 */
mReplayGainUntaggedDeBump = settings.getInt(PrefKeys.REPLAYGAIN_UNTAGGED_DEBUMP, 150); /* seek bar is 150 -> == 0 */
mReadaheadEnabled = settings.getBoolean(PrefKeys.ENABLE_READAHEAD, false);
PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "VanillaMusicLock");
@ -645,11 +659,15 @@ public final class PlaybackService extends Service
}
mp.setVolume(rg_result, rg_result);
}
/**
* Returns the (hopefully cached) replaygain
* values of given file
*/
public float[] getReplayGainValues(String path) {
return mBastpUtil.getReplayGainValues(path);
}
/**
* Destroys any currently prepared MediaPlayer and
* re-creates a newone if needed.
@ -691,9 +709,20 @@ public final class PlaybackService extends Service
else {
Log.d("VanillaMusic", "Must not create new media player object");
}
}
/**
* Stops or starts the readahead thread
*/
private void triggerReadAhead() {
Song song = mCurrentSong;
if(mReadaheadEnabled && (mState & FLAG_PLAYING) != 0 && song != null) {
mReadahead.setSource(song.path);
} else {
mReadahead.pause();
}
}
/**
* Return the SharedPreferences instance containing the PlaybackService
* settings, creating it if necessary.
@ -773,6 +802,8 @@ public final class PlaybackService extends Service
} else if (PrefKeys.REPLAYGAIN_UNTAGGED_DEBUMP.equals(key)) {
mReplayGainUntaggedDeBump = settings.getInt(PrefKeys.REPLAYGAIN_UNTAGGED_DEBUMP, 150);
refreshReplayGainValues();
} else if (PrefKeys.ENABLE_READAHEAD.equals(key)) {
mReadaheadEnabled = settings.getBoolean(PrefKeys.ENABLE_READAHEAD, false);
}
CompatFroyo.dataChanged(this);
@ -898,6 +929,7 @@ public final class PlaybackService extends Service
mTimeline.setFinishAction(finishAction(state));
triggerGaplessUpdate();
triggerReadAhead();
}
private void broadcastChange(int state, Song song, long uptime)
@ -1169,6 +1201,7 @@ public final class PlaybackService extends Service
mMediaPlayerInitialized = true;
triggerGaplessUpdate();
triggerReadAhead();
if (mPendingSeek != 0 && mPendingSeekSong == song.id) {
mMediaPlayer.seekTo(mPendingSeek);

View File

@ -1,4 +1,5 @@
/*
* Copyright (C) 2012-2013 Adrian Ulrich <adrian@blinkenlights.ch>
* Copyright (C) 2012 Christopher Eby <kreed@kreed.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
@ -60,4 +61,5 @@ public class PrefKeys {
public static final String ENABLE_ALBUM_REPLAYGAIN = "enable_album_replaygain";
public static final String REPLAYGAIN_BUMP = "replaygain_bump";
public static final String REPLAYGAIN_UNTAGGED_DEBUMP = "replaygain_untagged_debump";
public static final String ENABLE_READAHEAD = "enable_readahead";
}

View File

@ -0,0 +1,130 @@
/*
* Copyright (C) 2013 Adrian Ulrich <adrian@blinkenlights.ch>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package ch.blinkenlights.android.vanilla;
import android.util.Log;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
class ReadaheadThread extends Thread {
private String mCurrentPath = null;
private boolean mPaused = true;
private Thread mThread;
public ReadaheadThread() {
}
/**
* Starts the persistent RA-Thread. This thread is never stopped: Destroying + creating
* a new thread just because the user clicked on 'pause' doesn't make much sense
*/
public void start() {
mThread = new Thread(new Runnable() {
public void run() {
threadWorker();
}
});
mThread.start();
}
/**
* Updates the current read-ahead source and wakes up the ra-thread
*/
public void setSource(String path) {
mCurrentPath = path;
mPaused = false;
mThread.interrupt();
}
/**
* Tells the ra-thread to pause the read-ahead work
* Calling setSource with path = mCurrentPath will cause the
* thread to RESUME (the file is not closed by calling pause())
*/
public void pause() {
mPaused = true;
}
/**
* Sleep for x milli seconds
*/
private static void sleep(int millis) {
try { Thread.sleep(millis); }
catch(InterruptedException e) {}
}
/**
* Our thread mainloop
* This thread will read from mCurrentPath until
* we hit an EOF or mPaused is set to false.
* The readahead speed is controlled by 'sleepTime'
*/
private void threadWorker() {
String path = null;
FileInputStream fis = null;
byte[] scratch = new byte[8192]; // Read 8kB per call to read()
int sleepTime = (int)((1f/(256f/8f))*1000f); // We try to read 256kB/s (with 8kB blocks)
for(;;) {
if(mPaused) {
sleep(600*1000); /* Sleep 10 minutes */
continue;
}
if(path != mCurrentPath) {
// File changed: First we try to close the old FIS
// fis can be null or already closed, we therefore do
// not care about the result. (the GC would take care of it anyway)
try { fis.close(); } catch(Exception e) {}
// We can now try to open the new file.
// Errors are not fatal, we will simply switch into paused-mode
try {
fis = new FileInputStream(mCurrentPath);
path = mCurrentPath;
Log.v("VanillaMusic", "readahead of "+path+" starts");
} catch(FileNotFoundException e) {
path = null;
mPaused = true;
continue;
}
}
// 'fis' is now an open FileInputStream. Read 8kB per go until
// we hit EOF
try {
int br = fis.read(scratch);
if(br < 0) { // no more data -> EOF
mPaused = true;
Log.v("VanillaMusic", "readahead of "+path+" finished");
}
} catch(IOException e) {
path = null; // io error?! switch into paused mode
mPaused = true;
}
sleep(sleepTime);
}
}
}