WIP:WEB version control

This commit is contained in:
w-okada 2023-11-22 05:53:15 +09:00
parent 14c73a71d2
commit ab837561d9
4 changed files with 243 additions and 80 deletions

View File

@ -17,11 +17,13 @@ export type WebModelLoadingState = (typeof WebModelLoadingState)[keyof typeof We
export type VoiceChangerConfig = {
config: VoiceChangerJSClientConfig;
modelUrl: string;
progressCallback?: ((data: any) => void) | null;
portrait: string;
name: string;
termOfUse: string;
f0: boolean;
sampleRate: ModelSampleRateStr;
useF0: boolean;
inputLength: InputLengthKey;
progressCallback?: ((data: any) => void) | null;
};
export type WebInfoState = {
voiceChangerConfig: VoiceChangerConfig;
@ -35,6 +37,7 @@ export type WebInfoState = {
export type WebInfoStateAndMethod = WebInfoState & {
loadVoiceChanagerModel: () => Promise<void>;
setUpkey: (upkey: number) => void;
setVoiceChangerConfig: (voiceChangerType: VoiceChangerType, sampleRate: ModelSampleRateStr, useF0: boolean, inputLength: InputLengthKey) => void;
};
const ModelSampleRateStr = {
@ -120,72 +123,51 @@ const f0ModelUrl: { [modelType in VoiceChangerType]: { [inputLength in InputLeng
},
};
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_f0_8000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_f0_12000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_f0_16000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_f0_24000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_nof0_8000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_nof0_12000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_nof0_16000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_nof0_24000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_f0_8000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_f0_12000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_f0_16000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_f0_24000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_nof0_8000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_nof0_12000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_nof0_16000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_nof0_24000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_f0_8000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_f0_12000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_f0_16000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_f0_24000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_nof0_8000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_nof0_12000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_nof0_16000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_nof0_24000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_f0_8000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_f0_12000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_f0_16000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_f0_24000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_nof0_8000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_nof0_12000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_nof0_16000.bin
// https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_nof0_24000.bin
export const useWebInfo = (props: UseWebInfoProps): WebInfoStateAndMethod => {
const voiceChangerType: VoiceChangerType = "rvcv1";
const inputLength: InputLengthKey = "24000";
const useF0 = false;
const sampleRate: ModelSampleRateStr = "40k";
let modelUrl;
if (useF0) {
modelUrl = f0ModelUrl[voiceChangerType][inputLength][sampleRate];
} else {
modelUrl = noF0ModelUrl[voiceChangerType][inputLength][sampleRate];
}
const [voiceChangerConfig, setVoiceChangerConfig] = useState<VoiceChangerConfig>({
config: {
voiceChangerType: voiceChangerType,
const initVoiceChangerType: VoiceChangerType = "rvcv1";
const initInputLength: InputLengthKey = "24000";
const initUseF0 = false;
const initSampleRate: ModelSampleRateStr = "40k";
const progressCallback = (data: ProgreeeUpdateCallbcckInfo) => {
if (data.progressUpdateType === ProgressUpdateType.loadPreprocessModel) {
setProgressLoadPreprocess(data.progress);
} else if (data.progressUpdateType === ProgressUpdateType.loadVCModel) {
setProgressLoadVCModel(data.progress);
} else if (data.progressUpdateType === ProgressUpdateType.checkResponseTime) {
setProgressWarmup(data.progress);
}
};
const generateVoiceChangerConfig = (voiceChangerType: VoiceChangerType, sampleRate: ModelSampleRateStr, useF0: boolean, inputLength: InputLengthKey) => {
let modelUrl;
if (useF0) {
modelUrl = f0ModelUrl[voiceChangerType][inputLength][sampleRate];
} else {
modelUrl = noF0ModelUrl[voiceChangerType][inputLength][sampleRate];
}
const config: VoiceChangerConfig = {
config: {
voiceChangerType: voiceChangerType,
inputLength: inputLength,
baseUrl: window.location.origin,
inputSamplingRate: 48000,
outputSamplingRate: 48000,
},
modelUrl: modelUrl,
portrait: `${window.location.origin}/models/amitaro.png`,
name: "あみたろ",
termOfUse: "https://huggingface.co/wok000/vcclient_model/raw/main/rvc/amitaro_contentvec_256/term_of_use.txt",
sampleRate: sampleRate,
useF0: false,
inputLength: inputLength,
baseUrl: window.location.origin,
inputSamplingRate: 48000,
outputSamplingRate: 48000,
},
modelUrl: modelUrl,
progressCallback: null,
portrait: `${window.location.origin}/models/amitaro.png`,
name: "あみたろ",
termOfUse: "https://huggingface.co/wok000/vcclient_model/raw/main/rvc/amitaro_contentvec_256/term_of_use.txt",
f0: false,
});
progressCallback,
};
return config;
};
const [voiceChangerConfig, _setVoiceChangerConfig] = useState<VoiceChangerConfig>(generateVoiceChangerConfig(initVoiceChangerType, initSampleRate, initUseF0, initInputLength));
const [webModelLoadingState, setWebModelLoadingState] = useState<WebModelLoadingState>(WebModelLoadingState.none);
const [progressLoadPreprocess, setProgressLoadPreprocess] = useState<number>(0);
const [progressLoadVCModel, setProgressLoadVCModel] = useState<number>(0);
@ -205,24 +187,19 @@ export const useWebInfo = (props: UseWebInfoProps): WebInfoStateAndMethod => {
speakers: {},
defaultTune: 0,
modelType: "pyTorchRVCNono",
f0: voiceChangerConfig.f0,
f0: voiceChangerConfig.useF0,
samplingRate: 0,
modelFile: "",
};
}, []);
useEffect(() => {
const progressCallback = (data: ProgreeeUpdateCallbcckInfo) => {
if (data.progressUpdateType === ProgressUpdateType.loadPreprocessModel) {
setProgressLoadPreprocess(data.progress);
} else if (data.progressUpdateType === ProgressUpdateType.loadVCModel) {
setProgressLoadVCModel(data.progress);
} else if (data.progressUpdateType === ProgressUpdateType.checkResponseTime) {
setProgressWarmup(data.progress);
}
};
setVoiceChangerConfig({ ...voiceChangerConfig, progressCallback });
}, []);
const setVoiceChangerConfig = (voiceChangerType: VoiceChangerType, sampleRate: ModelSampleRateStr, useF0: boolean, inputLength: InputLengthKey) => {
const config = generateVoiceChangerConfig(voiceChangerType, sampleRate, useF0, inputLength);
_setVoiceChangerConfig(config);
};
// useEffect(() => {
// setVoiceChangerConfig({ ...voiceChangerConfig, progressCallback });
// }, []);
const loadVoiceChanagerModel = async () => {
if (!props.clientState) {
@ -232,9 +209,11 @@ export const useWebInfo = (props: UseWebInfoProps): WebInfoStateAndMethod => {
console.warn("[useWebInfo] clientState is not initialized yet");
return;
}
console.log("loadVoiceChanagerModel1", voiceChangerConfig);
setWebModelLoadingState("loading");
voiceChangerJSClient.current = new VoiceChangerJSClient();
await voiceChangerJSClient.current.initialize(voiceChangerConfig.config, voiceChangerConfig.modelUrl, voiceChangerConfig.progressCallback);
console.log("loadVoiceChanagerModel2");
// worm up
setWebModelLoadingState("warmup");
@ -266,6 +245,16 @@ export const useWebInfo = (props: UseWebInfoProps): WebInfoStateAndMethod => {
voiceChangerJSClient.current.setUpkey(upkey);
}, [upkey]);
useEffect(() => {
console.log("change voice ", voiceChangerConfig);
setProgressLoadPreprocess(0);
setProgressLoadVCModel(0);
setProgressWarmup(0);
loadVoiceChanagerModel();
}, [voiceChangerConfig, props.clientState?.initialized]);
return {
voiceChangerConfig,
webModelLoadingState,
@ -276,5 +265,6 @@ export const useWebInfo = (props: UseWebInfoProps): WebInfoStateAndMethod => {
upkey,
loadVoiceChanagerModel,
setUpkey,
setVoiceChangerConfig,
};
};

View File

@ -61,7 +61,7 @@ export const AppStateProvider = ({ children }: Props) => {
useEffect(() => {
if (appRoot.appGuiSettingState.edition.indexOf("web") >= 0 && clientState.clientState.initialized) {
clientState.clientState.setWorkletNodeSetting({ ...clientState.clientState.setting.workletNodeSetting, protocol: "internal" });
webInfoState.loadVoiceChanagerModel();
// webInfoState.loadVoiceChanagerModel(); // hook内でuseEffectでinvoke
}
}, [clientState.clientState.initialized]);

View File

@ -0,0 +1,168 @@
import React, { useMemo } from "react";
import { useAppState } from "../../../001_provider/001_AppStateProvider";
import { useAppRoot } from "../../../001_provider/001_AppRootProvider";
export type WebEditionSettingAreaProps = {};
export const WebEditionSettingArea = (_props: WebEditionSettingAreaProps) => {
const { serverSetting, webInfoState } = useAppState();
const { appGuiSettingState } = useAppRoot();
const webEdition = appGuiSettingState.edition.indexOf("web") >= 0;
const selected = useMemo(() => {
if (webEdition) {
return webInfoState.webModelslot;
}
return null;
}, [webEdition]);
const settingArea = useMemo(() => {
if (!selected) {
return <></>;
}
const versionV1ClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.config.voiceChangerType == "rvcv1" ? " character-area-control-button-active" : " character-area-control-button-stanby");
const versionV2ClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.config.voiceChangerType == "rvcv2" ? " character-area-control-button-active" : " character-area-control-button-stanby");
const verison = (
<div className="character-area-control">
<div className="character-area-control-title">Version</div>
<div className="character-area-control-field">
<div className="character-area-slider-control">
<span className="character-area-slider-control-kind"></span>
<span className="character-area-control-buttons">
<span
className={versionV1ClassName}
onClick={() => {
webInfoState.setVoiceChangerConfig("rvcv1", webInfoState.voiceChangerConfig.sampleRate, webInfoState.voiceChangerConfig.useF0, webInfoState.voiceChangerConfig.inputLength);
}}
>
v1
</span>
<span
className={versionV2ClassName}
onClick={() => {
console.log("v2 clicked!");
webInfoState.setVoiceChangerConfig("rvcv2", webInfoState.voiceChangerConfig.sampleRate, webInfoState.voiceChangerConfig.useF0, webInfoState.voiceChangerConfig.inputLength);
}}
>
v2
</span>
</span>
</div>
</div>
</div>
);
const sr32KClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.sampleRate == "32k" ? " character-area-control-button-active" : " character-area-control-button-stanby");
const sr40KClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.sampleRate == "40k" ? " character-area-control-button-active" : " character-area-control-button-stanby");
const sampleRate = (
<div className="character-area-control">
<div className="character-area-control-title">SR</div>
<div className="character-area-control-field">
<div className="character-area-slider-control">
<span className="character-area-slider-control-kind"></span>
<span className="character-area-control-buttons">
<span
className={sr32KClassName}
onClick={() => {
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, "32k", webInfoState.voiceChangerConfig.useF0, webInfoState.voiceChangerConfig.inputLength);
}}
>
32k
</span>
<span
className={sr40KClassName}
onClick={() => {
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, "40k", webInfoState.voiceChangerConfig.useF0, webInfoState.voiceChangerConfig.inputLength);
}}
>
40k
</span>
</span>
</div>
</div>
</div>
);
const pitchEnableClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.useF0 == true ? " character-area-control-button-active" : " character-area-control-button-stanby");
const pitchDisableClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.useF0 == false ? " character-area-control-button-active" : " character-area-control-button-stanby");
const pitch = (
<div className="character-area-control">
<div className="character-area-control-title">Pitch</div>
<div className="character-area-control-field">
<div className="character-area-slider-control">
<span className="character-area-slider-control-kind"></span>
<span className="character-area-control-buttons">
<span
className={pitchEnableClassName}
onClick={() => {
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, webInfoState.voiceChangerConfig.sampleRate, true, webInfoState.voiceChangerConfig.inputLength);
}}
>
Enable
</span>
<span
className={pitchDisableClassName}
onClick={() => {
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, webInfoState.voiceChangerConfig.sampleRate, false, webInfoState.voiceChangerConfig.inputLength);
}}
>
Disable
</span>
</span>
</div>
</div>
</div>
);
const latencyHighClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.inputLength == "24000" ? " character-area-control-button-active" : " character-area-control-button-stanby");
const latencyMidClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.inputLength == "12000" ? " character-area-control-button-active" : " character-area-control-button-stanby");
const latencyLowClassName = "character-area-control-button" + (webInfoState.voiceChangerConfig.inputLength == "8000" ? " character-area-control-button-active" : " character-area-control-button-stanby");
const latency = (
<div className="character-area-control">
<div className="character-area-control-title">Latency</div>
<div className="character-area-control-field">
<div className="character-area-slider-control">
<span className="character-area-slider-control-kind"></span>
<span className="character-area-control-buttons">
<span
className={latencyHighClassName}
onClick={() => {
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, webInfoState.voiceChangerConfig.sampleRate, webInfoState.voiceChangerConfig.useF0, "24000");
}}
>
High
</span>
<span
className={latencyMidClassName}
onClick={() => {
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, webInfoState.voiceChangerConfig.sampleRate, webInfoState.voiceChangerConfig.useF0, "12000");
}}
>
Mid
</span>
<span
className={latencyLowClassName}
onClick={() => {
webInfoState.setVoiceChangerConfig(webInfoState.voiceChangerConfig.config.voiceChangerType, webInfoState.voiceChangerConfig.sampleRate, webInfoState.voiceChangerConfig.useF0, "8000");
}}
>
Low
</span>
</span>
</div>
</div>
</div>
);
return (
<>
{verison}
{sampleRate}
{pitch}
{latency}
</>
);
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, selected, webInfoState.upkey, webInfoState.voiceChangerConfig.config.voiceChangerType]);
return settingArea;
};

View File

@ -12,6 +12,7 @@ import { DDSPSVC30SettingArea } from "./101-6_ddsp-svc30SettingArea";
import { DiffusionSVCSettingArea } from "./101-7_diffusion-svcSettingArea";
import { Portrait } from "./101-0_Portrait";
import { useAppRoot } from "../../../001_provider/001_AppRootProvider";
import { WebEditionSettingArea } from "./101-8_web-editionSettingArea";
export type CharacterAreaProps = {};
@ -226,6 +227,9 @@ export const CharacterArea = (_props: CharacterAreaProps) => {
if (!selected) {
return <></>;
}
if (webEdition) {
return <></>;
}
const onUpdateDefaultClicked = async () => {
await serverSetting.updateModelDefault();
};
@ -286,6 +290,7 @@ export const CharacterArea = (_props: CharacterAreaProps) => {
<SoVitsSVC40SettingArea />
<DDSPSVC30SettingArea />
<DiffusionSVCSettingArea />
<WebEditionSettingArea />
{modelSlotControl}
</div>
</div>