使用Web Audio与three.js实现音乐可视化

简介


HTML5可以不借助其他的插件仅仅通过浏览器实现非常酷炫的事情,而这篇文章就是关于借助three.js以及Web Audio接口来实现声音可视化的。Web Audio允许你在浏览器端操作音频,需要了解更多关于Web Audio的相关信息请移步至这 MDN Web Audio API。而关于THREE.JS,官网上说是一个让创建WebGL应用变得简单的javascript 3D库。这篇文章的目的就是以Web Audio接口获取音频信息并且通过three.js实现3D可视化。https://imgconvert.csdnimg.cn/aHR0cDovL3JhYXRoaWdlc2guY29tL2ltYWdlcy8yMDE0LTEwLTE4LUF1ZGlvJTIwVmlzdWFsaXphdGlvbiUyMHdpdGglMjBXZWIlMjBBdWRpbyUyMGFuZCUyMFRocmVlLmpzL0RlbW8uUE5H
让我们首先把过程分成单个的任务:

  • 创建一个Three.js场景并添加一些长方形条进去
  • 获取声音文件信息
  • 将音频信息解析并且渲染在视图上

示例


可以点来查看已经完成的示例

创建1个Three.js场景


创建一个3d场景并能让其看到需要下面三个最主要的元素:

  • 场景
  • 相机
  • 渲染器
function AudioVisualizer(){
	//渲染
	this.scene;
	this.camera;
	this.renderer;
	this.controls;
}

AudioVisualizer类里面放置了所有必要的东西,下面需要一个一个地初始化three.js的必要元素,这里展示的函数用于创建一个Threejs场景,一个WebGL渲染器,一个相机以及一个光源,如果你想要更多关于Three.js的信息,我建议你去看看专注前端30年的博客。当然官网也是不错的选择(最好的选择)。

AudioVisualizer.prototype.initialize = function(){
	//创建ThreeJS 场景
	this.scene = new THREE.Scene();
	
	//获取窗口长宽
	var  WIDTH = window.innerWidth,
		 HEIGHT = window.innerHeight;
	
	//获取渲染器
	this.renderer = new THREE.WebGLRenderer({antialias:true});
	this.renderer.setSize(WIDTH,HEIGHT);

	//创建并且加入相机
	this.camera = new THREE.PerspectiveCamera(40,WIDTH/HEIGHT,0.1,20000);
	this.camera.position.set(0,45,0);
	this.scene.add(this.camera);

	var that = this;

    //update renderer size, aspect ratio and projection matrix on resize
    //更新渲染器大小,方向(横竖)以及投影矩阵
    window.addEventListener('resize', function () {

        var WIDTH = window.innerWidth,
            HEIGHT = window.innerHeight;

        that.renderer.setSize(WIDTH, HEIGHT);

        that.camera.aspect = WIDTH / HEIGHT;
        that.camera.updateProjectionMatrix();

    });

    //背景颜色
    this.renderer.setClearColor(0x333F47, 1);

    //创建光源并添加到场景中
    var light = new THREE.PointLight(0xffffff);
    light.position.set(-100, 200, 100);
    this.scene.add(light);
}

一旦3d场景创建好之后,我们就能在里面加入3d几何体,createBars 函数创建了0.5x0.5x0.5的长方体(当然目前是正方体),每个长方体都有一个随机的颜色,这样看起来更酷炫一点23333,每个创建的条状物(长方体)都存储在一个数组中,便于之后根据音频信息来改变它们的形状。

//创建可视化所需的条状物(长方体)
AudioVisualizer.prototype.createBars = function () {

    //重复创建长方体
    for (var i = 0; i < this.numberOfBars; i++) {

        //长方体
        var barGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);

        //材质
        var material = new THREE.MeshPhongMaterial({
            color: this.getRandomColor(),
            ambient: 0x808080,
            specular: 0xffffff
        });

        //创建几何体框架并且放到特定的位置上
        this.bars[i] = new THREE.Mesh(barGeometry, material);
        this.bars[i].position.set(i - this.numberOfBars/2, 0, 0);

        //将长方体放到场景中
        this.scene.add(this.bars[i]);
    }
};

读取音频信息内容


现在我们有用来可视化的3d场景和3d块状物(条状物/长方体),下面就要从服务器或者用户自己的电脑里获得音频文件了,从服务器请求一个特定的文件并让它可视化确实没啥问题,但是如果用户自己可以把自己喜欢的音乐拉到里面,让他能够亲眼看到这牛逼的画面不是更有趣么。
下面的代码块可以直接读取拖到浏览器页面的文件,HTML真是牛逼坏了=。=

AudioVisualizer.prototype.handleDrop = function () {
    //丢下文件
    document.body.addEventListener("drop", function (e) {
        e.stopPropagation();

        e.preventDefault();

        //获取文件
        var file = e.dataTransfer.files[0];
        var fileName = file.name;

        $("#guide").text("Playing " + fileName);

        var fileReader = new FileReader();

        fileReader.onload = function (e) {
            var fileResult = e.target.result;
            visualizer.start(fileResult); //这里其实并没有真的开始
        };

        fileReader.onerror = function (e) {
          debugger
        };

        fileReader.readAsArrayBuffer(file);
    }, false);
}

解析音频信息并渲染到场景中


终于到了最为关键的时刻了!音频文件信息获取之后,我们得把它传递给之前创建的3d场景中,为了能够可视化,还需创建一些Web Audio元素并且将它们连接起来,没错,你没听错就是连接
这些元素是:

  • AudioContext
  • javascript Node
  • Buffer Source
  • Analyser

