Readahead support
This commit is contained in:
parent
70c7957d0a
commit
cdbe36056f
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
}
|
||||
|
130
src/ch/blinkenlights/android/vanilla/ReadaheadThread.java
Normal file
130
src/ch/blinkenlights/android/vanilla/ReadaheadThread.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user