fix some bugs

This commit is contained in:
wataru 2023-02-22 07:35:26 +09:00
parent 98db8862b5
commit 35ca5a7618
17 changed files with 225 additions and 2676 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@dannadori/voice-changer-client-js": "^1.0.76", "@dannadori/voice-changer-client-js": "^1.0.78",
"@fortawesome/fontawesome-svg-core": "^6.3.0", "@fortawesome/fontawesome-svg-core": "^6.3.0",
"@fortawesome/free-brands-svg-icons": "^6.3.0", "@fortawesome/free-brands-svg-icons": "^6.3.0",
"@fortawesome/free-regular-svg-icons": "^6.3.0", "@fortawesome/free-regular-svg-icons": "^6.3.0",
@ -3187,9 +3187,9 @@
} }
}, },
"node_modules/@dannadori/voice-changer-client-js": { "node_modules/@dannadori/voice-changer-client-js": {
"version": "1.0.76", "version": "1.0.78",
"resolved": "https://registry.npmjs.org/@dannadori/voice-changer-client-js/-/voice-changer-client-js-1.0.76.tgz", "resolved": "https://registry.npmjs.org/@dannadori/voice-changer-client-js/-/voice-changer-client-js-1.0.78.tgz",
"integrity": "sha512-NOYlPaarEwhxv7CzegXzbiC5XA8ARDwLnU2pn7JSCH8EAfk4SV+qGohdSg12H1Z3u0ry7RT2kyWPNJuMH0dZ5A==", "integrity": "sha512-1M5X7WKhpBXaiMxNxrXYeGRby52vq1QUkSzbBWq73kBd0ANesbeLxshhYN+fXCdazTbcDbb5ONNaFw797e7kSA==",
"dependencies": { "dependencies": {
"@types/readable-stream": "^2.3.15", "@types/readable-stream": "^2.3.15",
"amazon-chime-sdk-js": "^3.11.0", "amazon-chime-sdk-js": "^3.11.0",
@ -13473,9 +13473,9 @@
} }
}, },
"@dannadori/voice-changer-client-js": { "@dannadori/voice-changer-client-js": {
"version": "1.0.76", "version": "1.0.78",
"resolved": "https://registry.npmjs.org/@dannadori/voice-changer-client-js/-/voice-changer-client-js-1.0.76.tgz", "resolved": "https://registry.npmjs.org/@dannadori/voice-changer-client-js/-/voice-changer-client-js-1.0.78.tgz",
"integrity": "sha512-NOYlPaarEwhxv7CzegXzbiC5XA8ARDwLnU2pn7JSCH8EAfk4SV+qGohdSg12H1Z3u0ry7RT2kyWPNJuMH0dZ5A==", "integrity": "sha512-1M5X7WKhpBXaiMxNxrXYeGRby52vq1QUkSzbBWq73kBd0ANesbeLxshhYN+fXCdazTbcDbb5ONNaFw797e7kSA==",
"requires": { "requires": {
"@types/readable-stream": "^2.3.15", "@types/readable-stream": "^2.3.15",
"amazon-chime-sdk-js": "^3.11.0", "amazon-chime-sdk-js": "^3.11.0",

View File

@ -51,7 +51,7 @@
"webpack-dev-server": "^4.11.1" "webpack-dev-server": "^4.11.1"
}, },
"dependencies": { "dependencies": {
"@dannadori/voice-changer-client-js": "^1.0.76", "@dannadori/voice-changer-client-js": "^1.0.78",
"@fortawesome/fontawesome-svg-core": "^6.3.0", "@fortawesome/fontawesome-svg-core": "^6.3.0",
"@fortawesome/free-brands-svg-icons": "^6.3.0", "@fortawesome/free-brands-svg-icons": "^6.3.0",
"@fortawesome/free-regular-svg-icons": "^6.3.0", "@fortawesome/free-regular-svg-icons": "^6.3.0",

View File

@ -1,16 +1,18 @@
import * as React from "react"; import * as React from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import "./css/App.css" import "./css/App.css"
import { useMemo, } from "react"; import { ErrorInfo, useMemo, useState, } from "react";
import { useMicrophoneOptions } from "./100_options_microphone"; import { useMicrophoneOptions } from "./100_options_microphone";
import { AppStateProvider, useAppState } from "./001_provider/001_AppStateProvider"; import { AppStateProvider, useAppState } from "./001_provider/001_AppStateProvider";
import localForage from "localforage";
import { library } from "@fortawesome/fontawesome-svg-core"; import { library } from "@fortawesome/fontawesome-svg-core";
import { fas } from "@fortawesome/free-solid-svg-icons"; import { fas } from "@fortawesome/free-solid-svg-icons";
import { far } from "@fortawesome/free-regular-svg-icons"; import { far } from "@fortawesome/free-regular-svg-icons";
import { fab } from "@fortawesome/free-brands-svg-icons"; import { fab } from "@fortawesome/free-brands-svg-icons";
import { AppRootProvider } from "./001_provider/001_AppRootProvider"; import { AppRootProvider } 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 } from "@dannadori/voice-changer-client-js";
library.add(fas, far, fab); library.add(fas, far, fab);
@ -21,7 +23,6 @@ const root = createRoot(container);
const App = () => { const App = () => {
const appState = useAppState() const appState = useAppState()
const { voiceChangerSetting } = useMicrophoneOptions() const { voiceChangerSetting } = useMicrophoneOptions()
const titleRow = useMemo(() => { const titleRow = useMemo(() => {
return ( return (
<div className="top-title"> <div className="top-title">
@ -87,14 +88,73 @@ const App = () => {
} }
const AppStateWrapper = () => { const AppStateWrapper = () => {
// const appRoot = useAppRoot() // エラーバウンダリー設定
// if (!appRoot.audioContextState.audioContext) { const [error, setError] = useState<{ error: Error, errorInfo: ErrorInfo }>()
// return <>please click window</>
// } // localForage.config({
// driver: localForage.INDEXEDDB,
// name: INDEXEDDB_DB_APP_NAME,
// version: 1.0,
// storeName: INDEXEDDB_DB_NAME,
// description: 'appStorage'
// })
const errorComponent = useMemo(() => {
const errorName = error?.error.name || "no error name"
const errorMessage = error?.error.message || "no error message"
const errorInfos = (error?.errorInfo.componentStack || "no error stack").split("\n")
const onClearCacheClicked = () => {
[
INDEXEDDB_KEY_CLIENT,
INDEXEDDB_KEY_SERVER,
INDEXEDDB_KEY_WORKLETNODE,
INDEXEDDB_KEY_MODEL_DATA,
INDEXEDDB_KEY_WORKLET
].forEach((x) => {
localForage.removeItem(x)
})
location.reload();
}
return (
<div className="error-container">
<div className="top-error-message">
</div>
<div className="top-error-description">
<p></p>
<p></p>
<p><button onClick={onClearCacheClicked}></button></p>
</div>
<div className="error-detail">
<div className="error-name">
{errorName}
</div>
<div className="error-message">
{errorMessage}
</div>
<div className="error-info-container">
{errorInfos.map(x => {
return <div className="error-info-line" key={x}>{x}</div>
})}
</div>
</div>
</div>
)
}, [error])
const updateError = (error: Error, errorInfo: React.ErrorInfo) => {
console.log("error compo", error, errorInfo)
setError({ error, errorInfo })
}
return ( return (
<AppStateProvider> <ErrorBoundary fallback={errorComponent} onError={updateError}>
<App></App> <AppStateProvider>
</AppStateProvider> <App></App>
</AppStateProvider>
</ErrorBoundary>
) )
} }
@ -103,3 +163,4 @@ root.render(
<AppStateWrapper></AppStateWrapper> <AppStateWrapper></AppStateWrapper>
</AppRootProvider> </AppRootProvider>
); );

View File

@ -13,8 +13,8 @@ export const useAudioConfig = (): AudioConfigState => {
document.removeEventListener('mousedown', createAudioContext); document.removeEventListener('mousedown', createAudioContext);
setAudioContext(ctx) setAudioContext(ctx)
} }
document.addEventListener('touchstart', createAudioContext); document.addEventListener('touchstart', createAudioContext, false);
document.addEventListener('mousedown', createAudioContext); document.addEventListener('mousedown', createAudioContext, false);
}, []) }, [])
const ret: AudioConfigState = { const ret: AudioConfigState = {

View File

@ -1,4 +1,4 @@
import React, { useContext } from "react"; import React, { useContext, useRef } from "react";
import { ReactNode } from "react"; import { ReactNode } from "react";
import { AudioConfigState, useAudioConfig } from "../001_globalHooks/001_useAudioConfig"; import { AudioConfigState, useAudioConfig } from "../001_globalHooks/001_useAudioConfig";
@ -8,6 +8,7 @@ type Props = {
type AppRootValue = { type AppRootValue = {
audioContextState: AudioConfigState audioContextState: AudioConfigState
} }
const AppRootContext = React.createContext<AppRootValue | null>(null); const AppRootContext = React.createContext<AppRootValue | null>(null);
@ -21,9 +22,8 @@ export const useAppRoot = (): AppRootValue => {
export const AppRootProvider = ({ children }: Props) => { export const AppRootProvider = ({ children }: Props) => {
const audioContextState = useAudioConfig() const audioContextState = useAudioConfig()
const providerValue: AppRootValue = { const providerValue: AppRootValue = {
audioContextState audioContextState,
}; };
return <AppRootContext.Provider value={providerValue}>{children}</AppRootContext.Provider>; return <AppRootContext.Provider value={providerValue}>{children}</AppRootContext.Provider>;

View File

@ -1,5 +1,5 @@
import { ClientState } from "@dannadori/voice-changer-client-js"; import { ClientState } from "@dannadori/voice-changer-client-js";
import React, { useContext, useEffect } from "react"; import React, { useContext, useEffect, useRef } from "react";
import { ReactNode } from "react"; import { ReactNode } from "react";
import { useVCClient, VCClientState } from "../001_globalHooks/001_useVCClient"; import { useVCClient, VCClientState } from "../001_globalHooks/001_useVCClient";
import { FrontendManagerStateAndMethod, useFrontendManager } from "../001_globalHooks/010_useFrontendManager"; import { FrontendManagerStateAndMethod, useFrontendManager } from "../001_globalHooks/010_useFrontendManager";
@ -12,6 +12,7 @@ type Props = {
type AppStateValue = ClientState & { type AppStateValue = ClientState & {
audioContext: AudioContext audioContext: AudioContext
frontendManagerState: FrontendManagerStateAndMethod; frontendManagerState: FrontendManagerStateAndMethod;
initializedRef: React.MutableRefObject<boolean>
} }
const AppStateContext = React.createContext<AppStateValue | null>(null); const AppStateContext = React.createContext<AppStateValue | null>(null);
@ -28,10 +29,21 @@ export const AppStateProvider = ({ children }: Props) => {
const clientState = useVCClient({ audioContext: appRoot.audioContextState.audioContext }) const clientState = useVCClient({ audioContext: appRoot.audioContextState.audioContext })
const frontendManagerState = useFrontendManager(); const frontendManagerState = useFrontendManager();
const initializedRef = useRef<boolean>(false)
useEffect(() => {
if (clientState.clientState.initialized) {
initializedRef.current = true
}
}, [clientState.clientState.initialized])
const providerValue: AppStateValue = { const providerValue: AppStateValue = {
audioContext: appRoot.audioContextState.audioContext!, audioContext: appRoot.audioContextState.audioContext!,
...clientState.clientState, ...clientState.clientState,
frontendManagerState frontendManagerState,
initializedRef
}; };
return <AppStateContext.Provider value={providerValue}>{children}</AppStateContext.Provider>; return <AppStateContext.Provider value={providerValue}>{children}</AppStateContext.Provider>;

View File

@ -0,0 +1,56 @@
import React, { createRef, ErrorInfo } from 'react';
type ErrorBoundaryProps = {
children: React.ReactNode;
fallback: React.ReactNode;
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
}
type ErrorBoundaryState = {
hasError: boolean;
}
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
private eventHandler: () => void
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false };
this.eventHandler = this.updateError.bind(this);
}
static getDerivedStateFromError(_error: Error) {
// console.warn("React Error Boundary Catch", error)
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
// For logging
console.warn("React Error Boundary Catch", error, errorInfo)
const { onError } = this.props;
if (onError) {
onError(error, errorInfo);
}
}
// 非同期例外対応
updateError() {
this.setState({ hasError: true });
}
componentDidMount() {
window.addEventListener('unhandledrejection', this.eventHandler)
}
componentWillUnmount() {
window.removeEventListener('unhandledrejection', this.eventHandler)
}
render() {
if (this.state.hasError) {
return this.props.fallback;
}
return this.props.children;
}
}
export default ErrorBoundary;

View File

@ -29,11 +29,17 @@ export const useServerControl = () => {
const startButtonRow = useMemo(() => { const startButtonRow = useMemo(() => {
const onStartClicked = async () => { const onStartClicked = async () => {
if (!appState.audioContext) { if (!appState.initializedRef.current) {
await new Promise<void>((resolve) => { while (true) {
console.log("wait 2000ms") // console.log("wait 500ms")
setTimeout(resolve, 1000 * 2) await new Promise<void>((resolve) => {
}) setTimeout(resolve, 500)
})
// console.log("initiliazed", appState.initializedRef.current)
if (appState.initializedRef.current) {
break
}
}
setStartWithAudioContextCreate(true) setStartWithAudioContextCreate(true)
} else { } else {
appState.frontendManagerState.setIsConverting(true) appState.frontendManagerState.setIsConverting(true)

View File

@ -121,6 +121,9 @@ export const useModelSettingArea = (): ServerSettingState => {
const pyTorchFilenameText = appState.serverSetting.fileUploadSetting.pyTorchModel?.filename || appState.serverSetting.fileUploadSetting.pyTorchModel?.file?.name || "" const pyTorchFilenameText = appState.serverSetting.fileUploadSetting.pyTorchModel?.filename || appState.serverSetting.fileUploadSetting.pyTorchModel?.file?.name || ""
const correspondenceFileText = appState.clientSetting.clientSetting.correspondences ? JSON.stringify(appState.clientSetting.clientSetting.correspondences.map(x => { return x.dirname })) : "" const correspondenceFileText = appState.clientSetting.clientSetting.correspondences ? JSON.stringify(appState.clientSetting.clientSetting.correspondences.map(x => { return x.dirname })) : ""
const uploadingStatus = appState.serverSetting.isUploading ?
appState.serverSetting.uploadProgress == 0 ? `loading model...(wait about 20sec)` : `uploading.... ${appState.serverSetting.uploadProgress}%` : ""
return ( return (
<> <>
<div className="body-row split-3-3-4 left-padding-1 guided"> <div className="body-row split-3-3-4 left-padding-1 guided">
@ -190,7 +193,7 @@ export const useModelSettingArea = (): ServerSettingState => {
<div className="body-row split-3-3-4 left-padding-1 guided"> <div className="body-row split-3-3-4 left-padding-1 guided">
<div className="body-item-title left-padding-2"></div> <div className="body-item-title left-padding-2"></div>
<div className="body-item-text"> <div className="body-item-text">
{appState.serverSetting.isUploading ? `uploading.... ${appState.serverSetting.uploadProgress}%` : ""} {uploadingStatus}
</div> </div>
<div className="body-button-container"> <div className="body-button-container">
<div className={uploadButtonClassName} onClick={uploadButtonAction}>{uploadButtonLabel}</div> <div className={uploadButtonClassName} onClick={uploadButtonAction}>{uploadButtonLabel}</div>

View File

@ -2,7 +2,7 @@
@import url("https://fonts.googleapis.com/css2?family=Yusei+Magic&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Yusei+Magic&display=swap");
@import "./101_RotatedButton.css"; @import "./101_RotatedButton.css";
@import "./Error.css";
:root { :root {
--text-color: #333; --text-color: #333;
--company-color1: rgba(64, 119, 187, 1); --company-color1: rgba(64, 119, 187, 1);
@ -54,6 +54,12 @@ body {
height: 100%; height: 100%;
width: 100%; width: 100%;
} }
.first-gesture {
background: rgba(200, 0, 0, 0.2);
width: 100%;
height: 100%;
position: absolute;
}
/* Main + Section Partition*/ /* Main + Section Partition*/
.main-body { .main-body {

View File

@ -0,0 +1,30 @@
.error-container {
margin: 2rem;
.top-error-message {
color: #44a;
font-size: 2rem;
font-weight: 100;
}
.top-error-description {
color: #444;
font-size: 1rem;
font-weight: 100;
}
.error-detail {
margin-top: 2rem;
padding: 1rem;
border: 1px solid;
.error-name {
font-weight: 700;
}
.error-message {
margin-top: 0.5rem;
}
.error-info-container {
margin-top: 0.5rem;
font-size: 0.8rem;
.error-info-line {
}
}
}
}

View File

@ -1,12 +1,12 @@
{ {
"name": "@dannadori/voice-changer-client-js", "name": "@dannadori/voice-changer-client-js",
"version": "1.0.77", "version": "1.0.78",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@dannadori/voice-changer-client-js", "name": "@dannadori/voice-changer-client-js",
"version": "1.0.77", "version": "1.0.78",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@types/readable-stream": "^2.3.15", "@types/readable-stream": "^2.3.15",

View File

@ -1,6 +1,6 @@
{ {
"name": "@dannadori/voice-changer-client-js", "name": "@dannadori/voice-changer-client-js",
"version": "1.0.77", "version": "1.0.78",
"description": "", "description": "",
"main": "dist/index.js", "main": "dist/index.js",
"directories": { "directories": {

View File

@ -11,6 +11,7 @@ export type UseClientProps = {
} }
export type ClientState = { export type ClientState = {
initialized: boolean
// 各種設定I/Fへの参照 // 各種設定I/Fへの参照
workletSetting: WorkletSettingState workletSetting: WorkletSettingState
clientSetting: ClientSettingState clientSetting: ClientSettingState
@ -43,6 +44,7 @@ const InitialPerformanceData: PerformanceData = {
export const useClient = (props: UseClientProps): ClientState => { export const useClient = (props: UseClientProps): ClientState => {
const [initialized, setInitialized] = useState<boolean>(false)
// (1-1) クライアント // (1-1) クライアント
const voiceChangerClientRef = useRef<VoiceChangerClient | null>(null) const voiceChangerClientRef = useRef<VoiceChangerClient | null>(null)
const [voiceChangerClient, setVoiceChangerClient] = useState<VoiceChangerClient | null>(voiceChangerClientRef.current) const [voiceChangerClient, setVoiceChangerClient] = useState<VoiceChangerClient | null>(voiceChangerClientRef.current)
@ -110,6 +112,7 @@ export const useClient = (props: UseClientProps): ClientState => {
audio.srcObject = voiceChangerClientRef.current.stream audio.srcObject = voiceChangerClientRef.current.stream
audio.play() audio.play()
initializedResolveRef.current!() initializedResolveRef.current!()
setInitialized(true)
} }
initialized() initialized()
}, [props.audioContext]) }, [props.audioContext])
@ -133,6 +136,7 @@ export const useClient = (props: UseClientProps): ClientState => {
} }
return { return {
initialized,
// 各種設定I/Fへの参照 // 各種設定I/Fへの参照
clientSetting, clientSetting,
workletNodeSetting, workletNodeSetting,

View File

@ -28,6 +28,10 @@ export const useClientSetting = (props: UseClientSettingProps): ClientSettingSta
useEffect(() => { useEffect(() => {
const loadCache = async () => { const loadCache = async () => {
const setting = await getItem(INDEXEDDB_KEY_CLIENT) as VoiceChangerClientSetting const setting = await getItem(INDEXEDDB_KEY_CLIENT) as VoiceChangerClientSetting
if (!setting) {
return
}
console.log("[ClientSetting] Load Setting from db", setting) console.log("[ClientSetting] Load Setting from db", setting)
if (setting.audioInput == "null") { if (setting.audioInput == "null") {
setting.audioInput = null setting.audioInput = null