2023-11-19 16:28:38 +03:00
|
|
|
import { ClientState, WebModelSlot } from "@dannadori/voice-changer-client-js";
|
2023-11-22 01:10:34 +03:00
|
|
|
import { VoiceChangerJSClientConfig, VoiceChangerJSClient, ProgressUpdateType, ProgreeeUpdateCallbcckInfo, VoiceChangerType, InputLengthKey, ResponseTimeInfo } from "@dannadori/voice-changer-js";
|
2023-11-19 16:28:38 +03:00
|
|
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
|
|
|
|
|
|
export type UseWebInfoProps = {
|
|
|
|
clientState: ClientState | null;
|
2023-11-23 00:46:12 +03:00
|
|
|
webEdition: boolean;
|
2023-11-19 16:28:38 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
export const WebModelLoadingState = {
|
|
|
|
none: "none",
|
|
|
|
loading: "loading",
|
|
|
|
warmup: "warmup",
|
|
|
|
ready: "ready",
|
|
|
|
} as const;
|
|
|
|
export type WebModelLoadingState = (typeof WebModelLoadingState)[keyof typeof WebModelLoadingState];
|
|
|
|
|
|
|
|
export type VoiceChangerConfig = {
|
|
|
|
config: VoiceChangerJSClientConfig;
|
|
|
|
modelUrl: string;
|
|
|
|
portrait: string;
|
|
|
|
name: string;
|
|
|
|
termOfUse: string;
|
2023-11-21 23:53:15 +03:00
|
|
|
sampleRate: ModelSampleRateStr;
|
|
|
|
useF0: boolean;
|
|
|
|
inputLength: InputLengthKey;
|
|
|
|
progressCallback?: ((data: any) => void) | null;
|
2023-11-19 16:28:38 +03:00
|
|
|
};
|
|
|
|
export type WebInfoState = {
|
|
|
|
voiceChangerConfig: VoiceChangerConfig;
|
|
|
|
webModelLoadingState: WebModelLoadingState;
|
|
|
|
progressLoadPreprocess: number;
|
|
|
|
progressLoadVCModel: number;
|
2023-11-21 05:45:27 +03:00
|
|
|
progressWarmup: number;
|
2023-11-19 16:28:38 +03:00
|
|
|
webModelslot: WebModelSlot;
|
2023-11-21 17:10:43 +03:00
|
|
|
upkey: number;
|
2023-11-22 01:10:34 +03:00
|
|
|
responseTimeInfo: ResponseTimeInfo;
|
2023-11-19 16:28:38 +03:00
|
|
|
};
|
|
|
|
export type WebInfoStateAndMethod = WebInfoState & {
|
|
|
|
loadVoiceChanagerModel: () => Promise<void>;
|
2023-11-21 17:10:43 +03:00
|
|
|
setUpkey: (upkey: number) => void;
|
2023-11-21 23:53:15 +03:00
|
|
|
setVoiceChangerConfig: (voiceChangerType: VoiceChangerType, sampleRate: ModelSampleRateStr, useF0: boolean, inputLength: InputLengthKey) => void;
|
2023-11-21 17:10:43 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
const ModelSampleRateStr = {
|
|
|
|
"40k": "40k",
|
|
|
|
"32k": "32k",
|
2023-11-28 18:30:52 +03:00
|
|
|
"16k": "16k",
|
2023-11-21 17:10:43 +03:00
|
|
|
} as const;
|
|
|
|
type ModelSampleRateStr = (typeof ModelSampleRateStr)[keyof typeof ModelSampleRateStr];
|
|
|
|
|
|
|
|
const noF0ModelUrl: { [modelType in VoiceChangerType]: { [inputLength in InputLengthKey]: { [sampleRate in ModelSampleRateStr]: string } } } = {
|
|
|
|
rvcv1: {
|
|
|
|
"24000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_nof0_24000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_nof0_24000.bin",
|
|
|
|
},
|
|
|
|
"16000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_nof0_16000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_nof0_16000.bin",
|
|
|
|
},
|
|
|
|
"12000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_nof0_12000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_nof0_12000.bin",
|
|
|
|
},
|
|
|
|
"8000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_nof0_8000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_nof0_8000.bin",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
rvcv2: {
|
|
|
|
"24000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_nof0_24000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_nof0_24000.bin",
|
2023-11-28 18:30:52 +03:00
|
|
|
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_nof0_24000.bin",
|
2023-11-21 17:10:43 +03:00
|
|
|
},
|
|
|
|
"16000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_nof0_16000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_nof0_16000.bin",
|
2023-11-28 18:30:52 +03:00
|
|
|
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_nof0_16000.bin",
|
2023-11-21 17:10:43 +03:00
|
|
|
},
|
|
|
|
"12000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_nof0_12000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_nof0_12000.bin",
|
2023-11-28 18:30:52 +03:00
|
|
|
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_nof0_12000.bin",
|
2023-11-21 17:10:43 +03:00
|
|
|
},
|
|
|
|
"8000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_nof0_8000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_nof0_8000.bin",
|
2023-11-28 18:30:52 +03:00
|
|
|
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_nof0_8000.bin",
|
2023-11-21 17:10:43 +03:00
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
const f0ModelUrl: { [modelType in VoiceChangerType]: { [inputLength in InputLengthKey]: { [sampleRate in ModelSampleRateStr]: string } } } = {
|
|
|
|
rvcv1: {
|
|
|
|
"24000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_f0_24000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_f0_24000.bin",
|
|
|
|
},
|
|
|
|
"16000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_f0_16000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_f0_16000.bin",
|
|
|
|
},
|
|
|
|
"12000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_f0_12000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_f0_12000.bin",
|
|
|
|
},
|
|
|
|
"8000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_40k_f0_8000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv1_amitaro_v1_32k_f0_8000.bin",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
rvcv2: {
|
|
|
|
"24000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_f0_24000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_f0_24000.bin",
|
2023-11-28 18:30:52 +03:00
|
|
|
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_f0_24000.bin",
|
2023-11-21 17:10:43 +03:00
|
|
|
},
|
|
|
|
"16000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_f0_16000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_f0_16000.bin",
|
2023-11-28 18:30:52 +03:00
|
|
|
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_f0_16000.bin",
|
2023-11-21 17:10:43 +03:00
|
|
|
},
|
|
|
|
"12000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_f0_12000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_f0_12000.bin",
|
2023-11-28 18:30:52 +03:00
|
|
|
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_f0_12000.bin",
|
2023-11-21 17:10:43 +03:00
|
|
|
},
|
|
|
|
"8000": {
|
|
|
|
"40k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_40k_f0_8000.bin",
|
|
|
|
"32k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_32k_f0_8000.bin",
|
2023-11-28 18:30:52 +03:00
|
|
|
"16k": "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/rvcv2_amitaro_v2_16k_f0_8000.bin",
|
2023-11-21 17:10:43 +03:00
|
|
|
},
|
|
|
|
},
|
2023-11-19 16:28:38 +03:00
|
|
|
};
|
2023-11-21 05:45:27 +03:00
|
|
|
|
2023-11-21 23:53:15 +03:00
|
|
|
export const useWebInfo = (props: UseWebInfoProps): WebInfoStateAndMethod => {
|
2023-11-23 00:20:54 +03:00
|
|
|
const initVoiceChangerType: VoiceChangerType = "rvcv2";
|
2023-11-21 23:53:15 +03:00
|
|
|
const initInputLength: InputLengthKey = "24000";
|
|
|
|
const initUseF0 = false;
|
2023-11-23 00:20:54 +03:00
|
|
|
const initSampleRate: ModelSampleRateStr = "32k";
|
2023-11-21 05:45:27 +03:00
|
|
|
|
2023-11-21 23:53:15 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
2023-11-21 05:45:27 +03:00
|
|
|
|
2023-11-21 23:53:15 +03:00
|
|
|
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];
|
|
|
|
}
|
2023-11-19 16:28:38 +03:00
|
|
|
|
2023-11-21 23:53:15 +03:00
|
|
|
const config: VoiceChangerConfig = {
|
|
|
|
config: {
|
|
|
|
voiceChangerType: voiceChangerType,
|
|
|
|
inputLength: inputLength,
|
|
|
|
baseUrl: window.location.origin,
|
|
|
|
inputSamplingRate: 48000,
|
|
|
|
outputSamplingRate: 48000,
|
|
|
|
},
|
|
|
|
modelUrl: modelUrl,
|
2023-11-22 04:06:38 +03:00
|
|
|
portrait: "https://huggingface.co/wok000/vcclient_model/resolve/main/web_model/v_01_alpha/amitaro/amitaro.png",
|
2023-11-21 23:53:15 +03:00
|
|
|
name: "あみたろ",
|
|
|
|
termOfUse: "https://huggingface.co/wok000/vcclient_model/raw/main/rvc/amitaro_contentvec_256/term_of_use.txt",
|
|
|
|
sampleRate: sampleRate,
|
2023-11-22 01:10:34 +03:00
|
|
|
useF0: useF0,
|
2023-11-21 17:10:43 +03:00
|
|
|
inputLength: inputLength,
|
2023-11-21 23:53:15 +03:00
|
|
|
progressCallback,
|
|
|
|
};
|
|
|
|
return config;
|
|
|
|
};
|
|
|
|
|
|
|
|
const [voiceChangerConfig, _setVoiceChangerConfig] = useState<VoiceChangerConfig>(generateVoiceChangerConfig(initVoiceChangerType, initSampleRate, initUseF0, initInputLength));
|
2023-11-19 16:28:38 +03:00
|
|
|
const [webModelLoadingState, setWebModelLoadingState] = useState<WebModelLoadingState>(WebModelLoadingState.none);
|
|
|
|
const [progressLoadPreprocess, setProgressLoadPreprocess] = useState<number>(0);
|
|
|
|
const [progressLoadVCModel, setProgressLoadVCModel] = useState<number>(0);
|
2023-11-21 05:45:27 +03:00
|
|
|
const [progressWarmup, setProgressWarmup] = useState<number>(0);
|
2023-11-21 17:10:43 +03:00
|
|
|
const [upkey, setUpkey] = useState<number>(0);
|
2023-11-22 01:10:34 +03:00
|
|
|
const [responseTimeInfo, setResponseTimeInfo] = useState<ResponseTimeInfo>({
|
|
|
|
responseTime: 0,
|
|
|
|
realDuration: 0,
|
|
|
|
rtf: 0,
|
|
|
|
});
|
2023-11-19 16:28:38 +03:00
|
|
|
const voiceChangerJSClient = useRef<VoiceChangerJSClient>();
|
|
|
|
|
|
|
|
const webModelslot: WebModelSlot = useMemo(() => {
|
|
|
|
return {
|
|
|
|
slotIndex: -1,
|
|
|
|
voiceChangerType: "WebModel",
|
|
|
|
name: voiceChangerConfig.name,
|
|
|
|
description: "",
|
|
|
|
credit: "",
|
|
|
|
termsOfUseUrl: voiceChangerConfig.termOfUse,
|
|
|
|
iconFile: voiceChangerConfig.portrait,
|
|
|
|
speakers: {},
|
|
|
|
defaultTune: 0,
|
|
|
|
modelType: "pyTorchRVCNono",
|
2023-11-21 23:53:15 +03:00
|
|
|
f0: voiceChangerConfig.useF0,
|
2023-11-19 16:28:38 +03:00
|
|
|
samplingRate: 0,
|
|
|
|
modelFile: "",
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
|
2023-11-21 23:53:15 +03:00
|
|
|
const setVoiceChangerConfig = (voiceChangerType: VoiceChangerType, sampleRate: ModelSampleRateStr, useF0: boolean, inputLength: InputLengthKey) => {
|
|
|
|
const config = generateVoiceChangerConfig(voiceChangerType, sampleRate, useF0, inputLength);
|
|
|
|
_setVoiceChangerConfig(config);
|
|
|
|
};
|
|
|
|
// useEffect(() => {
|
|
|
|
// setVoiceChangerConfig({ ...voiceChangerConfig, progressCallback });
|
|
|
|
// }, []);
|
2023-11-19 16:28:38 +03:00
|
|
|
|
|
|
|
const loadVoiceChanagerModel = async () => {
|
|
|
|
if (!props.clientState) {
|
|
|
|
throw new Error("[useWebInfo] clientState is null");
|
|
|
|
}
|
|
|
|
if (!props.clientState.initialized) {
|
|
|
|
console.warn("[useWebInfo] clientState is not initialized yet");
|
|
|
|
return;
|
|
|
|
}
|
2023-11-23 00:46:12 +03:00
|
|
|
if (!props.webEdition) {
|
|
|
|
console.warn("[useWebInfo] this is not web edition");
|
|
|
|
return;
|
|
|
|
}
|
2023-11-21 23:53:15 +03:00
|
|
|
console.log("loadVoiceChanagerModel1", voiceChangerConfig);
|
2023-11-19 16:28:38 +03:00
|
|
|
setWebModelLoadingState("loading");
|
|
|
|
voiceChangerJSClient.current = new VoiceChangerJSClient();
|
|
|
|
await voiceChangerJSClient.current.initialize(voiceChangerConfig.config, voiceChangerConfig.modelUrl, voiceChangerConfig.progressCallback);
|
2023-11-21 23:53:15 +03:00
|
|
|
console.log("loadVoiceChanagerModel2");
|
2023-11-19 16:28:38 +03:00
|
|
|
|
|
|
|
// worm up
|
|
|
|
setWebModelLoadingState("warmup");
|
2023-11-21 17:10:43 +03:00
|
|
|
const warmupResult = await voiceChangerJSClient.current.checkResponseTime();
|
2023-11-19 16:28:38 +03:00
|
|
|
console.log("warmup result", warmupResult);
|
|
|
|
|
|
|
|
// check time
|
|
|
|
const responseTimeInfo = await voiceChangerJSClient.current.checkResponseTime();
|
|
|
|
console.log("responseTimeInfo", responseTimeInfo);
|
2023-11-22 01:10:34 +03:00
|
|
|
setResponseTimeInfo(responseTimeInfo);
|
2023-11-19 16:28:38 +03:00
|
|
|
|
|
|
|
props.clientState?.setInternalAudioProcessCallback({
|
|
|
|
processAudio: async (data: Uint8Array) => {
|
|
|
|
const audioF32 = new Float32Array(data.buffer);
|
|
|
|
const res = await voiceChangerJSClient.current!.convert(audioF32);
|
|
|
|
const audio = new Uint8Array(res[0].buffer);
|
2023-11-21 17:10:43 +03:00
|
|
|
if (res[1]) {
|
|
|
|
console.log("RESPONSE!", res[1]);
|
2023-11-22 01:10:34 +03:00
|
|
|
setResponseTimeInfo(res[1]);
|
2023-11-21 17:10:43 +03:00
|
|
|
}
|
2023-11-19 16:28:38 +03:00
|
|
|
return audio;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
setWebModelLoadingState("ready");
|
|
|
|
};
|
2023-11-21 17:10:43 +03:00
|
|
|
useEffect(() => {
|
|
|
|
if (!voiceChangerJSClient.current) {
|
|
|
|
console.log("setupkey", voiceChangerJSClient.current);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
voiceChangerJSClient.current.setUpkey(upkey);
|
|
|
|
}, [upkey]);
|
2023-11-19 16:28:38 +03:00
|
|
|
|
2023-11-21 23:53:15 +03:00
|
|
|
useEffect(() => {
|
|
|
|
console.log("change voice ", voiceChangerConfig);
|
|
|
|
|
|
|
|
setProgressLoadPreprocess(0);
|
|
|
|
setProgressLoadVCModel(0);
|
|
|
|
setProgressWarmup(0);
|
|
|
|
|
|
|
|
loadVoiceChanagerModel();
|
|
|
|
}, [voiceChangerConfig, props.clientState?.initialized]);
|
|
|
|
|
2023-11-19 16:28:38 +03:00
|
|
|
return {
|
|
|
|
voiceChangerConfig,
|
|
|
|
webModelLoadingState,
|
|
|
|
progressLoadPreprocess,
|
|
|
|
progressLoadVCModel,
|
2023-11-21 05:45:27 +03:00
|
|
|
progressWarmup,
|
2023-11-19 16:28:38 +03:00
|
|
|
webModelslot,
|
2023-11-21 17:10:43 +03:00
|
|
|
upkey,
|
2023-11-22 01:10:34 +03:00
|
|
|
responseTimeInfo,
|
2023-11-19 16:28:38 +03:00
|
|
|
loadVoiceChanagerModel,
|
2023-11-21 17:10:43 +03:00
|
|
|
setUpkey,
|
2023-11-21 23:53:15 +03:00
|
|
|
setVoiceChangerConfig,
|
2023-11-19 16:28:38 +03:00
|
|
|
};
|
|
|
|
};
|