audioContext & audio 音频播放

目的:通过对 audio 和 audioContext 的使用,加深对音频的处理

  • 使用 标签播放音频
  • 使用 AudioContext 对象播放音频
  • <进阶>通过 AudioContext 对音频进行精细化处理:失真、滤波,变调
  • <进阶>通过 AudioContext.createBuffer()生成一段音频

最终源码

使用 audio 标签播放音频

使用 audio 标签播放音乐, 加载音频文件可以通过直接在标签上的 src 写好,
或通过 audio.setAttribute(“src”,"./love105.mp3");此方法来设置

<audio id="audio" src="./love105.mp3"></audio>
<button onclick="play()">play</button>
<button onclick="suspend()">suspend</button>

<script>
  var audio = document.getElementById("audio");
  function play() {
    audio.play();
  }
  function suspend() {
    audio.pause();
  }
</script>

限制,chrome 已经不支持自动播放音频,规定只有用户与页面交互之后,比如点击了页面任意地方之后才会自动播放。而自动播放的需求非常常见,因此需要设计师根据场景去设计交互,交互之后进行音频得播放

示例:每秒检测是否已经开始播放,没有那么一直检测下去,直到播放。
在示例中,你会发现一直检测,一直报错,直到你与页面交互(点击)之后,那么播放限制解除,才会真正开始播放。

<audio id="audio" src="./love105.mp3" auto loop></audio>
<button onclick="play()">play</button>
<button onclick="suspend()">suspend</button>

<script>
  var audio = document.getElementById("audio");

  var isPlaying = false;
  checkIsPlaying();
  function checkIsPlaying() {
    setTimeout(() => {
      if (!isPlaying) {
        console.log("检测中,未播放");
        play();
        checkIsPlaying();
      }
    }, 1000);
  }

  // 使用异步确保开始播放
  async function play() {
    await audio.play();
    isPlaying = true;
    console.log("开始播放");
  }
  function suspend() {
    audio.pause();
  }
</script>

使用 audioContext 对象播放音频

使用 audioContext 播放也并不复杂,主要需要理解这一个方法 audioContext.createBufferSource()方法.

可以将音频视作竹子,一段完整得音频,由一个又一个节点相连而成。

audioContext 上有一个方法 var source = ctx.createBufferSource()用于生成音频头部节点.
audioContext 上有一个属性叫做 ctx.destination,标识音频尾节点.
source 上的一个方法source.connet(),可以对头尾进行链接 source.connet(ctx.destination)
接下来,头尾链接,就成为了一根完整的竹子,也就是一个完整的音频。

<button onclick="playAudio()">playAudio</button>
<button onclick="resumeAudio()">resumeAudio</button>
<button onclick="stopAudio()">stopAudio</button>
<script>
  var ctx = new (window.AudioContext || window.webkitAudioContext())();
  let source = ctx.createBufferSource(); // 创建音频源头姐点

  // 播放
  async function playAudio() {
    const audioBuffer = await loadAudio();
    playSound(audioBuffer);
  }
  // 暂停
  async function resumeAudio() {
    if (ctx.state === "running") {
      ctx.suspend();
    } else if (ctx.state === "suspended") {
      ctx.resume();
    }
  }
  // 停止
  async function stopAudio() {
    source.stop();
  }
  async function loadAudio() {
    const audioUrl = "love105.mp3";
    const res = await fetch(audioUrl);
    const arrayBuffer = await res.arrayBuffer(); // byte array字节数组
    const audioBuffer = await ctx.decodeAudioData(arrayBuffer, function(
      decodeData
    ) {
      return decodeData;
    });
    return audioBuffer;
  }
  async function playSound(audioBuffer) {
    source.buffer = audioBuffer; // 设置数据
    source.loop = true; //设置,循环播放
    source.connect(ctx.destination); // 头尾相连
    // 可以对音频做任何控制
    source.start(0); //立即播放
  }
</script>

使用 audioContext 对象,对音频进行细化处理

以下内容非专业音乐人士或感兴趣者可以跳过了.

以下内容用到了这几个 API

