音频的绘制
基础概念
常见名词解释
window.AudioContext 是 Web Audio API 中的一个接口,用于创建和管理音频处理图。它允许开发者在网页中进行复杂的音频操作和处理,包括音频源的创建、音频效果的应用以及音频信号的路由等。以下是 AudioContext 的一些关键特性和使用方法:
基本概念 音频处理图:AudioContext 表示一个音频处理图,由多个音频模块(AudioNode)链接而成。每个模块可以是音频源、音频效果或音频目标。 音频节点:这些节点可以是音频源(如振荡器或音频文件)、音频效果(如滤波器或增益节点)或音频目标(如扬声器)。
创建 AudioContext 要使用 Web Audio API,首先需要创建一个 AudioContext 实例:
复制 const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
这段代码确保在所有浏览器中都能正常工作,包括那些需要前缀的旧版浏览器。
常用方法和属性
1.创建音频源:例如,创建一个振荡器节点:
复制 const oscillator = audioCtx.createOscillator();
oscillator.type = "sine"; // 设置波形类型
oscillator.frequency.setValueAtTime(440, audioCtx.currentTime); // 设置频率
oscillator.connect(audioCtx.destination); // 连接到输出
oscillator.start(); // 开始播放
复制 const gainNode = audioCtx.createGain();
gainNode.gain.setValueAtTime(0.5, audioCtx.currentTime); // 设置增益值
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
相关方法
音频转为 wav 格式
复制 export const audioBufferToWav = async (audioBuffer: AudioBuffer) => {
const anotherArray: Float32Array[] = [];
for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
anotherArray.push(audioBuffer.getChannelData(channel));
}
const data = {
sampleRate: audioBuffer.sampleRate,
channelData: anotherArray,
};
const buffer = await WavEncoder.encode(data);
return new Blob([buffer], { type: "audio/wav" });
};
解码
复制 private decodeAudioData(arrayBuffer: ArrayBuffer): Promise<AudioBuffer> {
return new Promise((resolve, reject) => {
const audioContext = ShareAudioContext.getInstance().getContext();
audioContext.decodeAudioData(arrayBuffer, resolve, reject);
});
}
创建并复制相关的音频
该方法用于创建一个新的 AudioBuffer,其通道数、长度和采样率与原始缓冲区相同。这是为了在新的音频上下文中处理音频数据
复制 const newAudioBuffer = this.audioContext.createBuffer(
numberOfChannels,
sampleCount,
sampleRate
);
创建新的 AudioBuffer 后,需要将原始音频缓冲区的数据复制到新的缓冲区中。这是为了确保在新的音频上下文中可以访问和处理原始音频数据。
复制 const anotherArray = new Float32Array(sampleCount);
for (let channel = 0; channel < numberOfChannels; channel++) {
buffer.copyFromChannel(anotherArray, channel, 0);
newAudioBuffer.copyToChannel(anotherArray, channel, 0);
}
复制 this.audioBuffer = newAudioBuffer;
常见音频处理示例
复制 function trimAudio(audioBuffer, start, end) {
const sampleRate = audioBuffer.sampleRate;
const channels = audioBuffer.numberOfChannels;
const startOffset = Math.floor(start * sampleRate);
const endOffset = Math.floor(end * sampleRate);
const frameCount = endOffset - startOffset;
const newBuffer = new AudioContext().createBuffer(
channels,
frameCount,
sampleRate
);
for (let channel = 0; channel < channels; channel++) {
const newData = newBuffer.getChannelData(channel);
const originalData = audioBuffer.getChannelData(channel);
for (let i = 0; i < frameCount; i++) {
newData[i] = originalData[startOffset + i];
}
}
return newBuffer;
}
错误处理与兼容性
复制 function checkAudioSupport() {
if (!window.AudioContext && !window.webkitAudioContext) {
throw new Error("Web Audio API not supported");
}
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
throw new Error("Audio recording not supported");
}
}
音频可视化实现
复制 function drawWaveform(audioBuffer) {
const canvas = document.getElementById("waveform");
const ctx = canvas.getContext("2d");
const data = audioBuffer.getChannelData(0);
const step = Math.ceil(data.length / canvas.width);
ctx.beginPath();
for (let i = 0; i < canvas.width; i++) {
const offset = i * step;
let min = 1.0;
let max = -1.0;
for (let j = 0; j < step; j++) {
const datum = data[offset + j];
if (datum < min) min = datum;
if (datum > max) max = datum;
}
const y1 = ((1 + min) * canvas.height) / 2;
const y2 = ((1 + max) * canvas.height) / 2;
ctx.moveTo(i, y1);
ctx.lineTo(i, y2);
}
ctx.stroke();
}
复制 function drawSpectrum(audioContext, audioBuffer) {
const analyser = audioContext.createAnalyser();
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(analyser);
analyser.fftSize = 2048;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
function draw() {
analyser.getByteFrequencyData(dataArray);
// 绘制频谱
requestAnimationFrame(draw);
}
source.start(0);
draw();
}