音频的绘制
音频的绘制
基础概念
- 采样率(Sample Rate) 
- 每秒采集的音频样本数 
- 常见值:44.1kHz、48kHz 
- 影响音频质量和文件大小 
- 位深度(Bit Depth) 
- 每个采样点的数据精度 
- 常见值:16 位、24 位、32 位 
- 影响动态范围和音质 
- 声道数(Channels) 
- 音频的通道数量 
- 单声道(Mono)、立体声(Stereo) 
- 影响空间感和文件大小 
- 音频缓冲区(AudioBuffer) 
- 包含音频原始数据的容器 
- 可以进行音频数据的读写操作 
- 用于音频处理和分析 
常见名词解释
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();
}最后更新于
这有帮助吗?