js 音乐播放器的实现以及可视化

本贴最后更新于 1961 天前,其中的信息可能已经时异事殊

前言

我们尝试用原生 js 写一个音乐播放器,对音频做一个简单的可视化。

最终的效果如下图:

思路还是比较明显的,第一,我们要拿到音频的什么数据进行可视化,如何获取?第二,如何可视化,第二个问题就比较简单了,用 canvas 来操作。

获取音频相关的数据可以用 Web Audio API , 这里我们可以去 MDN 或者 W3C 上查看相关的文档。

audio API

我们都知道在 web 中播放音频用 audio 这个标签来操作。Web Audio API 是对 audio 标签功能上的补充,它可以:

  1. 一秒内同时发出多个声音
  2. 把音频流独立出来,进行复杂操作
  3. 将音频输出到多个地方,比如 canvas,从而实现可视化

audio node 有入口和出口,多个节点构成类似链表一样的结构,从一个或者多个音源出发,经过一个或者多个处理节点,最终输出到输出节点。web audio 的一个简单处理过程如下:

  1. 创建 AudioContext 对象
  2. 在 AudioContext 对象内设置音源,例如
  3. 创建 affcet node(效果节点)
  4. 选择音频的最终输出节点
  5. 音频经过效果节点处理后,然后输入到下一个节点
// 获取AudioContext对象
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext;
// 创建AudioContext对象实例
var actx = new AudioContext();
// 创建AuidoSource,此处的audios为<audio>标签
var audioSrc = actx.createMediaElementSource(audios);
var analyser = actx.createAnalyser();
// 输入的连接
audioSrc.connect(analyser);
// 输出的连接
analyser.connect(actx.destination);
// 本例子中我们获取音高数据来进行可视化
var voiceHigh = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(voiceHigh);

canvas

用 canvas 对音高数据进行可视化展示。

function draw() {
    var step=Math.round(voiceHigh.length/num);
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.beginPath();
    for(var i=0; i<num; i++) {
	 var value = voiceHigh[step*i];

         ctx.fillColor = color;
	 ctx.fillRect(i * 10 + canvas.width * .5, 250, 7, -value + 1);
         ctx.fillRect(canvas.width * .5 - (i - 1) * 10, 250, 7, -value + 1);
         ctx.fill();
         ctx.fillStyle = colort;
         ctx.fillRect(i * 10 + canvas.width * .5, 250, 7, value + 1);
         ctx.fillRect(canvas.width * .5 - (i - 1) * 10, 250, 7, value + 1);

     }
    requestAnimationFrame(draw);
}

进度条

进度条算是比较简单好做的了,我们用一个背景 div 填充,另外一个 div 表示当前进度,宽度是动态变化的,根据音频播放的当前时间除以总时间,然后乘背景 div 的宽度,计算得到结果即可。

var bglen = document.getElementById('bgline').offsetWidth;
var fillbar = document.getElementById('fillbar');
fillbar.style.width = parseFloat(cur / duration) * bglen + 'px';

滚动歌词

歌词这一块,首先要找好思路,如何解析歌词?如何控制歌词滚动?如何判断当前是哪句歌词?弄清楚这几个问题后,事情就变得不困难了。

解析歌词

这一块可能是这部分内容里面最复杂的,需要一些简单的算法,这里用到了递归。

解析前的 lrc 歌词是这样的:

[ti:不要在我寂寞的时候说爱我] [ar:郑源] [by:] [00:21.57]柔柔的晚风轻轻吹过 [00:25.39]我的心情平静而寂寞 [00:29.96]当我想忘记爱情去勇敢生活 [00:34.11]是谁到我身边唱起了情歌 [01:48.86][00:39.01]当初的爱情勿匆走过 [01:52.69][00:42.79]除了伤口没留下什么 [01:57.27][00:47.40]你总是在我寂寞流泪的时候 [02:01.67][00:51.80]用你的双臂紧紧抱着我 [02:59.75][02:05.19][00:55.27]不要在我寂寞的时候说爱我 [03:04.15][02:09.69][00:59.96]除非你真的能给予我快乐 [03:09.30][02:14.78][01:04.97]那过去的伤总在随时提醒我 [03:13.94][02:19.40][01:09.65]别再被那爱情折磨 [03:17.17][02:22.65][01:12.82]不要在我哭泣的时候说爱我 [03:21.62][02:27.11][01:17.32]除非你真的不让我难过 [03:25.65][02:31.13][01:21.33]我不想听太多那虚假的承诺 [03:30.00][02:35.44][01:25.69]让我为爱再次后悔犯下的错!

解析处理后是这样的:

image.png

function getnext(y, lrc) {
    var rs = '';
    var i = y + 1;
    if(lrc[i]) {
        t = lrc[i].split(']');
        if(t[1] == '') {
	    rs = getnext(i, lrc);
        }else {
	    rs = t[1]
    }
     }
     return rs;
 }

歌词滚动

歌词滚动最简单的一种方法,让包含歌词的盒子的 top 值不断减少,歌词就会一直往上走。通过当前播放的时间和歌词的时间对个比较,判断当前时间对应的歌词,给对应的 dom 添加标识。

cur = parseInt(audio.currentTime);
min = parseInt(cur / 60);
sec = parseInt(cur % 60);
sec = sec < 10 ? '0' + sec : sec;
curTime.innerHTML = min + ':' + sec;
fillbar.style.width = parseFloat(cur / duration) * bglen + 'px';
if(lrcTimeArr.indexOf(cur) > -1) {
	document.querySelectorAll('#lrc li').forEach(function(e) {
		e.className = '';
	})
	document.querySelector('#lrc li[time="'+cur+'"]').className = 'active';
	lrcEl.style.top = lrcOffsetTop - lrcTimeArr.indexOf(cur) * 16 + 'px';
}

写在最后

以上是在音乐播放器以及可视化方面的一些实验。

  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    729 引用 • 1327 回帖
  • 可视化
    6 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...