Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4931
  • 博文数量: 3
  • 博客积分: 140
  • 博客等级: 入伍新兵
  • 技术积分: 35
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-12 16:16
文章存档

2010年(3)

我的朋友
最近访客

分类: Java

2010-08-12 17:02:10

 1. 类图


2. 源码清单
/**
 * 文件名:LrcAnalyst.java
 * 环境: GNU/Linux Ubuntu 7.04 + Eclipse 3.2 + JDK 1.6
 * 功能:解析Lrc歌词文件的主驱动程序
 * 版本:0.0.2.0
 * 作者:88250
 * 日期:2007.4.27
 * E-mail & MDN:
 * QQ:845765
 */

import javax.swing.JFrame;

/**
 * 测试歌词解析结果
 */
public class LrcAnalyst
{
    public LrcAnalyst()
    {
    JFrame frame = new JFrame("Lrc Srcoll");

        frame.setBounds(600, 300, 400, 200); 
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       
        LrcScrollPane pane = new LrcScrollPane();
        frame.getContentPane().add(pane);
        frame.setVisible(true);
        pane.scroll();
    }
   
    /**
     * 主程序入口点
     *
     * @param args
     *                命令行参数,这里没用到,为null
     */
    public static void main(String[] args)
    {
    new LrcAnalyst();
    }
}

/**
 * 文件名:LrcAnalystBase.java
 * 环境: GNU/Linux Ubuntu 7.04 + Eclipse 3.2 + JDK 1.6
 * 功能:解析Lrc歌词文件
 * 版本:0.0.2.0
 * 作者:88250
 * 日期:2007.4.22
 * E-mail & MDN:
 * QQ:845765
 */

import java.io.*;
import java.util.*;

/**
 * 歌词解释类,用于对lrc歌词文件的解析
 */
public class LrcAnalystBase
{
    /**
     * lrc文件缓冲区
     */
    private static Vector lyrics;

    /**
     * 格式化好的歌词时间与索引,格式参考TimeAndIndex类
     */
    private static Vector lrcTimeValAndIndex;
    /**
     * 返回保存歌词时间与对应歌词的索引容器
     * @return LrcTimeAndIndex 时间标签与歌词索引
     */
    public Vector getLrcTimeValAndIndex()
    {
    return lrcTimeValAndIndex;
    }
   
    /**
     * 歌词的实际内容
     */
    private static Vector lyrcisContent;
   
    /**
     * 返回歌词实际内容的容器
     * @return 歌词实际内容lyrcisContent
     */
    public Vector getLrcContent()
    {
    return lyrcisContent;
    }
    /**
         * 时间标签的长度,用于区分时间标签的格式
         *
         * @see private float computeTimeTag(String timeStr)方法
         */
    private int timeTagLength;
   
    /**
     * 真正的歌词内容开始到lrc文本开头的偏移量
     */
    private int realLyrcisStartOffset;
   
    /**
     * 返回真正的歌词内容开始到lrc文本开头的偏移量
     * @return 偏移量
     */
    public int getRealLrcStartOffset()
    {
    return realLyrcisStartOffset;
    }
   
    /**
     * 是否确定了歌词标签的格式,确定的话是true, 否则是false
     * @see private float computeTimeTag(String timeStr)方法
     */
    private boolean isConfirmTimeTagLeng;
   
    /**
         * 歌词解析器的默认构造器

         * 初始化歌词文件缓冲等字段
         */
    public LrcAnalystBase()
    {
    lyrics = new Vector();
    lrcTimeValAndIndex = new Vector();
    lyrcisContent = new Vector();
    timeTagLength = 0;
    isConfirmTimeTagLeng = false;
    realLyrcisStartOffset = 0;
    }

