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
相关产品推荐
相关产品推荐

