WIP: keep uploaded models

This commit is contained in:
wataru 2023-05-16 10:38:23 +09:00
parent ffd2894d5e
commit 44593719f5
33 changed files with 523 additions and 1829 deletions

View File

@ -71,6 +71,10 @@
"fileKind": "rvcIndex" "fileKind": "rvcIndex"
} }
}, },
{
"name": "sampleModelSelect",
"options": {}
},
{ {
"name": "defaultTuneRow2", "name": "defaultTuneRow2",
"options": {} "options": {}

View File

@ -1,10 +1 @@
<!DOCTYPE html> <!doctype html><html style="width:100%;height:100%;overflow:hidden"><head><meta charset="utf-8"/><title>Voice Changer Client Demo</title><script defer="defer" src="index.js"></script></head><body style="width:100%;height:100%;margin:0"><div id="app" style="width:100%;height:100%"></div></body></html>
<html style="width: 100%; height: 100%; overflow: hidden">
<head>
<meta charset="utf-8" />
<title>Voice Changer Client Demo</title>
<script defer src="index.js"></script></head>
<body style="width: 100%; height: 100%; margin: 0px">
<div id="app" style="width: 100%; height: 100%"></div>
</body>
</html>

File diff suppressed because one or more lines are too long

31
client/demo/dist/index.js.LICENSE.txt vendored Normal file
View File

@ -0,0 +1,31 @@
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

View File

@ -9,7 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@dannadori/voice-changer-client-js": "^1.0.126", "@dannadori/voice-changer-client-js": "^1.0.127",
"@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-brands-svg-icons": "^6.4.0", "@fortawesome/free-brands-svg-icons": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-regular-svg-icons": "^6.4.0",
@ -24,7 +24,7 @@
"@babel/preset-env": "^7.21.5", "@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6", "@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.5", "@babel/preset-typescript": "^7.21.5",
"@types/node": "^20.1.4", "@types/node": "^20.1.5",
"@types/react": "^18.2.6", "@types/react": "^18.2.6",
"@types/react-dom": "^18.2.4", "@types/react-dom": "^18.2.4",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
@ -3170,9 +3170,9 @@
} }
}, },
"node_modules/@dannadori/voice-changer-client-js": { "node_modules/@dannadori/voice-changer-client-js": {
"version": "1.0.126", "version": "1.0.127",
"resolved": "https://registry.npmjs.org/@dannadori/voice-changer-client-js/-/voice-changer-client-js-1.0.126.tgz", "resolved": "https://registry.npmjs.org/@dannadori/voice-changer-client-js/-/voice-changer-client-js-1.0.127.tgz",
"integrity": "sha512-vIXXu0rPlbd220r30SsAVduFlK2jCM2985pHQ/biVeVM7l+qXEIY+Qbr5H1Usx5jHnVCnpjULbRQKhByPeoHTA==", "integrity": "sha512-EkzqkUy8/QO3VwZAR6MZh8zgwG7Oqx4uwzmnujfqlywjqdiYggDzp5n7innJJaJE/z+cUp32dsX7l7l9s2PiGw==",
"dependencies": { "dependencies": {
"@types/readable-stream": "^2.3.15", "@types/readable-stream": "^2.3.15",
"amazon-chime-sdk-js": "^3.14.0", "amazon-chime-sdk-js": "^3.14.0",
@ -3833,9 +3833,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.1.4", "version": "20.1.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.4.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.5.tgz",
"integrity": "sha512-At4pvmIOki8yuwLtd7BNHl3CiWNbtclUbNtScGx4OHfBd4/oWoJC8KRCIxXwkdndzhxOsPXihrsOoydxBjlE9Q==" "integrity": "sha512-IvGD1CD/nego63ySR7vrAKEX3AJTcmrAN2kn+/sDNLi1Ff5kBzDeEdqWDplK+0HAEoLYej137Sk0cUU8OLOlMg=="
}, },
"node_modules/@types/prop-types": { "node_modules/@types/prop-types": {
"version": "15.7.5", "version": "15.7.5",
@ -13995,9 +13995,9 @@
} }
}, },
"@dannadori/voice-changer-client-js": { "@dannadori/voice-changer-client-js": {
"version": "1.0.126", "version": "1.0.127",
"resolved": "https://registry.npmjs.org/@dannadori/voice-changer-client-js/-/voice-changer-client-js-1.0.126.tgz", "resolved": "https://registry.npmjs.org/@dannadori/voice-changer-client-js/-/voice-changer-client-js-1.0.127.tgz",
"integrity": "sha512-vIXXu0rPlbd220r30SsAVduFlK2jCM2985pHQ/biVeVM7l+qXEIY+Qbr5H1Usx5jHnVCnpjULbRQKhByPeoHTA==", "integrity": "sha512-EkzqkUy8/QO3VwZAR6MZh8zgwG7Oqx4uwzmnujfqlywjqdiYggDzp5n7innJJaJE/z+cUp32dsX7l7l9s2PiGw==",
"requires": { "requires": {
"@types/readable-stream": "^2.3.15", "@types/readable-stream": "^2.3.15",
"amazon-chime-sdk-js": "^3.14.0", "amazon-chime-sdk-js": "^3.14.0",
@ -14542,9 +14542,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "20.1.4", "version": "20.1.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.4.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.5.tgz",
"integrity": "sha512-At4pvmIOki8yuwLtd7BNHl3CiWNbtclUbNtScGx4OHfBd4/oWoJC8KRCIxXwkdndzhxOsPXihrsOoydxBjlE9Q==" "integrity": "sha512-IvGD1CD/nego63ySR7vrAKEX3AJTcmrAN2kn+/sDNLi1Ff5kBzDeEdqWDplK+0HAEoLYej137Sk0cUU8OLOlMg=="
}, },
"@types/prop-types": { "@types/prop-types": {
"version": "15.7.5", "version": "15.7.5",

View File

@ -24,7 +24,7 @@
"@babel/preset-env": "^7.21.5", "@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6", "@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.5", "@babel/preset-typescript": "^7.21.5",
"@types/node": "^20.1.4", "@types/node": "^20.1.5",
"@types/react": "^18.2.6", "@types/react": "^18.2.6",
"@types/react-dom": "^18.2.4", "@types/react-dom": "^18.2.4",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
@ -52,7 +52,7 @@
"webpack-dev-server": "^4.15.0" "webpack-dev-server": "^4.15.0"
}, },
"dependencies": { "dependencies": {
"@dannadori/voice-changer-client-js": "^1.0.126", "@dannadori/voice-changer-client-js": "^1.0.127",
"@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-brands-svg-icons": "^6.4.0", "@fortawesome/free-brands-svg-icons": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-regular-svg-icons": "^6.4.0",

View File

@ -71,6 +71,10 @@
"fileKind": "rvcIndex" "fileKind": "rvcIndex"
} }
}, },
{
"name": "sampleModelSelect",
"options": {}
},
{ {
"name": "defaultTuneRow2", "name": "defaultTuneRow2",
"options": {} "options": {}

View File

@ -22,6 +22,21 @@ export type AppGuiDemoSetting = {
} }
} }
// export type AppGuiDemoSetting2 = {
// type: "demo",
// id: ClientType,
// front: GuiSectionSetting[],
// dialogs: {
// "license": { title: string, auther: string, contact: string, url: string, license: string }[]
// }
// }
// export type GuiSectionSetting = {
// "title": string,
// "components": GuiComponentSetting[]
// }
export type GuiComponentSetting = { export type GuiComponentSetting = {
"name": string, "name": string,
"options": any "options": any

View File

@ -58,6 +58,7 @@ type GuiStateAndMethod = {
modelSlotNum: number modelSlotNum: number
setModelSlotNum: (val: number) => void setModelSlotNum: (val: number) => void
} }
const GuiStateContext = React.createContext<GuiStateAndMethod | null>(null); const GuiStateContext = React.createContext<GuiStateAndMethod | null>(null);
@ -85,7 +86,6 @@ export const GuiStateProvider = ({ children }: Props) => {
const [fileInputEchoback, setFileInputEchoback] = useState<boolean>(false)//最初のmuteが有効になるように。undefined <-- ??? falseしておけばよさそう。undefinedだとwarningがでる。 const [fileInputEchoback, setFileInputEchoback] = useState<boolean>(false)//最初のmuteが有効になるように。undefined <-- ??? falseしておけばよさそう。undefinedだとwarningがでる。
const [audioOutputForAnalyzer, setAudioOutputForAnalyzer] = useState<string>("default") const [audioOutputForAnalyzer, setAudioOutputForAnalyzer] = useState<string>("default")
const reloadDeviceInfo = async () => { const reloadDeviceInfo = async () => {
try { try {
const ms = await navigator.mediaDevices.getUserMedia({ video: false, audio: true }); const ms = await navigator.mediaDevices.getUserMedia({ video: false, audio: true });
@ -208,7 +208,8 @@ export const GuiStateProvider = ({ children }: Props) => {
setAudioOutputForAnalyzer, setAudioOutputForAnalyzer,
modelSlotNum, modelSlotNum,
setModelSlotNum setModelSlotNum,
}; };
return <GuiStateContext.Provider value={providerValue}>{children}</GuiStateContext.Provider>; return <GuiStateContext.Provider value={providerValue}>{children}</GuiStateContext.Provider>;
}; };

View File

@ -54,6 +54,7 @@ import { DiffEnablerRow, DiffEnablerRowProps } from "./components/611_DiffEnable
import { DiffSettingRow, DiffSettingRowProps } from "./components/612_DiffSettingRow" import { DiffSettingRow, DiffSettingRowProps } from "./components/612_DiffSettingRow"
import { DiffMethodRow, DiffMethodRowProps } from "./components/613_DiffMethodRow" import { DiffMethodRow, DiffMethodRowProps } from "./components/613_DiffMethodRow"
import { ServerOpertationRow, ServerOpertationRowProps } from "./components/207_ServerOpertationRow" import { ServerOpertationRow, ServerOpertationRowProps } from "./components/207_ServerOpertationRow"
import { SampleModelSelectRow, SampleModelSelectRowProps } from "./components/301-j_SampleModelSelectRow"
export const catalog: { [key: string]: (props: any) => JSX.Element } = {} export const catalog: { [key: string]: (props: any) => JSX.Element } = {}
@ -92,6 +93,8 @@ const initialize = () => {
addToCatalog("correspondenceSelectRow2", (props: CorrespondenceSelectRow2Props) => { return <CorrespondenceSelectRow2 {...props} /> }) addToCatalog("correspondenceSelectRow2", (props: CorrespondenceSelectRow2Props) => { return <CorrespondenceSelectRow2 {...props} /> })
addToCatalog("modelSlotRow2", (props: ModelSlotRow2Props) => { return <ModelSlotRow2 {...props} /> }) addToCatalog("modelSlotRow2", (props: ModelSlotRow2Props) => { return <ModelSlotRow2 {...props} /> })
addToCatalog("defaultTuneRow2", (props: DefaultTuneRow2Props) => { return <DefaultTuneRow2 {...props} /> }) addToCatalog("defaultTuneRow2", (props: DefaultTuneRow2Props) => { return <DefaultTuneRow2 {...props} /> })
addToCatalog("sampleModelSelect", (props: SampleModelSelectRowProps) => { return <SampleModelSelectRow {...props} /> })

View File

@ -10,7 +10,6 @@ import { SpeakerSetting } from "./600_SpeakerSetting";
import { ConverterSetting } from "./700_ConverterSetting"; import { ConverterSetting } from "./700_ConverterSetting";
import { AdvancedSetting } from "./800_AdvancedSetting"; import { AdvancedSetting } from "./800_AdvancedSetting";
import { Lab } from "./a00_Lab"; import { Lab } from "./a00_Lab";
import { useAppRoot } from "../../001_provider/001_AppRootProvider";
export const Demo = () => { export const Demo = () => {

View File

@ -37,7 +37,7 @@ export const ModelSwitchRow = (_props: ModelSwitchRowProps) => {
const tuning = `tune:${x.defaultTrans}` const tuning = `tune:${x.defaultTrans}`
const useIndex = x.indexFile != null && x.featureFile != null ? `index:true` : `index:false` const useIndex = x.indexFile != null && x.featureFile != null ? `index:true` : `index:false`
const subMetadata = `(${tuning},${useIndex})` const subMetadata = `(${tuning},${useIndex})`
const displayName = `${metadata} ${filename} ${subMetadata}` const displayName = `${metadata} ${x.name || filename} ${subMetadata}`
return ( return (

View File

@ -38,8 +38,14 @@ export const CommonFileSelectRow = (props: CommonFileSelectRowProps) => {
const guiState = useGuiState() const guiState = useGuiState()
const commonFileSelectRow = useMemo(() => { const commonFileSelectRow = useMemo(() => {
const slot = guiState.modelSlotNum const slot = guiState.modelSlotNum
if (!appState.serverSetting.fileUploadSettings[slot]) {
return <></>
}
if (appState.serverSetting.fileUploadSettings[slot].isSampleMode == true) {
return <></>
}
const getTargetModelData = () => { const getTargetModelData = () => {
const targetSlot = appState.serverSetting.fileUploadSettings[slot] const targetSlot = appState.serverSetting.fileUploadSettings[slot]
@ -66,12 +72,14 @@ export const CommonFileSelectRow = (props: CommonFileSelectRowProps) => {
return return
} }
appState.serverSetting.fileUploadSettings[slot][props.fileKind]! = { file: file } appState.serverSetting.fileUploadSettings[slot][props.fileKind]! = { file: file }
appState.serverSetting.fileUploadSettings[slot].sampleId = null
appState.serverSetting.setFileUploadSetting(slot, { appState.serverSetting.setFileUploadSetting(slot, {
...appState.serverSetting.fileUploadSettings[slot] ...appState.serverSetting.fileUploadSettings[slot]
}) })
} }
const onFileClearClicked = () => { const onFileClearClicked = () => {
appState.serverSetting.fileUploadSettings[slot][props.fileKind] = null appState.serverSetting.fileUploadSettings[slot][props.fileKind] = null
appState.serverSetting.fileUploadSettings[slot].sampleId = null
appState.serverSetting.setFileUploadSetting(slot, { appState.serverSetting.setFileUploadSetting(slot, {
...appState.serverSetting.fileUploadSettings[slot], ...appState.serverSetting.fileUploadSettings[slot],
}) })

View File

@ -11,15 +11,20 @@ export const ModelUploadButtonRow2 = (_props: ModelUploadButtonRow2Props) => {
const guiState = useGuiState() const guiState = useGuiState()
const modelUploadButtonRow = useMemo(() => { const modelUploadButtonRow = useMemo(() => {
const slot = guiState.modelSlotNum const slot = guiState.modelSlotNum
if (!appState.serverSetting.fileUploadSettings[slot]) {
return <></>
}
const onModelUploadClicked = async () => { const onModelUploadClicked = async () => {
appState.serverSetting.loadModel(slot) appState.serverSetting.loadModel(slot)
} }
const buttonText = appState.serverSetting.fileUploadSettings[slot].isSampleMode ? "select" : "upload"
const uploadButtonClassName = appState.serverSetting.isUploading ? "body-button-disabled" : "body-button" const uploadButtonClassName = appState.serverSetting.isUploading ? "body-button-disabled" : "body-button"
const uploadButtonAction = appState.serverSetting.isUploading ? () => { } : onModelUploadClicked const uploadButtonAction = appState.serverSetting.isUploading ? () => { } : onModelUploadClicked
const uploadButtonLabel = appState.serverSetting.isUploading ? "wait..." : "upload" const uploadButtonLabel = appState.serverSetting.isUploading ? "wait..." : buttonText
const uploadingStatus = appState.serverSetting.isUploading ? const uploadingStatus = appState.serverSetting.isUploading ?
appState.serverSetting.uploadProgress == 0 ? `loading model...(wait about 20sec)` : `uploading.... ${appState.serverSetting.uploadProgress.toFixed(1)}%` : "" appState.serverSetting.uploadProgress == 0 ? `loading model...(wait about 20sec)` : `processing.... ${appState.serverSetting.uploadProgress.toFixed(1)}%` : ""
const uploadedText = appState.serverSetting.fileUploadSettings[slot] == undefined ? "" : appState.serverSetting.fileUploadSettings[slot].uploaded ? "" : "not uploaded" const uploadedText = appState.serverSetting.fileUploadSettings[slot] == undefined ? "" : appState.serverSetting.fileUploadSettings[slot].uploaded ? "" : "not uploaded"

View File

@ -1,29 +1,57 @@
import { MAX_MODEL_SLOT_NUM } from "@dannadori/voice-changer-client-js" import { MAX_MODEL_SLOT_NUM } from "@dannadori/voice-changer-client-js"
import React, { useMemo } from "react" import React, { useMemo } from "react"
import { useGuiState } from "../001_GuiStateProvider" import { useGuiState } from "../001_GuiStateProvider"
import { useAppState } from "../../../001_provider/001_AppStateProvider"
export type ModelSlotRow2Props = {} export type ModelSlotRow2Props = {}
export const ModelSlotRow2 = (_prop: ModelSlotRow2Props) => { export const ModelSlotRow2 = (_prop: ModelSlotRow2Props) => {
const guiState = useGuiState() const guiState = useGuiState()
const appState = useAppState()
const modelSlotRow = useMemo(() => { const modelSlotRow = useMemo(() => {
const slot = guiState.modelSlotNum const slot = guiState.modelSlotNum
if (!appState.serverSetting.fileUploadSettings[slot]) {
return <></>
}
const onModelSlotChanged = (val: number) => { const onModelSlotChanged = (val: number) => {
guiState.setModelSlotNum(val) guiState.setModelSlotNum(val)
} }
const onModeChanged = (val: boolean) => {
appState.serverSetting.fileUploadSettings[slot].isSampleMode = val
appState.serverSetting.setFileUploadSetting(slot, {
...appState.serverSetting.fileUploadSettings[slot],
})
}
const isSampleMode = appState.serverSetting.fileUploadSettings[slot].isSampleMode
return ( return (
<div className="body-row split-3-7 left-padding-1 guided"> <div className="body-row split-3-3-4 left-padding-1 guided">
<div className="body-item-title left-padding-2">Model Slot</div> <div className="body-item-title left-padding-2">Model Slot</div>
<div className="body-input-container"> <div className="body-input-container">
<select value={slot} onChange={(e) => { onModelSlotChanged(Number(e.target.value)) }}> <select value={slot} onChange={(e) => { onModelSlotChanged(Number(e.target.value)) }}>
{Array(MAX_MODEL_SLOT_NUM).fill(0).map((_x, index) => { {Array(MAX_MODEL_SLOT_NUM).fill(0).map((_x, index) => {
return <option key={index} value={index} >{index}</option> return <option key={index} value={index} >{index}</option>
})} })}
</select> </select>
<div className="left-padding-1">
<input className="left-padding-1" type="radio" id="from-file" name="sample-mode" checked={isSampleMode == false} onChange={() => { onModeChanged(false) }} />
<label className="left-padding-05" htmlFor="from-file">file</label>
</div>
<div className="left-padding-1">
<input className="left-padding-1" type="radio" id="from-net" name="sample-mode" checked={isSampleMode == true} onChange={() => { onModeChanged(true) }} />
<label className="left-padding-05" htmlFor="from-net">from net</label>
</div>
</div> </div>
<div></div>
</div> </div>
) )
}, [guiState.modelSlotNum]) }, [guiState.modelSlotNum, appState.serverSetting.fileUploadSettings])
return modelSlotRow return modelSlotRow
} }

View File

@ -17,7 +17,7 @@ export const DefaultTuneRow2 = (_props: DefaultTuneRow2Props) => {
const onDefaultTuneChanged = (val: number) => { const onDefaultTuneChanged = (val: number) => {
appState.serverSetting.setFileUploadSetting(slot, { appState.serverSetting.setFileUploadSetting(slot, {
...appState.serverSetting.fileUploadSettings[slot], ...appState.serverSetting.fileUploadSettings[slot],
defaultTune: val defaultTune: val,
}) })
} }

View File

@ -0,0 +1,53 @@
import React, { useMemo } from "react"
import { useAppState } from "../../../001_provider/001_AppStateProvider"
import { useGuiState } from "../001_GuiStateProvider"
export type SampleModelSelectRowProps = {}
export const SampleModelSelectRow = (_props: SampleModelSelectRowProps) => {
const appState = useAppState()
const guiState = useGuiState()
const sampleModelSelectRow = useMemo(() => {
const slot = guiState.modelSlotNum
const fileUploadSetting = appState.serverSetting.fileUploadSettings[slot]
if (!fileUploadSetting) {
return <></>
}
if (fileUploadSetting.isSampleMode == false) {
return <></>
}
const options = (
appState.serverSetting.serverSetting.sampleModels.map(x => {
return <option key={x.id} value={x.id}>{x.name}</option>
})
)
const selectedSample = appState.serverSetting.serverSetting.sampleModels.find(x => { return x.id == fileUploadSetting.sampleId })
const creditText = selectedSample ? `credit:${selectedSample.credit}` : ""
const termOfUseLink = selectedSample ? <a href={selectedSample.termOfUseUrl} target="_blank" rel="noopener noreferrer" className="body-item-text-small">[term of use]</a> : <></>
const onModelSelected = (val: string) => {
appState.serverSetting.setFileUploadSetting(slot, {
...appState.serverSetting.fileUploadSettings[slot], sampleId: val
})
}
return (
<div className="body-row split-3-3-4 left-padding-1 guided">
<div className="body-item-title left-padding-2 ">Select Model</div>
<div>
<select value={fileUploadSetting.sampleId || ""} onChange={(e) => { onModelSelected(e.target.value) }}>
<option disabled value={""}> -- select model -- </option>
{options}
</select>
</div>
<div className="body-item-text">
{creditText}{termOfUseLink}
</div>
</div>
)
}, [appState.serverSetting.fileUploadSettings, guiState.modelSlotNum])
return sampleModelSelectRow
}

View File

@ -494,6 +494,9 @@ body {
.underline { .underline {
border-bottom: 3px solid #333; border-bottom: 3px solid #333;
} }
.left-padding-05 {
padding-left: 0.5rem;
}
.left-padding-1 { .left-padding-1 {
padding-left: 1rem; padding-left: 1rem;
} }

View File

@ -1,12 +1,12 @@
{ {
"name": "@dannadori/voice-changer-client-js", "name": "@dannadori/voice-changer-client-js",
"version": "1.0.126", "version": "1.0.127",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@dannadori/voice-changer-client-js", "name": "@dannadori/voice-changer-client-js",
"version": "1.0.126", "version": "1.0.127",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@types/readable-stream": "^2.3.15", "@types/readable-stream": "^2.3.15",
@ -19,7 +19,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/audioworklet": "^0.0.46", "@types/audioworklet": "^0.0.46",
"@types/node": "^20.1.4", "@types/node": "^20.1.5",
"@types/react": "18.2.6", "@types/react": "18.2.6",
"@types/react-dom": "18.2.4", "@types/react-dom": "18.2.4",
"eslint": "^8.40.0", "eslint": "^8.40.0",
@ -1878,9 +1878,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.1.4", "version": "20.1.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.4.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.5.tgz",
"integrity": "sha512-At4pvmIOki8yuwLtd7BNHl3CiWNbtclUbNtScGx4OHfBd4/oWoJC8KRCIxXwkdndzhxOsPXihrsOoydxBjlE9Q==" "integrity": "sha512-IvGD1CD/nego63ySR7vrAKEX3AJTcmrAN2kn+/sDNLi1Ff5kBzDeEdqWDplK+0HAEoLYej137Sk0cUU8OLOlMg=="
}, },
"node_modules/@types/prop-types": { "node_modules/@types/prop-types": {
"version": "15.7.5", "version": "15.7.5",
@ -9870,9 +9870,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "20.1.4", "version": "20.1.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.4.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.5.tgz",
"integrity": "sha512-At4pvmIOki8yuwLtd7BNHl3CiWNbtclUbNtScGx4OHfBd4/oWoJC8KRCIxXwkdndzhxOsPXihrsOoydxBjlE9Q==" "integrity": "sha512-IvGD1CD/nego63ySR7vrAKEX3AJTcmrAN2kn+/sDNLi1Ff5kBzDeEdqWDplK+0HAEoLYej137Sk0cUU8OLOlMg=="
}, },
"@types/prop-types": { "@types/prop-types": {
"version": "15.7.5", "version": "15.7.5",

View File

@ -1,6 +1,6 @@
{ {
"name": "@dannadori/voice-changer-client-js", "name": "@dannadori/voice-changer-client-js",
"version": "1.0.126", "version": "1.0.127",
"description": "", "description": "",
"main": "dist/index.js", "main": "dist/index.js",
"directories": { "directories": {
@ -27,7 +27,7 @@
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@types/audioworklet": "^0.0.46", "@types/audioworklet": "^0.0.46",
"@types/node": "^20.1.4", "@types/node": "^20.1.5",
"@types/react": "18.2.6", "@types/react": "18.2.6",
"@types/react-dom": "18.2.4", "@types/react-dom": "18.2.4",
"eslint": "^8.40.0", "eslint": "^8.40.0",

View File

@ -192,6 +192,12 @@ type ModelSlot = {
f0: boolean, f0: boolean,
samplingRate: number samplingRate: number
deprecated: boolean deprecated: boolean
name: string,
description: string,
credit: string,
termsOfUseUrl: string,
} }
type ServerAudioDevice = { type ServerAudioDevice = {
@ -210,6 +216,7 @@ export type ServerInfo = VoiceChangerServerSetting & {
modelSlots: ModelSlot[] modelSlots: ModelSlot[]
serverAudioInputDevices: ServerAudioDevice[] serverAudioInputDevices: ServerAudioDevice[]
serverAudioOutputDevices: ServerAudioDevice[] serverAudioOutputDevices: ServerAudioDevice[]
sampleModels: RVCSampleModel[]
} }
@ -217,6 +224,17 @@ export type ServerInfoSoVitsSVC = ServerInfo & {
speakers: { [key: string]: number } speakers: { [key: string]: number }
} }
export type RVCSampleModel = {
id: string
name: string
modelUrl: string
indexUrl: string
featureUrl: string
termOfUseUrl: string
credit: string
description: string
}
export const DefaultServerSetting: ServerInfo = { export const DefaultServerSetting: ServerInfo = {
// VC Common // VC Common
inputSampleRate: 48000, inputSampleRate: 48000,
@ -260,6 +278,7 @@ export const DefaultServerSetting: ServerInfo = {
modelSamplingRate: 48000, modelSamplingRate: 48000,
silenceFront: 1, silenceFront: 1,
modelSlotIndex: 0, modelSlotIndex: 0,
sampleModels: [],
useEnhancer: 0, useEnhancer: 0,
useDiff: 1, useDiff: 1,

View File

@ -31,6 +31,9 @@ export type FileUploadSetting = {
rvcFeature: ModelData | null rvcFeature: ModelData | null
rvcIndex: ModelData | null rvcIndex: ModelData | null
isSampleMode: boolean
sampleId: string | null
ddspSvcModel: ModelData | null ddspSvcModel: ModelData | null
ddspSvcModelConfig: ModelData | null ddspSvcModelConfig: ModelData | null
ddspSvcDiffusion: ModelData | null ddspSvcDiffusion: ModelData | null
@ -59,6 +62,9 @@ const InitialFileUploadSetting: FileUploadSetting = {
rvcFeature: null, rvcFeature: null,
rvcIndex: null, rvcIndex: null,
isSampleMode: false,
sampleId: null,
ddspSvcModel: null, ddspSvcModel: null,
ddspSvcModelConfig: null, ddspSvcModelConfig: null,
ddspSvcDiffusion: null, ddspSvcDiffusion: null,
@ -226,73 +232,84 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
const loadModel = useMemo(() => { const loadModel = useMemo(() => {
return async (slot: number) => { return async (slot: number) => {
if (props.clientType == "MMVCv13") { const fileUploadSetting = fileUploadSettings[slot]
if (!fileUploadSettings[slot].mmvcv13Config) {
alert("Configファイルを指定する必要があります。") if (fileUploadSetting.isSampleMode == false) {
if (props.clientType == "MMVCv13") {
if (!fileUploadSetting.mmvcv13Config) {
alert("Configファイルを指定する必要があります。")
return
}
if (!fileUploadSetting.mmvcv13Model) {
alert("モデルファイルを指定する必要があります。")
return
}
} else if (props.clientType == "MMVCv15") {
if (!fileUploadSetting.mmvcv15Config) {
alert("Configファイルを指定する必要があります。")
return
}
if (!fileUploadSetting.mmvcv15Model) {
alert("モデルファイルを指定する必要があります。")
return
}
} else if (props.clientType == "so-vits-svc-40") {
if (!fileUploadSetting.soVitsSvc40Config) {
alert("Configファイルを指定する必要があります。")
return
}
if (!fileUploadSetting.soVitsSvc40Model) {
alert("モデルファイルを指定する必要があります。")
return
}
} else if (props.clientType == "so-vits-svc-40v2") {
if (!fileUploadSetting.soVitsSvc40v2Config) {
alert("Configファイルを指定する必要があります。")
return
}
if (!fileUploadSetting.soVitsSvc40v2Model) {
alert("モデルファイルを指定する必要があります。")
return
}
} else if (props.clientType == "RVC") {
if (!fileUploadSetting.rvcModel) {
alert("モデルファイルを指定する必要があります。")
return
}
} else if (props.clientType == "DDSP-SVC") {
if (!fileUploadSetting.ddspSvcModel) {
alert("DDSPモデルを指定する必要があります。")
return
}
if (!fileUploadSetting.ddspSvcModelConfig) {
alert("DDSP Configファイルを指定する必要があります。")
return
}
if (!fileUploadSetting.ddspSvcDiffusion) {
alert("Diffusionモデルを指定する必要があります。")
return
}
if (!fileUploadSetting.ddspSvcDiffusionConfig) {
alert("Diffusion Configファイルを指定する必要があります。")
return
}
} else {
}
} else {//Sampleモード
if (!fileUploadSetting.sampleId) {
alert("Sample IDを指定する必要があります。")
return return
} }
if (!fileUploadSettings[slot].mmvcv13Model) {
alert("モデルファイルを指定する必要があります。")
return
}
} else if (props.clientType == "MMVCv15") {
if (!fileUploadSettings[slot].mmvcv15Config) {
alert("Configファイルを指定する必要があります。")
return
}
if (!fileUploadSettings[slot].mmvcv15Model) {
alert("モデルファイルを指定する必要があります。")
return
}
} else if (props.clientType == "so-vits-svc-40") {
if (!fileUploadSettings[slot].soVitsSvc40Config) {
alert("Configファイルを指定する必要があります。")
return
}
if (!fileUploadSettings[slot].soVitsSvc40Model) {
alert("モデルファイルを指定する必要があります。")
return
}
} else if (props.clientType == "so-vits-svc-40v2") {
if (!fileUploadSettings[slot].soVitsSvc40v2Config) {
alert("Configファイルを指定する必要があります。")
return
}
if (!fileUploadSettings[slot].soVitsSvc40v2Model) {
alert("モデルファイルを指定する必要があります。")
return
}
} else if (props.clientType == "RVC") {
if (!fileUploadSettings[slot].rvcModel) {
alert("モデルファイルを指定する必要があります。")
return
}
} else if (props.clientType == "DDSP-SVC") {
if (!fileUploadSettings[slot].ddspSvcModel) {
alert("DDSPモデルを指定する必要があります。")
return
}
if (!fileUploadSettings[slot].ddspSvcModelConfig) {
alert("DDSP Configファイルを指定する必要があります。")
return
}
if (!fileUploadSettings[slot].ddspSvcDiffusion) {
alert("Diffusionモデルを指定する必要があります。")
return
}
if (!fileUploadSettings[slot].ddspSvcDiffusionConfig) {
alert("Diffusion Configファイルを指定する必要があります。")
return
}
} else {
} }
if (!props.voiceChangerClient) return if (!props.voiceChangerClient) return
setUploadProgress(0) setUploadProgress(0)
setIsUploading(true) setIsUploading(true)
const fileUploadSetting = fileUploadSettings[slot]
// MMVCv13 // MMVCv13
const normalModels = [ const normalModels = [
@ -317,12 +334,14 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
normalModels[i].filename = await normalModels[i].file!.name normalModels[i].filename = await normalModels[i].file!.name
} }
} }
for (let i = 0; i < normalModels.length; i++) { if (fileUploadSetting.isSampleMode == false) {
const progRate = 1 / normalModels.length for (let i = 0; i < normalModels.length; i++) {
const progOffset = 100 * i * progRate const progRate = 1 / normalModels.length
await _uploadFile(normalModels[i], (progress: number, _end: boolean) => { const progOffset = 100 * i * progRate
setUploadProgress(progress * progRate + progOffset) await _uploadFile(normalModels[i], (progress: number, _end: boolean) => {
}) setUploadProgress(progress * progRate + progOffset)
})
}
} }
// DDSP-SVC // DDSP-SVC
@ -333,19 +352,22 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
ddspSvcModels[i].filename = await ddspSvcModels[i].file!.name ddspSvcModels[i].filename = await ddspSvcModels[i].file!.name
} }
} }
for (let i = 0; i < ddspSvcModels.length; i++) { if (fileUploadSetting.isSampleMode == false) {
const progRate = 1 / ddspSvcModels.length for (let i = 0; i < ddspSvcModels.length; i++) {
const progOffset = 100 * i * progRate const progRate = 1 / ddspSvcModels.length
const dir = i == 0 || i == 1 ? "ddsp_mod/" : "ddsp_diff/" const progOffset = 100 * i * progRate
await _uploadFile(ddspSvcModels[i], (progress: number, _end: boolean) => { const dir = i == 0 || i == 1 ? "ddsp_mod/" : "ddsp_diff/"
setUploadProgress(progress * progRate + progOffset) await _uploadFile(ddspSvcModels[i], (progress: number, _end: boolean) => {
}, dir) setUploadProgress(progress * progRate + progOffset)
}, dir)
}
} }
// const configFileName = fileUploadSetting.configFile?.filename || "-" // const configFileName = fileUploadSetting.configFile?.filename || "-"
const params = JSON.stringify({ const params = JSON.stringify({
trans: fileUploadSetting.defaultTune || 0, trans: fileUploadSetting.defaultTune || 0,
files: { sampleId: fileUploadSetting.isSampleMode ? fileUploadSetting.sampleId || "" : "",
files: fileUploadSetting.isSampleMode ? {} : {
mmvcv13Config: fileUploadSetting.mmvcv13Config?.filename || "", mmvcv13Config: fileUploadSetting.mmvcv13Config?.filename || "",
mmvcv13Model: fileUploadSetting.mmvcv13Model?.filename || "", mmvcv13Model: fileUploadSetting.mmvcv13Model?.filename || "",
mmvcv15Config: fileUploadSetting.mmvcv15Config?.filename || "", mmvcv15Config: fileUploadSetting.mmvcv15Config?.filename || "",
@ -360,32 +382,35 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
rvcIndex: fileUploadSetting.rvcIndex?.filename || "", rvcIndex: fileUploadSetting.rvcIndex?.filename || "",
rvcFeature: fileUploadSetting.rvcFeature?.filename || "", rvcFeature: fileUploadSetting.rvcFeature?.filename || "",
ddspSvcModel: fileUploadSetting.ddspSvcModel?.filename ? "ddsp_mod/" + fileUploadSetting.ddspSvcModel?.filename : "", ddspSvcModel: fileUploadSetting.ddspSvcModel?.filename ? "ddsp_mod/" + fileUploadSetting.ddspSvcModel?.filename : "",
ddspSvcModelConfig: fileUploadSetting.ddspSvcModelConfig?.filename ? "ddsp_mod/" + fileUploadSetting.ddspSvcModelConfig?.filename : "", ddspSvcModelConfig: fileUploadSetting.ddspSvcModelConfig?.filename ? "ddsp_mod/" + fileUploadSetting.ddspSvcModelConfig?.filename : "",
ddspSvcDiffusion: fileUploadSetting.ddspSvcDiffusion?.filename ? "ddsp_diff/" + fileUploadSetting.ddspSvcDiffusion?.filename : "", ddspSvcDiffusion: fileUploadSetting.ddspSvcDiffusion?.filename ? "ddsp_diff/" + fileUploadSetting.ddspSvcDiffusion?.filename : "",
ddspSvcDiffusionConfig: fileUploadSetting.ddspSvcDiffusionConfig?.filename ? "ddsp_diff/" + fileUploadSetting.ddspSvcDiffusionConfig.filename : "", ddspSvcDiffusionConfig: fileUploadSetting.ddspSvcDiffusionConfig?.filename ? "ddsp_diff/" + fileUploadSetting.ddspSvcDiffusionConfig.filename : "",
} }
}) })
if (fileUploadSetting.isHalf == undefined) { if (fileUploadSetting.isHalf == undefined) {
fileUploadSetting.isHalf = false fileUploadSetting.isHalf = false
} }
console.log("PARAMS:", params)
const loadPromise = props.voiceChangerClient.loadModel( const loadPromise = props.voiceChangerClient.loadModel(
slot, slot,
fileUploadSetting.isHalf, fileUploadSetting.isHalf,
params, params,
) )
// サーバでロード中にキャッシュにセーブ // サーバでロード中にキャッシュにセーブ
storeToCache(slot, fileUploadSetting) storeToCache(slot, fileUploadSetting)
await loadPromise await loadPromise
fileUploadSetting.uploaded = true fileUploadSetting.uploaded = true
fileUploadSettings[slot] = fileUploadSetting fileUploadSettings[slot] = fileUploadSetting
setFileUploadSettings([...fileUploadSettings]) setFileUploadSettings([...fileUploadSettings])
setUploadProgress(0) setUploadProgress(0)
setIsUploading(false) setIsUploading(false)
reloadServerInfo() reloadServerInfo()
@ -420,6 +445,9 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
ddspSvcModelConfig: fileUploadSetting.ddspSvcModelConfig ? { data: fileUploadSetting.ddspSvcModelConfig.data, filename: fileUploadSetting.ddspSvcModelConfig.filename } : null, ddspSvcModelConfig: fileUploadSetting.ddspSvcModelConfig ? { data: fileUploadSetting.ddspSvcModelConfig.data, filename: fileUploadSetting.ddspSvcModelConfig.filename } : null,
ddspSvcDiffusion: fileUploadSetting.ddspSvcDiffusion ? { data: fileUploadSetting.ddspSvcDiffusion.data, filename: fileUploadSetting.ddspSvcDiffusion.filename } : null, ddspSvcDiffusion: fileUploadSetting.ddspSvcDiffusion ? { data: fileUploadSetting.ddspSvcDiffusion.data, filename: fileUploadSetting.ddspSvcDiffusion.filename } : null,
ddspSvcDiffusionConfig: fileUploadSetting.ddspSvcDiffusionConfig ? { data: fileUploadSetting.ddspSvcDiffusionConfig.data, filename: fileUploadSetting.ddspSvcDiffusionConfig.filename } : null, ddspSvcDiffusionConfig: fileUploadSetting.ddspSvcDiffusionConfig ? { data: fileUploadSetting.ddspSvcDiffusionConfig.data, filename: fileUploadSetting.ddspSvcDiffusionConfig.filename } : null,
isSampleMode: fileUploadSetting.isSampleMode,
sampleId: fileUploadSetting.sampleId,
} }
setItem(`${INDEXEDDB_KEY_MODEL_DATA}_${slot}`, saveData) setItem(`${INDEXEDDB_KEY_MODEL_DATA}_${slot}`, saveData)
} catch (e) { } catch (e) {

View File

@ -17,7 +17,12 @@ from mods.ssl import create_self_signed_cert
from voice_changer.VoiceChangerManager import VoiceChangerManager from voice_changer.VoiceChangerManager import VoiceChangerManager
from sio.MMVC_SocketIOApp import MMVC_SocketIOApp from sio.MMVC_SocketIOApp import MMVC_SocketIOApp
from restapi.MMVC_Rest import MMVC_Rest from restapi.MMVC_Rest import MMVC_Rest
from const import NATIVE_CLIENT_FILE_MAC, NATIVE_CLIENT_FILE_WIN, SSL_KEY_DIR from const import (
NATIVE_CLIENT_FILE_MAC,
NATIVE_CLIENT_FILE_WIN,
SAMPLES_JSON,
SSL_KEY_DIR,
)
import subprocess import subprocess
import multiprocessing as mp import multiprocessing as mp
from misc.log_control import setup_loggers from misc.log_control import setup_loggers
@ -41,6 +46,8 @@ def setupArgParser():
default=True, default=True,
help="generate self-signed certificate", help="generate self-signed certificate",
) )
parser.add_argument("--samples", type=str, help="path to samples")
parser.add_argument("--model_dir", type=str, help="path to model files") parser.add_argument("--model_dir", type=str, help="path to model files")
parser.add_argument( parser.add_argument(
@ -214,6 +221,7 @@ if __name__ == "MMVCServerSIO":
mp.freeze_support() mp.freeze_support()
voiceChangerParams = VoiceChangerParams( voiceChangerParams = VoiceChangerParams(
model_dir=args.model_dir, model_dir=args.model_dir,
samples=args.samples,
content_vec_500=args.content_vec_500, content_vec_500=args.content_vec_500,
content_vec_500_onnx=args.content_vec_500_onnx, content_vec_500_onnx=args.content_vec_500_onnx,
content_vec_500_onnx_on=args.content_vec_500_onnx_on, content_vec_500_onnx_on=args.content_vec_500_onnx_on,
@ -223,6 +231,11 @@ if __name__ == "MMVCServerSIO":
nsf_hifigan=args.nsf_hifigan, nsf_hifigan=args.nsf_hifigan,
) )
try:
download({"url": SAMPLES_JSON, "saveTo": args.samples, "position": 0})
except Exception as e:
print("[Voice Changer] loading sample failed", e)
if ( if (
os.path.exists(voiceChangerParams.hubert_base) is False os.path.exists(voiceChangerParams.hubert_base) is False
or os.path.exists(voiceChangerParams.hubert_base_jp) is False or os.path.exists(voiceChangerParams.hubert_base_jp) is False
@ -244,6 +257,7 @@ if __name__ == "__main__":
printMessage("Voice Changerを起動しています。", level=2) printMessage("Voice Changerを起動しています。", level=2)
downloadWeight() downloadWeight()
os.makedirs(args.model_dir, exist_ok=True)
PORT = args.p PORT = args.p

36
server/ModelSample.py Normal file
View File

@ -0,0 +1,36 @@
from dataclasses import dataclass
import json
from const import ModelType
@dataclass
class RVCModelSample:
id: str = ""
name: str = ""
modelUrl: str = ""
indexUrl: str = ""
featureUrl: str = ""
termOfUseUrl: str = ""
credit: str = ""
description: str = ""
def getModelSamples(jsonPath: str, modelType: ModelType):
try:
with open(jsonPath, "r", encoding="utf-8") as f:
jsonDict = json.load(f)
modelList = jsonDict[modelType]
if modelType == "RVC":
samples: list[RVCModelSample] = []
for s in modelList:
modelSample = RVCModelSample(**s)
samples.append(modelSample)
return samples
else:
raise RuntimeError(f"Unknown model type {modelType}")
except Exception as e:
print("[Voice Changer] loading sample info error:", e)
return None

View File

@ -95,3 +95,6 @@ class EnumFrameworkTypes(Enum):
class ServerAudioDeviceTypes(Enum): class ServerAudioDeviceTypes(Enum):
audioinput = "audioinput" audioinput = "audioinput"
audiooutput = "audiooutput" audiooutput = "audiooutput"
SAMPLES_JSON = "https://huggingface.co/wok000/vcclient_model/raw/main/samples.json"

View File

@ -78,7 +78,7 @@ class MMVC_Rest_Fileuploader:
params: str = Form(...), params: str = Form(...),
): ):
paramDict = json.loads(params) paramDict = json.loads(params)
print("paramDict", paramDict) # print("paramDict", paramDict)
# Change Filepath # Change Filepath
newFilesDict = {} newFilesDict = {}

44
server/samples.json Normal file
View File

@ -0,0 +1,44 @@
{
"RVC": [
{
"id": "KikotoKurage_Song",
"name": "黄琴海月(Song)",
"modelUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc/kikoto_kurage_song/KikotoKurage_Song.pth",
"indexUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc/kikoto_kurage_song/added_IVF2252_Flat_nprobe_10.index.bin",
"featureUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc/kikoto_kurage_song/total_fea.npy",
"termOfUseUrl": "https://huggingface.co/wok000/vcclient_model/raw/main/rvc/kikoto_kurage_song/term_of_use.txt",
"credit": "黄琴海月",
"description": ""
},
{
"id": "KikotoKurage_Talk",
"name": "黄琴海月(Talk)",
"modelUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc/kikoto_kurage_talk/KikotoKurage_Talk.pth",
"indexUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc/kikoto_kurage_talk/added_IVF1022_Flat_nprobe_7.index.bin",
"featureUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc/kikoto_kurage_talk/total_fea.npy",
"termOfUseUrl": "https://huggingface.co/wok000/vcclient_model/raw/main/rvc/kikoto_kurage_talk/term_of_use.txt",
"credit": "黄琴海月",
"description": ""
},
{
"id": "KikotoMahiro_Song",
"name": "黄琴まひろ(Song)",
"modelUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc/kikoto_mahiro_song/KikotoMahiro_Song.pth",
"indexUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc/kikoto_mahiro_song/added_IVF3621_Flat_nprobe_11.index.bin",
"featureUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc/kikoto_mahiro_song/total_fea.npy",
"termOfUseUrl": "https://huggingface.co/wok000/vcclient_model/blob/main/rvc/kikoto_mahiro_song/term_of_use.txt",
"credit": "黄琴まひろ",
"description": ""
},
{
"id": "KikotoMahiro_Talk",
"name": "黄琴まひろ(Talk)",
"modelUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc/kikoto_mahiro_talk/KikotoMahiro_Talk.pth",
"indexUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc/kikoto_mahiro_talk/added_IVF1591_Flat_nprobe_9.index.bin",
"featureUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc/kikoto_mahiro_talk/total_fea.npy",
"termOfUseUrl": "https://huggingface.co/wok000/vcclient_model/raw/main/rvc/kikoto_mahiro_talk/term_of_use.txt",
"credit": "黄琴まひろ",
"description": ""
}
]
}

View File

@ -20,9 +20,9 @@ def list_audio_device():
outputDeviceList = [d for d in audioDeviceList if d["max_output_channels"] > 0] outputDeviceList = [d for d in audioDeviceList if d["max_output_channels"] > 0]
hostapis = sd.query_hostapis() hostapis = sd.query_hostapis()
print("input:", inputAudioDeviceList) # print("input:", inputAudioDeviceList)
print("output:", outputDeviceList) # print("output:", outputDeviceList)
print("hostapis", hostapis) # print("hostapis", hostapis)
serverAudioInputDevices = [] serverAudioInputDevices = []
serverAudioOutputDevices = [] serverAudioOutputDevices = []

View File

@ -18,3 +18,8 @@ class ModelSlot:
embChannels: int = 256 embChannels: int = 256
deprecated: bool = False deprecated: bool = False
embedder: EnumEmbedderTypes = EnumEmbedderTypes.hubert embedder: EnumEmbedderTypes = EnumEmbedderTypes.hubert
name: str = ""
description: str = ""
credit: str = ""
termsOfUseUrl: str = ""

View File

@ -32,6 +32,12 @@ def generateModelSlot(slotDir: str):
modelSlot.indexFile = None modelSlot.indexFile = None
modelSlot.defaultTrans = params["trans"] if "trans" in params else 0 modelSlot.defaultTrans = params["trans"] if "trans" in params else 0
modelSlot.name = params["name"] if "name" in params else None
modelSlot.description = params["description"] if "description" in params else None
modelSlot.credit = params["credit"] if "credit" in params else None
modelSlot.termsOfUseUrl = (
params["termsOfUseUrl"] if "termsOfUseUrl" in params else None
)
modelSlot.isONNX = modelSlot.modelFile.endswith(".onnx") modelSlot.isONNX = modelSlot.modelFile.endswith(".onnx")

View File

@ -1,3 +1,4 @@
from concurrent.futures import ThreadPoolExecutor
import sys import sys
import os import os
import resampy import resampy
@ -5,6 +6,8 @@ from dataclasses import asdict
from typing import cast from typing import cast
import numpy as np import numpy as np
import torch import torch
from MMVCServerSIO import download
from ModelSample import RVCModelSample, getModelSamples
# avoiding parse arg error in RVC # avoiding parse arg error in RVC
@ -35,7 +38,7 @@ from voice_changer.RVC.deviceManager.DeviceManager import DeviceManager
from voice_changer.RVC.pipeline.Pipeline import Pipeline from voice_changer.RVC.pipeline.Pipeline import Pipeline
from Exceptions import NoModeLoadedException from Exceptions import NoModeLoadedException
from const import UPLOAD_DIR from const import TMP_DIR, UPLOAD_DIR
import shutil import shutil
import json import json
@ -73,11 +76,86 @@ class RVC:
self.loadSlots() self.loadSlots()
print("RVC initialization: ", params) print("RVC initialization: ", params)
sampleModels = getModelSamples(params.samples, "RVC")
if sampleModels is not None:
self.settings.sampleModels = sampleModels
# 起動時にスロットにモデルがある場合はロードしておく
if len(self.settings.modelSlots) > 0:
for i, slot in enumerate(self.settings.modelSlots):
if len(slot.modelFile) > 0:
self.prepareModel(i)
self.settings.modelSlotIndex = i
self.switchModel()
self.initialLoad = False
break
def getSampleInfo(self, id: str):
sampleInfos = list(filter(lambda x: x.id == id, self.settings.sampleModels))
if len(sampleInfos) > 0:
return sampleInfos[0]
else:
None
def downloadModelFiles(self, sampleInfo: RVCModelSample):
downloadParams = []
modelPath = os.path.join(TMP_DIR, os.path.basename(sampleInfo.modelUrl))
downloadParams.append(
{
"url": sampleInfo.modelUrl,
"saveTo": modelPath,
"position": 0,
}
)
indexPath = None
if hasattr(sampleInfo, "indexUrl") and sampleInfo.indexUrl != "":
indexPath = os.path.join(TMP_DIR, os.path.basename(sampleInfo.indexUrl))
downloadParams.append(
{
"url": sampleInfo.indexUrl,
"saveTo": indexPath,
"position": 1,
}
)
featurePath = None
if hasattr(sampleInfo, "featureUrl") or sampleInfo.featureUrl != "":
featurePath = os.path.join(TMP_DIR, os.path.basename(sampleInfo.featureUrl))
downloadParams.append(
{
"url": sampleInfo.featureUrl,
"saveTo": featurePath,
"position": 2,
}
)
with ThreadPoolExecutor() as pool:
pool.map(download, downloadParams)
return modelPath, indexPath, featurePath
def loadModel(self, props: LoadModelParams): def loadModel(self, props: LoadModelParams):
target_slot_idx = props.slot target_slot_idx = props.slot
params = props.params params = props.params
# modelName = os.path.splitext(os.path.basename(params["files"]["rvcModel"]))[0] print("loadModel", params)
if len(params["sampleId"]) > 0:
sampleInfo = self.getSampleInfo(params["sampleId"])
if sampleInfo is None:
print("[Voice Changer] sampleInfo is None")
return
modelPath, indexPath, featurePath = self.downloadModelFiles(sampleInfo)
params["files"]["rvcModel"] = modelPath
if indexPath is not None:
params["files"]["rvcIndex"] = indexPath
if featurePath is not None:
params["files"]["rvcFeature"] = featurePath
params["credit"] = sampleInfo.credit
params["description"] = sampleInfo.description
params["name"] = sampleInfo.name
params["sampleId"] = sampleInfo.id
params["termOfUseUrl"] = sampleInfo.termOfUseUrl
slotDir = os.path.join( slotDir = os.path.join(
self.params.model_dir, RVC_MODEL_DIRNAME, str(target_slot_idx) self.params.model_dir, RVC_MODEL_DIRNAME, str(target_slot_idx)
) )
@ -95,6 +173,16 @@ class RVC:
shutil.move(f, dst) shutil.move(f, dst)
json.dump(params, open(os.path.join(slotDir, "params.json"), "w")) json.dump(params, open(os.path.join(slotDir, "params.json"), "w"))
self.loadSlots() self.loadSlots()
# 初回のみロード(起動時にスロットにモデルがあった場合はinitialLoadはFalseになっている)
if self.initialLoad:
self.prepareModel(target_slot_idx)
self.settings.modelSlotIndex = target_slot_idx
self.switchModel()
self.initialLoad = False
elif target_slot_idx == self.currentSlot:
self.prepareModel(target_slot_idx)
return self.get_info() return self.get_info()
def loadSlots(self): def loadSlots(self):
@ -110,24 +198,6 @@ class RVC:
modelSlot = generateModelSlot(slotDir) modelSlot = generateModelSlot(slotDir)
self.settings.modelSlots.append(modelSlot) self.settings.modelSlots.append(modelSlot)
# modelSlot = generateModelSlot(params)
# self.settings.modelSlots[target_slot_idx] = modelSlot
# print(
# f"[Voice Changer] RVC new model is uploaded,{target_slot_idx}",
# asdict(modelSlot),
# )
# # 初回のみロード
# if self.initialLoad:
# self.prepareModel(target_slot_idx)
# self.settings.modelSlotIndex = target_slot_idx
# self.switchModel()
# self.initialLoad = False
# elif target_slot_idx == self.currentSlot:
# self.prepareModel(target_slot_idx)
# return self.get_info()
def update_settings(self, key: str, val: int | float | str): def update_settings(self, key: str, val: int | float | str):
if key in self.settings.intData: if key in self.settings.intData:
# 設定前処理 # 設定前処理

View File

@ -1,4 +1,5 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from ModelSample import RVCModelSample
from voice_changer.RVC.ModelSlot import ModelSlot from voice_changer.RVC.ModelSlot import ModelSlot
@ -25,6 +26,8 @@ class RVCSettings:
ModelSlot(), # 6(merged) ModelSlot(), # 6(merged)
] ]
) )
sampleModels: list[RVCModelSample] = field(default_factory=lambda: [])
indexRatio: float = 0 indexRatio: float = 0
rvcQuality: int = 0 rvcQuality: int = 0
silenceFront: int = 1 # 0:off, 1:on silenceFront: int = 1 # 0:off, 1:on

View File

@ -4,6 +4,7 @@ from dataclasses import dataclass
@dataclass @dataclass
class VoiceChangerParams: class VoiceChangerParams:
model_dir: str model_dir: str
samples: str
content_vec_500: str content_vec_500: str
content_vec_500_onnx: str content_vec_500_onnx: str
content_vec_500_onnx_on: bool content_vec_500_onnx_on: bool