音频的绘制

音频的绘制

基础概念

  1. 采样率(Sample Rate)

  • 每秒采集的音频样本数

  • 常见值:44.1kHz、48kHz

  • 影响音频质量和文件大小

  1. 位深度(Bit Depth)

  • 每个采样点的数据精度

  • 常见值:16 位、24 位、32 位

  • 影响动态范围和音质

  1. 声道数(Channels)

  • 音频的通道数量

  • 单声道(Mono)、立体声(Stereo)

  • 影响空间感和文件大小

  1. 音频缓冲区(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(); // 开始播放
  1. 创建增益节点:用于控制音量:

const gainNode = audioCtx.createGain();
gainNode.gain.setValueAtTime(0.5, audioCtx.currentTime); // 设置增益值
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
  1. 音频解码

相关方法

音频转为 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);
    });
  }

创建并复制相关的音频

  1. 创建新的音频缓冲区

该方法用于创建一个新的 AudioBuffer,其通道数、长度和采样率与原始缓冲区相同。这是为了在新的音频上下文中处理音频数据

const newAudioBuffer = this.audioContext.createBuffer(
  numberOfChannels,
  sampleCount,
  sampleRate
);
  1. 复制音频数据

创建新的 AudioBuffer 后,需要将原始音频缓冲区的数据复制到新的缓冲区中。这是为了确保在新的音频上下文中可以访问和处理原始音频数据。

const anotherArray = new Float32Array(sampleCount);

for (let channel = 0; channel < numberOfChannels; channel++) {
  buffer.copyFromChannel(anotherArray, channel, 0);
  newAudioBuffer.copyToChannel(anotherArray, channel, 0);
}
  1. 更新音频上下文

this.audioBuffer = newAudioBuffer;

常见音频处理示例

  1. 音频裁剪

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;
}

错误处理与兼容性

  1. 浏览器兼容性检查

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");
  }
}

音频可视化实现

  1. 波形图绘制

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();
}
  1. 频谱图绘制

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();
}

最后更新于

这有帮助吗?