WIP:VC select
This commit is contained in:
parent
bd6080c52e
commit
4cc02540ea
@ -105,10 +105,6 @@
|
||||
"name": "indexRatio",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "noiseScale",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"name": "silentThreshold",
|
||||
"options": {}
|
||||
|
39
client/demo/dist/index.js
vendored
39
client/demo/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@ -10,9 +10,10 @@ import { far } from "@fortawesome/free-regular-svg-icons";
|
||||
import { fab } from "@fortawesome/free-brands-svg-icons";
|
||||
import { AppRootProvider, useAppRoot } from "./001_provider/001_AppRootProvider";
|
||||
import ErrorBoundary from "./001_provider/900_ErrorBoundary";
|
||||
import { INDEXEDDB_KEY_CLIENT, INDEXEDDB_KEY_MODEL_DATA, INDEXEDDB_KEY_SERVER, INDEXEDDB_KEY_WORKLET, INDEXEDDB_KEY_WORKLETNODE, useIndexedDB } from "@dannadori/voice-changer-client-js";
|
||||
import { ClientType, INDEXEDDB_KEY_CLIENT, INDEXEDDB_KEY_MODEL_DATA, INDEXEDDB_KEY_SERVER, INDEXEDDB_KEY_WORKLET, INDEXEDDB_KEY_WORKLETNODE, useIndexedDB } from "@dannadori/voice-changer-client-js";
|
||||
import { CLIENT_TYPE, INDEXEDDB_KEY_AUDIO_OUTPUT } from "./const";
|
||||
import { Demo } from "./components/demo/010_Demo";
|
||||
import { ClientSelector } from "./001_ClientSelector";
|
||||
|
||||
library.add(fas, far, fab);
|
||||
|
||||
@ -21,7 +22,7 @@ const container = document.getElementById("app")!;
|
||||
const root = createRoot(container);
|
||||
|
||||
const App = () => {
|
||||
const { appGuiSettingState } = useAppRoot()
|
||||
const { appGuiSettingState, clientType } = useAppRoot()
|
||||
const front = useMemo(() => {
|
||||
if (appGuiSettingState.appGuiSetting.type == "demo") {
|
||||
return <Demo></Demo>
|
||||
@ -38,7 +39,7 @@ const App = () => {
|
||||
}
|
||||
|
||||
const AppStateWrapper = () => {
|
||||
const { appGuiSettingState } = useAppRoot()
|
||||
const { appGuiSettingState, clientType } = useAppRoot()
|
||||
// エラーバウンダリー設定
|
||||
const [error, setError] = useState<{ error: Error, errorInfo: ErrorInfo }>()
|
||||
const { removeItem } = useIndexedDB({ clientType: CLIENT_TYPE })
|
||||
@ -96,7 +97,11 @@ const AppStateWrapper = () => {
|
||||
setError({ error, errorInfo })
|
||||
}
|
||||
|
||||
if (!appGuiSettingState.guiSettingLoaded) {
|
||||
|
||||
if (!clientType) {
|
||||
return <ClientSelector></ClientSelector>
|
||||
|
||||
} else if (!appGuiSettingState.guiSettingLoaded) {
|
||||
return <></>
|
||||
} else {
|
||||
return (
|
||||
|
16
client/demo/src/001_ClientSelector.tsx
Normal file
16
client/demo/src/001_ClientSelector.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from "react"
|
||||
import { useAppRoot } from "./001_provider/001_AppRootProvider"
|
||||
|
||||
export const ClientSelector = () => {
|
||||
const { setClientType } = useAppRoot()
|
||||
return (
|
||||
<div>
|
||||
<div onClick={() => { setClientType("MMVCv13") }}>MMVCv13</div>
|
||||
<div onClick={() => { setClientType("MMVCv15") }}>MMVCv15</div>
|
||||
<div onClick={() => { setClientType("so-vits-svc-40") }}>so-vits-svc-40</div>
|
||||
<div onClick={() => { setClientType("so-vits-svc-40v2") }}>so-vits-svc-40v2</div>
|
||||
<div onClick={() => { setClientType("RVC") }}>RVC</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
@ -51,13 +51,14 @@ export type AppGuiSettingState = {
|
||||
}
|
||||
|
||||
export type AppGuiSettingStateAndMethod = AppGuiSettingState & {
|
||||
getAppSetting: (url: string) => Promise<void>
|
||||
getAppGuiSetting: (url: string) => Promise<void>
|
||||
clearAppGuiSetting: () => void
|
||||
}
|
||||
|
||||
export const userAppGuiSetting = (): AppGuiSettingStateAndMethod => {
|
||||
const [guiSettingLoaded, setGuiSettingLoaded] = useState<boolean>(false)
|
||||
const [appGuiSetting, setAppGuiSetting] = useState<AppGuiSetting>(InitialAppGuiDemoSetting)
|
||||
const getAppSetting = async (url: string) => {
|
||||
const getAppGuiSetting = async (url: string) => {
|
||||
const res = await fetch(`${url}`, {
|
||||
method: "GET",
|
||||
})
|
||||
@ -65,9 +66,15 @@ export const userAppGuiSetting = (): AppGuiSettingStateAndMethod => {
|
||||
setAppGuiSetting(appSetting)
|
||||
setGuiSettingLoaded(true)
|
||||
}
|
||||
const clearAppGuiSetting = () => {
|
||||
setAppGuiSetting(InitialAppGuiDemoSetting)
|
||||
setGuiSettingLoaded(false)
|
||||
}
|
||||
return {
|
||||
appGuiSetting,
|
||||
guiSettingLoaded,
|
||||
getAppSetting,
|
||||
getAppGuiSetting,
|
||||
clearAppGuiSetting,
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2,23 +2,30 @@ import { ClientState, useClient, ClientType } from "@dannadori/voice-changer-cli
|
||||
|
||||
export type UseVCClientProps = {
|
||||
audioContext: AudioContext | null
|
||||
clientType: ClientType
|
||||
clientType: ClientType | null
|
||||
}
|
||||
|
||||
export type VCClientState = {
|
||||
clientState: ClientState
|
||||
}
|
||||
|
||||
export const useVCClient = (props: UseVCClientProps) => {
|
||||
export const useVCClient = (props: UseVCClientProps): VCClientState => {
|
||||
const clientState = useClient({
|
||||
clientType: props.clientType,
|
||||
audioContext: props.audioContext,
|
||||
clientType: props.clientType,
|
||||
|
||||
})
|
||||
|
||||
// const setClientType = (clientType: ClientType) => {
|
||||
// console.log("SET CLIENT TYPE", clientType)
|
||||
// clientState.setClientType(clientType)
|
||||
// }
|
||||
|
||||
const ret: VCClientState = {
|
||||
clientState
|
||||
}
|
||||
|
||||
|
||||
return ret
|
||||
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import React, { useContext, useEffect } from "react";
|
||||
import { ClientType } from "@dannadori/voice-changer-client-js";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { ReactNode } from "react";
|
||||
import { AppGuiSettingStateAndMethod, userAppGuiSetting } from "../001_globalHooks/001_useAppGuiSetting";
|
||||
import { AudioConfigState, useAudioConfig } from "../001_globalHooks/001_useAudioConfig";
|
||||
@ -10,6 +11,8 @@ type Props = {
|
||||
type AppRootValue = {
|
||||
audioContextState: AudioConfigState
|
||||
appGuiSettingState: AppGuiSettingStateAndMethod
|
||||
clientType: ClientType | null
|
||||
setClientType: (val: ClientType | null) => void
|
||||
}
|
||||
|
||||
const AppRootContext = React.createContext<AppRootValue | null>(null);
|
||||
@ -24,16 +27,20 @@ export const useAppRoot = (): AppRootValue => {
|
||||
export const AppRootProvider = ({ children }: Props) => {
|
||||
const audioContextState = useAudioConfig()
|
||||
const appGuiSettingState = userAppGuiSetting()
|
||||
const [clientType, setClientType] = useState<ClientType | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const modelType = params.get("modelType") || ""
|
||||
appGuiSettingState.getAppSetting(`/assets/gui_settings/${modelType}.json`)
|
||||
}, [])
|
||||
if (!clientType) {
|
||||
return
|
||||
}
|
||||
appGuiSettingState.getAppGuiSetting(`/assets/gui_settings/${clientType}.json`)
|
||||
}, [clientType])
|
||||
|
||||
const providerValue: AppRootValue = {
|
||||
audioContextState,
|
||||
appGuiSettingState
|
||||
appGuiSettingState,
|
||||
clientType,
|
||||
setClientType
|
||||
};
|
||||
return <AppRootContext.Provider value={providerValue}>{children}</AppRootContext.Provider>;
|
||||
};
|
||||
|
@ -24,7 +24,7 @@ export const useAppState = (): AppStateValue => {
|
||||
|
||||
export const AppStateProvider = ({ children }: Props) => {
|
||||
const appRoot = useAppRoot()
|
||||
const clientState = useVCClient({ audioContext: appRoot.audioContextState.audioContext, clientType: appRoot.appGuiSettingState.appGuiSetting.id })
|
||||
const clientState = useVCClient({ audioContext: appRoot.audioContextState.audioContext, clientType: appRoot.clientType })
|
||||
|
||||
|
||||
const initializedRef = useRef<boolean>(false)
|
||||
|
@ -3,14 +3,15 @@ import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
import { useIndexedDB } from "@dannadori/voice-changer-client-js";
|
||||
import { INDEXEDDB_KEY_AUDIO_OUTPUT } from "../../../const";
|
||||
import { useAppRoot } from "../../../001_provider/001_AppRootProvider";
|
||||
|
||||
import { useGuiState } from "../001_GuiStateProvider"
|
||||
export type ClearSettingRowProps = {
|
||||
}
|
||||
|
||||
|
||||
export const ClearSettingRow = (_props: ClearSettingRowProps) => {
|
||||
const appState = useAppState()
|
||||
const { appGuiSettingState } = useAppRoot()
|
||||
const { appGuiSettingState, setClientType } = useAppRoot()
|
||||
const guiState = useGuiState()
|
||||
const clientType = appGuiSettingState.appGuiSetting.id
|
||||
const { removeItem } = useIndexedDB({ clientType: clientType })
|
||||
|
||||
@ -21,12 +22,21 @@ export const ClearSettingRow = (_props: ClearSettingRowProps) => {
|
||||
await removeItem(INDEXEDDB_KEY_AUDIO_OUTPUT)
|
||||
location.reload()
|
||||
}
|
||||
const onReselectVCClicked = async () => {
|
||||
guiState.setIsConverting(false)
|
||||
await appState.clientSetting.stop()
|
||||
setClientType(null)
|
||||
appGuiSettingState.clearAppGuiSetting()
|
||||
|
||||
}
|
||||
return (
|
||||
<div className="body-row split-3-3-4 left-padding-1">
|
||||
<div className="body-button-container">
|
||||
<div className="body-button" onClick={onClearSettingClicked}>clear setting</div>
|
||||
</div>
|
||||
<div className="body-item-text"></div>
|
||||
<div className="body-button-container">
|
||||
<div className="body-button" onClick={onReselectVCClicked}>re-select vc</div>
|
||||
</div>
|
||||
<div className="body-item-text"></div>
|
||||
</div>
|
||||
)
|
||||
|
@ -11,7 +11,7 @@ export const TuneRow = (_props: TuneRowProps) => {
|
||||
<div className="body-row split-3-2-2-3 left-padding-1 guided">
|
||||
<div className="body-item-title left-padding-1 ">Tuning</div>
|
||||
<div>
|
||||
<input type="range" className="body-item-input-slider" min="-50" max="50" step="1" value={appState.serverSetting.serverSetting.tran} onChange={(e) => {
|
||||
<input type="range" className="body-item-input-slider" min="-50" max="50" step="1" value={appState.serverSetting.serverSetting.tran || 0} onChange={(e) => {
|
||||
appState.serverSetting.updateServerSettings({ ...appState.serverSetting.serverSetting, tran: Number(e.target.value) })
|
||||
}}></input>
|
||||
<span className="body-item-input-slider-val">{appState.serverSetting.serverSetting.tran}</span>
|
||||
|
@ -12,7 +12,7 @@ export const ClusterInferRatioRow = (_props: ClusterInferRatioRowProps) => {
|
||||
<div className="body-row split-3-3-4 left-padding-1 guided">
|
||||
<div className="body-item-title left-padding-1 ">Cluster infer ratio</div>
|
||||
<div>
|
||||
<input type="range" className="body-item-input-slider" min="0" max="1" step="0.1" value={appState.serverSetting.serverSetting.clusterInferRatio} onChange={(e) => {
|
||||
<input type="range" className="body-item-input-slider" min="0" max="1" step="0.1" value={appState.serverSetting.serverSetting.clusterInferRatio || 0} onChange={(e) => {
|
||||
appState.serverSetting.updateServerSettings({ ...appState.serverSetting.serverSetting, clusterInferRatio: Number(e.target.value) })
|
||||
}}></input>
|
||||
<span className="body-item-input-slider-val">{appState.serverSetting.serverSetting.clusterInferRatio}</span>
|
||||
|
@ -12,7 +12,7 @@ export const NoiseScaleRow = (_props: NoiseScaleRowProps) => {
|
||||
<div className="body-row split-3-3-4 left-padding-1 guided">
|
||||
<div className="body-item-title left-padding-1 ">Noise Scale</div>
|
||||
<div>
|
||||
<input type="range" className="body-item-input-slider" min="0" max="1" step="0.1" value={appState.serverSetting.serverSetting.noiceScale} onChange={(e) => {
|
||||
<input type="range" className="body-item-input-slider" min="0" max="1" step="0.1" value={appState.serverSetting.serverSetting.noiceScale || 0} onChange={(e) => {
|
||||
appState.serverSetting.updateServerSettings({ ...appState.serverSetting.serverSetting, noiceScale: Number(e.target.value) })
|
||||
}}></input>
|
||||
<span className="body-item-input-slider-val">{appState.serverSetting.serverSetting.noiceScale}</span>
|
||||
|
@ -12,7 +12,7 @@ export const SilentThresholdRow = (_props: SilentThresholdRowProps) => {
|
||||
<div className="body-row split-3-3-4 left-padding-1 guided">
|
||||
<div className="body-item-title left-padding-1 ">Silent Threshold</div>
|
||||
<div>
|
||||
<input type="range" className="body-item-input-slider" min="0.00000" max="0.001" step="0.00001" value={appState.serverSetting.serverSetting.silentThreshold} onChange={(e) => {
|
||||
<input type="range" className="body-item-input-slider" min="0.00000" max="0.001" step="0.00001" value={appState.serverSetting.serverSetting.silentThreshold || 0} onChange={(e) => {
|
||||
appState.serverSetting.updateServerSettings({ ...appState.serverSetting.serverSetting, silentThreshold: Number(e.target.value) })
|
||||
}}></input>
|
||||
<span className="body-item-input-slider-val">{appState.serverSetting.serverSetting.silentThreshold}</span>
|
||||
|
@ -12,7 +12,7 @@ export const IndexRatioRow = (_props: IndexRatioRowProps) => {
|
||||
<div className="body-row split-3-3-4 left-padding-1 guided">
|
||||
<div className="body-item-title left-padding-1 ">index ratio</div>
|
||||
<div>
|
||||
<input type="range" className="body-item-input-slider" min="0" max="1" step="0.1" value={appState.serverSetting.serverSetting.indexRatio} onChange={(e) => {
|
||||
<input type="range" className="body-item-input-slider" min="0" max="1" step="0.1" value={appState.serverSetting.serverSetting.indexRatio || 0} onChange={(e) => {
|
||||
appState.serverSetting.updateServerSettings({ ...appState.serverSetting.serverSetting, indexRatio: Number(e.target.value) })
|
||||
}}></input>
|
||||
<span className="body-item-input-slider-val">{appState.serverSetting.serverSetting.indexRatio}</span>
|
||||
|
@ -10,7 +10,7 @@ export const GPURow = (_props: GPURowProps) => {
|
||||
<div className="body-row split-3-7 left-padding-1 guided">
|
||||
<div className="body-item-title left-padding-1">GPU</div>
|
||||
<div className="body-input-container">
|
||||
<input type="number" min={-2} max={5} step={1} value={appState.serverSetting.serverSetting.gpu} onChange={(e) => {
|
||||
<input type="number" min={-2} max={5} step={1} value={appState.serverSetting.serverSetting.gpu || 0} onChange={(e) => {
|
||||
appState.serverSetting.updateServerSettings({ ...appState.serverSetting.serverSetting, gpu: Number(e.target.value) })
|
||||
}} />
|
||||
</div>
|
||||
|
@ -21,7 +21,7 @@ export const TrancateNumTresholdRow = (_props: TrancateNumTresholdRowProps) => {
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, [appState.workletNodeSetting.workletNodeSetting, appState.workletNodeSetting.updateWorkletNodeSetting])
|
||||
}, [appState.workletSetting.setting, appState.workletSetting.setSetting])
|
||||
|
||||
return trancateNumTresholdRow
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { ServerInfo, ServerSettingKey } from "./const";
|
||||
import { ClientType, ServerInfo, ServerSettingKey } from "./const";
|
||||
|
||||
|
||||
type FileChunk = {
|
||||
@ -132,4 +132,32 @@ export class ServerConfigurator {
|
||||
return await info
|
||||
}
|
||||
|
||||
switchModelType = async (clinetType: ClientType) => {
|
||||
const url = this.serverUrl + "/model_type"
|
||||
const info = new Promise<ServerInfo>(async (resolve) => {
|
||||
const formData = new FormData();
|
||||
formData.append("modelType", clinetType);
|
||||
|
||||
const request = new Request(url, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
const res = await (await fetch(request)).json() as ServerInfo
|
||||
resolve(res)
|
||||
})
|
||||
return await info
|
||||
}
|
||||
|
||||
getModelType = async () => {
|
||||
const url = this.serverUrl + "/model_type"
|
||||
const info = new Promise<ServerInfo>(async (resolve) => {
|
||||
const request = new Request(url, {
|
||||
method: 'GET',
|
||||
});
|
||||
const res = await (await fetch(request)).json() as ServerInfo
|
||||
resolve(res)
|
||||
})
|
||||
return await info
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { VoiceChangerWorkletNode, VoiceChangerWorkletListener } from "./VoiceCha
|
||||
import workerjs from "raw-loader!../worklet/dist/index.js";
|
||||
import { VoiceFocusDeviceTransformer, VoiceFocusTransformDevice } from "amazon-chime-sdk-js";
|
||||
import { createDummyMediaStream, validateUrl } from "./util";
|
||||
import { DefaultVoiceChangerClientSetting, ServerSettingKey, VoiceChangerClientSetting, WorkletNodeSetting, WorkletSetting } from "./const";
|
||||
import { ClientType, DefaultVoiceChangerClientSetting, ServerSettingKey, VoiceChangerClientSetting, WorkletNodeSetting, WorkletSetting } from "./const";
|
||||
import { ServerConfigurator } from "./ServerConfigurator";
|
||||
|
||||
// オーディオデータの流れ
|
||||
@ -44,15 +44,28 @@ export class VoiceChangerClient {
|
||||
this.vfEnable = vfEnable
|
||||
this.promiseForInitialize = new Promise<void>(async (resolve) => {
|
||||
const scriptUrl = URL.createObjectURL(new Blob([workerjs], { type: "text/javascript" }));
|
||||
await this.ctx.audioWorklet.addModule(scriptUrl)
|
||||
|
||||
this.vcInNode = new VoiceChangerWorkletNode(this.ctx, voiceChangerWorkletListener); // vc node
|
||||
// await this.ctx.audioWorklet.addModule(scriptUrl)
|
||||
// this.vcInNode = new VoiceChangerWorkletNode(this.ctx, voiceChangerWorkletListener); // vc node
|
||||
|
||||
try {
|
||||
this.vcInNode = new VoiceChangerWorkletNode(this.ctx, voiceChangerWorkletListener); // vc node
|
||||
} catch (err) {
|
||||
await this.ctx.audioWorklet.addModule(scriptUrl)
|
||||
this.vcInNode = new VoiceChangerWorkletNode(this.ctx, voiceChangerWorkletListener); // vc node
|
||||
}
|
||||
|
||||
|
||||
|
||||
// const ctx44k = new AudioContext({ sampleRate: 44100 }) // これでもプチプチが残る
|
||||
const ctx44k = new AudioContext({ sampleRate: 48000 }) // 結局これが一番まし。
|
||||
console.log("audio out:", ctx44k)
|
||||
await ctx44k.audioWorklet.addModule(scriptUrl)
|
||||
this.vcOutNode = new VoiceChangerWorkletNode(ctx44k, voiceChangerWorkletListener); // vc node
|
||||
try {
|
||||
this.vcOutNode = new VoiceChangerWorkletNode(ctx44k, voiceChangerWorkletListener); // vc node
|
||||
} catch (err) {
|
||||
await ctx44k.audioWorklet.addModule(scriptUrl)
|
||||
this.vcOutNode = new VoiceChangerWorkletNode(ctx44k, voiceChangerWorkletListener); // vc node
|
||||
}
|
||||
this.currentMediaStreamAudioDestinationNode = ctx44k.createMediaStreamDestination() // output node
|
||||
this.outputGainNode = ctx44k.createGain()
|
||||
this.outputGainNode.gain.value = this.setting.outputGain
|
||||
@ -248,6 +261,14 @@ export class VoiceChangerClient {
|
||||
// コンポーネント設定、操作
|
||||
/////////////////////////////////////////////////////
|
||||
//## Server ##//
|
||||
switchModelType = (clientType: ClientType) => {
|
||||
return this.configurator.switchModelType(clientType)
|
||||
}
|
||||
getModelType = () => {
|
||||
return this.configurator.getModelType()
|
||||
}
|
||||
|
||||
|
||||
updateServerSettings = (key: ServerSettingKey, val: string) => {
|
||||
return this.configurator.updateSettings(key, val)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import { useWorkletSetting, WorkletSettingState } from "./useWorkletSetting"
|
||||
|
||||
export type UseClientProps = {
|
||||
audioContext: AudioContext | null
|
||||
clientType: ClientType
|
||||
clientType: ClientType | null
|
||||
}
|
||||
|
||||
export type ClientState = {
|
||||
@ -26,6 +26,8 @@ export type ClientState = {
|
||||
volume: number;
|
||||
performance: PerformanceData
|
||||
|
||||
// setClientType: (val: ClientType) => void
|
||||
|
||||
// 情報取得
|
||||
getInfo: () => Promise<void>
|
||||
// 設定クリア
|
||||
@ -50,6 +52,7 @@ const InitialPerformanceData: PerformanceData = {
|
||||
export const useClient = (props: UseClientProps): ClientState => {
|
||||
|
||||
const [initialized, setInitialized] = useState<boolean>(false)
|
||||
// const [clientType, setClientType] = useState<ClientType | null>(null)
|
||||
// (1-1) クライアント
|
||||
const voiceChangerClientRef = useRef<VoiceChangerClient | null>(null)
|
||||
const [voiceChangerClient, setVoiceChangerClient] = useState<VoiceChangerClient | null>(voiceChangerClientRef.current)
|
||||
@ -165,6 +168,8 @@ export const useClient = (props: UseClientProps): ClientState => {
|
||||
volume,
|
||||
performance,
|
||||
|
||||
// setClientType,
|
||||
|
||||
// 情報取得
|
||||
getInfo,
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { VoiceChangerClient } from "../VoiceChangerClient"
|
||||
import { useIndexedDB } from "./useIndexedDB"
|
||||
|
||||
export type UseClientSettingProps = {
|
||||
clientType: ClientType
|
||||
clientType: ClientType | null
|
||||
voiceChangerClient: VoiceChangerClient | null
|
||||
audioContext: AudioContext | null
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { useMemo } from "react";
|
||||
import { ClientType, INDEXEDDB_DB_APP_NAME, INDEXEDDB_DB_NAME } from "../const";
|
||||
|
||||
export type UseIndexedDBProps = {
|
||||
clientType: ClientType
|
||||
clientType: ClientType | null
|
||||
}
|
||||
export type IndexedDBState = {
|
||||
dummy: string
|
||||
@ -16,31 +16,36 @@ export type IndexedDBStateAndMethod = IndexedDBState & {
|
||||
}
|
||||
|
||||
export const useIndexedDB = (props: UseIndexedDBProps): IndexedDBStateAndMethod => {
|
||||
const clientType = props.clientType || "default"
|
||||
localForage.config({
|
||||
driver: localForage.INDEXEDDB,
|
||||
name: INDEXEDDB_DB_APP_NAME,
|
||||
version: 1.0,
|
||||
storeName: `${INDEXEDDB_DB_NAME}_${props.clientType}`,
|
||||
storeName: `${INDEXEDDB_DB_NAME}`,
|
||||
description: 'appStorage'
|
||||
|
||||
})
|
||||
|
||||
const setItem = useMemo(() => {
|
||||
return async (key: string, value: unknown) => {
|
||||
await localForage.setItem(key, value)
|
||||
const clientKey = `${clientType}_${key}`
|
||||
await localForage.setItem(clientKey, value)
|
||||
}
|
||||
}, [])
|
||||
}, [props.clientType])
|
||||
|
||||
const getItem = useMemo(() => {
|
||||
return async (key: string) => {
|
||||
return await localForage.getItem(key)
|
||||
const clientKey = `${clientType}_${key}`
|
||||
return await localForage.getItem(clientKey)
|
||||
}
|
||||
}, [])
|
||||
}, [props.clientType])
|
||||
|
||||
const removeItem = useMemo(() => {
|
||||
return async (key: string) => {
|
||||
return await localForage.removeItem(key)
|
||||
const clientKey = `${clientType}_${key}`
|
||||
return await localForage.removeItem(clientKey)
|
||||
}
|
||||
}, [])
|
||||
}, [props.clientType])
|
||||
|
||||
|
||||
return {
|
||||
|
@ -38,7 +38,7 @@ const InitialFileUploadSetting: FileUploadSetting = {
|
||||
}
|
||||
|
||||
export type UseServerSettingProps = {
|
||||
clientType: ClientType
|
||||
clientType: ClientType | null
|
||||
voiceChangerClient: VoiceChangerClient | null
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ export type ServerSettingState = {
|
||||
|
||||
export const useServerSetting = (props: UseServerSettingProps): ServerSettingState => {
|
||||
// const settingRef = useRef<VoiceChangerServerSetting>(DefaultVoiceChangerServerSetting)
|
||||
const defaultServerSetting = useMemo(() => {
|
||||
const getDefaultServerSetting = () => {
|
||||
if (props.clientType == "MMVCv13") {
|
||||
return DefaultServerSetting_MMVCv13
|
||||
} else if (props.clientType == "MMVCv15") {
|
||||
@ -75,43 +75,57 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
|
||||
} else {
|
||||
return DefaultServerSetting_MMVCv15
|
||||
}
|
||||
}, [])
|
||||
const [serverSetting, setServerSetting] = useState<ServerInfo>(defaultServerSetting)
|
||||
}
|
||||
|
||||
const [serverSetting, setServerSetting] = useState<ServerInfo>(getDefaultServerSetting())
|
||||
const [fileUploadSetting, setFileUploadSetting] = useState<FileUploadSetting>(InitialFileUploadSetting)
|
||||
const { setItem, getItem, removeItem } = useIndexedDB({ clientType: props.clientType })
|
||||
|
||||
|
||||
// DBから設定取得(キャッシュによる初期化)
|
||||
// clientTypeが新しく設定されたときに、serverのmodelType動作を変更+設定反映
|
||||
useEffect(() => {
|
||||
const loadCache = async () => {
|
||||
const setting = await getItem(INDEXEDDB_KEY_SERVER)
|
||||
if (!setting) {
|
||||
if (!props.voiceChangerClient) return
|
||||
if (!props.clientType) return
|
||||
|
||||
const setInitialSetting = async () => {
|
||||
// Set Model Type
|
||||
await props.voiceChangerClient!.switchModelType(props.clientType!)
|
||||
|
||||
// Load Default (and Cache) and set
|
||||
const defaultServerSetting = getDefaultServerSetting()
|
||||
const cachedServerSetting = await getItem(INDEXEDDB_KEY_SERVER)
|
||||
let initialSetting: ServerInfo
|
||||
if (cachedServerSetting) {
|
||||
initialSetting = { ...defaultServerSetting, ...cachedServerSetting as ServerInfo }
|
||||
console.log("Initial Setting1:", initialSetting)
|
||||
} else {
|
||||
setServerSetting(setting as ServerInfo)
|
||||
initialSetting = { ...defaultServerSetting }
|
||||
console.log("Initial Setting2:", initialSetting)
|
||||
}
|
||||
setServerSetting(initialSetting)
|
||||
|
||||
// upload setting
|
||||
for (let i = 0; i < Object.values(ServerSettingKey).length; i++) {
|
||||
const k = Object.values(ServerSettingKey)[i] as keyof VoiceChangerServerSetting
|
||||
const v = initialSetting[k]
|
||||
if (v) {
|
||||
props.voiceChangerClient!.updateServerSettings(k, "" + v)
|
||||
}
|
||||
}
|
||||
|
||||
// Load file upload cache
|
||||
const fileuploadSetting = await getItem(INDEXEDDB_KEY_MODEL_DATA)
|
||||
if (!fileuploadSetting) {
|
||||
} else {
|
||||
setFileUploadSetting(fileuploadSetting as FileUploadSetting)
|
||||
}
|
||||
|
||||
reloadServerInfo()
|
||||
}
|
||||
|
||||
loadCache()
|
||||
}, [])
|
||||
setInitialSetting()
|
||||
|
||||
// サーバへキャッシュの内容を反映 (クライアント初期化した時の一回)
|
||||
useEffect(() => {
|
||||
if (!props.voiceChangerClient) return
|
||||
for (let i = 0; i < Object.values(ServerSettingKey).length; i++) {
|
||||
const k = Object.values(ServerSettingKey)[i] as keyof VoiceChangerServerSetting
|
||||
const v = serverSetting[k]
|
||||
if (v) {
|
||||
props.voiceChangerClient.updateServerSettings(k, "" + v)
|
||||
}
|
||||
}
|
||||
reloadServerInfo()
|
||||
}, [props.voiceChangerClient])
|
||||
}, [props.voiceChangerClient, props.clientType])
|
||||
|
||||
//////////////
|
||||
// 設定
|
||||
@ -125,7 +139,7 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
|
||||
const new_v = setting[k]
|
||||
if (cur_v != new_v) {
|
||||
const res = await props.voiceChangerClient.updateServerSettings(k, "" + new_v)
|
||||
if (res.onnxExecutionProviders.length > 0) {
|
||||
if (res.onnxExecutionProviders && res.onnxExecutionProviders.length > 0) {
|
||||
res.onnxExecutionProvider = res.onnxExecutionProviders[0]
|
||||
} else {
|
||||
res.onnxExecutionProvider = "CPUExecutionProvider"
|
||||
|
@ -5,7 +5,7 @@ import { VoiceChangerClient } from "../VoiceChangerClient"
|
||||
import { useIndexedDB } from "./useIndexedDB"
|
||||
|
||||
export type UseWorkletNodeSettingProps = {
|
||||
clientType: ClientType
|
||||
clientType: ClientType | null
|
||||
voiceChangerClient: VoiceChangerClient | null
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ import { VoiceChangerClient } from "../VoiceChangerClient";
|
||||
import { useIndexedDB } from "./useIndexedDB";
|
||||
|
||||
export type UseWorkletSettingProps = {
|
||||
clientType: ClientType
|
||||
clientType: ClientType | null
|
||||
voiceChangerClient: VoiceChangerClient | null
|
||||
}
|
||||
|
||||
|
@ -8,8 +8,8 @@ class UvicornSuppressFilter(logging.Filter):
|
||||
return False
|
||||
|
||||
|
||||
# logger = logging.getLogger("uvicorn.error")
|
||||
# logger.addFilter(UvicornSuppressFilter())
|
||||
logger = logging.getLogger("uvicorn.error")
|
||||
logger.addFilter(UvicornSuppressFilter())
|
||||
|
||||
logger = logging.getLogger("fairseq.tasks.hubert_pretraining")
|
||||
logger.addFilter(UvicornSuppressFilter())
|
||||
|
@ -9,7 +9,7 @@ from fastapi import HTTPException, FastAPI, UploadFile, File, Form
|
||||
from restapi.mods.FileUploader import upload_file, concat_file_chunks
|
||||
from voice_changer.VoiceChangerManager import VoiceChangerManager
|
||||
|
||||
from const import MODEL_DIR, UPLOAD_DIR
|
||||
from const import MODEL_DIR, UPLOAD_DIR, ModelType
|
||||
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
||||
os.makedirs(MODEL_DIR, exist_ok=True)
|
||||
|
||||
@ -25,8 +25,8 @@ class MMVC_Rest_Fileuploader:
|
||||
self.router.add_api_route("/load_model", self.post_load_model, methods=["POST"])
|
||||
self.router.add_api_route("/load_model_for_train", self.post_load_model_for_train, methods=["POST"])
|
||||
self.router.add_api_route("/extract_voices", self.post_extract_voices, methods=["POST"])
|
||||
|
||||
self.onnx_provider = ""
|
||||
self.router.add_api_route("/model_type", self.post_model_type, methods=["POST"])
|
||||
self.router.add_api_route("/model_type", self.get_model_type, methods=["GET"])
|
||||
|
||||
def post_upload_file(self, file: UploadFile = File(...), filename: str = Form(...)):
|
||||
res = upload_file(UPLOAD_DIR, file, filename)
|
||||
@ -95,3 +95,18 @@ class MMVC_Rest_Fileuploader:
|
||||
UPLOAD_DIR, zipFilename, zipFileChunkNum, UPLOAD_DIR)
|
||||
shutil.unpack_archive(zipFilePath, "MMVC_Trainer/dataset/textful/")
|
||||
return {"Zip file unpacked": f"{zipFilePath}"}
|
||||
|
||||
def post_model_type(
|
||||
self,
|
||||
modelType: ModelType = Form(...),
|
||||
):
|
||||
info = self.voiceChangerManager.switchModelType(modelType)
|
||||
json_compatible_item_data = jsonable_encoder(info)
|
||||
return JSONResponse(content=json_compatible_item_data)
|
||||
|
||||
def get_model_type(
|
||||
self,
|
||||
):
|
||||
info = self.voiceChangerManager.getModelType(modelType)
|
||||
json_compatible_item_data = jsonable_encoder(info)
|
||||
return JSONResponse(content=json_compatible_item_data)
|
||||
|
@ -273,6 +273,10 @@ class DDSP_SVC:
|
||||
del self.net_g
|
||||
del self.onnx_session
|
||||
|
||||
def __del__(self):
|
||||
del self.net_g
|
||||
del self.onnx_session
|
||||
|
||||
|
||||
def cross_fade(a: np.ndarray, b: np.ndarray, idx: int):
|
||||
result = np.zeros(idx + b.shape[0])
|
||||
|
@ -10,6 +10,7 @@ if sys.platform.startswith('darwin'):
|
||||
else:
|
||||
sys.path.append("MMVC_Client_v13/python")
|
||||
|
||||
|
||||
from dataclasses import dataclass, asdict
|
||||
import numpy as np
|
||||
import torch
|
||||
@ -199,6 +200,18 @@ class MMVCv13:
|
||||
audio = self._pyTorch_inference(data)
|
||||
return audio
|
||||
|
||||
def destroy(self):
|
||||
def __del__(self):
|
||||
del self.net_g
|
||||
del self.onnx_session
|
||||
remove_path = os.path.join("MMVC_Client_v13", "python")
|
||||
sys.path = [x for x in sys.path if x.endswith(remove_path) == False]
|
||||
|
||||
for key in list(sys.modules):
|
||||
val = sys.modules.get(key)
|
||||
try:
|
||||
file_path = val.__file__
|
||||
if file_path.find("MMVC_Client_v13/python") >= 0:
|
||||
print("remove", key, file_path)
|
||||
sys.modules.pop(key)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
@ -247,6 +247,19 @@ class MMVCv15:
|
||||
audio = self._pyTorch_inference(data)
|
||||
return audio
|
||||
|
||||
def destroy(self):
|
||||
def __del__(self):
|
||||
del self.net_g
|
||||
del self.onnx_session
|
||||
|
||||
remove_path = os.path.join("MMVC_Client_v15", "python")
|
||||
sys.path = [x for x in sys.path if x.endswith(remove_path) == False]
|
||||
|
||||
for key in list(sys.modules):
|
||||
val = sys.modules.get(key)
|
||||
try:
|
||||
file_path = val.__file__
|
||||
if file_path.find("MMVC_Client_v15/python") >= 0:
|
||||
print("remove", key, file_path)
|
||||
sys.modules.pop(key)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
@ -277,6 +277,19 @@ class RVC:
|
||||
audio = self._pyTorch_inference(data)
|
||||
return audio
|
||||
|
||||
def destroy(self):
|
||||
def __del__(self):
|
||||
del self.net_g
|
||||
del self.onnx_session
|
||||
|
||||
remove_path = os.path.join("RVC")
|
||||
sys.path = [x for x in sys.path if x.endswith(remove_path) == False]
|
||||
|
||||
for key in list(sys.modules):
|
||||
val = sys.modules.get(key)
|
||||
try:
|
||||
file_path = val.__file__
|
||||
if file_path.find("RVC/") >= 0:
|
||||
print("remove", key, file_path)
|
||||
sys.modules.pop(key)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
@ -351,9 +351,21 @@ class SoVitsSvc40:
|
||||
audio = self._pyTorch_inference(data)
|
||||
return audio
|
||||
|
||||
def destroy(self):
|
||||
def __del__(self):
|
||||
del self.net_g
|
||||
del self.onnx_session
|
||||
remove_path = os.path.join("so-vits-svc-40")
|
||||
sys.path = [x for x in sys.path if x.endswith(remove_path) == False]
|
||||
|
||||
for key in list(sys.modules):
|
||||
val = sys.modules.get(key)
|
||||
try:
|
||||
file_path = val.__file__
|
||||
if file_path.find("so-vits-svc-40/") >= 0:
|
||||
print("remove", key, file_path)
|
||||
sys.modules.pop(key)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
def resize_f0(x, target_len):
|
||||
|
@ -314,10 +314,23 @@ class SoVitsSvc40v2:
|
||||
audio = self._pyTorch_inference(data)
|
||||
return audio
|
||||
|
||||
def destroy(self):
|
||||
def __del__(self):
|
||||
del self.net_g
|
||||
del self.onnx_session
|
||||
|
||||
remove_path = os.path.join("so-vits-svc-40v2")
|
||||
sys.path = [x for x in sys.path if x.endswith(remove_path) == False]
|
||||
|
||||
for key in list(sys.modules):
|
||||
val = sys.modules.get(key)
|
||||
try:
|
||||
file_path = val.__file__
|
||||
if file_path.find("so-vits-svc-40v2/") >= 0:
|
||||
print("remove", key, file_path)
|
||||
sys.modules.pop(key)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
def resize_f0(x, target_len):
|
||||
source = np.array(x)
|
||||
|
@ -1,10 +1,10 @@
|
||||
from typing import Any, Callable, Optional, Protocol, TypeAlias, Union, cast
|
||||
from const import TMP_DIR, getModelType
|
||||
from const import TMP_DIR, ModelType
|
||||
import torch
|
||||
import os
|
||||
import traceback
|
||||
import numpy as np
|
||||
from dataclasses import dataclass, asdict
|
||||
from dataclasses import dataclass, asdict, field
|
||||
import resampy
|
||||
|
||||
|
||||
@ -46,9 +46,15 @@ class VoiceChangerSettings():
|
||||
recordIO: int = 0 # 0:off, 1:on
|
||||
|
||||
# ↓mutableな物だけ列挙
|
||||
intData: list[str] = ["inputSampleRate", "crossFadeOverlapSize", "recordIO"]
|
||||
floatData: list[str] = ["crossFadeOffsetRate", "crossFadeEndRate"]
|
||||
strData: list[str] = []
|
||||
intData: list[str] = field(
|
||||
default_factory=lambda: ["inputSampleRate", "crossFadeOverlapSize", "recordIO"]
|
||||
)
|
||||
floatData: list[str] = field(
|
||||
default_factory=lambda: ["crossFadeOffsetRate", "crossFadeEndRate"]
|
||||
)
|
||||
strData: list[str] = field(
|
||||
default_factory=lambda: []
|
||||
)
|
||||
|
||||
|
||||
class VoiceChanger():
|
||||
@ -64,8 +70,46 @@ class VoiceChanger():
|
||||
self.currentCrossFadeOverlapSize = 0 # setting
|
||||
self.crossfadeSize = 0 # calculated
|
||||
|
||||
self.modelType = getModelType()
|
||||
print("[VoiceChanger] activate model type:", self.modelType)
|
||||
# self.modelType = getModelType()
|
||||
# print("[VoiceChanger] activate model type:", self.modelType)
|
||||
# if self.modelType == "MMVCv15":
|
||||
# from voice_changer.MMVCv15.MMVCv15 import MMVCv15
|
||||
# self.voiceChanger = MMVCv15() # type: ignore
|
||||
# elif self.modelType == "MMVCv13":
|
||||
# from voice_changer.MMVCv13.MMVCv13 import MMVCv13
|
||||
# self.voiceChanger = MMVCv13()
|
||||
# elif self.modelType == "so-vits-svc-40v2":
|
||||
# from voice_changer.SoVitsSvc40v2.SoVitsSvc40v2 import SoVitsSvc40v2
|
||||
# self.voiceChanger = SoVitsSvc40v2(params)
|
||||
# elif self.modelType == "so-vits-svc-40" or self.modelType == "so-vits-svc-40_c":
|
||||
# from voice_changer.SoVitsSvc40.SoVitsSvc40 import SoVitsSvc40
|
||||
# self.voiceChanger = SoVitsSvc40(params)
|
||||
# elif self.modelType == "DDSP-SVC":
|
||||
# from voice_changer.DDSP_SVC.DDSP_SVC import DDSP_SVC
|
||||
# self.voiceChanger = DDSP_SVC(params)
|
||||
# elif self.modelType == "RVC":
|
||||
# from voice_changer.RVC.RVC import RVC
|
||||
# self.voiceChanger = RVC(params)
|
||||
# else:
|
||||
# from voice_changer.MMVCv13.MMVCv13 import MMVCv13
|
||||
# self.voiceChanger = MMVCv13()
|
||||
|
||||
self.voiceChanger = None
|
||||
self.modelType = None
|
||||
self.params = params
|
||||
self.gpu_num = torch.cuda.device_count()
|
||||
self.prev_audio = np.zeros(4096)
|
||||
self.mps_enabled: bool = getattr(torch.backends, "mps", None) is not None and torch.backends.mps.is_available()
|
||||
|
||||
print(f"VoiceChanger Initialized (GPU_NUM:{self.gpu_num}, mps_enabled:{self.mps_enabled})")
|
||||
|
||||
def switchModelType(self, modelType: ModelType):
|
||||
if hasattr(self, "voiceChanger") and self.voiceChanger != None:
|
||||
# return {"status": "ERROR", "msg": "vc is already selected. currently re-select is not implemented"}
|
||||
del self.voiceChanger
|
||||
self.voiceChanger = None
|
||||
|
||||
self.modelType = modelType
|
||||
if self.modelType == "MMVCv15":
|
||||
from voice_changer.MMVCv15.MMVCv15 import MMVCv15
|
||||
self.voiceChanger = MMVCv15() # type: ignore
|
||||
@ -74,25 +118,27 @@ class VoiceChanger():
|
||||
self.voiceChanger = MMVCv13()
|
||||
elif self.modelType == "so-vits-svc-40v2":
|
||||
from voice_changer.SoVitsSvc40v2.SoVitsSvc40v2 import SoVitsSvc40v2
|
||||
self.voiceChanger = SoVitsSvc40v2(params)
|
||||
self.voiceChanger = SoVitsSvc40v2(self.params)
|
||||
elif self.modelType == "so-vits-svc-40" or self.modelType == "so-vits-svc-40_c":
|
||||
from voice_changer.SoVitsSvc40.SoVitsSvc40 import SoVitsSvc40
|
||||
self.voiceChanger = SoVitsSvc40(params)
|
||||
self.voiceChanger = SoVitsSvc40(self.params)
|
||||
elif self.modelType == "DDSP-SVC":
|
||||
from voice_changer.DDSP_SVC.DDSP_SVC import DDSP_SVC
|
||||
self.voiceChanger = DDSP_SVC(params)
|
||||
self.voiceChanger = DDSP_SVC(self.params)
|
||||
elif self.modelType == "RVC":
|
||||
from voice_changer.RVC.RVC import RVC
|
||||
self.voiceChanger = RVC(params)
|
||||
self.voiceChanger = RVC(self.params)
|
||||
else:
|
||||
from voice_changer.MMVCv13.MMVCv13 import MMVCv13
|
||||
self.voiceChanger = MMVCv13()
|
||||
|
||||
self.gpu_num = torch.cuda.device_count()
|
||||
self.prev_audio = np.zeros(4096)
|
||||
self.mps_enabled: bool = getattr(torch.backends, "mps", None) is not None and torch.backends.mps.is_available()
|
||||
return {"status": "OK", "msg": "vc is switched."}
|
||||
|
||||
print(f"VoiceChanger Initialized (GPU_NUM:{self.gpu_num}, mps_enabled:{self.mps_enabled})")
|
||||
def getModelType(self):
|
||||
if self.modelType != None:
|
||||
return {"status": "OK", "vc": self.modelType}
|
||||
else:
|
||||
return {"status": "OK", "vc": "none"}
|
||||
|
||||
def loadModel(
|
||||
self,
|
||||
@ -115,7 +161,8 @@ class VoiceChanger():
|
||||
|
||||
def get_info(self):
|
||||
data = asdict(self.settings)
|
||||
data.update(self.voiceChanger.get_info())
|
||||
if hasattr(self, "voiceChanger"):
|
||||
data.update(self.voiceChanger.get_info())
|
||||
return data
|
||||
|
||||
def update_settings(self, key: str, val: Any):
|
||||
@ -148,10 +195,12 @@ class VoiceChanger():
|
||||
elif key in self.settings.strData:
|
||||
setattr(self.settings, key, str(val))
|
||||
else:
|
||||
ret = self.voiceChanger.update_settings(key, val)
|
||||
if ret == False:
|
||||
print(f"{key} is not mutable variable or unknown variable!")
|
||||
|
||||
if hasattr(self, "voiceChanger"):
|
||||
ret = self.voiceChanger.update_settings(key, val)
|
||||
if ret == False:
|
||||
print(f"{key} is not mutable variable or unknown variable!")
|
||||
else:
|
||||
print(f"voice changer is not initialized!")
|
||||
return self.get_info()
|
||||
|
||||
def _generate_strength(self, crossfadeSize: int):
|
||||
|
@ -1,5 +1,6 @@
|
||||
import numpy as np
|
||||
from voice_changer.VoiceChanger import VoiceChanger
|
||||
from const import ModelType
|
||||
|
||||
|
||||
class VoiceChangerManager():
|
||||
@ -37,3 +38,9 @@ class VoiceChangerManager():
|
||||
else:
|
||||
print("Voice Change is not loaded. Did you load a correct model?")
|
||||
return np.zeros(1).astype(np.int16), []
|
||||
|
||||
def switchModelType(self, modelType: ModelType):
|
||||
return self.voiceChanger.switchModelType(modelType)
|
||||
|
||||
def getModelType(self):
|
||||
return self.voiceChanger.getModelType()
|
||||
|
Loading…
x
Reference in New Issue
Block a user