mirror of https://github.com/jetkvm/kvm.git
Fix: improve EDID compatibility and add audio configuration options
- Update default EDID with registered manufacturer ID (Dell) and proper 24-inch display dimensions (52x32cm) for better macOS/OS compatibility - Add configurable sample rate (32/44.1/48/96 kHz) to support different HDMI audio sources - Add packet loss compensation percentage control for FEC overhead tuning - Fix config migration to ensure new audio parameters get defaults for existing configs - Update all language translations for new audio settings
This commit is contained in:
parent
df76cd0a3e
commit
11dadebb93
6
audio.go
6
audio.go
|
|
@ -53,6 +53,12 @@ func getAudioConfig() audio.AudioConfig {
|
||||||
if config.AudioBufferPeriods >= 2 && config.AudioBufferPeriods <= 24 {
|
if config.AudioBufferPeriods >= 2 && config.AudioBufferPeriods <= 24 {
|
||||||
cfg.BufferPeriods = uint8(config.AudioBufferPeriods)
|
cfg.BufferPeriods = uint8(config.AudioBufferPeriods)
|
||||||
}
|
}
|
||||||
|
if config.AudioSampleRate == 32000 || config.AudioSampleRate == 44100 || config.AudioSampleRate == 48000 || config.AudioSampleRate == 96000 {
|
||||||
|
cfg.SampleRate = uint32(config.AudioSampleRate)
|
||||||
|
}
|
||||||
|
if config.AudioPacketLossPerc >= 0 && config.AudioPacketLossPerc <= 100 {
|
||||||
|
cfg.PacketLossPerc = uint8(config.AudioPacketLossPerc)
|
||||||
|
}
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
19
config.go
19
config.go
|
|
@ -109,12 +109,14 @@ type Config struct {
|
||||||
VideoQualityFactor float64 `json:"video_quality_factor"`
|
VideoQualityFactor float64 `json:"video_quality_factor"`
|
||||||
AudioInputAutoEnable bool `json:"audio_input_auto_enable"`
|
AudioInputAutoEnable bool `json:"audio_input_auto_enable"`
|
||||||
AudioOutputEnabled bool `json:"audio_output_enabled"`
|
AudioOutputEnabled bool `json:"audio_output_enabled"`
|
||||||
AudioOutputSource string `json:"audio_output_source"` // "hdmi" or "usb"
|
AudioOutputSource string `json:"audio_output_source"` // "hdmi" or "usb"
|
||||||
AudioBitrate int `json:"audio_bitrate"` // kbps (64-256)
|
AudioBitrate int `json:"audio_bitrate"` // kbps (64-256)
|
||||||
AudioComplexity int `json:"audio_complexity"` // 0-10
|
AudioComplexity int `json:"audio_complexity"` // 0-10
|
||||||
AudioDTXEnabled bool `json:"audio_dtx_enabled"`
|
AudioDTXEnabled bool `json:"audio_dtx_enabled"`
|
||||||
AudioFECEnabled bool `json:"audio_fec_enabled"`
|
AudioFECEnabled bool `json:"audio_fec_enabled"`
|
||||||
AudioBufferPeriods int `json:"audio_buffer_periods"` // 2-24
|
AudioBufferPeriods int `json:"audio_buffer_periods"` // 2-24
|
||||||
|
AudioSampleRate int `json:"audio_sample_rate"` // Hz (32000, 44100, 48000)
|
||||||
|
AudioPacketLossPerc int `json:"audio_packet_loss_perc"` // 0-100
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetDisplayRotation() uint16 {
|
func (c *Config) GetDisplayRotation() uint16 {
|
||||||
|
|
@ -196,6 +198,8 @@ func getDefaultConfig() Config {
|
||||||
AudioDTXEnabled: true,
|
AudioDTXEnabled: true,
|
||||||
AudioFECEnabled: true,
|
AudioFECEnabled: true,
|
||||||
AudioBufferPeriods: 12,
|
AudioBufferPeriods: 12,
|
||||||
|
AudioSampleRate: 48000,
|
||||||
|
AudioPacketLossPerc: 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -275,6 +279,13 @@ func LoadConfig() {
|
||||||
loadedConfig.AudioBufferPeriods = defaults.AudioBufferPeriods
|
loadedConfig.AudioBufferPeriods = defaults.AudioBufferPeriods
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if loadedConfig.AudioSampleRate == 0 {
|
||||||
|
loadedConfig.AudioSampleRate = getDefaultConfig().AudioSampleRate
|
||||||
|
}
|
||||||
|
if loadedConfig.AudioPacketLossPerc == 0 {
|
||||||
|
loadedConfig.AudioPacketLossPerc = getDefaultConfig().AudioPacketLossPerc
|
||||||
|
}
|
||||||
|
|
||||||
// fixup old keyboard layout value
|
// fixup old keyboard layout value
|
||||||
if loadedConfig.KeyboardLayout == "en_US" {
|
if loadedConfig.KeyboardLayout == "en_US" {
|
||||||
loadedConfig.KeyboardLayout = "en-US"
|
loadedConfig.KeyboardLayout = "en-US"
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ int jetkvm_audio_decode_write(void *opus_buf, int opus_size);
|
||||||
void update_audio_constants(uint32_t bitrate, uint8_t complexity,
|
void update_audio_constants(uint32_t bitrate, uint8_t complexity,
|
||||||
uint32_t sr, uint8_t ch, uint16_t fs, uint16_t max_pkt,
|
uint32_t sr, uint8_t ch, uint16_t fs, uint16_t max_pkt,
|
||||||
uint32_t sleep_us, uint8_t max_attempts, uint32_t max_backoff,
|
uint32_t sleep_us, uint8_t max_attempts, uint32_t max_backoff,
|
||||||
uint8_t dtx_enabled, uint8_t fec_enabled, uint8_t buf_periods);
|
uint8_t dtx_enabled, uint8_t fec_enabled, uint8_t buf_periods, uint8_t pkt_loss_perc);
|
||||||
void update_audio_decoder_constants(uint32_t sr, uint8_t ch, uint16_t fs, uint16_t max_pkt,
|
void update_audio_decoder_constants(uint32_t sr, uint8_t ch, uint16_t fs, uint16_t max_pkt,
|
||||||
uint32_t sleep_us, uint8_t max_attempts, uint32_t max_backoff,
|
uint32_t sleep_us, uint8_t max_attempts, uint32_t max_backoff,
|
||||||
uint8_t buf_periods);
|
uint8_t buf_periods);
|
||||||
|
|
@ -98,7 +98,7 @@ int update_opus_encoder_params(uint32_t bitrate, uint8_t complexity);
|
||||||
void update_audio_constants(uint32_t bitrate, uint8_t complexity,
|
void update_audio_constants(uint32_t bitrate, uint8_t complexity,
|
||||||
uint32_t sr, uint8_t ch, uint16_t fs, uint16_t max_pkt,
|
uint32_t sr, uint8_t ch, uint16_t fs, uint16_t max_pkt,
|
||||||
uint32_t sleep_us, uint8_t max_attempts, uint32_t max_backoff,
|
uint32_t sleep_us, uint8_t max_attempts, uint32_t max_backoff,
|
||||||
uint8_t dtx_enabled, uint8_t fec_enabled, uint8_t buf_periods) {
|
uint8_t dtx_enabled, uint8_t fec_enabled, uint8_t buf_periods, uint8_t pkt_loss_perc) {
|
||||||
opus_bitrate = (bitrate >= 64000 && bitrate <= 256000) ? bitrate : 128000;
|
opus_bitrate = (bitrate >= 64000 && bitrate <= 256000) ? bitrate : 128000;
|
||||||
opus_complexity = (complexity <= 10) ? complexity : 5;
|
opus_complexity = (complexity <= 10) ? complexity : 5;
|
||||||
sample_rate = sr > 0 ? sr : 48000;
|
sample_rate = sr > 0 ? sr : 48000;
|
||||||
|
|
@ -112,6 +112,7 @@ void update_audio_constants(uint32_t bitrate, uint8_t complexity,
|
||||||
opus_dtx_enabled = dtx_enabled ? 1 : 0;
|
opus_dtx_enabled = dtx_enabled ? 1 : 0;
|
||||||
opus_fec_enabled = fec_enabled ? 1 : 0;
|
opus_fec_enabled = fec_enabled ? 1 : 0;
|
||||||
buffer_period_count = (buf_periods >= 2 && buf_periods <= 24) ? buf_periods : 12;
|
buffer_period_count = (buf_periods >= 2 && buf_periods <= 24) ? buf_periods : 12;
|
||||||
|
opus_packet_loss_perc = (pkt_loss_perc <= 100) ? pkt_loss_perc : 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_audio_decoder_constants(uint32_t sr, uint8_t ch, uint16_t fs, uint16_t max_pkt,
|
void update_audio_decoder_constants(uint32_t sr, uint8_t ch, uint16_t fs, uint16_t max_pkt,
|
||||||
|
|
|
||||||
|
|
@ -91,13 +91,15 @@ func (c *CgoSource) Connect() error {
|
||||||
Bool("dtx", c.config.DTXEnabled).
|
Bool("dtx", c.config.DTXEnabled).
|
||||||
Bool("fec", c.config.FECEnabled).
|
Bool("fec", c.config.FECEnabled).
|
||||||
Uint8("buffer_periods", c.config.BufferPeriods).
|
Uint8("buffer_periods", c.config.BufferPeriods).
|
||||||
|
Uint32("sample_rate", c.config.SampleRate).
|
||||||
|
Uint8("packet_loss_perc", c.config.PacketLossPerc).
|
||||||
Str("alsa_device", c.alsaDevice).
|
Str("alsa_device", c.alsaDevice).
|
||||||
Msg("Initializing audio capture")
|
Msg("Initializing audio capture")
|
||||||
|
|
||||||
C.update_audio_constants(
|
C.update_audio_constants(
|
||||||
C.uint(uint32(c.config.Bitrate)*1000),
|
C.uint(uint32(c.config.Bitrate)*1000),
|
||||||
C.uchar(c.config.Complexity),
|
C.uchar(c.config.Complexity),
|
||||||
C.uint(48000),
|
C.uint(c.config.SampleRate),
|
||||||
C.uchar(2),
|
C.uchar(2),
|
||||||
C.ushort(960),
|
C.ushort(960),
|
||||||
C.ushort(1500),
|
C.ushort(1500),
|
||||||
|
|
@ -107,6 +109,7 @@ func (c *CgoSource) Connect() error {
|
||||||
dtx,
|
dtx,
|
||||||
fec,
|
fec,
|
||||||
C.uchar(c.config.BufferPeriods),
|
C.uchar(c.config.BufferPeriods),
|
||||||
|
C.uchar(c.config.PacketLossPerc),
|
||||||
)
|
)
|
||||||
|
|
||||||
rc := C.jetkvm_audio_capture_init()
|
rc := C.jetkvm_audio_capture_init()
|
||||||
|
|
@ -118,7 +121,7 @@ func (c *CgoSource) Connect() error {
|
||||||
os.Setenv("ALSA_PLAYBACK_DEVICE", c.alsaDevice)
|
os.Setenv("ALSA_PLAYBACK_DEVICE", c.alsaDevice)
|
||||||
|
|
||||||
C.update_audio_decoder_constants(
|
C.update_audio_decoder_constants(
|
||||||
C.uint(48000),
|
C.uint(c.config.SampleRate),
|
||||||
C.uchar(2),
|
C.uchar(2),
|
||||||
C.ushort(960),
|
C.ushort(960),
|
||||||
C.ushort(1500),
|
C.ushort(1500),
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,24 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type AudioConfig struct {
|
type AudioConfig struct {
|
||||||
Bitrate uint16
|
Bitrate uint16
|
||||||
Complexity uint8
|
Complexity uint8
|
||||||
BufferPeriods uint8
|
BufferPeriods uint8
|
||||||
DTXEnabled bool
|
DTXEnabled bool
|
||||||
FECEnabled bool
|
FECEnabled bool
|
||||||
|
SampleRate uint32
|
||||||
|
PacketLossPerc uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultAudioConfig() AudioConfig {
|
func DefaultAudioConfig() AudioConfig {
|
||||||
return AudioConfig{
|
return AudioConfig{
|
||||||
Bitrate: 128,
|
Bitrate: 128,
|
||||||
Complexity: 5,
|
Complexity: 5,
|
||||||
BufferPeriods: 12,
|
BufferPeriods: 12,
|
||||||
DTXEnabled: true,
|
DTXEnabled: true,
|
||||||
FECEnabled: true,
|
FECEnabled: true,
|
||||||
|
SampleRate: 48000,
|
||||||
|
PacketLossPerc: 20,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ import (
|
||||||
const sleepModeFile = "/sys/devices/platform/ff470000.i2c/i2c-4/4-000f/sleep_mode"
|
const sleepModeFile = "/sys/devices/platform/ff470000.i2c/i2c-4/4-000f/sleep_mode"
|
||||||
|
|
||||||
// DefaultEDID is the default EDID (identifies as "JetKVM HDMI" with full TC358743 audio/video capabilities).
|
// DefaultEDID is the default EDID (identifies as "JetKVM HDMI" with full TC358743 audio/video capabilities).
|
||||||
const DefaultEDID = "00ffffffffffff002a8b01000100000001230104800000782ec9a05747982712484c00000000d1c081c0a9c0b3000101010101010101083a801871382d40582c450000000000001e011d007251d01e206e28550000000000001e000000fc004a65746b564d2048444d490a20000000fd00187801ff1d000a20202020202001e102032e7229097f070d07070f0707509005040302011f132220111214061507831f000068030c0010003021e2050700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047"
|
// Updated with 24-inch display dimensions (52x32cm) and Dell manufacturer ID for compatibility.
|
||||||
|
const DefaultEDID = "00ffffffffffff0010ac01000100000001230104803420782ec9a05747982712484c00000000d1c081c0a9c0b3000101010101010101083a801871382d40582c450000000000001e011d007251d01e206e28550000000000001e000000fc004a65746b564d2048444d490a20000000fd00187801ff1d000a202020202020016602032e7229097f070d07070f0707509005040302011f132220111214061507831f000068030c0010003021e2050700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047"
|
||||||
|
|
||||||
var extraLockTimeout = 5 * time.Second
|
var extraLockTimeout = 5 * time.Second
|
||||||
|
|
||||||
|
|
|
||||||
44
jsonrpc.go
44
jsonrpc.go
|
|
@ -1040,11 +1040,13 @@ func rpcSetAudioOutputSource(source string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AudioConfigResponse struct {
|
type AudioConfigResponse struct {
|
||||||
Bitrate int `json:"bitrate"`
|
Bitrate int `json:"bitrate"`
|
||||||
Complexity int `json:"complexity"`
|
Complexity int `json:"complexity"`
|
||||||
DTXEnabled bool `json:"dtx_enabled"`
|
DTXEnabled bool `json:"dtx_enabled"`
|
||||||
FECEnabled bool `json:"fec_enabled"`
|
FECEnabled bool `json:"fec_enabled"`
|
||||||
BufferPeriods int `json:"buffer_periods"`
|
BufferPeriods int `json:"buffer_periods"`
|
||||||
|
SampleRate int `json:"sample_rate"`
|
||||||
|
PacketLossPerc int `json:"packet_loss_perc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcGetAudioConfig() (AudioConfigResponse, error) {
|
func rpcGetAudioConfig() (AudioConfigResponse, error) {
|
||||||
|
|
@ -1057,16 +1059,26 @@ func rpcGetAudioConfig() (AudioConfigResponse, error) {
|
||||||
if bufferPeriods < 2 || bufferPeriods > 24 {
|
if bufferPeriods < 2 || bufferPeriods > 24 {
|
||||||
bufferPeriods = 12
|
bufferPeriods = 12
|
||||||
}
|
}
|
||||||
|
sampleRate := config.AudioSampleRate
|
||||||
|
if sampleRate != 32000 && sampleRate != 44100 && sampleRate != 48000 && sampleRate != 96000 {
|
||||||
|
sampleRate = 48000
|
||||||
|
}
|
||||||
|
packetLossPerc := config.AudioPacketLossPerc
|
||||||
|
if packetLossPerc < 0 || packetLossPerc > 100 {
|
||||||
|
packetLossPerc = 20
|
||||||
|
}
|
||||||
return AudioConfigResponse{
|
return AudioConfigResponse{
|
||||||
Bitrate: bitrate,
|
Bitrate: bitrate,
|
||||||
Complexity: config.AudioComplexity,
|
Complexity: config.AudioComplexity,
|
||||||
DTXEnabled: config.AudioDTXEnabled,
|
DTXEnabled: config.AudioDTXEnabled,
|
||||||
FECEnabled: config.AudioFECEnabled,
|
FECEnabled: config.AudioFECEnabled,
|
||||||
BufferPeriods: bufferPeriods,
|
BufferPeriods: bufferPeriods,
|
||||||
|
SampleRate: sampleRate,
|
||||||
|
PacketLossPerc: packetLossPerc,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcSetAudioConfig(bitrate int, complexity int, dtxEnabled bool, fecEnabled bool, bufferPeriods int) error {
|
func rpcSetAudioConfig(bitrate int, complexity int, dtxEnabled bool, fecEnabled bool, bufferPeriods int, sampleRate int, packetLossPerc int) error {
|
||||||
ensureConfigLoaded()
|
ensureConfigLoaded()
|
||||||
|
|
||||||
if bitrate < 64 || bitrate > 256 {
|
if bitrate < 64 || bitrate > 256 {
|
||||||
|
|
@ -1078,12 +1090,20 @@ func rpcSetAudioConfig(bitrate int, complexity int, dtxEnabled bool, fecEnabled
|
||||||
if bufferPeriods < 2 || bufferPeriods > 24 {
|
if bufferPeriods < 2 || bufferPeriods > 24 {
|
||||||
return fmt.Errorf("buffer periods must be between 2 and 24")
|
return fmt.Errorf("buffer periods must be between 2 and 24")
|
||||||
}
|
}
|
||||||
|
if sampleRate != 32000 && sampleRate != 44100 && sampleRate != 48000 && sampleRate != 96000 {
|
||||||
|
return fmt.Errorf("sample rate must be 32000, 44100, 48000, or 96000 Hz")
|
||||||
|
}
|
||||||
|
if packetLossPerc < 0 || packetLossPerc > 100 {
|
||||||
|
return fmt.Errorf("packet loss percentage must be between 0 and 100")
|
||||||
|
}
|
||||||
|
|
||||||
config.AudioBitrate = bitrate
|
config.AudioBitrate = bitrate
|
||||||
config.AudioComplexity = complexity
|
config.AudioComplexity = complexity
|
||||||
config.AudioDTXEnabled = dtxEnabled
|
config.AudioDTXEnabled = dtxEnabled
|
||||||
config.AudioFECEnabled = fecEnabled
|
config.AudioFECEnabled = fecEnabled
|
||||||
config.AudioBufferPeriods = bufferPeriods
|
config.AudioBufferPeriods = bufferPeriods
|
||||||
|
config.AudioSampleRate = sampleRate
|
||||||
|
config.AudioPacketLossPerc = packetLossPerc
|
||||||
|
|
||||||
return SaveConfig()
|
return SaveConfig()
|
||||||
}
|
}
|
||||||
|
|
@ -1434,7 +1454,7 @@ var rpcHandlers = map[string]RPCHandler{
|
||||||
"setAudioOutputSource": {Func: rpcSetAudioOutputSource, Params: []string{"source"}},
|
"setAudioOutputSource": {Func: rpcSetAudioOutputSource, Params: []string{"source"}},
|
||||||
"refreshHdmiConnection": {Func: rpcRefreshHdmiConnection},
|
"refreshHdmiConnection": {Func: rpcRefreshHdmiConnection},
|
||||||
"getAudioConfig": {Func: rpcGetAudioConfig},
|
"getAudioConfig": {Func: rpcGetAudioConfig},
|
||||||
"setAudioConfig": {Func: rpcSetAudioConfig, Params: []string{"bitrate", "complexity", "dtxEnabled", "fecEnabled", "bufferPeriods"}},
|
"setAudioConfig": {Func: rpcSetAudioConfig, Params: []string{"bitrate", "complexity", "dtxEnabled", "fecEnabled", "bufferPeriods", "sampleRate", "packetLossPerc"}},
|
||||||
"restartAudioOutput": {Func: rpcRestartAudioOutput},
|
"restartAudioOutput": {Func: rpcRestartAudioOutput},
|
||||||
"getAudioInputAutoEnable": {Func: rpcGetAudioInputAutoEnable},
|
"getAudioInputAutoEnable": {Func: rpcGetAudioInputAutoEnable},
|
||||||
"setAudioInputAutoEnable": {Func: rpcSetAudioInputAutoEnable, Params: []string{"enabled"}},
|
"setAudioInputAutoEnable": {Func: rpcSetAudioInputAutoEnable, Params: []string{"enabled"}},
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,10 @@
|
||||||
"audio_settings_fec_description": "Forbedre lydkvaliteten på tabende forbindelser",
|
"audio_settings_fec_description": "Forbedre lydkvaliteten på tabende forbindelser",
|
||||||
"audio_settings_buffer_title": "Bufferperioder",
|
"audio_settings_buffer_title": "Bufferperioder",
|
||||||
"audio_settings_buffer_description": "ALSA bufferstørrelse (højere = mere stabil, mere latens)",
|
"audio_settings_buffer_description": "ALSA bufferstørrelse (højere = mere stabil, mere latens)",
|
||||||
|
"audio_settings_sample_rate_title": "Samplingsrate",
|
||||||
|
"audio_settings_sample_rate_description": "Lydsamplingsfrekven (match HDMI-kilde for bedste kvalitet)",
|
||||||
|
"audio_settings_packet_loss_title": "Pakketabskompensation",
|
||||||
|
"audio_settings_packet_loss_description": "FEC overhead-procent (højere = bedre gendannelse, mere båndbredde)",
|
||||||
"audio_settings_config_updated": "Lydkonfiguration opdateret",
|
"audio_settings_config_updated": "Lydkonfiguration opdateret",
|
||||||
"audio_settings_apply_button": "Anvend indstillinger",
|
"audio_settings_apply_button": "Anvend indstillinger",
|
||||||
"audio_settings_applied": "Lydindstillinger anvendt",
|
"audio_settings_applied": "Lydindstillinger anvendt",
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,10 @@
|
||||||
"audio_settings_fec_description": "Audioqualität bei verlustbehafteten Verbindungen verbessern",
|
"audio_settings_fec_description": "Audioqualität bei verlustbehafteten Verbindungen verbessern",
|
||||||
"audio_settings_buffer_title": "Pufferperioden",
|
"audio_settings_buffer_title": "Pufferperioden",
|
||||||
"audio_settings_buffer_description": "ALSA-Puffergröße (höher = stabiler, mehr Latenz)",
|
"audio_settings_buffer_description": "ALSA-Puffergröße (höher = stabiler, mehr Latenz)",
|
||||||
|
"audio_settings_sample_rate_title": "Abtastrate",
|
||||||
|
"audio_settings_sample_rate_description": "Audio-Abtastfrequenz (HDMI-Quelle anpassen für beste Qualität)",
|
||||||
|
"audio_settings_packet_loss_title": "Paketverlust-Kompensation",
|
||||||
|
"audio_settings_packet_loss_description": "FEC-Overhead-Prozentsatz (höher = bessere Wiederherstellung, mehr Bandbreite)",
|
||||||
"audio_settings_config_updated": "Audiokonfiguration aktualisiert",
|
"audio_settings_config_updated": "Audiokonfiguration aktualisiert",
|
||||||
"audio_settings_apply_button": "Einstellungen anwenden",
|
"audio_settings_apply_button": "Einstellungen anwenden",
|
||||||
"audio_settings_applied": "Audioeinstellungen angewendet",
|
"audio_settings_applied": "Audioeinstellungen angewendet",
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,10 @@
|
||||||
"audio_settings_fec_description": "Improve audio quality on lossy connections",
|
"audio_settings_fec_description": "Improve audio quality on lossy connections",
|
||||||
"audio_settings_buffer_title": "Buffer Periods",
|
"audio_settings_buffer_title": "Buffer Periods",
|
||||||
"audio_settings_buffer_description": "ALSA buffer size (higher = more stable, more latency)",
|
"audio_settings_buffer_description": "ALSA buffer size (higher = more stable, more latency)",
|
||||||
|
"audio_settings_sample_rate_title": "Sample Rate",
|
||||||
|
"audio_settings_sample_rate_description": "Audio sampling frequency (match HDMI source for best quality)",
|
||||||
|
"audio_settings_packet_loss_title": "Packet Loss Compensation",
|
||||||
|
"audio_settings_packet_loss_description": "FEC overhead percentage (higher = better recovery, more bandwidth)",
|
||||||
"audio_settings_config_updated": "Audio configuration updated",
|
"audio_settings_config_updated": "Audio configuration updated",
|
||||||
"audio_settings_apply_button": "Apply Settings",
|
"audio_settings_apply_button": "Apply Settings",
|
||||||
"audio_settings_applied": "Audio settings applied",
|
"audio_settings_applied": "Audio settings applied",
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,10 @@
|
||||||
"audio_settings_fec_description": "Mejorar la calidad de audio en conexiones con pérdida",
|
"audio_settings_fec_description": "Mejorar la calidad de audio en conexiones con pérdida",
|
||||||
"audio_settings_buffer_title": "Períodos de Buffer",
|
"audio_settings_buffer_title": "Períodos de Buffer",
|
||||||
"audio_settings_buffer_description": "Tamaño del buffer ALSA (mayor = más estable, más latencia)",
|
"audio_settings_buffer_description": "Tamaño del buffer ALSA (mayor = más estable, más latencia)",
|
||||||
|
"audio_settings_sample_rate_title": "Tasa de Muestreo",
|
||||||
|
"audio_settings_sample_rate_description": "Frecuencia de muestreo de audio (coincidir con fuente HDMI para mejor calidad)",
|
||||||
|
"audio_settings_packet_loss_title": "Compensación de Pérdida de Paquetes",
|
||||||
|
"audio_settings_packet_loss_description": "Porcentaje de sobrecarga FEC (mayor = mejor recuperación, más ancho de banda)",
|
||||||
"audio_settings_config_updated": "Configuración de audio actualizada",
|
"audio_settings_config_updated": "Configuración de audio actualizada",
|
||||||
"audio_settings_apply_button": "Aplicar configuración",
|
"audio_settings_apply_button": "Aplicar configuración",
|
||||||
"audio_settings_applied": "Configuración de audio aplicada",
|
"audio_settings_applied": "Configuración de audio aplicada",
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,10 @@
|
||||||
"audio_settings_fec_description": "Améliorer la qualité audio sur les connexions avec perte",
|
"audio_settings_fec_description": "Améliorer la qualité audio sur les connexions avec perte",
|
||||||
"audio_settings_buffer_title": "Périodes de Tampon",
|
"audio_settings_buffer_title": "Périodes de Tampon",
|
||||||
"audio_settings_buffer_description": "Taille du tampon ALSA (plus élevé = plus stable, plus de latence)",
|
"audio_settings_buffer_description": "Taille du tampon ALSA (plus élevé = plus stable, plus de latence)",
|
||||||
|
"audio_settings_sample_rate_title": "Fréquence d'Échantillonnage",
|
||||||
|
"audio_settings_sample_rate_description": "Fréquence d'échantillonnage audio (correspondre à la source HDMI pour une meilleure qualité)",
|
||||||
|
"audio_settings_packet_loss_title": "Compensation de Perte de Paquets",
|
||||||
|
"audio_settings_packet_loss_description": "Pourcentage de surcharge FEC (plus élevé = meilleure récupération, plus de bande passante)",
|
||||||
"audio_settings_config_updated": "Configuration audio mise à jour",
|
"audio_settings_config_updated": "Configuration audio mise à jour",
|
||||||
"audio_settings_apply_button": "Appliquer les paramètres",
|
"audio_settings_apply_button": "Appliquer les paramètres",
|
||||||
"audio_settings_applied": "Paramètres audio appliqués",
|
"audio_settings_applied": "Paramètres audio appliqués",
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,10 @@
|
||||||
"audio_settings_fec_description": "Migliora la qualità audio su connessioni con perdita",
|
"audio_settings_fec_description": "Migliora la qualità audio su connessioni con perdita",
|
||||||
"audio_settings_buffer_title": "Periodi Buffer",
|
"audio_settings_buffer_title": "Periodi Buffer",
|
||||||
"audio_settings_buffer_description": "Dimensione buffer ALSA (più alto = più stabile, più latenza)",
|
"audio_settings_buffer_description": "Dimensione buffer ALSA (più alto = più stabile, più latenza)",
|
||||||
|
"audio_settings_sample_rate_title": "Frequenza di Campionamento",
|
||||||
|
"audio_settings_sample_rate_description": "Frequenza di campionamento audio (abbinare alla sorgente HDMI per la migliore qualità)",
|
||||||
|
"audio_settings_packet_loss_title": "Compensazione Perdita Pacchetti",
|
||||||
|
"audio_settings_packet_loss_description": "Percentuale overhead FEC (più alto = migliore recupero, più banda)",
|
||||||
"audio_settings_config_updated": "Configurazione audio aggiornata",
|
"audio_settings_config_updated": "Configurazione audio aggiornata",
|
||||||
"audio_settings_apply_button": "Applica impostazioni",
|
"audio_settings_apply_button": "Applica impostazioni",
|
||||||
"audio_settings_applied": "Impostazioni audio applicate",
|
"audio_settings_applied": "Impostazioni audio applicate",
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,10 @@
|
||||||
"audio_settings_fec_description": "Forbedre lydkvaliteten på tapende tilkoblinger",
|
"audio_settings_fec_description": "Forbedre lydkvaliteten på tapende tilkoblinger",
|
||||||
"audio_settings_buffer_title": "Bufferperioder",
|
"audio_settings_buffer_title": "Bufferperioder",
|
||||||
"audio_settings_buffer_description": "ALSA bufferstørrelse (høyere = mer stabil, mer latens)",
|
"audio_settings_buffer_description": "ALSA bufferstørrelse (høyere = mer stabil, mer latens)",
|
||||||
|
"audio_settings_sample_rate_title": "Samplingsrate",
|
||||||
|
"audio_settings_sample_rate_description": "Lydsampleringsfrekvens (match HDMI-kilde for beste kvalitet)",
|
||||||
|
"audio_settings_packet_loss_title": "Pakketapskompensasjon",
|
||||||
|
"audio_settings_packet_loss_description": "FEC overhead-prosent (høyere = bedre gjenoppretting, mer båndbredde)",
|
||||||
"audio_settings_config_updated": "Lydkonfigurasjon oppdatert",
|
"audio_settings_config_updated": "Lydkonfigurasjon oppdatert",
|
||||||
"audio_settings_apply_button": "Bruk innstillinger",
|
"audio_settings_apply_button": "Bruk innstillinger",
|
||||||
"audio_settings_applied": "Lydinnstillinger brukt",
|
"audio_settings_applied": "Lydinnstillinger brukt",
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,10 @@
|
||||||
"audio_settings_fec_description": "Förbättra ljudkvaliteten på förlustdrabbade anslutningar",
|
"audio_settings_fec_description": "Förbättra ljudkvaliteten på förlustdrabbade anslutningar",
|
||||||
"audio_settings_buffer_title": "Bufferperioder",
|
"audio_settings_buffer_title": "Bufferperioder",
|
||||||
"audio_settings_buffer_description": "ALSA bufferstorlek (högre = mer stabil, mer latens)",
|
"audio_settings_buffer_description": "ALSA bufferstorlek (högre = mer stabil, mer latens)",
|
||||||
|
"audio_settings_sample_rate_title": "Samplingsfrekvens",
|
||||||
|
"audio_settings_sample_rate_description": "Ljudsamplingsfrekvens (matcha HDMI-källa för bästa kvalitet)",
|
||||||
|
"audio_settings_packet_loss_title": "Paketförlustkompensation",
|
||||||
|
"audio_settings_packet_loss_description": "FEC overhead-procent (högre = bättre återställning, mer bandbredd)",
|
||||||
"audio_settings_config_updated": "Ljudkonfiguration uppdaterad",
|
"audio_settings_config_updated": "Ljudkonfiguration uppdaterad",
|
||||||
"audio_settings_apply_button": "Tillämpa inställningar",
|
"audio_settings_apply_button": "Tillämpa inställningar",
|
||||||
"audio_settings_applied": "Ljudinställningar tillämpade",
|
"audio_settings_applied": "Ljudinställningar tillämpade",
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,10 @@
|
||||||
"audio_settings_fec_description": "改善有损连接上的音频质量",
|
"audio_settings_fec_description": "改善有损连接上的音频质量",
|
||||||
"audio_settings_buffer_title": "缓冲周期",
|
"audio_settings_buffer_title": "缓冲周期",
|
||||||
"audio_settings_buffer_description": "ALSA 缓冲大小(越高 = 越稳定,延迟越高)",
|
"audio_settings_buffer_description": "ALSA 缓冲大小(越高 = 越稳定,延迟越高)",
|
||||||
|
"audio_settings_sample_rate_title": "采样率",
|
||||||
|
"audio_settings_sample_rate_description": "音频采样频率(匹配 HDMI 源以获得最佳质量)",
|
||||||
|
"audio_settings_packet_loss_title": "丢包补偿",
|
||||||
|
"audio_settings_packet_loss_description": "FEC 开销百分比(越高 = 恢复越好,带宽越大)",
|
||||||
"audio_settings_config_updated": "音频配置已更新",
|
"audio_settings_config_updated": "音频配置已更新",
|
||||||
"audio_settings_apply_button": "应用设置",
|
"audio_settings_apply_button": "应用设置",
|
||||||
"audio_settings_applied": "音频设置已应用",
|
"audio_settings_applied": "音频设置已应用",
|
||||||
|
|
|
||||||
|
|
@ -403,6 +403,10 @@ export interface SettingsState {
|
||||||
setAudioFECEnabled: (enabled: boolean) => void;
|
setAudioFECEnabled: (enabled: boolean) => void;
|
||||||
audioBufferPeriods: number;
|
audioBufferPeriods: number;
|
||||||
setAudioBufferPeriods: (value: number) => void;
|
setAudioBufferPeriods: (value: number) => void;
|
||||||
|
audioSampleRate: number;
|
||||||
|
setAudioSampleRate: (value: number) => void;
|
||||||
|
audioPacketLossPerc: number;
|
||||||
|
setAudioPacketLossPerc: (value: number) => void;
|
||||||
|
|
||||||
resetMicrophoneState: () => void;
|
resetMicrophoneState: () => void;
|
||||||
}
|
}
|
||||||
|
|
@ -472,6 +476,10 @@ export const useSettingsStore = create(
|
||||||
setAudioFECEnabled: (enabled: boolean) => set({ audioFECEnabled: enabled }),
|
setAudioFECEnabled: (enabled: boolean) => set({ audioFECEnabled: enabled }),
|
||||||
audioBufferPeriods: 12,
|
audioBufferPeriods: 12,
|
||||||
setAudioBufferPeriods: (value: number) => set({ audioBufferPeriods: value }),
|
setAudioBufferPeriods: (value: number) => set({ audioBufferPeriods: value }),
|
||||||
|
audioSampleRate: 48000,
|
||||||
|
setAudioSampleRate: (value: number) => set({ audioSampleRate: value }),
|
||||||
|
audioPacketLossPerc: 20,
|
||||||
|
setAudioPacketLossPerc: (value: number) => set({ audioPacketLossPerc: value }),
|
||||||
|
|
||||||
resetMicrophoneState: () => set({ microphoneEnabled: false }),
|
resetMicrophoneState: () => set({ microphoneEnabled: false }),
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ interface AudioConfigResult {
|
||||||
dtx_enabled: boolean;
|
dtx_enabled: boolean;
|
||||||
fec_enabled: boolean;
|
fec_enabled: boolean;
|
||||||
buffer_periods: number;
|
buffer_periods: number;
|
||||||
|
sample_rate: number;
|
||||||
|
packet_loss_perc: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SettingsAudioRoute() {
|
export default function SettingsAudioRoute() {
|
||||||
|
|
@ -37,6 +39,10 @@ export default function SettingsAudioRoute() {
|
||||||
setAudioFECEnabled,
|
setAudioFECEnabled,
|
||||||
audioBufferPeriods,
|
audioBufferPeriods,
|
||||||
setAudioBufferPeriods,
|
setAudioBufferPeriods,
|
||||||
|
audioSampleRate,
|
||||||
|
setAudioSampleRate,
|
||||||
|
audioPacketLossPerc,
|
||||||
|
setAudioPacketLossPerc,
|
||||||
} = useSettingsStore();
|
} = useSettingsStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -63,8 +69,10 @@ export default function SettingsAudioRoute() {
|
||||||
setAudioDTXEnabled(config.dtx_enabled);
|
setAudioDTXEnabled(config.dtx_enabled);
|
||||||
setAudioFECEnabled(config.fec_enabled);
|
setAudioFECEnabled(config.fec_enabled);
|
||||||
setAudioBufferPeriods(config.buffer_periods);
|
setAudioBufferPeriods(config.buffer_periods);
|
||||||
|
setAudioSampleRate(config.sample_rate);
|
||||||
|
setAudioPacketLossPerc(config.packet_loss_perc);
|
||||||
});
|
});
|
||||||
}, [send, setAudioOutputEnabled, setAudioInputAutoEnable, setAudioOutputSource, setAudioBitrate, setAudioComplexity, setAudioDTXEnabled, setAudioFECEnabled, setAudioBufferPeriods]);
|
}, [send, setAudioOutputEnabled, setAudioInputAutoEnable, setAudioOutputSource, setAudioBitrate, setAudioComplexity, setAudioDTXEnabled, setAudioFECEnabled, setAudioBufferPeriods, setAudioSampleRate, setAudioPacketLossPerc]);
|
||||||
|
|
||||||
const handleAudioOutputEnabledChange = (enabled: boolean) => {
|
const handleAudioOutputEnabledChange = (enabled: boolean) => {
|
||||||
send("setAudioOutputEnabled", { enabled }, (resp: JsonRpcResponse) => {
|
send("setAudioOutputEnabled", { enabled }, (resp: JsonRpcResponse) => {
|
||||||
|
|
@ -111,11 +119,13 @@ export default function SettingsAudioRoute() {
|
||||||
complexity: number,
|
complexity: number,
|
||||||
dtxEnabled: boolean,
|
dtxEnabled: boolean,
|
||||||
fecEnabled: boolean,
|
fecEnabled: boolean,
|
||||||
bufferPeriods: number
|
bufferPeriods: number,
|
||||||
|
sampleRate: number,
|
||||||
|
packetLossPerc: number
|
||||||
) => {
|
) => {
|
||||||
send(
|
send(
|
||||||
"setAudioConfig",
|
"setAudioConfig",
|
||||||
{ bitrate, complexity, dtxEnabled, fecEnabled, bufferPeriods },
|
{ bitrate, complexity, dtxEnabled, fecEnabled, bufferPeriods, sampleRate, packetLossPerc },
|
||||||
(resp: JsonRpcResponse) => {
|
(resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(String(resp.error.data || m.unknown_error()));
|
notifications.error(String(resp.error.data || m.unknown_error()));
|
||||||
|
|
@ -126,6 +136,8 @@ export default function SettingsAudioRoute() {
|
||||||
setAudioDTXEnabled(dtxEnabled);
|
setAudioDTXEnabled(dtxEnabled);
|
||||||
setAudioFECEnabled(fecEnabled);
|
setAudioFECEnabled(fecEnabled);
|
||||||
setAudioBufferPeriods(bufferPeriods);
|
setAudioBufferPeriods(bufferPeriods);
|
||||||
|
setAudioSampleRate(sampleRate);
|
||||||
|
setAudioPacketLossPerc(packetLossPerc);
|
||||||
notifications.success(m.audio_settings_config_updated());
|
notifications.success(m.audio_settings_config_updated());
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -204,7 +216,9 @@ export default function SettingsAudioRoute() {
|
||||||
audioComplexity,
|
audioComplexity,
|
||||||
audioDTXEnabled,
|
audioDTXEnabled,
|
||||||
audioFECEnabled,
|
audioFECEnabled,
|
||||||
audioBufferPeriods
|
audioBufferPeriods,
|
||||||
|
audioSampleRate,
|
||||||
|
audioPacketLossPerc
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
@ -230,7 +244,9 @@ export default function SettingsAudioRoute() {
|
||||||
parseInt(e.target.value),
|
parseInt(e.target.value),
|
||||||
audioDTXEnabled,
|
audioDTXEnabled,
|
||||||
audioFECEnabled,
|
audioFECEnabled,
|
||||||
audioBufferPeriods
|
audioBufferPeriods,
|
||||||
|
audioSampleRate,
|
||||||
|
audioPacketLossPerc
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
@ -248,7 +264,9 @@ export default function SettingsAudioRoute() {
|
||||||
audioComplexity,
|
audioComplexity,
|
||||||
e.target.checked,
|
e.target.checked,
|
||||||
audioFECEnabled,
|
audioFECEnabled,
|
||||||
audioBufferPeriods
|
audioBufferPeriods,
|
||||||
|
audioSampleRate,
|
||||||
|
audioPacketLossPerc
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
@ -266,7 +284,9 @@ export default function SettingsAudioRoute() {
|
||||||
audioComplexity,
|
audioComplexity,
|
||||||
audioDTXEnabled,
|
audioDTXEnabled,
|
||||||
e.target.checked,
|
e.target.checked,
|
||||||
audioBufferPeriods
|
audioBufferPeriods,
|
||||||
|
audioSampleRate,
|
||||||
|
audioPacketLossPerc
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
@ -292,6 +312,65 @@ export default function SettingsAudioRoute() {
|
||||||
audioComplexity,
|
audioComplexity,
|
||||||
audioDTXEnabled,
|
audioDTXEnabled,
|
||||||
audioFECEnabled,
|
audioFECEnabled,
|
||||||
|
parseInt(e.target.value),
|
||||||
|
audioSampleRate,
|
||||||
|
audioPacketLossPerc
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
<SettingsItem
|
||||||
|
title={m.audio_settings_sample_rate_title()}
|
||||||
|
description={m.audio_settings_sample_rate_description()}
|
||||||
|
>
|
||||||
|
<SelectMenuBasic
|
||||||
|
size="SM"
|
||||||
|
value={String(audioSampleRate)}
|
||||||
|
options={[
|
||||||
|
{ value: "32000", label: "32 kHz" },
|
||||||
|
{ value: "44100", label: "44.1 kHz" },
|
||||||
|
{ value: "48000", label: "48 kHz (default)" },
|
||||||
|
{ value: "96000", label: "96 kHz" },
|
||||||
|
]}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleAudioConfigChange(
|
||||||
|
audioBitrate,
|
||||||
|
audioComplexity,
|
||||||
|
audioDTXEnabled,
|
||||||
|
audioFECEnabled,
|
||||||
|
audioBufferPeriods,
|
||||||
|
parseInt(e.target.value),
|
||||||
|
audioPacketLossPerc
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
<SettingsItem
|
||||||
|
title={m.audio_settings_packet_loss_title()}
|
||||||
|
description={m.audio_settings_packet_loss_description()}
|
||||||
|
>
|
||||||
|
<SelectMenuBasic
|
||||||
|
size="SM"
|
||||||
|
value={String(audioPacketLossPerc)}
|
||||||
|
options={[
|
||||||
|
{ value: "0", label: "0% (no compensation)" },
|
||||||
|
{ value: "5", label: "5%" },
|
||||||
|
{ value: "10", label: "10%" },
|
||||||
|
{ value: "15", label: "15%" },
|
||||||
|
{ value: "20", label: "20% (default)" },
|
||||||
|
{ value: "25", label: "25%" },
|
||||||
|
{ value: "30", label: "30%" },
|
||||||
|
]}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleAudioConfigChange(
|
||||||
|
audioBitrate,
|
||||||
|
audioComplexity,
|
||||||
|
audioDTXEnabled,
|
||||||
|
audioFECEnabled,
|
||||||
|
audioBufferPeriods,
|
||||||
|
audioSampleRate,
|
||||||
parseInt(e.target.value)
|
parseInt(e.target.value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import notifications from "@/notifications";
|
||||||
import { m } from "@localizations/messages.js";
|
import { m } from "@localizations/messages.js";
|
||||||
|
|
||||||
const defaultEdid =
|
const defaultEdid =
|
||||||
"00ffffffffffff002a8b01000100000001230104800000782ec9a05747982712484c00000000d1c081c0a9c0b3000101010101010101083a801871382d40582c450000000000001e011d007251d01e206e28550000000000001e000000fc004a65744b564d2048444d490a20000000fd00187801ff1d000a20202020202001e102032e7229097f070d07070f0707509005040302011f132220111214061507831f000068030c0010003021e2050700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047";
|
"00ffffffffffff0010ac01000100000001230104803420782ec9a05747982712484c00000000d1c081c0a9c0b3000101010101010101083a801871382d40582c450000000000001e011d007251d01e206e28550000000000001e000000fc004a65746b564d2048444d490a20000000fd00187801ff1d000a202020202020016602032e7229097f070d07070f0707509005040302011f132220111214061507831f000068030c0010003021e2050700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047";
|
||||||
const edids = [
|
const edids = [
|
||||||
{
|
{
|
||||||
value: defaultEdid,
|
value: defaultEdid,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue