fix: always use mono Opus encoder for voice transmission

Remove automatic stereo encoder selection based on buffer size. Voice
transmission in the Mumble protocol uses mono Opus with the AppVoIP
profile. The stereo encoder (AppAudio profile) is only appropriate for
file playback, which is controlled by the explicit EnableStereoEncoder
flag.

Additionally, downmix stereo microphone input to mono before sending
when no file playback is active. Previously, a stereo mic would cause
the stereo encoder to be selected via the buffer-size heuristic,
sending non-standard stereo Opus that could confuse other clients'
decoders and cause distorted audio.

This fixes 'user c has issues hearing audio from user a with distorted
audio' when user a has a stereo-input USB headset or similar device.
This commit is contained in:
Brandon McGinty (deepseek)
2026-08-08 19:17:01 -04:00
committed by Brandon McGinty
parent dfa184211e
commit 679127bcf1
2 changed files with 17 additions and 8 deletions
+3 -5
View File
@@ -53,12 +53,10 @@ type AudioStreamEvent struct {
type AudioBuffer []int16
func (a AudioBuffer) writeAudio(client *Client, seq int64, final bool) error {
// Choose encoder based on whether buffer size indicates stereo or mono
// Always use the mono encoder for voice transmission. The stereo
// encoder is only used when explicitly enabled for file playback.
encoder := client.AudioEncoder
frameSize := client.Config.AudioFrameSize()
if len(a) == frameSize*AudioChannels && client.AudioEncoderStereo != nil {
encoder = client.AudioEncoderStereo
} else if client.IsStereoEncoderEnabled() && client.AudioEncoderStereo != nil {
if client.IsStereoEncoderEnabled() && client.AudioEncoderStereo != nil {
encoder = client.AudioEncoderStereo
}
if encoder == nil {
+14 -3
View File
@@ -503,10 +503,21 @@ func (s *Stream) sourceRoutine(inputDevice *string) {
recorder.RecordAudioFrame(recorderOutgoingSource, outputBuffer)
}
} else if hasMicInput {
// Send mic when no file is playing
outgoing <- gumble.AudioBuffer(int16Buffer)
// Send mic when no file is playing. If the microphone is
// stereo, downmix to mono since Mumble voice transmission
// uses mono Opus encoding.
outBuf := int16Buffer
if s.sourceChannels == 2 {
monoBuf := make([]int16, frameSize)
for i := 0; i < frameSize; i++ {
// Average left and right channels
monoBuf[i] = int16((int32(int16Buffer[i*2]) + int32(int16Buffer[i*2+1])) / 2)
}
outBuf = monoBuf
}
outgoing <- gumble.AudioBuffer(outBuf)
if recorder := s.getRecorder(); recorder != nil {
recorder.RecordAudioFrame(recorderOutgoingSource, int16Buffer)
recorder.RecordAudioFrame(recorderOutgoingSource, outBuf)
}
}
}