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

Web Audio API中播放按钮仅生效一次,如何实现多次触发?

Web Audio API播放按钮仅生效一次的问题分析与解决方法

问题原因

Web Audio API的OscillatorNode是一次性资源,调用stop()方法后,该振荡器实例就彻底失效,无法再次调用start()启动。你的代码中仅初始化了一个全局osc实例,首次点击停止后,这个实例已经不能再被复用,后续点击播放按钮调用osc.start()自然不会有任何反应。

修复后的完整代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Frequency Slider</title>
</head>
<body>
  <label for="frequencySlider">Frequency:</label>
  <input type="range" id="frequencySlider" min="20" max="2000" step="1" value="440">
  <span id="frequencyDisplay">440 Hz</span>
  <button id="playButton">Play</button>
  <button id="stopButton">Stop</button>
  <canvas id="waveformCanvas" width="400" height="200"></canvas>

  <script>
    // Web Audio API setup
    const audioContext = new (window.AudioContext || window.webkitAudioContext)();
    let frequencyValue = 440;
    let waveform = "sawtooth";
    let osc = null; // 改为变量,初始为空

    const analyser = audioContext.createAnalyser();
    analyser.fftSize = 2048;
    const dataArray = new Uint8Array(analyser.fftSize);

    // DOM elements
    const frequencySlider = document.getElementById('frequencySlider');
    const frequencyDisplay = document.getElementById('frequencyDisplay');
    const playButton = document.getElementById('playButton');
    const stopButton = document.getElementById('stopButton');
    const waveformCanvas = document.getElementById('waveformCanvas');
    const canvasContext = waveformCanvas.getContext('2d');

    // Event listeners
    frequencySlider.addEventListener('input', updateFrequency);
    playButton.addEventListener('click', playSound);
    stopButton.addEventListener('click', stopSound);

    // Functions
    function updateFrequency() {
      frequencyValue = parseInt(frequencySlider.value);
      frequencyDisplay.textContent = frequencyValue + ' Hz';
      // 仅当振荡器存在时更新频率
      if (osc) {
        osc.frequency.value = frequencyValue;
      }
    }

    async function playSound() {
      // 恢复可能被浏览器挂起的音频上下文
      if (audioContext.state === 'suspended') {
        await audioContext.resume();
      }

      // 创建新的振荡器实例
      osc = audioContext.createOscillator();
      osc.type = waveform;
      osc.frequency.value = frequencyValue;

      // 连接节点并启动
      osc.connect(analyser);
      analyser.connect(audioContext.destination);
      osc.start(audioContext.currentTime);
      requestAnimationFrame(drawWaveform);
    }

    function stopSound() {
      if (osc) {
        osc.stop(audioContext.currentTime);
        osc.disconnect(); // 断开所有连接
        osc = null; // 释放实例
      }
      analyser.disconnect(audioContext.destination);
      canvasContext.clearRect(0, 0, waveformCanvas.width, waveformCanvas.height);
    }

    function drawWaveform() {
      // 仅当振荡器存在时继续绘制
      if (!osc) return;

      analyser.getByteTimeDomainData(dataArray);
      canvasContext.fillStyle = '#ffffff';
      canvasContext.fillRect(0, 0, waveformCanvas.width, waveformCanvas.height);
      canvasContext.lineWidth = 2;
      canvasContext.strokeStyle = '#000000';
      canvasContext.beginPath();

      const sliceWidth = waveformCanvas.width / analyser.fftSize;
      let x = 0;
      for (let i = 0; i < analyser.fftSize; i++) {
        const v = dataArray[i] / 128.0;
        const y = v * waveformCanvas.height / 2;
        if (i === 0) {
          canvasContext.moveTo(x, y);
        } else {
          canvasContext.lineTo(x, y);
        }
        x += sliceWidth;
      }
      canvasContext.lineTo(waveformCanvas.width, waveformCanvas.height / 2);
      canvasContext.stroke();
      requestAnimationFrame(drawWaveform);
    }
  </script>
</body>
</html>

关键改动说明

  1. 振荡器实例动态创建:将全局osc从常量改为变量,每次点击播放时创建新的OscillatorNode实例,避免复用已失效的实例。
  2. 音频上下文恢复:添加了audioContext.resume()的逻辑,解决浏览器自动挂起音频上下文导致无法发声的问题(现代浏览器会限制未交互的音频上下文)。
  3. 频率更新安全判断:在updateFrequency中仅当振荡器存在时才更新频率,避免空指针报错。
  4. 资源清理:停止时断开振荡器连接并将其设为null,及时释放资源,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 09:46:28