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

Web Audio API+SoundtouchJS:音频节点图构建问题求助

解决方案:针对指定AudioBufferSourceNode实现SoundTouchJS移调

正确的音频节点拓扑结构

仅为3个AudioBufferSourceNode添加SoundTouch移调处理器,将所有源(含未处理的MediaElementAudioSourceNode)通过合并器汇总后输出到扬声器,正确结构如下:

MS --------------------\
S1 --- SoundTouchWorklet --\\
S2 --- SoundTouchWorklet ---> Merger -- Destination
S3 --- SoundTouchWorklet --/

分步实现代码

1. 注册Formant Correction Worklet

确保Worklet处理器代码无TypeScript残留语法,采用内联方式加载避免跨域问题:

// 内联Worklet处理器核心代码(TS转JS后版本)
const workletCode = `
class FormantCorrectionProcessor extends AudioWorkletProcessor {
  // 此处放入SoundTouchJS提供的Worklet处理器逻辑,移除所有TypeScript类型注解
  // 示例核心逻辑框架(需替换为官方转译后的完整代码)
  static get parameterDescriptors() {
    return [
      { name: 'pitchShift', defaultValue: 0, minValue: -12, maxValue: 12 },
      { name: 'rate', defaultValue: 1, minValue: 0.5, maxValue: 2 },
      { name: 'formantShift', defaultValue: 0, minValue: -12, maxValue: 12 }
    ];
  }

  process(inputs, outputs, parameters) {
    // 官方处理器核心处理逻辑
    return true;
  }
}

registerProcessor('formant-correction-processor', FormantCorrectionProcessor);
`;

// 注册Worklet到音频上下文
async function setupWorklet(audioContext) {
  const blob = new Blob([workletCode], { type: 'application/javascript' });
  const url = URL.createObjectURL(blob);
  await audioContext.audioWorklet.addModule(url);
}

2. 初始化音频节点并建立连接

async function initAudio() {
  const audioContext = new (window.AudioContext || window.webkitAudioContext)();
  await setupWorklet(audioContext);

  // 初始化媒体源(无需移调)
  const mediaElement = document.getElementById('your-media-element');
  const mediaSource = audioContext.createMediaElementSource(mediaElement);

  // 初始化3个音频缓冲源(假设已加载audioBuffer1/2/3)
  const source1 = audioContext.createBufferSource();
  source1.buffer = audioBuffer1;
  const source2 = audioContext.createBufferSource();
  source2.buffer = audioBuffer2;
  const source3 = audioContext.createBufferSource();
  source3.buffer = audioBuffer3;

  // 创建移调处理器工厂函数
  const createPitchShifter = (context, pitchShift) => {
    const processor = new AudioWorkletNode(context, 'formant-correction-processor');
    processor.parameters.get('pitchShift').value = pitchShift; // 设置半音移调值
    return processor;
  };

  const shifter1 = createPitchShifter(audioContext, 2); // 升2半音
  const shifter2 = createPitchShifter(audioContext, -1); // 降1半音
  const shifter3 = createPitchShifter(audioContext, 3); // 升3半音

  // 创建4通道合并器(对应4个音频源)
  const merger = audioContext.createChannelMerger(4);

  // 建立节点连接
  mediaSource.connect(merger, 0, 0); // 媒体源直接连合并器第0通道

  source1.connect(shifter1);
  shifter1.connect(merger, 0, 1); // 处理后的源1连合并器第1通道

  source2.connect(shifter2);
  shifter2.connect(merger, 0, 2); // 处理后的源2连合并器第2通道

  source3.connect(shifter3);
  shifter3.connect(merger, 0, 3); // 处理后的源3连合并器第3通道

  merger.connect(audioContext.destination);

  // 启动音频缓冲源(媒体源播放由DOM控制)
  source1.start();
  source2.start();
  source3.start();
}

常见问题排查

  • Worklet注册失败:检查浏览器是否支持AudioWorklet,处理器代码是否有语法错误,确保完全移除TypeScript类型注解。
  • 移调无效果:确认pitchShift参数设置在有效范围(-12~12半音),节点连接顺序为源→处理器→合并器,且AudioBufferSourceNode已调用start()。
  • 音频不同步:若缓冲源与媒体源不同步,可通过audioContext.currentTime指定启动时间,如source1.start(audioContext.currentTime)。
  • 节点连接错误:禁止将AudioBufferSourceNode连接到MediaElementAudioSourceNode(源节点无法作为输入节点),不要给媒体源添加移调处理器。

内容的提问来源于stack exchange,提问作者Mike Johnson Jr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.03 15:13:10