mirror of
https://github.com/w-okada/voice-changer.git
synced 2025-02-03 00:33:57 +03:00
improve: add monitor to client mode
This commit is contained in:
parent
2b08e47377
commit
0e878edc1e
11
client/demo/dist/index.html
vendored
11
client/demo/dist/index.html
vendored
@ -1 +1,10 @@
|
||||
<!doctype html><html style="width:100%;height:100%;overflow:hidden"><head><meta charset="utf-8"/><title>Voice Changer Client Demo</title><script defer="defer" src="index.js"></script></head><body style="width:100%;height:100%;margin:0"><div id="app" style="width:100%;height:100%"></div></body></html>
|
||||
<!DOCTYPE html>
|
||||
<html style="width: 100%; height: 100%; overflow: hidden">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Voice Changer Client Demo</title>
|
||||
<script defer src="index.js"></script></head>
|
||||
<body style="width: 100%; height: 100%; margin: 0px">
|
||||
<div id="app" style="width: 100%; height: 100%"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
1147
client/demo/dist/index.js
vendored
1147
client/demo/dist/index.js
vendored
File diff suppressed because one or more lines are too long
31
client/demo/dist/index.js.LICENSE.txt
vendored
31
client/demo/dist/index.js.LICENSE.txt
vendored
@ -1,31 +0,0 @@
|
||||
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
@ -11,8 +11,8 @@
|
||||
"build:dev": "npm-run-all clean webpack:dev",
|
||||
"start": "webpack-dev-server --config webpack.dev.js",
|
||||
"build:mod": "cd ../lib && npm run build:dev && cd - && cp -r ../lib/dist/* node_modules/@dannadori/voice-changer-client-js/dist/",
|
||||
"build:mod_dos": "cd ../lib && npm run build:dev && cd ../demo && copy ../lib/dist/index.js node_modules/@dannadori/voice-changer-client-js/dist/",
|
||||
"build:mod_dos2": "copy ../lib/dist/index.js node_modules/@dannadori/voice-changer-client-js/dist/",
|
||||
"build:mod_dos": "cd ../lib && npm run build:dev && cd ../demo && npm-run-all build:mod_copy",
|
||||
"build:mod_copy": "XCOPY ..\\lib\\dist\\* .\\node_modules\\@dannadori\\voice-changer-client-js\\dist\\* /s /e /h /y",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
|
@ -63,6 +63,7 @@ type GuiStateAndMethod = {
|
||||
outputAudioDeviceInfo: MediaDeviceInfo[];
|
||||
audioInputForGUI: string;
|
||||
audioOutputForGUI: string;
|
||||
audioMonitorForGUI: string;
|
||||
fileInputEchoback: boolean | undefined;
|
||||
shareScreenEnabled: boolean;
|
||||
audioOutputForAnalyzer: string;
|
||||
@ -70,6 +71,7 @@ type GuiStateAndMethod = {
|
||||
setOutputAudioDeviceInfo: (val: MediaDeviceInfo[]) => void;
|
||||
setAudioInputForGUI: (val: string) => void;
|
||||
setAudioOutputForGUI: (val: string) => void;
|
||||
setAudioMonitorForGUI: (val: string) => void;
|
||||
setFileInputEchoback: (val: boolean) => void;
|
||||
setShareScreenEnabled: (val: boolean) => void;
|
||||
setAudioOutputForAnalyzer: (val: string) => void;
|
||||
@ -106,6 +108,7 @@ export const GuiStateProvider = ({ children }: Props) => {
|
||||
const [outputAudioDeviceInfo, setOutputAudioDeviceInfo] = useState<MediaDeviceInfo[]>([]);
|
||||
const [audioInputForGUI, setAudioInputForGUI] = useState<string>("none");
|
||||
const [audioOutputForGUI, setAudioOutputForGUI] = useState<string>("none");
|
||||
const [audioMonitorForGUI, setAudioMonitorForGUI] = useState<string>("none");
|
||||
const [fileInputEchoback, setFileInputEchoback] = useState<boolean>(false); //最初のmuteが有効になるように。undefined <-- ??? falseしておけばよさそう。undefinedだとwarningがでる。
|
||||
const [shareScreenEnabled, setShareScreenEnabled] = useState<boolean>(false);
|
||||
const [audioOutputForAnalyzer, setAudioOutputForAnalyzer] = useState<string>("default");
|
||||
@ -270,6 +273,7 @@ export const GuiStateProvider = ({ children }: Props) => {
|
||||
outputAudioDeviceInfo,
|
||||
audioInputForGUI,
|
||||
audioOutputForGUI,
|
||||
audioMonitorForGUI,
|
||||
fileInputEchoback,
|
||||
shareScreenEnabled,
|
||||
audioOutputForAnalyzer,
|
||||
@ -277,6 +281,7 @@ export const GuiStateProvider = ({ children }: Props) => {
|
||||
setOutputAudioDeviceInfo,
|
||||
setAudioInputForGUI,
|
||||
setAudioOutputForGUI,
|
||||
setAudioMonitorForGUI,
|
||||
setFileInputEchoback,
|
||||
setShareScreenEnabled,
|
||||
setAudioOutputForAnalyzer,
|
||||
|
@ -2,14 +2,14 @@ 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 { AUDIO_ELEMENT_FOR_PLAY_MONITOR, AUDIO_ELEMENT_FOR_PLAY_RESULT, AUDIO_ELEMENT_FOR_TEST_CONVERTED, AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK, AUDIO_ELEMENT_FOR_TEST_ORIGINAL, INDEXEDDB_KEY_AUDIO_MONITR, INDEXEDDB_KEY_AUDIO_OUTPUT } from "../../../const";
|
||||
import { isDesktopApp } from "../../../const";
|
||||
|
||||
export type DeviceAreaProps = {};
|
||||
|
||||
export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
const { setting, serverSetting, audioContext, setAudioOutputElementId, initializedRef, setVoiceChangerClientSetting, startOutputRecording, stopOutputRecording } = useAppState();
|
||||
const { isConverting, audioInputForGUI, inputAudioDeviceInfo, setAudioInputForGUI, fileInputEchoback, setFileInputEchoback, setAudioOutputForGUI, audioOutputForGUI, outputAudioDeviceInfo, shareScreenEnabled, setShareScreenEnabled } = useGuiState();
|
||||
const { setting, serverSetting, audioContext, setAudioOutputElementId, setAudioMonitorElementId, initializedRef, setVoiceChangerClientSetting, startOutputRecording, stopOutputRecording } = useAppState();
|
||||
const { isConverting, audioInputForGUI, inputAudioDeviceInfo, setAudioInputForGUI, fileInputEchoback, setFileInputEchoback, setAudioOutputForGUI, setAudioMonitorForGUI, audioOutputForGUI, audioMonitorForGUI, outputAudioDeviceInfo, shareScreenEnabled, setShareScreenEnabled } = useGuiState();
|
||||
const [inputHostApi, setInputHostApi] = useState<string>("ALL");
|
||||
const [outputHostApi, setOutputHostApi] = useState<string>("ALL");
|
||||
const [monitorHostApi, setMonitorHostApi] = useState<string>("ALL");
|
||||
@ -244,10 +244,10 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
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();
|
||||
// // 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";
|
||||
@ -256,7 +256,7 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
<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 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>
|
||||
@ -381,7 +381,8 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
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) => {
|
||||
// [AUDIO_ELEMENT_FOR_PLAY_RESULT, AUDIO_ELEMENT_FOR_TEST_ORIGINAL, AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK].forEach((x) => {
|
||||
[AUDIO_ELEMENT_FOR_PLAY_RESULT, AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK].forEach((x) => {
|
||||
const audio = document.getElementById(x) as HTMLAudioElement;
|
||||
if (audio) {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 1) {
|
||||
@ -598,7 +599,88 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
);
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, serverSetting.serverSetting.enableServerAudio]);
|
||||
|
||||
// (6) Monitor
|
||||
// (6) モニター
|
||||
useEffect(() => {
|
||||
const loadCache = async () => {
|
||||
const key = await getItem(INDEXEDDB_KEY_AUDIO_MONITR);
|
||||
if (key) {
|
||||
setAudioMonitorForGUI(key as string);
|
||||
}
|
||||
};
|
||||
loadCache();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const setAudioMonitor = async () => {
|
||||
const mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices();
|
||||
|
||||
[AUDIO_ELEMENT_FOR_PLAY_MONITOR].forEach((x) => {
|
||||
const audio = document.getElementById(x) as HTMLAudioElement;
|
||||
if (audio) {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 1) {
|
||||
// Server Audio を使う場合はElementから音は出さない。
|
||||
audio.volume = 0;
|
||||
} else if (audioMonitorForGUI == "none") {
|
||||
// @ts-ignore
|
||||
audio.setSinkId("");
|
||||
audio.volume = 0;
|
||||
} else {
|
||||
const audioOutputs = mediaDeviceInfos.filter((x) => {
|
||||
return x.kind == "audiooutput";
|
||||
});
|
||||
const found = audioOutputs.some((x) => {
|
||||
return x.deviceId == audioMonitorForGUI;
|
||||
});
|
||||
if (found) {
|
||||
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
||||
audio.setSinkId(audioMonitorForGUI);
|
||||
audio.volume = 1;
|
||||
} else {
|
||||
console.warn("No audio output device. use default");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
setAudioMonitor();
|
||||
}, [audioMonitorForGUI, serverSetting.serverSetting.enableServerAudio]);
|
||||
|
||||
// (6-1) クライアント
|
||||
const clientMonitorRow = useMemo(() => {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 1) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="config-sub-area-control">
|
||||
<div className="config-sub-area-control-title left-padding-1">monitor</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<select
|
||||
className="body-select"
|
||||
value={audioMonitorForGUI}
|
||||
onChange={(e) => {
|
||||
setAudioMonitorForGUI(e.target.value);
|
||||
setItem(INDEXEDDB_KEY_AUDIO_MONITR, e.target.value);
|
||||
}}
|
||||
>
|
||||
{outputAudioDeviceInfo.map((x) => {
|
||||
return (
|
||||
<option key={x.deviceId} value={x.deviceId}>
|
||||
{x.label}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [serverSetting.serverSetting.enableServerAudio, outputAudioDeviceInfo, audioMonitorForGUI]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("initializedRef.current", initializedRef.current);
|
||||
setAudioMonitorElementId(AUDIO_ELEMENT_FOR_PLAY_MONITOR);
|
||||
}, [initializedRef.current]);
|
||||
|
||||
// (6-2) サーバ
|
||||
const serverMonitorRow = useMemo(() => {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 0) {
|
||||
return <></>;
|
||||
@ -685,10 +767,12 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
{audioInputScreenRow}
|
||||
{clientAudioOutputRow}
|
||||
{serverAudioOutputRow}
|
||||
{clientMonitorRow}
|
||||
{serverMonitorRow}
|
||||
|
||||
{outputRecorderRow}
|
||||
<audio hidden id={AUDIO_ELEMENT_FOR_PLAY_RESULT}></audio>
|
||||
<audio hidden id={AUDIO_ELEMENT_FOR_PLAY_MONITOR}></audio>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,13 +1,15 @@
|
||||
|
||||
export const AUDIO_ELEMENT_FOR_PLAY_RESULT = "audio-result"
|
||||
export const AUDIO_ELEMENT_FOR_TEST_ORIGINAL = "audio-test-original"
|
||||
export const AUDIO_ELEMENT_FOR_TEST_CONVERTED = "audio-test-converted"
|
||||
export const AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK = "audio-test-converted-echoback"
|
||||
export const AUDIO_ELEMENT_FOR_PLAY_RESULT = "audio-result" // 変換後の出力用プレイヤー
|
||||
export const AUDIO_ELEMENT_FOR_PLAY_MONITOR = "audio-monitor" // 変換後のモニター用プレイヤー
|
||||
export const AUDIO_ELEMENT_FOR_TEST_ORIGINAL = "audio-test-original" // ??? 使ってないかも。
|
||||
export const AUDIO_ELEMENT_FOR_TEST_CONVERTED = "audio-test-converted" // ファイルインプットのコントロール
|
||||
export const AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK = "audio-test-converted-echoback" // ファイルインプットのエコーバック
|
||||
|
||||
export const AUDIO_ELEMENT_FOR_SAMPLING_INPUT = "body-wav-container-wav-input"
|
||||
export const AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT = "body-wav-container-wav-output"
|
||||
|
||||
export const INDEXEDDB_KEY_AUDIO_OUTPUT = "INDEXEDDB_KEY_AUDIO_OUTPUT"
|
||||
export const INDEXEDDB_KEY_AUDIO_MONITR = "INDEXEDDB_KEY_AUDIO_MONITOR"
|
||||
export const INDEXEDDB_KEY_DEFAULT_MODEL_TYPE = "INDEXEDDB_KEY_DEFALT_MODEL_TYPE"
|
||||
|
||||
|
||||
|
@ -97,6 +97,7 @@ export const ServerSettingKey = {
|
||||
"serverReadChunkSize": "serverReadChunkSize",
|
||||
"serverInputAudioGain": "serverInputAudioGain",
|
||||
"serverOutputAudioGain": "serverOutputAudioGain",
|
||||
"serverMonitorAudioGain": "serverMonitorAudioGain",
|
||||
|
||||
"tran": "tran",
|
||||
"noiseScale": "noiseScale",
|
||||
@ -157,6 +158,7 @@ export type VoiceChangerServerSetting = {
|
||||
serverReadChunkSize: number
|
||||
serverInputAudioGain: number
|
||||
serverOutputAudioGain: number
|
||||
serverMonitorAudioGain: number
|
||||
|
||||
|
||||
tran: number // so-vits-svc
|
||||
@ -361,6 +363,7 @@ export const DefaultServerSetting: ServerInfo = {
|
||||
serverReadChunkSize: 256,
|
||||
serverInputAudioGain: 1.0,
|
||||
serverOutputAudioGain: 1.0,
|
||||
serverMonitorAudioGain: 1.0,
|
||||
|
||||
// VC Specific
|
||||
srcId: 0,
|
||||
|
@ -47,6 +47,7 @@ export type ClientState = {
|
||||
clearSetting: () => Promise<void>
|
||||
// AudioOutputElement 設定
|
||||
setAudioOutputElementId: (elemId: string) => void
|
||||
setAudioMonitorElementId: (elemId: string) => void
|
||||
|
||||
ioErrorCount: number
|
||||
resetIoErrorCount: () => void
|
||||
@ -215,6 +216,18 @@ export const useClient = (props: UseClientProps): ClientState => {
|
||||
}
|
||||
}
|
||||
|
||||
const setAudioMonitorElementId = (elemId: string) => {
|
||||
if (!voiceChangerClientRef.current) {
|
||||
console.warn("[voiceChangerClient] is not ready for set audio output.")
|
||||
return
|
||||
}
|
||||
const audio = document.getElementById(elemId) as HTMLAudioElement
|
||||
if (audio.paused) {
|
||||
audio.srcObject = voiceChangerClientRef.current.stream
|
||||
audio.play()
|
||||
}
|
||||
}
|
||||
|
||||
// (2-2) 情報リロード
|
||||
const getInfo = useMemo(() => {
|
||||
return async () => {
|
||||
@ -286,6 +299,7 @@ export const useClient = (props: UseClientProps): ClientState => {
|
||||
|
||||
// AudioOutputElement 設定
|
||||
setAudioOutputElementId,
|
||||
setAudioMonitorElementId,
|
||||
|
||||
ioErrorCount,
|
||||
resetIoErrorCount
|
||||
|
Loading…
Reference in New Issue
Block a user