    /**
         * 按行对去读取文本文件内容
         * @param fileName
         *                文件路径与文件名
         * @throws IOException
         */
    public void readFile(String fileName)
    {
    try
    {
        InputStream r = new FileInputStream(fileName);
        ByteArrayOutputStream byteout = new ByteArrayOutputStream();
        byte tmp[] = new byte[1024];
        byte context[];
     
        r.read(tmp);
        byteout.write(tmp);
        context = byteout.toByteArray();
        String str = new String(context, "GBK");    // 解决汉字问题
        String lyrcisText[] = str.split(" ");
       
        for (int i = 0; i < lyrcisText.length; i++)
        {
        lyrics.add(lyrcisText[i]);
        }
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
    }

    /**
     * 显示读取lrc里的所有内容到控制台
     */
    public void displayLrcContent()
    {
    for (int i = 0; i < lyrics.size(); i++)
    {
        System.out.println(lyrics.get(i));
    }
    }

    /**
         * 从文件缓冲lyrics Vector里进行歌词文件内容的解析,并将解析结果保存

         * 解析方法:

         * Step0. 用']'分割一行内容到String[]

         * Step1. 查看每一行在第3个char是否是':'

         * Step2. 查看每一行在第6个char是否是'.'

         * Step3. 确定为歌词行,确定该lrc用的格式

         * 注意:目前,时间标签的格式有两种,分别是长度为8和5的

         * Step4. 循环直到歌词缓冲结束

         * 注意:这个算法可能存在严重缺陷
         *
         * @see 关于时间标签的格式,参考TimeTag类
         */
    public void parseLyrics()
    {
    for (int i = 0; i < lyrics.size(); i++)
    {
        String aLineLyrics = (String) lyrics.get(i);
        String[] partsLrc = aLineLyrics.split("]");
       
        char flag1 = 0;
        char flag2 = 0;
        if (!aLineLyrics.isEmpty())
        {// 很多制作lrc文件的人不负责,乱搞格式!!!!
        flag1 = aLineLyrics.charAt(1);
        flag2 = aLineLyrics.charAt(3);
        }
       
        if ((aLineLyrics.length() > 3)
            && (flag1 >= 48 && flag1 <= 57)    // 是否是数字
            && (':' == flag2))
        {// 确定为歌词内容行
        if (!isConfirmTimeTagLeng && aLineLyrics.length() > 9)
        {
            if (aLineLyrics.charAt(9) == ']')
            {// 长度为8的时间标签格式
            timeTagLength = 8;
            }
            else if (aLineLyrics.charAt(6) == ']')
            {// 长度为5的时间标签格式
            timeTagLength = 5;
            }
            realLyrcisStartOffset = i;
            isConfirmTimeTagLeng = true;
        }
       
        for (int j = 0; j < partsLrc.length; j++)
        {
            if (partsLrc[j].charAt(0) == '[')
            {// 确定为时间标签部分,而不是歌词内容部分
            String timeTagStr = partsLrc[j].substring(1, timeTagLength+1);
            float timeValue = computeTimeTag(timeTagStr);
            // 添加歌词时间于对应的歌词索引
            lrcTimeValAndIndex.add(new LrcTimeAndIndex(timeValue, i));
            }
            else    // 如果歌词开头的内容也是'['将出现FATAL ERROR
            {// 确定为歌词内容部分
            lyrcisContent.add(partsLrc[j]);
            }
        }
        }
    }
    // 通过对歌词出现时间的先后顺序进行排序
    sortByTimeTag();
    }
   
    /**
         * @param timeStr
         *                表示歌词显示时间的String

         *                例如:

         *                "02:03.30"表示2*60s+3.3s=123.3s=1233ms的时刻

         *                注意:考虑到歌曲长度不会很长,所以算法中直接取前两位作为分钟,

         *                而从':'后的单位是秒。但是因为现在lrc文件格式不是很统一,例如:

         *                "02:03"表示2*60s+3s=123s=1230ms,这是短格式表示,也要考虑到。

         *                目前,将时间标签分为的两种格式,长度分别为8,5
         * @return 计算好的时间值
         */
    private float computeTimeTag(String timeStr)
    {
    int minutes = Integer.parseInt(timeStr.substring(0, 2));    // 取得分钟
    float seconds = 0;
   
    if (timeStr.length() == 8)
    {
        seconds = Float.parseFloat(timeStr.substring(3, 8));    // 取得秒钟
    }
    else if (timeStr.length() == 5)
    {
        seconds = Float.parseFloat(timeStr.substring(3, 5));
    }

    return (minutes * 60 + seconds);    // 返回时间值
    }

    /**
     * 更具歌词显示的先后时间顺序进行排序的标准
     */
    private void sortByTimeTag()
    {
    Collections.sort(lrcTimeValAndIndex, new Comparator()
    {
        // Parameters:
        // o1 - the first object to be compared.
        // o2 - the second object to be compared.
        // Returns:
        // a negative integer, zero, or a positive integer as the first
        // argument is less than, equal to, or greater than the second.
        // Throws:
        // ClassCastException - if the arguments' types prevent them from
        // being compared by this Comparator.
        public int compare(Object o1, Object o2)
        {
        LrcTimeAndIndex fl1 = (LrcTimeAndIndex) o1;
        LrcTimeAndIndex fl2 = (LrcTimeAndIndex) o2;

        if (fl1.getLrcTime() - fl2.getLrcTime() > 0)
        {
            return 1;
        }
        else if (fl1.getLrcTime() - fl2.getLrcTime() > 0)
        {
            return -1;
        }
        else
        {
            return 0;
        }
        }

    });
    }
}

/**
 * 用于保存歌词时间与歌词容器的下标索引
 */
class LrcTimeAndIndex
{
    /**
     * 该行歌词的显示时间
     * @uml.property   name="lrcTime"
     */
    private float lrcTime;

    /**
     * 该行歌词行索引
     * @uml.property   name="lrcIndex"
     */
    private int lrcIndex;

