代码已托管到码云,有兴趣的小伙伴可以下载看看

https://git.oschina.net/joy_yuan/MobilePlayer

效果图:

有一个小的遗憾,就是该MP3文件和歌词文件要在同一路径下,才能读取到歌词,否则读取不到录音文件。

将录音文件发到这里,是.lrc格式的文件,其实TXT文件的也行;如果在手机上显示是乱码的话,就改一下文件的编码为Unicode,再尝试下。

[ti:北京北京]
[00:00.05]献给我最爱的老婆 --常长丽
[00:02.17]歌曲名:北京北京
[00:04.00]演唱:汪峰
[00:06.84]原立鹏 制
[00:08.62] 献给我最爱的老婆 --常长丽
[00:31.16]当我走在这里的每一条街道
[00:37.32]我的心似乎从来都不能平静
[00:45.23]就让花朵妒忌红名和电气致意
[00:51.66]我似乎听到了他这不慢的心跳
[00:59.74]我在这里欢笑我在这里哭泣
[01:06.93]我在这里活着也在这死去
[01:14.09]我在这里祈祷 我在这里迷惘
[01:21.25]我在这里寻找 在这里寻求
[04:11.76][04:04.59][02:31.70][01:27.19]北京 北京
[01:35.46]咖啡管与广场又散着天气
[01:41.73]就象夜空的哪月亮的距离
[01:49.80]人们在挣扎中相互告慰和拥抱
[01:56.14]寻找着 追著着 夜夜时的睡梦
[02:04.19]我在这欢笑 我们在这哭泣
[02:11.23]我在这活着也在这死去
[02:18.70]我在这祈祷 我在这迷惘
[02:25.76]我在这寻找 在这追求
[03:08.56]如果有一天我不得不离去
[03:14.55]我希望人们把我埋葬在这里
[03:22.95]在这忘了感觉到我在存在
[03:29.10]在这有太多有我眷恋的东西
[03:37.38]我在这欢笑 我在这哭泣
[03:44.55]我在这里活着也在这死去
[03:51.74]我在这里祈祷 我在这里迷惘
[03:58.90]我在这里寻找 也在这死去
[04:04.35]北京 北京
[04:11.48]北京 北京

1、新建一个解析歌词的类,接收一个File类型的文件,由于解析的类太复杂,就不在这里分析,直接贴源码,大家以后用的时候直接参考即可

