2024-09-05 03:24:07
nodeJs+express+http+fs实现环境搭建,读取歌曲并生成列表
ajax获取音频
AudioContextAPI解析音频
Canvas绘制图形图像
requestAnimationFrame调起动画
重点canvas绘制、AudioContextAPI应用代码在'public/javascripts/index.js'中本博文重点讲解实现过程中使用的AudioContext相关的API如果对webaudioAPI很有兴趣,请参见webaudioAPI
AudioContext音频处理的环境和上下文,可以控制它所包含的节点的创建,以及音频处理、音频解码操作,读取播放状态等。做任何音频相关的事情之前都要先创建AudioContext对象,因为一切都发生在这个环境之中。
那么如何创建AudioContext这个环境呢?
constAC=new(window.AudioContext||window.webkitAudioContext)();这个环境下包含许多非常重要的方法和属性,挑几个说说:
AudioBufferSourceNode:代表一个音频源,可以挂载一段写在内存中的音频数据,没有输入,有一个输出方法,可以利用start方法来调用,利用stop方法来关闭
constAudioBufferSourceNode=AC.createBufferSource();//这里创建了个音频源AudioBufferSourceNode.start([when[,offset[,duration]]);//安排声音在指定时间播放。没有指定时间,则声音立即开始播放。AudioBufferSourceNode.buffer=buffer;//挂载一段内存中的音频片段,AudioBufferSourceNode.stop([when]);//停止播放AudioBufferSourceNode.onended=()=>{}//当前音频播放完毕后的回调函数这里需要注意一个事情:
GainNode:代表一个音量控制器,通过它可以控制输出音频的音量大小,一般取值0-1
constGainNode=AC.createGain()GainNode.gain.value=0.6AnalyserNode:代表一个音频分析器,赋予了节点可以提供实时频率及时间域分析的信息
constAnalyserNode=AC.createAnalyser();AnalyserNode.fftSize=number*2//FFT是离散傅里叶变换的快速算法,用于将一个信号变换到频域,//得到的值是32-2048之间的2的整数次倍,默认是2048。console.log(AnalyserNode.frequencyBinCount)//number,通常代表要用于可视化的数据值的数量,值为analyserNode.fftSize/2AudioDestinationNode:表示音频的最终输出地址-通常为扬声器
constAudioDestinationNode=AC.destinationcurrentTime:当前音频环境播放时长
AC.currentTime(关于currentTime我的理解和翻译过来的API解读文档有所不同),下图是翻译来的意思:
这张图是英文原文
英文的意思大概是用来调节音频播放、可视化时间戳等(schedulingaudioplayback,visualizingtimelines),所以我的理解是当前环境的播放时长,事实上在案例中我也实际使用了这个值,在音频开始播放前,AC.currentTime始终为0,并且音频环境被suspend()掉的时候,currentTime的值也不再变化,即被暂停掉了
decodeAudioData:这是一个方法,通常用于异步解码音频文件,被解码的文件是通过使用responseType为arraybuffer类型的ajax获取的,该方法只能作用于完整的音频文件
xhr.responseType="arraybuffer";//返回一段二进制数据xhr.onload=function(){//类似onreadystatechangeAC.decodeAudioData(this.response,(buffer)=>{console.log(buffer)//解码后得到的文件,可以挂在到AudioBufferSourceNode.buffer上console.log(buffer.duration)//音频时间长度},(err)=>{console.log(`errMessage:${err}`);});}resume:重新启动一个已被暂停的音频环境
AC.resume()suspend:暂停音频内容的进度.暂时停止音频硬件访问和减少在过程中的CPU/电池使用.
AC.suspend()这里还少了一个重要步骤,即将音频和音量控制器,音频分析器,播放器相互关联,这样他们才能够正常使用
AudioBufferSourceNode.connect(AnalyserNode);AnalyserNode.connect(GainNode);GainNode.connect(AudioDestinationNode);梳理一下音乐播放的实现流程node使用fs模块读取所有的歌曲并生成列表渲染到前端页面
当点击播放的时候,我们先查找本地缓存中是否有当前歌曲,有的话,我们创建一个音频环境进行播放,没有的话,我们则通过ajax获取到音频数据,然后通过AC.decodeAudioData解析成buffer存到缓存中,然后创建音频环境进行播放
在每次播放不同的歌曲之前,先调用AC.suspend停止当前播放的音乐,这样保证同一时间只有一首歌处于播放状态
如果是点击暂停/播放按钮,那么我们单纯的通过resume/suspend来控制当前音频的播放与暂停
播放的时长是通过AC.currentTime拿到的,总时长是buffer.duration,进度条是二者的比值
音量的控制通过gainNode.gain.value来控制
本地文件播放则是通过fileReader.readAsArrayBuffer将文件读取,然后依然通过AC.decodeAudioData解码来进行播放
canvas图形绘制首先我们得到AnalyserNode.frequencyBinCount,即可视化数据值,然后将其存为Uint8Array数组类型,
constAudioBufferSourceNode=AC.createBufferSource();//这里创建了个音频源AudioBufferSourceNode.start([when[,offset[,duration]]);//安排声音在指定时间播放。没有指定时间,则声音立即开始播放。AudioBufferSourceNode.buffer=buffer;//挂载一段内存中的音频片段,AudioBufferSourceNode.stop([when]);//停止播放AudioBufferSourceNode.onended=()=>{}//当前音频播放完毕后的回调函数0之后通过getByteFrequencyData方法将当前频率数据复制到传递给它的uint8array中,
constAudioBufferSourceNode=AC.createBufferSource();//这里创建了个音频源AudioBufferSourceNode.start([when[,offset[,duration]]);//安排声音在指定时间播放。没有指定时间,则声音立即开始播放。AudioBufferSourceNode.buffer=buffer;//挂载一段内存中的音频片段,AudioBufferSourceNode.stop([when]);//停止播放AudioBufferSourceNode.onended=()=>{}//当前音频播放完毕后的回调函数1得到的值大概是这样
这就是我们可视化音频需要的数据了,他是随着音频播放而不断变化的值,初始全部为0,然后遍历这个数组,拿到数组中每一项的值,就是一组音谱条应该绘制的高度,看图就明白了
每一组音谱条其实就是一条有宽度的,带有渐变背景色的线,线的高度就是arr[i],这条线被平均分成了n个小方格,每个小方格的高度为4,彼此间隔为0.5,然后我们不断计算更新小方块和小红块的位置,通过requestAnimationFrame重新渲染就可以了。
结束最后附上代码地址,欢迎star和issue。
作者:晴天同学