Discord语音录制中静音时长计算与写入问题排查
Discord语音录制:静音时长计算错误与缓冲区溢出问题
Discord在用户未发言时不会发送音频,因此需要计算用户上次语音数据接收后的时长,向缓冲区写入对应静音来保证时序正确。以下是音频数据处理的核心代码:
private async Task OnVoiceReceived(VoiceNextConnection sender, VoiceReceiveEventArgs e) { try { if (!_isRecording) return; if (e.User is null) return; if (!_circularBuffers.TryGetValue(e.User.Id, out var circularBuffer)) { circularBuffer = new CircularBuffer(e.AudioFormat.SampleRate * e.AudioFormat.ChannelCount * 2 * _bufferDurationSeconds, e.AudioFormat.ChannelCount); _circularBuffers[e.User.Id] = circularBuffer; } TimeSpan delay; if (!_lastVoiceActivity.TryGetValue(e.User.Id, out var lastData)) { // 若用户无历史数据,假设上次语音时间等于录制开始时间 delay = DateTimeOffset.Now - _recordingStartedAt; } else delay = DateTimeOffset.Now - lastData; // 计算需要添加的静音数据长度 var silence = new byte[e.AudioFormat.SampleRate * e.AudioFormat.ChannelCount * 2 * (int)Math.Round(delay.TotalSeconds)]; await circularBuffer.WriteAsync(silence); _lastVoiceActivity[e.User.Id] = DateTimeOffset.Now; await circularBuffer.WriteAsync(e.PcmData); } catch (Exception exception) { Log.Error(exception, "Error while receiving voice data"); } }
录制完成后,使用ffmpeg将缓冲区内容写入.wav文件并完成混音。
当前问题
- 静音时长计算错误,多用户混音后时序错乱:有时静音不足导致发言提前,有时静音过长导致回复延迟
- 缓冲区溢出触发数据删除逻辑后,上述问题频发
- 已尝试修改静音计算公式、调整
_lastVoiceActivity赋值时机、设置仅当延迟超过100ms时添加静音,但均无改善 - 测试使用的是可存储30秒音频的循环缓冲区
更新发现
禁用缓冲区溢出删除数据的逻辑后,问题几乎消失。以下是当前的CircularBuffer实现代码:
public class CircularBuffer { private readonly ConcurrentQueue<byte> _buffer; private readonly object _lockObject; private readonly int _bufferSize; public int Channels { get; private set; } public int Count { get { lock (_lockObject) { return _buffer.Count; } } } public CircularBuffer(int size, int channels) { _buffer = new ConcurrentQueue<byte>(); _bufferSize = size; Channels = channels; _lockObject = new object(); } public async Task WriteAsync(ReadOnlyMemory<byte> data) { lock (_lockObject) { if (data.Length > _bufferSize) data = data[^_bufferSize..]; if (_buffer.Count + data.Length > _bufferSize) { var bytesToRemove = _buffer.Count + data.Length - _bufferSize; for (var i = 0; i < bytesToRemove; i++) { _buffer.TryDequeue(out _); } } for (var index = 0; index < data.Span.Length; index++) { var b = data.Span[index]; _buffer.Enqueue(b); } } await Task.CompletedTask; } public async Task ReadAsync(Memory<byte> data) { lock (_lockObject) { if (data.Length > _buffer.Count) { data = data[.._buffer.Count]; } for (var index = 0; index < data.Span.Length; index++) { _buffer.TryDequeue(out var b); data.Span[index] = b; } } await Task.CompletedTask; } public void Clear() { lock (_lockObject) { _buffer.Clear(); } } }
现寻求解决静音时长计算错误及缓冲区溢出相关问题的可行方案。
内容的提问来源于stack exchange,提问作者Bohdan Petrenko
相关产品推荐
相关产品推荐

