看到跳动的音符你难道不想知道它是如何实现的么?开撸!
问:实现这个有什么用呢?
答:装杯用!😥其实这个操作能实现音频效果器。就是主播开麦后大妈声音变成少女,男人声音可以变女声!😱因为音频分析器可以拿到音频的频谱,既可以改变音调啊音色啊什么的就可以实现音频效果器!🐷
不想看我啰嗦的直接看最后,有整体html+js代码,粘贴到你的html页面内,换以下音频资源链接就能看到效果了!
效果图:
技术分析
音频加载
在html
内增加audio
标签。将音乐加载进来。给audio
标签增加controls
属性用来控制音乐播放。如下:<html> <body> <audio src="/source/YOU.m4a" controls></audio> </body> </html>
频谱获取
要获取音频的频谱需要使用javascript
的AudioContext
对象。var audio = document.querySelector("audio"); let dataArray = new Uint8Array(512); let analyser = null audio.onplay = function() { let audctx = new AudioContext() // 创建音频上下文 let source = audctx.createMediaElementSource(audio) // 创建音频源 analyser = audctx.createAnalyser() // 创建音频分析器 analyser.fftSize = 512 // 设置分析器大小, 必须为2^n次方 // 创建一个数组,用于存放分析器节点数据 // analyser.frequencyBinCount可以拿到傅里叶变换的值,因为傅里叶变换是对称的因此只要一半即可(傅里叶变换请自行搜索) dataArray = new Uint8Array(analyser.frequencyBinCount) source.connect(analyser) // 音频源连接到音频分析器 analyser.connect(audctx.destination) // 音频分析器连接到音频输出 }
至此我们已经获取到了音频的频谱数据。
频谱可视化
对于高效绘制需要用到画布,既canvas
标签,在body
内增加canvas
标签,并起id
为cvs
。<body> <canvas id="cvs"></canvas> <audio src="/source/YOU.m4a" controls></audio> </body>
var cvs = document.getElementById("cvs"); var ctx = cvs.getContext("2d"); function initCanvas() { // 设置canvas宽度为浏览器可视区域 cvs.width = window.innerWidth * devicePixelRatio; // 设置canvas高度为浏览器可视区域的一半 cvs.height = window.innerHeight / 2 * devicePixelRatio; } // 窗口变化时候重新设置canvas宽高 window.onresize = function() { initCanvas(); } // 定义draw方法用于绘制频谱 function draw() { // 使用浏览器自带帧动画函数实现每一帧都绘制。 requestAnimationFrame(draw); // 清空画布 ctx.clearRect(0, 0, cvs.width, cvs.height); const len = dataArray.length / 1.2; // 截取频谱绘制长度 const barWidth = cvs.width / len; // 每一根柱子的宽度 ctx.fillStyle = '#c875fc'; // 给柱子填充颜色 // 遍历每一根柱子,设置其位置以及边框。 for (let i = 0; i < len; i++) { const data = dataArray[i] const barHeight = data / 255 * cvs.height const x = i * barWidth const y = cvs.height - barHeight drawBorder(x,y,barWidth, barHeight) ctx.fillStyle = '#8ca86d' ctx.fillRect(x, y, barWidth, barHeight) } }
至此基本完成了绘制方面的任务。
全部合并后就是以下代码,将以下代码在你本机创建一个html复制进去就可以看到效果(注意把音频资源换成自己的):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>声音波浪</title> </head> <style> #cvs { border: 1px solid #000; } audio { display: block; } </style> <body> <canvas id="cvs"></canvas> <audio src="/source/YOU.m4a" controls></audio> </body> <script> var cvs = document.getElementById("cvs"); var ctx = cvs.getContext("2d"); var audio = document.querySelector("audio"); let isInitAudio = false; let analyser = null let dataArray = new Uint8Array(512); /* 初始化canvas */ function initCanvas() { cvs.width = window.innerWidth * devicePixelRatio; cvs.height = window.innerHeight / 2 * devicePixelRatio; } window.onresize = function() { initCanvas(); } /* 初始化音频 */ function initAudio() { audio.onplay = function() { let audctx = new AudioContext() // 创建音频上下文 let source = audctx.createMediaElementSource(audio) // 创建音频源 analyser = audctx.createAnalyser() // 创建音频分析器 analyser.fftSize = 512 // 设置分析器大小, 必须为2^n次方 // 创建一个数组,用于存放分析器节点数据 // analyser.frequencyBinCount可以拿到傅里叶变换的值,因为傅里叶变换是对称的因此只要一半即可 dataArray = new Uint8Array(analyser.frequencyBinCount) draw() source.connect(analyser) // 音频源连接到音频分析器 analyser.connect(audctx.destination) // 音频分析器连接到音频输出 let bufferLength = analyser.frequencyBinCount } } function draw() { requestAnimationFrame(draw); // 清空画布 ctx.clearRect(0, 0, cvs.width, cvs.height); analyser.getByteFrequencyData(dataArray) const len = dataArray.length / 1.2 const barWidth = cvs.width / len ctx.fillStyle = '#c875fc' for (let i = 0; i < len; i++) { const data = dataArray[i] const barHeight = data / 255 * cvs.height const x = i * barWidth const y = cvs.height - barHeight drawBorder(x,y,barWidth, barHeight) ctx.fillStyle = '#8ca86d' ctx.fillRect(x, y, barWidth, barHeight) } } function drawBorder(xPos, yPos, width, height, thickness = 1) { ctx.fillStyle='#000'; ctx.fillRect(xPos - (thickness), yPos - (thickness), width + (thickness * 2), height + (thickness * 2)); } function initAll() { initCanvas(); initAudio() } initAll() </script> </html>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。