mirror of
https://github.com/w-okada/voice-changer.git
synced 2025-03-13 19:34:02 +03:00
WIP: new gui
This commit is contained in:
parent
8c21d4e5ff
commit
c273a40f44
60
client/demo/dist/assets/gui_settings/RVC.json
vendored
60
client/demo/dist/assets/gui_settings/RVC.json
vendored
@ -2,64 +2,24 @@
|
||||
"type": "demo",
|
||||
"id": "RVC",
|
||||
"front": {
|
||||
"title": [
|
||||
{
|
||||
"name": "title",
|
||||
"options": {
|
||||
"mainTitle": "Realtime Voice Changer Client",
|
||||
"subTitle": "for RVC",
|
||||
"lineNum": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clearSetting",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"title": [],
|
||||
"serverControl": [],
|
||||
"modelSetting": [],
|
||||
"lab": [
|
||||
{
|
||||
"name": "mergeLab",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"lab": [],
|
||||
|
||||
"deviceSetting": [],
|
||||
"qualityControl": [],
|
||||
"speakerSetting": [],
|
||||
"converterSetting": [],
|
||||
"advancedSetting": [
|
||||
{
|
||||
"name": "protocol",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "crossFadeOverlapSize",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "crossFadeOffsetRate",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "crossFadeEndRate",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "trancateNumThreshold",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "silenceFront",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "protect",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"advancedSetting": [],
|
||||
"modelSlotControl": [
|
||||
{
|
||||
"name": "headerArea",
|
||||
"options": {
|
||||
"mainTitle": "Realtime Voice Changer Client",
|
||||
"subTitle": "for RVC"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "modelSlotArea",
|
||||
"options": {}
|
||||
|
11
client/demo/dist/index.html
vendored
11
client/demo/dist/index.html
vendored
@ -1,10 +1 @@
|
||||
<!DOCTYPE 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>
|
||||
<!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>
|
1838
client/demo/dist/index.js
vendored
1838
client/demo/dist/index.js
vendored
File diff suppressed because one or more lines are too long
31
client/demo/dist/index.js.LICENSE.txt
vendored
Normal file
31
client/demo/dist/index.js.LICENSE.txt
vendored
Normal 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.
|
||||
*/
|
5498
client/demo/package-lock.json
generated
5498
client/demo/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -19,27 +19,27 @@
|
||||
"author": "wataru.okada@flect.co.jp",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.1",
|
||||
"@babel/plugin-transform-runtime": "^7.22.4",
|
||||
"@babel/preset-env": "^7.22.4",
|
||||
"@babel/preset-react": "^7.22.3",
|
||||
"@babel/preset-typescript": "^7.21.5",
|
||||
"@types/node": "^20.2.5",
|
||||
"@types/react": "^18.2.8",
|
||||
"@babel/core": "^7.22.5",
|
||||
"@babel/plugin-transform-runtime": "^7.22.5",
|
||||
"@babel/preset-env": "^7.22.5",
|
||||
"@babel/preset-react": "^7.22.5",
|
||||
"@babel/preset-typescript": "^7.22.5",
|
||||
"@types/node": "^20.2.6",
|
||||
"@types/react": "^18.2.9",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"babel-loader": "^9.1.2",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.8.1",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint": "^8.42.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-webpack-plugin": "^4.0.1",
|
||||
"html-loader": "^4.2.0",
|
||||
"html-webpack-plugin": "^5.5.1",
|
||||
"html-webpack-plugin": "^5.5.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss-loader": "^7.3.2",
|
||||
"postcss-loader": "^7.3.3",
|
||||
"postcss-nested": "^6.0.1",
|
||||
"prettier": "^2.8.8",
|
||||
"rimraf": "^5.0.1",
|
||||
@ -47,12 +47,12 @@
|
||||
"ts-loader": "^9.4.3",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.1.3",
|
||||
"webpack": "^5.85.0",
|
||||
"webpack-cli": "^5.1.1",
|
||||
"webpack-dev-server": "^4.15.0"
|
||||
"webpack": "^5.86.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dannadori/voice-changer-client-js": "^1.0.139",
|
||||
"@dannadori/voice-changer-client-js": "^1.0.143",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
||||
|
@ -2,64 +2,24 @@
|
||||
"type": "demo",
|
||||
"id": "RVC",
|
||||
"front": {
|
||||
"title": [
|
||||
{
|
||||
"name": "title",
|
||||
"options": {
|
||||
"mainTitle": "Realtime Voice Changer Client",
|
||||
"subTitle": "for RVC",
|
||||
"lineNum": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clearSetting",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"title": [],
|
||||
"serverControl": [],
|
||||
"modelSetting": [],
|
||||
"lab": [
|
||||
{
|
||||
"name": "mergeLab",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"lab": [],
|
||||
|
||||
"deviceSetting": [],
|
||||
"qualityControl": [],
|
||||
"speakerSetting": [],
|
||||
"converterSetting": [],
|
||||
"advancedSetting": [
|
||||
{
|
||||
"name": "protocol",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "crossFadeOverlapSize",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "crossFadeOffsetRate",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "crossFadeEndRate",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "trancateNumThreshold",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "silenceFront",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "protect",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"advancedSetting": [],
|
||||
"modelSlotControl": [
|
||||
{
|
||||
"name": "headerArea",
|
||||
"options": {
|
||||
"mainTitle": "Realtime Voice Changer Client",
|
||||
"subTitle": "for RVC"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "modelSlotArea",
|
||||
"options": {}
|
||||
|
@ -16,6 +16,8 @@ export const OpenLicenseDialogCheckbox = "open-license-dialog-checkbox"
|
||||
export const OpenWaitingDialogCheckbox = "open-waiting-dialog-checkbox"
|
||||
export const OpenStartingNoticeDialogCheckbox = "open-starting-notice-dialog-checkbox"
|
||||
export const OpenModelSlotManagerDialogCheckbox = "open-model-slot-manager-dialog-checkbox"
|
||||
export const OpenMergeLabDialogCheckbox = "open-merge-lab-dialog-checkbox"
|
||||
export const OpenAdvancedSettingDialogCheckbox = "open-advanced-setting-dialog-checkbox"
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
@ -36,6 +38,9 @@ export type StateControls = {
|
||||
showStartingNoticeCheckbox: StateControlCheckbox
|
||||
showModelSlotManagerCheckbox: StateControlCheckbox
|
||||
|
||||
showMergeLabCheckbox: StateControlCheckbox
|
||||
showAdvancedSettingCheckbox: StateControlCheckbox
|
||||
|
||||
}
|
||||
|
||||
type GuiStateAndMethod = {
|
||||
@ -154,6 +159,8 @@ export const GuiStateProvider = ({ children }: Props) => {
|
||||
const showWaitingCheckbox = useStateControlCheckbox(OpenWaitingDialogCheckbox);
|
||||
const showStartingNoticeCheckbox = useStateControlCheckbox(OpenStartingNoticeDialogCheckbox);
|
||||
const showModelSlotManagerCheckbox = useStateControlCheckbox(OpenModelSlotManagerDialogCheckbox);
|
||||
const showMergeLabCheckbox = useStateControlCheckbox(OpenMergeLabDialogCheckbox);
|
||||
const showAdvancedSettingCheckbox = useStateControlCheckbox(OpenAdvancedSettingDialogCheckbox);
|
||||
|
||||
useEffect(() => {
|
||||
openServerControlCheckbox.updateState(true)
|
||||
@ -172,6 +179,9 @@ export const GuiStateProvider = ({ children }: Props) => {
|
||||
|
||||
showStartingNoticeCheckbox.updateState(false)
|
||||
showModelSlotManagerCheckbox.updateState(false)
|
||||
showMergeLabCheckbox.updateState(false)
|
||||
showAdvancedSettingCheckbox.updateState(false)
|
||||
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
@ -209,7 +219,11 @@ export const GuiStateProvider = ({ children }: Props) => {
|
||||
showLicenseCheckbox,
|
||||
showWaitingCheckbox,
|
||||
showStartingNoticeCheckbox,
|
||||
showModelSlotManagerCheckbox
|
||||
showModelSlotManagerCheckbox,
|
||||
|
||||
showMergeLabCheckbox,
|
||||
showAdvancedSettingCheckbox
|
||||
|
||||
},
|
||||
isConverting,
|
||||
setIsConverting,
|
||||
|
@ -60,6 +60,7 @@ import { ProtectRow, ProtectRowProps } from "./components/610_ProtectRow"
|
||||
import { ModelSlotArea, ModelSlotAreaProps } from "./components2/100_ModelSlotArea"
|
||||
import { CharacterArea, CharacterAreaProps } from "./components2/101_CharacterArea"
|
||||
import { ConfigArea, ConfigAreaProps } from "./components2/102_ConfigArea"
|
||||
import { HeaderArea, HeaderAreaProps } from "./components2/001_HeaderArea"
|
||||
|
||||
export const catalog: { [key: string]: (props: any) => JSX.Element } = {}
|
||||
|
||||
@ -155,11 +156,13 @@ const initialize = () => {
|
||||
|
||||
|
||||
addToCatalog("mergeLab", (props: MergeLabRowProps) => { return <MergeLabRow {...props} /> })
|
||||
|
||||
|
||||
addToCatalog("headerArea", (props: HeaderAreaProps) => { return <HeaderArea {...props} /> })
|
||||
addToCatalog("modelSlotArea", (props: ModelSlotAreaProps) => { return <ModelSlotArea {...props} /> })
|
||||
addToCatalog("characterArea", (props: CharacterAreaProps) => { return <CharacterArea {...props} /> })
|
||||
addToCatalog("configArea", (props: ConfigAreaProps) => { return <ConfigArea {...props} /> })
|
||||
|
||||
|
||||
}
|
||||
|
||||
initialize()
|
@ -4,6 +4,8 @@ import { LicenseDialog } from "./901_LicenseDialog";
|
||||
import { WaitingDialog } from "./902_WaitingDialog";
|
||||
import { StartingNoticeDialog } from "./903_StartingNoticeDialog";
|
||||
import { ModelSlotManagerDialog } from "./904_ModelSlotManagerDialog";
|
||||
import { MergeLabDialog } from "./905_MergeLabDialog";
|
||||
import { AdvancedSettingDialog } from "./906_AdvancedSettingDialog";
|
||||
|
||||
export const Dialogs = () => {
|
||||
const guiState = useGuiState()
|
||||
@ -13,6 +15,8 @@ export const Dialogs = () => {
|
||||
{guiState.stateControls.showWaitingCheckbox.trigger}
|
||||
{guiState.stateControls.showStartingNoticeCheckbox.trigger}
|
||||
{guiState.stateControls.showModelSlotManagerCheckbox.trigger}
|
||||
{guiState.stateControls.showMergeLabCheckbox.trigger}
|
||||
{guiState.stateControls.showAdvancedSettingCheckbox.trigger}
|
||||
<div className="dialog-container" id="dialog">
|
||||
{guiState.stateControls.showLicenseCheckbox.trigger}
|
||||
<LicenseDialog></LicenseDialog>
|
||||
@ -22,6 +26,10 @@ export const Dialogs = () => {
|
||||
<StartingNoticeDialog></StartingNoticeDialog>
|
||||
{guiState.stateControls.showModelSlotManagerCheckbox.trigger}
|
||||
<ModelSlotManagerDialog></ModelSlotManagerDialog>
|
||||
{guiState.stateControls.showMergeLabCheckbox.trigger}
|
||||
<MergeLabDialog></MergeLabDialog>
|
||||
{guiState.stateControls.showAdvancedSettingCheckbox.trigger}
|
||||
<AdvancedSettingDialog></AdvancedSettingDialog>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -131,7 +131,7 @@ export const ModelSlotManagerDialog = () => {
|
||||
|
||||
const isRegisterd = modelFileName.length > 0 ? true : false
|
||||
const name = x.name && x.name.length > 0 ? x.name : isRegisterd ? modelFileName : "blank"
|
||||
const termOfUseUrlLink = x.termsOfUseUrl.length > 0 ? <a href={x.termsOfUseUrl} target="_blank" rel="noopener noreferrer" className="body-item-text-small">[terms of use]</a> : <></>
|
||||
const termOfUseUrlLink = x.termsOfUseUrl && x.termsOfUseUrl.length > 0 ? <a href={x.termsOfUseUrl} target="_blank" rel="noopener noreferrer" className="body-item-text-small">[terms of use]</a> : <></>
|
||||
|
||||
const nameValueClass = isRegisterd ? "model-slot-detail-row-value-pointable" : "model-slot-detail-row-value"
|
||||
const nameValueAction = isRegisterd ? async (index: number) => {
|
||||
@ -260,7 +260,7 @@ export const ModelSlotManagerDialog = () => {
|
||||
}
|
||||
const options = (
|
||||
serverSetting.serverSetting.sampleModels.filter(x => { return lang == "All" ? true : x.lang == lang }).map((x, index) => {
|
||||
const termOfUseUrlLink = x.termsOfUseUrl.length > 0 ? <a href={x.termsOfUseUrl} target="_blank" rel="noopener noreferrer" className="body-item-text-small">[terms of use]</a> : <></>
|
||||
const termOfUseUrlLink = x.termsOfUseUrl && x.termsOfUseUrl.length > 0 ? <a href={x.termsOfUseUrl} target="_blank" rel="noopener noreferrer" className="body-item-text-small">[terms of use]</a> : <></>
|
||||
|
||||
return (
|
||||
<div key={index} className="model-slot">
|
||||
|
164
client/demo/src/components/demo/905_MergeLabDialog.tsx
Normal file
164
client/demo/src/components/demo/905_MergeLabDialog.tsx
Normal file
@ -0,0 +1,164 @@
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useGuiState } from "./001_GuiStateProvider";
|
||||
import { useAppState } from "../../001_provider/001_AppStateProvider";
|
||||
import { MergeElement, ModelType } from "@dannadori/voice-changer-client-js";
|
||||
|
||||
|
||||
export const MergeLabDialog = () => {
|
||||
const guiState = useGuiState()
|
||||
|
||||
const { serverSetting } = useAppState()
|
||||
const [currentFilter, setCurrentFilter] = useState<string>("")
|
||||
const [mergeElements, setMergeElements] = useState<MergeElement[]>([])
|
||||
|
||||
// スロットが変更されたときの初期化処理
|
||||
const newSlotChangeKey = useMemo(() => {
|
||||
if (!serverSetting.serverSetting.modelSlots) {
|
||||
return ""
|
||||
}
|
||||
return serverSetting.serverSetting.modelSlots.reduce((prev, cur) => {
|
||||
return prev + "_" + cur.modelFile
|
||||
}, "")
|
||||
}, [serverSetting.serverSetting.modelSlots])
|
||||
|
||||
const filterItems = useMemo(() => {
|
||||
return serverSetting.serverSetting.modelSlots.reduce((prev, cur) => {
|
||||
const key = `${cur.modelType},${cur.samplingRate},${cur.embChannels}`
|
||||
const val = { type: cur.modelType, samplingRate: cur.samplingRate, embChannels: cur.embChannels }
|
||||
const existKeys = Object.keys(prev)
|
||||
if (cur.modelFile.length == 0) {
|
||||
return prev
|
||||
}
|
||||
if (cur.modelType == "onnxRVC" || cur.modelType == "onnxRVCNono") {
|
||||
return prev
|
||||
}
|
||||
if (!existKeys.includes(key)) {
|
||||
prev[key] = val
|
||||
}
|
||||
return prev
|
||||
}, {} as { [key: string]: { type: ModelType, samplingRate: number, embChannels: number } })
|
||||
|
||||
}, [newSlotChangeKey])
|
||||
|
||||
const models = useMemo(() => {
|
||||
return serverSetting.serverSetting.modelSlots.filter(x => {
|
||||
const filterVals = filterItems[currentFilter]
|
||||
if (!filterVals) {
|
||||
return false
|
||||
}
|
||||
if (x.modelType == filterVals.type && x.samplingRate == filterVals.samplingRate && x.embChannels == filterVals.embChannels) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}, [filterItems, currentFilter])
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(filterItems).length > 0) {
|
||||
setCurrentFilter(Object.keys(filterItems)[0])
|
||||
}
|
||||
}, [filterItems])
|
||||
useEffect(() => {
|
||||
const newMergeElements = models.map((x) => {
|
||||
return { filename: x.modelFile, strength: 0 }
|
||||
})
|
||||
setMergeElements(newMergeElements)
|
||||
}, [models])
|
||||
|
||||
const dialog = useMemo(() => {
|
||||
const closeButtonRow = (
|
||||
<div className="body-row split-3-4-3 left-padding-1">
|
||||
<div className="body-item-text">
|
||||
</div>
|
||||
<div className="body-button-container body-button-container-space-around">
|
||||
<div className="body-button" onClick={() => { guiState.stateControls.showMergeLabCheckbox.updateState(false) }} >close</div>
|
||||
</div>
|
||||
<div className="body-item-text"></div>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
const filterOptions = Object.keys(filterItems).map(x => {
|
||||
return <option key={x} value={x}>{x}</option>
|
||||
}).filter(x => x != null)
|
||||
|
||||
const onMergeElementsChanged = (filename: string, strength: number) => {
|
||||
const newMergeElements = mergeElements.map((x) => {
|
||||
if (x.filename == filename) {
|
||||
return { filename: x.filename, strength: strength }
|
||||
} else {
|
||||
return x
|
||||
}
|
||||
})
|
||||
setMergeElements(newMergeElements)
|
||||
}
|
||||
|
||||
const onMergeClicked = () => {
|
||||
serverSetting.mergeModel({
|
||||
command: "mix",
|
||||
defaultTune: 0,
|
||||
defaultIndexRatio: 1,
|
||||
defaultProtect: 0.5,
|
||||
files: mergeElements
|
||||
})
|
||||
}
|
||||
|
||||
const modelList = mergeElements.map((x, index) => {
|
||||
const name = models.find(model => { return model.modelFile == x.filename })?.name || ""
|
||||
return (
|
||||
<div key={index} className="merge-lab-model-item">
|
||||
<div>
|
||||
{name}
|
||||
</div>
|
||||
<div>
|
||||
<input type="range" className="body-item-input-slider" min="0" max="100" step="1" value={x.strength} onChange={(e) => {
|
||||
onMergeElementsChanged(x.filename, Number(e.target.value))
|
||||
}}></input>
|
||||
<span className="body-item-input-slider-val">{x.strength}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
const content = (
|
||||
<div className="merge-lab-container">
|
||||
<div className="merge-lab-type-filter">
|
||||
<div>
|
||||
Type:
|
||||
</div>
|
||||
<div>
|
||||
<select value={currentFilter} onChange={(e) => { setCurrentFilter(e.target.value) }}>
|
||||
{filterOptions}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="merge-lab-manipulator">
|
||||
<div className="merge-lab-model-list">
|
||||
{modelList}
|
||||
</div>
|
||||
<div className="merge-lab-merge-buttons">
|
||||
<div className="merge-lab-merge-buttons-notice">
|
||||
The merged model is stored in the final slot. If you assign this slot, it will be overwritten.
|
||||
</div>
|
||||
<div className="merge-lab-merge-button" onClick={onMergeClicked}>
|
||||
merge
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div className="dialog-frame">
|
||||
<div className="dialog-title">MergeLab</div>
|
||||
<div className="dialog-content">
|
||||
{content}
|
||||
{closeButtonRow}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [newSlotChangeKey, currentFilter, mergeElements]);
|
||||
return dialog;
|
||||
|
||||
};
|
182
client/demo/src/components/demo/906_AdvancedSettingDialog.tsx
Normal file
182
client/demo/src/components/demo/906_AdvancedSettingDialog.tsx
Normal file
@ -0,0 +1,182 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { useGuiState } from "./001_GuiStateProvider";
|
||||
import { useAppState } from "../../001_provider/001_AppStateProvider";
|
||||
import { CrossFadeOverlapSize, Protocol } from "@dannadori/voice-changer-client-js";
|
||||
|
||||
|
||||
export const AdvancedSettingDialog = () => {
|
||||
const guiState = useGuiState()
|
||||
const { workletNodeSetting, workletSetting, serverSetting } = useAppState()
|
||||
const dialog = useMemo(() => {
|
||||
const closeButtonRow = (
|
||||
<div className="body-row split-3-4-3 left-padding-1">
|
||||
<div className="body-item-text">
|
||||
</div>
|
||||
<div className="body-button-container body-button-container-space-around">
|
||||
<div className="body-button" onClick={() => { guiState.stateControls.showAdvancedSettingCheckbox.updateState(false) }} >close</div>
|
||||
</div>
|
||||
<div className="body-item-text"></div>
|
||||
</div>
|
||||
|
||||
)
|
||||
|
||||
const onProtocolChanged = async (val: Protocol) => {
|
||||
workletNodeSetting.updateWorkletNodeSetting({ ...workletNodeSetting.workletNodeSetting, protocol: val })
|
||||
}
|
||||
const protocolRow = (
|
||||
<div className="advanced-setting-container-row">
|
||||
<div className="advanced-setting-container-row-title">
|
||||
protocol
|
||||
</div>
|
||||
<div className="advanced-setting-container-row-field">
|
||||
<select value={workletNodeSetting.workletNodeSetting.protocol} onChange={(e) => {
|
||||
onProtocolChanged(e.target.value as
|
||||
Protocol)
|
||||
}}>
|
||||
{
|
||||
Object.values(Protocol).map(x => {
|
||||
return <option key={x} value={x}>{x}</option>
|
||||
})
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
const crossfaceRow = (
|
||||
<div className="advanced-setting-container-row">
|
||||
<div className="advanced-setting-container-row-title">
|
||||
Crossfade
|
||||
</div>
|
||||
<div className="advanced-setting-container-row-field">
|
||||
<div className="advanced-setting-container-row-field-crossfade-container">
|
||||
<div>
|
||||
<div>overlap:</div>
|
||||
<div>
|
||||
<select className="body-select" value={serverSetting.serverSetting.crossFadeOverlapSize} onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, crossFadeOverlapSize: Number(e.target.value) as CrossFadeOverlapSize })
|
||||
}}>
|
||||
{
|
||||
Object.values(CrossFadeOverlapSize).map(x => {
|
||||
return <option key={x} value={x}>{x}</option>
|
||||
})
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>start:</div>
|
||||
<div>
|
||||
<input type="number" min={0} max={1} step={0.1} value={serverSetting.serverSetting.crossFadeOffsetRate} onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, crossFadeOffsetRate: Number(e.target.value) })
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>end:</div>
|
||||
<div>
|
||||
<input type="number" min={0} max={1} step={0.1} value={serverSetting.serverSetting.crossFadeEndRate} onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, crossFadeEndRate: Number(e.target.value) })
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const trancateRow = (
|
||||
<div className="advanced-setting-container-row">
|
||||
<div className="advanced-setting-container-row-title">
|
||||
Trancate
|
||||
</div>
|
||||
<div className="advanced-setting-container-row-field">
|
||||
<input type="number" min={5} max={300} step={1} value={workletSetting.setting.numTrancateTreshold} onChange={(e) => {
|
||||
workletSetting.setSetting({
|
||||
...workletSetting.setting,
|
||||
numTrancateTreshold: Number(e.target.value)
|
||||
})
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const onSilenceFrontChanged = (val: number) => {
|
||||
serverSetting.updateServerSettings({
|
||||
...serverSetting.serverSetting,
|
||||
silenceFront: val
|
||||
})
|
||||
}
|
||||
const silenceFrontRow = (
|
||||
<div className="advanced-setting-container-row">
|
||||
<div className="advanced-setting-container-row-title">
|
||||
SilenceFront
|
||||
</div>
|
||||
<div className="advanced-setting-container-row-field">
|
||||
<select value={serverSetting.serverSetting.silenceFront} onChange={(e) => { onSilenceFrontChanged(Number(e.target.value)) }}>
|
||||
<option value="0" >off</option>
|
||||
<option value="1" >on</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const protectRow = (
|
||||
<div className="advanced-setting-container-row">
|
||||
<div className="advanced-setting-container-row-title">
|
||||
Protect
|
||||
</div>
|
||||
<div className="advanced-setting-container-row-field">
|
||||
<div>
|
||||
<input type="range" className="body-item-input-slider" min="0" max="0.5" step="0.1" value={serverSetting.serverSetting.protect || 0} onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, protect: Number(e.target.value) })
|
||||
}}></input>
|
||||
<span className="body-item-input-slider-val">{serverSetting.serverSetting.protect}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const onRVCQualityChanged = (val: number) => {
|
||||
serverSetting.updateServerSettings({
|
||||
...serverSetting.serverSetting,
|
||||
rvcQuality: val
|
||||
})
|
||||
}
|
||||
const rvcQualityRow = (
|
||||
<div className="advanced-setting-container-row">
|
||||
<div className="advanced-setting-container-row-title">
|
||||
RVC Quality
|
||||
</div>
|
||||
<div className="advanced-setting-container-row-field">
|
||||
<select value={serverSetting.serverSetting.rvcQuality} onChange={(e) => { onRVCQualityChanged(Number(e.target.value)) }}>
|
||||
<option value="0" >low</option>
|
||||
<option value="1" >high</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const content = (
|
||||
<div className="advanced-setting-container">
|
||||
{protocolRow}
|
||||
{crossfaceRow}
|
||||
{trancateRow}
|
||||
{silenceFrontRow}
|
||||
{protectRow}
|
||||
{rvcQualityRow}
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="dialog-frame">
|
||||
<div className="dialog-title">Advanced Setting</div>
|
||||
<div className="dialog-content">
|
||||
{content}
|
||||
{closeButtonRow}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, workletNodeSetting.workletNodeSetting, workletNodeSetting.updateWorkletNodeSetting, workletSetting.setting, workletSetting.setSetting]);
|
||||
return dialog;
|
||||
|
||||
};
|
@ -21,6 +21,9 @@ export const Lab = () => {
|
||||
}, []);
|
||||
|
||||
const deviceSetting = useMemo(() => {
|
||||
if (componentSettings.length == 0) {
|
||||
return <></>
|
||||
}
|
||||
const components = componentSettings.map((x, index) => {
|
||||
const c = generateComponent(x.name, x.options)
|
||||
return <div key={`${x.name}_${index}`}>{c}</div>
|
||||
|
@ -41,7 +41,11 @@ export const AudioInputMediaRow = () => {
|
||||
|
||||
const dst = appState.audioContext.createMediaStreamDestination()
|
||||
audioSrcNode.current.connect(dst)
|
||||
appState.clientSetting.updateClientSetting({ ...appState.clientSetting.clientSetting, audioInput: dst.stream })
|
||||
try {
|
||||
appState.clientSetting.updateClientSetting({ ...appState.clientSetting.clientSetting, audioInput: dst.stream })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
const audio_echo = document.getElementById(AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) as HTMLAudioElement
|
||||
audio_echo.srcObject = dst.stream
|
||||
|
@ -36,7 +36,11 @@ export const AudioInputRow = (_props: AudioInputRowProps) => {
|
||||
<select className="body-select" value={guiState.audioInputForGUI} onChange={(e) => {
|
||||
guiState.setAudioInputForGUI(e.target.value)
|
||||
if (guiState.audioInputForGUI != "file") {
|
||||
appState.clientSetting.updateClientSetting({ ...appState.clientSetting.clientSetting, audioInput: e.target.value })
|
||||
try {
|
||||
appState.clientSetting.updateClientSetting({ ...appState.clientSetting.clientSetting, audioInput: e.target.value })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}}>
|
||||
{
|
||||
|
@ -14,17 +14,29 @@ export const NoiseControlRow = (_props: NoiseControlRowProps) => {
|
||||
<div className="body-item-title left-padding-1 ">Noise Suppression</div>
|
||||
<div>
|
||||
<input type="checkbox" checked={appState.clientSetting.clientSetting.echoCancel} onChange={(e) => {
|
||||
appState.clientSetting.updateClientSetting({ ...appState.clientSetting.clientSetting, echoCancel: e.target.checked })
|
||||
try {
|
||||
appState.clientSetting.updateClientSetting({ ...appState.clientSetting.clientSetting, echoCancel: e.target.checked })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}} /> echo cancel
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" checked={appState.clientSetting.clientSetting.noiseSuppression} onChange={(e) => {
|
||||
appState.clientSetting.updateClientSetting({ ...appState.clientSetting.clientSetting, noiseSuppression: e.target.checked })
|
||||
try {
|
||||
appState.clientSetting.updateClientSetting({ ...appState.clientSetting.clientSetting, noiseSuppression: e.target.checked })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}} /> suppression1
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" checked={appState.clientSetting.clientSetting.noiseSuppression2} onChange={(e) => {
|
||||
appState.clientSetting.updateClientSetting({ ...appState.clientSetting.clientSetting, noiseSuppression2: e.target.checked })
|
||||
try {
|
||||
appState.clientSetting.updateClientSetting({ ...appState.clientSetting.clientSetting, noiseSuppression2: e.target.checked })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}} /> suppression2
|
||||
</div>
|
||||
<div className="body-button-container">
|
||||
|
@ -13,7 +13,11 @@ export const SampleRateRow = (_props: SampleRateRowProps) => {
|
||||
<div className="body-item-title left-padding-1">Sample Rate</div>
|
||||
<div className="body-select-container">
|
||||
<select className="body-select" value={appState.clientSetting.clientSetting.sampleRate} onChange={(e) => {
|
||||
appState.clientSetting.updateClientSetting({ ...appState.clientSetting.clientSetting, sampleRate: Number(e.target.value) as SampleRate })
|
||||
try {
|
||||
appState.clientSetting.updateClientSetting({ ...appState.clientSetting.clientSetting, sampleRate: Number(e.target.value) as SampleRate })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}}>
|
||||
{
|
||||
Object.values(SampleRate).map(x => {
|
||||
|
162
client/demo/src/components/demo/components2/001_HeaderArea.tsx
Normal file
162
client/demo/src/components/demo/components2/001_HeaderArea.tsx
Normal file
@ -0,0 +1,162 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { INDEXEDDB_KEY_AUDIO_OUTPUT, INDEXEDDB_KEY_DEFAULT_MODEL_TYPE, isDesktopApp } from "../../../const";
|
||||
import { useGuiState } from "../001_GuiStateProvider";
|
||||
import { useAppRoot } from "../../../001_provider/001_AppRootProvider";
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
import { useIndexedDB } from "@dannadori/voice-changer-client-js";
|
||||
|
||||
|
||||
export type HeaderAreaProps = {
|
||||
mainTitle: string
|
||||
subTitle: string
|
||||
}
|
||||
|
||||
export const HeaderArea = (props: HeaderAreaProps) => {
|
||||
const { appGuiSettingState, setClientType } = useAppRoot()
|
||||
const { clientSetting, clearSetting } = useAppState()
|
||||
const { setIsConverting, isConverting } = useGuiState()
|
||||
|
||||
const clientType = appGuiSettingState.appGuiSetting.id
|
||||
const { removeItem } = useIndexedDB({ clientType: clientType })
|
||||
const { setItem } = useIndexedDB({ clientType: null })
|
||||
|
||||
|
||||
const githubLink = useMemo(() => {
|
||||
return isDesktopApp() ?
|
||||
(
|
||||
// @ts-ignore
|
||||
<span className="link tooltip" onClick={() => { window.electronAPI.openBrowser("https://github.com/w-okada/voice-changer") }}>
|
||||
<img src="./assets/icons/github.svg" />
|
||||
<div className="tooltip-text">github</div>
|
||||
</span>
|
||||
)
|
||||
:
|
||||
(
|
||||
<a className="link tooltip" href="https://github.com/w-okada/voice-changer" target="_blank" rel="noopener noreferrer">
|
||||
<img src="./assets/icons/github.svg" />
|
||||
<div className="tooltip-text">github</div>
|
||||
</a>
|
||||
)
|
||||
}, [])
|
||||
|
||||
|
||||
const manualLink = useMemo(() => {
|
||||
return isDesktopApp() ?
|
||||
(
|
||||
// @ts-ignore
|
||||
<span className="link tooltip" onClick={() => { window.electronAPI.openBrowser("https://github.com/w-okada/voice-changer/blob/master/tutorials/tutorial_rvc_ja_latest.md") }}>
|
||||
<img src="./assets/icons/help-circle.svg" />
|
||||
<div className="tooltip-text">manual</div>
|
||||
</span>
|
||||
)
|
||||
:
|
||||
(
|
||||
<a className="link tooltip" href="https://github.com/w-okada/voice-changer/blob/master/tutorials/tutorial_rvc_ja_latest.md" target="_blank" rel="noopener noreferrer">
|
||||
<img src="./assets/icons/help-circle.svg" />
|
||||
<div className="tooltip-text">manual</div>
|
||||
</a>
|
||||
)
|
||||
}, [])
|
||||
|
||||
|
||||
const toolLink = useMemo(() => {
|
||||
return isDesktopApp() ?
|
||||
(
|
||||
<div className="link tooltip">
|
||||
<img src="./assets/icons/tool.svg" />
|
||||
<div className="tooltip-text tooltip-text-100px">
|
||||
<p onClick={() => {
|
||||
// @ts-ignore
|
||||
window.electronAPI.openBrowser("https://w-okada.github.io/screen-recorder-ts/")
|
||||
}}>
|
||||
screen capture
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
:
|
||||
(
|
||||
<div className="link tooltip">
|
||||
<img src="./assets/icons/tool.svg" />
|
||||
<div className="tooltip-text tooltip-text-100px">
|
||||
<p onClick={() => {
|
||||
window.open("https://w-okada.github.io/screen-recorder-ts/", '_blank', "noreferrer")
|
||||
}}>
|
||||
screen capture
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, [])
|
||||
|
||||
|
||||
const coffeeLink = useMemo(() => {
|
||||
return isDesktopApp() ?
|
||||
(
|
||||
// @ts-ignore
|
||||
<span className="link tooltip" onClick={() => { window.electronAPI.openBrowser("https://www.buymeacoffee.com/wokad") }}>
|
||||
<img className="donate-img" src="./assets/buymeacoffee.png" />
|
||||
<div className="tooltip-text tooltip-text-100px">donate(寄付)</div>
|
||||
</span>
|
||||
)
|
||||
:
|
||||
(
|
||||
<a className="link tooltip" href="https://www.buymeacoffee.com/wokad" target="_blank" rel="noopener noreferrer">
|
||||
<img className="donate-img" src="./assets/buymeacoffee.png" />
|
||||
<div className="tooltip-text tooltip-text-100px">
|
||||
donate(寄付)
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
}, [])
|
||||
|
||||
const headerArea = useMemo(() => {
|
||||
const onClearSettingClicked = async () => {
|
||||
await clearSetting()
|
||||
await removeItem(INDEXEDDB_KEY_AUDIO_OUTPUT)
|
||||
location.reload()
|
||||
}
|
||||
const onReloadClicked = async () => {
|
||||
location.reload()
|
||||
}
|
||||
const onReselectVCClicked = async () => {
|
||||
setIsConverting(false)
|
||||
if (isConverting) {
|
||||
await clientSetting.stop()
|
||||
setIsConverting(false)
|
||||
}
|
||||
setItem(INDEXEDDB_KEY_DEFAULT_MODEL_TYPE, "null")
|
||||
setClientType(null)
|
||||
appGuiSettingState.clearAppGuiSetting()
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="headerArea">
|
||||
<div className="title1">
|
||||
<span className="title">{props.mainTitle}</span>
|
||||
<span className="title-version">{props.subTitle}</span>
|
||||
<span className="title-version-number">{appGuiSettingState.version}</span>
|
||||
<span className="title-version-number">{appGuiSettingState.edition}</span>
|
||||
</div>
|
||||
<div className="icons">
|
||||
<span className="belongings">
|
||||
{githubLink}
|
||||
{manualLink}
|
||||
{toolLink}
|
||||
{coffeeLink}
|
||||
{/* {licenseButton} */}
|
||||
</span>
|
||||
<span className="belongings">
|
||||
<div className="belongings-button" onClick={onClearSettingClicked}>clear setting</div>
|
||||
<div className="belongings-button" onClick={onReloadClicked}>reload</div>
|
||||
<div className="belongings-button" onClick={onReselectVCClicked}>select vc</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}, [props.subTitle, props.mainTitle, appGuiSettingState.version, appGuiSettingState.edition])
|
||||
|
||||
return headerArea
|
||||
};
|
@ -253,7 +253,7 @@ export const CharacterArea = (_props: CharacterAreaProps) => {
|
||||
}
|
||||
|
||||
const exportOnnx = selected.modelFile.endsWith("pth") ? (
|
||||
<div className="character-area-buutton" onClick={onnxExportButtonAction}>export onnx</div>
|
||||
<div className="character-area-button" onClick={onnxExportButtonAction}>export onnx</div>
|
||||
) : <></>
|
||||
return (
|
||||
<div className="character-area-control">
|
||||
@ -261,8 +261,8 @@ export const CharacterArea = (_props: CharacterAreaProps) => {
|
||||
|
||||
</div>
|
||||
<div className="character-area-control-field">
|
||||
<div className="character-area-buuttons">
|
||||
<div className="character-area-buutton" onClick={onUpdateDefaultClicked}>save default</div>
|
||||
<div className="character-area-buttons">
|
||||
<div className="character-area-button" onClick={onUpdateDefaultClicked}>save default</div>
|
||||
{exportOnnx}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -20,19 +20,31 @@ export const QualityArea = (props: QualityAreaProps) => {
|
||||
<div className="config-sub-area-noise-container">
|
||||
<div className="config-sub-area-noise-checkbox-container">
|
||||
<input type="checkbox" disabled={serverSetting.serverSetting.enableServerAudio != 0} checked={clientSetting.clientSetting.echoCancel} onChange={(e) => {
|
||||
clientSetting.updateClientSetting({ ...clientSetting.clientSetting, echoCancel: e.target.checked })
|
||||
try {
|
||||
clientSetting.updateClientSetting({ ...clientSetting.clientSetting, echoCancel: e.target.checked })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}} /> <span>Echo</span>
|
||||
|
||||
</div>
|
||||
<div className="config-sub-area-noise-checkbox-container">
|
||||
<input type="checkbox" disabled={serverSetting.serverSetting.enableServerAudio != 0} checked={clientSetting.clientSetting.noiseSuppression} onChange={(e) => {
|
||||
clientSetting.updateClientSetting({ ...clientSetting.clientSetting, noiseSuppression: e.target.checked })
|
||||
try {
|
||||
clientSetting.updateClientSetting({ ...clientSetting.clientSetting, noiseSuppression: e.target.checked })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}} /> <span>Sup1</span>
|
||||
|
||||
</div>
|
||||
<div className="config-sub-area-noise-checkbox-container">
|
||||
<input type="checkbox" disabled={serverSetting.serverSetting.enableServerAudio != 0} checked={clientSetting.clientSetting.noiseSuppression2} onChange={(e) => {
|
||||
clientSetting.updateClientSetting({ ...clientSetting.clientSetting, noiseSuppression2: e.target.checked })
|
||||
try {
|
||||
clientSetting.updateClientSetting({ ...clientSetting.clientSetting, noiseSuppression2: e.target.checked })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}} /> <span>Sup2</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -65,7 +65,7 @@ export const ConvertArea = (props: ConvertProps) => {
|
||||
}}>
|
||||
{
|
||||
gpusEntry.map(x => {
|
||||
return <option key={x.id} value={x.id}>{x.name}({(x.memory / 1024 / 1024 / 1024).toFixed(0)}GB) </option>
|
||||
return <option key={x.id} value={x.id}>{x.name}{x.name == "cpu" ? "" : `(${(x.memory / 1024 / 1024 / 1024).toFixed(0)})GB`} </option>
|
||||
})
|
||||
}
|
||||
</select>
|
||||
|
@ -75,10 +75,17 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
<div className="config-sub-area-control">
|
||||
<div className="config-sub-area-control-title left-padding-1">input</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<select className="body-select" value={audioInputForGUI} onChange={(e) => {
|
||||
<select className="body-select" value={audioInputForGUI} onChange={async (e) => {
|
||||
setAudioInputForGUI(e.target.value)
|
||||
if (audioInputForGUI != "file") {
|
||||
clientSetting.updateClientSetting({ ...clientSetting.clientSetting, audioInput: e.target.value })
|
||||
try {
|
||||
await clientSetting.updateClientSetting({ ...clientSetting.clientSetting, audioInput: e.target.value })
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
console.error(e)
|
||||
setAudioInputForGUI("none")
|
||||
await clientSetting.updateClientSetting({ ...clientSetting.clientSetting, audioInput: null })
|
||||
}
|
||||
}
|
||||
}}>
|
||||
{
|
||||
@ -164,7 +171,11 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
|
||||
const dst = audioContext.createMediaStreamDestination()
|
||||
audioSrcNode.current.connect(dst)
|
||||
clientSetting.updateClientSetting({ ...clientSetting.clientSetting, audioInput: dst.stream })
|
||||
try {
|
||||
clientSetting.updateClientSetting({ ...clientSetting.clientSetting, audioInput: dst.stream })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
const audio_echo = document.getElementById(AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) as HTMLAudioElement
|
||||
audio_echo.srcObject = dst.stream
|
||||
@ -325,6 +336,9 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
|
||||
// (4) レコーダー
|
||||
const outputRecorderRow = useMemo(() => {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 1) {
|
||||
return <></>
|
||||
}
|
||||
const onOutputRecordStartClicked = async () => {
|
||||
setOutputRecordingStarted(true)
|
||||
await workletNodeSetting.startOutputRecording()
|
||||
@ -335,13 +349,13 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
downloadRecord(record)
|
||||
}
|
||||
|
||||
const startClassName = outputRecordingStarted ? "config-sub-area-buutton-active" : "config-sub-area-buutton"
|
||||
const stopClassName = outputRecordingStarted ? "config-sub-area-buutton" : "config-sub-area-buutton-active"
|
||||
const startClassName = outputRecordingStarted ? "config-sub-area-button-active" : "config-sub-area-button"
|
||||
const stopClassName = outputRecordingStarted ? "config-sub-area-button" : "config-sub-area-button-active"
|
||||
return (
|
||||
<div className="config-sub-area-control">
|
||||
<div className="config-sub-area-control-title">REC.</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<div className="config-sub-area-buuttons">
|
||||
<div className="config-sub-area-buttons">
|
||||
<div onClick={onOutputRecordStartClicked} className={startClassName}>start</div>
|
||||
<div onClick={onOutputRecordStopClicked} className={stopClassName}>stop</div>
|
||||
</div>
|
||||
|
@ -12,17 +12,6 @@ export const RecorderArea = (_props: RecorderAreaProps) => {
|
||||
|
||||
const [serverIORecording, setServerIORecording] = useState<boolean>(false)
|
||||
|
||||
// const recorderRow = useMemo(() => {
|
||||
// return (
|
||||
// <div className="config-sub-area-control">
|
||||
// <div className="config-sub-area-control-title">RECORD:</div>
|
||||
// <div className="config-sub-area-control-field">
|
||||
// </div>
|
||||
// </div>
|
||||
// )
|
||||
// }, [serverSetting.serverSetting, serverSetting.updateServerSettings])
|
||||
|
||||
|
||||
|
||||
const serverIORecorderRow = useMemo(() => {
|
||||
const onServerIORecordStartClicked = async () => {
|
||||
@ -48,8 +37,8 @@ export const RecorderArea = (_props: RecorderAreaProps) => {
|
||||
wavOutput.setSinkId(audioOutputForAnalyzer)
|
||||
}
|
||||
|
||||
const startClassName = serverIORecording ? "config-sub-area-buutton-active" : "config-sub-area-buutton"
|
||||
const stopClassName = serverIORecording ? "config-sub-area-buutton" : "config-sub-area-buutton-active"
|
||||
const startClassName = serverIORecording ? "config-sub-area-button-active" : "config-sub-area-button"
|
||||
const stopClassName = serverIORecording ? "config-sub-area-button" : "config-sub-area-button-active"
|
||||
return (
|
||||
<>
|
||||
<div className="config-sub-area-control">
|
||||
@ -59,7 +48,7 @@ export const RecorderArea = (_props: RecorderAreaProps) => {
|
||||
<div className="config-sub-area-control left-padding-1">
|
||||
<div className="config-sub-area-control-title">SIO rec.</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<div className="config-sub-area-buuttons">
|
||||
<div className="config-sub-area-buttons">
|
||||
<div onClick={onServerIORecordStartClicked} className={startClassName}>start</div>
|
||||
<div onClick={onServerIORecordStopClicked} className={stopClassName}>stop</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,41 @@
|
||||
import React, { useMemo, } from "react"
|
||||
import { useGuiState } from "../001_GuiStateProvider"
|
||||
|
||||
export type MoreActionAreaProps = {
|
||||
}
|
||||
|
||||
export const MoreActionArea = (_props: MoreActionAreaProps) => {
|
||||
const { stateControls } = useGuiState()
|
||||
|
||||
const serverIORecorderRow = useMemo(() => {
|
||||
const onOpenMergeLabClicked = () => {
|
||||
stateControls.showMergeLabCheckbox.updateState(true)
|
||||
}
|
||||
const onOpenAdvancedSettingClicked = () => {
|
||||
stateControls.showAdvancedSettingCheckbox.updateState(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="config-sub-area-control left-padding-1">
|
||||
<div className="config-sub-area-control-title">more...</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<div className="config-sub-area-buttons">
|
||||
<div onClick={onOpenMergeLabClicked} className="config-sub-area-button">Merge Lab</div>
|
||||
<div onClick={onOpenAdvancedSettingClicked} className="config-sub-area-button">Advanced Setting</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
}, [stateControls])
|
||||
|
||||
return (
|
||||
<div className="config-sub-area">
|
||||
{serverIORecorderRow}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { QualityArea } from "./102-1_QualityArea"
|
||||
import { ConvertArea } from "./102-2_ConvertArea"
|
||||
import { DeviceArea } from "./102-3_DeviceArea"
|
||||
import { RecorderArea } from "./102-4_RecorderArea"
|
||||
import { MoreActionArea } from "./102-5_MoreActionArea"
|
||||
|
||||
export type ConfigAreaProps = {
|
||||
detectors: string[]
|
||||
@ -22,6 +23,9 @@ export const ConfigArea = (props: ConfigAreaProps) => {
|
||||
<DeviceArea></DeviceArea>
|
||||
<RecorderArea></RecorderArea>
|
||||
</div>
|
||||
<div className="config-area">
|
||||
<MoreActionArea></MoreActionArea>
|
||||
</div>
|
||||
</>
|
||||
|
||||
)
|
||||
|
@ -1130,11 +1130,11 @@ body {
|
||||
width: 3rem;
|
||||
}
|
||||
}
|
||||
.character-area-buuttons {
|
||||
.character-area-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 5px;
|
||||
.character-area-buutton {
|
||||
.character-area-button {
|
||||
border: solid 2px #999;
|
||||
color: white;
|
||||
font-size: 0.8rem;
|
||||
@ -1143,7 +1143,7 @@ body {
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
}
|
||||
.character-area-buutton:hover {
|
||||
.character-area-button:hover {
|
||||
border: solid 2px #faa;
|
||||
}
|
||||
}
|
||||
@ -1212,12 +1212,12 @@ audio::-webkit-media-controls-overlay-enclosure{
|
||||
width: 3rem;
|
||||
}
|
||||
}
|
||||
.config-sub-area-buuttons {
|
||||
.config-sub-area-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
.config-sub-area-buutton {
|
||||
.config-sub-area-button {
|
||||
border: solid 2px #999;
|
||||
color: white;
|
||||
background: #666;
|
||||
@ -1230,10 +1230,10 @@ audio::-webkit-media-controls-overlay-enclosure{
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
.config-sub-area-buutton:hover {
|
||||
.config-sub-area-button:hover {
|
||||
border: solid 2px #faa;
|
||||
}
|
||||
.config-sub-area-buutton-active {
|
||||
.config-sub-area-button-active {
|
||||
border: solid 2px #999;
|
||||
color: white;
|
||||
background: #844;
|
||||
@ -1298,3 +1298,146 @@ audio::-webkit-media-controls-overlay-enclosure{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.headerArea {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.title1 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 5px;
|
||||
align-items: flex-end;
|
||||
.title {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
text-shadow: 0 0 2px #333;
|
||||
}
|
||||
.title-version {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.title-version-number {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
}
|
||||
.icons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 20px;
|
||||
.belongings {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 3px;
|
||||
.belongings-button {
|
||||
border: solid 2px #999;
|
||||
color: white;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 5px;
|
||||
background: #666;
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
height: 1.7rem;
|
||||
top: -2px;
|
||||
}
|
||||
.belongings-button:hover {
|
||||
border: solid 2px #cc6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-setting-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
margin: 10px;
|
||||
.advanced-setting-container-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 5px;
|
||||
.advanced-setting-container-row-title {
|
||||
width: 7rem;
|
||||
font-weight: 700;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.advanced-setting-container-row-field {
|
||||
width: 15rem;
|
||||
font-size: 0.9rem;
|
||||
.advanced-setting-container-row-field-crossfade-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 5px;
|
||||
width: 10rem;
|
||||
& > div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 3px;
|
||||
& > div:nth-child(1) {
|
||||
color: #333;
|
||||
}
|
||||
& > div:nth-child(2) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.merge-lab-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 10px;
|
||||
gap: 10px;
|
||||
.merge-lab-type-filter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
& > div:nth-child(1) {
|
||||
width: 50%;
|
||||
}
|
||||
& > div:nth-child(2) {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
.merge-lab-manipulator {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
.merge-lab-model-list {
|
||||
width: 70%;
|
||||
.merge-lab-model-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
& > div:nth-child(1) {
|
||||
width: 50%;
|
||||
}
|
||||
& > div:nth-child(2) {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.merge-lab-merge-buttons {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
width: 30%;
|
||||
.merge-lab-merge-button {
|
||||
border: solid 2px #ddd;
|
||||
color: black;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 5px;
|
||||
background: #eee;
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
height: 1.7rem;
|
||||
text-align: center;
|
||||
}
|
||||
.merge-lab-merge-button:hover {
|
||||
border: solid 2px #aaa;
|
||||
}
|
||||
.merge-lab-merge-buttons-notice {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3162
client/lib/package-lock.json
generated
3162
client/lib/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@dannadori/voice-changer-client-js",
|
||||
"version": "1.0.140",
|
||||
"version": "1.0.143",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"directories": {
|
||||
@ -28,9 +28,9 @@
|
||||
"devDependencies": {
|
||||
"@types/audioworklet": "^0.0.46",
|
||||
"@types/node": "^20.2.5",
|
||||
"@types/react": "18.2.7",
|
||||
"@types/react": "18.2.9",
|
||||
"@types/react-dom": "18.2.4",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint": "^8.42.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
@ -40,14 +40,14 @@
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^5.0.1",
|
||||
"ts-loader": "^9.4.3",
|
||||
"typescript": "^5.0.4",
|
||||
"webpack": "^5.85.0",
|
||||
"webpack-cli": "^5.1.1",
|
||||
"webpack-dev-server": "^4.15.0"
|
||||
"typescript": "^5.1.3",
|
||||
"webpack": "^5.86.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/readable-stream": "^2.3.15",
|
||||
"amazon-chime-sdk-js": "^2.7.0",
|
||||
"amazon-chime-sdk-js": "^3.14.1",
|
||||
"buffer": "^6.0.3",
|
||||
"localforage": "^1.10.0",
|
||||
"react": "^18.2.0",
|
||||
|
@ -144,6 +144,8 @@ export class VoiceChangerClient {
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
this.vcInNode.stop()
|
||||
await this.unlock(lockNum)
|
||||
throw e
|
||||
}
|
||||
// this.currentMediaStream.getAudioTracks().forEach((x) => {
|
||||
@ -219,8 +221,7 @@ export class VoiceChangerClient {
|
||||
this.configurator.setServerUrl(url)
|
||||
}
|
||||
|
||||
updateClientSetting = (setting: VoiceChangerClientSetting) => {
|
||||
console.log(`[VoiceChangerClient] Updating Client Setting,`, this.setting, setting)
|
||||
updateClientSetting = async (setting: VoiceChangerClientSetting) => {
|
||||
let reconstructInputRequired = false
|
||||
if (
|
||||
this.setting.audioInput != setting.audioInput ||
|
||||
@ -241,7 +242,7 @@ export class VoiceChangerClient {
|
||||
|
||||
this.setting = setting
|
||||
if (reconstructInputRequired) {
|
||||
this.setup()
|
||||
await this.setup()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,8 +45,15 @@ export const useClientSetting = (props: UseClientSettingProps): ClientSettingSta
|
||||
}, [])
|
||||
// 初期化 その2 クライアントに設定
|
||||
useEffect(() => {
|
||||
if (!props.voiceChangerClient) return
|
||||
props.voiceChangerClient.updateClientSetting(clientSetting)
|
||||
const initialSetup = async () => {
|
||||
if (!props.voiceChangerClient) return
|
||||
try {
|
||||
await props.voiceChangerClient.updateClientSetting(clientSetting)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
initialSetup()
|
||||
}, [props.voiceChangerClient])
|
||||
|
||||
|
||||
@ -67,14 +74,14 @@ export const useClientSetting = (props: UseClientSettingProps): ClientSettingSta
|
||||
// 設定
|
||||
/////////////
|
||||
const updateClientSetting = useMemo(() => {
|
||||
return (_clientSetting: VoiceChangerClientSetting) => {
|
||||
return async (_clientSetting: VoiceChangerClientSetting) => {
|
||||
if (!props.voiceChangerClient) return
|
||||
for (let k in _clientSetting) {
|
||||
const cur_v = clientSetting[k as keyof VoiceChangerClientSetting]
|
||||
const new_v = _clientSetting[k as keyof VoiceChangerClientSetting]
|
||||
if (cur_v != new_v) {
|
||||
storeSetting(_clientSetting)
|
||||
props.voiceChangerClient.updateClientSetting(_clientSetting)
|
||||
await props.voiceChangerClient.updateClientSetting(_clientSetting)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -75,8 +75,7 @@ class MMVC_Rest_Fileuploader:
|
||||
def post_update_settings(
|
||||
self, key: str = Form(...), val: Union[int, str, float] = Form(...)
|
||||
):
|
||||
# print("[Voice Changer] update configuration:", key, val)
|
||||
print("post_update_settings", key, type(val))
|
||||
print("[Voice Changer] update configuration:", key, val)
|
||||
info = self.voiceChangerManager.update_settings(key, val)
|
||||
json_compatible_item_data = jsonable_encoder(info)
|
||||
return JSONResponse(content=json_compatible_item_data)
|
||||
|
@ -125,16 +125,19 @@ class RVC:
|
||||
if sampleInfo is None:
|
||||
print("[Voice Changer] sampleInfo is None")
|
||||
return
|
||||
modelPath, indexPath = downloadModelFiles(sampleInfo, useIndex)
|
||||
modelPath, indexPath, iconPath = downloadModelFiles(sampleInfo, useIndex)
|
||||
slotInfo.modelFile = modelPath
|
||||
if indexPath is not None:
|
||||
slotInfo.indexFile = indexPath
|
||||
if iconPath is not None:
|
||||
slotInfo.iconFile = iconPath
|
||||
|
||||
slotInfo.sampleId = sampleInfo.id
|
||||
slotInfo.credit = sampleInfo.credit
|
||||
slotInfo.description = sampleInfo.description
|
||||
slotInfo.name = sampleInfo.name
|
||||
slotInfo.termsOfUseUrl = sampleInfo.termsOfUseUrl
|
||||
|
||||
# slotInfo.samplingRate = sampleInfo.sampleRate
|
||||
# slotInfo.modelType = sampleInfo.modelType
|
||||
# slotInfo.f0 = sampleInfo.f0
|
||||
@ -163,6 +166,8 @@ class RVC:
|
||||
slotInfo.modelFile = self.moveToModelDir(slotInfo.modelFile, slotDir)
|
||||
if slotInfo.indexFile is not None and len(slotInfo.indexFile) > 0:
|
||||
slotInfo.indexFile = self.moveToModelDir(slotInfo.indexFile, slotDir)
|
||||
if slotInfo.iconFile is not None and len(slotInfo.iconFile) > 0:
|
||||
slotInfo.iconFile = self.moveToModelDir(slotInfo.iconFile, slotDir)
|
||||
json.dump(asdict(slotInfo), open(os.path.join(slotDir, "params.json"), "w"))
|
||||
self.loadSlots()
|
||||
|
||||
@ -197,13 +202,9 @@ class RVC:
|
||||
self.settings.modelSlots = modelSlots
|
||||
|
||||
def update_settings(self, key: str, val: int | float | str):
|
||||
print("update", key, val)
|
||||
if key in self.settings.intData:
|
||||
# 設定前処理
|
||||
print("update(int)1", type(val), type(self.settings.tran))
|
||||
val = cast(int, val)
|
||||
print("update(int)2", type(val), type(self.settings.tran))
|
||||
print("update(int)3", key, val)
|
||||
if key == "modelSlotIndex":
|
||||
if val < 0:
|
||||
return True
|
||||
@ -217,9 +218,7 @@ class RVC:
|
||||
self.prepareModel(val)
|
||||
|
||||
# 設定
|
||||
print("update(int)4", key, val)
|
||||
setattr(self.settings, key, val)
|
||||
print("update(int)5", type(val), type(self.settings.tran))
|
||||
|
||||
if key == "gpu":
|
||||
self.deviceManager.setForceTensor(False)
|
||||
@ -228,7 +227,6 @@ class RVC:
|
||||
elif key in self.settings.floatData:
|
||||
setattr(self.settings, key, float(val))
|
||||
elif key in self.settings.strData:
|
||||
print("update(str)", key, val)
|
||||
setattr(self.settings, key, str(val))
|
||||
if key == "f0Detector" and self.pipeline is not None:
|
||||
pitchExtractor = PitchExtractorManager.getPitchExtractor(
|
||||
|
@ -156,8 +156,19 @@ def downloadModelFiles(sampleInfo: RVCModelSample, useIndex: bool = True):
|
||||
}
|
||||
)
|
||||
|
||||
iconPath = None
|
||||
if hasattr(sampleInfo, "icon") and sampleInfo.icon != "":
|
||||
iconPath = os.path.join(TMP_DIR, os.path.basename(sampleInfo.icon))
|
||||
downloadParams.append(
|
||||
{
|
||||
"url": sampleInfo.icon,
|
||||
"saveTo": iconPath,
|
||||
"position": 2,
|
||||
}
|
||||
)
|
||||
|
||||
print("[Voice Changer] Downloading model files...", end="")
|
||||
with ThreadPoolExecutor() as pool:
|
||||
pool.map(download_no_tqdm, downloadParams)
|
||||
print("")
|
||||
return modelPath, indexPath
|
||||
return modelPath, indexPath, iconPath
|
||||
|
Loading…
x
Reference in New Issue
Block a user