You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Slicer切换音频时AudioContext关联Analyzer二次选择报错排查

问题描述

页面加载完成后,首次选择下拉框中的音频轨道时,音频分析功能正常运行;但后续切换其他轨道时,控制台在audioSourceNode = audioCtx.createMediaElementSource(audio);行抛出错误,无法正常创建媒体元素源。

报错位置

错误触发代码行:

audioSourceNode = audioCtx.createMediaElementSource(audio);

代码实现

JavaScript代码

// Audio DB
const audioDB = [
  { index: 0, src: 'https://s.cdpn.io/1202/Star_Wars_original_opening_crawl_1977.mp3' },
  { index: 1, src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/128337/ongoing-thing-crop.ogg' }
];

const indexString = ['Please select a track', ...audioDB.map(a => a.index.toString())];

let audioCtx;
let analyzer;
let audioSourceNode;

const audio = document.querySelector('audio');
const select = document.querySelector('.slicer');

select.addEventListener('change', function () {
  if (audioCtx && audioCtx.state !== 'closed') {
    audioCtx.close().then(function () {
      createNewContextAndPlay();
    });
  } else {
    createNewContextAndPlay();
  }

  function createNewContextAndPlay() {
    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    play();
  }

  function play() {
    const index = select.selectedIndex;
    if (audioSourceNode) {
      audioSourceNode.disconnect();
    }

    if (index !== 0) {
      const managedIndex = index - 1;
      const val = audioDB.find(a => a.index === managedIndex)?.src;

      if (val) {
        audio.src = val;
        audio.play();

        if (audioSourceNode) {
          audioSourceNode.disconnect();
        }

        if (audioCtx && audioCtx.state !== 'closed') {
          audioSourceNode = audioCtx.createMediaElementSource(audio);
          analyzer = audioCtx.createAnalyser();

          if (audioSourceNode && analyzer) {
            audioSourceNode.connect(analyzer);
            analyzer.connect(audioCtx.destination);

            console.log(1);
          }
        }
      }
    }
  }
});

HTML代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Document</title>
  </head>
  <body>
    <audio id="audio" crossorigin="anonymous"></audio>
    <div class="slcr">
      <label for="Tracks" style="font-size: 16px; font-family: Oswald, sans-serif; color: red;"> Tracks :- <select class="slicer" id="slicer" style="font-family: Oswald, sans-serif; background-color: aqua;">
          <option>Please select a track</option>
          <option>Track1</option>
          <option>Track2</option>          
        </select>
      </label>
    </div>
  </body>  
</html>

问题分析与修复方案

核心原因

  1. 同一个<audio>元素不能被多个AudioContext的createMediaElementSource绑定,旧Context关闭后,之前创建的audioSourceNode仍持有对audio元素的引用,导致新Context无法重新绑定。
  2. 每次切换都创建新的AudioContext是冗余操作,且容易引发资源冲突;同时切换时未正确暂停并重置当前音频,导致旧播放进程干扰新操作。

修复后的代码

// Audio DB
const audioDB = [
  { index: 0, src: 'https://s.cdpn.io/1202/Star_Wars_original_opening_crawl_1977.mp3' },
  { index: 1, src: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/128337/ongoing-thing-crop.ogg' }
];

let audioCtx;
let analyzer;
let audioSourceNode;

const audio = document.querySelector('audio');
const select = document.querySelector('.slicer');

select.addEventListener('change', async function () {
  // 清理当前播放状态
  audio.pause();
  audio.src = '';
  audio.load();

  // 清理旧的音频节点
  if (audioSourceNode) {
    audioSourceNode.disconnect();
    audioSourceNode = null;
  }
  if (analyzer) {
    analyzer.disconnect();
    analyzer = null;
  }

  const index = select.selectedIndex;
  if (index === 0) return;

  // 初始化AudioContext(仅第一次创建)
  if (!audioCtx || audioCtx.state === 'closed') {
    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
  }
  // 如果Context处于挂起状态,恢复它
  if (audioCtx.state === 'suspended') {
    await audioCtx.resume();
  }

  const managedIndex = index - 1;
  const track = audioDB.find(a => a.index === managedIndex);
  if (!track) return;

  audio.src = track.src;
  await audio.play();

  // 创建新的源节点和分析器
  audioSourceNode = audioCtx.createMediaElementSource(audio);
  analyzer = audioCtx.createAnalyser();

  audioSourceNode.connect(analyzer);
  analyzer.connect(audioCtx.destination);

  console.log('分析器已初始化');
});

修复要点

  • 复用同一个AudioContext,避免重复创建导致的资源冲突
  • 切换轨道时先暂停当前音频、重置src并清理旧的音频节点
  • 处理AudioContext的挂起状态(浏览器会自动挂起闲置的音频上下文)
  • 简化逻辑,去掉不必要的Context关闭操作,改用节点清理实现资源释放

内容的提问来源于stack exchange,提问作者noviceCoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 01:40:55