当我们挺一段音频(音乐)时,调子有时很高,譬如歌曲<青藏高原>,有时又很低<慢慢>,这在音频上时如何标识的呢?我们使用一段频谱均匀的音频来演示

  1. 生成一段音频,生成一段最简单的白噪音音频,也就是很熟悉的电视雪花音

在上面 audioContext 代码的基础上添加一个按钮和一个方法

async function playSound(audioBuffer) {
  var myArrayBuffer = getDefineBuffer(); // 数据源使用刚生成的白噪音
  source.buffer = myArrayBuffer;
  source.loop = true; //设置,循环播放
  source.connect(ctx.destination); // 头尾相连
  // 可以对音频做任何控制
  source.start(0); //立即播放
}

function getDefineBuffer() {
  // 立体声
  var channels = 2;
  // 创建一个 采样率与音频环境(AudioContext)相同的 时长5秒的 音频片段。
  var frameCount = ctx.sampleRate * 5.0; // ctx.sampleRate:缓存区内,PCM数据每秒钟的采样率
  var myArrayBuffer = ctx.createBuffer(channels, frameCount, ctx.sampleRate);
  // 使用白噪声填充;就是 -1.0 到 1.0 之间的随机数
  for (var channel = 0; channel < channels; channel++) {
    // 向myArrayBuffer的频道中填充数据。
    // 此方法myArrayBuffer.getChannelData()设计的与常见理解相反,nowBuffering内一旦填充了数据,myArrayBuffer内部评到也就填充了
    var nowBuffering = myArrayBuffer.getChannelData(channel);
    for (var i = 0; i < frameCount; i++) {
      nowBuffering[i] = Math.random() * 2 - 1;
    }
  }
  return myArrayBuffer;
}

音频细化处理方法

  1. 细化处理之 对音频进行音量设置
<script>
  var ctx = new (window.AudioContext || window.webkitAudioContext())();
  let source = ctx.createBufferSource();
  let gainNode = ctx.createGain(); // 创建音量控制节点

  addEventListenerlGainNode();

  async function playSound(audioBuffer) {
    var myArrayBuffer = getDefineBuffer(); // 数据源使用刚生成的白噪音
    source.buffer = myArrayBuffer;
    source.loop = true; //循环播放

    // 音频头 ->链接到 ->音量控制节点
    // 音量控制节点 ->链接到 -> 音频尾
    source.connect(gainNode);
    gainNode.connect(ctx.destination);

    source.start(0); //立即播放
  }
  function addEventListenerlGainNode() {
    var max = window.innerHeight;
    document.addEventListener("mousemove", function(event) {
      var y = event.clientY; //取得纵坐标
      var volume = (y / max).toFixed(2);
      gainNode.gain.value = volume;
    });
  }
</script>
  1. 细化处理之 对音频进行滤波处理

在上面 audioContext 代码的基础上创建一个低频滤波器,并连接到头尾节点
滤波器,默认是含有默认滤波处理值得,所以一旦链接,再播放音频,能明显听出声音有所变化。

<script>
  var ctx = new (window.AudioContext || window.webkitAudioContext())();
  let source = ctx.createBufferSource();
  let gainNode = ctx.createGain();
  let biquadFilter = ctx.createBiquadFilter(); // 创建低频滤波器, 返回 BiquadFilterNode。

  async function playSound(audioBuffer) {
    var myArrayBuffer = getDefineBuffer();
    source.buffer = myArrayBuffer;
    source.loop = true;

    // 音频头 ->链接到 ->音量控制节点
    // 音量控制节点 ->链接到 ->滤波器
    // 滤波器 ->链接到 ->音频尾
    source.connect(gainNode);
    gainNode.connect(biquadFilter);
    biquadFilter.connect(ctx.destination);

    source.start(0);
  }
</script>
  1. 细化处理之 对音频进行失真

在上面代码的基础上创建一个双二阶滤波器,并连接到头尾节点.
添加一个点击按钮,点击则让音频失真, makeDistortionCurve()
细心得同学发现了,使用 connect()得时候,只需要向首尾得中间添加各种控制节点,就可以对音频进行处理并返回

<button onclick="controlDistortion()">失真</button>
<script>
  var ctx = new (window.AudioContext || window.webkitAudioContext())();
  let source = ctx.createBufferSource();
  let gainNode = ctx.createGain();
  let biquadFilter = ctx.createBiquadFilter();
  let distortion = ctx.createWaveShaper(); // 创建双二阶滤波器,返回 WaveShaperNode.常用于添加失真效果

  async function playSound(audioBuffer) {
    var myArrayBuffer = getDefineBuffer();
    source.buffer = myArrayBuffer;
    source.loop = true;

    // 音频头 ->链接到 ->滤波器
    // 滤波器 ->链接到 ->失真器
    // 失真器 ->链接到 ->音频尾
    source.connect(gainNode);
    gainNode.connect(biquadFilter);
    biquadFilter.connect(distortion);
    distortion.connect(ctx.destination);

    source.start(0); //立即播放
  }

  let controlDistortion = function(value) {
    distortion.curve = makeDistortionCurve(400);
  };
  // 失真
  function makeDistortionCurve(amount) {
    var k = typeof amount === "number" ? amount : 50,
      n_samples = 44100,
      curve = new Float32Array(n_samples),
      deg = Math.PI / 180,
      i = 0,
      x;
    for (; i < n_samples; ++i) {
      x = (i * 2) / n_samples - 1;
      curve[i] = ((3 + k) * x * 20 * deg) / (Math.PI + k * Math.abs(x));
    }
    return curve;
  }
</script>
  1. 细化处理之 对音频进行混音,也就是常见得大厅效果,厕所,客厅等场景效果

混音比较特殊,能够创造距离,空间感

<button onclick="controlConvolver()">混音</button>
<script>
  var ctx = new (window.AudioContext || window.webkitAudioContext())();
  let source = ctx.createBufferSource();
  let gainNode = ctx.createGain();
  let biquadFilter = ctx.createBiquadFilter();
  let distortion = ctx.createWaveShaper();
  var convolver = ctx.createConvolver(); // 创建一个ConvolverNode,通常用来应用混响效果

  // 添加混响
    let controlConvolver = function (value) {
      biquadFilter.disconnect(0);
      biquadFilter.connect(convolver)
    }


  async function playSound(audioBuffer) {
    var myArrayBuffer = getDefineBuffer(); // 数据源使用刚生成的白噪音
    source.buffer = myArrayBuffer;

    // 混音节点必须要设置音频数据!!!
    convolver.buffer = myArrayBuffer;

    source.loop = true; //循环播放

    // 节点顺序改变
    source.connect(distortion);
    distortion.connect(biquadFilter);
    biquadFilter.connect(gainNode);

    // 节点顺序会音响混音效果
    convolver.connect(gainNode);

    gainNode.connect(ctx.destination);
    source.start(0); //立即播放
  }
</script>

扩展

音响分类为:1.0,2.0,2.1, 5.1, 7.1,其实就是混音得区别。所以只有电影院才能真正感受到环绕音,电影院设置5个以上喇叭环绕在观众周围是标准配置,家里没有那么多喇叭。

  • 1.0 单声道 :单声道
  • 2.0 双声道 :left, right。 左前,右前 各一个喇叭
  • 2.1 立体音 : left, right。 在 2.0 得基础上加了一个低音喇叭
  • 5.1 环绕音 : left, right,left around, right around,left behind,right behind 。 在 2.1 得基础上细分了一下,左前,右前,左侧,右侧 ,低音喇叭 共 5 喇叭放声音
  • 7.1 环绕音 : left, right,left around, right around,left behind,right behind .再 5.1 得基础上细分了一下,左前,右前,左侧,右侧,左后,右后 ,低音喇叭 共 7 喇叭放声音

参考资料

MDN AudioBuffer
失真
voice-change-o-matic
voice-change-o-matic srouce

------ 如果文章对你有用,感谢右上角 >>>点赞 | 收藏 <<<


版权声明:本文为win7583362原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。