21.JS | Web Audio API (下) 我的音乐浪

“沧海一声唱 滔滔两岸潮
浮沉随浪歌舞今朝
苍天唱 纷纷世上潮
谁负谁胜出天知晓
江山唱 烟雨遥
涛浪淘尽红尘俗世几多娇
清风唱 竟惹寂寥
豪情还剩了一襟晚照
苍生唱 不再寂寥
豪情仍在痴痴唱唱”
—— 题记,《沧海一声唱》

正文

在上文 JS | Web Audio API (上) 你的音谱 中,我们了解到Audio API简单的音频知识点,重在理论,今天搞点有趣的试验,偏重实践。大家知道,光有光谱,电磁波有频谱,音乐呢?当然也有自己的谱。想奥斯特实验揭示电流周围存在磁场,分散的铁屑显现磁铁的磁场分布,那音乐如何看到自身的频率,所以,本文的主题来了,音频可视化,让你的音乐浪起来。先附上效果图,接下来会主要围绕效果例子出发:

21.JS | Web Audio API (下) 我的音乐浪_第1张图片
效果图.png

这种音波似的效果,我们可能会在音乐室或音乐人或音乐播放器那儿看到,并不少见,当第一次发现可以实现时,ohMyGod,震撼,神奇,而对于喜欢的事物,总会想为我所用,闲话不多说,一起看看它是怎么实现的吧。根据已有的web audio API知识,实践音频可视化,自我总结,步骤大致分为以下几步:

  1. 创建音频环境
  2. 获取音频,创建buffer节点
  3. 解码音频,分析音频
  4. 连接音频输入输出
  5. canvas绘制频谱
  6. 连接播放
创建音频环境AudioContext

音频环境是所有音效操作的前提,好比canvas的画布,先有个做画之地,再来笔墨横姿

// Webkit/blink browser require a prefix, and it needs the window object specifically declared to work in Safari
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame;

// declare new audio context
try {
    var audioCtx = new AudioContext();
} catch (e) {
    alert('Your browser does not support AudioContext!');
    console.log(e);
}
获取音频,创建buffer节点

首先获取音频,也就是说拿到这个素材输入之后,我们可以赶制加工,这里通过XMLHttpRequest获取,将请求的返回类型设为“arraybuffer”,方便音频数据处理;另外,创建音频节点createBufferSource,来获取输入的音频。

// use XHR to load an audio track, and
// decodeAudioData to decode it and stick it in a buffer.
// Then we put the buffer into the source
var xhr = new XMLHttpRequest();

// 初始化 HTTP 请求参数, 配置请求类型,文件路径等
xhr.open('GET', 'audio/music1.mp3');

// 将responseType设为arraybuffer,二进制数据
xhr.responseType = "arraybuffer";

// 获取完成,对音频进一步操作,解码
xhr.onload = function() {
    var audioData = xhr.response;
    // Get an AudioBufferSourceNode.
    // This is the AudioNode to use when we want to play an AudioBuffer
    var source = audioCtx.createBufferSource();
    ……
}
解码音频,分析音频

好的,现在我们拿到了音乐,但计算机仍然不懂,需要对其进行解码decodeAudioData
一看到解码后的数据,我们不能让计算机“啪啪啪”就来吧,观个全局,做个自我分析,createAnalyser

