package com.android.music.lrc;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* LRC is a data structure to restore the lrc file's informations.
*
* @author liwd<mailto:liwd@thunderst.com>
*/
public class LRC {
// Basic informations:
public String artist;
public String title;
public String album;
public String by;
public long offset = 0;
public String key;
private List<Offset> offsets = new ArrayList<Offset>();
private List<String> lyrics = new ArrayList<String>();
private boolean isReady = true;
private int curLrc = -1; // the current shown lyric index.
private int lastLrc = -1; // the lyric index shown at last time.
public LRC() {
}
public List<Offset> getOffsets() {
return offsets;
}
public List<String> getLyrics() {
return lyrics;
}
/**
* Reset the pointers befor restart switch or scroll lyrics.
*/
public void reset() {
curLrc = -1;
lastLrc = -1;
}
/**
* Add an Offset.
*
* @param time the offset time
* @param lrcInd the index of the lyric according to the current offset
* time.
*/
public void addOffset(long time, int lrcInd) {
if (isReady && lrcInd >= 0 && lrcInd < lyrics.size()) {
Offset o = new Offset(time, lrcInd);
int ind = offsets.indexOf(o);
if (ind >= 0) {
offsets.remove(ind);
}
offsets.add(o);
}
}
/**
* Add a lyric.
*
* @param lyric
* @return int
*/
public int addLyric(String lyric) {
if (isReady) {
lyrics.add(lyric);
return lyrics.size() - 1;
}
throw new IllegalStateException("LRC is not ready. Did you forget to invoke init() first?");
}
/**
* setBasicInfo
*
* @param key
* @param value
* @return boolean
*/
public boolean setBasicInfo(String key, String value) {
if (key != null) {
key = key.toLowerCase();
if (LyricConstants.KEY_AR.equals(key)) {
artist = value;
} else if (LyricConstants.KEY_TI.equals(key)) {
title = value;
} else if (LyricConstants.KEY_AL.equals(key)) {
album = value;
} else if (LyricConstants.KEY_BY.equals(key)) {
by = value;
} else if (LyricConstants.KEY_OFFSET.equals(key)) {
offset = new Long(value);
} else if (LyricConstants.KEY_KEY.equals(key)) {
key = value;
}
return true;
}
return false;
}
/**
* When you have added all the offsets, you should invoke this method.
*/
public void sortOffsets() {
if (isReady) {
Collections.sort(offsets, new Comparator<Offset>() {
public int compare(Offset one, Offset another) {
long t1 = one.time;
long t2 = another.time;
return t1 == t2 ? 0 : t1 > t2 ? 1 : -1;
}
});
for (Offset o : offsets) {
o.time -= offset;
}
}
}
/**
* @param row -1 means all lyrics, otherwise means the selected one.
* @param after True means ajust the after lyrics, False means the selected
* one.
* @param advance True means advance, False means Delayed.
* @param time ajust time
*/
public void ajust(int row, boolean after, boolean advance, int time) {
int next = 0;
if (row >= 0 && row < offsets.size()) {
if (after) {
if (advance) {
next = row + 1;
int len = offsets.size();
for (; next < len; next++) {
offsets.get(next).time -= time;
}
int i = 0, j = 0;
Offset curOff = null, nextOff = null;
for (i = row; i >= 0; i--) {
curOff = offsets.get(i);
for (j = i + 1; j < len;) {
nextOff = offsets.get(j);
if (curOff.time > nextOff.time) {
offsets.set(j - 1, nextOff);
nextOff = offsets.get(++j);
} else {
break;
}
}
if (j != i + 1) {
offsets.set(j - 1, curOff);
}
}
} else {
next = row + 1;
int len = offsets.size();
for (; next < len; next++) {
offsets.get(next).time += time;
}
}
} else {
if (advance) {
next = row;
Offset curOff = offsets.get(next);
Offset nextOff = offsets.get(next - 1);
curOff.time -= time;
while (nextOff != null && curOff.time < nextOff.time) {
offsets.set(next--, nextOff);
nextOff = offsets.get(next - 1);
}
offsets.set(next, curOff);
} else {
next = row;
Offset curOff = offsets.get(next);
Offset nextOff = offsets.get(next + 1);
curOff.time += time;
while (nextOff != null && curOff.time > nextOff.time) {
offsets.set(next++, nextOff);
nextOff = offsets.get(next + 1);
}
offsets.set(next, curOff);
}
}
} else {
if (advance) {
for (Offset o : offsets) {
o.time -= time;
}
} else {
for (Offset o : offsets) {
o.time += time;
}
}
}
}
/**
* next
*
* @param elapsedTime
* @return object
*/
public Object[] next(long elapsedTime) {
if (isReady) {
int size = offsets.size();
if (size == 0 || curLrc >= (size - 1)) {
return null;
}
for (++curLrc; curLrc < size && offsets.get(curLrc).time < elapsedTime; curLrc++)
;
long nextTime = -1; // Delayed time for the next lyric.
int curLyricInd = -1; // The current displayed lyric index.
if (curLrc >= size) {
nextTime = -1;
curLyricInd = size - 1;
lastLrc = curLyricInd;
} else {
Offset o = offsets.get(curLrc);
if (o.time > elapsedTime) {
nextTime = o.time - elapsedTime;
--curLrc;
} else {// o.time == elapsedTime
nextTime = curLrc == size - 1 ? -1 : offsets.get(curLrc + 1).time - elapsedTime;
}
curLyricInd = curLrc == lastLrc ? -1 : curLrc;
lastLrc = curLrc;
}
return new Object[] {
nextTime, curLyricInd == -1 ? null : lyrics.get(curLyricInd)
};
}
throw new IllegalStateException("LRC is not ready. Did you forget to invoke init() first?");
}
/**
* listLyrics
*
* @return String
*/
public String[] listLyrics() {
if (isReady) {
int size = offsets.size();
String[] lrcs = new String[size];
for (int i = 0; i < size; i++) {
lrcs[i] = lyrics.get(offsets.get(i).lrcInd);
}
return lrcs;
}
return null;
}
@Override
public String toString() {
if (isReady) {
StringBuilder sb = new StringBuilder();
sb.append("[artist:").append(artist).append("]\n").append("[title:").append(title)
.append("]\n").append("[album:").append(album).append("]\n").append("[by:")
.append(by).append("]\n").append("[offset:").append(offset).append("]\n")
.append("[key:").append(key).append("]\n");
for (Offset o : offsets) {
sb.append("[").append(o.time).append("]").append(lyrics.get(o.lrcInd)).append("\n");
}
return sb.toString();
}
return null;
}
/**
* A data structure to resotre the offset times and the pointer to the
* lyrics.
*/
public static class Offset {
long time;
int lrcInd;
Offset(long t, int l) {
time = t;
lrcInd = l;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Offset) {
Offset another = (Offset) obj;
if (another.time == time) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
int result = 17;
int l = (int) (time ^ (time >>> 32));
result = 37 * result + l;
return result;
}
}
/**
* PositionProvider
*
* @author lisc
*/
public interface PositionProvider {
/**
* getPosition
*
* @return long
*/
long getPosition();
/**
* getDuration
*
* @return long
*/
long getDuration();
}
}