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

Py-Cord语音录制:如何添加首尾静音并统一用户音频时长?

解决Pycord录制Discord语音时音轨长度不一致的问题

你的核心问题是时间单位不匹配:datetime.now().timestamp()返回的是秒级时间戳,但Pycord中data.timestamp是Opus编码的语音帧计数(每帧包含960个采样点,对应48kHz采样率下的20ms时长),直接用两者相减会导致静音时长计算完全错误,这就是为什么5秒静音变成了1分钟。

下面是不需要修改库文件的正确实现方案:

1. 统一时间基准

录音开始时,以第一个收到的语音包的timestamp作为全局起始时间(而非系统时间),这样所有用户的静音计算都基于Discord语音的帧时间轴,避免单位混乱。

2. 修正开头静音计算

当首次收到某个用户的语音时,计算该用户的第一个语音包与全局起始时间的帧差,以此生成开头静音。

3. 补充结尾静音

录音结束后,找到所有用户中最后一个语音包的timestamp,给每个用户的音轨补上从其最后一个语音包到该时间点的静音,确保所有音轨长度一致。

完整代码示例

import struct
import opus  # 确保已安装pycord依赖的opus库

class VoiceRecorder:
    def __init__(self):
        self.recording_start_ts = None  # 全局录音起始的Opus帧时间戳
        self.user_timestamps = {}       # 存储每个用户的最后语音帧时间戳
        self.user_audio = {}            # 存储每个用户的完整音频数据

    def recv_decoded_audio(self, data):
        # 初始化全局录音起始时间为第一个收到的语音包时间戳
        if self.recording_start_ts is None:
            self.recording_start_ts = data.timestamp

        ssrc = data.ssrc
        if ssrc not in self.user_timestamps:
            # 首次接收该用户语音:计算从录音开始到当前的静音帧数量
            silence_frames = data.timestamp - self.recording_start_ts
            self.user_timestamps[ssrc] = data.timestamp
        else:
            # 非首次接收:计算两次语音之间的静音帧数量(减去当前帧的960个采样)
            silence_frames = data.timestamp - self.user_timestamps[ssrc] - 960
            self.user_timestamps[ssrc] = data.timestamp

        # 生成静音数据:每个采样是16位小端0,乘以帧数量和通道数
        silence_data = struct.pack("<h", 0) * silence_frames * opus._OpusStruct.CHANNELS
        # 拼接静音与当前解码后的音频
        full_audio_data = silence_data + data.decoded_data

        # 将数据追加到对应用户的音频缓存
        if ssrc not in self.user_audio:
            self.user_audio[ssrc] = b""
        self.user_audio[ssrc] += full_audio_data

    def stop_recording(self):
        if not self.user_timestamps:
            print("没有录制到任何音频")
            return

        # 获取所有用户中最晚的语音帧时间戳
        max_end_ts = max(self.user_timestamps.values())
        channel_count = opus._OpusStruct.CHANNELS

        # 为每个用户补充结尾静音
        for ssrc, audio in self.user_audio.items():
            last_user_ts = self.user_timestamps[ssrc]
            end_silence_frames = max_end_ts - last_user_ts
            if end_silence_frames > 0:
                end_silence_data = struct.pack("<h", 0) * end_silence_frames * channel_count
                self.user_audio[ssrc] += end_silence_data

        # 这里可以添加保存音频文件的逻辑,比如用wave库写入
        # 示例:保存为WAV文件(假设是单通道16位48kHz)
        import wave
        for ssrc, audio in self.user_audio.items():
            with wave.open(f"user_{ssrc}.wav", "wb") as wf:
                wf.setnchannels(channel_count)
                wf.setsampwidth(2)  # 16位采样
                wf.setframerate(48000)
                wf.writeframes(audio)

关键说明

  • Opus帧与时间的换算:每帧960个采样点,48kHz采样率下,1秒对应50帧(48000 / 960 = 50),所以silence_frames除以50就是静音的秒数。
  • 无需修改Pycord库文件:通过自己封装录音逻辑,在接收解码音频时处理静音拼接,完全在业务代码层解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 15:39:23