audioCtx.decodeAudioData(audioData, function(buffer) {
        // set the buffer in the AudioBufferSourceNode
        source.buffer = buffer;
        
        // create audio node to play the audio in the buffer
        var analyser = audioCtx.createAnalyser();
}
连接音频输入输出

必经之路,input ——> 音频处理 ——> 输出,connect连接。

// connect the analyser to the destination(the speaker), or we won't hear the sound
// from audioCtx.createBuffer, or audioCtx.decodeAudioData
source.connect(analyser);
analyser.connect(audioCtx.destination);
canvas绘制频谱

大头戏,音乐播放捣鼓捣鼓还是有声音的,频谱怎么着,一头雾水。不着急,慢慢来,首先我们需要数据,数据怎么来:

var bufferLength = analyser.frequencyBinCount,
    dataArray = new Uint8Array(bufferLength);

analyser.getByteFrequencyData(dataArray);

好,数据有了,计算机也能懂,怎么画,先说个简单的:

var canvas = document.getElementById('audio_canvas'),
    ctx = canvas.getContext("2d"),
    c_width = canvas.width,
    c_height = canvas.height;

**************
for(var i = 0; i < bufferLength; i++) {
     value = dataArray[i];
     ctx.fillStyle = '#f99';
     ctx.fillRect(i, c_height - value, 1, value);
}

好了,频谱图有了,但没有动效,不会变化,别急,利用requestAnimationFrame,同时这侧面反应了获取的dataArray数组的数值,出来的效果如此这般:

21.JS | Web Audio API (下) 我的音乐浪_第2张图片
数值

可是我们想,如果把所有的数值都展现出来,一来太多,二来更耗资源,而且频率邻值是相似的,非智者所为,怎么处理呢?数学中有学过采样频率的方法,采样对于信息信号来说,是个常用的方式。根据画布长度,美观起见,让每一频占据一定宽度,各个频之间留些空隙,同时用数学逻辑思维换算,计算出画布可放的频数,也就是说画布上选择哪几个频率值显示,取相对应“编号”的频率,进行绘制。

// 条形的宽度
var bar_width = 10,
    bar_gap = 2,
    bar_part = bar_width + bar_gap,
    bar_num = Math.round(c_width / bar_part);

***************************************
      function drawVisual() {
            var i = 0, value;
            
            var bufferLength = analyser.frequencyBinCount,
                dataArray = new Uint8Array(bufferLength);

            // 每段包含的频谱宽
            var array_width = Math.round(bufferLength / bar_num);

            analyser.getByteFrequencyData(dataArray);

            ctx.clearRect(0,0,c_width,c_height)

            for(i; i < bar_num; i++) {
                value = dataArray[i * array_width];
               
                ctx.fillStyle = '#f99';
                ctx.fillRect(bar_part * i, c_height - value, bar_width, value);
            }

            animation_id = requestAnimationFrame(drawVisual);
            // console.log(animation_id)
        }
21.JS | Web Audio API (下) 我的音乐浪_第3张图片
类似
思考

如此一来,大致效果已经实现。在做的过程中,有一个问题需要思考: 动画什么时候停止,也就是说,如何在音乐播放结束的情况下,页面频谱流畅地回归空白,浏览器也不会继续动画,做到“该停止时就停止”。【实践结果证明,如果在音乐播放结束就停止动画或者清空,达不到想要的效果】

21.JS | Web Audio API (下) 我的音乐浪_第4张图片
立刻停止页面

为了美观及更有趣味性,我们可以加个缓慢降落的条形;甚者,采取上传文件的形式,根据上传的音乐“舞动”自己的音浪,因频制浪。这里有个稍难的点:已经播放一首音乐的时候,如何做到继续上传,原音乐停止,新音乐播放并出现相应的频谱。

21.JS | Web Audio API (下) 我的音乐浪_第5张图片
加条形.png
21.JS | Web Audio API (下) 我的音乐浪_第6张图片
上传文件形式.png

【代码存在于 github,仅供参考,敬请交流】

参考文章:
https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0ahUKEwj9xpzE2K_SAhUNtJQKHQQMCu4QFggaMAA&url=https%3a%2f%2fdeveloper%2emozilla%2eorg%2fzh-CN%2fdocs%2fWeb%2fAPI%2fFileReader&usg=AFQjCNGz5Veo8Ux5iQ_w_1oFQc3fqNlynA
http://www.cnblogs.com/Wayou/p/html5_audio_api_visualizer.html
https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API
https://forestmist.org/blog/web-audio-api-loops#source

你可能感兴趣的:(21.JS | Web Audio API (下) 我的音乐浪)