lrc歌词解析(正则表达式)与歌词卡拉ok显示的思路

来源:互联网 发布:电源检测软件 编辑:程序博客网 时间:2024/05/01 06:43

 一、从本地读取lrc文件
     从本地读取文件,android提供了与java相同的io操作,只不过要在AndroidManifest.xml添加读写sdcard权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    只要把路径传输进去,就可以取得io流。


File file=new File(FileUril.SDPATH  + str);


在项目包src/file/FileUril.java中定义了静态的变量
Public SDPATH=Environment.getExternalStorageDirectory()+"/";

此句取得android的根目录("/"),这样就可以通过str这个参数定位到路径里。取得io流FileInputStream in=new FileInputStream(file);然后封装io流,且以UTF-8的格式,BufferedReaderb=newBufferedReader(new InputStreamReader(in,"utf-8")),接下来通过b.readLine()读取一行歌词数据,当读到结束时返回空。通过while循环就可以读取lrc所有数据。

代码如下:

public LrcParser(String str){index=0;musicDate=new ArrayList<String>();map=new HashMap<Long,String>();    String s="";File file=new File(FileUril.SDPATH+str);      try {FileInputStream in=new FileInputStream(file); BufferedReader b=new BufferedReader(new InputStreamReader(in,"utf-8"));while((s=b.readLine())!=null){parser(s);}treeMap=new TreeMap<Long, String>(map);} catch (FileNotFoundException e) {   e.printStackTrace();} catch (IOException e) {e.printStackTrace();}   }  


 

 

 

二、解析歌词lrc文件

   在读取lrc数据时,通过调用parser(s)方法,实现歌词文件的读取,由于歌词信息不仅仅包含歌词,还有歌曲的一些作者信息,所以我们单独用字符判断就可以了。

   在滤除了前面的信息后,设置正则表达式,由于歌词文件信息头为”[59:59.99]“,所以设置正则表达式String p="\\[([0-9]+:[0-9]+.[0-9]+)\\]";在正则表达式里有些标点字符用“\\加该标点符号表示,如“[”,表示成“\\]”,因为“[]-”等是正则表达式的关键字。

   “[0-9]”表示0到9中的一个数字,这样就可以匹配歌词信息。构造Pattern,这样就可以通过Pattern实例调用matcher匹配字符串了,返回的是Matcher对象,再通过该对象的find()方法就可以查询是否匹配了。

    在find()函数返回的布尔类型,当为true时就表示查询的到。查询成功的话,利用pattern.split(str);将要解析的字符串以匹配字符为分割点,将字符串拆分,返回数组数据。

    由于“[59:59.99][59:59.99]”找到的就是空格+空格+再加我们的歌词,我们需要的是最后面的数据,map.put(num,lrcContent[lrcContent.length-1]);知道我为什么len-1了吧。数据0开始,最后一个当然就是len-1了。将我们的数据保存。

    但如何记录对应的时间呢,Matcher的group(int i)返回匹配字符,并前后剔除i个字符String t=m.group(1);所以得到的字符数据位"59:59.99",再通过split将“59”,“59”,“99”分离出来,然后将字符串转换为整型,这样一来,我们的歌词时间点也记录了,这样一个个歌词信息通过map哈希表记录下来了。还有一个问题,由于有些歌词时间点几个都在一行,就是复歌部分,所以此map记录的数据歌词对应的时间就没有按一定的排序。所以我们还用到TreeMap,可以把map直接按键值排列。(另一种方法就是实现Comparable借口来实现排序,这样就更灵活)。


实现代码如下:

public  void parser(String str){if(str.startsWith("[ti:")){musicDate.add(str.substring(4,str.length()-1));}if(str.startsWith("[ar:")){musicDate.add(str.substring(4,str.length()-1));}if(str.startsWith("[al:")){ musicDate.add(str.substring(4,str.length()-1));}String p="\\[([0-9]+:[0-9]+.[0-9]+)\\]";Pattern pattern=Pattern.compile(p);Matcher m=pattern.matcher(str);while(m.find()){String t=m.group(1);    long num=ChartoLong(t);        String lrcContent[]=pattern.split(str);    if(lrcContent.length>1){    map.put(num,lrcContent[lrcContent.length-1]);    }else{    map.put(num,"");    }}}


2.4.1.3歌词显示

    在工程项目res/com/lrc/LrcFactory.java,此类负责根据MediaPlay播放的时间,产生歌词信息数组,产生歌词信息的时间间距,然后将在LrcView里将数据显示在页面上,在这里我们使用到同步显示的概念,要同步,必须在同一个线程里。Android 提供了Handler线程消息传输机制,handler采用堆栈的机制,它有两个作用: 
(1): 安排消息或Runnable 在某个主线程中某个地方执行;
(2):安排一个动作在不同的线程中执行。在播放的同时,负责刷新歌词信息,由于歌词信息时间点不连续,不可能将取得的歌曲时间play.getCurrentPosition()点去取得相应的歌词信息。所以只能去估计,遍历所有歌词时间点,当时间有大于play.getCurrentPosition(),就停止遍历,可想而知,既然记录时间点大了,证明现在播放的是该记录点的前一个。所以返回该位置,然后通过该位置,去刷新要显示的歌词数组,然后将歌词显示出来,在编程之前,规定数组第几个为当前播放的歌词,然后按照这个规定填充数组。显示的时候也按照这个规定显示。这样就实现了歌词同步。但是我们还不满足,我们要实现卡拉ok的歌词渐变功,Shader shader = new LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions, TileMode tile);
paint.setShader(shader);

这个类就能实现字体的渐变过程,x0,y0表示起始的位置,x1,y1,表示终点。这里歌词要横向过去的,所以我们设置
x0=0,y0=0,y1=0,x1等于字符的 长度。Colors这里要设置颜色值,第1位置表示渐变的开始颜色,第2位置就是终点颜色,positions设置的位置值比例,1表示全部,也是要设置两个值,分别表示起始颜色的位置,终点颜色的位置,后面是类型。

Shader shader = new LinearGradient(0,0,lrcLen + paint.getTextSize() * 5,0,new int[] { Color.RED, Color.GREEN },new float[] {deltax* (play.getCurrentPosition() - startTime)+ 0.1f,deltax* (play.getCurrentPosition() - startTime)+ 0.3f }, TileMode.CLAMP);paint.setShader(shader);


    首先,在线程的开始时,记录开始时间startTime,然后也记录与下个歌词时间点的时间间隔,将deltax=1/时间间隔,通过deltax* (play.getCurrentPosition() - startTime)就可是知道多少时间前增加多少,等到play.getCurrentPosition() - startTime大于时间间隔,重新记录时间间隔与开始时间,循环往复。歌词滚动也是同理,详细就代码。这样就可以实现了歌词的卡拉ok功能。



原创粉丝点击