    /**
     * 默认的构造器
     */
    public LrcTimeAndIndex()
    {

    }

    /**
     * 带参数的构造器,设置时间标签和对应的歌词行索引
     * @param lrcTime 时间标签
     * @param lrcIndex 歌词行索引
     */
    public LrcTimeAndIndex(float lrcTime, int lrcIndex)
    {
    this.lrcTime = lrcTime;
    this.lrcIndex = lrcIndex;
    }

    /**
     * 返回歌词应该出现的时间
     * @return   lrcTime 出现时间
     * @uml.property   name="lrcTime"
     */
    public float getLrcTime()
    {
        return lrcTime;
    }

    /**
     * 返回歌词内容行索引
     * @return   歌词内容行索引
     * @uml.property   name="lrcIndex"
     */
    public int getLrcIndex()
    {
        return lrcIndex;
    }
}

/**
 * 文件名:LrcScrollPane.java
 * 环境: GNU/Linux Ubuntu 7.04 + Eclipse 3.2 + JDK 1.6
 * 功能:滚动的Lrc歌词显示面板
 * 版本:0.0.2.0
 * 作者:88250
 * 日期:2007.4.28
 * E-mail & MDN:
 * QQ:845765
 */

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JPanel;
import java.util.Date;

public class LrcScrollPane extends JPanel
{
    private final int heightOfChar = 20;

    private final Font fontOfChar = new Font("monospaced", Font.BOLD, 14);

    private Point scrollPoint; // 参考点,会慢慢向上移。

    private LrcAnalystBase lrcAnalyst;
   
    private static int lrcIndex = 0;
   
    public LrcScrollPane()
    {
    lrcAnalyst = new LrcAnalystBase();
    lrcAnalyst.readFile("六月的雨.lrc");
    lrcAnalyst.parseLyrics();
    scrollPoint = new Point(5, getSize().height);
    }

    /**
     * 实现歌词的滚动显示
     */
    public void scroll()
    {
    Date startTime = new Date();
    for (int i = 0; i < lrcAnalyst.getLrcTimeValAndIndex().size(); i++)
    {
        while (true)
        {
        try
        {
            Thread.currentThread().sleep(50);
           
            Date currentTime = new Date();
            LrcTimeAndIndex fl = (LrcTimeAndIndex) (lrcAnalyst
                .getLrcTimeValAndIndex().get(i));
            float diffTime = currentTime.getTime()
                - startTime.getTime();
            lrcIndex = fl.getLrcIndex()- lrcAnalyst.getRealLrcStartOffset();
            if (fl.getLrcTime() - (float) diffTime / 1000 < 0.0)
            {
            scrollPoint.y--;
            //System.out.println((String) lrcAnalyst.getLrcContent().get(
            //    fl.getLrcIndex()- lrcAnalyst.getRealLrcStartOffset()));
            repaint();
           
            break;
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        }
    }
    }
   
    public void paintComponent(Graphics brush)
    {
    // 用背景色覆盖,原来的文字就看不到了。再写上新的文字,由于视觉停留,
    // 看起来文字就会滚动
    brush.setColor(getBackground());
    brush.fillRect(0, 0, getSize().width, getSize().height);

    brush.setColor(Color.blue);
    brush.setFont(fontOfChar);

    // i为下标,用来历遍歌词缓存容器,
    // 当scrollPoint.y+(heightOfChar*i)<0 时,会把文字写到面板上方,看不见。
    // 没有必要显示。
    // scrollPoint.y+(heightOfChar*i)=0 => i=(-scrollPoint.y/heightOfChar)
    // 故要使(-scrollPoint.y/heightOfChar)和 0 作比较,要是小于0,i取0
    /*int i = 0;
    if ((-scrollPoint.y / heightOfChar) > 0)
    {
        i = (-scrollPoint.y / heightOfChar);
    }*/
   
    /*String lyrics = null;
    for (; i < lrcAnalyst.getLrcContent().size(); i++)
    {
        // 当条件成立,文字会写到面板下方,也没有必要显示,跳出循环
        if (scrollPoint.y + (heightOfChar * i) > getSize().height)
        {
        break;
        }
        lyrics = (String) (lrcAnalyst.getLrcContent().get(i));
        // 见scroll(),靠scrollPoint.y的上移,使文字上移
        brush.drawString(lyrics, scrollPoint.x, scrollPoint.y
             + (heightOfChar * i));
    }    */
   
    String lyrics = (String) lrcAnalyst.getLrcContent().get(lrcIndex);
        // 见scroll(),靠scrollPoint.y的上移,使文字上移
        brush.drawString(lyrics, scrollPoint.x, -scrollPoint.y + 20);
    }
}

3. 工程文件
    LrcAnalyst0.0.2.0 by eclipse 3.2.0

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/DL88250/archive/2007/04/29/1591539.aspx

 
阅读(641) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~