mirror of
https://github.com/w-okada/voice-changer.git
synced 2025-02-02 16:23:58 +03:00
WIP: new GUI
This commit is contained in:
parent
bc5fd76cc2
commit
8c21d4e5ff
153
client/demo/dist/assets/gui_settings/RVC.json
vendored
153
client/demo/dist/assets/gui_settings/RVC.json
vendored
@ -16,70 +16,8 @@
|
|||||||
"options": {}
|
"options": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"serverControl": [
|
"serverControl": [],
|
||||||
{
|
"modelSetting": [],
|
||||||
"name": "startButton",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "performance",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "modelSwitch",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "serverOperation",
|
|
||||||
"options": {
|
|
||||||
"showDownload": true,
|
|
||||||
"showExportOnnx": true,
|
|
||||||
"showReload": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"modelSetting": [
|
|
||||||
{
|
|
||||||
"name": "modelUploaderv2",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "modelSlotRow2",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "commonFileSelect",
|
|
||||||
"options": {
|
|
||||||
"title": "Model(.onnx, .pt, pth)",
|
|
||||||
"acceptExtentions": ["onnx", "pth", "pt"],
|
|
||||||
"fileKind": "rvcModel"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "commonFileSelect",
|
|
||||||
"options": {
|
|
||||||
"title": "index(.index)",
|
|
||||||
"acceptExtentions": ["index"],
|
|
||||||
"fileKind": "rvcIndex"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "sampleModelSelect",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "sampleDownloadControlRow",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "defaultTuneRow2",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "modelUploadButtonRow2",
|
|
||||||
"options": {}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lab": [
|
"lab": [
|
||||||
{
|
{
|
||||||
"name": "mergeLab",
|
"name": "mergeLab",
|
||||||
@ -87,72 +25,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"deviceSetting": [
|
"deviceSetting": [],
|
||||||
{
|
"qualityControl": [],
|
||||||
"name": "audioDeviceMode",
|
"speakerSetting": [],
|
||||||
"options": {}
|
"converterSetting": [],
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "audioInput",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "audioOutput",
|
|
||||||
"options": {}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"qualityControl": [
|
|
||||||
{
|
|
||||||
"name": "noiseControl",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "gainControl",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "f0Detector",
|
|
||||||
"options": {
|
|
||||||
"detectors": ["dio", "harvest", "crepe"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "divider",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "analyzer",
|
|
||||||
"options": {}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"speakerSetting": [
|
|
||||||
{
|
|
||||||
"name": "tune",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "indexRatio",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "silentThreshold",
|
|
||||||
"options": {}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"converterSetting": [
|
|
||||||
{
|
|
||||||
"name": "inputChunkNum",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "extraDataLength",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "gpu",
|
|
||||||
"options": {}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"advancedSetting": [
|
"advancedSetting": [
|
||||||
{
|
{
|
||||||
"name": "protocol",
|
"name": "protocol",
|
||||||
@ -182,6 +58,23 @@
|
|||||||
"name": "protect",
|
"name": "protect",
|
||||||
"options": {}
|
"options": {}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"modelSlotControl": [
|
||||||
|
{
|
||||||
|
"name": "modelSlotArea",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "characterArea",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "configArea",
|
||||||
|
"options": {
|
||||||
|
"detectors": ["dio", "harvest", "crepe"],
|
||||||
|
"inputChunkNums": [8, 16, 24, 32, 40, 48, 64, 80, 96, 112, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024, 2048]
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
1
client/demo/dist/assets/icons/folder.svg
vendored
Normal file
1
client/demo/dist/assets/icons/folder.svg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-folder"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
After Width: | Height: | Size: 311 B |
BIN
client/demo/dist/assets/icons/human.png
vendored
Normal file
BIN
client/demo/dist/assets/icons/human.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
114
client/demo/dist/index.js
vendored
114
client/demo/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@ -16,70 +16,8 @@
|
|||||||
"options": {}
|
"options": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"serverControl": [
|
"serverControl": [],
|
||||||
{
|
"modelSetting": [],
|
||||||
"name": "startButton",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "performance",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "modelSwitch",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "serverOperation",
|
|
||||||
"options": {
|
|
||||||
"showDownload": true,
|
|
||||||
"showExportOnnx": true,
|
|
||||||
"showReload": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"modelSetting": [
|
|
||||||
{
|
|
||||||
"name": "modelUploaderv2",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "modelSlotRow2",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "commonFileSelect",
|
|
||||||
"options": {
|
|
||||||
"title": "Model(.onnx, .pt, pth)",
|
|
||||||
"acceptExtentions": ["onnx", "pth", "pt"],
|
|
||||||
"fileKind": "rvcModel"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "commonFileSelect",
|
|
||||||
"options": {
|
|
||||||
"title": "index(.index)",
|
|
||||||
"acceptExtentions": ["index"],
|
|
||||||
"fileKind": "rvcIndex"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "sampleModelSelect",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "sampleDownloadControlRow",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "defaultTuneRow2",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "modelUploadButtonRow2",
|
|
||||||
"options": {}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lab": [
|
"lab": [
|
||||||
{
|
{
|
||||||
"name": "mergeLab",
|
"name": "mergeLab",
|
||||||
@ -87,72 +25,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"deviceSetting": [
|
"deviceSetting": [],
|
||||||
{
|
"qualityControl": [],
|
||||||
"name": "audioDeviceMode",
|
"speakerSetting": [],
|
||||||
"options": {}
|
"converterSetting": [],
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "audioInput",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "audioOutput",
|
|
||||||
"options": {}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"qualityControl": [
|
|
||||||
{
|
|
||||||
"name": "noiseControl",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "gainControl",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "f0Detector",
|
|
||||||
"options": {
|
|
||||||
"detectors": ["dio", "harvest", "crepe"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "divider",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "analyzer",
|
|
||||||
"options": {}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"speakerSetting": [
|
|
||||||
{
|
|
||||||
"name": "tune",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "indexRatio",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "silentThreshold",
|
|
||||||
"options": {}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"converterSetting": [
|
|
||||||
{
|
|
||||||
"name": "inputChunkNum",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "extraDataLength",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "gpu",
|
|
||||||
"options": {}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"advancedSetting": [
|
"advancedSetting": [
|
||||||
{
|
{
|
||||||
"name": "protocol",
|
"name": "protocol",
|
||||||
@ -182,6 +58,23 @@
|
|||||||
"name": "protect",
|
"name": "protect",
|
||||||
"options": {}
|
"options": {}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"modelSlotControl": [
|
||||||
|
{
|
||||||
|
"name": "modelSlotArea",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "characterArea",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "configArea",
|
||||||
|
"options": {
|
||||||
|
"detectors": ["dio", "harvest", "crepe"],
|
||||||
|
"inputChunkNums": [8, 16, 24, 32, 40, 48, 64, 80, 96, 112, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024, 2048]
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
1
client/demo/public/assets/icons/folder.svg
Normal file
1
client/demo/public/assets/icons/folder.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-folder"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
After Width: | Height: | Size: 311 B |
BIN
client/demo/public/assets/icons/human.png
Normal file
BIN
client/demo/public/assets/icons/human.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
@ -16,6 +16,7 @@ export type AppGuiDemoSetting = {
|
|||||||
"converterSetting": GuiComponentSetting[],
|
"converterSetting": GuiComponentSetting[],
|
||||||
"advancedSetting": GuiComponentSetting[],
|
"advancedSetting": GuiComponentSetting[],
|
||||||
"lab": GuiComponentSetting[],
|
"lab": GuiComponentSetting[],
|
||||||
|
"modelSlotControl": GuiComponentSetting[],
|
||||||
},
|
},
|
||||||
dialogs: {
|
dialogs: {
|
||||||
"license": { title: string, auther: string, contact: string, url: string, license: string }[]
|
"license": { title: string, auther: string, contact: string, url: string, license: string }[]
|
||||||
@ -54,7 +55,8 @@ const InitialAppGuiDemoSetting: AppGuiDemoSetting = {
|
|||||||
"speakerSetting": [],
|
"speakerSetting": [],
|
||||||
"converterSetting": [],
|
"converterSetting": [],
|
||||||
"advancedSetting": [],
|
"advancedSetting": [],
|
||||||
"lab": []
|
"lab": [],
|
||||||
|
"modelSlotControl": []
|
||||||
},
|
},
|
||||||
dialogs: {
|
dialogs: {
|
||||||
"license": [{ title: "", auther: "", contact: "", url: "", license: "MIT" }]
|
"license": [{ title: "", auther: "", contact: "", url: "", license: "MIT" }]
|
||||||
|
@ -57,6 +57,9 @@ import { SampleModelSelectRow, SampleModelSelectRowProps } from "./components/30
|
|||||||
import { SampleDownloadControlRow, SampleDownloadControlRowProps } from "./components/301-k_SampleDownloadControl"
|
import { SampleDownloadControlRow, SampleDownloadControlRowProps } from "./components/301-k_SampleDownloadControl"
|
||||||
import { IndexRatioRow, IndexRatioRowProps } from "./components/609_IndexRatioRow copy"
|
import { IndexRatioRow, IndexRatioRowProps } from "./components/609_IndexRatioRow copy"
|
||||||
import { ProtectRow, ProtectRowProps } from "./components/610_ProtectRow"
|
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"
|
||||||
|
|
||||||
export const catalog: { [key: string]: (props: any) => JSX.Element } = {}
|
export const catalog: { [key: string]: (props: any) => JSX.Element } = {}
|
||||||
|
|
||||||
@ -152,6 +155,10 @@ const initialize = () => {
|
|||||||
|
|
||||||
|
|
||||||
addToCatalog("mergeLab", (props: MergeLabRowProps) => { return <MergeLabRow {...props} /> })
|
addToCatalog("mergeLab", (props: MergeLabRowProps) => { return <MergeLabRow {...props} /> })
|
||||||
|
addToCatalog("modelSlotArea", (props: ModelSlotAreaProps) => { return <ModelSlotArea {...props} /> })
|
||||||
|
addToCatalog("characterArea", (props: CharacterAreaProps) => { return <CharacterArea {...props} /> })
|
||||||
|
addToCatalog("configArea", (props: ConfigAreaProps) => { return <ConfigArea {...props} /> })
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from "react"
|
import React from "react"
|
||||||
import { GuiStateProvider } from "./001_GuiStateProvider";
|
import { GuiStateProvider } from "./001_GuiStateProvider";
|
||||||
import { Dialogs } from "./900_Dialogs";
|
import { Dialogs } from "./900_Dialogs";
|
||||||
import { TitleArea } from "./100_TitleArea";
|
import { TitleArea } from "./100_TitleArea";
|
||||||
@ -10,6 +10,7 @@ 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 { ModelSlotControl } from "./b00_ModelSlotControl";
|
||||||
|
|
||||||
export const Demo = () => {
|
export const Demo = () => {
|
||||||
return (
|
return (
|
||||||
@ -17,6 +18,7 @@ export const Demo = () => {
|
|||||||
<div className="main-body">
|
<div className="main-body">
|
||||||
<Dialogs />
|
<Dialogs />
|
||||||
<TitleArea />
|
<TitleArea />
|
||||||
|
<ModelSlotControl></ModelSlotControl>
|
||||||
<ServerControl />
|
<ServerControl />
|
||||||
<ModelSetting />
|
<ModelSetting />
|
||||||
<SpeakerSetting />
|
<SpeakerSetting />
|
||||||
|
@ -23,6 +23,9 @@ export const ServerControl = () => {
|
|||||||
|
|
||||||
|
|
||||||
const serverControl = useMemo(() => {
|
const serverControl = useMemo(() => {
|
||||||
|
if (componentSettings.length == 0) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const components = componentSettings.map((x, index) => {
|
const components = componentSettings.map((x, index) => {
|
||||||
const c = generateComponent(x.name, x.options)
|
const c = generateComponent(x.name, x.options)
|
||||||
return <div key={`${x.name}_${index}`}>{c}</div>
|
return <div key={`${x.name}_${index}`}>{c}</div>
|
||||||
|
@ -22,6 +22,9 @@ export const ModelSetting = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const modelSetting = useMemo(() => {
|
const modelSetting = useMemo(() => {
|
||||||
|
if (componentSettings.length == 0) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const components = componentSettings.map((x, index) => {
|
const components = componentSettings.map((x, index) => {
|
||||||
const c = generateComponent(x.name, x.options)
|
const c = generateComponent(x.name, x.options)
|
||||||
return <div key={`${x.name}_${index}`}>{c}</div>
|
return <div key={`${x.name}_${index}`}>{c}</div>
|
||||||
|
@ -22,6 +22,9 @@ export const DeviceSetting = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const deviceSetting = useMemo(() => {
|
const deviceSetting = useMemo(() => {
|
||||||
|
if (componentSettings.length == 0) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const components = componentSettings.map((x, index) => {
|
const components = componentSettings.map((x, index) => {
|
||||||
const c = generateComponent(x.name, x.options)
|
const c = generateComponent(x.name, x.options)
|
||||||
return <div key={`${x.name}_${index}`}>{c}</div>
|
return <div key={`${x.name}_${index}`}>{c}</div>
|
||||||
|
@ -22,6 +22,9 @@ export const QualityControl = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const deviceSetting = useMemo(() => {
|
const deviceSetting = useMemo(() => {
|
||||||
|
if (componentSettings.length == 0) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const components = componentSettings.map((x, index) => {
|
const components = componentSettings.map((x, index) => {
|
||||||
const c = generateComponent(x.name, x.options)
|
const c = generateComponent(x.name, x.options)
|
||||||
return <div key={`${x.name}_${index}`}>{c}</div>
|
return <div key={`${x.name}_${index}`}>{c}</div>
|
||||||
|
@ -22,6 +22,9 @@ export const SpeakerSetting = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const deviceSetting = useMemo(() => {
|
const deviceSetting = useMemo(() => {
|
||||||
|
if (componentSettings.length == 0) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const components = componentSettings.map((x, index) => {
|
const components = componentSettings.map((x, index) => {
|
||||||
const c = generateComponent(x.name, x.options)
|
const c = generateComponent(x.name, x.options)
|
||||||
return <div key={`${x.name}_${index}`}>{c}</div>
|
return <div key={`${x.name}_${index}`}>{c}</div>
|
||||||
|
@ -21,7 +21,12 @@ export const ConverterSetting = () => {
|
|||||||
return <HeaderButton {...accodionButtonProps}></HeaderButton>;
|
return <HeaderButton {...accodionButtonProps}></HeaderButton>;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const deviceSetting = useMemo(() => {
|
const deviceSetting = useMemo(() => {
|
||||||
|
if (componentSettings.length == 0) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const components = componentSettings.map((x, index) => {
|
const components = componentSettings.map((x, index) => {
|
||||||
const c = generateComponent(x.name, x.options)
|
const c = generateComponent(x.name, x.options)
|
||||||
return <div key={`${x.name}_${index}`}>{c}</div>
|
return <div key={`${x.name}_${index}`}>{c}</div>
|
||||||
|
@ -21,6 +21,9 @@ export const AdvancedSetting = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const deviceSetting = useMemo(() => {
|
const deviceSetting = useMemo(() => {
|
||||||
|
if (componentSettings.length == 0) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
const components = componentSettings.map((x, index) => {
|
const components = componentSettings.map((x, index) => {
|
||||||
const c = generateComponent(x.name, x.options)
|
const c = generateComponent(x.name, x.options)
|
||||||
return <div key={`${x.name}_${index}`}>{c}</div>
|
return <div key={`${x.name}_${index}`}>{c}</div>
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { useGuiState } from "./001_GuiStateProvider";
|
import { useGuiState } from "./001_GuiStateProvider";
|
||||||
import { getMessage } from "./messages/MessageBuilder";
|
|
||||||
import { isDesktopApp } from "../../const";
|
|
||||||
import { useAppRoot } from "../../001_provider/001_AppRootProvider";
|
|
||||||
import { useAppState } from "../../001_provider/001_AppStateProvider";
|
import { useAppState } from "../../001_provider/001_AppStateProvider";
|
||||||
import { InitialFileUploadSetting, fileSelector } from "@dannadori/voice-changer-client-js";
|
import { InitialFileUploadSetting, fileSelector } from "@dannadori/voice-changer-client-js";
|
||||||
|
|
||||||
@ -26,7 +23,6 @@ export const ModelSlotManagerDialog = () => {
|
|||||||
const [mode, setMode] = useState<Mode>("localFile")
|
const [mode, setMode] = useState<Mode>("localFile")
|
||||||
const [fromNetTargetIndex, setFromNetTargetIndex] = useState<number>(0)
|
const [fromNetTargetIndex, setFromNetTargetIndex] = useState<number>(0)
|
||||||
const [lang, setLang] = useState<string>("All")
|
const [lang, setLang] = useState<string>("All")
|
||||||
const [sampleId, setSampleId] = useState<string>("")
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
@ -36,6 +32,9 @@ export const ModelSlotManagerDialog = () => {
|
|||||||
if (mode != "localFile") {
|
if (mode != "localFile") {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
if (!serverSetting.serverSetting.modelSlots) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
const checkExtention = (filename: string, acceptExtentions: string[]) => {
|
const checkExtention = (filename: string, acceptExtentions: string[]) => {
|
||||||
const ext = filename.split('.').pop();
|
const ext = filename.split('.').pop();
|
||||||
@ -154,13 +153,13 @@ export const ModelSlotManagerDialog = () => {
|
|||||||
} : async (_index: number) => { }
|
} : async (_index: number) => { }
|
||||||
|
|
||||||
const fileValueClass = (uploadData?.slot == index) ? "model-slot-detail-row-value-edit" : isRegisterd ? "model-slot-detail-row-value-download" : "model-slot-detail-row-value"
|
const fileValueClass = (uploadData?.slot == index) ? "model-slot-detail-row-value-edit" : isRegisterd ? "model-slot-detail-row-value-download" : "model-slot-detail-row-value"
|
||||||
const fileValueAction = (uploadData?.slot == index) ? (url: string) => {
|
const fileValueAction = (uploadData?.slot == index) ? (_url: string) => {
|
||||||
} : (url: string) => {
|
} : isRegisterd ? (url: string) => {
|
||||||
const link = document.createElement("a")
|
const link = document.createElement("a")
|
||||||
link.href = url
|
link.href = url
|
||||||
link.download = url.replace(/^.*[\\\/]/, '')
|
link.download = url.replace(/^.*[\\\/]/, '')
|
||||||
link.click()
|
link.click()
|
||||||
}
|
} : (_url: string) => { }
|
||||||
|
|
||||||
const iconUrl = x.modelFile && x.modelFile.length > 0 ? (x.iconFile && x.iconFile.length > 0 ? x.iconFile : "/assets/icons/noimage.png") : "/assets/icons/blank.png"
|
const iconUrl = x.modelFile && x.modelFile.length > 0 ? (x.iconFile && x.iconFile.length > 0 ? x.iconFile : "/assets/icons/noimage.png") : "/assets/icons/blank.png"
|
||||||
|
|
||||||
|
29
client/demo/src/components/demo/b00_ModelSlotControl.tsx
Normal file
29
client/demo/src/components/demo/b00_ModelSlotControl.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React, { useMemo } from "react"
|
||||||
|
import { useAppRoot } from "../../001_provider/001_AppRootProvider"
|
||||||
|
import { generateComponent } from "./002_ComponentGenerator"
|
||||||
|
|
||||||
|
export const ModelSlotControl = () => {
|
||||||
|
const { appGuiSettingState } = useAppRoot()
|
||||||
|
const componentSettings = appGuiSettingState.appGuiSetting.front.modelSlotControl
|
||||||
|
|
||||||
|
const deviceSetting = useMemo(() => {
|
||||||
|
if (!componentSettings || 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>
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="partition">
|
||||||
|
<div className="partition-content">
|
||||||
|
{components}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return deviceSetting
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
import React, { useMemo } from "react"
|
||||||
|
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||||
|
import { useGuiState } from "../001_GuiStateProvider"
|
||||||
|
|
||||||
|
export type ModelSlotAreaProps = {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const ModelSlotArea = (_props: ModelSlotAreaProps) => {
|
||||||
|
const { serverSetting, getInfo } = useAppState()
|
||||||
|
const guiState = useGuiState()
|
||||||
|
const modelTiles = useMemo(() => {
|
||||||
|
if (!serverSetting.serverSetting.modelSlots) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return serverSetting.serverSetting.modelSlots.map((x, index) => {
|
||||||
|
if (x.modelFile.length == 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const tileContainerClass = index == serverSetting.serverSetting.modelSlotIndex ? "model-slot-tile-container-selected" : "model-slot-tile-container"
|
||||||
|
const name = x.name.length > 8 ? x.name.substring(0, 7) + "..." : x.name
|
||||||
|
const iconElem = x.iconFile.length > 0 ?
|
||||||
|
<img className="model-slot-tile-icon" src={x.iconFile} alt={x.name} /> :
|
||||||
|
<div className="model-slot-tile-icon-no-entry">no entry.</div>
|
||||||
|
|
||||||
|
const clickAction = async () => {
|
||||||
|
const dummyModelSlotIndex = (Math.floor(Date.now() / 1000)) * 1000 + index
|
||||||
|
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, modelSlotIndex: dummyModelSlotIndex })
|
||||||
|
setTimeout(() => { // quick hack
|
||||||
|
getInfo()
|
||||||
|
}, 1000 * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={index} className={tileContainerClass} onClick={clickAction}>
|
||||||
|
<div className="model-slot-tile-icon-div">
|
||||||
|
{iconElem}
|
||||||
|
</div>
|
||||||
|
<div className="model-slot-tile-dscription">
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
</div >
|
||||||
|
)
|
||||||
|
}).filter(x => x != null)
|
||||||
|
}, [serverSetting.serverSetting.modelSlots, serverSetting.serverSetting.modelSlotIndex])
|
||||||
|
|
||||||
|
|
||||||
|
const modelSlotArea = useMemo(() => {
|
||||||
|
const onModelSlotEditClicked = () => {
|
||||||
|
guiState.stateControls.showModelSlotManagerCheckbox.updateState(true)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="model-slot-area">
|
||||||
|
<div className="model-slot-panel">
|
||||||
|
<div className="model-slot-tiles-container">{modelTiles}</div>
|
||||||
|
<div className="model-slot-buttons">
|
||||||
|
<div className="model-slot-button" onClick={onModelSlotEditClicked}>
|
||||||
|
edit
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [modelTiles])
|
||||||
|
|
||||||
|
return modelSlotArea
|
||||||
|
}
|
@ -0,0 +1,289 @@
|
|||||||
|
import React, { useEffect, useMemo, useState } from "react"
|
||||||
|
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||||
|
import { useGuiState } from "../001_GuiStateProvider"
|
||||||
|
import { OnnxExporterInfo } from "@dannadori/voice-changer-client-js"
|
||||||
|
|
||||||
|
export type CharacterAreaProps = {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const CharacterArea = (_props: CharacterAreaProps) => {
|
||||||
|
const { serverSetting, clientSetting, initializedRef, volume, bufferingTime, performance } = useAppState()
|
||||||
|
const guiState = useGuiState()
|
||||||
|
|
||||||
|
const selected = useMemo(() => {
|
||||||
|
if (serverSetting.serverSetting.modelSlotIndex == undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return serverSetting.serverSetting.modelSlots[serverSetting.serverSetting.modelSlotIndex]
|
||||||
|
}, [serverSetting.serverSetting.modelSlotIndex, serverSetting.serverSetting.modelSlots])
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const vol = document.getElementById("status-vol") as HTMLSpanElement
|
||||||
|
const buf = document.getElementById("status-buf") as HTMLSpanElement
|
||||||
|
const res = document.getElementById("status-res") as HTMLSpanElement
|
||||||
|
if (!vol || !buf || !res) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vol.innerText = volume.toFixed(4)
|
||||||
|
buf.innerText = bufferingTime.toString()
|
||||||
|
res.innerText = performance.responseTime.toString()
|
||||||
|
|
||||||
|
}, [volume, bufferingTime, performance])
|
||||||
|
|
||||||
|
const portrait = useMemo(() => {
|
||||||
|
if (!selected) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
const icon = selected.iconFile.length > 0 ? selected.iconFile : "./assets/icons/human.png"
|
||||||
|
const selectedTermOfUseUrlLink = selected.termsOfUseUrl ? <a href={selected.termsOfUseUrl} target="_blank" rel="noopener noreferrer" className="portrait-area-terms-of-use-link">[terms of use]</a> : <></>
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="portrait-area">
|
||||||
|
<div className="portrait-container">
|
||||||
|
<img className="portrait" src={icon} alt={selected.name} />
|
||||||
|
<div className="portrait-area-status">
|
||||||
|
<p>vol: <span id="status-vol">0</span></p>
|
||||||
|
<p>buf: <span id="status-buf">0</span> ms</p>
|
||||||
|
<p>res: <span id="status-res">0</span> ms</p>
|
||||||
|
</div>
|
||||||
|
<div className="portrait-area-terms-of-use">
|
||||||
|
{selectedTermOfUseUrlLink}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [selected])
|
||||||
|
|
||||||
|
|
||||||
|
const [startWithAudioContextCreate, setStartWithAudioContextCreate] = useState<boolean>(false)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!startWithAudioContextCreate) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guiState.setIsConverting(true)
|
||||||
|
clientSetting.start()
|
||||||
|
}, [startWithAudioContextCreate])
|
||||||
|
|
||||||
|
const startControl = useMemo(() => {
|
||||||
|
const onStartClicked = async () => {
|
||||||
|
if (serverSetting.serverSetting.enableServerAudio == 0) {
|
||||||
|
if (!initializedRef.current) {
|
||||||
|
while (true) {
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
setTimeout(resolve, 500)
|
||||||
|
})
|
||||||
|
if (initializedRef.current) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setStartWithAudioContextCreate(true)
|
||||||
|
} else {
|
||||||
|
guiState.setIsConverting(true)
|
||||||
|
await clientSetting.start()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverAudioStated: 1 })
|
||||||
|
guiState.setIsConverting(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onStopClicked = async () => {
|
||||||
|
if (serverSetting.serverSetting.enableServerAudio == 0) {
|
||||||
|
guiState.setIsConverting(false)
|
||||||
|
await clientSetting.stop()
|
||||||
|
} else {
|
||||||
|
guiState.setIsConverting(false)
|
||||||
|
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverAudioStated: 0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const startClassName = guiState.isConverting ? "character-area-control-button-active" : "character-area-control-button-stanby"
|
||||||
|
const stopClassName = guiState.isConverting ? "character-area-control-button-stanby" : "character-area-control-button-active"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="character-area-control">
|
||||||
|
<div className="character-area-control-buttons">
|
||||||
|
<div onClick={onStartClicked} className={startClassName}>start</div>
|
||||||
|
<div onClick={onStopClicked} className={stopClassName}>stop</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [
|
||||||
|
guiState.isConverting,
|
||||||
|
clientSetting.start,
|
||||||
|
clientSetting.stop,
|
||||||
|
serverSetting.serverSetting,
|
||||||
|
serverSetting.updateServerSettings
|
||||||
|
])
|
||||||
|
|
||||||
|
const gainControl = useMemo(() => {
|
||||||
|
const currentInputGain = serverSetting.serverSetting.enableServerAudio == 0 ? clientSetting.clientSetting.inputGain : serverSetting.serverSetting.serverInputAudioGain
|
||||||
|
const inputValueUpdatedAction = serverSetting.serverSetting.enableServerAudio == 0 ?
|
||||||
|
async (val: number) => {
|
||||||
|
await clientSetting.updateClientSetting({ ...clientSetting.clientSetting, inputGain: val })
|
||||||
|
} :
|
||||||
|
async (val: number) => {
|
||||||
|
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverInputAudioGain: val })
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentOutputGain = serverSetting.serverSetting.enableServerAudio == 0 ? clientSetting.clientSetting.outputGain : serverSetting.serverSetting.serverOutputAudioGain
|
||||||
|
const outputValueUpdatedAction = serverSetting.serverSetting.enableServerAudio == 0 ?
|
||||||
|
async (val: number) => {
|
||||||
|
await clientSetting.updateClientSetting({ ...clientSetting.clientSetting, outputGain: val })
|
||||||
|
} :
|
||||||
|
async (val: number) => {
|
||||||
|
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverOutputAudioGain: val })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="character-area-control">
|
||||||
|
<div className="character-area-control-title">
|
||||||
|
GAIN:
|
||||||
|
</div>
|
||||||
|
<div className="character-area-control-field">
|
||||||
|
<div className="character-area-slider-control">
|
||||||
|
<span className="character-area-slider-control-kind">in</span>
|
||||||
|
<span className="character-area-slider-control-slider">
|
||||||
|
<input type="range" min="0.1" max="10.0" step="0.1" value={currentInputGain} onChange={(e) => {
|
||||||
|
inputValueUpdatedAction(Number(e.target.value))
|
||||||
|
}}></input>
|
||||||
|
</span>
|
||||||
|
<span className="character-area-slider-control-val">{currentInputGain}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="character-area-slider-control">
|
||||||
|
<span className="character-area-slider-control-kind">out</span>
|
||||||
|
<span className="character-area-slider-control-slider">
|
||||||
|
<input type="range" min="0.1" max="10.0" step="0.1" value={currentOutputGain} onChange={(e) => {
|
||||||
|
outputValueUpdatedAction(Number(e.target.value))
|
||||||
|
}}></input>
|
||||||
|
</span>
|
||||||
|
<span className="character-area-slider-control-val">{currentOutputGain}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [serverSetting.serverSetting, clientSetting.clientSetting, clientSetting.updateClientSetting, serverSetting.updateServerSettings])
|
||||||
|
|
||||||
|
const tuningCotrol = useMemo(() => {
|
||||||
|
const currentTuning = serverSetting.serverSetting.tran
|
||||||
|
const tranValueUpdatedAction = async (val: number) => {
|
||||||
|
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, tran: val })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="character-area-control">
|
||||||
|
<div className="character-area-control-title">
|
||||||
|
TUNE:
|
||||||
|
</div>
|
||||||
|
<div className="character-area-control-field">
|
||||||
|
<div className="character-area-slider-control">
|
||||||
|
<span className="character-area-slider-control-kind"></span>
|
||||||
|
<span className="character-area-slider-control-slider">
|
||||||
|
<input type="range" min="-50" max="50" step="1" value={currentTuning} onChange={(e) => {
|
||||||
|
tranValueUpdatedAction(Number(e.target.value))
|
||||||
|
}}></input>
|
||||||
|
</span>
|
||||||
|
<span className="character-area-slider-control-val">{currentTuning}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [serverSetting.serverSetting, clientSetting.updateClientSetting])
|
||||||
|
|
||||||
|
|
||||||
|
const indexCotrol = useMemo(() => {
|
||||||
|
const currentIndexRatio = serverSetting.serverSetting.indexRatio
|
||||||
|
const indexRatioValueUpdatedAction = async (val: number) => {
|
||||||
|
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, indexRatio: val })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="character-area-control">
|
||||||
|
<div className="character-area-control-title">
|
||||||
|
INDEX:
|
||||||
|
</div>
|
||||||
|
<div className="character-area-control-field">
|
||||||
|
<div className="character-area-slider-control">
|
||||||
|
<span className="character-area-slider-control-kind"></span>
|
||||||
|
<span className="character-area-slider-control-slider">
|
||||||
|
<input type="range" min="0" max="1" step="0.1" value={currentIndexRatio} onChange={(e) => {
|
||||||
|
indexRatioValueUpdatedAction(Number(e.target.value))
|
||||||
|
}}></input>
|
||||||
|
</span>
|
||||||
|
<span className="character-area-slider-control-val">{currentIndexRatio}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [serverSetting.serverSetting, clientSetting.updateClientSetting])
|
||||||
|
|
||||||
|
|
||||||
|
const modelSlotControl = useMemo(() => {
|
||||||
|
if (!selected) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
const onUpdateDefaultClicked = async () => {
|
||||||
|
await serverSetting.updateModelDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onnxExportButtonAction = async () => {
|
||||||
|
if (guiState.isConverting) {
|
||||||
|
alert("cannot export onnx when voice conversion is enabled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("dialog")?.classList.add("dialog-container-show")
|
||||||
|
guiState.stateControls.showWaitingCheckbox.updateState(true)
|
||||||
|
const res = await serverSetting.getOnnx() as OnnxExporterInfo
|
||||||
|
const a = document.createElement("a")
|
||||||
|
a.href = res.path
|
||||||
|
a.download = res.filename;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
guiState.stateControls.showWaitingCheckbox.updateState(false)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const exportOnnx = selected.modelFile.endsWith("pth") ? (
|
||||||
|
<div className="character-area-buutton" onClick={onnxExportButtonAction}>export onnx</div>
|
||||||
|
) : <></>
|
||||||
|
return (
|
||||||
|
<div className="character-area-control">
|
||||||
|
<div className="character-area-control-title">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className="character-area-control-field">
|
||||||
|
<div className="character-area-buuttons">
|
||||||
|
<div className="character-area-buutton" onClick={onUpdateDefaultClicked}>save default</div>
|
||||||
|
{exportOnnx}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [selected, serverSetting.getOnnx, serverSetting.updateModelDefault])
|
||||||
|
|
||||||
|
const characterArea = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<div className="character-area">
|
||||||
|
{portrait}
|
||||||
|
<div className="character-area-control-area">
|
||||||
|
{startControl}
|
||||||
|
{gainControl}
|
||||||
|
{tuningCotrol}
|
||||||
|
{indexCotrol}
|
||||||
|
{modelSlotControl}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [portrait, startControl, gainControl, tuningCotrol, modelSlotControl])
|
||||||
|
|
||||||
|
return characterArea
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
import React, { useMemo } from "react"
|
||||||
|
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||||
|
import { F0Detector, } from "@dannadori/voice-changer-client-js"
|
||||||
|
|
||||||
|
export type QualityAreaProps = {
|
||||||
|
detectors: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const QualityArea = (props: QualityAreaProps) => {
|
||||||
|
const { clientSetting, serverSetting } = useAppState()
|
||||||
|
const qualityArea = useMemo(() => {
|
||||||
|
if (!serverSetting.updateServerSettings || !clientSetting.updateClientSetting || !serverSetting.serverSetting || !clientSetting.clientSetting) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="config-sub-area">
|
||||||
|
<div className="config-sub-area-control">
|
||||||
|
<div className="config-sub-area-control-title">NOISE:</div>
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<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 })
|
||||||
|
}} /> <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 })
|
||||||
|
}} /> <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 })
|
||||||
|
}} /> <span>Sup2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="config-sub-area-control">
|
||||||
|
<div className="config-sub-area-control-title">F0 Det.:</div>
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<select className="body-select" value={serverSetting.serverSetting.f0Detector} onChange={(e) => {
|
||||||
|
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, f0Detector: e.target.value as F0Detector })
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
Object.values(props.detectors).map(x => {
|
||||||
|
//@ts-ignore
|
||||||
|
return <option key={x} value={x}>{x}</option>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="config-sub-area-control">
|
||||||
|
<div className="config-sub-area-control-title">S.Thresh.:</div>
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<div className="config-sub-area-slider-control">
|
||||||
|
<span className="config-sub-area-slider-control-kind"></span>
|
||||||
|
<span className="config-sub-area-slider-control-slider">
|
||||||
|
<input type="range" className="config-sub-area-slider-control-slider" min="0.00000" max="0.001" step="0.00001" value={serverSetting.serverSetting.silentThreshold || 0} onChange={(e) => {
|
||||||
|
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, silentThreshold: Number(e.target.value) })
|
||||||
|
}}></input>
|
||||||
|
</span>
|
||||||
|
<span className="config-sub-area-slider-control-val">{serverSetting.serverSetting.silentThreshold}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [serverSetting.serverSetting, clientSetting.clientSetting, serverSetting.updateServerSettings, clientSetting.updateClientSetting])
|
||||||
|
|
||||||
|
|
||||||
|
return qualityArea
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
import React, { useMemo } from "react"
|
||||||
|
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||||
|
|
||||||
|
export type ConvertProps = {
|
||||||
|
inputChunkNums: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const ConvertArea = (props: ConvertProps) => {
|
||||||
|
const { clientSetting, serverSetting, workletNodeSetting } = useAppState()
|
||||||
|
|
||||||
|
const convertArea = useMemo(() => {
|
||||||
|
let nums: number[]
|
||||||
|
if (!props.inputChunkNums) {
|
||||||
|
nums = [8, 16, 24, 32, 40, 48, 64, 80, 96, 112, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024, 2048]
|
||||||
|
} else {
|
||||||
|
nums = props.inputChunkNums
|
||||||
|
}
|
||||||
|
|
||||||
|
const gpusEntry = [...serverSetting.serverSetting.gpus]
|
||||||
|
gpusEntry.push({
|
||||||
|
id: -1,
|
||||||
|
name: "cpu",
|
||||||
|
memory: 0
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<div className="config-sub-area">
|
||||||
|
<div className="config-sub-area-control">
|
||||||
|
<div className="config-sub-area-control-title">CHUNK:</div>
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<select className="body-select" value={workletNodeSetting.workletNodeSetting.inputChunkNum} onChange={(e) => {
|
||||||
|
workletNodeSetting.updateWorkletNodeSetting({ ...workletNodeSetting.workletNodeSetting, inputChunkNum: Number(e.target.value) })
|
||||||
|
workletNodeSetting.trancateBuffer()
|
||||||
|
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverReadChunkSize: Number(e.target.value) })
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
nums.map(x => {
|
||||||
|
return <option key={x} value={x}>{x} ({(x * 128 * 1000 / 48000).toFixed(1)} ms, {x * 128})</option>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="config-sub-area-control">
|
||||||
|
<div className="config-sub-area-control-title">EXTRA:</div>
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<select className="body-select" value={serverSetting.serverSetting.extraConvertSize} onChange={(e) => {
|
||||||
|
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, extraConvertSize: Number(e.target.value) })
|
||||||
|
workletNodeSetting.trancateBuffer()
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
[1024 * 4, 1024 * 8, 1024 * 16, 1024 * 32, 1024 * 64, 1024 * 128].map(x => {
|
||||||
|
return <option key={x} value={x}>{x}</option>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="config-sub-area-control">
|
||||||
|
<div className="config-sub-area-control-title">GPU:</div>
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<select className="body-select" value={serverSetting.serverSetting.gpu} onChange={(e) => {
|
||||||
|
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, gpu: Number(e.target.value) })
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
gpusEntry.map(x => {
|
||||||
|
return <option key={x.id} value={x.id}>{x.name}({(x.memory / 1024 / 1024 / 1024).toFixed(0)}GB) </option>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [serverSetting.serverSetting, clientSetting.clientSetting, workletNodeSetting.workletNodeSetting, serverSetting.updateServerSettings, clientSetting.updateClientSetting, workletNodeSetting.updateWorkletNodeSetting])
|
||||||
|
|
||||||
|
|
||||||
|
return convertArea
|
||||||
|
}
|
416
client/demo/src/components/demo/components2/102-3_DeviceArea.tsx
Normal file
416
client/demo/src/components/demo/components2/102-3_DeviceArea.tsx
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
import React, { useEffect, useMemo, useRef, useState } from "react"
|
||||||
|
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||||
|
import { fileSelectorAsDataURL, useIndexedDB, } from "@dannadori/voice-changer-client-js"
|
||||||
|
import { useGuiState } from "../001_GuiStateProvider"
|
||||||
|
import { AUDIO_ELEMENT_FOR_PLAY_RESULT, AUDIO_ELEMENT_FOR_TEST_CONVERTED, AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK, AUDIO_ELEMENT_FOR_TEST_ORIGINAL, INDEXEDDB_KEY_AUDIO_OUTPUT } from "../../../const"
|
||||||
|
import { useAppRoot } from "../../../001_provider/001_AppRootProvider"
|
||||||
|
|
||||||
|
export type DeviceAreaProps = {
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||||
|
const { clientSetting, serverSetting, workletNodeSetting, audioContext, setAudioOutputElementId, initializedRef } = useAppState()
|
||||||
|
const { isConverting, audioInputForGUI, inputAudioDeviceInfo, setAudioInputForGUI, fileInputEchoback, setFileInputEchoback, setAudioOutputForGUI, audioOutputForGUI, outputAudioDeviceInfo } = useGuiState()
|
||||||
|
const [hostApi, setHostApi] = useState<string>("ALL")
|
||||||
|
const audioSrcNode = useRef<MediaElementAudioSourceNode>()
|
||||||
|
const { appGuiSettingState } = useAppRoot()
|
||||||
|
const clientType = appGuiSettingState.appGuiSetting.id
|
||||||
|
const { getItem, setItem } = useIndexedDB({ clientType: clientType })
|
||||||
|
const [outputRecordingStarted, setOutputRecordingStarted] = useState<boolean>(false)
|
||||||
|
|
||||||
|
// (1) Audio Mode
|
||||||
|
const deviceModeRow = useMemo(() => {
|
||||||
|
const enableServerAudio = serverSetting.serverSetting.enableServerAudio
|
||||||
|
const clientChecked = enableServerAudio == 1 ? false : true
|
||||||
|
const serverChecked = enableServerAudio == 1 ? true : false
|
||||||
|
|
||||||
|
const onDeviceModeChanged = (val: number) => {
|
||||||
|
if (isConverting) {
|
||||||
|
alert("cannot change mode when voice conversion is enabled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, enableServerAudio: val })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="config-sub-area-control">
|
||||||
|
<div className="config-sub-area-control-title">AUDIO:</div>
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<div className="config-sub-area-noise-container">
|
||||||
|
<div className="config-sub-area-noise-checkbox-container">
|
||||||
|
<input type="radio" id="client-device" name="device-mode" checked={clientChecked} onChange={() => { onDeviceModeChanged(0) }} /> <label htmlFor="client-device">client</label>
|
||||||
|
</div>
|
||||||
|
<div className="config-sub-area-noise-checkbox-container">
|
||||||
|
<input className="left-padding-1" type="radio" id="server-device" name="device-mode" checked={serverChecked} onChange={() => { onDeviceModeChanged(1) }} />
|
||||||
|
<label htmlFor="server-device">server</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [serverSetting.serverSetting, serverSetting.updateServerSettings])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// (2) Audio Input
|
||||||
|
// キャッシュの設定は反映(たぶん、設定操作の時も起動していしまう。が問題は起こらないはず)
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof clientSetting.clientSetting.audioInput == "string") {
|
||||||
|
if (inputAudioDeviceInfo.find(x => {
|
||||||
|
// console.log("COMPARE:", x.deviceId, appState.clientSetting.setting.audioInput)
|
||||||
|
return x.deviceId == clientSetting.clientSetting.audioInput
|
||||||
|
})) {
|
||||||
|
setAudioInputForGUI(clientSetting.clientSetting.audioInput)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [inputAudioDeviceInfo, clientSetting.clientSetting.audioInput])
|
||||||
|
|
||||||
|
// (2-1) クライアント
|
||||||
|
const clientAudioInputRow = useMemo(() => {
|
||||||
|
if (serverSetting.serverSetting.enableServerAudio == 1) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<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) => {
|
||||||
|
setAudioInputForGUI(e.target.value)
|
||||||
|
if (audioInputForGUI != "file") {
|
||||||
|
clientSetting.updateClientSetting({ ...clientSetting.clientSetting, audioInput: e.target.value })
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
inputAudioDeviceInfo.map(x => {
|
||||||
|
return <option key={x.deviceId} value={x.deviceId}>{x.label}</option>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [clientSetting.updateClientSetting, clientSetting.clientSetting, inputAudioDeviceInfo, audioInputForGUI, serverSetting.serverSetting.enableServerAudio])
|
||||||
|
|
||||||
|
|
||||||
|
// (2-2) サーバ
|
||||||
|
const serverAudioInputRow = useMemo(() => {
|
||||||
|
if (serverSetting.serverSetting.enableServerAudio == 0) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
const devices = serverSetting.serverSetting.serverAudioInputDevices
|
||||||
|
const hostAPIs = new Set(devices.map(x => { return x.hostAPI }))
|
||||||
|
const hostAPIOptions = Array.from(hostAPIs).map((x, index) => { return <option value={x} key={index} >{x}</option> })
|
||||||
|
|
||||||
|
const filteredDevice = devices.map((x, index) => {
|
||||||
|
if (hostApi != "ALL" && x.hostAPI != hostApi) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return <option value={x.index} key={index}>[{x.hostAPI}]{x.name}</option>
|
||||||
|
}).filter(x => x != null)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<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">
|
||||||
|
<div className="config-sub-area-control-field-auido-io">
|
||||||
|
<select className="config-sub-area-control-field-auido-io-filter" name="kinds" id="kinds" value={hostApi} onChange={(e) => { setHostApi(e.target.value) }}>
|
||||||
|
{hostAPIOptions}
|
||||||
|
</select>
|
||||||
|
<select className="config-sub-area-control-field-auido-io-select" value={serverSetting.serverSetting.serverInputDeviceId} onChange={(e) => {
|
||||||
|
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverInputDeviceId: Number(e.target.value) })
|
||||||
|
|
||||||
|
}}>
|
||||||
|
{filteredDevice}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
}, [clientSetting.updateClientSetting, clientSetting.clientSetting, inputAudioDeviceInfo, audioInputForGUI, serverSetting.serverSetting.enableServerAudio])
|
||||||
|
|
||||||
|
// (2-3) File
|
||||||
|
useEffect(() => {
|
||||||
|
[AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK].forEach(x => {
|
||||||
|
const audio = document.getElementById(x) as HTMLAudioElement
|
||||||
|
if (audio) {
|
||||||
|
audio.volume = fileInputEchoback ? 1 : 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [fileInputEchoback])
|
||||||
|
|
||||||
|
const audioInputMediaRow = useMemo(() => {
|
||||||
|
if (audioInputForGUI != "file" || serverSetting.serverSetting.enableServerAudio == 1) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFileLoadClicked = async () => {
|
||||||
|
const url = await fileSelectorAsDataURL("")
|
||||||
|
|
||||||
|
// input stream for client.
|
||||||
|
const audio = document.getElementById(AUDIO_ELEMENT_FOR_TEST_CONVERTED) as HTMLAudioElement
|
||||||
|
audio.pause()
|
||||||
|
audio.srcObject = null
|
||||||
|
audio.src = url
|
||||||
|
await audio.play()
|
||||||
|
if (!audioSrcNode.current) {
|
||||||
|
audioSrcNode.current = audioContext!.createMediaElementSource(audio);
|
||||||
|
}
|
||||||
|
if (audioSrcNode.current.mediaElement != audio) {
|
||||||
|
audioSrcNode.current = audioContext!.createMediaElementSource(audio);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dst = audioContext.createMediaStreamDestination()
|
||||||
|
audioSrcNode.current.connect(dst)
|
||||||
|
clientSetting.updateClientSetting({ ...clientSetting.clientSetting, audioInput: dst.stream })
|
||||||
|
|
||||||
|
const audio_echo = document.getElementById(AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) as HTMLAudioElement
|
||||||
|
audio_echo.srcObject = dst.stream
|
||||||
|
audio_echo.play()
|
||||||
|
audio_echo.volume = 0
|
||||||
|
setFileInputEchoback(false)
|
||||||
|
|
||||||
|
// original stream to play.
|
||||||
|
const audio_org = document.getElementById(AUDIO_ELEMENT_FOR_TEST_ORIGINAL) as HTMLAudioElement
|
||||||
|
audio_org.src = url
|
||||||
|
audio_org.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
const echobackClass = fileInputEchoback ? "config-sub-area-control-field-wav-file-echoback-button-active" : "config-sub-area-control-field-wav-file-echoback-button"
|
||||||
|
return (
|
||||||
|
<div className="config-sub-area-control">
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<div className="config-sub-area-control-field-wav-file left-padding-1">
|
||||||
|
<div className="config-sub-area-control-field-wav-file-audio-container">
|
||||||
|
<audio id={AUDIO_ELEMENT_FOR_TEST_ORIGINAL} controls hidden></audio>
|
||||||
|
<audio className="config-sub-area-control-field-wav-file-audio" id={AUDIO_ELEMENT_FOR_TEST_CONVERTED} controls controlsList="nodownload noplaybackrate"></audio>
|
||||||
|
<audio id={AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK} controls hidden></audio>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<img className="config-sub-area-control-field-wav-file-folder" src="./assets/icons/folder.svg" onClick={onFileLoadClicked} />
|
||||||
|
</div>
|
||||||
|
<div className={echobackClass} onClick={() => { setFileInputEchoback(!fileInputEchoback) }}>
|
||||||
|
echo{fileInputEchoback}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [audioInputForGUI, fileInputEchoback, serverSetting.serverSetting])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// (3) Audio Output
|
||||||
|
useEffect(() => {
|
||||||
|
const loadCache = async () => {
|
||||||
|
const key = await getItem(INDEXEDDB_KEY_AUDIO_OUTPUT)
|
||||||
|
if (key) {
|
||||||
|
setAudioOutputForGUI(key as string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadCache()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const setAudioOutput = async () => {
|
||||||
|
const mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices();
|
||||||
|
|
||||||
|
[AUDIO_ELEMENT_FOR_PLAY_RESULT, AUDIO_ELEMENT_FOR_TEST_ORIGINAL, AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK].forEach(x => {
|
||||||
|
const audio = document.getElementById(x) as HTMLAudioElement
|
||||||
|
if (audio) {
|
||||||
|
if (serverSetting.serverSetting.enableServerAudio == 1) {
|
||||||
|
|
||||||
|
// Server Audio を使う場合はElementから音は出さない。
|
||||||
|
audio.volume = 0
|
||||||
|
} else if (audioOutputForGUI == "none") {
|
||||||
|
// @ts-ignore
|
||||||
|
audio.setSinkId("")
|
||||||
|
if (x == AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) {
|
||||||
|
audio.volume = 0
|
||||||
|
} else {
|
||||||
|
audio.volume = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const audioOutputs = mediaDeviceInfos.filter(x => { return x.kind == "audiooutput" })
|
||||||
|
const found = audioOutputs.some(x => { return x.deviceId == audioOutputForGUI })
|
||||||
|
if (found) {
|
||||||
|
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
||||||
|
audio.setSinkId(audioOutputForGUI)
|
||||||
|
} else {
|
||||||
|
console.warn("No audio output device. use default")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x == AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) {
|
||||||
|
audio.volume = fileInputEchoback ? 1 : 0
|
||||||
|
} else {
|
||||||
|
audio.volume = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setAudioOutput()
|
||||||
|
}, [audioOutputForGUI, fileInputEchoback, serverSetting.serverSetting.enableServerAudio])
|
||||||
|
|
||||||
|
|
||||||
|
// (3-1) クライアント
|
||||||
|
const clientAudioOutputRow = useMemo(() => {
|
||||||
|
if (serverSetting.serverSetting.enableServerAudio == 1) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="config-sub-area-control">
|
||||||
|
<div className="config-sub-area-control-title left-padding-1">output</div>
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<select className="body-select" value={audioOutputForGUI} onChange={(e) => {
|
||||||
|
setAudioOutputForGUI(e.target.value)
|
||||||
|
setItem(INDEXEDDB_KEY_AUDIO_OUTPUT, e.target.value)
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
outputAudioDeviceInfo.map(x => {
|
||||||
|
return <option key={x.deviceId} value={x.deviceId}>{x.label}</option>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [serverSetting.serverSetting.enableServerAudio, outputAudioDeviceInfo, audioOutputForGUI])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("initializedRef.current", initializedRef.current)
|
||||||
|
setAudioOutputElementId(AUDIO_ELEMENT_FOR_PLAY_RESULT)
|
||||||
|
}, [initializedRef.current])
|
||||||
|
|
||||||
|
// (3-2) サーバ
|
||||||
|
const serverAudioOutputRow = useMemo(() => {
|
||||||
|
if (serverSetting.serverSetting.enableServerAudio == 0) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
const devices = serverSetting.serverSetting.serverAudioOutputDevices
|
||||||
|
const hostAPIs = new Set(devices.map(x => { return x.hostAPI }))
|
||||||
|
const hostAPIOptions = Array.from(hostAPIs).map((x, index) => { return <option value={x} key={index} >{x}</option> })
|
||||||
|
|
||||||
|
// const filteredDevice = devices.filter(x => { return x.hostAPI == hostApi || hostApi == "" }).map((x, index) => { return <option value={x.index} key={index}>{x.name}</option> })
|
||||||
|
const filteredDevice = devices.map((x, index) => {
|
||||||
|
const className = (x.hostAPI == hostApi || hostApi == "") ? "select-option-red" : ""
|
||||||
|
return <option className={className} value={x.index} key={index}>[{x.hostAPI}]{x.name}</option>
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="config-sub-area-control">
|
||||||
|
<div className="config-sub-area-control-title left-padding-1">output</div>
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<div className="config-sub-area-control-field-auido-io">
|
||||||
|
<select className="config-sub-area-control-field-auido-io-filter" name="kinds" id="kinds" value={hostApi} onChange={(e) => { setHostApi(e.target.value) }}>
|
||||||
|
{hostAPIOptions}
|
||||||
|
</select>
|
||||||
|
<select className="config-sub-area-control-field-auido-io-select" value={serverSetting.serverSetting.serverOutputDeviceId} onChange={(e) => {
|
||||||
|
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverOutputDeviceId: Number(e.target.value) })
|
||||||
|
|
||||||
|
}}>
|
||||||
|
{filteredDevice}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [hostApi, serverSetting.serverSetting, serverSetting.updateServerSettings])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// (4) レコーダー
|
||||||
|
const outputRecorderRow = useMemo(() => {
|
||||||
|
const onOutputRecordStartClicked = async () => {
|
||||||
|
setOutputRecordingStarted(true)
|
||||||
|
await workletNodeSetting.startOutputRecording()
|
||||||
|
}
|
||||||
|
const onOutputRecordStopClicked = async () => {
|
||||||
|
setOutputRecordingStarted(false)
|
||||||
|
const record = await workletNodeSetting.stopOutputRecording()
|
||||||
|
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"
|
||||||
|
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 onClick={onOutputRecordStartClicked} className={startClassName}>start</div>
|
||||||
|
<div onClick={onOutputRecordStopClicked} className={stopClassName}>stop</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
}, [outputRecordingStarted, workletNodeSetting])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="config-sub-area">
|
||||||
|
{deviceModeRow}
|
||||||
|
{clientAudioInputRow}
|
||||||
|
{serverAudioInputRow}
|
||||||
|
{audioInputMediaRow}
|
||||||
|
{clientAudioOutputRow}
|
||||||
|
{serverAudioOutputRow}
|
||||||
|
|
||||||
|
{outputRecorderRow}
|
||||||
|
<audio hidden id={AUDIO_ELEMENT_FOR_PLAY_RESULT}></audio>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const downloadRecord = (data: Float32Array) => {
|
||||||
|
|
||||||
|
const writeString = (view: DataView, offset: number, string: string) => {
|
||||||
|
for (var i = 0; i < string.length; i++) {
|
||||||
|
view.setUint8(offset + i, string.charCodeAt(i));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const floatTo16BitPCM = (output: DataView, offset: number, input: Float32Array) => {
|
||||||
|
for (var i = 0; i < input.length; i++, offset += 2) {
|
||||||
|
var s = Math.max(-1, Math.min(1, input[i]));
|
||||||
|
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const buffer = new ArrayBuffer(44 + data.length * 2);
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
|
||||||
|
// https://www.youfit.co.jp/archives/1418
|
||||||
|
writeString(view, 0, 'RIFF'); // RIFFヘッダ
|
||||||
|
view.setUint32(4, 32 + data.length * 2, true); // これ以降のファイルサイズ
|
||||||
|
writeString(view, 8, 'WAVE'); // WAVEヘッダ
|
||||||
|
writeString(view, 12, 'fmt '); // fmtチャンク
|
||||||
|
view.setUint32(16, 16, true); // fmtチャンクのバイト数
|
||||||
|
view.setUint16(20, 1, true); // フォーマットID
|
||||||
|
view.setUint16(22, 1, true); // チャンネル数
|
||||||
|
view.setUint32(24, 48000, true); // サンプリングレート
|
||||||
|
view.setUint32(28, 48000 * 2, true); // データ速度
|
||||||
|
view.setUint16(32, 2, true); // ブロックサイズ
|
||||||
|
view.setUint16(34, 16, true); // サンプルあたりのビット数
|
||||||
|
writeString(view, 36, 'data'); // dataチャンク
|
||||||
|
view.setUint32(40, data.length * 2, true); // 波形データのバイト数
|
||||||
|
floatTo16BitPCM(view, 44, data); // 波形データ
|
||||||
|
const audioBlob = new Blob([view], { type: 'audio/wav' });
|
||||||
|
|
||||||
|
const url = URL.createObjectURL(audioBlob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = `output.wav`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
import React, { useMemo, useState } from "react"
|
||||||
|
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||||
|
import { useGuiState } from "../001_GuiStateProvider"
|
||||||
|
import { AUDIO_ELEMENT_FOR_SAMPLING_INPUT, AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT } from "../../../const"
|
||||||
|
|
||||||
|
export type RecorderAreaProps = {
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RecorderArea = (_props: RecorderAreaProps) => {
|
||||||
|
const { serverSetting, workletNodeSetting } = useAppState()
|
||||||
|
const { audioOutputForAnalyzer, setAudioOutputForAnalyzer, outputAudioDeviceInfo } = useGuiState()
|
||||||
|
|
||||||
|
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 () => {
|
||||||
|
setServerIORecording(true)
|
||||||
|
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 1 })
|
||||||
|
}
|
||||||
|
const onServerIORecordStopClicked = async () => {
|
||||||
|
setServerIORecording(false)
|
||||||
|
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 0 })
|
||||||
|
|
||||||
|
// set wav (input)
|
||||||
|
const wavInput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_INPUT) as HTMLAudioElement
|
||||||
|
wavInput.src = "/tmp/in.wav?" + new Date().getTime()
|
||||||
|
wavInput.controls = true
|
||||||
|
// @ts-ignore
|
||||||
|
wavInput.setSinkId(audioOutputForAnalyzer)
|
||||||
|
|
||||||
|
// set wav (output)
|
||||||
|
const wavOutput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT) as HTMLAudioElement
|
||||||
|
wavOutput.src = "/tmp/out.wav?" + new Date().getTime()
|
||||||
|
wavOutput.controls = true
|
||||||
|
// @ts-ignore
|
||||||
|
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"
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="config-sub-area-control">
|
||||||
|
<div className="config-sub-area-control-title-long">ServerIO Analyzer</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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 onClick={onServerIORecordStartClicked} className={startClassName}>start</div>
|
||||||
|
<div onClick={onServerIORecordStopClicked} className={stopClassName}>stop</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div className="config-sub-area-control left-padding-1">
|
||||||
|
<div className="config-sub-area-control-title">dev</div>
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<div className="config-sub-area-control-field-auido-io">
|
||||||
|
<select className="body-select" value={audioOutputForAnalyzer} onChange={(e) => {
|
||||||
|
setAudioOutputForAnalyzer(e.target.value)
|
||||||
|
const wavInput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_INPUT) as HTMLAudioElement
|
||||||
|
const wavOutput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT) as HTMLAudioElement
|
||||||
|
//@ts-ignore
|
||||||
|
wavInput.setSinkId(e.target.value)
|
||||||
|
//@ts-ignore
|
||||||
|
wavOutput.setSinkId(e.target.value)
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
outputAudioDeviceInfo.map(x => {
|
||||||
|
if (x.deviceId == "none") {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return <option key={x.deviceId} value={x.deviceId}>{x.label}</option>
|
||||||
|
}).filter(x => { return x != null })
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="config-sub-area-control left-padding-1">
|
||||||
|
<div className="config-sub-area-control-title">in</div>
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<div className="config-sub-area-control-field-wav-file">
|
||||||
|
<div className="config-sub-area-control-field-wav-file-audio-container">
|
||||||
|
<audio className="config-sub-area-control-field-wav-file-audio" id={AUDIO_ELEMENT_FOR_SAMPLING_INPUT} controls></audio>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="config-sub-area-control left-padding-1">
|
||||||
|
<div className="config-sub-area-control-title">out</div>
|
||||||
|
<div className="config-sub-area-control-field">
|
||||||
|
<div className="config-sub-area-control-field-wav-file">
|
||||||
|
<div className="config-sub-area-control-field-wav-file-audio-container">
|
||||||
|
<audio className="config-sub-area-control-field-wav-file-audio" id={AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT} controls></audio>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
}, [serverIORecording, workletNodeSetting])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="config-sub-area">
|
||||||
|
{serverIORecorderRow}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
|||||||
|
import React, { useMemo } from "react"
|
||||||
|
import { QualityArea } from "./102-1_QualityArea"
|
||||||
|
import { ConvertArea } from "./102-2_ConvertArea"
|
||||||
|
import { DeviceArea } from "./102-3_DeviceArea"
|
||||||
|
import { RecorderArea } from "./102-4_RecorderArea"
|
||||||
|
|
||||||
|
export type ConfigAreaProps = {
|
||||||
|
detectors: string[]
|
||||||
|
inputChunkNums: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const ConfigArea = (props: ConfigAreaProps) => {
|
||||||
|
const configArea = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="config-area">
|
||||||
|
<QualityArea detectors={props.detectors}></QualityArea>
|
||||||
|
<ConvertArea inputChunkNums={props.inputChunkNums}></ConvertArea>
|
||||||
|
</div>
|
||||||
|
<div className="config-area">
|
||||||
|
<DeviceArea></DeviceArea>
|
||||||
|
<RecorderArea></RecorderArea>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
|
||||||
|
)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return configArea
|
||||||
|
}
|
@ -960,3 +960,341 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.model-slot-area {
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--company-color2);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
.model-slot-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 5px;
|
||||||
|
.model-slot-tiles-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 2px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
/* width: calc(30rem + 40px + 10px); */
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-slot-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
.model-slot-button {
|
||||||
|
border: solid 2px #999;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: #333;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
.model-slot-button:hover {
|
||||||
|
border: solid 2px #faa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.model-slot-tile-container,
|
||||||
|
.model-slot-tile-container-selected {
|
||||||
|
width: 6rem;
|
||||||
|
height: 6rem;
|
||||||
|
border-radius: 2px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.model-slot-tile-container-selected {
|
||||||
|
background: #43030c;
|
||||||
|
}
|
||||||
|
.model-slot-tile-container:hover {
|
||||||
|
background: #43030c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-slot-tile-icon-div {
|
||||||
|
width: 5rem;
|
||||||
|
height: 5rem;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
.model-slot-tile-icon {
|
||||||
|
width: 5rem;
|
||||||
|
height: 5rem;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.model-slot-tile-icon-no-entry {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
.model-slot-tile-dscription {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: navajowhite;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-area {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 20px;
|
||||||
|
.portrait-area {
|
||||||
|
width: 20rem;
|
||||||
|
height: 20rem;
|
||||||
|
.portrait-container {
|
||||||
|
position: relative;
|
||||||
|
width: 20rem;
|
||||||
|
height: 20rem;
|
||||||
|
.portrait {
|
||||||
|
width: 20rem;
|
||||||
|
height: 20rem;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 10px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.portrait-area-status {
|
||||||
|
width: 5rem;
|
||||||
|
background: rgba(100, 100, 100, 0.5);
|
||||||
|
color: white;
|
||||||
|
position: absolute;
|
||||||
|
paddig: 2px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
left: 5px;
|
||||||
|
top: 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.portrait-area-terms-of-use {
|
||||||
|
width: 5rem;
|
||||||
|
background: rgba(100, 100, 100, 0.5);
|
||||||
|
color: white;
|
||||||
|
position: absolute;
|
||||||
|
paddig: 2px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
right: 5px;
|
||||||
|
bottom: 5px;
|
||||||
|
.portrait-area-terms-of-use-link {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.character-area-control-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
.character-area-control {
|
||||||
|
display: flex;
|
||||||
|
gap: 3px;
|
||||||
|
.character-area-control-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 10px;
|
||||||
|
.character-area-control-button-active {
|
||||||
|
width: 5rem;
|
||||||
|
border: solid 1px #333;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: #ada;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.character-area-control-button-stanby {
|
||||||
|
width: 5rem;
|
||||||
|
border: solid 1px #999;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: #aba;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
&:hover {
|
||||||
|
border: solid 1px #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.character-area-control-title {
|
||||||
|
width: 3rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.character-area-control-field {
|
||||||
|
/* width: 20rem; */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.character-area-slider-control {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
.character-area-slider-control-kind {
|
||||||
|
width: 2rem;
|
||||||
|
}
|
||||||
|
.character-area-slider-control-slider {
|
||||||
|
width: 10rem;
|
||||||
|
}
|
||||||
|
.character-area-slider-control-val {
|
||||||
|
width: 3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.character-area-buuttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 5px;
|
||||||
|
.character-area-buutton {
|
||||||
|
border: solid 2px #999;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: #666;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
.character-area-buutton:hover {
|
||||||
|
border: solid 2px #faa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* audio::-webkit-media-controls-play-button,
|
||||||
|
audio::-webkit-media-controls-panel {
|
||||||
|
background-color: #ff0;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
audio::-webkit-media-controls-enclosure {
|
||||||
|
max-height: 1rem;
|
||||||
|
}
|
||||||
|
audio::-webkit-media-controls {
|
||||||
|
justify-content: start;
|
||||||
|
}
|
||||||
|
audio::-webkit-media-controls-overlay-enclosure{
|
||||||
|
height: 1rem;
|
||||||
|
} */
|
||||||
|
|
||||||
|
.config-area {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 20px;
|
||||||
|
.config-sub-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
.config-sub-area-control {
|
||||||
|
display: flex;
|
||||||
|
.config-sub-area-control-title {
|
||||||
|
width: 5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.config-sub-area-control-title-long {
|
||||||
|
width: 20rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.config-sub-area-control-field {
|
||||||
|
width: 15rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
.config-sub-area-noise-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
.config-sub-area-noise-checkbox-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-sub-area-slider-control {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
.config-sub-area-slider-control-kind {
|
||||||
|
width: 1rem;
|
||||||
|
}
|
||||||
|
.config-sub-area-slider-control-slider {
|
||||||
|
width: 10rem;
|
||||||
|
}
|
||||||
|
.config-sub-area-slider-control-val {
|
||||||
|
width: 3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.config-sub-area-buuttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 5px;
|
||||||
|
align-items: center;
|
||||||
|
.config-sub-area-buutton {
|
||||||
|
border: solid 2px #999;
|
||||||
|
color: white;
|
||||||
|
background: #666;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
font-size: 0.8rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 1.2rem;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 2px;
|
||||||
|
}
|
||||||
|
.config-sub-area-buutton:hover {
|
||||||
|
border: solid 2px #faa;
|
||||||
|
}
|
||||||
|
.config-sub-area-buutton-active {
|
||||||
|
border: solid 2px #999;
|
||||||
|
color: white;
|
||||||
|
background: #844;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
font-size: 0.8rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 1.2rem;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.config-sub-area-control-field-auido-io {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
.config-sub-area-control-field-auido-io-filter {
|
||||||
|
max-width: 30%;
|
||||||
|
}
|
||||||
|
.config-sub-area-control-field-auido-io-select {
|
||||||
|
max-width: 70%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.config-sub-area-control-field-wav-file {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 5px;
|
||||||
|
.config-sub-area-control-field-wav-file-audio-container {
|
||||||
|
height: 1rem;
|
||||||
|
.config-sub-area-control-field-wav-file-audio {
|
||||||
|
height: 1rem;
|
||||||
|
width: 15rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.config-sub-area-control-field-wav-file-folder {
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.config-sub-area-control-field-wav-file-echoback-button {
|
||||||
|
border: solid 1px #333;
|
||||||
|
background: #fff;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 1.2rem;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.config-sub-area-control-field-wav-file-echoback-button-active {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
border: solid 1px #333;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #ada;
|
||||||
|
height: 1.2rem;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -237,6 +237,11 @@ export type ServerInfo = VoiceChangerServerSetting & {
|
|||||||
serverAudioInputDevices: ServerAudioDevice[]
|
serverAudioInputDevices: ServerAudioDevice[]
|
||||||
serverAudioOutputDevices: ServerAudioDevice[]
|
serverAudioOutputDevices: ServerAudioDevice[]
|
||||||
sampleModels: RVCSampleModel[]
|
sampleModels: RVCSampleModel[]
|
||||||
|
gpus: {
|
||||||
|
id: number,
|
||||||
|
name: string,
|
||||||
|
memory: number,
|
||||||
|
}[]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,6 +313,7 @@ export const DefaultServerSetting: ServerInfo = {
|
|||||||
silenceFront: 1,
|
silenceFront: 1,
|
||||||
modelSlotIndex: 0,
|
modelSlotIndex: 0,
|
||||||
sampleModels: [],
|
sampleModels: [],
|
||||||
|
gpus: [],
|
||||||
|
|
||||||
useEnhancer: 0,
|
useEnhancer: 0,
|
||||||
useDiff: 1,
|
useDiff: 1,
|
||||||
|
@ -159,8 +159,10 @@ export const useClient = (props: UseClientProps): ClientState => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const audio = document.getElementById(elemId) as HTMLAudioElement
|
const audio = document.getElementById(elemId) as HTMLAudioElement
|
||||||
audio.srcObject = voiceChangerClientRef.current.stream
|
if (audio.paused) {
|
||||||
audio.play()
|
audio.srcObject = voiceChangerClientRef.current.stream
|
||||||
|
audio.play()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// (2-2) 情報リロード
|
// (2-2) 情報リロード
|
||||||
|
@ -76,6 +76,7 @@ class MMVC_Rest_Fileuploader:
|
|||||||
self, key: str = Form(...), val: Union[int, str, float] = Form(...)
|
self, key: str = Form(...), val: Union[int, str, float] = Form(...)
|
||||||
):
|
):
|
||||||
# print("[Voice Changer] update configuration:", key, val)
|
# print("[Voice Changer] update configuration:", key, val)
|
||||||
|
print("post_update_settings", key, type(val))
|
||||||
info = self.voiceChangerManager.update_settings(key, val)
|
info = self.voiceChangerManager.update_settings(key, val)
|
||||||
json_compatible_item_data = jsonable_encoder(info)
|
json_compatible_item_data = jsonable_encoder(info)
|
||||||
return JSONResponse(content=json_compatible_item_data)
|
return JSONResponse(content=json_compatible_item_data)
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
"modelUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc_v2_alpha/tokina_shigure/tokina_shigure_v2_40k_e100_simple.onnx",
|
"modelUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc_v2_alpha/tokina_shigure/tokina_shigure_v2_40k_e100_simple.onnx",
|
||||||
"indexUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc_v2_alpha/tokina_shigure/added_IVF2736_Flat_nprobe_1_v2.index.bin",
|
"indexUrl": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc_v2_alpha/tokina_shigure/added_IVF2736_Flat_nprobe_1_v2.index.bin",
|
||||||
"termsOfUseUrl": "https://huggingface.co/wok000/vcclient_model/raw/main/rvc_v2_alpha/tokina_shigure/terms_of_use.txt",
|
"termsOfUseUrl": "https://huggingface.co/wok000/vcclient_model/raw/main/rvc_v2_alpha/tokina_shigure/terms_of_use.txt",
|
||||||
"icon": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc_v2_alpha/tokina_shigure/dummy.png",
|
"icon": "https://huggingface.co/wok000/vcclient_model/resolve/main/rvc_v2_alpha/tokina_shigure/tokina_shigure.png",
|
||||||
"credit": "刻鳴時雨",
|
"credit": "刻鳴時雨",
|
||||||
"description": "",
|
"description": "",
|
||||||
"sampleRate": 40000,
|
"sampleRate": 40000,
|
||||||
|
@ -28,6 +28,10 @@ class MMVC_SocketIOApp:
|
|||||||
"filename": f"{getFrontendPath()}/assets/icons/tool.svg",
|
"filename": f"{getFrontendPath()}/assets/icons/tool.svg",
|
||||||
"content_type": "image/svg+xml",
|
"content_type": "image/svg+xml",
|
||||||
},
|
},
|
||||||
|
"/assets/icons/folder.svg": {
|
||||||
|
"filename": f"{getFrontendPath()}/assets/icons/folder.svg",
|
||||||
|
"content_type": "image/svg+xml",
|
||||||
|
},
|
||||||
"/buymeacoffee.png": {
|
"/buymeacoffee.png": {
|
||||||
"filename": f"{getFrontendPath()}/assets/buymeacoffee.png",
|
"filename": f"{getFrontendPath()}/assets/buymeacoffee.png",
|
||||||
"content_type": "image/png",
|
"content_type": "image/png",
|
||||||
|
@ -197,9 +197,13 @@ class RVC:
|
|||||||
self.settings.modelSlots = modelSlots
|
self.settings.modelSlots = modelSlots
|
||||||
|
|
||||||
def update_settings(self, key: str, val: int | float | str):
|
def update_settings(self, key: str, val: int | float | str):
|
||||||
|
print("update", key, val)
|
||||||
if key in self.settings.intData:
|
if key in self.settings.intData:
|
||||||
# 設定前処理
|
# 設定前処理
|
||||||
|
print("update(int)1", type(val), type(self.settings.tran))
|
||||||
val = cast(int, val)
|
val = cast(int, val)
|
||||||
|
print("update(int)2", type(val), type(self.settings.tran))
|
||||||
|
print("update(int)3", key, val)
|
||||||
if key == "modelSlotIndex":
|
if key == "modelSlotIndex":
|
||||||
if val < 0:
|
if val < 0:
|
||||||
return True
|
return True
|
||||||
@ -213,7 +217,9 @@ class RVC:
|
|||||||
self.prepareModel(val)
|
self.prepareModel(val)
|
||||||
|
|
||||||
# 設定
|
# 設定
|
||||||
|
print("update(int)4", key, val)
|
||||||
setattr(self.settings, key, val)
|
setattr(self.settings, key, val)
|
||||||
|
print("update(int)5", type(val), type(self.settings.tran))
|
||||||
|
|
||||||
if key == "gpu":
|
if key == "gpu":
|
||||||
self.deviceManager.setForceTensor(False)
|
self.deviceManager.setForceTensor(False)
|
||||||
@ -222,6 +228,7 @@ class RVC:
|
|||||||
elif key in self.settings.floatData:
|
elif key in self.settings.floatData:
|
||||||
setattr(self.settings, key, float(val))
|
setattr(self.settings, key, float(val))
|
||||||
elif key in self.settings.strData:
|
elif key in self.settings.strData:
|
||||||
|
print("update(str)", key, val)
|
||||||
setattr(self.settings, key, str(val))
|
setattr(self.settings, key, str(val))
|
||||||
if key == "f0Detector" and self.pipeline is not None:
|
if key == "f0Detector" and self.pipeline is not None:
|
||||||
pitchExtractor = PitchExtractorManager.getPitchExtractor(
|
pitchExtractor = PitchExtractorManager.getPitchExtractor(
|
||||||
@ -513,7 +520,7 @@ class RVC:
|
|||||||
try:
|
try:
|
||||||
shutil.move(uploadPath, storePath)
|
shutil.move(uploadPath, storePath)
|
||||||
params = json.load(open(storeJson, "r", encoding="utf-8"))
|
params = json.load(open(storeJson, "r", encoding="utf-8"))
|
||||||
params[paramsDict["name"]] = storePath
|
params[paramsDict["name"]] = storePath # type:ignore
|
||||||
json.dump(params, open(storeJson, "w"))
|
json.dump(params, open(storeJson, "w"))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Exception::::", e)
|
print("Exception::::", e)
|
||||||
|
@ -341,6 +341,15 @@ class VoiceChanger:
|
|||||||
data = asdict(self.settings)
|
data = asdict(self.settings)
|
||||||
if self.voiceChanger is not None:
|
if self.voiceChanger is not None:
|
||||||
data.update(self.voiceChanger.get_info())
|
data.update(self.voiceChanger.get_info())
|
||||||
|
|
||||||
|
devCount = torch.cuda.device_count()
|
||||||
|
gpus = []
|
||||||
|
for id in range(devCount):
|
||||||
|
name = torch.cuda.get_device_name(id)
|
||||||
|
memory = torch.cuda.get_device_properties(id).total_memory
|
||||||
|
gpu = {"id": id, "name": name, "memory": memory}
|
||||||
|
gpus.append(gpu)
|
||||||
|
data["gpus"] = gpus
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_performance(self):
|
def get_performance(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user