Fix USB Audio Gadget sample rate constraints

USB Audio Gadget (hw:1,0) hardware only supports 48kHz for both capture
and playback due to configfs p_srate/c_srate being hardcoded. This commit
ensures both audio paths respect this hardware limitation:

- Output path: Force 48kHz when using hw:1,0, allow configurable rates for HDMI
- Input path: Always use 48kHz regardless of UI configuration
- Calculate frame size dynamically based on actual sample rate used

Also removes redundant comments that don't add debugging or maintainability value.
This commit is contained in:
Alex P 2025-11-21 01:56:56 +02:00
parent 57baa14ee6
commit 2040db6094
3 changed files with 15 additions and 18 deletions

View File

@ -49,28 +49,24 @@ func initAudio() {
func getAudioConfig() audio.AudioConfig {
cfg := audio.DefaultAudioConfig()
// Apply bitrate (64-256 kbps)
if config.AudioBitrate >= 64 && config.AudioBitrate <= 256 {
cfg.Bitrate = uint16(config.AudioBitrate)
} else if config.AudioBitrate != 0 {
audioLogger.Warn().Int("bitrate", config.AudioBitrate).Msg("Invalid audio bitrate, using default")
}
// Apply complexity (0-10)
if config.AudioComplexity >= 0 && config.AudioComplexity <= 10 {
cfg.Complexity = uint8(config.AudioComplexity)
} else if config.AudioComplexity != 0 {
audioLogger.Warn().Int("complexity", config.AudioComplexity).Msg("Invalid audio complexity, using default")
}
// Apply buffer periods (2-24)
if config.AudioBufferPeriods >= 2 && config.AudioBufferPeriods <= 24 {
cfg.BufferPeriods = uint8(config.AudioBufferPeriods)
} else if config.AudioBufferPeriods != 0 {
audioLogger.Warn().Int("buffer_periods", config.AudioBufferPeriods).Msg("Invalid buffer periods, using default")
}
// Apply sample rate (Opus supports: 8k, 12k, 16k, 24k, 48k)
switch config.AudioSampleRate {
case 8000, 12000, 16000, 24000, 48000:
cfg.SampleRate = uint32(config.AudioSampleRate)
@ -80,7 +76,6 @@ func getAudioConfig() audio.AudioConfig {
}
}
// Apply packet loss percentage (0-100)
if config.AudioPacketLossPerc >= 0 && config.AudioPacketLossPerc <= 100 {
cfg.PacketLossPerc = uint8(config.AudioPacketLossPerc)
} else if config.AudioPacketLossPerc != 0 {
@ -118,7 +113,6 @@ func startAudio() error {
inputErr = startInputAudioUnderMutex(getAlsaDevice("usb"))
}
// Simplified error handling - both errors are worth reporting
if outputErr != nil || inputErr != nil {
if outputErr != nil && inputErr != nil {
return fmt.Errorf("audio start failed - output: %w, input: %v", outputErr, inputErr)

View File

@ -83,7 +83,13 @@ func (c *CgoSource) Connect() error {
func (c *CgoSource) connectOutput() error {
os.Setenv("ALSA_CAPTURE_DEVICE", c.alsaDevice)
frameSize := uint16(c.config.SampleRate * 20 / 1000)
// USB Audio Gadget (hw:1,0) only supports 48kHz
// For HDMI (hw:0,0), use configured sample rate
sampleRate := c.config.SampleRate
if c.alsaDevice == "hw:1,0" {
sampleRate = 48000
}
frameSize := uint16(sampleRate * 20 / 1000)
c.logger.Debug().
Uint16("bitrate_kbps", c.config.Bitrate).
@ -91,7 +97,7 @@ func (c *CgoSource) connectOutput() error {
Bool("dtx", c.config.DTXEnabled).
Bool("fec", c.config.FECEnabled).
Uint8("buffer_periods", c.config.BufferPeriods).
Uint32("sample_rate", c.config.SampleRate).
Uint32("sample_rate", sampleRate).
Uint16("frame_size", frameSize).
Uint8("packet_loss_perc", c.config.PacketLossPerc).
Msg("Initializing audio capture")
@ -99,7 +105,7 @@ func (c *CgoSource) connectOutput() error {
C.update_audio_constants(
C.uint(uint32(c.config.Bitrate)*1000),
C.uchar(c.config.Complexity),
C.uint(c.config.SampleRate),
C.uint(sampleRate),
C.uchar(2),
C.ushort(frameSize),
C.ushort(1500),
@ -125,10 +131,13 @@ func (c *CgoSource) connectOutput() error {
func (c *CgoSource) connectInput() error {
os.Setenv("ALSA_PLAYBACK_DEVICE", c.alsaDevice)
frameSize := uint16(c.config.SampleRate * 20 / 1000)
// USB Audio Gadget (hw:1,0) is hardcoded to 48kHz in usbgadget/config.go
// Always use 48kHz for input path regardless of UI configuration
const inputSampleRate = 48000
frameSize := uint16(inputSampleRate * 20 / 1000)
C.update_audio_decoder_constants(
C.uint(c.config.SampleRate),
C.uint(inputSampleRate),
C.uchar(1),
C.ushort(frameSize),
C.ushort(1500),

View File

@ -20,18 +20,15 @@ interface AudioConfigResult {
packet_loss_perc: number;
}
// UI display defaults - used to mark default options in dropdown menus
// Note: These should match backend defaults in config.go, but are fetched dynamically from API
const AUDIO_DEFAULTS = {
bitrate: 192,
complexity: 8,
packetLossPerc: 20, // Backend default is 20, not 0
packetLossPerc: 20,
} as const;
export default function SettingsAudioRoute() {
const { send } = useJsonRpc();
// Helper function to handle RPC errors consistently
const handleRpcError = (resp: JsonRpcResponse, defaultMsg?: string) => {
if ("error" in resp) {
notifications.error(String(resp.error.data || defaultMsg || m.unknown_error()));
@ -64,7 +61,6 @@ export default function SettingsAudioRoute() {
} = useSettingsStore();
useEffect(() => {
// Load boolean settings
send("getAudioOutputEnabled", {}, (resp: JsonRpcResponse) => {
if ("error" in resp) return;
setAudioOutputEnabled(resp.result as boolean);
@ -80,7 +76,6 @@ export default function SettingsAudioRoute() {
setAudioOutputSource(resp.result as string);
});
// Load complex audio configuration
send("getAudioConfig", {}, (resp: JsonRpcResponse) => {
if ("error" in resp) return;
const config = resp.result as AudioConfigResult;
@ -137,7 +132,6 @@ export default function SettingsAudioRoute() {
});
};
// Create a configuration object from current state
const getCurrentConfig = () => ({
bitrate: audioBitrate,
complexity: audioComplexity,