Attempt to fix a bug that can randomly drop audio streams.

This commit is contained in:
Storm Dragon
2026-08-11 20:15:49 -04:00
parent 4c5a54c2dd
commit 0f34831c5b
6 changed files with 47 additions and 10 deletions
+2
View File
@@ -86,6 +86,8 @@ type AudioPacket struct {
Sender *User Sender *User
Target *VoiceTarget Target *VoiceTarget
Final bool Final bool
// Channels is the number of interleaved PCM channels in AudioBuffer.
Channels int
// LocallyMuted and LocalMuteGeneration snapshot the receiver's local mute // LocallyMuted and LocalMuteGeneration snapshot the receiver's local mute
// state when the packet entered the playback queue. // state when the packet entered the playback queue.
LocallyMuted bool LocallyMuted bool
+1
View File
@@ -50,6 +50,7 @@ type AudioEncoder interface {
// AudioDecoder decodes an encoded byte slice to a chunk of PCM audio samples. // AudioDecoder decodes an encoded byte slice to a chunk of PCM audio samples.
type AudioDecoder interface { type AudioDecoder interface {
ID() int ID() int
Channels() int
Decode(data []byte, frameSize int) ([]int16, error) Decode(data []byte, frameSize int) ([]int16, error)
Reset() Reset()
} }
+1
View File
@@ -152,6 +152,7 @@ func (c *Client) handleUDPTunnel(buffer []byte) error {
Client: c, Client: c,
Sender: user, Sender: user,
Final: final, Final: final,
Channels: decoder.Channels(),
LocallyMuted: locallyMuted, LocallyMuted: locallyMuted,
LocalMuteGeneration: localMuteGeneration, LocalMuteGeneration: localMuteGeneration,
Target: &VoiceTarget{ Target: &VoiceTarget{
+21 -10
View File
@@ -41,6 +41,23 @@ func shouldStartPlayback(state openal.State, queued int32, final bool) bool {
return state != openal.Playing && queued > 0 && (queued >= playbackPrebufferBuffers || final) return state != openal.Playing && queued > 0 && (queued >= playbackPrebufferBuffers || final)
} }
func playbackLayout(packet *gumble.AudioPacket) (openal.Format, int, bool) {
if packet == nil || len(packet.AudioBuffer) == 0 {
return 0, 0, false
}
switch packet.Channels {
case gumble.AudioMonoChannels:
return openal.FormatMono16, len(packet.AudioBuffer), true
case gumble.AudioChannels:
if len(packet.AudioBuffer)%gumble.AudioChannels != 0 {
return 0, 0, false
}
return openal.FormatStereo16, len(packet.AudioBuffer) / gumble.AudioChannels, true
default:
return 0, 0, false
}
}
type playbackCommand struct { type playbackCommand struct {
gain float32 gain float32
muted bool muted bool
@@ -550,14 +567,14 @@ func (s *Stream) OnAudioStream(e *gumble.AudioStreamEvent) {
} }
var boost uint16 = uint16(1) var boost uint16 = uint16(1)
samples := len(packet.AudioBuffer) if len(packet.AudioBuffer) == 0 {
if samples == 0 {
if shouldStartPlayback(source.State(), source.BuffersQueued(), packet.Final) { if shouldStartPlayback(source.State(), source.BuffersQueued(), packet.Final) {
source.Play() source.Play()
} }
continue continue
} }
if samples > cap(raw)/2 { format, samples, validLayout := playbackLayout(packet)
if !validLayout || len(packet.AudioBuffer) > cap(raw)/2 {
continue continue
} }
@@ -569,13 +586,7 @@ func (s *Stream) OnAudioStream(e *gumble.AudioStreamEvent) {
recordBuffer = make([]int16, len(packet.AudioBuffer)*gumble.AudioChannels) recordBuffer = make([]int16, len(packet.AudioBuffer)*gumble.AudioChannels)
} }
// Check if sample count suggests stereo data isStereo := packet.Channels == gumble.AudioChannels
isStereo := samples > gumble.AudioDefaultFrameSize && samples%2 == 0
format := openal.FormatMono16
if isStereo {
format = openal.FormatStereo16
samples = samples / 2
}
rawPtr := 0 rawPtr := 0
if isStereo { if isStereo {
+18
View File
@@ -32,6 +32,24 @@ func TestShouldStartPlayback(t *testing.T) {
} }
} }
func TestPlaybackLayoutUsesExplicitStereoChannelCount(t *testing.T) {
packet := &gumble.AudioPacket{
Channels: gumble.AudioChannels,
AudioBuffer: make(gumble.AudioBuffer, gumble.AudioDefaultFrameSize),
}
format, frames, ok := playbackLayout(packet)
if !ok {
t.Fatal("expected a valid short stereo packet")
}
if format != openal.FormatStereo16 {
t.Fatalf("format = %v, want stereo", format)
}
if frames != gumble.AudioDefaultFrameSize/gumble.AudioChannels {
t.Fatalf("frames = %d, want %d", frames, gumble.AudioDefaultFrameSize/gumble.AudioChannels)
}
}
func TestSendOutgoingAudioStopsWhileSendIsBlocked(t *testing.T) { func TestSendOutgoingAudioStopsWhileSendIsBlocked(t *testing.T) {
stop := make(chan struct{}) stop := make(chan struct{})
outgoing := make(chan gumble.AudioBuffer) outgoing := make(chan gumble.AudioBuffer)
+4
View File
@@ -88,6 +88,10 @@ func (*Decoder) ID() int {
return ID return ID
} }
func (d *Decoder) Channels() int {
return d.channels
}
func (d *Decoder) Decode(data []byte, frameSize int) ([]int16, error) { func (d *Decoder) Decode(data []byte, frameSize int) ([]int16, error) {
// Allocate buffer for stereo - frameSize is per channel // Allocate buffer for stereo - frameSize is per channel
pcm := make([]int16, frameSize*gumble.AudioChannels) pcm := make([]int16, frameSize*gumble.AudioChannels)