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

MediaStreamAPI实时音频流传输出现节律性杂音问题求助

问题根因
  • 播放时序完全未对齐:当前收到音频块后直接调用source.start(),没有和上一块的结束时间做校准,两块音频之间要么出现空白间隙(插入零采样导致电平突变)、要么出现重叠,直接产生爆音。你调高audioC采样率后单块音频时长更短,间隙出现频率升高,杂音自然更密集。
  • 采样率不匹配:创建audioC时未指定采样率,默认会使用系统音频设备的采样率(通常为44100/48000),和你发送端用的16384采样率不一致,浏览器每块音频都要做重采样,接缝处的采样转换误差会叠加产生爆音。
  • 无接缝平滑处理:即使时序对齐,前后两块音频的首尾采样如果没有落在过零点,电平跳变也会产生咔哒声。
  • 使用已废弃的ScriptProcessor API:该API回调时序不稳定,采集和输出的块时间偏差会不断累积,进一步加剧时序错位问题。
修复方案

核心修改后的完整代码如下:

const socket = io();
const AudioContext = window.AudioContext || window.webkitAudioContext;
// 统一只用一个AudioContext,避免多上下文采样率冲突
const audioCtx = new AudioContext({
  latencyHint: 'interactive',
  sampleRate: 16384,
});
// 维护下一块音频的播放时间戳
let nextPlayTime = 0;
// 淡入淡出长度,16384采样率下约10ms
const FADE_LENGTH = 160;

navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

if (navigator.getUserMedia) {
  console.log('getUserMedia supported.');
  navigator.getUserMedia(
    { audio: true },
    stream => {
      const source = audioCtx.createMediaStreamSource(stream);
      // 可根据延迟需求调整bufferSize,越小延迟越低,建议2048/4096
      const processor = audioCtx.createScriptProcessor(4096, 1, 1);
      source.connect(processor);
      // 不需要本地自听可以去掉这行,避免回声
      // processor.connect(audioCtx.destination);
      
      processor.onaudioprocess = e => {
        // 直接传输ArrayBuffer,避免序列化精度损失
        socket.emit('Voice', e.inputBuffer.getChannelData(0).buffer);
      };

      socket.on('Voice', msg => {
        const z = new Float32Array(msg, 0, 4096);
        // 加淡入淡出处理,避免电平跳变
        for (let i = 0; i < FADE_LENGTH; i++) {
          z[i] *= i / FADE_LENGTH;
        }
        for (let i = z.length - FADE_LENGTH; i < z.length; i++) {
          z[i] *= (z.length - i) / FADE_LENGTH;
        }
        
        const audioBuffer = new AudioBuffer({
          length: 4096,
          sampleRate: 16384 
        });
        audioBuffer.copyToChannel(z, 0, 0);
        const playSource = audioCtx.createBufferSource();
        playSource.buffer = audioBuffer;
        playSource.connect(audioCtx.destination);
        
        // 对齐播放时序
        if (nextPlayTime === 0) {
          // 首次播放加50ms缓冲,避免网络抖动导致断流
          nextPlayTime = audioCtx.currentTime + 0.05;
        }
        playSource.start(nextPlayTime);
        // 更新下一次播放时间
        nextPlayTime += audioBuffer.duration;
      });
      
      audioCtx.resume();
    },
    err => console.error(err)
  );
}

核心改动点说明:

  • 统一使用同一个AudioContext,所有环节采样率都固定为16384,消除重采样误差
  • 维护全局播放时间戳,所有音频块按预设时间顺序播放,彻底消除间隙和重叠
  • 对每块音频的首尾做淡入淡出处理,平滑电平跳变
  • 传输时直接发送ArrayBuffer二进制数据,避免JSON序列化带来的精度损失
  • 可根据实际延迟需求调整ScriptProcessor的bufferSize,条件允许建议替换为性能更稳定、时序更准的AudioWorklet

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 16:57:04