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

如何在Discord.js v14语音流中插入静音片段?

解决Discord.js v14语音录制静音插入与音频合并问题

一、正确实现静音插入(用Transform流替代Readable)

你之前的Readable流写法问题在于每次read()都会重复绑定data事件,导致同一音频块被多次推送,进而拉伸音频时长。改用Transform流可以更精准地控制数据流转,在无音频输入时定时插入静音帧:

const { Transform } = require('stream');
const SILENCE = Buffer.from([0xf8, 0xff, 0xfe]); // 20ms Opus静音帧
const SILENCE_INTERVAL = 20; // 与Discord音频帧时长匹配(ms)

function createSilenceInjectStream() {
  let silenceTimer;

  return new Transform({
    transform(chunk, encoding, callback) {
      // 收到音频块时清除静音定时器
      clearTimeout(silenceTimer);
      // 推送当前音频块
      this.push(chunk);
      // 重置定时器:20ms内无新数据则推送静音,且持续循环直到有新输入
      silenceTimer = setTimeout(() => {
        this.push(SILENCE);
        silenceTimer = setTimeout(() => this.push(SILENCE), SILENCE_INTERVAL);
      }, SILENCE_INTERVAL);
      callback();
    },
    flush(callback) {
      // 流结束时清理定时器
      clearTimeout(silenceTimer);
      callback();
    }
  });
}

async function createListeningStream(connection, userId) {
  const receiverStream = connection.receiver.subscribe(userId, {
    end: { behavior: EndBehaviorType.Manual }
  });

  // 将接收流接入静音插入流
  const userStream = receiverStream.pipe(createSilenceInjectStream());

  // 后续写入文件逻辑保持不变(注意输出为Opus格式,可后续转码)
}

二、判断用户是否发声,避免空文件

可以通过两种方式实现:

  • 监听Speaking事件:
    在录制开始时监听语音连接的speaking事件,记录用户是否有过发声行为:
let userSpoke = false;
connection.on('speaking', (user, speaking) => {
  if (user.id === userId && speaking.bitfield !== 0) {
    userSpoke = true;
  }
});

// 录制结束后判断
if (!userSpoke) {
  // 删除对应空文件
  fs.unlinkSync(userRecordPath);
}
  • 检测流中音频数据:
    在Transform流中判断音频块是否为静音帧,记录是否收到过有效音频:
function createSilenceInjectStream() {
  let silenceTimer;
  let hasValidAudio = false;

  return new Transform({
    transform(chunk, encoding, callback) {
      clearTimeout(silenceTimer);
      // 判断当前块是否为非静音帧
      if (!chunk.equals(SILENCE)) {
        hasValidAudio = true;
      }
      this.push(chunk);
      silenceTimer = setTimeout(() => {
        this.push(SILENCE);
        silenceTimer = setTimeout(() => this.push(SILENCE), SILENCE_INTERVAL);
      }, SILENCE_INTERVAL);
      callback();
    },
    flush(callback) {
      clearTimeout(silenceTimer);
      this.hasValidAudio = hasValidAudio;
      callback();
    }
  });
}

// 使用时
const silenceInjectStream = createSilenceInjectStream();
const userStream = receiverStream.pipe(silenceInjectStream);

// 录制结束后判断
if (!silenceInjectStream.hasValidAudio) {
  fs.unlinkSync(userRecordPath);
}

三、音频合集(多用户音频叠加)

直接用Node流叠加音频容易出现同步问题,推荐用ffmpeg处理:

  1. 先将每个用户的Opus文件转成48kHz单声道WAV(与Discord音频参数一致);
  2. 用amix滤镜合并多个WAV文件:
const ffmpeg = require('fluent-ffmpeg');

function mergeAudioFiles(userAudioPaths, outputPath) {
  const command = ffmpeg();
  userAudioPaths.forEach(path => command.input(path));
  command
    .complexFilter([`amix=inputs=${userAudioPaths.length}:duration=longest`])
    .output(outputPath)
    .on('end', () => console.log('音频合并完成'))
    .run();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 23:25:27