mirror of https://github.com/jetkvm/kvm.git
392 lines
13 KiB
TypeScript
392 lines
13 KiB
TypeScript
import { useEffect } from "react";
|
|
|
|
import { SettingsItem } from "@components/SettingsItem";
|
|
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
|
import { useSettingsStore } from "@/hooks/stores";
|
|
import { JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc";
|
|
import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
|
import Checkbox from "@components/Checkbox";
|
|
import { m } from "@localizations/messages.js";
|
|
|
|
import notifications from "../notifications";
|
|
|
|
interface AudioConfigResult {
|
|
bitrate: number;
|
|
complexity: number;
|
|
dtx_enabled: boolean;
|
|
fec_enabled: boolean;
|
|
buffer_periods: number;
|
|
sample_rate: number;
|
|
packet_loss_perc: number;
|
|
}
|
|
|
|
export default function SettingsAudioRoute() {
|
|
const { send } = useJsonRpc();
|
|
const {
|
|
setAudioOutputEnabled,
|
|
setAudioInputAutoEnable,
|
|
setAudioOutputSource,
|
|
audioOutputEnabled,
|
|
audioInputAutoEnable,
|
|
audioOutputSource,
|
|
audioBitrate,
|
|
setAudioBitrate,
|
|
audioComplexity,
|
|
setAudioComplexity,
|
|
audioDTXEnabled,
|
|
setAudioDTXEnabled,
|
|
audioFECEnabled,
|
|
setAudioFECEnabled,
|
|
audioBufferPeriods,
|
|
setAudioBufferPeriods,
|
|
audioSampleRate,
|
|
setAudioSampleRate,
|
|
audioPacketLossPerc,
|
|
setAudioPacketLossPerc,
|
|
} = useSettingsStore();
|
|
|
|
useEffect(() => {
|
|
send("getAudioOutputEnabled", {}, (resp: JsonRpcResponse) => {
|
|
if ("error" in resp) return;
|
|
setAudioOutputEnabled(resp.result as boolean);
|
|
});
|
|
|
|
send("getAudioInputAutoEnable", {}, (resp: JsonRpcResponse) => {
|
|
if ("error" in resp) return;
|
|
setAudioInputAutoEnable(resp.result as boolean);
|
|
});
|
|
|
|
send("getAudioOutputSource", {}, (resp: JsonRpcResponse) => {
|
|
if ("error" in resp) return;
|
|
setAudioOutputSource(resp.result as string);
|
|
});
|
|
|
|
send("getAudioConfig", {}, (resp: JsonRpcResponse) => {
|
|
if ("error" in resp) return;
|
|
const config = resp.result as AudioConfigResult;
|
|
setAudioBitrate(config.bitrate);
|
|
setAudioComplexity(config.complexity);
|
|
setAudioDTXEnabled(config.dtx_enabled);
|
|
setAudioFECEnabled(config.fec_enabled);
|
|
setAudioBufferPeriods(config.buffer_periods);
|
|
setAudioSampleRate(config.sample_rate);
|
|
setAudioPacketLossPerc(config.packet_loss_perc);
|
|
});
|
|
}, [send, setAudioOutputEnabled, setAudioInputAutoEnable, setAudioOutputSource, setAudioBitrate, setAudioComplexity, setAudioDTXEnabled, setAudioFECEnabled, setAudioBufferPeriods, setAudioSampleRate, setAudioPacketLossPerc]);
|
|
|
|
const handleAudioOutputEnabledChange = (enabled: boolean) => {
|
|
send("setAudioOutputEnabled", { enabled }, (resp: JsonRpcResponse) => {
|
|
if ("error" in resp) {
|
|
const errorMsg = enabled
|
|
? m.audio_output_failed_enable({ error: String(resp.error.data || m.unknown_error()) })
|
|
: m.audio_output_failed_disable({ error: String(resp.error.data || m.unknown_error()) });
|
|
notifications.error(errorMsg);
|
|
return;
|
|
}
|
|
setAudioOutputEnabled(enabled);
|
|
const successMsg = enabled ? m.audio_output_enabled() : m.audio_output_disabled();
|
|
notifications.success(successMsg);
|
|
});
|
|
};
|
|
|
|
const handleAudioOutputSourceChange = (source: string) => {
|
|
send("setAudioOutputSource", { source }, (resp: JsonRpcResponse) => {
|
|
if ("error" in resp) {
|
|
notifications.error(m.audio_settings_output_source_failed({ error: String(resp.error.data || m.unknown_error()) }));
|
|
return;
|
|
}
|
|
setAudioOutputSource(source);
|
|
notifications.success(m.audio_settings_output_source_success());
|
|
});
|
|
};
|
|
|
|
const handleAudioInputAutoEnableChange = (enabled: boolean) => {
|
|
send("setAudioInputAutoEnable", { enabled }, (resp: JsonRpcResponse) => {
|
|
if ("error" in resp) {
|
|
notifications.error(String(resp.error.data || m.unknown_error()));
|
|
return;
|
|
}
|
|
setAudioInputAutoEnable(enabled);
|
|
const successMsg = enabled
|
|
? m.audio_input_auto_enable_enabled()
|
|
: m.audio_input_auto_enable_disabled();
|
|
notifications.success(successMsg);
|
|
});
|
|
};
|
|
|
|
const handleAudioConfigChange = (
|
|
bitrate: number,
|
|
complexity: number,
|
|
dtxEnabled: boolean,
|
|
fecEnabled: boolean,
|
|
bufferPeriods: number,
|
|
sampleRate: number,
|
|
packetLossPerc: number
|
|
) => {
|
|
send(
|
|
"setAudioConfig",
|
|
{ bitrate, complexity, dtxEnabled, fecEnabled, bufferPeriods, sampleRate, packetLossPerc },
|
|
(resp: JsonRpcResponse) => {
|
|
if ("error" in resp) {
|
|
notifications.error(String(resp.error.data || m.unknown_error()));
|
|
return;
|
|
}
|
|
setAudioBitrate(bitrate);
|
|
setAudioComplexity(complexity);
|
|
setAudioDTXEnabled(dtxEnabled);
|
|
setAudioFECEnabled(fecEnabled);
|
|
setAudioBufferPeriods(bufferPeriods);
|
|
setAudioSampleRate(sampleRate);
|
|
setAudioPacketLossPerc(packetLossPerc);
|
|
notifications.success(m.audio_settings_config_updated());
|
|
}
|
|
);
|
|
};
|
|
|
|
const handleRestartAudio = () => {
|
|
send("restartAudioOutput", {}, (resp: JsonRpcResponse) => {
|
|
if ("error" in resp) {
|
|
notifications.error(String(resp.error.data || m.unknown_error()));
|
|
return;
|
|
}
|
|
notifications.success(m.audio_settings_applied());
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<SettingsPageHeader
|
|
title={m.audio_settings_title()}
|
|
description={m.audio_settings_description()}
|
|
/>
|
|
<div className="space-y-4">
|
|
<SettingsItem
|
|
title={m.audio_settings_output_title()}
|
|
description={m.audio_settings_output_description()}
|
|
>
|
|
<Checkbox
|
|
checked={audioOutputEnabled || false}
|
|
onChange={(e) => handleAudioOutputEnabledChange(e.target.checked)}
|
|
/>
|
|
</SettingsItem>
|
|
|
|
<SettingsItem
|
|
title={m.audio_settings_output_source_title()}
|
|
description={m.audio_settings_output_source_description()}
|
|
>
|
|
<SelectMenuBasic
|
|
size="SM"
|
|
value={audioOutputSource || "usb"}
|
|
options={[
|
|
{ value: "usb", label: m.audio_settings_usb_label() },
|
|
{ value: "hdmi", label: m.audio_settings_hdmi_label() },
|
|
]}
|
|
onChange={(e) => handleAudioOutputSourceChange(e.target.value)}
|
|
/>
|
|
</SettingsItem>
|
|
|
|
<SettingsItem
|
|
title={m.audio_settings_auto_enable_microphone_title()}
|
|
description={m.audio_settings_auto_enable_microphone_description()}
|
|
>
|
|
<Checkbox
|
|
checked={audioInputAutoEnable || false}
|
|
onChange={(e) => handleAudioInputAutoEnableChange(e.target.checked)}
|
|
/>
|
|
</SettingsItem>
|
|
|
|
<SettingsItem
|
|
title={m.audio_settings_bitrate_title()}
|
|
description={m.audio_settings_bitrate_description()}
|
|
>
|
|
<SelectMenuBasic
|
|
size="SM"
|
|
value={String(audioBitrate)}
|
|
options={[
|
|
{ value: "64", label: "64 kbps" },
|
|
{ value: "96", label: "96 kbps" },
|
|
{ value: "128", label: "128 kbps" },
|
|
{ value: "160", label: "160 kbps" },
|
|
{ value: "192", label: "192 kbps" },
|
|
{ value: "256", label: "256 kbps" },
|
|
]}
|
|
onChange={(e) =>
|
|
handleAudioConfigChange(
|
|
parseInt(e.target.value),
|
|
audioComplexity,
|
|
audioDTXEnabled,
|
|
audioFECEnabled,
|
|
audioBufferPeriods,
|
|
audioSampleRate,
|
|
audioPacketLossPerc
|
|
)
|
|
}
|
|
/>
|
|
</SettingsItem>
|
|
|
|
<SettingsItem
|
|
title={m.audio_settings_complexity_title()}
|
|
description={m.audio_settings_complexity_description()}
|
|
>
|
|
<SelectMenuBasic
|
|
size="SM"
|
|
value={String(audioComplexity)}
|
|
options={[
|
|
{ value: "0", label: "0 (fastest)" },
|
|
{ value: "2", label: "2" },
|
|
{ value: "5", label: "5 (balanced)" },
|
|
{ value: "8", label: "8" },
|
|
{ value: "10", label: "10 (best)" },
|
|
]}
|
|
onChange={(e) =>
|
|
handleAudioConfigChange(
|
|
audioBitrate,
|
|
parseInt(e.target.value),
|
|
audioDTXEnabled,
|
|
audioFECEnabled,
|
|
audioBufferPeriods,
|
|
audioSampleRate,
|
|
audioPacketLossPerc
|
|
)
|
|
}
|
|
/>
|
|
</SettingsItem>
|
|
|
|
<SettingsItem
|
|
title={m.audio_settings_dtx_title()}
|
|
description={m.audio_settings_dtx_description()}
|
|
>
|
|
<Checkbox
|
|
checked={audioDTXEnabled}
|
|
onChange={(e) =>
|
|
handleAudioConfigChange(
|
|
audioBitrate,
|
|
audioComplexity,
|
|
e.target.checked,
|
|
audioFECEnabled,
|
|
audioBufferPeriods,
|
|
audioSampleRate,
|
|
audioPacketLossPerc
|
|
)
|
|
}
|
|
/>
|
|
</SettingsItem>
|
|
|
|
<SettingsItem
|
|
title={m.audio_settings_fec_title()}
|
|
description={m.audio_settings_fec_description()}
|
|
>
|
|
<Checkbox
|
|
checked={audioFECEnabled}
|
|
onChange={(e) =>
|
|
handleAudioConfigChange(
|
|
audioBitrate,
|
|
audioComplexity,
|
|
audioDTXEnabled,
|
|
e.target.checked,
|
|
audioBufferPeriods,
|
|
audioSampleRate,
|
|
audioPacketLossPerc
|
|
)
|
|
}
|
|
/>
|
|
</SettingsItem>
|
|
|
|
<SettingsItem
|
|
title={m.audio_settings_buffer_title()}
|
|
description={m.audio_settings_buffer_description()}
|
|
>
|
|
<SelectMenuBasic
|
|
size="SM"
|
|
value={String(audioBufferPeriods)}
|
|
options={[
|
|
{ value: "4", label: "4 (80ms)" },
|
|
{ value: "8", label: "8 (160ms)" },
|
|
{ value: "12", label: "12 (240ms)" },
|
|
{ value: "16", label: "16 (320ms)" },
|
|
{ value: "24", label: "24 (480ms)" },
|
|
]}
|
|
onChange={(e) =>
|
|
handleAudioConfigChange(
|
|
audioBitrate,
|
|
audioComplexity,
|
|
audioDTXEnabled,
|
|
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)
|
|
)
|
|
}
|
|
/>
|
|
</SettingsItem>
|
|
|
|
<div className="pt-4">
|
|
<button
|
|
onClick={handleRestartAudio}
|
|
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
>
|
|
{m.audio_settings_apply_button()}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|