卡拉OK歌词原理和实现高仿Android网易云音乐

大家好,我们是爱学啊,继上一篇讲解了【LRC歌词原理和实现高仿Android网易云音乐】,今天给大家带来一篇关于卡拉OK歌词原理和在Android上如何实现歌词逐字滚动的效果,本文来自【Android开发项目实战我的云音乐】课程。

效果图

相信大家都懂一张图胜过千言万语。

ksc.gif

效果和现在市面上大部分播放器差不多,当然如果要运用到商业项目中,肯定还需要进行一些优化,例如:滚动效果有弹性,字体大小,字体颜色等。

什么是卡拉OK歌词

要明白什么是卡拉OK歌词,就先要搞明白什么是卡拉OK,简单来讲就是卡拉OK是一种伴奏系统,演唱者可以在预先录制的音乐伴奏下参与唱歌,现多叫KTV(Karaoke);卡拉OK歌词默认格式为ksc,当然现在市面上的一些软件在他的基础上做了定制,具体的在我们的课程中讲解了;我们这里就讲解ksc,因为卡拉OK歌词的核心就是精确到每一个字,所以搞明白他的原理,我们也就可以在他的基础上定制了。

LRC歌词格式

在实现歌词功能前,肯定需要搞明白ksc歌词格式,例如:我们找了一段LRC歌词:

karaoke := CreateKaraokeObject;
karaoke.rows := 2;
karaoke.TimeAfterAnimate := 2000;
karaoke.TimeBeforeAnimate := 4000;
karaoke.clear;
karaoke.add('00:20.699', '00:27.055', '[●●●●●●]', '7356',RGB(255,0,0));

karaoke.add('00:27.487', '00:32.068', '一时失志不免怨叹', '347,373,1077,320,344,386,638,1096');
karaoke.add('00:33.221', '00:38.068', '一时落魄不免胆寒', '282,362,1118,296,317,395,718,1359');
karaoke.add('00:38.914', '00:42.164', '那通失去希望', '290,373,348,403,689,1147');
karaoke.add('00:42.485', '00:44.530', '每日醉茫茫', '298,346,366,352,683');
karaoke.add('00:45.273', '00:49.029', '无魂有体亲像稻草人', '317,364,380,351,326,351,356,389,922');
karaoke.add('00:50.281', '00:55.585', '人生可比是海上的波浪', '628,1081,376,326,406,371,375,1045,378,318');
karaoke.add('00:56.007', '01:00.934', '有时起有时落', '303,362,1416,658,750,1438');
karaoke.add('01:02.020', '01:04.581', '好运歹运', '360,1081,360,760');
karaoke.add('01:05.283', '01:09.453', '总嘛要照起来行', '303,338,354,373,710,706,1386');
karaoke.add('01:10.979', '01:13.029', '三分天注定', '304,365,353,338,690');
karaoke.add('01:13.790', '01:15.950', '七分靠打拼', '356,337,338,421,708');
karaoke.add('01:16.339', '01:20.870', '爱拼才会赢', '325,1407,709,660,1430');

可以看到内容是用换行符分割的,如果这些数据是通过接口返回,而不是直接返回一个文件,那么这里面的换行符应该变为\n换行符,这一点我们也在课程中讲解到了。

每一行是一句歌词;每一行歌词又分为四部分:

第一部分:这一行开始时间
第二部分:这一行结束时间
第三部分:这一句歌词
第四部分:每个字持续的毫秒

其中顶部的一些信息是元数据:不同的播放器可能实现不一样。

查看上面的歌词,我们可以发现有大部分的重复内容,所以可以定制。

歌词滚动原理

将每行歌词前面的时间解析后,转为毫秒,这样播放器在播放的时候可以获取到播放时间,然后拿着时间查找当前时间对应哪一行歌词,然后在查看当前时间对应该行的哪一个字,然后进行相应的绘制,具体的在可以有讲解。

歌词解析

歌词解析就很简单了,就是字符串拆分,所以就不贴代码了;但希望大家在写代码的时候不要只局限于功能,也要注重架构;歌词有很多种,所以可以搞成用不同的类来解析,对外暴露统一的接口;这部分在课程中有讲解。

歌词绘制

不同的平台也不一样,我们这里是Android,所以绘制用Canvas。我们这里的思路是:歌词View的高度是固定的,由于我们希望当前行歌词始终显示到歌词View中间,所以先算出View的中心高度,然后在该位置绘制当前行歌词,这一步根据不同的歌词处理的逻辑也不一样,但歌词可分为两类,一类是逐行,一类是逐字,对于逐行来说就直接绘制就行了,只是颜色,大小不一样而已;逐字下一节讲解;然后从当前行歌词位置像前绘制歌词,直到超出View顶部为止,在从当前行歌词向下歌词绘制,直到超出View底部为止;当前你可以使用LinearLayout添加所有歌词当前容器内,然后滚动。

相对于LRC歌词,只需要添加ksc格式格式时绘制:

if (lyric.isAccurate()) {
    //精确到字歌词,格式可以有很多种
    //只是解析的时候不一样,但都组成成通用的model
    //所以在歌词View中,我们已经不需要知道是ksc,还是QQ歌词,还是酷狗歌词等。
    canvas.drawText(line.getLineLyrics(), x, y, backgroundTextPaint);

    if (lyricCurrentWordIndex == -1) {
        //该行已经播放完了
        lineLyricPlayedWidth = textWidth;
    } else {
        String[] lyricsWord = line.getLyricsWord();
        int[] wordDuration = line.getWordDuration();

        //获取当前时间前面的文字
        String beforeText = line.getLineLyrics().substring(0, lyricCurrentWordIndex);
        float beforeTextWidth = getTextWidth(foregroundTextPaint, beforeText);

        //当前字
        String currentWord = lyricsWord[lyricCurrentWordIndex];
        float currentWordTextWidth = getTextWidth(foregroundTextPaint, currentWord);

        //当前字已经演唱的宽度
        float currentWordWidth = currentWordTextWidth / wordDuration[lyricCurrentWordIndex] * wordPlayedTime;

        lineLyricPlayedWidth = beforeTextWidth + currentWordWidth;
    }

    canvas.save();
    //裁剪一个矩形用来绘制已经唱的歌词
    canvas.clipRect(x, y - textHeight, x + lineLyricPlayedWidth,
            y + textHeight);


    //这个矩形包是文字的高度+行高
    //canvas.drawRect(x, y - textHeight, x + lineLyricPlayedWidth,
    //        y + textHeight,foregroundTextPaint);

    canvas.drawText(line.getLineLyrics(), x, y, foregroundTextPaint);

    canvas.restore();
} else {
    //精确到行
}

歌词滚动

歌词和LRC是一样的。

到这里歌词View核心功能基本就实现完成了,如果要深入学习可以查看我们的【高仿Android网易云音乐】课程,或者在线电子书【电子书】。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容