Zero, the technology involved, Api, nouns, etc
You can search on MDN
AudioContext AudioBufferSourceNode AnalyserNode ArrayBuffer TypeArray – Uint8Array etc
XMLHttpRequest
Canvas
requestAnimationFrame
1. Technical analysis – Basic ideas
- 1. Play audio
- 2. Get real-time data while the audio plays
- 3. Draw on the Canvas according to certain rules
2. Download an audio
I am used to use promise to encapsulate requests, which is convenient for asynchronous programming, and better use async and await to perform asynchronous synchronization and other operations
// Load the audio file
function loadSound(url) {
return new Promise((resolve) = > {
const request = new XMLHttpRequest(); // Create a request
request.open('GET', url, true); // Configure the request type, file path, etc
request.responseType = 'arraybuffer'; // Configure the data return type
// Once the capture is complete, perform further operations on the audio, such as decoding
request.onload = () = > {
resolve(request.response);
}
request.send();
});
}
Copy the code
Write an asynchronous init function. And load the audio inside
// Create a function to facilitate asynchronous programming
async function init() {
const arrayBuffer = await loadSound("https://yun.duiba.com.cn/aurora/assets/3457521d415e67cd2311a0df0ac4152439e75de7.mp3");
audioBufferSourceNode.buffer = audioBuffer;
}
init();
Copy the code
Play an audio
Create an AudioContext object and an audio player node in a global variable that can be used to play audio
/ / compatible
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
// Create the audio player node
const audioBufferSourceNode = audioContext.createBufferSource();
audioBufferSourceNode.connect(audioContext.destination); Connect to the AudioContext object
Copy the code
Use AudioContext in the init function to convert the ArrayBuffer to an AudioBuffer for audio playback and bind to the AudioBufferSourceNode
/** * Turn ArrayBuffer to AudioBuffer *@param arrayBuffer
* @returns {Promise}* /
function bufferToAudio(arrayBuffer) {
return new Promise((resolve, reject) = > {
audioContext.decodeAudioData(
arrayBuffer,
(res) = > {
resolve(res);
},
(err) = >{ reject(err); }); }); }// Create a function to facilitate asynchronous programming
async function init() {
const arrayBuffer = await loadSound("https://yun.duiba.com.cn/aurora/assets/3457521d415e67cd2311a0df0ac4152439e75de7.mp3");
// Convert arrayBuffer to audioBuffer
const audioBuffer = await bufferToAudio(arrayBuffer);
// Bind the audio object
audioBufferSourceNode.buffer = audioBuffer;
}
Copy the code
Write a play button
<button onclick="play()">play</button>
<script>
function play() {
if (audioBufferSourceNode.isStart) {
audioBufferSourceNode.isStart = false;
audioBufferSourceNode.stop(); // Pause the audio
} else {
audioBufferSourceNode.isStart = true;
audioBufferSourceNode.start(0); // Play the audio from 0}}</script>
Copy the code
Open the page, click the button to find a wonderful song out
To learn the gods ~ flying cranes ~ the Midas touch into gold ~ wonderful ~
Four, audio analyzer
Create an audio parser in a global variable and connect it to the audio object
// Create an audio analyzer and connect the audio analyzer
const analyser = audioContext.createAnalyser();
audioBufferSourceNode.connect(analyser);
analyser.fftSize = 2048; // indicates the length of a single piece of data, which can only be 2 to the n
// Set fftSize to frequencyBinCount
const bufferLength = analyser.frequencyBinCount;
// Create a Uint8Array using frequencyBinCount for storing data
const dataArray = new Uint8Array(bufferLength);
Copy the code
Through the analyser. GetByteTimeDomainData current data can be filled in dataArray
analyser.getByteTimeDomainData(dataArray);
Copy the code
5. Get real-time data and draw on Canvas according to the rules
Create a Canvas
<canvas id="myCanvas" width="450" height="450"></canvas>
Copy the code
So I’m going to write a draw function and I’m going to write the drawing logic in there and I’m just going to copy it from the web, just find a nice one and bind it to the requestAnimationFrame and then call draw directly after init
// canvas
const canvas = document.getElementById("myCanvas");
const canvasCtx = canvas.getContext("2d");
function draw() {
requestAnimationFrame(draw); // Loop call
analyser.getByteTimeDomainData(dataArray); // Fill the current data in dataArray
canvasCtx.fillStyle = 'rgb(200, 200, 200)';
canvasCtx.fillRect(0.0, canvas.width, canvas.height);
canvasCtx.lineWidth = 2;
canvasCtx.strokeStyle = 'rgb(0, 0, 0)';
canvasCtx.beginPath();
const sliceWidth = canvas.width * 1.0 / bufferLength;
let x = 0;
for (let i = 0; i < bufferLength; i++) {
const v = dataArray[i] / 128.0;
const y = v * canvas.height / 2;
if (i === 0) {
canvasCtx.moveTo(x, y);
} else {
canvasCtx.lineTo(x, y);
}
x += sliceWidth;
}
canvasCtx.lineTo(canvas.width, canvas.height / 2);
canvasCtx.stroke();
}
init();
draw();
Copy the code