package com.yuanlp.mobileplayer.utils;import com.yuanlp.mobileplayer.bean.Lyrc;import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;/*** Created by 原立鹏 on 2017/7/31.* 解析歌词工具类*/public class LyrcUtils {/*** 得到解析好的歌词列表* @return*/public ArrayList<Lyrc> getLyrcs() {return Lyrcs;}private ArrayList<Lyrc> Lyrcs;/*** 是否存在歌词* @return*/public boolean isExistsLyrc() {return isExistsLyrc;}/*** 是否存在歌词*/private boolean isExistsLyrc  = false;/*** 读取歌词文件* @param file /mnt/scard/audio/beijingbeijing.txt*/public void readLyrcFile(File file){if(file == null || !file.exists()){//歌词文件不存在Lyrcs = null;isExistsLyrc = false;}else{//歌词文件存在//1.解析歌词 一行的读取-解析Lyrcs = new ArrayList<>();isExistsLyrc = true;BufferedReader reader = null;try {reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),getCharset(file)));String line = "";while ((line = reader.readLine())!= null){line = parsedLyrc(line);//}reader.close();} catch (Exception e) {e.printStackTrace();}//2.排序Collections.sort(Lyrcs, new Comparator<Lyrc>() {@Overridepublic int compare(Lyrc lhs, Lyrc rhs) {if(lhs.getTimePosition() < rhs.getTimePosition()){return  -1;}else if(lhs.getTimePosition() > rhs.getTimePosition()){return  1;}else{return 0;}}});//3.计算每句高亮显示的时间for(int i=0;i<Lyrcs.size();i++){Lyrc oneLyrc = Lyrcs.get(i);if(i+1 < Lyrcs.size()){Lyrc twoLyrc = Lyrcs.get(i+1);oneLyrc.setSleepTime(twoLyrc.getTimePosition()-oneLyrc.getTimePosition());}}}}/*** 判断文件编码* @param file 文件* @return 编码:GBK,UTF-8,UTF-16LE*/public String getCharset(File file) {String charset = "GBK";byte[] first3Bytes = new byte[3];try {boolean checked = false;BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));bis.mark(0);int read = bis.read(first3Bytes, 0, 3);if (read == -1)return charset;if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {charset = "UTF-16LE";checked = true;} else if (first3Bytes[0] == (byte) 0xFE&& first3Bytes[1] == (byte) 0xFF) {charset = "UTF-16BE";checked = true;} else if (first3Bytes[0] == (byte) 0xEF&& first3Bytes[1] == (byte) 0xBB&& first3Bytes[2] == (byte) 0xBF) {charset = "UTF-8";checked = true;}bis.reset();if (!checked) {int loc = 0;while ((read = bis.read()) != -1) {loc++;if (read >= 0xF0)break;if (0x80 <= read && read <= 0xBF)break;if (0xC0 <= read && read <= 0xDF) {read = bis.read();if (0x80 <= read && read <= 0xBF)continue;elsebreak;} else if (0xE0 <= read && read <= 0xEF) {read = bis.read();if (0x80 <= read && read <= 0xBF) {read = bis.read();if (0x80 <= read && read <= 0xBF) {charset = "UTF-8";break;} elsebreak;} elsebreak;}}}bis.close();} catch (Exception e) {e.printStackTrace();}return charset;}/*** 解析一句歌词* @param line [02:04.12][03:37.32][00:59.73]我在这里欢笑* @return*/private String parsedLyrc(String line) {indexOf第一次出现[的位置int pos1 = line.indexOf("[");//0,如果没有返回-1int pos2 = line.indexOf("]");//9,如果没有返回-1if(pos1 ==0 && pos2 != -1){//肯定是由一句歌词//装时间long[] times = new long[getCountTag(line)];String strTime =line.substring(pos1+1,pos2) ;//02:04.12times[0] = strTime2LongTime(strTime);String content = line;int i = 1;while (pos1 ==0 && pos2 != -1){content = content.substring(pos2 + 1); //[03:37.32][00:59.73]我在这里欢笑--->[00:59.73]我在这里欢笑-->我在这里欢笑pos1 = content.indexOf("[");//0/-1pos2 = content.indexOf("]");//9//-1if(pos2 != -1 ){strTime = content.substring(pos1 + 1, pos2);//03:37.32-->00:59.73times[i] = strTime2LongTime(strTime);if(times[i] == -1){return  "";}i++;}}Lyrc Lyrc = new Lyrc();//把时间数组和文本关联起来,并且加入到集合中for(int j = 0;j < times.length;j++){if(times[j] !=0){//有时间戳Lyrc.setContent(content);Lyrc.setTimePosition(times[j]);//添加到集合中Lyrcs.add(Lyrc);Lyrc = new Lyrc();}}return  content;//我在这里欢笑}return "";}/*** 把String类型是时间转换成long类型* @param strTime 02:04.12* @return*/private long strTime2LongTime(String strTime) {long result = -1;try{//1.把02:04.12按照:切割成02和04.12String[] s1 = strTime.split(":");//2.把04.12按照.切割成04和12String[] s2 = s1[1].split("\\.");//1.分long min = Long.parseLong(s1[0]);//2.秒long second = Long.parseLong(s2[0]);//3.毫秒long mil = Long.parseLong(s2[1]);result =  min * 60 * 1000 + second * 1000 + mil*10;}catch (Exception e){e.printStackTrace();result = -1;}return result;}/*** 判断有多少句歌词* @param line [02:04.12][03:37.32][00:59.73]我在这里欢笑* @return*/private int getCountTag(String line) {int result = -1;String [] left = line.split("\\[");String [] right = line.split("\\]");if(left.length==0 && right.length ==0){result = 1;}else if(left.length > right.length){result = left.length;}else{result = right.length;}return result;}
}

2、在audioplayerActivity里,接收到service发送的EventBus的准备好播放的事件后,获取该MP3 文件的路径,最后获取歌词并解析,然后发送到handler里来显示歌词

/*** 订阅eventbus*/
@Subscribe(threadMode=ThreadMode.MAIN,sticky = false,priority = 99)
public void showData(MediaItem item) {showLyrc();  //去获取MP3文件的路径然后获取歌词文件,并解析歌词,最终显示歌词showViewData();checkPlayMode();
}/*** 去获取MP3文件的路径然后获取歌词文件,并解析歌词,最终显示歌词*/
public void showLyrc() {LyrcUtils lyrcUtils=new LyrcUtils();try {//得到录音文件的地址String path=mservice.getAudioPath();path=path.substring(0,path.lastIndexOf("."));File file=new File(path+".lrc");  //先去查找lrc格式的歌词文件if (!file.exists()){file=new File(path+".txt");  //拼一个TXT格式的歌词文件}lyrcUtils.readLyrcFile(file);showlyrcView.setLyrcs(lyrcUtils.getLyrcs());if (lyrcUtils.isExistsLyrc()){//存在歌词,才发消息,否则不发消息handler.sendEmptyMessage(SHOW_LYRC);}} catch (RemoteException e) {e.printStackTrace();}}

最终实现了歌词的解析。

这样实现的歌词,匹配到不同的分辨率的手机上时,字的大小会不同,那么下面就是来解决这个显示字大小的修改,最终效果如下。

定义一个工具类,DensityUtil,里面代码如下:

    /*** 根据手机的分辨率从 dip 的单位 转成为 px(像素)*/public static int dip2px(Context context, float dpValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}/*** 根据手机的分辨率从 px(像素) 的单位 转成为 dp*/public static int px2dip(Context context, float pxValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (pxValue / scale + 0.5f);  }

那么我们只需要在各自文字设置那里,通过这个工具类,修改下文字大小即可。

在ShowlyrcView类里,把textHeight=DensityUtil.dip2px(context,20);

然后2个画笔处,修改文字大小即可

//创建画笔----当前高亮的画笔
paint=new Paint();
paint.setColor(Color.GREEN); //高亮颜色
paint.setTextSize(DensityUtil.dip2px(context,20));
paint.setAntiAlias(true); //抗锯齿
paint.setTextAlign(Paint.Align.CENTER);  //对齐方式,居中显示//白色画笔
whitepaint=new Paint();
whitepaint.setColor(Color.WHITE);
whitepaint.setTextAlign(Paint.Align.CENTER);
whitepaint.setTextSize(DensityUtil.dip2px(context,20));
whitepaint.setAntiAlias(true);

最终达到了将歌词匹配屏幕的大小。

转载于:https://blog.51cto.com/cm0425/1952517

手机影音第十七天,实现歌词同步相关推荐

  1. android 歌词同步代码,android手机音乐播放器实现歌词同步

    最近在做一款android手机上的音乐播放器,学习到了很多东西,像是Fragment,ActionBar的使用等等,这里就先介绍一下歌词同步的实现问题. 歌词同步的实现思路很简单:获取歌词文件LRC中 ...

  2. Android 手机影音 开发过程记录(六)

    Android 手机影音 开发过程记录(六) 前一篇已经将音乐播放及切换的相关逻辑弄好了,今天主要理一下剩余的部分,包含: 1. 自己定义通知栏的布局及逻辑处理 2. 滚动歌词的绘制 3. 歌词解析 ...

  3. 安卓音乐播放器中歌词同步问题

    音乐文件是.lrc格式的,lrc格式的文件,是MP3播放器唯一能识别的歌词文件,在MP3播放器中可以去同步 显示歌词.它是一种包含着"[]"形式的"标签"的.基 ...

  4. Android视频《手机影音_项目实战》-杨光福-专题视频课程

    Android视频<手机影音_项目实战>-49877人已学习 课程介绍         手机影音项目是真实的上线项目,本视频在原项目基础上,进行了新技术的更新和优化.该项目包括本地音乐播放 ...

  5. HTML5实践之歌词同步播放器

    歌曲播放我们会发现他的兼容性不是很好,譬如IE上能播放的flash播放器,再firfox或者chrome上就不是很好的应用了,因为有插件的阻碍!HTML5的出现让这一切成为了可能,但是播放器虽然播放了 ...

  6. php中文歌词,详细介绍HTML5使用Audio标签实现歌词同步的效果

    HTML5的最强大之处莫过于对媒体文件的处理,如利用一个简单的vedio标签就可以实现视频播放.类似地,在HTML5中也有对应的处理音频文件的标签,那就是audio标签.通过本文给大家介绍HTML5使 ...

  7. mysql ubb html_UBB中轻松实现歌词同步播放_html

    Windows Media Player 格式: 1.[wmp=http://homepage.yesky.com/a.wmv]歌词内容[/wmp] 2.[wmp=http://homepage.ye ...

  8. 论HTML5 Audio 标签歌词同步的实现

    HTML5草案里面其实有原生的字幕标签(<track> Tag)的,但使用的是vtt格式的文件,非常规的字幕(.sub, .srt)或歌词文件(.lrc). 用法如下(代码来自W3Scho ...

  9. TextView实现歌词同步《IT蓝豹》

    2019独角兽企业重金招聘Python工程师标准>>> 利用TextView实现歌词同步显示,这是一个简单的利用TextView实现滚动实时显示歌词的. 里面的内容都已经写上了详细的 ...

  10. html+js的lrc歌词同步播放器

    <html> <head> <title>lrc歌词同步播放器</title> <style> body, td { font-family ...

最新文章

  1. div 下 的img水平居中
  2. 评分卡模型、WOE(Weight of Evidence)和IV(Information Value)
  3. Handler研究2-AsyncTask,AsyncQueryHandler分析
  4. Java并发基本概念
  5. centos普通用户SSH免密登录
  6. GMM_example(1)
  7. when is One Order gt_plan_exets filled
  8. 最受IT公司欢迎的50款开源软件
  9. 转:Jmeter 用户思考时间(User think time),定时器,和代理服务器(proxy server)...
  10. CSS3渐变——线性渐变
  11. python 抽象类、抽象方法、接口、依赖注入、SOLIP
  12. Jakarta EE 9 企业版本合规性
  13. html 报文头 参数 详解 本文转载于 http://stephen830.javaeye.com/blog/335578
  14. 强烈推荐一个在线caffe网络可视化工具!!
  15. 如何给Word参考文献加方括号
  16. 基于改进区域生长算法的图像分割方法及实现
  17. 如何换主题不被搜索降权
  18. 社科院与杜兰大学金融管理硕士项目——有规划的人生值得期待,你当下的规划是什么呢
  19. HDU oj wod sticks
  20. Web服务器之Tomcat大全

热门文章

  1. 电源输出端串入IN4007,测量正负电压,压降只有0.3v,为什么不是0.7v左右呢?
  2. 什么是群发单显和分别发送,有什么区别,发客户邮件忘记群发单显
  3. 编译 nginx + http-flv 模块
  4. 快递柜智能柜C语言程序,智能快递柜的设计与实现_李浩然.pdf
  5. 【Linux】【RedHat】下载 安装 注册
  6. “抛弃 Gmail!”
  7. PayPal美元和人民币之货币转换问题
  8. element click intercepted
  9. 云计算储存基础(3)
  10. html5 spice 虚拟桌面,开源桌面虚拟化spice体验