Readahead support
This commit is contained in:
parent
70c7957d0a
commit
cdbe36056f
@ -164,6 +164,9 @@ THE SOFTWARE.
|
|||||||
<string name="replaygain_untagged_debump_title">Songs without Replay Gain tag</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="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="notifications">Notifications</string>
|
||||||
<string name="notification_mode_title">Notification Mode</string>
|
<string name="notification_mode_title">Notification Mode</string>
|
||||||
<string name="notification_action_title">Notification Action</string>
|
<string name="notification_action_title">Notification Action</string>
|
||||||
|
@ -43,6 +43,11 @@ THE SOFTWARE.
|
|||||||
android:title="@string/headset_pause_title"
|
android:title="@string/headset_pause_title"
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:summary="@string/headset_pause_summary" />
|
android:summary="@string/headset_pause_summary" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="enable_readahead"
|
||||||
|
android:title="@string/readahead"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:summary="@string/readahead_summary" />
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
android:title="@string/replaygain"
|
android:title="@string/replaygain"
|
||||||
android:summary="@string/replaygain_summary">
|
android:summary="@string/replaygain_summary">
|
||||||
|
@ -373,7 +373,17 @@ public final class PlaybackService extends Service
|
|||||||
private boolean mReplayGainAlbumEnabled;
|
private boolean mReplayGainAlbumEnabled;
|
||||||
private int mReplayGainBump;
|
private int mReplayGainBump;
|
||||||
private int mReplayGainUntaggedDeBump;
|
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;
|
private BastpUtil mBastpUtil;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -388,6 +398,8 @@ public final class PlaybackService extends Service
|
|||||||
|
|
||||||
mMediaPlayer = getNewMediaPlayer();
|
mMediaPlayer = getNewMediaPlayer();
|
||||||
mBastpUtil = new BastpUtil();
|
mBastpUtil = new BastpUtil();
|
||||||
|
mReadahead = new ReadaheadThread();
|
||||||
|
mReadahead.start();
|
||||||
|
|
||||||
mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
|
mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
|
||||||
mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
|
mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
|
||||||
@ -413,6 +425,8 @@ public final class PlaybackService extends Service
|
|||||||
mReplayGainBump = settings.getInt(PrefKeys.REPLAYGAIN_BUMP, 75); /* seek bar is 150 -> 75 == middle == 0 */
|
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 */
|
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);
|
PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
|
||||||
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "VanillaMusicLock");
|
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "VanillaMusicLock");
|
||||||
|
|
||||||
@ -646,6 +660,10 @@ public final class PlaybackService extends Service
|
|||||||
mp.setVolume(rg_result, rg_result);
|
mp.setVolume(rg_result, rg_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the (hopefully cached) replaygain
|
||||||
|
* values of given file
|
||||||
|
*/
|
||||||
public float[] getReplayGainValues(String path) {
|
public float[] getReplayGainValues(String path) {
|
||||||
return mBastpUtil.getReplayGainValues(path);
|
return mBastpUtil.getReplayGainValues(path);
|
||||||
}
|
}
|
||||||
@ -691,7 +709,18 @@ public final class PlaybackService extends Service
|
|||||||
else {
|
else {
|
||||||
Log.d("VanillaMusic", "Must not create new media player object");
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -773,6 +802,8 @@ public final class PlaybackService extends Service
|
|||||||
} else if (PrefKeys.REPLAYGAIN_UNTAGGED_DEBUMP.equals(key)) {
|
} else if (PrefKeys.REPLAYGAIN_UNTAGGED_DEBUMP.equals(key)) {
|
||||||
mReplayGainUntaggedDeBump = settings.getInt(PrefKeys.REPLAYGAIN_UNTAGGED_DEBUMP, 150);
|
mReplayGainUntaggedDeBump = settings.getInt(PrefKeys.REPLAYGAIN_UNTAGGED_DEBUMP, 150);
|
||||||
refreshReplayGainValues();
|
refreshReplayGainValues();
|
||||||
|
} else if (PrefKeys.ENABLE_READAHEAD.equals(key)) {
|
||||||
|
mReadaheadEnabled = settings.getBoolean(PrefKeys.ENABLE_READAHEAD, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompatFroyo.dataChanged(this);
|
CompatFroyo.dataChanged(this);
|
||||||
@ -898,6 +929,7 @@ public final class PlaybackService extends Service
|
|||||||
mTimeline.setFinishAction(finishAction(state));
|
mTimeline.setFinishAction(finishAction(state));
|
||||||
|
|
||||||
triggerGaplessUpdate();
|
triggerGaplessUpdate();
|
||||||
|
triggerReadAhead();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void broadcastChange(int state, Song song, long uptime)
|
private void broadcastChange(int state, Song song, long uptime)
|
||||||
@ -1169,6 +1201,7 @@ public final class PlaybackService extends Service
|
|||||||
|
|
||||||
mMediaPlayerInitialized = true;
|
mMediaPlayerInitialized = true;
|
||||||
triggerGaplessUpdate();
|
triggerGaplessUpdate();
|
||||||
|
triggerReadAhead();
|
||||||
|
|
||||||
if (mPendingSeek != 0 && mPendingSeekSong == song.id) {
|
if (mPendingSeek != 0 && mPendingSeekSong == song.id) {
|
||||||
mMediaPlayer.seekTo(mPendingSeek);
|
mMediaPlayer.seekTo(mPendingSeek);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2012-2013 Adrian Ulrich <adrian@blinkenlights.ch>
|
||||||
* Copyright (C) 2012 Christopher Eby <kreed@kreed.org>
|
* Copyright (C) 2012 Christopher Eby <kreed@kreed.org>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* 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 ENABLE_ALBUM_REPLAYGAIN = "enable_album_replaygain";
|
||||||
public static final String REPLAYGAIN_BUMP = "replaygain_bump";
|
public static final String REPLAYGAIN_BUMP = "replaygain_bump";
|
||||||
public static final String REPLAYGAIN_UNTAGGED_DEBUMP = "replaygain_untagged_debump";
|
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