这些节点要以下面这样的方式链接起来,我们不需要知道这些功能的具体细节,因为我们又不是给好莱坞做一个重磅音频可视化产品-。-

我们需要创建一个脚本处理器(Script Processor),脚本处理器是一个可以用javascript来做音频输出处理的音频处理节点。

提醒一下,脚本处理器已经被弃用了,具体细节请查看这里本译者将在另外一篇博客中介绍更好的方法。

注意:这个特性在2014年8月29日发布的Web Audio API规范中已经标记为不推荐,将很快会被Audio Workers代替.

下面我们将创建一个源缓冲来放置音频,分析器(analyser)可以提供音频的实时信息,便于我们渲染与之相关的3d场景
首先将源缓冲与分析器连接起来,分析器连接javascript节点源缓冲连接destination,destination其实就是音频输出,如果不将destination与源缓冲连接起来的话,就算我们能看到显示出来的东西,也不能听到声音

AudioVisualizer.prototype.setupAudioProcessing = function () {
    //获取AudioContext
    this.audioContext = new AudioContext();

    //创建javascript节点
    this.javascriptNode = this.audioContext.createScriptProcessor(2048, 1, 1);//此处参数为缓冲区大小(2的倍数),输入声道与输出声道
    this.javascriptNode.connect(this.audioContext.destination);

    //创建音源
    this.sourceBuffer = this.audioContext.createBufferSource();

    //创建分析器 
    this.analyser = this.audioContext.createAnalyser();
    this.analyser.smoothingTimeConstant = 0.3;
    this.analyser.fftSize = 512;

    //将音源与分析器连接
    this.sourceBuffer.connect(this.analyser);

    //分析器与javascript节点连接
    this.analyser.connect(this.javascriptNode);

    //分析器与音频输出连接(就是喇叭--。耳机等等)
    this.sourceBuffer.connect(this.audioContext.destination);

    var that = this;

    //这里就是将喇叭信息经过javascript处理并且可视化的过程
    this.javascriptNode.onaudioprocess = function () {

        // 通过分析器解析出音频频率与音强信息
        var array = new Uint8Array(that.analyser.frequencyBinCount);
        that.analyser.getByteFrequencyData(array);

        //渲染
        visualizer.renderer.render(visualizer.scene, visualizer.camera);

        var step = Math.round(array.length / visualizer.numberOfBars);

        //循环改变不同长方体在z轴上的缩放
        for (var i = 0; i < visualizer.numberOfBars; i++) {
            var value = array[i * step] / 4;
            value = value < 1 ? 1 : value;
            visualizer.bars[i].scale.z = value;
        }
    }
};

'onaudioprocess’事件会在有声音播放的时候一直触发并提供音频信息,这个方法可以从分析器那获取频率信息,渲染场景,根据不同频率的音强变化改变长方体的z轴上的缩放
(译者按:此处使用的onaudioprocess事件有性能上的问题,当创建的ScriptProcessor缓冲区大小为2048时,每秒只有22次左右的触发,使用谷歌浏览器69.0,所以在显示的时候长方体的变化看起来有点卡卡的,而且这个函数在没有播放声音的时候也会触发,将其改为1024会好很多 (值越小则可能导致一定的延迟)(取值256, 512, 1024, 2048, 4096, 8192, 16384),其实也可以不写)

开始音频处理


所有用于处理的函数都设置好了,下面就要开始进行音频处理。我们从文件读取器(fileReader)读取文件并且用"audioContext.decodeAudioData"这个方法来解析出音频信息。这个方法有两个回调,一个是解析失败,一个是解析成功,解析成功之后将其放置在一个缓冲之中,然后就可以通过"start()"函数开始播放音频,当音频播放的时候"javascriptNode.onaudioprocess"将会被持续触发并输出当前时间的音频频率信息,在3d场景中获得这些信息并把它展现出来

//开始处理音频信息
AudioVisualizer.prototype.start = function (buffer) {
    this.audioContext.decodeAudioData(buffer, decodeAudioDataSuccess, decodeAudioDataFailed);
    var that = this;

    function decodeAudioDataSuccess(decodedBuffer) {
    	//decodedBuffer为解析成功之后的音频缓冲
        that.sourceBuffer.buffer = decodedBuffer
        that.sourceBuffer.start(0);
    }

    function decodeAudioDataFailed() {
        debugger
    }
};

工具


下面的方法用于生成一个随机的颜色,来源于stackoverflow上面的一个问题的答案:随机颜色生成

AudioVisualizer.prototype.getRandomColor = function () {
    var letters = '0123456789ABCDEF'.split('');
    var color = '#';
    for (var i = 0; i < 6; i++) {
        color += letters[Math.floor(Math.random() * 16)];
    }
    return color;
};

可以让场景放大缩小旋转的three.js小工具,OrbitControls.js
将其添加到初始化的函数中(initialize)

this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);

并将下面这行代码放到’onaudioprocess’函数中

 visualizer.controls.update();

完整代码


下面为作者的完整代码
https://github.com/Raathigesh/HTML5AudioVisualizer


结语


这篇文章提供了一个用WebAudio接口和three.js将音频信息可视化的方法,目前看来javascriptNode,HTML5的WebAudio 接口还不够稳定,这些代码可能会失效(但是我会跟进的),html5可以做很多疯狂而又酷炫的事情,敲下这些代码的时候我还是蛮开心的,做出来的结果也非常满意,继续用html5震撼世界吧 233333.

引用


链接


文章来源

作者 Raathigeshan
译者 角角兔
角角兔 towrabbit
相关作品:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值