📄 lyric.java
字号:
/* * To change this template, choose Tools | Templates * and open the template in the editor. */package com.hadeslee.yoyoplayer.lyric;import com.hadeslee.yoyoplayer.playlist.PlayListItem;import com.hadeslee.yoyoplayer.util.Config;import com.hadeslee.yoyoplayer.util.Util;import java.awt.Color;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.LinearGradientPaint;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import java.io.FileFilter;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.Serializable;import java.io.StringReader;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.List;import java.util.logging.Level;import java.util.logging.Logger;import java.util.regex.Matcher;import java.util.regex.Pattern;/** * 表示一首歌的歌词对象,它可以以某种方式来画自己 * @author hadeslee */public class Lyric implements Serializable { private static final long serialVersionUID = 20071125L; private static Logger log = Logger.getLogger(Lyric.class.getName()); private int width;//表示歌词的显示区域的宽度 private int height;//表示歌词的显示区域的高度 private long time;//表示当前的时间是多少了。以毫秒为单位 private long tempTime;//表示一个暂时的时间,用于拖动的时候,确定应该到哪了 private List<Sentence> list = new ArrayList<Sentence>();//里面装的是所有的句子 private boolean isMoving;//是否正在被拖动 private int currentIndex;//当前正在显示的歌词的下标 private boolean initDone;//是否初始化完毕了 private transient PlayListItem info;//有关于这首歌的信息 private transient File file;//该歌词所存在文件 private boolean enabled = true;//是否起用了该对象,默认是起用的 private long during = Integer.MAX_VALUE;//这首歌的长度 /** * 用ID3V1标签的字节和歌名来初始化歌词 * 歌词将自动在本地或者网络上搜索相关的歌词并建立关联 * 本地搜索将硬编码为user.home文件夹下面的Lyrics文件夹 * 以后改为可以手动设置. * @param songName 歌名 * @param data ID3V1的数据 */ public Lyric(final PlayListItem info) { this.info = info; this.during = info.getLength() * 1000; this.file = info.getLyricFile(); log.info("传进来的歌名是:" + info.toString()); //只要有关联好了的,就不用搜索了直接用就是了 if (file != null&&file.exists()) { log.log(Level.INFO, "不用找了,直接关联到的歌词是:" + file); init(file); initDone = true; return; } else { //否则就起一个线程去找了,先是本地找,然后再是网络上找 new Thread() { public void run() { doInit(info); initDone = true; } }.start(); } } /** * 读取某个指定的歌词文件,这个构造函数一般用于 * 拖放歌词文件到歌词窗口时调用的,拖放以后,两个自动关联 * @param file 歌词文件 * @param info 歌曲信息 */ public Lyric(File file, PlayListItem info) { this.file = file; this.info = info; init(file); initDone = true; } /** * 根据歌词内容和播放项构造一个 * 歌词对象 * @param lyric 歌词内容 * @param info 播放项 */ public Lyric(String lyric, PlayListItem info) { this.info = info; this.init(lyric); initDone = true; } private void doInit(PlayListItem info) { init(info); Sentence temp = null; //这个时候就要去网络上找了 if (list.size() == 1) { temp = list.remove(0); try { String lyric = Util.getLyric(info); if (lyric != null) { init(lyric); saveLyric(lyric, info); } else {//如果网络也没有找到,就要加回去了 list.add(temp); } } catch (IOException ex) { Logger.getLogger(Lyric.class.getName()).log(Level.SEVERE, null, ex); //如果抛了任何异常,也要加回去了 list.add(temp); } } } /** * 把下载到的歌词保存起来,免得下次再去找 * @param lyric 歌词内容 * @param info 歌的信息 */ private void saveLyric(String lyric, PlayListItem info) { try { //如果歌手不为空,则以歌手名+歌曲名为最好组合 String name = info.getFormattedName() + ".lrc";// File dir = new File(Config.HOME, "Lyrics" + File.separator); File dir = Config.getConfig().getSaveLyricDir(); dir.mkdirs(); file = new File(dir, name); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "GBK")); bw.write(lyric); bw.close(); info.setLyricFile(file); log.info("保存完毕,保存在:" + file); } catch (Exception exe) { log.log(Level.SEVERE, "保存歌词出错", exe); } } /** * 设置此歌词是否起用了,否则就不动了 * @param b 是否起用 */ public void setEnabled(boolean b) { this.enabled = b; } /** * 得到此歌词保存的地方 * @return 文件 */ public File getLyricFile() { return file; } /** * 调整整体的时间,比如歌词统一快多少 * 或者歌词统一慢多少,为正说明要快,为负说明要慢 * @param time 要调的时间,单位是毫秒 */ public void adjustTime(int time) { //如果是只有一个显示的,那就说明没有什么效对的意义了,直接返回 if (list.size() == 1) { return; } for (Sentence s : list) { s.setFromTime(s.getFromTime() - time); s.setToTime(s.getToTime() - time); } } /** * 根据一个文件夹,和一个歌曲的信息 * 从本地搜到最匹配的歌词 * @param dir 目录 * @param info 歌曲信息 * @return 歌词文件 */ private File getMathedLyricFile(File dir, PlayListItem info) { File matched = null;//已经匹配的文件 File[] fs = dir.listFiles(new FileFilter() { public boolean accept(File pathname) { return pathname.getName().toLowerCase().endsWith(".lrc"); } }); for (File f : fs) { //全部匹配或者部分匹配都行 if (matchAll(info, f) || matchSongName(info, f)) { matched = f; break; } } return matched; } /** * 根据歌的信息去初始化,这个时候 * 可能在本地找到歌词文件,也可能要去网络上搜索了 * @param info 歌曲信息 */ private void init(PlayListItem info) { File matched = null; for (File dir : Config.getConfig().getSearchLyricDirs()) { log.log(Level.FINE, "正在搜索文件夹:" + dir); //得到歌曲信息后,先本地搜索,先搜索HOME文件夹 //如果还不存在的话,那建一个目录,然后直接退出不管了 if (!dir.exists()) { dir.mkdirs(); } matched = getMathedLyricFile(dir, info); //当搜索到了,就退出 if (matched != null) { break; } } log.info("找到的是:" + matched); if (matched != null&&matched.exists()) { info.setLyricFile(matched); file = matched; init(matched); } else { init(""); } } /** * 根据文件来初始化 * @param file 文件 */ private void init(File file) { BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "GBK")); StringBuilder sb = new StringBuilder(); String temp = null; while ((temp = br.readLine()) != null) { sb.append(temp).append("\n"); } init(sb.toString()); } catch (Exception ex) { Logger.getLogger(Lyric.class.getName()).log(Level.SEVERE, null, ex); } finally { try { br.close(); } catch (Exception ex) { Logger.getLogger(Lyric.class.getName()).log(Level.SEVERE, null, ex); } } } /** * 是否完全匹配,完全匹配是指直接对应到ID3V1的标签, * 如果一样,则完全匹配了,完全匹配的LRC的文件格式是: * 阿木 - 有一种爱叫放手.lrc * @param info 歌曲信息 * @param file 侯选文件 * @return 是否合格 */ private boolean matchAll(PlayListItem info, File file) { String name = info.getFormattedName(); String fn = file.getName().substring(0, file.getName().lastIndexOf(".")); if (name.equals(fn)) { return true; } else { return false; } } /** * 是否匹配了歌曲名 * @param info 歌曲信息 * @param file 侯选文件 * @return 是否合格 */ private boolean matchSongName(PlayListItem info, File file) { String name = info.getFormattedName(); String rn = file.getName().substring(0, file.getName().lastIndexOf(".")); if (name.equalsIgnoreCase(rn) || info.getTitle().equalsIgnoreCase(rn)) { return true; } else { return false; } } /** * 最重要的一个方法,它根据读到的歌词内容 * 进行初始化,比如把歌词一句一句分开并计算好时间 * @param content 歌词内容 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -