抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

AudioContext设计的API简直太多了,而且目前好多API还处在实验阶段,并未被所有浏览器完全支持, 所以本文不打算覆盖所有知识,只简单学习几个常用的API。先知其然,再知其所以然,也不失为一种学习方法嘛。 (其实好多我是真的不懂 😂)

简介

一个简单而典型的web audio流程如下:

创建音频上下文
在音频上下文里创建源 — 例如 aduio, 振荡器, 流
创建效果节点,例如混响、双二阶滤波器、平移、压缩
为音频选择一个目的地,例如你的系统扬声器
连接源到效果器,对目的地进行效果输出
流程图

效果视频

代码就不放了需要的可以点击这里

实现

首先实现简单的播放音乐
html媒体标签有<HTML>,<video>等。其余如<input>标签需要转换为arraryBfuuer对象或者HTMLMediaElement对象处理后使用。

从html标签获得媒体源播放

媒体标签元素

<!-- 通过html标签获得音频源 -->
  <audio id='medio' controls src="https://jsdelivr.007666.xyz/gh/1802024110/GitHub_Oss@main/music/群青.mp3"></audio>
  <!-- 或者video,input等标签也可 -->
  <button onclick="play()">播放</button>
const medioAudio = document.getElementById('medio');
      // 获得音频源标签
      medioAudio.crossOrigin = 'anonymous';
      //结局跨域没声音问题

    function play() {
      const ca = new (AudioContext || webkitAudioContext);
      /* 
        因为所有事情都是在上下文中发生的。
        建议创建一个AudioContext对象并复用它,
        而不是每次初始化一个新的AudioContext对象,
        并且可以对多个不同的音频源和管道同时使用一个AudioContext对象。
      */
      const source = ca.createMediaElementSource(medioAudio);
      // 创建一个MediaElementSource对象,并将音频源添加到其中
      source.connect(ca.destination);
      // 将音频源添加到AudioContext的destination属性中
      medioAudio.play();
      // 播放音频源
    }

非媒体标签元素

<input type="file" accept="video/*,audio/*" id="medio2">
<script>
    const ca = new (AudioContext || webkitAudioContext);
    const input = document.getElementById('medio2');

    input.addEventListener('change', function () {
      // 上传文件触发事件
      input.files[0].arrayBuffer().then(function (buffer) {
        // 获得文件的arrayBuffer
        ca.decodeAudioData(buffer).then(function (data) {
          // 解码arrayBuffer
          const source = ca.createBufferSource();
          source.buffer = data;
          source.connect(ca.destination);
          source.start();
        })
      })
    })
</script>

从网络获得媒体源播放

<button onclick="play()">播放</button>
<script>
function play() {
      const ca = new (AudioContext || webkitAudioContext);

      fetch('https://jsdelivr.007666.xyz/gh/1802024110/GitHub_Oss@main/music/群青.mp3')
        // fetch类似ajax/axios,返回一个Response对象
        .then(res => {
          res.arrayBuffer().then(arrayBuffer => {
            // res.arrayBuffer()返回一个ArrayBuffer对象
            ca.decodeAudioData(arrayBuffer).then(audioBuffer => {
              // audioBuffer是AudioBuffer对象
              const source = ca.createBufferSource();
              // 创建一个流对象
              source.buffer = audioBuffer;
              // 把音频数据放入流对象
              source.connect(ca.destination);
              // 把流对象连接到输出设备
              source.start();
              // 播放
            });
          })
        })
    }
</script>

从设备录音播放(手机大部分不可用)

<button id='medio' >开始录音</button>
if (navigator.mediaDevices === undefined) {
      alert('您的浏览器不支持录音功能,请更换浏览器');
    }
    // 检查是否支持API
    const medio = document.getElementById('medio');
    medio.addEventListener('click', function () {
      // button的点击事件
      navigator.mediaDevices.getUserMedia({
        //设置获取设备
        audio: {
          sampleRate: 44100,
          channelCount: 2,
        },
        video: false
      }).then(function (stream) {
        // 录音得到的是MediaStream对象
        const ac = new (AudioContext || webkitAudioContext);
        const soure = ac.createMediaStreamSource(stream);
        soure.connect(ac.destination);
        // 链接到播放器
      }).catch(function (err) {
        switch (err.name) {
          case 'NotFoundError':
            alert('没有找到设备');
            break;
          case 'SecurityError':
            alert('安全错误');
            break;
          case 'NotAllowedError':
            alert('您以拒绝访问麦克风');
            break;
          case 'AbortError':
            alert('硬件出错');
            break;
          default:
            alert('未知错误');
            break;
        }
      });
    })

获得音频数据

<input type="file" accept="video/*,audio/*" id="medio3">
<!-- svg展示波形,可忽略 -->
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg"></svg>
<script>
    const ca = new (AudioContext || webkitAudioContext);
    const input = document.getElementById('medio3');
    input.addEventListener('change', function () {
      input.files[0].arrayBuffer().then(function (buffer) {
        ca.decodeAudioData(buffer).then(function (data) {
          const source = ca.createBufferSource();
          source.buffer = data;

          const analyser = ca.createAnalyser();
          // 创建一个音频分析器
          analyser.fftSize = 512;

          setInterval(() => {

            let arr = new Uint8Array(analyser.frequencyBinCount);
            analyser.getByteFrequencyData(arr)
            arr = arr.slice(5, 170);
            // 取arr里165个数据因为其最活跃
            draw(arr);
            // 往svg画画,不重要
          }, 50);
          source.connect(analyser);
          // 流对象连接到分析器
          analyser.connect(ca.destination);
          // 分析器连接到输出设备
          source.start();
          // 播放
        })
      })
    })

    function draw(arr) {
      /* 绘制音频图 */
      const svg = document.querySelector('svg');
      // 删除svg中的所有元素
      svg.innerHTML = '';
      // svg插入line
      // 创建一个line
      for (i = 1; i < arr.length; i++) {
        const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
        // 屏幕宽度
        const width = window.innerWidth;
        // x坐标为百分比屏幕宽除数组长度
        line.setAttribute('x1', (i * (width / arr.length)));
        line.setAttribute('y1', 255);
        line.setAttribute('x2', i * (width / arr.length));
        line.setAttribute('y2', arr[i]);
        line.setAttribute('stroke', `rgb(${arr[i]},99,${arr[i]})`);
        line.setAttribute('stroke-width', 1 + 'px');
        svg.appendChild(line);
      }
    }
</script>

Markdown显示效果不好

评论