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

使用Tone.js从数组创建振荡器及Buffer播放问题求助

使用Tone.js动态生成并播放音频的问题解答

核心疑问解答

1. 是否需要createBufferSource?

不需要。你使用的Tone.Player已经封装了原生Web Audio API的BufferSourceNode,内部会自动处理Buffer的加载与播放逻辑,无需手动创建createBufferSource。

2. 能否直接覆盖AudioBuffer的内容?

不能。原生AudioBuffer是不可变对象,一旦创建完成就无法修改其内部数据。如果需要更新音频内容,要么重新创建新的AudioBuffer,要么采用实时生成音频样本的方式(而非依赖静态Buffer)。

实现持续播放动态音频的解决方案

你的代码存在两个关键问题:

  • useEffect中的while(isPlaying)会阻塞主线程,导致页面无响应
  • Tone.Player是为播放静态音频Buffer设计的,不适合动态更新音频数据

以下是修改后的代码,采用Tone.ScriptProcessor在音频线程中实时生成样本,实现持续播放:

import React, { useEffect, useState } from 'react';
import * as Tone from 'tone';

function AudioPlayer() {
  const [isPlaying, setIsPlaying] = useState(false);
  const sampleRate = 16384;
  let t = 0; // 样本计数

  // 创建音频处理节点,每次处理1024个样本
  const processor = new Tone.ScriptProcessor(1024);
  processor.connect(Tone.Destination);

  // 音频线程回调:实时生成样本
  processor.onaudioprocess = (e) => {
    if (!isPlaying) return;
    const outputBuffer = e.outputBuffer.getChannelData(0);
    for (let i = 0; i < outputBuffer.length; i++) {
      // 生成440Hz正弦波样本
      outputBuffer[i] = Math.sin(2 * Math.PI * 440 * (t / sampleRate));
      t++;
    }
  };

  const handlePlayClick = async () => {
    await Tone.start(); // 初始化音频上下文(必须在用户交互中调用)
    setIsPlaying(true);
    t = 0; // 重置样本计数
  };

  const handleStopClick = () => {
    setIsPlaying(false);
  };

  return (
    <div>
      {!isPlaying && (
        <button onClick={handlePlayClick}>Play</button>
      )}
      {isPlaying && (
        <button onClick={handleStopClick}>Stop</button>
      )}
    </div>
  );
}

export default AudioPlayer;

代码说明

  • Tone.ScriptProcessor会在音频渲染线程中触发onaudioprocess回调,每次生成指定数量的音频样本,不会阻塞主线程
  • 必须在用户交互(如点击按钮)中调用Tone.start(),这是浏览器的音频自动播放策略要求
  • 通过控制isPlaying状态,决定是否生成新的音频样本,实现播放/停止控制

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 20:02:41