写在前面:
根据Web项目开发需求,需要在H5页面中,通过点击视频列表页中的任意视频进入视频详情页,然后根据视频的链接地址,主要是 .mp4 文件格式,在进行播放时实时的显示该视频的音频轨道情况,并实时的将各音频轨道数据以可视化(峰值电平 / 响度跳表)的形式展现出来。
实现效果
关键技术
在Web浏览器中,想要获取多媒体文件的相关数据信息,需要借助对应的API来完成,比如获取视音文件的音频信息,就需要用到Web Audio API,通过该API我们可以轻松做到播放声音、获取声音数据,修改声音数据、甚至还可以制造声音。
🚀Web Audio API
Web Audio API 提供了在 Web 上控制音频的一个非常有效通用的系统,允许开发者来自选音频源,对音频添加特效,使音频可视化,添加空间效果(如平移),等等。
它可以设置不同的音频来源(包括节点、 ArrayBuffer 、用户设备等),对音频添加音效,生成可视化图形等。
Web Audio API 使用户可以在音频上下文(AudioContext)中进行音频操作,具有模块化路由的特点。在音频节点上操作进行基础的音频,它们连接在一起构成音频路由图。
即使在单个上下文中也支持多源,尽管这些音频源具有多种不同类型通道布局。这种模块化设计提供了灵活创建动态效果的复合音频的方法。
Web Audio API 是 JavaScript 中主要用于在网页应用中处理音频请求的一个高级应用接口,其复杂度比Canvas相关的API还要高,如果将Web Audio API 和 Canvas相关API 相结合起来使用能做出很多有趣的东西,比如:音频数据可视化、峰值电平、响度跳表等。这个 API 目的是用于让最新技术与传统的游戏音频引擎的综合处理相兼容,也即尽力提供一些桌面音频处理程序的要求。
- 查看音频播放期间调度事件发生的确切时间;
- 支持各种类型的音频过滤波器以实现各种效果,包括回声、消除噪音等;
- 支持利用合成声音(Sound synthesis)创建电子音乐;
- 支持3D位置音频模拟效果,比如某种声音随着游戏场景而移动;
- 支持外部输入的声音与 WebRTC 进行集成(调用 WebRTC ,在你的设备中增添吉他声),或者在 - WebRTC 中调用其他地方传输过来的声音;
- 利用音频数据分析创造良好的可视化声音等。
🚀AudioContext
AudioContext接口表示由链接在一起的音频模块构建的音频处理图,每个模块由一个AudioNode表示。音频上下文控制它包含的节点的创建和音频处理或解码的执行。在做任何其他操作之前,你需要创建一个AudioContext对象,因为所有事情都是在上下文中发生的。建议创建一个AudioContext对象并复用它,而不是每次初始化一个新的AudioContext对象,并且可以对多个不同的音频源和管道同时使用一个AudioContext对象。
📢需要注意: 在没有和用户进行交互时,直接通过代码运行
new AudioContext()
时!浏览器会发出:The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.
意思是说:不允许启动AudioContext。必须要用户在页面上做出手势后才能 创建 或 恢复。
⚔️主要原因:浏览器这样做其目的是,为了防止不必要的自动播放音频,一些浏览器不允许在首次创建网络音频API的AudioContext时启动。它必须在用户与页面交互后通过在上下文AudioContext对象上调用resume()方法来启动。
📊解决方案:不同的浏览器以不同的方式实现此要求:如使用调用AudioContext.resume()的方法来激活它。
- Chrome和Edge浏览器:就是先不运行new AudioContext()。等到用户单击事件的回调 或 向音频 / 视频元素的 播放事件添加侦听器来恢复。想了解更多:https://developer.chrome.com/blog/autoplay/#webaudio
- Firefox浏览器:目前还能直接运行 new AudioContext();
- Webkit/Safari 浏览器:最初已暂停。只能通过附加到单击事件的回调来恢复 - (就是通过附加到单击事件的回调或通过向音频/视频元素的事件添加侦听器来恢复)。
🚀关于CORS访问限制
⚔️主要原因:四个字【为了安全】,当在使用或元素,媒体源与网站不在同一域上(就是视音频的src域名地址和当前运行代码的域名地址不是同一个域名时),则会导至MediaElementAudioSource零输出。
📊解决方案:为媒体提供服务的服务器必须在响应中添加一个带有网站域的访问控制允许来源标头(就是在src媒体源服务器中添加请求白名单,这个只要对接或联调过后端API接口的小伙伴都知道的!)。想了解更多:https://developer.mozilla.org/zh-CN/docs/Web/HTML/Attributes/crossorigin
了解更多相关标准 和 API:https://www.w3.org/TR/webaudio、https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Audio_API
实例代码
1. HTML标签
由于video和audio都有音频,所以我们可以video或者audio来获取输入源,除此以外,我们还可以通过 navigator.getUserMedia API 或 Ajax请求的方式来获取输入源。
<video loop controls><source src="./media/xxx.mp4" type="video/mp4" />
</video>
<audio loop controls><source src="./media/xxx.mp3" type="audio/mp3" />
</audio>
2.JS关键代码
这里要注意的点就是上面提到过的:在创建AudioContext 上下文环境对象时, 由于浏览器安全策略要求音频上下文必须在用户事件(单击、键盘按键等)中启用。
// 创建一个 AudioContext 环境
const ac = new (window.AudioContext || window.webkitAudioContext)();// 从 video 或 audio 标签元素中拿到输入源
const audio = document.querySelector("video");
// const audio = document.querySelector("audio");// 创建并获取输入源
const audioSource = ac.createMediaElementSource(audio);
// 音频通道数 默认值是 2,最高能取 32
const channelCount = 2 || audioSource.channelCount;
// 缓冲区大小 取值为 2 的幂次方的一个常数
const bufferSize = 2048;// 创建音频处理器
const processor = ac.createScriptProcessor(bufferSize, channelCount, channelCount);
// 链接音频处理器
audioSource.connect(processor).connect(ac.destination);
// connect到扬声器
audioSource.connect(ac.destination);// 监听音频处理器每次处理的样本帧
processor.onaudioprocess = (evt) => {{//获取声轨1(左声道)输入的缓冲区数据const input =evt.inputBuffer.getChannelData(0);//获取声轨1(左声道)输出的缓冲区数据const output = evt.outputBuffer.getChannelData(0);};{//获取声轨2(右声道)输入的缓冲区数据const input =evt.inputBuffer.getChannelData(1);//获取声轨2(右声道)输出的缓冲区数据const output = evt.outputBuffer.getChannelData(1);};
};
3. 完整实例代码
可以通过添加本地的视频 或 音频文件,来测试对应的声道,并实时的渲染到响度跳表中,需要注意的是,音频峰值电平跳表从-60开始的原因主要是,当输出音量接近满载时,THD(总谐波失真)的表现会比较差,此时产生的谐波会盖掉原本存在的背景噪音,影响到测试成绩。因此,采用-60dB的测试信号。
音频峰值电平跳表值:通常在-60dB到+3dB之间。在音频设备测试中,跳表值反映了设备的频率响应和增益。不同的音频设备可能会有不同的跳表值范围,根据测试标准和设备要求而定,更多相关标准和算法从这里 ITU R-REC-BS.1770 获得了解。
>>> 完整实例代码,请点击前往GitHub仓库自行提取!!
4. 完整实例效果
扩展封装
为了能在项目上提高开发效率,我将其封装发布至Npm上,在我们Web前端常用的开发框架(如:Vue.js,React.js,Angular.js等)中,使用Npm命令直接下载安装即可:
Npm安装命令:
npm i @muguilin/web-audio-track
Yarn安装命令:
yarn add @muguilin/web-audio-track